Compare commits

...
20 Commits
Author SHA1 Message Date
Bitsy c76f629971 feat(bridge): expose the material3-adaptive version to consumers
material3-adaptive rides its own release train upstream (1.3.0-rc01),
like material3 does, so a consumer had no way to express its version
through the plugin - they would have had to hardcode a number and keep
it in sync with the port by hand.

Adds composeDesktopNative.composeMaterial3Adaptive alongside the existing
compose / composeMaterial3 / composeRuntime accessors, stamped into
bridge-version.properties from the version catalog like the others.

The README consumer snippet now shows the adaptive coordinates, and calls
out that window-core has to be declared DIRECTLY when commonMain touches
WindowSizeClass - substituted modules hide their transitives from the
common metadata classpath.
2026-09-22 16:52:25 +02:00
Bitsy c4564b3a10 feat(demo): make the Adaptive screen fold on a live resize
The first version of this screen proved the vendored adaptive modules
render, but it did not really demonstrate adaptation: it left
rememberListDetailPaneScaffoldNavigator on its default directive, which
is the WINDOW size class, and pinned the scaffold to a fixed 420dp.

The window is the wrong input here. The screen does not get the window -
the demo shell spends 190dp on the sidebar plus 48dp of padding - so
defaulting folds ~240dp too late and leaves two panes side by side in a
region already too cramped for them. The directive is now computed from
the width the screen actually gets (BoxWithConstraints), which is what
any pane-hosting app that is not full-window should do; the pane height
tracks the window height instead of being a constant. The header prints
the window size class and the pane-area size class side by side so the
difference is visible while dragging.

Also adds selection highlighting and a Back button that only appears
while folded, so the single-pane mode is actually navigable.

New `--adaptivetest` probe: drives one window wide -> narrow -> wide with
setSize and asserts the scaffold folds and unfolds. This is what proves
the resize chain end to end - SDL resize, the owner's snapshot-backed
containerSize, recomposition, BoxWithConstraints re-measure, a new
PaneScaffoldDirective on the navigator, a new scaffold value. The
--screen=Adaptive --width=N screenshots could only ever prove the fold
was right for the size the window OPENED at, since each run is a fresh
process. The probe reads a small internal seam on the screen
(gAdaptiveTwoPane); it is documented as a probe hook and is not UI.

Verified on mingwX64: folds at 800dp and stays two-pane at 1100/1400dp,
--adaptivetest passes repeatedly, and the JVM parity leg plus commonMain
metadata still compile.
2026-09-22 16:46:57 +02:00
Bitsy fb8e97b74d feat(material3): port material3-adaptive to desktop native
Closes the request in issue #4. org.jetbrains.compose.material3.adaptive:*
1.3.0-rc01 publishes ios_arm64 / ios_simulator_arm64 / macos_arm64 and
nothing else - no linux, no mingw. (It does carry macosArm64, which
answers the reporter's open question.)

All four artifacts are now vendored and built for every desktop-native
target, with ZERO source edits. Two things made it a pure re-target:

- every platform actual the native legs need already exists upstream:
  adaptive/nonAndroidMain (calculatePosture), adaptive-layout/skikoMain
  (~75 l10n tables + Strings + the drag handle) and
  adaptive-layout/nativeMain (identityHashCode, isGraphicsLayerElement)
- androidx.window:window-core - the dependency `adaptive` api-exposes and
  the obvious suspect for the apple-only publication - has shipped
  linux_x64 / linux_arm64 / mingw_x64 / macos_arm64 klibs all along, under
  BOTH the google and jetbrains coordinates

So the artifacts were missing only because upstream never enabled the
targets, not because anything blocked them.

125 files vendored off the existing COMPOSE_CORE_REF pin - no new
SET_REPO. Upstream's skikoMain lands in src/vendor/native/ beside
upstream's own nativeMain; the two actualise disjoint expects so they
share one source set.

Both bridges substitute all four coordinates, so consumers keep declaring
the official org.jetbrains.compose.material3.adaptive:* coords.

Verified: all four compile for mingwX64 + linuxX64 + linuxArm64, API
dumps generated, vendor-clean and audit-exclusions pass, and
`demo --screen=Adaptive` renders a live ListDetailPaneScaffold with the
window size class resolving correctly. adaptive-navigation3 builds and
dumps API but has no demo screen yet, so its nav3 SceneStrategy path is
unproven at runtime.

Two notes for later:
- the new demo screen hit the granular-metadata trap documented in
  CLAUDE.md: because `adaptive` is substituted on native configs, KGP
  drops its transitives from the commonMain metadata classpath, so
  window-core had to be declared DIRECTLY in demo's commonMain
- demo's JVM parity leg moves navigation3-ui 1.2.0-alpha02 -> 1.1.2 to
  follow CMP 1.12.1's navigation3 reset onto the 1.1 line
2026-09-22 16:07:23 +02:00
Bitsy 6c3bfd8679 chore(deps): coil 3.6.2 -> 3.6.3, ktor 3.5.2 -> 3.6.0
Sweep of every pin the project owns. Only these two moved:
- coil: COIL_REF bumped with it and the vendored sources re-synced (no
  local edits needed)
- ktor: plain Maven dep, apidemo's HTTP client

Already latest, left alone: SDL3 (release-3.4.16), koin 4.2.2, okio
3.18.2, coroutines 1.11.0, kotlinx-io-okio 0.9.1. Deliberately not
bumped: kotlinx-serialization (latest is 1.12.0-RC, a prerelease) and
everything under policy 1, which CMP 1.12.1 still pins where we are.

The pulse group rename also lands in the klib dumps here - the unique
name embeds the group, and BCV infers the macOS dumps from the checked-in
files on Windows, so those headers had to be rewritten for apiDump to run.
2026-09-22 15:51:26 +02:00
Bitsy ec2d9b06ad chore(pulse): namespace the republish under the project group
Pulse is the one vendored dependency that is ITSELF a com.bitsycore
library (upstream com.bitsycore.lib:*), so republishing it as a bare
com.bitsycore.pulse:* read like an upstream coordinate and left room for
a real collision if upstream ever moves under that group.

Group becomes com.bitsycore.compose.desktop.native.pulse:*. Nothing
changes for consumers: they still declare the upstream com.bitsycore.lib
coordinate and the bridge plugin substitutes it on native configs.
2026-09-22 15:48:21 +02:00
Bitsy 1d6e5a76de chore: port to Compose Multiplatform 1.12.1
Bumps COMPOSE_CORE_REF / COMPOSE_REF to v1.12.1 and re-syncs all 1760
vendored files. No vendored file needed an edit; the ten manual vendors
were all verified unchanged upstream base..pin and only needed their
VENDOR-BASE ref re-stamped.

skiko stays 0.150.1, so the mingwX64 fork (0.150.1-mingw.2) still matches
its Skia base - no republish.

navigation3 moves BACKWARDS, 1.2.0-alpha04 -> 1.1.7: upstream re-checked
navigation3 out of release/1.11 for this patch (redirectversions.toml), so
the vendored :navigation3:navigation3-ui is now 1.1.7 source and the Maven
runtime has to follow it down. 1.1.7 publishes every target this port needs.

Two upstream-driven API deltas, both matching CMP:
- ui gains LocalSystemTheme, re-exposed upstream as @InternalComposeUiApi
- navigation3-ui loses NavigationBackHandler / rememberNavigationEventState,
  deleted by the 1.1.7 reset

LayerTransformationMatrix.kt's VENDOR-BASE path also corrected - it still
pointed at ui/ui, but upstream moved Matrices.skiko.kt to ui/ui-graphics,
which is why the drift check could not diff it.

Verified on mingwX64: both apps link, full probe suite passes (nav3test,
backtest, multiwintest included), vendor-clean and audit-exclusions clean.
2026-09-22 15:47:19 +02:00
Bitsy eb44db915e refactor: finish the SDL3* -> Sdl3* rename and regenerate the API dumps
Picks up the file/type renames (SDL3Backend -> Sdl3Backend, SDL3FrameClock ->
Sdl3FrameClock, SDL3EventMapper.kt -> AppEvent.kt) and unifies the 15 references
the rename left behind. All were prose or strings, which is why it still
compiled:

  - a user-visible error message ("SDL3Backend received GpuMode.Auto ...")
  - two user-visible demo UI strings ("driven by the SDL3FrameClock ...")
  - the MARK headers in AppEvent.kt and Sdl3Backend.kt
  - KDoc cross-references, including to `SDL3EventMapper.kt` which no longer
    exists - repointed at AppEvent.kt
  - 3 PLAN.md references

The codebase now uses Sdl3* uniformly, matching the Sdl3MainDispatcher /
Sdl3Clipboard / Sdl3DragAndDropOwner / Sdl3ScrollConfig that already followed
that form. `sdl3.*` (the cinterop package) and SDL_* (the C API) are untouched.

API dumps regenerated: a clean 1:1 rename, 41 SDL3Backend -> 41 Sdl3Backend in
:ui and nothing else - the other renamed types were not public. Breaking, but
com.compose.sdl only; no androidx.compose.* / org.jetbrains.compose.* change.

Verified beyond compiling: apiCheck green, both metadata publish gates compile,
nav3test/backtest/multiwintest PASS, raw SDL handles still resolve
(gpu=OpenGL window=true glContext=true), and the Images screen renders.
2026-09-19 00:12:36 +02:00
Bitsy 1120abc488 Update README to remove Infrastructure and add Support
Removed the Infrastructure section and added a Support section with a Discord link.
2026-09-18 23:54:32 +02:00
Bitsy 9d2656f905 refactor: make module-local project declarations internal
162 declarations leave the published API. They were public only because nothing
had ever audited the surface - none is Compose Multiplatform API, and none was
a deliberate native extra.

Removed by package:
  com.compose.sdl.graphics       45   canvas/painter/gradient/path plumbing
  com.compose.sdl.renderer.skia  23   SkiaBridge + SkiaSurfaceBridge
  com.compose.sdl.platform       21   DefaultViewConfiguration
  com.compose.sdl.res            17   ResourcePainter, vector->SVG, resource kinds
  com.compose.sdl.window         11   popup host internals
  com.compose.sdl.icons           7   IconDefaults

Method: inventory each module's public declarations from its klib dump by
DECLARATION OWNER (not by grepping names, which the earlier pass got wrong),
keep those referenced from another module, mark the rest internal, then let the
COMPILER adjudicate. That last step matters - it rejected three candidates that
look module-local but are real API, and I would have got at least one of them
wrong by reasoning alone:

  EncodedImageDecoder  exposed by a public property - it is an install seam
  RawSdlHandles        the return type of rawSdlHandles()
  ApplicationScope     the receiver of nativeComposeApp { Window(...) }

All three stay public.

Compose Multiplatform surface untouched: every removed line is owned by a
com.compose.sdl.* package, zero androidx.compose.* or org.jetbrains.compose.*.

Verified at runtime, since internalising the resource and renderer plumbing is
exactly the kind of change that compiles and then fails to draw: nav3test,
backtest and multiwintest PASS; the Images screen (which drives ResourcePainter,
the vector->SVG path and the image cache) renders 2112 distinct colours; the CPU
raster path still renders.

NOTE what this does NOT do. Kotlin `internal` is per-module and this repo spans
~20 Gradle modules, so anything crossing a module boundary must stay public -
SDL3Backend, ComposeRootHost, AppEvent and the rest of the cross-module plumbing
are still exposed. Narrowing those needs a @RequiresOptIn marker (upstream's
@InternalComposeUiApi pattern); none exists in this project yet.
2026-09-18 22:39:13 +02:00
Bitsy 48cfa5da4d docs(renderer): record why RenderBackend keeps its one-implementation interface
SkiaRenderBackend is the only implementor, which invites 'collapse the
interface, it is dual-backend residue'. It is not, and I tried it: the concrete
type lives in skikoRendererMain, BELOW nativeMain, so :desktop-native-window's
WindowInstance - which holds the renderer in nativeMain - cannot name it.
compileNativeMainKotlinMetadata fails with "Unresolved reference 'renderer'".

Same constraint as the expect fun createRenderBackend that returns it, whose
KDoc already explains its half. Documented on the interface so the next pass
does not spend the same detour.
2026-09-18 22:32:31 +02:00
Bitsy 856cbe5ef3 refactor(renderer): flatten GpuMode - every mode is Skia
GpuMode read as `Software` vs a nested `Skia.*` group, which dated from when
the alternative to Skia was SDL's own renderer. With SDL reduced to windowing
that split is actively misleading: its own KDoc said "Software - SKIA CPU
raster", so all three modes were Skia and the taxonomy implied otherwise.

  Auto / Skia.Metal / Skia.OpenGL / Software
    -> Auto / Metal / OpenGL / CpuRaster

The axis is now what it actually is: how Skia's output reaches the screen.
Added GpuMode.isGpuAccelerated so the GPU-to-CPU fallback in WindowInstance
reads as intent rather than `gpuMode is GpuMode.Skia`.

CLI compatibility kept: --gpu still accepts the retired skia.metal / skia.opengl
/ skia-* spellings alongside the bare metal / opengl / cpu / software / raster
names, so existing scripts and docs do not break.

API break is in com.compose.sdl only - no androidx.compose.* or
org.jetbrains.compose.* declaration changes, so the Compose Multiplatform
surface this port mirrors is untouched.

Verified the renderer actually still works on both paths rather than just
compiling: the resolved default reports gpu=OpenGL, every --gpu alias parses and
renders, and CPU-raster vs OpenGL screenshots of the Buttons screen differ by
0.01% of pixels (antialiasing only) with identical mean RGB.
2026-09-18 22:31:20 +02:00
Bitsy 5c7308c614 refactor(demo,renderer): split the probe suite out of MainNative; drawRoot is abstract
MainNative.kt held the entry point plus 20 probe scenarios in 1240 lines. The
scenarios now live in demo/src/nativeMain/kotlin/probes/, grouped by what they
exercise, and MainNative.kt is 284 lines of argument dispatch:

  InputProbes.kt      click, toggle, key, scroll, cursor, IME
  WindowProbes.kt     multi-window, window info, search-escape, locale
  AnimationProbes.kt  shared transition, AnimatedVisibility, dialog entry
  TextProbes.kt       font resolution, paragraph layout, metrics
  LifecycleProbes.kt  back navigation, Navigation3
  RenderProbes.kt     soak run, encoded-image decode

Behaviour is unchanged - each probe is `internal` so main()'s dispatch still
reaches it - and 134 imports that did not apply to the file they landed in were
pruned. Verified by running them, not just compiling: nav3test, backtest,
multiwintest and winattrtest still PASS, and clicktest/scrolltest (whose
scenarios moved) still report their expected values.

RenderBackend.drawRoot is now abstract rather than carrying an empty default
body. The no-op dated from when a second, non-Skia backend could opt out of it;
with SkiaRenderBackend the only implementation, an unimplemented drawRoot would
silently render nothing instead of failing to compile.

NOT done, and it should not be: removing the expect/actual createRenderBackend
seam. It looks like dual-backend residue but is load-bearing for a different
reason its KDoc already documents - the actual sits in skikoRendererMain, below
nativeMain, so without the expect it is invisible to nativeMain-level consumers.
Tested by deleting it: compileCommonMainKotlinMetadata still passed, but
:desktop-native-window's compileNativeMainKotlinMetadata failed with
"Unresolved reference 'createRenderBackend'" - and that is the compile the
WINDOWS publish job runs to produce the root modules, so it would have broken
publishing rather than local builds.
2026-09-18 22:25:58 +02:00
Bitsy 2fb322776a refactor: rename the f-prefixed member convention to m
500 occurrences across 31 files: mWidth, mSurface, mCache and friends. The `f`
prefix is retired project-wide.

Scope was checked before touching anything, because a blind rename here could
have done real damage:

- VENDORED code is untouched. All 32 src/vendor trees contain ZERO f-prefixed
  names (upstream Compose does not use the convention), so nothing in the
  byte-for-byte copies could be caught by the pattern. check-vendor-clean and
  check-vendor-drift both still pass.
- API-NEUTRAL. No f-prefixed name appears in any published .klib.api dump -
  they were all private - so this changes no consumer-visible surface, and
  apiCheck passes without a re-dump.
- The only matches inside quotes were string TEMPLATES ("...at $mPos",
  "frames=$mFrames"), i.e. code references that had to be renamed with
  everything else, not literal text.

Verified at runtime, not just by compiling: --nav3test, --backtest,
--multiwintest and --winattrtest all pass, and a Buttons screenshot still
renders.

Also corrects CLAUDE.md's Conventions section, which claimed the project used
"no f/in/v prefixes". That had not been true for a long time - the code uses v
for locals, in for parameters and k for file-level constants throughout. It now
documents what the code actually does, including that Composable parameters stay
plain because the name is part of the call-site API.
2026-09-18 22:00:40 +02:00
Bitsy 7c7bb63cae refactor(input): fold LegacyPointerEvent into AppEvent.Pointer
LegacyPointerEvent was a hand-written 4-field carrier (x, y, type, button) with
longhand equals/hashCode/toString - a data class written out by hand. Its whole
life was three lines in SDL3EventMapper that wrapped it and four in
WindowInstance that immediately took it apart again, so the indirection bought
nothing while putting the word "Legacy" on the live input path.

AppEvent.Pointer now carries the four fields directly, which also makes it
consistent with its MouseWheel sibling (already flat). LegacyPointerEvent.kt is
deleted, and com.compose.sdl.input went empty with it.

API: this removes a published declaration, so it is binary-breaking for anyone
who reached into the raw SDL event pipeline. Nothing in-tree did - demo and
apidemo never reference AppEvent or com.compose.sdl.input at all - and this
layer is internal plumbing rather than documented consumer surface. Net effect
on the klib dump is 26 declarations out, 16 in.

Verified pointer dispatch still works rather than assuming: --searchesctest
CLICKS the SearchBar to expand it, and its before/after screenshots differ by
84.87% of pixels, so the click landed and the overlay opened. --nav3test,
--backtest and --multiwintest also pass.
2026-09-18 21:42:00 +02:00
Bitsy 02fb2ddf4e fix(renderer): bind each window's GL context per frame
`demo --multiwintest` exited 139 (SIGSEGV) with no output, so the multi-window
regression probe had silently been asserting nothing.

An OpenGL context is per-WINDOW, but "current" is per-THREAD state, and
SDL_GL_MakeCurrent ran exactly once per window at creation (SDL3Backend.init)
with nothing re-binding it per frame. So with two windows open, whichever
initialised last owned the thread: window A issued its draw and swap against
window B's context. That is silent corruption while both windows live, and a
hard fault in present() the moment B is destroyed and the current context is
left dangling - which is what the probe hit.

SkiaGLBridge now binds its own context at every entry point that touches GL:
init, ensureSize (before the unchanged-size early return, so it is the
per-frame bind covering the draw phase that follows), present, and destroy (a
window frees its GPU objects against its own context, not whichever happens to
be current). Purely additive.

Verified both paths, because the single-window path is the one with the most to
lose:
  single window  Buttons screenshot mean RGB [46.6 44.4 51.5], 167 distinct
                 colours - IDENTICAL to before the change, as expected since
                 SDL short-circuits a redundant bind
  multi window   --multiwintest PASS (was exit 139)
  --nav3test, --backtest, --winattrtest all still pass

Worth recording: multi-window was never correct here, not merely broken at
teardown - two live windows were sharing one context the whole time.
2026-09-18 21:35:25 +02:00
Bitsy 126e83c056 refactor(window): split the window module; add the Compose Desktop Window attributes + a raw SDL escape hatch
ComposeWindow.kt had grown to 985 lines holding four unrelated concerns. Split
by concern, no behaviour change:

  ComposeWindow.kt    337  public surface (ApplicationScope, Window(),
                           nativeComposeApp, nativeComposeWindow) + main loop
  WindowInstance.kt   540  one SDL window: backend, render backend, root host,
                           recomposer/composition, events, frame pump, teardown
  AppRuntime.kt        82  live-window registry + create/destroy lifecycle
  FrameTiming.kt       83  virtual/deterministic frame clocks + probe quiescence
  WindowAttributes.kt  30  the new attribute bundle

Also pruned 25 imports that no longer applied to what stayed behind.

WINDOW ATTRIBUTES. Window() and nativeComposeWindow() now take the same
parameters as Compose Desktop's Window(): undecorated, transparent, resizable,
enabled, focusable, alwaysOnTop, onPreviewKeyEvent and onKeyEvent. The mutable
ones re-apply whenever they change (DisposableEffect keyed on the bundle);
transparent is creation-only because SDL needs SDL_WINDOW_TRANSPARENT at
SDL_CreateWindow and offers no setter, which WindowAttributes documents.

`enabled = false` drops pointer / key / wheel / text / drop events while the
window keeps rendering, matching Compose Desktop. Key dispatch follows
upstream's order: window preview -> focused content -> window onKeyEvent -> app
shortcut handler -> back navigation.

RAW SDL ACCESS. ComposeNativeWindow.rawSdlHandles() returns a RawSdlHandles data
class of the four opaque pointers (window / renderer / glContext / metalView) so
advanced users can call the sdl3.* cinterop directly. Named for what it is
rather than getRawSdlAccessor(): it hands back handles, not an accessor object,
and it is a snapshot - the KDoc spells out that the pointers die with the window
and which ones are non-null per resolved GpuMode.

Verified by a new `demo --winattrtest` probe, which asserts all four behaviours
rather than eyeballing them:
  ATTR      undecorated=true resizable=false alwaysOnTop=true focusable=true
  RAW       gpu=Skia.OpenGL window=true glContext=true renderer=false metalView=false
  KEYCHAIN  preview=2 onKey=2   (both window handlers saw the down/up pair)
  DISABLED  preview=2 onKey=2   (unchanged after injecting with enabled=false)

Existing probes still pass (--nav3test, --backtest, --searchesctest).

Records PLAN.md 7c: `demo --multiwintest` segfaults on mingwX64. Confirmed
PRE-EXISTING by stashing this work and reproducing on the previous commit, so
it is not fallout from the split - the probe is just silent on crash, so it had
gone unnoticed.
2026-09-18 20:54:25 +02:00
Bitsy 0c09109e6e fix(bridge): make the app-icon object a real input of the mingw link
Editing an app-icon PNG rebuilt the .ico and the windres .o and then left the
link UP-TO-DATE, so the .exe silently kept the OLD embedded icon. Reproduced on
:apidemo - generateComposeNativeIco and compileComposeNativeIconResource both
re-ran, linkDebugExecutableMingwX64 reported UP-TO-DATE, and the executable's
md5 did not move.

The link was wired with a bare `dependsOn(windresTask)`, which only ORDERS the
two. The object's path reaches the linker as a `linkerOpts` STRING, so the path
is an input but its CONTENT never is - nothing about a changed .o could
invalidate the link.

Replaced with `inputs.files(windresTask)`, which carries the task dependency AND
puts the object's content into the up-to-date check. Verified: the link now
re-runs and the .exe changes when an icon PNG is edited, in both directions.

The .rgba window-icon blobs were never affected - they go into the data.kres Zip
via `from(dir)`, and a Copy spec is already a declared input.

Also annotates the three icon task types with @DisableCachingByDefault. They
had no cacheability declaration, which failed `validatePlugins` and with it
:compose-desktop-native-bridge:build; that failure predates this change.
2026-09-12 18:44:46 +02:00
Bitsy 5804128e1d fix(bridge): aggregate dependency modules' composeResources into data.kres
In a multi-module project, resources declared in a LIBRARY module never reached
the final data.kres - only the app module's own composeResources were packaged,
so every Res.* accessor pointing at a submodule resource failed at runtime.

The Zip sourced the app's own `generated/compose/resourceGenerator/
preparedResources/<sourceSet>/composeResources`, which is per-module by
construction. Kotlin already solves this: `<target>AggregateResources` merges
this project's own composeResources with those of every dependency module into
`build/kotlin-multiplatform-resources/aggregated-resources/<target>/`, each
under its OWN `composeResources/<package>/` prefix. The plugin now reads that.

Two bugs fixed at once. The old path also forced a SINGLE resolved package onto
everything it copied, which is wrong the moment more than one module
contributes - the aggregate keeps each module's real package instead.

The aggregate directory is already rooted at `composeResources/`, so it is
copied with no `into()` remapping. Kept the old per-module wiring as a fallback
for Kotlin versions that do not register the aggregation tasks.

Verified with a throwaway library module carrying a composeResources file:
  before  data.kres had only the app's resources
  after   composeResources/<app>.generated.resources/files/app_marker.txt
          composeResources/<lib>.generated.resources/files/submodule_marker.txt
Existing single-module apps are unaffected - :demo's resources still land under
`demo.generated.resources` (it sets packageOfResClass explicitly, which the old
code also read), and its Images screen still loads PNG/JPG/SVG/XML at runtime.
2026-09-12 13:10:30 +02:00
Bitsy 04d1df58de docs(plan): record the icon-font centring fix 2026-09-12 12:32:39 +02:00
Bitsy a87ff2d7f6 fix(icons): centre the icon-font glyph so Material Symbols stop rendering low
Material Symbols icons sat 2-4px lower on native than on the JVM reference,
the offset scaling with icon size (~13% of it): eye 11px -> +2, gear/lock 16px
-> +2, download 28px -> +4. Equal top and bottom deltas with identical ink
heights, so it was a pure downward translation, not a sizing problem.

The two stacks positioned the glyph differently. The JVM actual centres it
explicitly (MaterialSymbolsIcon.jvm.kt: y = height/2 - (ascent+descent)/2),
while IconFontIcon just placed IconText in a Box with the DEFAULT TopStart
alignment. The glyph's natural text box is taller than the nominal icon size -
its height is the font's ascent+descent and Material Symbols' em box overshoots
the icon grid - so top-aligning it pushed the glyph down.

Centering the text box is equivalent to the JVM's baseline math, because
IconText sets no lineHeight and its box height therefore IS ascent+descent.

wrapContentSize(unbounded = true) is load-bearing and was the whole reason a
first attempt at this changed nothing: Modifier.size() hands the child FIXED
constraints, so the text measured at exactly `size`, nothing overflowed, and
contentAlignment had nothing to align.

Measured on apidemo, native vs jvm ink boxes:
  gear      +2/+1 -> 0/0
  download  +4/+4 -> 0/0
  lock      +2/+1 -> 0/0
  eye       +2/+2 -> +1/+1   (half-pixel rounding: an odd-height glyph centred
                              in an even box; the JVM draws at a float y where
                              native placement is integer)
Whole-window drift >32/255: 0.29% -> 0.17%.

Found by measuring rather than by eye, off the apidemo --screenshot harness.
2026-09-12 12:32:30 +02:00
118 changed files with 4801 additions and 2568 deletions
+75 -11
View File
@@ -96,7 +96,30 @@ compose/
│ ├── foundation/ → :compose:foundation:foundation - androidx.compose.foundation.*
│ └── foundation-layout/ → :compose:foundation:foundation-layout - androidx.compose.foundation.layout.*
├── material3/
│ └── material3/ → :compose:material3:material3 - androidx.compose.material3.*
│ ├── material3/ → :compose:material3:material3 - androidx.compose.material3.*
│ └── adaptive/ → the material3-adaptive family, VENDORED VERBATIM.
│ │ org.jetbrains.compose.material3.adaptive:* publishes
│ │ ios + macosArm64 ONLY - no linux, no mingw (issue #4).
│ │ Nothing in the sources blocks those targets: every
│ │ actual the native legs need already exists upstream
│ │ (nonAndroidMain / skikoMain / nativeMain), and the one
│ │ real dependency, androidx.window:window-core, ALREADY
│ │ publishes linux + mingw + macos. The artifacts were
│ │ missing purely because upstream never enabled the
│ │ targets - so this port is vendor-only, ZERO source
│ │ edits. Upstream's skikoMain (the ~75 l10n tables +
│ │ Strings / drag-handle actuals) lands in src/vendor/
│ │ native/ next to upstream's own nativeMain actuals;
│ │ they actualise disjoint expects so they coexist.
│ │ com.bitsycore.compose.material3.adaptive:<module>.
│ ├── adaptive/ → :compose:material3:adaptive:adaptive - WindowAdaptiveInfo /
│ │ Posture / window size class. api-exposes window-core.
│ ├── adaptive-layout/ → :compose:material3:adaptive:adaptive-layout - ListDetail /
│ │ SupportingPane / ThreePane scaffolds + pane motion
│ ├── adaptive-navigation/ → :compose:material3:adaptive:adaptive-navigation -
│ │ ThreePaneScaffoldNavigator + back behaviour
│ └── adaptive-navigation3/ → :compose:material3:adaptive:adaptive-navigation3 -
│ SceneStrategy bindings for nav3 (→ :navigation3-ui)
├── material/
│ └── material-ripple/ → :compose:material:material-ripple - androidx.compose.material.ripple.*
└── desktop/native/desktop-native-window/ → :compose:desktop:native:desktop-native-window - nativeComposeApp { Window(...) {} }
@@ -184,10 +207,14 @@ pulse/ → Pulse MVI. Upstream (bitsycore/pulse-mvi, PULSE_REF) ta
├── pulse-viewmodel/ → :pulse:pulse-viewmodel - ViewModel binding
├── pulse-savedstate/ → :pulse:pulse-savedstate - SavedStateHandle persistence
└── pulse-compose/ → :pulse:pulse-compose - Compose bindings
Published as com.bitsycore.pulse:<module>, NOT
upstream's com.bitsycore.lib:<module> - the two
must not collide; the bridge substitutes the
upstream coord on native configs.
Published as com.bitsycore.compose.desktop.native
.pulse:<module>, NOT upstream's com.bitsycore.lib:
<module>. Pulse is the one vendored dep that is
ITSELF a com.bitsycore library, so the republish is
namespaced under this project rather than a bare
com.bitsycore.pulse that would read like upstream;
the bridge substitutes the upstream coord on
native configs.
navigation3/
└── navigation3-ui/ → :navigation3:navigation3-ui - androidx.navigation3.ui.* + scene machinery,
@@ -304,6 +331,14 @@ sees the split modules. Full DAG: `:ui-util → collection`; `:ui-geometry → :
`:foundation → :animation, :foundation-layout, :animation-core, :ui`;
`:material-ripple → :foundation, :animation-core`;
`:material3 → :foundation, :material-ripple, :animation-core, :foundation-layout`.
The adaptive family sits beside material3 rather than under it - it depends on
`:ui` / `:foundation` / `:animation-core`, NOT on `:material3`:
`:adaptive → :ui, :foundation, window-core`;
`:adaptive-layout → :adaptive, :ui, :animation-core, :animation, :foundation,
:foundation-layout, :ui-geometry, collection, window-core`;
`:adaptive-navigation → :adaptive-layout, :foundation, :ui-util`;
`:adaptive-navigation3 → :adaptive-navigation, :navigation3-ui, collection,
navigationevent-compose`.
`:ui` is the Compose core + the Skia RenderBackend + the SDL↔Compose bridges; it
sits on `:ui-graphics` / `:ui-text` (the graphics/text primitives + their skiko
@@ -566,8 +601,18 @@ drift / vendor-clean guardrails are in
## Key files by area - start here when you need to find something
### Renderer + main loop
- `compose/desktop/native/desktop-native-window/src/nativeMain/…/ComposeWindow.kt` - main loop,
recomposer lifecycle, SDL event dispatch, composition-local seeding.
- `compose/desktop/native/desktop-native-window/src/nativeMain/…/` - the window
module, one concern per file:
- `ComposeWindow.kt` - the PUBLIC surface (`ApplicationScope`, `Window()`,
`nativeComposeApp`, `nativeComposeWindow`) + the main loop.
- `WindowInstance.kt` - one SDL window: backend + render backend + root host +
its own recomposer/composition, event handling, frame pump, teardown. This is
where composition-local seeding lives.
- `AppRuntime.kt` - the live-window registry + create/destroy lifecycle.
- `WindowAttributes.kt` - the Compose Desktop `Window()` attribute bundle.
- `FrameTiming.kt` - deterministic/virtual frame clocks and the
probe-quiescence hooks (`disableInfiniteAnimations`, `useVirtualFrameTime`,
`windowHasInvalidations`).
- `compose/ui/ui/src/nativeMain/…/RenderBackend.kt` - the interface.
- `compose/ui/ui/src/nativeMain/…/GpuMode.kt` - sealed driver picker
(`Auto` / `Software` / `Skia.OpenGL` / `Skia.Metal`).
@@ -632,9 +677,23 @@ Open renderer / fidelity / release work is tracked in [PLAN.md](PLAN.md).
## Conventions
Kotlin standard style - plain `camelCase` for parameters, local variables,
and fields. **No `f`/`in`/`v` prefixes**, no SPIRTECH scheme (see the global
CLAUDE.md - that scheme is C-only).
Kotlin standard style for formatting (braces, wrapping, indentation).
Naming, as the code actually is - this paragraph used to claim "no prefixes",
which had not been true for a long time:
- **`m` for private/protected members** - `mWidth`, `mSurface`, `mCache`. NOT
`f`: the `f` prefix was retired across the project, so do not reintroduce it.
- **`v` for local variables** - `vWindow`, `vResult`.
- **`in` for function parameters** - `inTitle`, `inWidth`. Note Compose
`@Composable` parameters are the exception: they stay plain, because the name
is part of the call-site API (`title = `, `onCloseRequest = `).
- **`k` for private file-level constants** - `kMaxFrames`, `kIsWindows`.
- Public API surface is plain `camelCase` - prefixes are for implementation
detail only, and nothing prefixed should ever reach the klib dump.
VENDORED code under `src/vendor/` follows UPSTREAM's naming, untouched - it is
copied byte-for-byte, so never re-prefix anything in it.
Section headers inside a file, when useful:
@@ -725,6 +784,11 @@ Verified in-tree (api-exposed by `:ui` unless noted):
- `androidx.navigationevent:navigationevent-compose` 1.1.2 - predictive-back
event plumbing (BackHandler, NavDisplay gestures).
- `androidx.collection:collection` - plain Maven dep, not a module.
- `androidx.window:window-core` **1.5.0** - WindowSizeClass / the adaptive
window model. Publishes mingwX64 + linux + macos under BOTH coordinate sets;
the port takes the google one like the rest of the androidx stack. This is
what makes the material3-adaptive vendoring a pure re-target: the dependency
everybody assumed was the blocker was never missing.
- `io.insert-koin:koin-core` **4.2.2** - the DI container itself publishes
mingwX64 + linux + macos. Only Koin's viewmodel / compose layers stop at
apple+android and are vendored (`koin/`).
@@ -752,7 +816,7 @@ Caveats that make these work here (all already wired - listed so nobody
- The google `LocalViewModelStoreOwner` / `LocalSavedStateRegistryOwner` /
`LocalLifecycleOwner` are PLAIN composition locals - the JB HostDefault
mechanism (`compositionLocalWithHostDefaultOf`) does not exist in google
artifacts. `WindowArchitectureOwner` (ComposeWindow.kt) provides all three
artifacts. `WindowArchitectureOwner` (WindowInstance.kt) provides all three
per window, mirrors upstream desktop's DefaultArchitectureComponentsOwner,
calls `enableSavedStateHandles()` at construction, and follows SDL focus /
visibility (focused → RESUMED, unfocused → STARTED, minimised → CREATED).
+96 -14
View File
@@ -391,21 +391,21 @@ re-vendoring.** The debt is **completing native-actual stubs**.
### P0 - blocks fidelity/correctness
- [~] **P0** Text context menu (right-click copy/paste/select-all) - **RECONCILED: already
works, no reimplementation.** Static trace confirmed the menu is wired end-to-end
through the vendored LEGACY path: text-level `ContextMenuArea` (`ContextMenu.native.kt`)
works, no reimplementation.** Static trace confirmed the menu is wired end-to-end
through the vendored LEGACY path: text-level `ContextMenuArea` (`ContextMenu.native.kt`)
→ `CommonContextMenuArea` (`vendor/common/.../text/CommonContextMenuArea.kt`) → (native
`ComposeFoundationFlags.isNewContextMenuEnabled = false`, so the legacy branch) →
`contextmenu.ContextMenuArea` → `contextMenuGestures`/`onRightClickDown`
(`isSecondaryPressed`) → `ContextMenuPopup` → `Popup.native.kt` (hosted by
`LocalPopupHost` at the app root). Items are real: `TextFieldSelectionManager` /
`SelectionManager.contextMenuBuilder` emit Cut/Copy/Paste/SelectAll with real actions.
SDL right-click → `PointerButton.Secondary` (`SDL3EventMapper.kt`) →
SDL right-click → `PointerButton.Secondary` (`AppEvent.kt`) →
`isSecondaryPressed` (`PointerEventBridge`). The three "NOP" seams belong to the
DISABLED *new* context-menu API and are unreachable - **DONE:** their misleading
DISABLED *new* context-menu API and are unreachable - **DONE:** their misleading
`TODO(CMP-7819)` comments corrected to say so, in all three files.
**REMAINING:** an interactive right-click smoke check (no headless driver here - fold
into manual/WIN-SMOKE). Known limitation, not a breakage: Paste enablement is
plain-text-only (`ClipboardPasteState.hasClip = hasText`, see P2 below).
**REMAINING:** an interactive right-click smoke check (no headless driver here - fold
into manual/WIN-SMOKE). Known limitation, not a breakage: Paste enablement is
plain-text-only (`ClipboardPasteState.hasClip = hasText`, see P2 below).
- [ ] **P1** DatePicker/TimePicker localization - VERIFIED current state:
`material3/.../internal/PlatformDateFormat.native.kt` is a real kotlinx-datetime
formatter that DOES honor CLDR patterns/skeletons (renders "Jul 29, 2026" /
@@ -417,14 +417,14 @@ re-vendoring.** The debt is **completing native-actual stubs**.
(author):** bundle a CLDR subset or K/N i18n lib for localized names + locale-aware
first-day/24h; also unblocks `CalendarLocale.native.kt:20` (fixed `"en"`). Ships
readable English dates today - polish, not a P0 gate.
- [x] **P0** Float pointer coordinates - `SDL3EventMapper.kt` `.toInt()`-truncated SDL's
Float `mb.x`/`mb.y`/`mm.x`/`mm.y` and the wheel `mouse_x/y` before the DPR multiply in
- [x] **P0** Float pointer coordinates - `AppEvent.kt` `.toInt()`-truncated SDL's
Float `mb.x`/`mb.y`/`mm.x`/`mm.y` and the wheel `mouse_x/y` before the DPR multiply in
`ComposeWindow.kt`. On 2× displays a click at logical 100.9 → physical 200 not ~201,
quantizing the caret to 2px steps near glyph edges. **DONE:** widened
quantizing the caret to 2px steps near glyph edges. **DONE:** widened
`LegacyPointerEvent.x/y` and `AppEvent.MouseWheel.x/y` to `Float`, dropped the
`.toInt()` at the three mapper construction sites, and removed the now-redundant
`.toFloat()` at the two `ComposeWindow` read sites. Consumers fully contained (verified
by grep). Builds + runs clean on macosArm64.
by grep). Builds + runs clean on macosArm64.
### P1 - quality / parity
@@ -521,7 +521,7 @@ re-vendoring.** The debt is **completing native-actual stubs**.
Static lib built from source per host (`scripts/build-sdl/build-all.py`, ref
`release-3.4.12`); linked into the exe via `sdl3.def` (`staticLibraries=libSDL3.a`). Today
only tests/examples + Windows D3D12/GPU are off (`build-all.py:311-319`). `SDL_Init` uses
**`SDL_INIT_VIDEO` only** (`SDL3Backend.kt:49`).
**`SDL_INIT_VIDEO` only** (`Sdl3Backend.kt:49`).
**USED (keep ON):** Video/window, Events, Clipboard, Dialog (file open/save - no cheaper
substitute; Linux uses portal/zenity, keep deps), OpenGL + Metal contexts, Render (2D CPU-
@@ -554,7 +554,7 @@ joystick.
disabled SDL subsystems.)
- [ ] **P2** (higher-risk, flag-don't-apply) Render-driver pruning to software-only. Only
the CPU-raster fallback uses `SDL_Render`; GL/Metal go direct. But
`SDL_CreateRenderer(window, null)` (`SDL3Backend.kt:117`) lets SDL pick the first
`SDL_CreateRenderer(window, null)` (`Sdl3Backend.kt:117`) lets SDL pick the first
driver - with only software present it picks software (fine) but couples the fallback
to that assumption. **Conservative: leave render drivers alone.**
@@ -727,6 +727,30 @@ A read-only feasibility map (no code changed) found two layers:
proves infeasible, fall back to keeping the seam but at least converting the hand files to tracked
Rule-3 vendors (like `FontRasterizationSettings.native.kt` now has a `VENDOR-BASE` line).
### 7c. `demo --multiwintest` segfault on mingwX64 - ✅ FIXED
The multi-window probe exited 139 with no output, so it had silently been
asserting nothing. Root cause: an OpenGL context is per-WINDOW but "current" is
per-THREAD, and `SDL_GL_MakeCurrent` ran only once at creation
(`Sdl3Backend.init`) with nothing re-binding it per frame. With two windows open,
whichever initialised last owned the thread - so window A issued its draw and
swap against window B's context (silent corruption while both lived), and
destroying B left the current context dangling, faulting in `present()`.
Fixed in `SkiaGLBridge` by binding the window's own context at every entry point
that touches GL: `init`, `ensureSize` (before its unchanged-size early return -
this is the per-frame bind that covers the draw phase), `present`, and `destroy`
(so a window frees its GPU objects against its own context). Purely additive, 29
lines.
Single-window rendering is bit-identical before and after (same mean RGB
[46.6 44.4 51.5] and 167 distinct colours on the Buttons screenshot), which is
expected: SDL short-circuits a redundant bind. `--multiwintest` now PASSes.
NOTE the multi-window path was never correct, not just at teardown - two live
windows were sharing one context. Anything that looked like flicker or
cross-window corruption before this is explained by it.
## Accepted 1.0.0 gaps (documented, not fixed)
Drag-OUT of window (SDL platform limit; drop-IN works), full accessibility pipeline (out of
@@ -735,6 +759,52 @@ hand-rolled text engine as an architectural deviation (§2 P1), per-focus
`SDL_StartTextInput/StopTextInput`, `loadImageBitmap`/`loadSvgPainter` (JVM `InputStream`
signatures, N/A on K/N).
## 8. material3-adaptive ported to desktop native (2026-09-22) - ✅ LANDED
Asked for in [issue #4](https://github.com/bitsycore/compose-desktop-native/issues/4):
`org.jetbrains.compose.material3.adaptive:*` 1.3.0-rc01 publishes `ios_arm64
ios_simulator_arm64 macos_arm64` and nothing else - no linux, no mingw. (It DOES
carry macosArm64, which answers the reporter's open question.)
**The port needed zero source edits.** Two things made it a pure re-target:
1. Every platform actual the native legs want already exists upstream -
`adaptive/nonAndroidMain` (`calculatePosture`), `adaptive-layout/skikoMain`
(the ~75 l10n tables + `Strings` + the drag handle) and
`adaptive-layout/nativeMain` (`identityHashCode`, `isGraphicsLayerElement`).
2. `androidx.window:window-core` - the dependency `adaptive` api-exposes, and the
obvious suspect for why the artifacts stop at apple - has published
`linux_x64 linux_arm64 mingw_x64 macos_arm64` klibs all along, under BOTH the
google and jetbrains coordinates.
So the artifacts were missing purely because upstream never enabled the targets.
**Landed** as four modules under `compose/material3/adaptive/`
(`com.bitsycore.compose.material3.adaptive:*`), 125 files vendored verbatim off the
existing `COMPOSE_CORE_REF` pin - no new SET_REPO. Upstream's `skikoMain` lands in
`src/vendor/native/` beside upstream's own `nativeMain`; they actualise disjoint
expects so they share one source set. Both bridges substitute all four. `demo --screen=Adaptive` renders a
`ListDetailPaneScaffold`, and `demo --adaptivetest` drives one window
wide -> narrow -> wide and asserts the scaffold folds and unfolds, which is what
actually proves the resize chain end to end (SDL resize -> the owner's
snapshot-backed containerSize -> recomposition -> BoxWithConstraints re-measure ->
a new PaneScaffoldDirective -> a new scaffold value).
The demo screen computes its directive from the width IT gets rather than taking
the `rememberListDetailPaneScaffoldNavigator` default, which is the window size
class: the shell spends 190dp on the sidebar plus 48dp of padding, so defaulting
folds ~240dp too late and leaves two panes in a region already too cramped for
them. Pane-hosting apps that are not full-window should do the same.
One wrinkle worth remembering, and a textbook case of the granular-metadata trap
already documented in CLAUDE.md: because `adaptive` is SUBSTITUTED on native
configs, KGP drops its transitives from the commonMain metadata classpath, so the
shared demo screen could not see `WindowSizeClass` until `window-core` was declared
DIRECTLY in `demo`'s commonMain.
Not yet exercised: `adaptive-navigation3` builds and dumps API but has no demo
screen - the nav3 SceneStrategy path is unproven at runtime.
## 7. Post-1.12.0 open items (opened 2026-09-12)
### 7a. JVM parity reference used a DIFFERENT default font than native - ✅ FIXED
@@ -813,6 +883,18 @@ Two residuals, neither a text-metrics bug:
(0.29% at +0px vs 2.13% at +1px), where before +2px halved the error. The residual 0.29%
is rasterizer AA.
- **Icon-font glyphs rendered LOW - ✅ FIXED.** Material Symbols sat 2-4px lower on native
than on jvm, the offset scaling with icon size (~13% of it). Cause: the JVM actual centres
the glyph explicitly (`y = height/2 - (ascent+descent)/2`), while `IconFontIcon` placed
`IconText` in a Box at the DEFAULT TopStart alignment - and the glyph's natural text box is
taller than the nominal icon size. Fixed by centring, with
`wrapContentSize(unbounded = true)` so the text measures at its natural size first:
`Modifier.size()` imposes FIXED constraints, so without it nothing overflows and
contentAlignment is a no-op (a first attempt without it changed literally nothing).
gear/download/lock are now pixel-exact; the eye is 1px off from half-pixel rounding (odd
glyph height centred in an even box; jvm draws at a float y, native placement is integer).
Whole-window >32/255 drift 0.29% -> 0.17%.
- **Missing glyph in the session header.** The leading icon next to "Untitled session"
renders as a `.notdef` TOFU on jvm and as NOTHING on native - so the glyph is absent from
the subsetted Material Symbols font on both, and the two stacks just disagree on how to
@@ -850,7 +932,7 @@ WRONG - both coordinate sets publish mingwX64 + linux + macos, and the port alre
CLAUDE.md, exercised by `demo --backtest`). Nothing to vendor there.
**Landed** as `koin/` (4 modules, `com.bitsycore.koin`), `coil/` (7 modules,
`com.bitsycore.coil3`) and `pulse/` (4 modules, `com.bitsycore.pulse`) - 202 files vendored
`com.bitsycore.coil3`) and `pulse/` (4 modules, `com.bitsycore.compose.desktop.native.pulse`) - 202 files vendored
verbatim across three new pinned repos (`KOIN_REF=4.2.2`, `COIL_REF=3.6.2`,
`PULSE_REF=0.3.7`), ZERO local source edits. All build for mingwX64 + linuxX64/arm64, are
BCV-dumped, publish, and carry bridge substitution rules so consumers keep declaring the
+58 -25
View File
@@ -71,6 +71,23 @@ Each window gets its own `Lifecycle`, `ViewModelStore`, and `SavedStateRegistry`
owners, driven by real SDL focus and visibility events, so ViewModels and
saved state behave as they do on Android.
`Window()` takes the same attributes as Compose Desktop's - `undecorated`,
`transparent`, `resizable`, `enabled`, `focusable`, `alwaysOnTop`,
`onPreviewKeyEvent` and `onKeyEvent` - and re-applies them when they change.
(`transparent` is the one exception: SDL needs it at window-creation time, so it
is fixed for the window's life.)
When the Compose-level API doesn't reach far enough, drop to SDL directly:
```kotlin
val handles = window.rawSdlHandles() // SDL_Window* / SDL_Renderer* / GL / Metal
handles.window?.let { sdl3.SDL_FlashWindow(it.reinterpret(), SDL_FLASH_BRIEFLY) }
```
The pointers are a snapshot valid only while the window lives, and which ones
are non-null depends on the resolved renderer (`glContext` for Skia OpenGL,
`metalView` for Metal, `renderer` for CPU raster).
### Building in this repo
```kotlin
@@ -101,11 +118,21 @@ plugins {
commonMain.dependencies {
// The plugin exposes the exact Compose versions the port tracks, so you
// never hand-match them (material3 is versioned separately upstream).
// never hand-match them (material3 and material3-adaptive each ride their
// own release train upstream).
implementation("org.jetbrains.compose.runtime:runtime:${composeDesktopNative.composeRuntime}")
implementation("org.jetbrains.compose.ui:ui:${composeDesktopNative.compose}")
implementation("org.jetbrains.compose.foundation:foundation:${composeDesktopNative.compose}")
implementation("org.jetbrains.compose.material3:material3:${composeDesktopNative.composeMaterial3}")
// material3-adaptive, which upstream publishes for apple targets only.
// Note adaptive api-exposes androidx.window:window-core - declare it
// DIRECTLY if your commonMain touches WindowSizeClass, since substituted
// modules hide their transitives from the common metadata classpath.
val vAdaptive = composeDesktopNative.composeMaterial3Adaptive
implementation("org.jetbrains.compose.material3.adaptive:adaptive:$vAdaptive")
implementation("org.jetbrains.compose.material3.adaptive:adaptive-layout:$vAdaptive")
implementation("androidx.window:window-core:1.5.0")
}
```
@@ -168,7 +195,8 @@ targets, so you keep writing against the official coordinates:
| `components-resources`, `navigation3-ui` | no mingwX64 / linux klibs | `com.bitsycore.compose.components`, `com.bitsycore.navigation3` |
| **Koin** viewmodel + compose modules | apple + android only upstream (`koin-core` itself is fine) | `com.bitsycore.koin` |
| **Coil 3** (whole stack) | no mingwX64 anywhere; no desktop native at all for its compose layer | `com.bitsycore.coil3` |
| **Pulse MVI** | no desktop-native artifact upstream | `com.bitsycore.pulse` |
| **Pulse MVI** | no desktop-native artifact upstream | `com.bitsycore.compose.desktop.native.pulse` |
| **material3-adaptive** (all four) | ios + macosArm64 only upstream - no linux, no mingw | `com.bitsycore.compose.material3.adaptive` |
The bridge plugin substitutes each of these on native desktop configurations, so
app code declares `io.insert-koin:koin-compose`, `io.coil-kt.coil3:coil-compose`
@@ -196,8 +224,8 @@ one (`savedstate`, below).
| | Version | Notes |
|---|---|---|
| Kotlin | **2.4.20** | Newer than the 2.3.20 CMP 1.12.0 is built with. Kotlin/Native consumes older klibs fine, and the whole port builds clean on 2.4.20 with no klib API diff, so there is no reason to hold back. |
| Compose Multiplatform | **1.12.0** | The vendored sources are the `v1.12.0` tag of both upstream repos, and the JVM parity leg forces the same version - no dev-build skew. |
| Kotlin | **2.4.20** | Newer than the 2.3.20 CMP 1.12.1 is built with. Kotlin/Native consumes older klibs fine, and the whole port builds clean on 2.4.20 with no klib API diff, so there is no reason to hold back. |
| Compose Multiplatform | **1.12.1** | The vendored sources are the `v1.12.1` tag of both upstream repos, and the JVM parity leg forces the same version - no dev-build skew. skiko is unchanged at 0.150.1, so the mingwX64 fork still matches its Skia base. |
| SDL3 | **release-3.4.16** | Built from source as a static lib per host (`scripts/build-sdl/build-all.py`), linked into the executable. |
| Skiko | **0.150.1** | macOS / Linux use the official `org.jetbrains.skiko`. |
| Skiko (Windows fork) | **0.150.1-mingw.2** | `com.bitsycore.skiko:skiko` - Skiko + Skia in `skiko-windows-x64.dll`. Same Skia base as the official build. Public repo, no auth. |
@@ -210,19 +238,31 @@ the bridge plugin and you keep declaring the **official** coordinate.
| Library | Official coordinate | Port coordinate |
|---|---|---|
| Runtime | `org.jetbrains.compose.runtime:runtime*:1.12.0` | *not forked - the official klibs serve every target* |
| UI | `org.jetbrains.compose.ui:ui*:1.12.0` | `com.bitsycore.compose.ui:ui*` |
| Foundation | `org.jetbrains.compose.foundation:foundation*:1.12.0` | `com.bitsycore.compose.foundation:foundation*` |
| Animation | `org.jetbrains.compose.animation:animation*:1.12.0` | `com.bitsycore.compose.animation:animation*` |
| Material Ripple | `org.jetbrains.compose.material:material-ripple:1.12.0` | `com.bitsycore.compose.material:material-ripple` |
| Runtime | `org.jetbrains.compose.runtime:runtime*:1.12.1` | *not forked - the official klibs serve every target* |
| UI | `org.jetbrains.compose.ui:ui*:1.12.1` | `com.bitsycore.compose.ui:ui*` |
| Foundation | `org.jetbrains.compose.foundation:foundation*:1.12.1` | `com.bitsycore.compose.foundation:foundation*` |
| Animation | `org.jetbrains.compose.animation:animation*:1.12.1` | `com.bitsycore.compose.animation:animation*` |
| Material Ripple | `org.jetbrains.compose.material:material-ripple:1.12.1` | `com.bitsycore.compose.material:material-ripple` |
| Material3 | `org.jetbrains.compose.material3:material3:1.12.0-alpha03` | `com.bitsycore.compose.material3:material3` |
| Resources | `org.jetbrains.compose.components:components-resources:1.12.0` | `com.bitsycore.compose.components:components-resources` |
| Material3 Adaptive | `org.jetbrains.compose.material3.adaptive:adaptive*:1.3.0-rc01` | `com.bitsycore.compose.material3.adaptive:adaptive*` |
| Resources | `org.jetbrains.compose.components:components-resources:1.12.1` | `com.bitsycore.compose.components:components-resources` |
| Navigation3 UI | `org.jetbrains.androidx.navigation3:navigation3-ui` | `com.bitsycore.navigation3:navigation3-ui` |
| Window shell | *no upstream equivalent* | `com.bitsycore.compose:desktop-native-window` |
| SDL layer | *no upstream equivalent* | `com.bitsycore.compose.sdl:sdl-core` |
Material3 rides its own release train upstream: `1.12.0-alpha03` **is** the
version Compose Multiplatform 1.12.0 ships (Jetpack Material3 1.5.0-alpha22).
version Compose Multiplatform 1.12.1 ships (Jetpack Material3 1.5.0-alpha22).
material3-adaptive rides a third train again.
**material3-adaptive** (`adaptive`, `adaptive-layout`, `adaptive-navigation`,
`adaptive-navigation3`) is published upstream for ios and macosArm64 only - no
linux, no mingw. This port ships all four for every desktop-native target. It
needed no source changes at all: every platform actual the native legs want is
already in upstream's `nonAndroidMain` / `skikoMain` / `nativeMain`, and
`androidx.window:window-core` - the one dependency involved - has published
linux, mingw and macos klibs the whole time. The artifacts were missing only
because upstream never turned the targets on. `demo --screen=Adaptive` renders
a live `ListDetailPaneScaffold`.
### Ecosystem libraries
@@ -233,8 +273,8 @@ substitutes them on native desktop targets only.
|---|---|---|---|
| Koin | `io.insert-koin:koin-compose*:4.2.2` | `com.bitsycore.koin:*` | apple + android only upstream |
| Koin core | `io.insert-koin:koin-core:4.2.2` | *not forked* | already ships mingwX64 + linux |
| Coil 3 | `io.coil-kt.coil3:coil*:3.6.2` | `com.bitsycore.coil3:*` | no mingwX64 anywhere; no desktop native at all for the compose layer |
| Pulse MVI | `com.bitsycore.lib:pulse*:0.3.7` | `com.bitsycore.pulse:*` | no desktop-native artifact upstream |
| Coil 3 | `io.coil-kt.coil3:coil*:3.6.3` | `com.bitsycore.coil3:*` | no mingwX64 anywhere; no desktop native at all for the compose layer |
| Pulse MVI | `com.bitsycore.lib:pulse*:0.3.7` | `com.bitsycore.compose.desktop.native.pulse:*` | no desktop-native artifact upstream - and Pulse is itself a `com.bitsycore` library, so the republish is namespaced under this project |
Koin's `koin-compose-viewmodel-navigation` is deliberately **not** provided: it
needs Navigation 2's `navigation-compose`, which has no mingwX64 or linux klibs
@@ -251,22 +291,11 @@ mirrors or you will have every class twice.
|---|---|
| Lifecycle / ViewModel | `androidx.lifecycle:lifecycle-*:2.11.0` |
| SavedState | `androidx.savedstate:savedstate*:1.5.0` (CMP builds against 1.4.0; lifecycle 2.11.0 requires it only as a *minimum*, so 1.5.0 resolves cleanly and gives consumers the newer API) |
| Navigation3 runtime | `androidx.navigation3:navigation3-runtime:1.2.0-alpha04` |
| Navigation3 runtime | `androidx.navigation3:navigation3-runtime:1.1.7` |
| Navigation Event | `androidx.navigationevent:navigationevent-compose:1.1.2` |
| Collection | `androidx.collection:collection:1.5.0` |
| Graphics Shapes | `androidx.graphics:graphics-shapes:1.1.0` |
### Infrastructure
| Library | Version |
|---|---|
| kotlinx-coroutines | 1.11.0 |
| kotlinx-serialization | 1.11.0 |
| kotlinx-datetime | 0.8.0 |
| kotlinx-io-okio | 0.9.1 |
| Okio | 3.18.2 |
| Ktor | 3.5.2 |
## Building
Build the native libraries once per machine, then build any app target:
@@ -281,6 +310,10 @@ command line tools; Linux needs gcc/g++ and the X11 / Wayland / audio dev
headers; Windows needs a mingw-w64 g++ on PATH. See [TOOLING.md](TOOLING.md) for
the full build and verification workflow.
## Support
[Bitsycore's Discord](https://discord.gg/kGnraVAtDR)
## Documentation
- [TOOLING.md](TOOLING.md): building the native libs, vendoring, and verification.
+2 -1
View File
@@ -154,6 +154,7 @@ demo.kexe --backtest # predictive-back / BackHandler
demo.kexe --clicktest # pointer -> upstream clickable
demo.kexe --scrolltest # wheel -> scrollable
demo.kexe --multiwintest # multi-window lifecycle
demo.kexe --adaptivetest # material3-adaptive folds/unfolds on a LIVE window resize
demo.kexe --soaktest # memory soak (CDN_SOAK_SCREEN, CDN_SOAK_STATIC, CDN_SOAK_CYCLES)
```
@@ -194,7 +195,7 @@ single file to edit; know which axis you are changing.
| Project release version | The git tag `vX.Y.Z`. `PUBLISH_VERSION` (from the tag) feeds `vPublishVersion` in `build.gradle.kts`, which strips the leading `v`. Groups mirror upstream per area (`com.bitsycore.compose.<area>:<module>`, e.g. `com.bitsycore.compose.ui:ui`); project-only modules use `com.bitsycore.compose.sdl` and `:desktop-native-window` is `com.bitsycore.compose` - see `groupFor()` in the root build. | Set by the tag, not edited by hand. A non-publish build is `0.0.0-SNAPSHOT`. |
| Vendored Compose (native side) | `COMPOSE_CORE_REF` and `COMPOSE_REF` in `scripts/compose-fork/compose.properties`, plus `compose` in `gradle/libs.versions.toml`. | Pin to a durable tag (not a `+dev` commit upstream may GC). Re-sync after changing. |
| Vendored ecosystem libs | `KOIN_REF`, `COIL_REF`, `PULSE_REF` in the same file, plus `koin` / `coil` in `gradle/libs.versions.toml`. | Same rule: durable tags. Each repo gets its own sibling sparse clone (`../cmp-ref-koin`, `../cmp-ref-coil`, `../cmp-ref-pulse-mvi`). Re-sync, then `apiDump` - these modules are BCV-validated like the rest. |
| JVM parity forcing | `compose` / `composeMaterial3` / `composeRuntime` in `gradle/libs.versions.toml` (read by `demo`, `apidemo`, `material-symbols` and the root build's forcing map). | Must be a version PUBLISHED to Maven Central. It may lag the vendored native ref (a documented skew) until the matching version is published; at v1.12.0 they are in lockstep. |
| JVM parity forcing | `compose` / `composeMaterial3` / `composeRuntime` in `gradle/libs.versions.toml` (read by `demo`, `apidemo`, `material-symbols` and the root build's forcing map). | Must be a version PUBLISHED to Maven Central. It may lag the vendored native ref (a documented skew) until the matching version is published; at v1.12.1 they are in lockstep. |
| Skiko | `skiko` (official) and `skikoMingw` (the fork) in `gradle/libs.versions.toml`; keep the fork's `-mingw.N` base equal to `skiko`. | macOS/Linux use official Skiko (`org.jetbrains.skiko`); mingwX64 uses the bitsycore fork (`com.bitsycore.skiko:skiko:0.150.1-mingw.2` from the public maven.bitsycore.com), published out of band by the fork repo's own workflow. Must expose the `org.jetbrains.skiko.node` `RenderNode` / `GraphicsContext` API the vendored compose-core uses (the fork keeps upstream's `org.jetbrains.skiko.*` package names; only the Maven coord is rebranded). Verify with a throwaway `skikoRendererMain` compile if unsure. |
| SDL3 | `scripts/build-sdl/build-sdl.properties`. | Rebuild `libs/` with `build-all.py` after any change. |
| Bridge substituted version | Defaults to the bridge plugin's own published version; consumers override with the `composeDesktopNative.version` Gradle property. | The plugin publishes with the release tag, so a consumer on the matching plugin version resolves the right klibs automatically. |
+2 -2
View File
@@ -56,11 +56,11 @@ internal class PackState(
// A linked copy mirrors its source's requests read-only; non-linked packs own
// their requests. `requests` resolves to whichever applies.
var linkedSource by mutableStateOf<PackState?>(null)
private val fOwnRequests = mutableStateListOf<ReqState>().apply {
private val mOwnRequests = mutableStateListOf<ReqState>().apply {
inPack.requests.forEach { add(ReqState(it)) } // packs (and the loose root) may be empty
}
val requests: androidx.compose.runtime.snapshots.SnapshotStateList<ReqState>
get() = linkedSource?.requests ?: fOwnRequests
get() = linkedSource?.requests ?: mOwnRequests
val isLinked: Boolean get() = linkedSource != null
var parent: PackState? =
null // enclosing pack (null = top-level / root)
+2 -2
View File
@@ -11,7 +11,7 @@ import kotlinx.serialization.json.JsonElement
// respects the editor's "tab size" (TextLayoutConfig.tabWidth), exactly like
// typed tabs. Lenient so slightly-off JSON (single quotes, trailing commas)
// still reformats instead of failing.
private val fPrettyJson = Json {
private val mPrettyJson = Json {
prettyPrint = true
prettyPrintIndent = "\t"
isLenient = true
@@ -23,7 +23,7 @@ private val fPrettyJson = Json {
fun formatBody(inText: String, inFormat: BodyFormat): String =
when (inFormat) {
BodyFormat.JSON -> runCatching {
fPrettyJson.encodeToString(JsonElement.serializer(), fPrettyJson.parseToJsonElement(inText))
mPrettyJson.encodeToString(JsonElement.serializer(), mPrettyJson.parseToJsonElement(inText))
}.getOrDefault(inText)
BodyFormat.XML -> runCatching { formatXml(inText) }.getOrDefault(inText)
+3 -3
View File
@@ -22,7 +22,7 @@ macOS/Linux). Same TLS stack as the client-cert path in CurlMtls.kt.
run() is a suspend fun - call it off the UI dispatcher. */
class HttpRunner {
private val fClient = createApiHttpClient()
private val mClient = createApiHttpClient()
init {
// Clear any temporary client certs a prior crash left in the Windows
@@ -36,7 +36,7 @@ class HttpRunner {
if (inReq.hasClientCert) return curlSendWithClientCert(inReq)
val vMark = TimeSource.Monotonic.markNow()
return try {
val vResp: HttpResponse = fClient.request(inReq.url.trim()) {
val vResp: HttpResponse = mClient.request(inReq.url.trim()) {
method = HttpMethod.parse(inReq.method.name)
inReq.params
.filter { it.enabled && it.key.isNotBlank() }
@@ -113,7 +113,7 @@ class HttpRunner {
}
}
fun close() = fClient.close()
fun close() = mClient.close()
}
/** Heuristic for a non-text body: textual content types (text, json, xml, html,
+6 -6
View File
@@ -9,7 +9,7 @@ import okio.Path.Companion.toPath
// MARK: Pack persistence (okio + kotlinx.serialization)
// ==================
private val fJson = Json {
private val mJson = Json {
prettyPrint = true
prettyPrintIndent = " "
ignoreUnknownKeys = true
@@ -20,7 +20,7 @@ private val fJson = Json {
error message. */
fun exportPack(inPack: Pack, inPath: String): String? = try {
systemFileSystem.write(inPath.trim().toPath()) {
writeUtf8(fJson.encodeToString(inPack))
writeUtf8(mJson.encodeToString(inPack))
}
null
} catch (e: Throwable) {
@@ -30,7 +30,7 @@ fun exportPack(inPack: Pack, inPath: String): String? = try {
/** Read a pack back from inPath. */
fun importPack(inPath: String): Result<Pack> = try {
val vText = systemFileSystem.read(inPath.trim().toPath()) { readUtf8() }
Result.success(fJson.decodeFromString<Pack>(vText))
Result.success(mJson.decodeFromString<Pack>(vText))
} catch (e: Throwable) {
Result.failure(e)
}
@@ -38,7 +38,7 @@ fun importPack(inPath: String): Result<Pack> = try {
/** Write a whole self-contained session to inPath. Returns null on success. */
fun exportSession(inSession: Session, inPath: String): String? = try {
systemFileSystem.write(inPath.trim().toPath()) {
writeUtf8(fJson.encodeToString(inSession))
writeUtf8(mJson.encodeToString(inSession))
}
null
} catch (e: Throwable) {
@@ -48,7 +48,7 @@ fun exportSession(inSession: Session, inPath: String): String? = try {
/** Read a session back from inPath. */
fun importSession(inPath: String): Result<Session> = try {
val vText = systemFileSystem.read(inPath.trim().toPath()) { readUtf8() }
Result.success(fJson.decodeFromString<Session>(vText))
Result.success(mJson.decodeFromString<Session>(vText))
} catch (e: Throwable) {
Result.failure(e)
}
@@ -85,7 +85,7 @@ fun writeBytesFile(inPath: String, inBytes: ByteArray): String? = try {
/** Re-indent a JSON response for display; returns the input unchanged if it
isn't valid JSON (so plain-text / HTML responses still show). */
fun prettyJsonOrRaw(inText: String): String = try {
fJson.encodeToString(JsonElement.serializer(), fJson.parseToJsonElement(inText))
mJson.encodeToString(JsonElement.serializer(), mJson.parseToJsonElement(inText))
} catch (_: Throwable) {
inText
}
+3 -3
View File
@@ -64,7 +64,7 @@ data class Session(
// MARK: Load / save (app-data dir via okio)
// ==================
private val fStateJson = Json {
private val mStateJson = Json {
ignoreUnknownKeys = true
encodeDefaults = true
prettyPrint = true
@@ -88,7 +88,7 @@ fun loadAppState(): AppState {
val vPath = stateFilePath() ?: return AppState()
return try {
val vText = systemFileSystem.read(vPath.toPath()) { readUtf8() }
fStateJson.decodeFromString<AppState>(vText)
mStateJson.decodeFromString<AppState>(vText)
} catch (_: Throwable) {
AppState()
}
@@ -99,7 +99,7 @@ the session, not the user's exported .json packs). */
fun saveAppState(inState: AppState) {
val vPath = stateFilePath() ?: return
try {
systemFileSystem.write(vPath.toPath()) { writeUtf8(fStateJson.encodeToString(inState)) }
systemFileSystem.write(vPath.toPath()) { writeUtf8(mStateJson.encodeToString(inState)) }
} catch (_: Throwable) {
// ignore - nothing the user can act on, and packs can still be exported
}
@@ -157,9 +157,9 @@ actual fun wrappedRowCount(inText: String, inFontPx: Int, inMaxWidthPx: Int, inF
/** The jvm parity app only stores the preference - upstream's text pipeline has
no global tab-width knob. Backed by state so menu checkmarks recompose. */
private var fTabWidth by mutableStateOf(4)
private var mTabWidth by mutableStateOf(4)
actual var editorTabWidth: Int
get() = fTabWidth
get() = mTabWidth
set(value) {
fTabWidth = value
mTabWidth = value
}
+18 -5
View File
@@ -46,6 +46,10 @@ val kAreaGroups = mapOf(
":compose:animation:animation-core" to "com.bitsycore.compose.animation",
":compose:animation:animation-graphics" to "com.bitsycore.compose.animation",
":compose:material3:material3" to "com.bitsycore.compose.material3",
":compose:material3:adaptive:adaptive" to "com.bitsycore.compose.material3.adaptive",
":compose:material3:adaptive:adaptive-layout" to "com.bitsycore.compose.material3.adaptive",
":compose:material3:adaptive:adaptive-navigation" to "com.bitsycore.compose.material3.adaptive",
":compose:material3:adaptive:adaptive-navigation3" to "com.bitsycore.compose.material3.adaptive",
":compose:material:material-ripple" to "com.bitsycore.compose.material",
":components:resources:components-resources" to "com.bitsycore.compose.components",
":navigation3:navigation3-ui" to "com.bitsycore.navigation3",
@@ -60,10 +64,10 @@ val kAreaGroups = mapOf(
":coil:coil-svg" to "com.bitsycore.coil3",
":coil:coil-network-core" to "com.bitsycore.coil3",
":coil:coil-network-ktor3" to "com.bitsycore.coil3",
":pulse:pulse" to "com.bitsycore.pulse",
":pulse:pulse-viewmodel" to "com.bitsycore.pulse",
":pulse:pulse-savedstate" to "com.bitsycore.pulse",
":pulse:pulse-compose" to "com.bitsycore.pulse",
":pulse:pulse" to "com.bitsycore.compose.desktop.native.pulse",
":pulse:pulse-viewmodel" to "com.bitsycore.compose.desktop.native.pulse",
":pulse:pulse-savedstate" to "com.bitsycore.compose.desktop.native.pulse",
":pulse:pulse-compose" to "com.bitsycore.compose.desktop.native.pulse",
":compose:desktop:native:desktop-native-window" to "com.bitsycore.compose",
)
fun groupFor(path: String): String = kAreaGroups[path] ?: kDefaultGroup
@@ -99,6 +103,7 @@ val kPublishedLibs = setOf(
":compose:animation:animation-graphics",
":compose:foundation:foundation", ":compose:foundation:foundation-layout",
":compose:material3:material3", ":compose:material:material-ripple",
":compose:material3:adaptive:adaptive", ":compose:material3:adaptive:adaptive-layout", ":compose:material3:adaptive:adaptive-navigation", ":compose:material3:adaptive:adaptive-navigation3",
":compose:desktop:native:desktop-native-window", ":utils:material-symbols",
":navigation3:navigation3-ui", ":components:resources:components-resources",
":koin:koin-core-viewmodel", ":koin:koin-compose",
@@ -222,6 +227,12 @@ allprojects {
substitute(module("org.jetbrains.compose.animation:animation")).using(project(":compose:animation:animation"))
substitute(module("org.jetbrains.compose.animation:animation-core")).using(project(":compose:animation:animation-core"))
substitute(module("org.jetbrains.compose.material3:material3")).using(project(":compose:material3:material3"))
// material3-adaptive: upstream publishes ios + macosArm64 only - no
// linux, no mingw (issues/4). Vendored as :compose:material3:adaptive:*.
substitute(module("org.jetbrains.compose.material3.adaptive:adaptive")).using(project(":compose:material3:adaptive:adaptive"))
substitute(module("org.jetbrains.compose.material3.adaptive:adaptive-layout")).using(project(":compose:material3:adaptive:adaptive-layout"))
substitute(module("org.jetbrains.compose.material3.adaptive:adaptive-navigation")).using(project(":compose:material3:adaptive:adaptive-navigation"))
substitute(module("org.jetbrains.compose.material3.adaptive:adaptive-navigation3")).using(project(":compose:material3:adaptive:adaptive-navigation3"))
// navigation3-ui: the JB Maven artifact has no K/N desktop
// klibs - the port vendors it as :navigation3-ui.
substitute(module("org.jetbrains.androidx.navigation3:navigation3-ui")).using(project(":navigation3:navigation3-ui"))
@@ -244,7 +255,9 @@ allprojects {
substitute(module("io.coil-kt.coil3:coil-network-core")).using(project(":coil:coil-network-core"))
substitute(module("io.coil-kt.coil3:coil-network-ktor3")).using(project(":coil:coil-network-ktor3"))
// Pulse MVI: upstream publishes com.bitsycore.lib:* with no desktop
// native targets. The port republishes under com.bitsycore.pulse so the
// native targets. Pulse is itself a com.bitsycore library, so the port's
// republish is namespaced under THIS project rather than a bare
// com.bitsycore.pulse, which would read like the upstream artifact. The
// two coordinates never collide, and substitutes the official one here.
substitute(module("com.bitsycore.lib:pulse")).using(project(":pulse:pulse"))
substitute(module("com.bitsycore.lib:pulse-viewmodel")).using(project(":pulse:pulse-viewmodel"))
@@ -11,12 +11,6 @@ plugins {
alias(libs.plugins.kotlin.plugin.compose)
}
repositories {
google()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
kotlin {
@@ -51,20 +51,20 @@ private class SvgPainter(private val bytes: ByteArray) : Painter() {
override val intrinsicSize: Size = svgIntrinsicSize(bytes) ?: Size.Unspecified
private var fAlpha: Float = 1f
private var fColorFilter: ColorFilter? = null
private var fCacheKey: Long = -1L
private var fCacheBitmap: ImageBitmap? = null
private var mAlpha: Float = 1f
private var mColorFilter: ColorFilter? = null
private var mCacheKey: Long = -1L
private var mCacheBitmap: ImageBitmap? = null
override fun applyAlpha(alpha: Float): Boolean { fAlpha = alpha; return true }
override fun applyColorFilter(colorFilter: ColorFilter?): Boolean { fColorFilter = colorFilter; return true }
override fun applyAlpha(alpha: Float): Boolean { mAlpha = alpha; return true }
override fun applyColorFilter(colorFilter: ColorFilter?): Boolean { mColorFilter = colorFilter; return true }
private fun rasterFor(inWidthPx: Int, inHeightPx: Int): ImageBitmap? {
val vKey = (inWidthPx.toLong() shl 32) or inHeightPx.toLong()
if (vKey == fCacheKey && fCacheBitmap != null) return fCacheBitmap
if (vKey == mCacheKey && mCacheBitmap != null) return mCacheBitmap
val vBitmap = decodeSvgAt(bytes, inWidthPx, inHeightPx) ?: return null
fCacheKey = vKey
fCacheBitmap = vBitmap
mCacheKey = vKey
mCacheBitmap = vBitmap
return vBitmap
}
@@ -79,8 +79,8 @@ private class SvgPainter(private val bytes: ByteArray) : Painter() {
srcSize = IntSize(vBitmap.width, vBitmap.height),
dstOffset = IntOffset.Zero,
dstSize = IntSize(vWpx, vHpx),
alpha = fAlpha,
colorFilter = fColorFilter,
alpha = mAlpha,
colorFilter = mColorFilter,
)
}
}
@@ -16,7 +16,7 @@ package org.jetbrains.compose.resources.vector.xmldom
CDATA skipping, and the five predefined entities plus numeric character
references. No DTD expansion, no processing-instruction handling beyond
skipping - malformed input throws MalformedXMLException like upstream. */
// VENDOR-BASE(COMPOSE_REF): components/resources/library/src/nativeMain/kotlin/org/jetbrains/compose/resources/vector/xmldom/DomXmlParser.kt @ v1.12.0
// VENDOR-BASE(COMPOSE_REF): components/resources/library/src/nativeMain/kotlin/org/jetbrains/compose/resources/vector/xmldom/DomXmlParser.kt @ v1.12.1
// (fresh REIMPL, not a copy-edit - the base ref marks the upstream API it tracks)
internal fun parse(xml: String): Element {
@@ -32,13 +32,13 @@ private class Attr(val prefix: String, val localName: String, val nsUri: String,
val qualifiedName: String get() = if (prefix.isEmpty()) localName else "$prefix:$localName"
}
private class NodeListImpl(private val fNodes: List<Node>) : NodeList {
override fun item(i: Int): Node = fNodes[i]
override val length: Int get() = fNodes.size
private class NodeListImpl(private val mNodes: List<Node>) : NodeList {
override fun item(i: Int): Node = mNodes[i]
override val length: Int get() = mNodes.size
}
private class TextNodeImpl(private val fText: String) : Node {
override val textContent: String? get() = fText
private class TextNodeImpl(private val mText: String) : Node {
override val textContent: String? get() = mText
override val nodeName: String get() = "#text"
override val localName: String get() = ""
override val childNodes: NodeList = NodeListImpl(emptyList())
@@ -47,16 +47,16 @@ private class TextNodeImpl(private val fText: String) : Node {
}
private class ElementImpl(
private val fPrefix: String,
private val mPrefix: String,
override val localName: String,
override val namespaceURI: String,
private val fAttrs: List<Attr>,
private val mAttrs: List<Attr>,
// Innermost-first chain of xmlns scopes: each is prefix -> uri ("" = default ns).
private val fNsScope: List<Map<String, String>>,
private val mNsScope: List<Map<String, String>>,
) : Element {
var children: List<Node> = emptyList()
override val nodeName: String get() = if (fPrefix.isEmpty()) localName else "$fPrefix:$localName"
override val nodeName: String get() = if (mPrefix.isEmpty()) localName else "$mPrefix:$localName"
override val childNodes: NodeList get() = NodeListImpl(children)
// Concatenated descendant text - what DOM's textContent does.
@@ -72,7 +72,7 @@ private class ElementImpl(
override fun lookupPrefix(namespaceURI: String): String {
if (namespaceURI.isEmpty()) return ""
for (vScope in fNsScope) {
for (vScope in mNsScope) {
for ((vPrefix, vUri) in vScope) {
if (vUri == namespaceURI && vPrefix.isNotEmpty()) return vPrefix
}
@@ -81,22 +81,22 @@ private class ElementImpl(
}
override fun getAttributeNS(nameSpaceURI: String, localName: String): String =
fAttrs.firstOrNull { it.nsUri == nameSpaceURI && it.localName == localName }?.value ?: ""
mAttrs.firstOrNull { it.nsUri == nameSpaceURI && it.localName == localName }?.value ?: ""
override fun getAttribute(name: String): String =
fAttrs.firstOrNull { it.qualifiedName == name }?.value ?: ""
mAttrs.firstOrNull { it.qualifiedName == name }?.value ?: ""
}
// ============
// The parser - one pass over the string with a cursor.
private class XmlDomParser(private val fXml: String) {
private var fPos = 0
private class XmlDomParser(private val mXml: String) {
private var mPos = 0
fun skipProlog() {
while (true) {
skipWhitespace()
when {
lookingAt("") -> fPos++
lookingAt("") -> mPos++
lookingAt("<?") -> skipUntil("?>")
lookingAt("<!--") -> skipUntil("-->")
lookingAt("<!DOCTYPE") -> skipDoctype()
@@ -112,7 +112,7 @@ private class XmlDomParser(private val fXml: String) {
if (!lookingAt("<") || lookingAt("</")) return null
expect("<")
val vQName = readName()
if (vQName.isEmpty()) throw MalformedXMLException("empty element name at $fPos")
if (vQName.isEmpty()) throw MalformedXMLException("empty element name at $mPos")
// Attributes (collect raw first - xmlns declarations shape the scope
// that THIS element's own prefix resolves against).
@@ -121,7 +121,7 @@ private class XmlDomParser(private val fXml: String) {
skipWhitespace()
if (lookingAt("/>") || lookingAt(">")) break
val vAttrName = readName()
if (vAttrName.isEmpty()) throw MalformedXMLException("bad attribute at $fPos")
if (vAttrName.isEmpty()) throw MalformedXMLException("bad attribute at $mPos")
skipWhitespace(); expect("="); skipWhitespace()
vRawAttrs.add(vAttrName to readQuotedValue())
}
@@ -160,7 +160,7 @@ private class XmlDomParser(private val fXml: String) {
val vElement = ElementImpl(vPrefix, vLocal, resolveNs(vPrefix), vAttrs, vScope)
if (lookingAt("/>")) {
fPos += 2
mPos += 2
return vElement
}
expect(">")
@@ -169,9 +169,9 @@ private class XmlDomParser(private val fXml: String) {
val vChildren = ArrayList<Node>()
while (true) {
when {
fPos >= fXml.length -> throw MalformedXMLException("unclosed <$vQName>")
mPos >= mXml.length -> throw MalformedXMLException("unclosed <$vQName>")
lookingAt("</") -> {
fPos += 2
mPos += 2
val vClose = readName()
skipWhitespace(); expect(">")
if (vClose != vQName) throw MalformedXMLException("mismatched </$vClose> for <$vQName>")
@@ -180,18 +180,18 @@ private class XmlDomParser(private val fXml: String) {
}
lookingAt("<!--") -> skipUntil("-->")
lookingAt("<![CDATA[") -> {
val vEnd = fXml.indexOf("]]>", fPos + 9)
val vEnd = mXml.indexOf("]]>", mPos + 9)
if (vEnd < 0) throw MalformedXMLException("unclosed CDATA")
vChildren.add(TextNodeImpl(fXml.substring(fPos + 9, vEnd)))
fPos = vEnd + 3
vChildren.add(TextNodeImpl(mXml.substring(mPos + 9, vEnd)))
mPos = vEnd + 3
}
lookingAt("<?") -> skipUntil("?>")
lookingAt("<") -> parseElement(vScope)?.let { vChildren.add(it) }
else -> {
val vNext = fXml.indexOf('<', fPos).let { if (it < 0) fXml.length else it }
val vText = decodeEntities(fXml.substring(fPos, vNext))
val vNext = mXml.indexOf('<', mPos).let { if (it < 0) mXml.length else it }
val vText = decodeEntities(mXml.substring(mPos, vNext))
if (vText.isNotBlank()) vChildren.add(TextNodeImpl(vText))
fPos = vNext
mPos = vNext
}
}
}
@@ -200,28 +200,28 @@ private class XmlDomParser(private val fXml: String) {
// ============
// Lexing helpers
private fun lookingAt(inToken: String): Boolean = fXml.startsWith(inToken, fPos)
private fun lookingAt(inToken: String): Boolean = mXml.startsWith(inToken, mPos)
private fun expect(inToken: String) {
if (!lookingAt(inToken)) throw MalformedXMLException("expected '$inToken' at $fPos")
fPos += inToken.length
if (!lookingAt(inToken)) throw MalformedXMLException("expected '$inToken' at $mPos")
mPos += inToken.length
}
private fun skipWhitespace() {
while (fPos < fXml.length && fXml[fPos].isWhitespace()) fPos++
while (mPos < mXml.length && mXml[mPos].isWhitespace()) mPos++
}
private fun skipUntil(inToken: String) {
val vEnd = fXml.indexOf(inToken, fPos)
val vEnd = mXml.indexOf(inToken, mPos)
if (vEnd < 0) throw MalformedXMLException("unterminated '$inToken' section")
fPos = vEnd + inToken.length
mPos = vEnd + inToken.length
}
/* DOCTYPE may nest an internal subset in [ ] - skip to the matching '>'. */
private fun skipDoctype() {
var vDepth = 0
while (fPos < fXml.length) {
val c = fXml[fPos++]
while (mPos < mXml.length) {
val c = mXml[mPos++]
if (c == '[') vDepth++
else if (c == ']') vDepth--
else if (c == '>' && vDepth <= 0) return
@@ -230,24 +230,24 @@ private class XmlDomParser(private val fXml: String) {
}
private fun readName(): String {
val vStart = fPos
while (fPos < fXml.length) {
val c = fXml[fPos]
val vStart = mPos
while (mPos < mXml.length) {
val c = mXml[mPos]
if (c.isWhitespace() || c == '=' || c == '>' || c == '/' || c == '<') break
fPos++
mPos++
}
return fXml.substring(vStart, fPos)
return mXml.substring(vStart, mPos)
}
private fun readQuotedValue(): String {
if (fPos >= fXml.length) throw MalformedXMLException("unterminated attribute value")
val vQuote = fXml[fPos]
if (vQuote != '"' && vQuote != '\'') throw MalformedXMLException("attribute value must be quoted at $fPos")
fPos++
val vEnd = fXml.indexOf(vQuote, fPos)
if (mPos >= mXml.length) throw MalformedXMLException("unterminated attribute value")
val vQuote = mXml[mPos]
if (vQuote != '"' && vQuote != '\'') throw MalformedXMLException("attribute value must be quoted at $mPos")
mPos++
val vEnd = mXml.indexOf(vQuote, mPos)
if (vEnd < 0) throw MalformedXMLException("unterminated attribute value")
val vRaw = fXml.substring(fPos, vEnd)
fPos = vEnd + 1
val vRaw = mXml.substring(mPos, vEnd)
mPos = vEnd + 1
return decodeEntities(vRaw)
}
@@ -28,8 +28,8 @@ final var com.compose.sdl/useVirtualFrameTime // com.compose.sdl/useVirtualFrame
final fun <get-useVirtualFrameTime>(): kotlin/Boolean // com.compose.sdl/useVirtualFrameTime.<get-useVirtualFrameTime>|<get-useVirtualFrameTime>(){}[0]
final fun <set-useVirtualFrameTime>(kotlin/Boolean) // com.compose.sdl/useVirtualFrameTime.<set-useVirtualFrameTime>|<set-useVirtualFrameTime>(kotlin.Boolean){}[0]
final fun (com.compose.sdl/ApplicationScope).com.compose.sdl/Window(kotlin/Function0<kotlin/Unit>, kotlin/String?, kotlin/Int, kotlin/Int, com.compose.sdl/GpuMode?, com.compose.sdl/AppWindowIcon?, kotlin/Function2<com.compose.sdl/RenderBackend, kotlin/Int, kotlin/Boolean>?, kotlin/Function3<com.compose.sdl/ComposeWindowScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.compose.sdl/Window|Window@com.compose.sdl.ApplicationScope(kotlin.Function0<kotlin.Unit>;kotlin.String?;kotlin.Int;kotlin.Int;com.compose.sdl.GpuMode?;com.compose.sdl.AppWindowIcon?;kotlin.Function2<com.compose.sdl.RenderBackend,kotlin.Int,kotlin.Boolean>?;kotlin.Function3<com.compose.sdl.ComposeWindowScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun (com.compose.sdl/ApplicationScope).com.compose.sdl/Window(kotlin/Function0<kotlin/Unit>, kotlin/String?, kotlin/Int, kotlin/Int, com.compose.sdl/GpuMode?, com.compose.sdl/AppWindowIcon?, kotlin/Boolean, kotlin/Boolean, kotlin/Boolean, kotlin/Boolean, kotlin/Boolean, kotlin/Boolean, kotlin/Function1<androidx.compose.ui.input.key/KeyEvent, kotlin/Boolean>?, kotlin/Function1<androidx.compose.ui.input.key/KeyEvent, kotlin/Boolean>?, kotlin/Function2<com.compose.sdl/RenderBackend, kotlin/Int, kotlin/Boolean>?, kotlin/Function3<com.compose.sdl/ComposeWindowScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int, kotlin/Int) // com.compose.sdl/Window|Window@com.compose.sdl.ApplicationScope(kotlin.Function0<kotlin.Unit>;kotlin.String?;kotlin.Int;kotlin.Int;com.compose.sdl.GpuMode?;com.compose.sdl.AppWindowIcon?;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Function1<androidx.compose.ui.input.key.KeyEvent,kotlin.Boolean>?;kotlin.Function1<androidx.compose.ui.input.key.KeyEvent,kotlin.Boolean>?;kotlin.Function2<com.compose.sdl.RenderBackend,kotlin.Int,kotlin.Boolean>?;kotlin.Function3<com.compose.sdl.ComposeWindowScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
final fun com.compose.sdl/com_compose_sdl_AppWindowIcon$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_AppWindowIcon$stableprop_getter|com_compose_sdl_AppWindowIcon$stableprop_getter(){}[0]
final fun com.compose.sdl/nativeComposeApp(kotlin/Function3<com.compose.sdl/ApplicationScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>) // com.compose.sdl/nativeComposeApp|nativeComposeApp(kotlin.Function3<com.compose.sdl.ApplicationScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>){}[0]
final fun com.compose.sdl/nativeComposeWindow(kotlin/String = ..., kotlin/Int = ..., kotlin/Int = ..., com.compose.sdl/GpuMode = ..., com.compose.sdl/AppWindowIcon? = ..., kotlin/Function2<com.compose.sdl/RenderBackend, kotlin/Int, kotlin/Boolean>? = ..., kotlin/Function3<com.compose.sdl/ComposeWindowScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>) // com.compose.sdl/nativeComposeWindow|nativeComposeWindow(kotlin.String;kotlin.Int;kotlin.Int;com.compose.sdl.GpuMode;com.compose.sdl.AppWindowIcon?;kotlin.Function2<com.compose.sdl.RenderBackend,kotlin.Int,kotlin.Boolean>?;kotlin.Function3<com.compose.sdl.ComposeWindowScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>){}[0]
final fun com.compose.sdl/nativeComposeWindow(kotlin/String = ..., kotlin/Int = ..., kotlin/Int = ..., com.compose.sdl/GpuMode = ..., com.compose.sdl/AppWindowIcon? = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Function1<androidx.compose.ui.input.key/KeyEvent, kotlin/Boolean> = ..., kotlin/Function1<androidx.compose.ui.input.key/KeyEvent, kotlin/Boolean> = ..., kotlin/Function2<com.compose.sdl/RenderBackend, kotlin/Int, kotlin/Boolean>? = ..., kotlin/Function3<com.compose.sdl/ComposeWindowScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>) // com.compose.sdl/nativeComposeWindow|nativeComposeWindow(kotlin.String;kotlin.Int;kotlin.Int;com.compose.sdl.GpuMode;com.compose.sdl.AppWindowIcon?;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Function1<androidx.compose.ui.input.key.KeyEvent,kotlin.Boolean>;kotlin.Function1<androidx.compose.ui.input.key.KeyEvent,kotlin.Boolean>;kotlin.Function2<com.compose.sdl.RenderBackend,kotlin.Int,kotlin.Boolean>?;kotlin.Function3<com.compose.sdl.ComposeWindowScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>){}[0]
final fun com.compose.sdl/windowHasInvalidations(): kotlin/Boolean // com.compose.sdl/windowHasInvalidations|windowHasInvalidations(){}[0]
@@ -0,0 +1,88 @@
@file:OptIn(
androidx.compose.ui.InternalComposeUiApi::class,
androidx.compose.runtime.InternalComposeApi::class,
)
package com.compose.sdl
import androidx.compose.runtime.AbstractApplier
import androidx.compose.runtime.Composable
import kotlinx.coroutines.CoroutineScope
// ==================
// MARK: App runtime internals
// ==================
internal class ApplicationScopeImpl(val runtime: AppRuntime) : ApplicationScope {
override fun exitApplication() { runtime.exitRequested = true }
}
/** Registry + lifecycle for the live windows. Windows are created by the app
composition (Window()'s remember) and destroyed by the LOOP - DisposableEffect
onDispose only schedules, because teardown disposes a composition and that
must not run re-entrantly inside another composition's apply pass. */
internal class AppRuntime {
lateinit var scope: CoroutineScope
val windows = mutableListOf<WindowInstance>()
private val destroyQueue = mutableListOf<WindowInstance>()
var exitRequested = false
var hadWindow = false
fun windowFor(inId: UInt): WindowInstance? =
if (inId == 0u) windows.firstOrNull()
else windows.firstOrNull { it.backend.windowId == inId } ?: windows.firstOrNull()
fun markAllNeedFrame() {
for (vW in windows) vW.needsFrame = true
}
fun createWindow(
inTitle: String,
inWidth: Int,
inHeight: Int,
inGpu: GpuMode,
inIcon: AppWindowIcon?,
inAttrs: WindowAttributes,
inOnFrame: ((RenderBackend, Int) -> Boolean)?,
inPreviewKeyHandler: () -> ((androidx.compose.ui.input.key.KeyEvent) -> Boolean),
inKeyHandler: () -> ((androidx.compose.ui.input.key.KeyEvent) -> Boolean),
inContent: () -> (@Composable ComposeWindowScope.() -> Unit),
): WindowInstance {
val vWindow = WindowInstance(
inTitle, inWidth, inHeight, inGpu, inIcon, inAttrs, inOnFrame,
inPreviewKeyHandler, inKeyHandler, inContent,
)
// A Window() was declared either way - the "exit when the last window
// is gone" rule must also fire when every declared window failed to
// initialise (otherwise the loop would spin forever with none).
hadWindow = true
if (vWindow.init(scope)) {
windows.add(vWindow)
} else {
println("nativeComposeApp: window '$inTitle' failed to initialise")
}
return vWindow
}
fun scheduleDestroy(inWindow: WindowInstance) {
if (inWindow in windows && inWindow !in destroyQueue) destroyQueue.add(inWindow)
}
fun reapDestroyed() {
while (destroyQueue.isNotEmpty()) {
val vW = destroyQueue.removeLast()
windows.remove(vW)
vW.destroy()
}
}
}
// No-op applier for the app-level composition - it emits no UI nodes, only
// side effects (each Window() manages a native window).
internal class UnitApplier : AbstractApplier<Unit>(Unit) {
override fun insertTopDown(index: Int, instance: Unit) {}
override fun insertBottomUp(index: Int, instance: Unit) {}
override fun remove(index: Int, count: Int) {}
override fun move(from: Int, to: Int, count: Int) {}
override fun onClear() {}
}
@@ -5,46 +5,21 @@
package com.compose.sdl
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.AbstractApplier
import androidx.compose.runtime.Composable
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.runtime.Composition
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Recomposer
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
import androidx.compose.ui.input.pointer.PointerButton
import androidx.compose.ui.input.pointer.PointerEventType
import com.compose.sdl.node.ComposeRootHost
import com.compose.sdl.res.currentImageLoader
import com.compose.sdl.window.LocalPopupHost
import com.compose.sdl.window.PopupLayer
import com.compose.sdl.window.createPopupHostState
import kotlinx.coroutines.*
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import kotlinx.cinterop.toKString
import kotlinx.cinterop.reinterpret
import kotlinx.cinterop.alloc
import kotlinx.cinterop.ptr
import kotlinx.cinterop.COpaquePointer
import kotlinx.cinterop.pointed
import sdl3.SDL_Delay
import sdl3.SDL_GetCurrentDisplayMode
import sdl3.SDL_GetDisplayForWindow
import sdl3.SDL_GetPerformanceCounter
import sdl3.SDL_GetPerformanceFrequency
import sdl3.SDL_GetTicks
import sdl3.SDL_GetTicksNS
import sdl3.SDL_Quit
import sdl3.SDL_SetWindowTitle
import sdl3.SDL_WaitEventTimeout
// ==================
@@ -92,6 +67,14 @@ fun ApplicationScope.Window(
height: Int = 600,
gpu: GpuMode = GpuMode.Auto,
icon: AppWindowIcon? = null,
undecorated: Boolean = false,
transparent: Boolean = false,
resizable: Boolean = true,
enabled: Boolean = true,
focusable: Boolean = true,
alwaysOnTop: Boolean = false,
onPreviewKeyEvent: (KeyEvent) -> Boolean = { false },
onKeyEvent: (KeyEvent) -> Boolean = { false },
onFrame: ((backend: RenderBackend, frameIndex: Int) -> Boolean)? = null,
content: @Composable ComposeWindowScope.() -> Unit,
) {
@@ -99,6 +82,16 @@ fun ApplicationScope.Window(
?: error("Window() must be called inside nativeComposeApp { ... }")
val vContent = rememberUpdatedState(content)
val vClose = rememberUpdatedState(onCloseRequest)
val vPreviewKey = rememberUpdatedState(onPreviewKeyEvent)
val vKey = rememberUpdatedState(onKeyEvent)
val vAttrs = WindowAttributes(
undecorated = undecorated,
transparent = transparent,
resizable = resizable,
enabled = enabled,
focusable = focusable,
alwaysOnTop = alwaysOnTop,
)
val vWindow = remember {
vScope.runtime.createWindow(
inTitle = title,
@@ -106,7 +99,12 @@ fun ApplicationScope.Window(
inHeight = height,
inGpu = gpu,
inIcon = icon,
inAttrs = vAttrs,
inOnFrame = onFrame,
// Read through holders so updated lambdas propagate without
// recreating the window (same reason as the content holder below).
inPreviewKeyHandler = { vPreviewKey.value },
inKeyHandler = { vKey.value },
// The window composition reads the holder each recomposition, so
// updated content lambdas propagate without recreating the window.
inContent = { vContent.value },
@@ -115,6 +113,12 @@ fun ApplicationScope.Window(
SideEffect {
if (vWindow.facade.title != title) vWindow.facade.setTitle(title)
}
// Re-apply only on a real change; `transparent` is creation-only (see
// WindowAttributes) so it is deliberately not re-applied here.
DisposableEffect(vAttrs) {
vWindow.applyAttributes(vAttrs)
onDispose { }
}
DisposableEffect(Unit) {
onDispose { vScope.runtime.scheduleDestroy(vWindow) }
}
@@ -136,7 +140,7 @@ fun nativeComposeApp(content: @Composable ApplicationScope.() -> Unit) {
runtime.scope = this
// App-level composition: no UI tree - it only declares Window()s.
val appClock = SDL3FrameClock()
val appClock = Sdl3FrameClock()
val appRecomposer = Recomposer(coroutineContext + appClock)
// The clock must be in the collector's context - runRecomposeAndApplyChanges
// awaits parent frames through it.
@@ -311,74 +315,6 @@ fun nativeComposeApp(content: @Composable ApplicationScope.() -> Unit) {
SDL_Quit()
}
// ==================
// MARK: Probe / screenshot support (render-to-quiescence)
// ==================
/** Set BEFORE nativeComposeApp/nativeComposeWindow: every window's composition then runs
under an InfiniteAnimationPolicy that CANCELS infinite animations, so
rememberInfiniteTransition & co. freeze at their initial value - the same mechanism
upstream's test rules use. Screenshot/parity runs enable it so looping screens can
reach quiescence and capture deterministically. */
var disableInfiniteAnimations: Boolean = false
/** The cancelling policy: a coroutine that ends in CancellationException counts as
cancelled (not failed), so only the animation coroutine stops - nothing propagates. */
private object CancelInfiniteAnimationsPolicy : androidx.compose.ui.platform.InfiniteAnimationPolicy {
override suspend fun <R> onInfiniteOperation(block: suspend () -> R): R =
throw CancellationException("infinite animations are disabled (disableInfiniteAnimations)")
}
/** Set BEFORE nativeComposeApp/nativeComposeWindow: the composition + animation frame
clocks advance a VIRTUAL 16.6ms per main-loop iteration instead of reading SDL's
real-time ticks - the native mirror of the JVM parity leg's render(nanos) stepping.
Animations then progress by exact per-frame deltas, so anything time-raced (e.g. a
bring-into-view scroll interrupted mid-flight) resolves identically on every run and
screenshots become deterministic. Input timestamps and FPS stay on real time. */
var useVirtualFrameTime: Boolean = false
private var virtualFrameNanos = 0L
private fun advanceVirtualFrame() {
if (useVirtualFrameTime) virtualFrameNanos += 16_666_667L
}
// Timestamp for the composition frame clocks (recomposer + withFrameNanos animations).
private fun frameClockNanos(): Long =
if (useVirtualFrameTime) virtualFrameNanos else SDL_GetTicksNS().toLong()
// Timestamp for the owner's node-animation clock - real path keeps the pre-existing
// ms-resolution SDL_GetTicks base so non-screenshot behaviour is bit-for-bit unchanged.
private fun animationClockNanos(): Long =
if (useVirtualFrameTime) virtualFrameNanos else SDL_GetTicks().toLong() * 1_000_000L
// The window currently inside renderFrame - the loop is single-threaded, so a plain
// var is enough for onFrame probes to address "the window I'm being called for".
private var renderingWindow: WindowInstance? = null
/** From inside an onFrame callback: true while the just-rendered window still has pending
work (recomposition, layout/draw invalidation, or an animation awaiting the next frame).
Screenshot probes capture once this stays false for a few consecutive frames instead of
at a fixed frame count. Outside onFrame it answers false. */
fun windowHasInvalidations(): Boolean = renderingWindow?.hasInvalidations() ?: false
/** Trigger a Kotlin/Native GC so Cleaner-managed renderer resources release
their native memory (see the main loop's native-memory nudge). */
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class)
private fun collectNativeGarbage() = kotlin.native.runtime.GC.collect()
/** Frame interval (ms) of the window's current display, for the non-vsync
fallback pacing (SDL_Delay). Falls back to 16ms (~60Hz) when the mode can't
be read, so a 144Hz panel on a non-vsync path isn't capped to 60. */
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
private fun displayFrameDelayMs(window: COpaquePointer?): UInt {
val vWindow = window ?: return 16u
val vDisplay = SDL_GetDisplayForWindow(vWindow.reinterpret())
val vMode = SDL_GetCurrentDisplayMode(vDisplay) ?: return 16u
val vHz = vMode.pointed.refresh_rate
return if (vHz > 0f) (1000f / vHz).toUInt().coerceAtLeast(1u) else 16u
}
/** Single-window compatibility wrapper - the pre-multi-window entry point.
Closing the window exits the app, exactly as before. */
fun nativeComposeWindow(
@@ -387,6 +323,14 @@ fun nativeComposeWindow(
height: Int = 600,
gpu: GpuMode = GpuMode.Auto,
icon: AppWindowIcon? = null,
undecorated: Boolean = false,
transparent: Boolean = false,
resizable: Boolean = true,
enabled: Boolean = true,
focusable: Boolean = true,
alwaysOnTop: Boolean = false,
onPreviewKeyEvent: (KeyEvent) -> Boolean = { false },
onKeyEvent: (KeyEvent) -> Boolean = { false },
onFrame: ((backend: RenderBackend, frameIndex: Int) -> Boolean)? = null,
content: @Composable ComposeWindowScope.() -> Unit,
) {
@@ -398,592 +342,16 @@ fun nativeComposeWindow(
height = height,
gpu = gpu,
icon = icon,
undecorated = undecorated,
transparent = transparent,
resizable = resizable,
enabled = enabled,
focusable = focusable,
alwaysOnTop = alwaysOnTop,
onPreviewKeyEvent = onPreviewKeyEvent,
onKeyEvent = onKeyEvent,
onFrame = onFrame,
content = content,
)
}
}
// ==================
// MARK: App runtime internals
// ==================
internal class ApplicationScopeImpl(val runtime: AppRuntime) : ApplicationScope {
override fun exitApplication() { runtime.exitRequested = true }
}
/** Registry + lifecycle for the live windows. Windows are created by the app
composition (Window()'s remember) and destroyed by the LOOP - DisposableEffect
onDispose only schedules, because teardown disposes a composition and that
must not run re-entrantly inside another composition's apply pass. */
internal class AppRuntime {
lateinit var scope: CoroutineScope
val windows = mutableListOf<WindowInstance>()
private val destroyQueue = mutableListOf<WindowInstance>()
var exitRequested = false
var hadWindow = false
fun windowFor(inId: UInt): WindowInstance? =
if (inId == 0u) windows.firstOrNull()
else windows.firstOrNull { it.backend.windowId == inId } ?: windows.firstOrNull()
fun markAllNeedFrame() {
for (vW in windows) vW.needsFrame = true
}
fun createWindow(
inTitle: String,
inWidth: Int,
inHeight: Int,
inGpu: GpuMode,
inIcon: AppWindowIcon?,
inOnFrame: ((RenderBackend, Int) -> Boolean)?,
inContent: () -> (@Composable ComposeWindowScope.() -> Unit),
): WindowInstance {
val vWindow = WindowInstance(inTitle, inWidth, inHeight, inGpu, inIcon, inOnFrame, inContent)
// A Window() was declared either way - the "exit when the last window
// is gone" rule must also fire when every declared window failed to
// initialise (otherwise the loop would spin forever with none).
hadWindow = true
if (vWindow.init(scope)) {
windows.add(vWindow)
} else {
println("nativeComposeApp: window '$inTitle' failed to initialise")
}
return vWindow
}
fun scheduleDestroy(inWindow: WindowInstance) {
if (inWindow in windows && inWindow !in destroyQueue) destroyQueue.add(inWindow)
}
fun reapDestroyed() {
while (destroyQueue.isNotEmpty()) {
val vW = destroyQueue.removeLast()
windows.remove(vW)
vW.destroy()
}
}
}
// No-op applier for the app-level composition - it emits no UI nodes, only
// side effects (each Window() manages a native window).
private class UnitApplier : AbstractApplier<Unit>(Unit) {
override fun insertTopDown(index: Int, instance: Unit) {}
override fun insertBottomUp(index: Int, instance: Unit) {}
override fun remove(index: Int, count: Int) {}
override fun move(from: Int, to: Int, count: Int) {}
override fun onClear() {}
}
// ==================
// MARK: WindowInstance - one SDL window + renderer + composition
// ==================
internal class WindowInstance(
private val initialTitle: String,
inWidth: Int,
inHeight: Int,
inGpu: GpuMode,
inIcon: AppWindowIcon?,
private val onFrame: ((RenderBackend, Int) -> Boolean)?,
private val contentHolder: () -> (@Composable ComposeWindowScope.() -> Unit),
) {
// Resolved renderer mode. `var` so init() can fall back GPU → CPU raster when
// a Skia GPU context can't be created (RDP / headless / missing GL driver).
private var gpuMode = if (inGpu is GpuMode.Auto) rendererPreferredGpuMode() else inGpu
private val initialWidth = inWidth
private val initialHeight = inHeight
private val icon = inIcon
// Assigned by init() from the first backend that comes up (see makeBackend()).
lateinit var backend: SDL3Backend
private set
private var renderBackend: RenderBackend? = null
lateinit var host: ComposeRootHost
private set
lateinit var facade: ComposeNativeWindow
private set
private var popupHost: com.compose.sdl.window.PopupHostState? = null
val frameClock = SDL3FrameClock()
private var recomposer: Recomposer? = null
private var recomposeJob: Job? = null
private var composition: Composition? = null
// Set by Window(); the compat wrapper points it at exitApplication().
var onCloseRequest: () -> Unit = {}
var closeDispatched = false
// Render-on-demand flag (see the pre-multi-window loop): set by events,
// state writes (global observer), resize, expose.
var needsFrame = true
// Hover refresh: last pointer position in PIXEL space, re-dispatched as a
// synthetic Move after layout so items scrolled under the cursor hover.
private var lastMouseX = -1f
private var lastMouseY = -1f
private var hasMousePos = false
private var frameIndex = 0
private var fpsEma = 0.0
private var fpsLastFrameMs = SDL_GetTicks()
private var fpsLastTitleMs = 0uL
// Escape → back (upstream desktop's BackNavigationEventInput): unconsumed
// Escape completes a back navigation on THIS window's dispatcher.
private val navigationEventOwner = object : androidx.navigationevent.NavigationEventDispatcherOwner {
override val navigationEventDispatcher = androidx.navigationevent.NavigationEventDispatcher()
}
private val backNavigationInput = BackNavigationInput()
// WINDOW-scoped architecture-components owner (Lifecycle + ViewModelStore +
// SavedStateRegistry) - the same trio upstream desktop's
// DefaultArchitectureComponentsOwner supplies through the window
// PlatformContext (compose/ui skikoMain PlatformOwnerProvider.skiko.kt).
// viewModel(), SavedStateHandle plumbing and navigation3's
// rememberViewModelStoreNavEntryDecorator all resolve their parent owners
// from this; destroy() moves it to DESTROYED and clears the store.
private val architectureOwner = WindowArchitectureOwner()
// Window focus / visibility → Lifecycle.State, Compose Desktop's mapping:
// focused → RESUMED, visible unfocused → STARTED, hidden/minimised →
// CREATED. Starts focused+visible (SDL fires FOCUS_GAINED right after
// creation anyway; headless probe runs simply stay RESUMED).
private var windowFocused = true
private var windowVisible = true
fun onActivationEvent(inEvent: AppEvent.WindowActivation) {
inEvent.focused?.let { windowFocused = it }
inEvent.visible?.let { windowVisible = it }
if (::host.isInitialized) host.setWindowFocused(windowFocused)
architectureOwner.setLifecycleState(
when {
!windowVisible -> androidx.lifecycle.Lifecycle.State.CREATED
windowFocused -> androidx.lifecycle.Lifecycle.State.RESUMED
else -> androidx.lifecycle.Lifecycle.State.STARTED
}
)
// Shown / restored / focus-gained also invalidate the contents - keep
// the pre-lifecycle RedrawNeeded behaviour of these SDL events.
needsFrame = true
}
// Creates + initialises an SDL3Backend for [mode] and its RenderBackend. On
// success commits `backend` and returns the renderer; else tears everything
// down and returns null so the caller can retry with a different mode.
private fun makeBackend(mode: GpuMode): RenderBackend? {
val vBackend = SDL3Backend(
initialTitle, initialWidth, initialHeight, gpuMode = mode,
iconLightResourcePaths = icon?.light ?: emptyList(),
iconDarkResourcePaths = icon?.dark ?: emptyList(),
)
if (!vBackend.init()) {
vBackend.destroy(inQuitSdl = false)
return null
}
vBackend.updateWindowSize()
val vRender = createRenderBackend(vBackend, mode)
if (vRender == null || !vRender.ensureSize(vBackend.pixelWidth, vBackend.pixelHeight)) {
vRender?.destroy()
vBackend.destroy(inQuitSdl = false)
return null
}
backend = vBackend
return vRender
}
fun init(inScope: CoroutineScope): Boolean {
// Try the resolved gpuMode; if it's a Skia GPU mode whose context/bridge
// can't come up (RDP / headless / missing GL driver), fall back once to
// CPU raster (Software) so the window still opens instead of failing.
var vRender = makeBackend(gpuMode)
if (vRender == null && gpuMode is GpuMode.Skia) {
println("GPU renderer ($gpuMode) unavailable - falling back to CPU raster (Software)")
gpuMode = GpuMode.Software
vRender = makeBackend(gpuMode)
}
if (vRender == null) {
println("Failed to init render backend")
return false
}
renderBackend = vRender
host = ComposeRootHost(inDensity = backend.pixelDensity)
host.attach()
// A layer whose content changed (OwnedLayer.invalidate) schedules a frame
// even when nothing else (recompose / relayout) is pending - retained layers
// need this so a draw-only state change still repaints.
host.setInvalidationCallback { needsFrame = true }
facade = ComposeNativeWindow(backend, gpuMode, initialTitle)
navigationEventOwner.navigationEventDispatcher.addInput(backNavigationInput)
// The render-bridge globals must point at THIS window's renderer while
// its content composes/measures (first composition happens inside
// setContent below).
installGlobals()
// Effect context for this window's composition - the screenshot flag injects the
// infinite-animation-cancelling policy (see disableInfiniteAnimations above).
var vEffectContext = inScope.coroutineContext + frameClock
if (disableInfiniteAnimations) vEffectContext += CancelInfiniteAnimationsPolicy
val vRecomposer = Recomposer(vEffectContext)
recomposer = vRecomposer
recomposeJob = inScope.launch(frameClock) { vRecomposer.runRecomposeAndApplyChanges() }
val vComposition = Composition(host.applier, vRecomposer)
composition = vComposition
val vUriHandler = object : androidx.compose.ui.platform.UriHandler {
override fun openUri(uri: String) { openUrl(uri) }
}
val vPopupHost = createPopupHostState()
popupHost = vPopupHost
val vWindowScope = object : ComposeWindowScope {
override val window: ComposeNativeWindow = facade
}
vComposition.setContent {
CompositionLocalProvider(
LocalComposeNativeWindow provides facade,
LocalPopupHost provides vPopupHost,
// Upstream vendored CompositionLocals.kt declares each of these as
// `staticCompositionLocalOf<T> { noLocalProvidedFor("…") }` - reading
// one without a Provider throws. Seed them all from the ComposeOwner.
androidx.compose.ui.platform.LocalDensity provides host.density,
androidx.compose.ui.platform.LocalLayoutDirection provides host.layoutDirection,
androidx.compose.ui.platform.LocalFocusManager provides host.focusManager,
androidx.compose.ui.platform.LocalGraphicsContext provides host.graphicsContext,
androidx.compose.ui.platform.LocalViewConfiguration provides host.viewConfiguration,
androidx.compose.ui.platform.LocalInputModeManager provides host.inputModeManager,
androidx.compose.ui.platform.LocalHapticFeedback provides host.hapticFeedback,
androidx.compose.ui.platform.LocalTextToolbar provides host.textToolbar,
androidx.compose.ui.platform.LocalWindowInfo provides host.windowInfo,
androidx.compose.ui.platform.LocalSoftwareKeyboardController provides host.softwareKeyboardController,
androidx.compose.ui.platform.LocalClipboard provides
androidx.compose.ui.platform.platformClipboard(),
androidx.compose.ui.platform.LocalFontFamilyResolver provides
com.compose.sdl.text.font.projectFontFamilyResolver,
androidx.compose.ui.platform.LocalUriHandler provides vUriHandler,
// Desktop windows have no system bars / notch / IME insets - the
// interface's all-zero defaults are exactly right.
androidx.compose.ui.platform.LocalPlatformWindowInsets provides
object : androidx.compose.ui.platform.PlatformWindowInsets {},
// The window's architecture-components owner backs all three arch
// locals, exactly like upstream desktop's window PlatformContext:
// - LocalLifecycleOwner: RESUMED for the window's whole life;
// lifecycle-aware content (NavDisplay, rememberLifecycleOwner, …)
// errors without it.
// - LocalViewModelStoreOwner: window-scoped ViewModels (google
// lifecycle-viewmodel-compose's local is a plain composition
// local; the JB HostDefault route doesn't exist in the google
// artifacts this port ships).
// - LocalSavedStateRegistryOwner: SavedStateHandle / rememberSaveable
// registry parent (nav3's ViewModelStoreNavEntryDecorator requires it).
androidx.lifecycle.compose.LocalLifecycleOwner provides architectureOwner,
androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner provides architectureOwner,
androidx.savedstate.compose.LocalSavedStateRegistryOwner provides architectureOwner,
// Runtime-level host defaults - this window's navigation-event
// owner (SearchBar / sheets back plumbing). The viewmodel store
// is provided through the plain LocalViewModelStoreOwner above
// instead: the google lifecycle artifacts this port ships have no
// HostDefault key for it (that mechanism is JB-variant-only).
androidx.compose.runtime.LocalHostDefaultProvider provides
remember {
object : androidx.compose.runtime.HostDefaultProvider {
@Suppress("UNCHECKED_CAST")
override fun <T> getHostDefault(key: androidx.compose.runtime.HostDefaultKey<T>): T = when (key) {
androidx.navigationevent.compose.NavigationEventDispatcherOwnerHostDefaultKey -> navigationEventOwner
else -> null
} as T
}
},
) {
// Seed the :ui-internal LocalPointerIconService so Modifier.pointerHoverIcon
// drives the SDL cursor (the local + service type can't cross the module boundary).
host.ProvidePointerIconService {
Box(modifier = Modifier.fillMaxSize()) {
// Read through the holder so Window() recompositions with a
// new content lambda propagate into this composition.
val vContent = contentHolder()
with(vWindowScope) { vContent() }
PopupLayer(vPopupHost)
}
}
}
}
// First composition done (setContent is synchronous) - promote the
// window lifecycle from CREATED to the focus/visibility-derived state.
// Composition itself runs at CREATED so enableSavedStateHandles()
// callers see a legal state, mirroring upstream desktop's
// compose-first-resume-after ordering.
architectureOwner.setLifecycleState(
when {
!windowVisible -> androidx.lifecycle.Lifecycle.State.CREATED
windowFocused -> androidx.lifecycle.Lifecycle.State.RESUMED
else -> androidx.lifecycle.Lifecycle.State.STARTED
}
)
return true
}
/** Points the render-bridge globals (text measurer / image loader /
viewport) at this window's renderer. Must be called before composing,
measuring, or drawing this window's tree. */
fun installGlobals() {
val vRender = renderBackend ?: return
currentImageLoader = vRender.imageLoader
com.compose.sdl.text.currentViewportWidth = backend.pixelWidth
com.compose.sdl.text.currentViewportHeight = backend.pixelHeight
// Register bundled generic fonts (FontFamily.Monospace → NotoSansMono) once
// data.kres is loadable; idempotent, no-op if the font isn't bundled.
com.compose.sdl.text.registerGenericFonts()
// Position the OS IME candidate window the moment a field in THIS window
// gains focus, not only on the first TEXT_EDITING event (repointed per
// window here so multi-window focus targets the right SDL window).
com.compose.sdl.text.input.ImeBridge.onSessionActiveChange = { active ->
if (active) updateImeArea()
}
}
// ============
// Event handling (loop-routed, per window)
fun onPointerEvent(inEvent: AppEvent.Pointer) {
needsFrame = true
installGlobals()
val vType = when (inEvent.event.type) {
PointerEventType.Press -> 1
PointerEventType.Release -> 2
else -> 0
}
val vBtn = when (inEvent.event.button) {
PointerButton.Secondary -> 1
PointerButton.Tertiary -> 2
else -> 0
}
// SDL3 delivers mouse coords in logical points on HiDPI - multiply by
// DPR so hit-testing lands in the pixel space layout uses.
val vDpr = backend.pixelDensity
val vPx = inEvent.event.x * vDpr
val vPy = inEvent.event.y * vDpr
if (inEvent.event.type == PointerEventType.Press) {
popupHost?.notifyOutsidePress(vPx.toInt(), vPy.toInt())
}
host.onPointerRaw(vPx, vPy, vType, vBtn, SDL_GetTicks().toLong())
lastMouseX = vPx
lastMouseY = vPy
hasMousePos = true
}
/** Pointer left the window (SDL_EVENT_WINDOW_MOUSE_LEAVE). Stop the per-frame
synthetic hover re-dispatch and fire one Exit at the last position so a
hovered widget clears its highlight instead of sticking forever. */
fun onPointerExit() {
needsFrame = true
if (hasMousePos) {
host.onPointerRaw(lastMouseX, lastMouseY, 3, 0, SDL_GetTicks().toLong())
}
hasMousePos = false
}
fun onWheelEvent(inEvent: AppEvent.MouseWheel) {
needsFrame = true
installGlobals()
val vDpr = backend.pixelDensity
host.onWheel(inEvent.x * vDpr, inEvent.y * vDpr, inEvent.deltaX, inEvent.deltaY, SDL_GetTicks().toLong())
}
fun onKeyEvent(inEvent: AppEvent.Key) {
needsFrame = true
installGlobals()
// Focused chain → window-level shortcuts → back navigation (Escape).
if (!host.dispatchKeyEvent(inEvent.event) && !facade.dispatchKeyShortcut(inEvent.event)) {
backNavigationInput.onKeyEvent(inEvent.event)
}
}
fun onTextInputEvent(inEvent: AppEvent.TextInput) {
needsFrame = true
installGlobals()
// A focused text field runs an IME session (ComposeOwner.textInputSession):
// commit through it so the text REPLACES any active composition. Falls back
// to synthesising typed KeyEvents when no field is focused - SDL key events
// carry UNSHIFTED keycodes (no uppercase/numpad/dead keys), so committed
// text is the only layout-correct character source.
if (!com.compose.sdl.text.input.ImeBridge.commit(inEvent.text)) {
dispatchTypedText(host, inEvent.text)
}
}
fun onTextEditingEvent(inEvent: AppEvent.TextEditing) {
needsFrame = true
installGlobals()
// IME preedit: show the composing (underlined) region in the focused field.
// No-op if no field is focused. Also nudge the OS candidate window to the
// field's on-screen rect.
com.compose.sdl.text.input.ImeBridge.compose(inEvent.text)
updateImeArea()
}
// Nudge the OS IME candidate window to the focused field's on-screen rect.
// Best-effort: no-op without an active session or a laid-out field.
@OptIn(androidx.compose.ui.ExperimentalComposeUiApi::class, kotlinx.cinterop.ExperimentalForeignApi::class)
private fun updateImeArea() {
val vRequest = com.compose.sdl.text.input.ImeBridge.request ?: return
val vRect = vRequest.focusedRectInRoot() ?: return
val vWindow = backend.window ?: return
val vDpr = backend.pixelDensity
kotlinx.cinterop.memScoped {
// SDL wants the area in logical window points; layout runs in physical px.
val vSdlRect = alloc<sdl3.SDL_Rect>()
vSdlRect.x = (vRect.left / vDpr).toInt()
vSdlRect.y = (vRect.top / vDpr).toInt()
vSdlRect.w = (vRect.width / vDpr).toInt().coerceAtLeast(1)
vSdlRect.h = (vRect.height / vDpr).toInt().coerceAtLeast(1)
sdl3.SDL_SetTextInputArea(vWindow.reinterpret(), vSdlRect.ptr, 0)
}
}
fun onDropEvent(inEvent: AppEvent.Drop) {
needsFrame = true
installGlobals()
// SDL fires drop coords in logical points at DPR-1; scale to the pixel
// space layout runs in (Option-B density flow) so hit-testing lands
// where the pointer is.
val vDpr = backend.pixelDensity
when (inEvent.phase) {
AppEvent.DropPhase.BEGIN -> host.onDropBegin()
AppEvent.DropPhase.POSITION -> host.onDropPosition(inEvent.x * vDpr, inEvent.y * vDpr)
AppEvent.DropPhase.FILE -> inEvent.data?.let { host.onDropFile(it) }
AppEvent.DropPhase.TEXT -> inEvent.data?.let { host.onDropText(it) }
AppEvent.DropPhase.COMPLETE -> host.onDropComplete()
}
}
fun onResizedEvent() {
needsFrame = true
backend.updateWindowSize()
facade.onResized()
}
/** OS light/dark theme changed - re-pick the theme-appropriate window icon
(no-op if this window has no icon configured or the choice is unchanged). */
fun onSystemThemeChanged() {
backend.applyThemeIcon()
}
/** OS close button / app-level Quit: honour the veto handler, then hand the
decision to the Window() declaration. */
fun requestClose() {
if (closeDispatched) return
if (facade.requestCloseFromUser()) {
closeDispatched = true
onCloseRequest()
}
}
// ============
// Frame pump
fun shouldRender(): Boolean =
needsFrame || (recomposer?.hasPendingWork == true) || onFrame != null || kForceRender
/** True while this window still has pending work: a state/layout/draw invalidation
(needsFrame), recomposer work, or a composition/node animation awaiting the next
frame. Sampled by windowHasInvalidations() from onFrame probes - the quiescence
signal for render-to-settle screenshot capture. */
fun hasInvalidations(): Boolean =
needsFrame || recomposer?.hasPendingWork == true ||
frameClock.hasAwaiters || host.hasAnimationAwaiters()
fun renderFrame() {
val vRender = renderBackend ?: return
needsFrame = false
if (recomposer?.hasPendingWork == true) needsFrame = true
backend.updateWindowSize()
vRender.ensureSize(backend.pixelWidth, backend.pixelHeight)
// Charge the GPU back-buffer acquire to its own phase. On Metal this is
// where nextDrawable() blocks on vsync (the natural frame pacing), so
// folding it into "layout" made the profiler read the vsync wait as
// layout cost - a ~6-7ms phantom that hid the real (tiny) layout time.
FrameProfiler.phase(" acquire")
host.setConstraints(backend.pixelWidth, backend.pixelHeight)
// Deliver any state written by this iteration's frame-clock continuations
// (withFrameNanos animations - notably smooth wheel scrolling) BEFORE we lay
// out. Those writes happen in the per-window pump's sendFrame()/yield(), which
// is AFTER the loop's last sendApplyNotifications() - so without this the
// relayout they trigger wasn't registered until the next frame, and the frame
// drew last frame's positions. That one-frame trail read as "the clip is a
// frame late" when scrolling (content briefly drawn past the viewport edge).
Snapshot.sendApplyNotifications()
host.measureAndLayout()
FrameProfiler.phase(" layout")
// Hover refresh after layout (upstream skiko: SyntheticEventSender).
if (hasMousePos) {
host.onPointerRaw(lastMouseX, lastMouseY, 0, 0, SDL_GetTicks().toLong())
}
vRender.beginFrame(1f)
vRender.drawRoot { canvas -> host.drawRoot(canvas) }
if (onFrame != null) {
renderingWindow = this
val vContinue = onFrame.invoke(vRender, frameIndex)
renderingWindow = null
// Probe consumers end the app when their scenario completes.
if (!vContinue) facade.close()
}
FrameProfiler.phase(" draw")
vRender.endFrame()
FrameProfiler.phase(" present")
frameIndex++
// FPS - instantaneous inter-frame rate, EMA-smoothed, title refreshed
// ~4x/sec. This shows within ~2 rendered frames of ANY activity rather
// than waiting for a full second of unbroken rendering to accumulate
// (the old fixed-window counter never got its first update: bursty
// interaction kept idling before 1s elapsed, and the idle-skip reset the
// window each time - so FPS only appeared during a long enough page
// transition). A dt outside 1..100ms is the first frame or a resume from
// idle (the gap isn't a real frame interval), so it's not sampled - the
// title just holds the last active rate while idle.
val vNowMs = SDL_GetTicks()
val vDt = (vNowMs - fpsLastFrameMs).toInt()
fpsLastFrameMs = vNowMs
if (vDt in 1..100) {
val vInst = 1000.0 / vDt
fpsEma = if (fpsEma <= 0.0) vInst else fpsEma * 0.9 + vInst * 0.1
if ((vNowMs - fpsLastTitleMs).toInt() >= 250) {
val vFps = (fpsEma + 0.5).toInt()
facade.updateFps(vFps)
SDL_SetWindowTitle(backend.window?.reinterpret(), "${facade.title} · $vFps FPS")
fpsLastTitleMs = vNowMs
}
}
}
// Idle-skip calls this: drop the frame timer so the resume-from-idle frame's
// dt (the whole idle gap) isn't sampled as a real interval. The EMA is left
// intact so the title keeps showing the last active rate while idle.
fun resetFpsWindow() {
fpsLastFrameMs = SDL_GetTicks()
}
fun destroy() {
composition?.dispose()
composition = null
// Window gone → DESTROYED lifecycle + ViewModels cleared (onCleared
// runs), matching upstream desktop's window-scoped owner lifetime.
architectureOwner.destroy()
recomposer?.cancel()
recomposer = null
recomposeJob?.cancel()
recomposeJob = null
renderBackend?.destroy()
renderBackend = null
backend.destroy(inQuitSdl = false)
}
}
// WindowArchitectureOwner -> WindowArchitectureOwner.kt
// BackNavigationInput / dispatchTypedText -> WindowInputHelpers.kt
@@ -18,56 +18,56 @@ import sdl3.SDL_GetTicks
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
internal object FrameProfiler {
// null until first checked; then true/false for the run's lifetime.
private var fEnabled: Boolean? = null
private var mEnabled: Boolean? = null
// Output file - resolved once from CDN_PROFILE. Writing to a file (not
// stdout) lets GUI-subsystem apps (the demo links --subsystem,windows, so
// it has no console) be profiled too. CDN_PROFILE=1 → "cdn_profile.log" in
// the cwd; CDN_PROFILE=<path> → that path.
private var fPath: String = "cdn_profile.log"
private var mPath: String = "cdn_profile.log"
val enabled: Boolean
get() = fEnabled ?: run {
get() = mEnabled ?: run {
val vEnv = platform.posix.getenv("CDN_PROFILE")?.toKString()
if (vEnv != null && vEnv != "1" && vEnv.isNotEmpty()) fPath = vEnv
(vEnv != null).also { fEnabled = it }
if (vEnv != null && vEnv != "1" && vEnv.isNotEmpty()) mPath = vEnv
(vEnv != null).also { mEnabled = it }
}
private val fFreq = SDL_GetPerformanceFrequency().toDouble()
private val mFreq = SDL_GetPerformanceFrequency().toDouble()
// Insertion-ordered so the printed line follows the call order.
private val fSum = LinkedHashMap<String, Double>()
private val fMax = LinkedHashMap<String, Double>()
private var fFrames = 0
private var fLastPrintMs = SDL_GetTicks()
private var fMark = 0uL
private val mSum = LinkedHashMap<String, Double>()
private val mMax = LinkedHashMap<String, Double>()
private var mFrames = 0
private var mLastPrintMs = SDL_GetTicks()
private var mMark = 0uL
fun mark() { if (enabled) fMark = SDL_GetPerformanceCounter() }
fun mark() { if (enabled) mMark = SDL_GetPerformanceCounter() }
fun phase(inName: String) {
if (!enabled) return
val vNow = SDL_GetPerformanceCounter()
val vMs = (vNow - fMark).toDouble() * 1000.0 / fFreq
fSum[inName] = (fSum[inName] ?: 0.0) + vMs
if (vMs > (fMax[inName] ?: 0.0)) fMax[inName] = vMs
fMark = vNow
val vMs = (vNow - mMark).toDouble() * 1000.0 / mFreq
mSum[inName] = (mSum[inName] ?: 0.0) + vMs
if (vMs > (mMax[inName] ?: 0.0)) mMax[inName] = vMs
mMark = vNow
}
fun frameDone(inRendered: Boolean) {
if (!enabled) return
if (inRendered) fFrames++
if (inRendered) mFrames++
val vNowMs = SDL_GetTicks()
if (vNowMs - fLastPrintMs >= 2000u && fFrames > 0) {
val vParts = fSum.keys.map { vName ->
val vAvg = (fSum[vName] ?: 0.0) / fFrames
"$vName=${(vAvg * 100).toInt() / 100.0}/${((fMax[vName] ?: 0.0) * 100).toInt() / 100.0}ms"
if (vNowMs - mLastPrintMs >= 2000u && mFrames > 0) {
val vParts = mSum.keys.map { vName ->
val vAvg = (mSum[vName] ?: 0.0) / mFrames
"$vName=${(vAvg * 100).toInt() / 100.0}/${((mMax[vName] ?: 0.0) * 100).toInt() / 100.0}ms"
}
val vLine = "[profile] frames=$fFrames avg/max " + vParts.joinToString(" ") + "\n"
val vFile = platform.posix.fopen(fPath, "a")
val vLine = "[profile] frames=$mFrames avg/max " + vParts.joinToString(" ") + "\n"
val vFile = platform.posix.fopen(mPath, "a")
if (vFile != null) {
platform.posix.fputs(vLine, vFile)
platform.posix.fclose(vFile)
}
fSum.clear(); fMax.clear()
fFrames = 0
fLastPrintMs = vNowMs
mSum.clear(); mMax.clear()
mFrames = 0
mLastPrintMs = vNowMs
}
}
}
@@ -0,0 +1,83 @@
@file:OptIn(
androidx.compose.ui.InternalComposeUiApi::class,
androidx.compose.runtime.InternalComposeApi::class,
)
package com.compose.sdl
import kotlinx.cinterop.COpaquePointer
import kotlinx.cinterop.reinterpret
import kotlinx.cinterop.pointed
import kotlinx.coroutines.CancellationException
import sdl3.SDL_GetCurrentDisplayMode
import sdl3.SDL_GetDisplayForWindow
import sdl3.SDL_GetTicks
import sdl3.SDL_GetTicksNS
// ==================
// MARK: Deterministic frame timing + probe/screenshot support
// ==================
/** Set BEFORE nativeComposeApp/nativeComposeWindow: every window's composition then runs
under an InfiniteAnimationPolicy that CANCELS infinite animations, so
rememberInfiniteTransition & co. freeze at their initial value - the same mechanism
upstream's test rules use. Screenshot/parity runs enable it so looping screens can
reach quiescence and capture deterministically. */
var disableInfiniteAnimations: Boolean = false
/** The cancelling policy: a coroutine that ends in CancellationException counts as
cancelled (not failed), so only the animation coroutine stops - nothing propagates. */
internal object CancelInfiniteAnimationsPolicy : androidx.compose.ui.platform.InfiniteAnimationPolicy {
override suspend fun <R> onInfiniteOperation(block: suspend () -> R): R =
throw CancellationException("infinite animations are disabled (disableInfiniteAnimations)")
}
/** Set BEFORE nativeComposeApp/nativeComposeWindow: the composition + animation frame
clocks advance a VIRTUAL 16.6ms per main-loop iteration instead of reading SDL's
real-time ticks - the native mirror of the JVM parity leg's render(nanos) stepping.
Animations then progress by exact per-frame deltas, so anything time-raced (e.g. a
bring-into-view scroll interrupted mid-flight) resolves identically on every run and
screenshots become deterministic. Input timestamps and FPS stay on real time. */
var useVirtualFrameTime: Boolean = false
private var virtualFrameNanos = 0L
internal fun advanceVirtualFrame() {
if (useVirtualFrameTime) virtualFrameNanos += 16_666_667L
}
// Timestamp for the composition frame clocks (recomposer + withFrameNanos animations).
internal fun frameClockNanos(): Long =
if (useVirtualFrameTime) virtualFrameNanos else SDL_GetTicksNS().toLong()
// Timestamp for the owner's node-animation clock - real path keeps the pre-existing
// ms-resolution SDL_GetTicks base so non-screenshot behaviour is bit-for-bit unchanged.
internal fun animationClockNanos(): Long =
if (useVirtualFrameTime) virtualFrameNanos else SDL_GetTicks().toLong() * 1_000_000L
// The window currently inside renderFrame - the loop is single-threaded, so a plain
// var is enough for onFrame probes to address "the window I'm being called for".
internal var renderingWindow: WindowInstance? = null
/** From inside an onFrame callback: true while the just-rendered window still has pending
work (recomposition, layout/draw invalidation, or an animation awaiting the next frame).
Screenshot probes capture once this stays false for a few consecutive frames instead of
at a fixed frame count. Outside onFrame it answers false. */
fun windowHasInvalidations(): Boolean = renderingWindow?.hasInvalidations() ?: false
/** Trigger a Kotlin/Native GC so Cleaner-managed renderer resources release
their native memory (see the main loop's native-memory nudge). */
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class)
internal fun collectNativeGarbage() = kotlin.native.runtime.GC.collect()
/** Frame interval (ms) of the window's current display, for the non-vsync
fallback pacing (SDL_Delay). Falls back to 16ms (~60Hz) when the mode can't
be read, so a 144Hz panel on a non-vsync path isn't capped to 60. */
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
internal fun displayFrameDelayMs(window: COpaquePointer?): UInt {
val vWindow = window ?: return 16u
val vDisplay = SDL_GetDisplayForWindow(vWindow.reinterpret())
val vMode = SDL_GetCurrentDisplayMode(vDisplay) ?: return 16u
val vHz = vMode.pointed.refresh_rate
return if (vHz > 0f) (1000f / vHz).toUInt().coerceAtLeast(1u) else 16u
}
@@ -20,7 +20,7 @@ import sdl3.SDL_GetTicksNS
Both come from delegating to the runtime's BroadcastFrameClock, which is
exactly upstream's dispatch structure. */
internal class SDL3FrameClock : MonotonicFrameClock {
internal class Sdl3FrameClock : MonotonicFrameClock {
private val broadcast = BroadcastFrameClock()
override suspend fun <R> withFrameNanos(onFrame: (Long) -> R): R =
@@ -36,16 +36,16 @@ import sdl3.SDL_GetCurrentThreadID
writes land at the loop's drainPending(), observable on the same frame. */
internal class Sdl3MainDispatcher : MainCoroutineDispatcher() {
private val fQueue = Channel<Runnable>(Channel.UNLIMITED)
private val mQueue = Channel<Runnable>(Channel.UNLIMITED)
// The SDL main thread - the dispatcher is constructed by nativeComposeApp
// on the thread that then runs the loop.
private val fMainThreadId = SDL_GetCurrentThreadID()
private val mMainThreadId = SDL_GetCurrentThreadID()
override val immediate: MainCoroutineDispatcher = ImmediateDispatcher()
override fun dispatch(context: CoroutineContext, block: Runnable) {
fQueue.trySend(block)
mQueue.trySend(block)
}
/** Drains all pending tasks. Called from the SDL3 main loop once per
@@ -54,7 +54,7 @@ internal class Sdl3MainDispatcher : MainCoroutineDispatcher() {
composition. */
fun drainPending() {
while (true) {
val vTask = fQueue.tryReceive().getOrNull() ?: break
val vTask = mQueue.tryReceive().getOrNull() ?: break
runCatching { vTask.run() }.onFailure { vErr ->
println("Sdl3MainDispatcher: dispatched task threw: ${vErr.message}")
}
@@ -64,7 +64,7 @@ internal class Sdl3MainDispatcher : MainCoroutineDispatcher() {
/** Closes the queue. After this, dispatch() drops new posts silently.
Called from nativeComposeWindow as part of shutdown. */
fun close() {
fQueue.close()
mQueue.close()
}
override fun toString(): String = "Sdl3MainDispatcher"
@@ -75,10 +75,10 @@ internal class Sdl3MainDispatcher : MainCoroutineDispatcher() {
override val immediate: MainCoroutineDispatcher get() = this
override fun isDispatchNeeded(context: CoroutineContext): Boolean =
SDL_GetCurrentThreadID() != fMainThreadId
SDL_GetCurrentThreadID() != mMainThreadId
override fun dispatch(context: CoroutineContext, block: Runnable) {
fQueue.trySend(block)
mQueue.trySend(block)
}
override fun toString(): String = "Sdl3MainDispatcher.immediate"
@@ -0,0 +1,31 @@
package com.compose.sdl
// ==================
// MARK: WindowAttributes
// ==================
/**
The Compose Desktop `Window()` attributes this port supports, bundled so they
travel as one value from the composable down to [WindowInstance] instead of as
six positional booleans.
[transparent] is creation-only: SDL needs "SDL_WINDOW_TRANSPARENT" at
"SDL_CreateWindow" time and offers no setter, so changing it after the window
exists has no effect. The rest are re-applied whenever "Window()" recomposes
with a new value.
*/
internal data class WindowAttributes(
/** Hides the title bar and border (`SDL_WINDOW_BORDERLESS`). */
val undecorated: Boolean = false,
/** Per-pixel alpha for the window surface. CREATION-ONLY - see above. */
val transparent: Boolean = false,
/** Whether the user can resize the window. */
val resizable: Boolean = true,
/** False drops input (pointer / key / text / wheel / drop) while the window
keeps rendering - Compose Desktop's `enabled = false` behaviour. */
val enabled: Boolean = true,
/** False stops the window taking keyboard focus when clicked or raised. */
val focusable: Boolean = true,
/** Keeps the window above other windows. */
val alwaysOnTop: Boolean = false,
)
@@ -0,0 +1,575 @@
@file:OptIn(
androidx.compose.ui.InternalComposeUiApi::class,
androidx.compose.runtime.InternalComposeApi::class,
)
package com.compose.sdl
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composition
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.Recomposer
import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.pointer.PointerButton
import androidx.compose.ui.input.pointer.PointerEventType
import com.compose.sdl.node.ComposeRootHost
import com.compose.sdl.res.currentImageLoader
import com.compose.sdl.window.LocalPopupHost
import com.compose.sdl.window.PopupLayer
import com.compose.sdl.window.createPopupHostState
import kotlinx.cinterop.alloc
import kotlinx.cinterop.ptr
import kotlinx.cinterop.reinterpret
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import sdl3.SDL_GetTicks
import sdl3.SDL_SetWindowTitle
// ==================
// MARK: WindowInstance - one SDL window + renderer + composition
// ==================
internal class WindowInstance(
private val initialTitle: String,
inWidth: Int,
inHeight: Int,
inGpu: GpuMode,
inIcon: AppWindowIcon?,
private val attrs: WindowAttributes,
private val onFrame: ((RenderBackend, Int) -> Boolean)?,
// Window-level key handlers, read through holders so Window() recompositions
// with new lambdas take effect without recreating the window.
private val previewKeyHandler: () -> ((KeyEvent) -> Boolean),
private val keyHandler: () -> ((KeyEvent) -> Boolean),
private val contentHolder: () -> (@Composable ComposeWindowScope.() -> Unit),
) {
// Resolved renderer mode. `var` so init() can fall back GPU → CPU raster when
// a Skia GPU context can't be created (RDP / headless / missing GL driver).
private var gpuMode = if (inGpu is GpuMode.Auto) rendererPreferredGpuMode() else inGpu
private val initialWidth = inWidth
private val initialHeight = inHeight
private val icon = inIcon
// Assigned by init() from the first backend that comes up (see makeBackend()).
lateinit var backend: Sdl3Backend
private set
private var renderBackend: RenderBackend? = null
lateinit var host: ComposeRootHost
private set
lateinit var facade: ComposeNativeWindow
private set
private var popupHost: com.compose.sdl.window.PopupHostState? = null
val frameClock = Sdl3FrameClock()
private var recomposer: Recomposer? = null
private var recomposeJob: Job? = null
private var composition: Composition? = null
// Set by Window(); the compat wrapper points it at exitApplication().
var onCloseRequest: () -> Unit = {}
var closeDispatched = false
// Render-on-demand flag (see the pre-multi-window loop): set by events,
// state writes (global observer), resize, expose.
var needsFrame = true
// Hover refresh: last pointer position in PIXEL space, re-dispatched as a
// synthetic Move after layout so items scrolled under the cursor hover.
private var lastMouseX = -1f
private var lastMouseY = -1f
private var hasMousePos = false
private var frameIndex = 0
private var fpsEma = 0.0
private var fpsLastFrameMs = SDL_GetTicks()
private var fpsLastTitleMs = 0uL
// Escape → back (upstream desktop's BackNavigationEventInput): unconsumed
// Escape completes a back navigation on THIS window's dispatcher.
private val navigationEventOwner = object : androidx.navigationevent.NavigationEventDispatcherOwner {
override val navigationEventDispatcher = androidx.navigationevent.NavigationEventDispatcher()
}
private val backNavigationInput = BackNavigationInput()
// WINDOW-scoped architecture-components owner (Lifecycle + ViewModelStore +
// SavedStateRegistry) - the same trio upstream desktop's
// DefaultArchitectureComponentsOwner supplies through the window
// PlatformContext (compose/ui skikoMain PlatformOwnerProvider.skiko.kt).
// viewModel(), SavedStateHandle plumbing and navigation3's
// rememberViewModelStoreNavEntryDecorator all resolve their parent owners
// from this; destroy() moves it to DESTROYED and clears the store.
private val architectureOwner = WindowArchitectureOwner()
// Window focus / visibility → Lifecycle.State, Compose Desktop's mapping:
// focused → RESUMED, visible unfocused → STARTED, hidden/minimised →
// CREATED. Starts focused+visible (SDL fires FOCUS_GAINED right after
// creation anyway; headless probe runs simply stay RESUMED).
private var windowFocused = true
private var windowVisible = true
fun onActivationEvent(inEvent: AppEvent.WindowActivation) {
inEvent.focused?.let { windowFocused = it }
inEvent.visible?.let { windowVisible = it }
if (::host.isInitialized) host.setWindowFocused(windowFocused)
architectureOwner.setLifecycleState(
when {
!windowVisible -> androidx.lifecycle.Lifecycle.State.CREATED
windowFocused -> androidx.lifecycle.Lifecycle.State.RESUMED
else -> androidx.lifecycle.Lifecycle.State.STARTED
}
)
// Shown / restored / focus-gained also invalidate the contents - keep
// the pre-lifecycle RedrawNeeded behaviour of these SDL events.
needsFrame = true
}
// Creates + initialises an Sdl3Backend for [mode] and its RenderBackend. On
// success commits `backend` and returns the renderer; else tears everything
// down and returns null so the caller can retry with a different mode.
private fun makeBackend(mode: GpuMode): RenderBackend? {
val vBackend = Sdl3Backend(
initialTitle, initialWidth, initialHeight, gpuMode = mode,
iconLightResourcePaths = icon?.light ?: emptyList(),
iconDarkResourcePaths = icon?.dark ?: emptyList(),
undecorated = attrs.undecorated,
transparent = attrs.transparent,
resizable = attrs.resizable,
alwaysOnTop = attrs.alwaysOnTop,
focusable = attrs.focusable,
)
if (!vBackend.init()) {
vBackend.destroy(inQuitSdl = false)
return null
}
vBackend.updateWindowSize()
val vRender = createRenderBackend(vBackend, mode)
if (vRender == null || !vRender.ensureSize(vBackend.pixelWidth, vBackend.pixelHeight)) {
vRender?.destroy()
vBackend.destroy(inQuitSdl = false)
return null
}
backend = vBackend
return vRender
}
fun init(inScope: CoroutineScope): Boolean {
// Try the resolved gpuMode; if it's a Skia GPU mode whose context/bridge
// can't come up (RDP / headless / missing GL driver), fall back once to
// CPU raster (Software) so the window still opens instead of failing.
var vRender = makeBackend(gpuMode)
if (vRender == null && gpuMode.isGpuAccelerated) {
println("GPU renderer ($gpuMode) unavailable - falling back to CPU raster (Software)")
gpuMode = GpuMode.CpuRaster
vRender = makeBackend(gpuMode)
}
if (vRender == null) {
println("Failed to init render backend")
return false
}
renderBackend = vRender
host = ComposeRootHost(inDensity = backend.pixelDensity)
host.attach()
// A layer whose content changed (OwnedLayer.invalidate) schedules a frame
// even when nothing else (recompose / relayout) is pending - retained layers
// need this so a draw-only state change still repaints.
host.setInvalidationCallback { needsFrame = true }
facade = ComposeNativeWindow(backend, gpuMode, initialTitle)
// Mirror the creation flags into the facade so isResizable & co. read true
// from the start and a later Window() update only fires on a real change.
applyAttributes(attrs)
navigationEventOwner.navigationEventDispatcher.addInput(backNavigationInput)
// The render-bridge globals must point at THIS window's renderer while
// its content composes/measures (first composition happens inside
// setContent below).
installGlobals()
// Effect context for this window's composition - the screenshot flag injects the
// infinite-animation-cancelling policy (see disableInfiniteAnimations above).
var vEffectContext = inScope.coroutineContext + frameClock
if (disableInfiniteAnimations) vEffectContext += CancelInfiniteAnimationsPolicy
val vRecomposer = Recomposer(vEffectContext)
recomposer = vRecomposer
recomposeJob = inScope.launch(frameClock) { vRecomposer.runRecomposeAndApplyChanges() }
val vComposition = Composition(host.applier, vRecomposer)
composition = vComposition
val vUriHandler = object : androidx.compose.ui.platform.UriHandler {
override fun openUri(uri: String) { openUrl(uri) }
}
val vPopupHost = createPopupHostState()
popupHost = vPopupHost
val vWindowScope = object : ComposeWindowScope {
override val window: ComposeNativeWindow = facade
}
vComposition.setContent {
CompositionLocalProvider(
LocalComposeNativeWindow provides facade,
LocalPopupHost provides vPopupHost,
// Upstream vendored CompositionLocals.kt declares each of these as
// `staticCompositionLocalOf<T> { noLocalProvidedFor("…") }` - reading
// one without a Provider throws. Seed them all from the ComposeOwner.
androidx.compose.ui.platform.LocalDensity provides host.density,
androidx.compose.ui.platform.LocalLayoutDirection provides host.layoutDirection,
androidx.compose.ui.platform.LocalFocusManager provides host.focusManager,
androidx.compose.ui.platform.LocalGraphicsContext provides host.graphicsContext,
androidx.compose.ui.platform.LocalViewConfiguration provides host.viewConfiguration,
androidx.compose.ui.platform.LocalInputModeManager provides host.inputModeManager,
androidx.compose.ui.platform.LocalHapticFeedback provides host.hapticFeedback,
androidx.compose.ui.platform.LocalTextToolbar provides host.textToolbar,
androidx.compose.ui.platform.LocalWindowInfo provides host.windowInfo,
androidx.compose.ui.platform.LocalSoftwareKeyboardController provides host.softwareKeyboardController,
androidx.compose.ui.platform.LocalClipboard provides
androidx.compose.ui.platform.platformClipboard(),
androidx.compose.ui.platform.LocalFontFamilyResolver provides
com.compose.sdl.text.font.projectFontFamilyResolver,
androidx.compose.ui.platform.LocalUriHandler provides vUriHandler,
// Desktop windows have no system bars / notch / IME insets - the
// interface's all-zero defaults are exactly right.
androidx.compose.ui.platform.LocalPlatformWindowInsets provides
object : androidx.compose.ui.platform.PlatformWindowInsets {},
// The window's architecture-components owner backs all three arch
// locals, exactly like upstream desktop's window PlatformContext:
// - LocalLifecycleOwner: RESUMED for the window's whole life;
// lifecycle-aware content (NavDisplay, rememberLifecycleOwner, …)
// errors without it.
// - LocalViewModelStoreOwner: window-scoped ViewModels (google
// lifecycle-viewmodel-compose's local is a plain composition
// local; the JB HostDefault route doesn't exist in the google
// artifacts this port ships).
// - LocalSavedStateRegistryOwner: SavedStateHandle / rememberSaveable
// registry parent (nav3's ViewModelStoreNavEntryDecorator requires it).
androidx.lifecycle.compose.LocalLifecycleOwner provides architectureOwner,
androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner provides architectureOwner,
androidx.savedstate.compose.LocalSavedStateRegistryOwner provides architectureOwner,
// Runtime-level host defaults - this window's navigation-event
// owner (SearchBar / sheets back plumbing). The viewmodel store
// is provided through the plain LocalViewModelStoreOwner above
// instead: the google lifecycle artifacts this port ships have no
// HostDefault key for it (that mechanism is JB-variant-only).
androidx.compose.runtime.LocalHostDefaultProvider provides
remember {
object : androidx.compose.runtime.HostDefaultProvider {
@Suppress("UNCHECKED_CAST")
override fun <T> getHostDefault(key: androidx.compose.runtime.HostDefaultKey<T>): T = when (key) {
androidx.navigationevent.compose.NavigationEventDispatcherOwnerHostDefaultKey -> navigationEventOwner
else -> null
} as T
}
},
) {
// Seed the :ui-internal LocalPointerIconService so Modifier.pointerHoverIcon
// drives the SDL cursor (the local + service type can't cross the module boundary).
host.ProvidePointerIconService {
Box(modifier = Modifier.fillMaxSize()) {
// Read through the holder so Window() recompositions with a
// new content lambda propagate into this composition.
val vContent = contentHolder()
with(vWindowScope) { vContent() }
PopupLayer(vPopupHost)
}
}
}
}
// First composition done (setContent is synchronous) - promote the
// window lifecycle from CREATED to the focus/visibility-derived state.
// Composition itself runs at CREATED so enableSavedStateHandles()
// callers see a legal state, mirroring upstream desktop's
// compose-first-resume-after ordering.
architectureOwner.setLifecycleState(
when {
!windowVisible -> androidx.lifecycle.Lifecycle.State.CREATED
windowFocused -> androidx.lifecycle.Lifecycle.State.RESUMED
else -> androidx.lifecycle.Lifecycle.State.STARTED
}
)
return true
}
/** Points the render-bridge globals (text measurer / image loader /
viewport) at this window's renderer. Must be called before composing,
measuring, or drawing this window's tree. */
fun installGlobals() {
val vRender = renderBackend ?: return
currentImageLoader = vRender.imageLoader
com.compose.sdl.text.currentViewportWidth = backend.pixelWidth
com.compose.sdl.text.currentViewportHeight = backend.pixelHeight
// Register bundled generic fonts (FontFamily.Monospace → NotoSansMono) once
// data.kres is loadable; idempotent, no-op if the font isn't bundled.
com.compose.sdl.text.registerGenericFonts()
// Position the OS IME candidate window the moment a field in THIS window
// gains focus, not only on the first TEXT_EDITING event (repointed per
// window here so multi-window focus targets the right SDL window).
com.compose.sdl.text.input.ImeBridge.onSessionActiveChange = { active ->
if (active) updateImeArea()
}
}
// ============
// Event handling (loop-routed, per window)
fun onPointerEvent(inEvent: AppEvent.Pointer) {
if (!facade.isEnabled) return
needsFrame = true
installGlobals()
val vType = when (inEvent.type) {
PointerEventType.Press -> 1
PointerEventType.Release -> 2
else -> 0
}
val vBtn = when (inEvent.button) {
PointerButton.Secondary -> 1
PointerButton.Tertiary -> 2
else -> 0
}
// SDL3 delivers mouse coords in logical points on HiDPI - multiply by
// DPR so hit-testing lands in the pixel space layout uses.
val vDpr = backend.pixelDensity
val vPx = inEvent.x * vDpr
val vPy = inEvent.y * vDpr
if (inEvent.type == PointerEventType.Press) {
popupHost?.notifyOutsidePress(vPx.toInt(), vPy.toInt())
}
host.onPointerRaw(vPx, vPy, vType, vBtn, SDL_GetTicks().toLong())
lastMouseX = vPx
lastMouseY = vPy
hasMousePos = true
}
/** Pointer left the window (SDL_EVENT_WINDOW_MOUSE_LEAVE). Stop the per-frame
synthetic hover re-dispatch and fire one Exit at the last position so a
hovered widget clears its highlight instead of sticking forever. */
fun onPointerExit() {
if (!facade.isEnabled) return
needsFrame = true
if (hasMousePos) {
host.onPointerRaw(lastMouseX, lastMouseY, 3, 0, SDL_GetTicks().toLong())
}
hasMousePos = false
}
fun onWheelEvent(inEvent: AppEvent.MouseWheel) {
if (!facade.isEnabled) return
needsFrame = true
installGlobals()
val vDpr = backend.pixelDensity
host.onWheel(inEvent.x * vDpr, inEvent.y * vDpr, inEvent.deltaX, inEvent.deltaY, SDL_GetTicks().toLong())
}
fun onKeyEvent(inEvent: AppEvent.Key) {
if (!facade.isEnabled) return
needsFrame = true
installGlobals()
// Compose Desktop's order: window preview → focused content → window
// onKeyEvent → app shortcut handler → back navigation (Escape).
if (previewKeyHandler().invoke(inEvent.event)) return
if (host.dispatchKeyEvent(inEvent.event)) return
if (keyHandler().invoke(inEvent.event)) return
if (!facade.dispatchKeyShortcut(inEvent.event)) {
backNavigationInput.onKeyEvent(inEvent.event)
}
}
/** Applies the mutable window attributes. Called at init from the creation
flags and again whenever Window() sees one of them change. */
fun applyAttributes(inAttrs: WindowAttributes) {
facade.setUndecorated(inAttrs.undecorated)
facade.setResizable(inAttrs.resizable)
facade.setAlwaysOnTop(inAttrs.alwaysOnTop)
facade.setFocusable(inAttrs.focusable)
facade.setEnabled(inAttrs.enabled)
}
fun onTextInputEvent(inEvent: AppEvent.TextInput) {
if (!facade.isEnabled) return
needsFrame = true
installGlobals()
// A focused text field runs an IME session (ComposeOwner.textInputSession):
// commit through it so the text REPLACES any active composition. Falls back
// to synthesising typed KeyEvents when no field is focused - SDL key events
// carry UNSHIFTED keycodes (no uppercase/numpad/dead keys), so committed
// text is the only layout-correct character source.
if (!com.compose.sdl.text.input.ImeBridge.commit(inEvent.text)) {
dispatchTypedText(host, inEvent.text)
}
}
fun onTextEditingEvent(inEvent: AppEvent.TextEditing) {
if (!facade.isEnabled) return
needsFrame = true
installGlobals()
// IME preedit: show the composing (underlined) region in the focused field.
// No-op if no field is focused. Also nudge the OS candidate window to the
// field's on-screen rect.
com.compose.sdl.text.input.ImeBridge.compose(inEvent.text)
updateImeArea()
}
// Nudge the OS IME candidate window to the focused field's on-screen rect.
// Best-effort: no-op without an active session or a laid-out field.
@OptIn(androidx.compose.ui.ExperimentalComposeUiApi::class, kotlinx.cinterop.ExperimentalForeignApi::class)
private fun updateImeArea() {
val vRequest = com.compose.sdl.text.input.ImeBridge.request ?: return
val vRect = vRequest.focusedRectInRoot() ?: return
val vWindow = backend.window ?: return
val vDpr = backend.pixelDensity
kotlinx.cinterop.memScoped {
// SDL wants the area in logical window points; layout runs in physical px.
val vSdlRect = alloc<sdl3.SDL_Rect>()
vSdlRect.x = (vRect.left / vDpr).toInt()
vSdlRect.y = (vRect.top / vDpr).toInt()
vSdlRect.w = (vRect.width / vDpr).toInt().coerceAtLeast(1)
vSdlRect.h = (vRect.height / vDpr).toInt().coerceAtLeast(1)
sdl3.SDL_SetTextInputArea(vWindow.reinterpret(), vSdlRect.ptr, 0)
}
}
fun onDropEvent(inEvent: AppEvent.Drop) {
if (!facade.isEnabled) return
needsFrame = true
installGlobals()
// SDL fires drop coords in logical points at DPR-1; scale to the pixel
// space layout runs in (Option-B density flow) so hit-testing lands
// where the pointer is.
val vDpr = backend.pixelDensity
when (inEvent.phase) {
AppEvent.DropPhase.BEGIN -> host.onDropBegin()
AppEvent.DropPhase.POSITION -> host.onDropPosition(inEvent.x * vDpr, inEvent.y * vDpr)
AppEvent.DropPhase.FILE -> inEvent.data?.let { host.onDropFile(it) }
AppEvent.DropPhase.TEXT -> inEvent.data?.let { host.onDropText(it) }
AppEvent.DropPhase.COMPLETE -> host.onDropComplete()
}
}
fun onResizedEvent() {
needsFrame = true
backend.updateWindowSize()
facade.onResized()
}
/** OS light/dark theme changed - re-pick the theme-appropriate window icon
(no-op if this window has no icon configured or the choice is unchanged). */
fun onSystemThemeChanged() {
backend.applyThemeIcon()
}
/** OS close button / app-level Quit: honour the veto handler, then hand the
decision to the Window() declaration. */
fun requestClose() {
if (closeDispatched) return
if (facade.requestCloseFromUser()) {
closeDispatched = true
onCloseRequest()
}
}
// ============
// Frame pump
fun shouldRender(): Boolean =
needsFrame || (recomposer?.hasPendingWork == true) || onFrame != null || kForceRender
/** True while this window still has pending work: a state/layout/draw invalidation
(needsFrame), recomposer work, or a composition/node animation awaiting the next
frame. Sampled by windowHasInvalidations() from onFrame probes - the quiescence
signal for render-to-settle screenshot capture. */
fun hasInvalidations(): Boolean =
needsFrame || recomposer?.hasPendingWork == true ||
frameClock.hasAwaiters || host.hasAnimationAwaiters()
fun renderFrame() {
val vRender = renderBackend ?: return
needsFrame = false
if (recomposer?.hasPendingWork == true) needsFrame = true
backend.updateWindowSize()
vRender.ensureSize(backend.pixelWidth, backend.pixelHeight)
// Charge the GPU back-buffer acquire to its own phase. On Metal this is
// where nextDrawable() blocks on vsync (the natural frame pacing), so
// folding it into "layout" made the profiler read the vsync wait as
// layout cost - a ~6-7ms phantom that hid the real (tiny) layout time.
FrameProfiler.phase(" acquire")
host.setConstraints(backend.pixelWidth, backend.pixelHeight)
// Deliver any state written by this iteration's frame-clock continuations
// (withFrameNanos animations - notably smooth wheel scrolling) BEFORE we lay
// out. Those writes happen in the per-window pump's sendFrame()/yield(), which
// is AFTER the loop's last sendApplyNotifications() - so without this the
// relayout they trigger wasn't registered until the next frame, and the frame
// drew last frame's positions. That one-frame trail read as "the clip is a
// frame late" when scrolling (content briefly drawn past the viewport edge).
Snapshot.sendApplyNotifications()
host.measureAndLayout()
FrameProfiler.phase(" layout")
// Hover refresh after layout (upstream skiko: SyntheticEventSender).
if (hasMousePos) {
host.onPointerRaw(lastMouseX, lastMouseY, 0, 0, SDL_GetTicks().toLong())
}
vRender.beginFrame(1f)
vRender.drawRoot { canvas -> host.drawRoot(canvas) }
if (onFrame != null) {
renderingWindow = this
val vContinue = onFrame.invoke(vRender, frameIndex)
renderingWindow = null
// Probe consumers end the app when their scenario completes.
if (!vContinue) facade.close()
}
FrameProfiler.phase(" draw")
vRender.endFrame()
FrameProfiler.phase(" present")
frameIndex++
// FPS - instantaneous inter-frame rate, EMA-smoothed, title refreshed
// ~4x/sec. This shows within ~2 rendered frames of ANY activity rather
// than waiting for a full second of unbroken rendering to accumulate
// (the old fixed-window counter never got its first update: bursty
// interaction kept idling before 1s elapsed, and the idle-skip reset the
// window each time - so FPS only appeared during a long enough page
// transition). A dt outside 1..100ms is the first frame or a resume from
// idle (the gap isn't a real frame interval), so it's not sampled - the
// title just holds the last active rate while idle.
val vNowMs = SDL_GetTicks()
val vDt = (vNowMs - fpsLastFrameMs).toInt()
fpsLastFrameMs = vNowMs
if (vDt in 1..100) {
val vInst = 1000.0 / vDt
fpsEma = if (fpsEma <= 0.0) vInst else fpsEma * 0.9 + vInst * 0.1
if ((vNowMs - fpsLastTitleMs).toInt() >= 250) {
val vFps = (fpsEma + 0.5).toInt()
facade.updateFps(vFps)
SDL_SetWindowTitle(backend.window?.reinterpret(), "${facade.title} · $vFps FPS")
fpsLastTitleMs = vNowMs
}
}
}
// Idle-skip calls this: drop the frame timer so the resume-from-idle frame's
// dt (the whole idle gap) isn't sampled as a real interval. The EMA is left
// intact so the title keeps showing the last active rate while idle.
fun resetFpsWindow() {
fpsLastFrameMs = SDL_GetTicks()
}
fun destroy() {
composition?.dispose()
composition = null
// Window gone → DESTROYED lifecycle + ViewModels cleared (onCleared
// runs), matching upstream desktop's window-scoped owner lifetime.
architectureOwner.destroy()
recomposer?.cancel()
recomposer = null
recomposeJob?.cancel()
recomposeJob = null
renderBackend?.destroy()
renderBackend = null
backend.destroy(inQuitSdl = false)
}
}
@@ -2090,13 +2090,6 @@ final object androidx.compose.foundation/MarqueeDefaults { // androidx.compose.f
final fun <get-Velocity>(): androidx.compose.ui.unit/Dp // androidx.compose.foundation/MarqueeDefaults.Velocity.<get-Velocity>|<get-Velocity>(){}[0]
}
final object com.compose.sdl.icons/IconDefaults { // com.compose.sdl.icons/IconDefaults|null[0]
final val DefaultIconSize // com.compose.sdl.icons/IconDefaults.DefaultIconSize|{}DefaultIconSize[0]
final fun <get-DefaultIconSize>(): androidx.compose.ui.unit/Dp // com.compose.sdl.icons/IconDefaults.DefaultIconSize.<get-DefaultIconSize>|<get-DefaultIconSize>(){}[0]
final val LocalContentColor // com.compose.sdl.icons/IconDefaults.LocalContentColor|{}LocalContentColor[0]
final fun <get-LocalContentColor>(): androidx.compose.ui.graphics/Color // com.compose.sdl.icons/IconDefaults.LocalContentColor.<get-LocalContentColor>|<get-LocalContentColor>(){}[0]
}
final object com.compose.sdl.widgets/SplitPaneDefaults { // com.compose.sdl.widgets/SplitPaneDefaults|null[0]
final val DividerThickness // com.compose.sdl.widgets/SplitPaneDefaults.DividerThickness|{}DividerThickness[0]
final fun <get-DividerThickness>(): androidx.compose.ui.unit/Dp // com.compose.sdl.widgets/SplitPaneDefaults.DividerThickness.<get-DividerThickness>|<get-DividerThickness>(){}[0]
@@ -2233,7 +2226,6 @@ final val androidx.compose.foundation/androidx_compose_foundation_MarqueeDefault
final val androidx.compose.foundation/androidx_compose_foundation_MutatorMutex$stableprop // androidx.compose.foundation/androidx_compose_foundation_MutatorMutex$stableprop|#static{}androidx_compose_foundation_MutatorMutex$stableprop[0]
final val androidx.compose.foundation/androidx_compose_foundation_ScrollState$stableprop // androidx.compose.foundation/androidx_compose_foundation_ScrollState$stableprop|#static{}androidx_compose_foundation_ScrollState$stableprop[0]
final val androidx.compose.foundation/androidx_compose_foundation_ScrollbarStyle$stableprop // androidx.compose.foundation/androidx_compose_foundation_ScrollbarStyle$stableprop|#static{}androidx_compose_foundation_ScrollbarStyle$stableprop[0]
final val com.compose.sdl.icons/com_compose_sdl_icons_IconDefaults$stableprop // com.compose.sdl.icons/com_compose_sdl_icons_IconDefaults$stableprop|#static{}com_compose_sdl_icons_IconDefaults$stableprop[0]
final val com.compose.sdl.widgets/com_compose_sdl_widgets_SplitPaneDefaults$stableprop // com.compose.sdl.widgets/com_compose_sdl_widgets_SplitPaneDefaults$stableprop|#static{}com_compose_sdl_widgets_SplitPaneDefaults$stableprop[0]
final fun (androidx.compose.foundation.content/TransferableContent).androidx.compose.foundation.content/hasMediaType(androidx.compose.foundation.content/MediaType): kotlin/Boolean // androidx.compose.foundation.content/hasMediaType|hasMediaType@androidx.compose.foundation.content.TransferableContent(androidx.compose.foundation.content.MediaType){}[0]
@@ -2651,7 +2643,6 @@ final fun androidx.compose.foundation/rememberScrollbarAdapter(androidx.compose.
final fun androidx.compose.foundation/rememberScrollbarAdapter(androidx.compose.foundation.text/TextFieldScrollState, androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.foundation.v2/ScrollbarAdapter // androidx.compose.foundation/rememberScrollbarAdapter|rememberScrollbarAdapter(androidx.compose.foundation.text.TextFieldScrollState;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun androidx.compose.foundation/rememberScrollbarAdapter(androidx.compose.foundation/ScrollState, androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.foundation.v2/ScrollbarAdapter // androidx.compose.foundation/rememberScrollbarAdapter|rememberScrollbarAdapter(androidx.compose.foundation.ScrollState;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun com.compose.sdl.icons/IconFontIcon(kotlin/Int, kotlin/String, kotlin/String?, androidx.compose.ui/Modifier?, androidx.compose.ui.graphics/Color, androidx.compose.ui.unit/Dp, kotlin.collections/List<androidx.compose.ui.text.font/FontVariation.Setting>?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.compose.sdl.icons/IconFontIcon|IconFontIcon(kotlin.Int;kotlin.String;kotlin.String?;androidx.compose.ui.Modifier?;androidx.compose.ui.graphics.Color;androidx.compose.ui.unit.Dp;kotlin.collections.List<androidx.compose.ui.text.font.FontVariation.Setting>?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun com.compose.sdl.icons/com_compose_sdl_icons_IconDefaults$stableprop_getter(): kotlin/Int // com.compose.sdl.icons/com_compose_sdl_icons_IconDefaults$stableprop_getter|com_compose_sdl_icons_IconDefaults$stableprop_getter(){}[0]
final fun com.compose.sdl.modifier/rememberMutableInteractionSource(androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.foundation.interaction/MutableInteractionSource // com.compose.sdl.modifier/rememberMutableInteractionSource|rememberMutableInteractionSource(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun com.compose.sdl.text/IconText(kotlin/String, kotlin/String, androidx.compose.ui/Modifier?, androidx.compose.ui.graphics/Color, androidx.compose.ui.unit/TextUnit, androidx.compose.ui.text.style/TextAlign, kotlin.collections/List<androidx.compose.ui.text.font/FontVariation.Setting>?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.compose.sdl.text/IconText|IconText(kotlin.String;kotlin.String;androidx.compose.ui.Modifier?;androidx.compose.ui.graphics.Color;androidx.compose.ui.unit.TextUnit;androidx.compose.ui.text.style.TextAlign;kotlin.collections.List<androidx.compose.ui.text.font.FontVariation.Setting>?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun com.compose.sdl.text/IconText(kotlin/String, kotlin/String, androidx.compose.ui/Modifier?, androidx.compose.ui.graphics/Color, androidx.compose.ui.unit/TextUnit, androidx.compose.ui.text.style/TextAlign?, kotlin.collections/List<androidx.compose.ui.text.font/FontVariation.Setting>?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // com.compose.sdl.text/IconText|IconText(kotlin.String;kotlin.String;androidx.compose.ui.Modifier?;androidx.compose.ui.graphics.Color;androidx.compose.ui.unit.TextUnit;androidx.compose.ui.text.style.TextAlign?;kotlin.collections.List<androidx.compose.ui.text.font.FontVariation.Setting>?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
@@ -1,4 +1,4 @@
// VENDOR-BASE: compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/Scrollbar.skiko.kt @ v1.12.0
// VENDOR-BASE: compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/Scrollbar.skiko.kt @ v1.12.1
// MANUAL VENDOR: refused in compose-fork.txt (flat native source set). Only edit vs base is the
// K2 @file:Suppress below - identical to what sync.py injects into src/vendor .kt (this file lives
// in src/nativeMain so sync doesn't regenerate it). Needed so compileNativeMainKotlinMetadata (the
@@ -1,4 +1,4 @@
// VENDOR-BASE: compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/v2/Scrollbar.skiko.kt @ v1.12.0
// VENDOR-BASE: compose/foundation/foundation/src/skikoMain/kotlin/androidx/compose/foundation/v2/Scrollbar.skiko.kt @ v1.12.1
// MANUAL VENDOR: expect fun runBlockingIfPossible inlined to runBlocking (flat native source set can't hold expect+actual together).
// Also carries the K2 @file:Suppress below (same as sync.py injects into src/vendor) so the native metadata compile / Windows publish accepts the orphaned JVM annotation.
/*
@@ -2,7 +2,9 @@ package com.compose.sdl.icons
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontVariation
@@ -36,9 +38,21 @@ fun IconFontIcon(
size: Dp = IconDefaults.DefaultIconSize,
fontVariationSettings: List<FontVariation.Setting>? = null,
) {
Box(modifier = modifier.size(size)) {
// CENTER the glyph in the icon box. The glyph's natural text box is TALLER than
// `size` - its height is the font's ascent+descent, and Material Symbols' em box
// overshoots the nominal icon size - so the glyph used to render ~13% of the icon
// size too LOW. This mirrors the JVM actual's explicit baseline math
// (`y = height/2 - (ascent+descent)/2` in MaterialSymbolsIcon.jvm.kt): IconText
// sets no lineHeight, so its box height IS ascent+descent and centering that box
// centers the same span the JVM centers.
//
// `wrapContentSize(unbounded = true)` is load-bearing: Modifier.size() hands the
// child FIXED constraints, so without it the text is measured at exactly `size`,
// nothing overflows, and contentAlignment has nothing to align.
Box(modifier = modifier.size(size), contentAlignment = Alignment.Center) {
com.compose.sdl.text.IconText(
text = codepointToString(codepoint),
modifier = Modifier.wrapContentSize(Alignment.Center, unbounded = true),
fontFamily = fontFamily,
color = tint,
fontSize = size.value.sp,
@@ -47,7 +61,7 @@ fun IconFontIcon(
}
}
object IconDefaults {
internal object IconDefaults {
val DefaultIconSize: Dp = 24.dp
/** STATIC fallback tint for direct IconFontIcon use - :foundation cannot
read material3's LocalContentColor (module layering, same as upstream).
@@ -0,0 +1,658 @@
// Klib ABI Dump
// Targets: [linuxArm64, linuxX64, macosArm64, mingwX64]
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: true
// - Show declarations: true
// Library unique name: <com.bitsycore.compose.material3.adaptive:adaptive-layout>
final enum class androidx.compose.material3.adaptive.layout/DockedEdge : kotlin/Enum<androidx.compose.material3.adaptive.layout/DockedEdge> { // androidx.compose.material3.adaptive.layout/DockedEdge|null[0]
enum entry Bottom // androidx.compose.material3.adaptive.layout/DockedEdge.Bottom|null[0]
enum entry End // androidx.compose.material3.adaptive.layout/DockedEdge.End|null[0]
enum entry Start // androidx.compose.material3.adaptive.layout/DockedEdge.Start|null[0]
enum entry Top // androidx.compose.material3.adaptive.layout/DockedEdge.Top|null[0]
final val entries // androidx.compose.material3.adaptive.layout/DockedEdge.entries|#static{}entries[0]
final fun <get-entries>(): kotlin.enums/EnumEntries<androidx.compose.material3.adaptive.layout/DockedEdge> // androidx.compose.material3.adaptive.layout/DockedEdge.entries.<get-entries>|<get-entries>#static(){}[0]
final fun valueOf(kotlin/String): androidx.compose.material3.adaptive.layout/DockedEdge // androidx.compose.material3.adaptive.layout/DockedEdge.valueOf|valueOf#static(kotlin.String){}[0]
final fun values(): kotlin/Array<androidx.compose.material3.adaptive.layout/DockedEdge> // androidx.compose.material3.adaptive.layout/DockedEdge.values|values#static(){}[0]
}
final enum class androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole : androidx.compose.material3.adaptive.layout/PaneScaffoldRole, kotlin/Enum<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole> { // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole|null[0]
enum entry Primary // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole.Primary|null[0]
enum entry Secondary // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole.Secondary|null[0]
enum entry Tertiary // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole.Tertiary|null[0]
final val entries // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole.entries|#static{}entries[0]
final fun <get-entries>(): kotlin.enums/EnumEntries<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole> // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole.entries.<get-entries>|<get-entries>#static(){}[0]
final fun valueOf(kotlin/String): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole.valueOf|valueOf#static(kotlin.String){}[0]
final fun values(): kotlin/Array<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole> // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole.values|values#static(){}[0]
}
abstract interface androidx.compose.material3.adaptive.layout/AnimatedPaneOverride { // androidx.compose.material3.adaptive.layout/AnimatedPaneOverride|null[0]
abstract fun <#A1: androidx.compose.material3.adaptive.layout/PaneScaffoldRole, #B1: androidx.compose.material3.adaptive.layout/PaneScaffoldValue<#A1>> (androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope<#A1, #B1>).AnimatedPane(androidx.compose.runtime/Composer?, kotlin/Int) // androidx.compose.material3.adaptive.layout/AnimatedPaneOverride.AnimatedPane|AnimatedPane@androidx.compose.material3.adaptive.layout.AnimatedPaneOverrideScope<0:0,0:1>(androidx.compose.runtime.Composer?;kotlin.Int){0§<androidx.compose.material3.adaptive.layout.PaneScaffoldRole>;1§<androidx.compose.material3.adaptive.layout.PaneScaffoldValue<0:0>>}[0]
}
abstract interface androidx.compose.material3.adaptive.layout/PaneScaffoldRole // androidx.compose.material3.adaptive.layout/PaneScaffoldRole|null[0]
abstract interface androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverride { // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverride|null[0]
abstract fun (androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope).ThreePaneScaffold(androidx.compose.runtime/Composer?, kotlin/Int) // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverride.ThreePaneScaffold|ThreePaneScaffold@androidx.compose.material3.adaptive.layout.ThreePaneScaffoldOverrideScope(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
}
sealed interface <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole, #B: androidx.compose.material3.adaptive.layout/PaneScaffoldValue<#A>> androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldPaneScope : androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldScope<#A, #B>, androidx.compose.material3.adaptive.layout/PaneScaffoldPaneScope<#A> // androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldPaneScope|null[0]
sealed interface <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole, #B: androidx.compose.material3.adaptive.layout/PaneScaffoldValue<#A>> androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldScope : androidx.compose.material3.adaptive.layout/PaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope<#A, #B>, androidx.compose.ui.layout/LookaheadScope { // androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldScope|null[0]
abstract val focusRequesters // androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldScope.focusRequesters|{}focusRequesters[0]
abstract fun <get-focusRequesters>(): kotlin.collections/Map<#A, androidx.compose.ui.focus/FocusRequester> // androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldScope.focusRequesters.<get-focusRequesters>|<get-focusRequesters>(){}[0]
}
sealed interface <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole, #B: androidx.compose.material3.adaptive.layout/PaneScaffoldValue<#A>> androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope { // androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope|null[0]
abstract val motionDataProvider // androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope.motionDataProvider|{}motionDataProvider[0]
abstract fun <get-motionDataProvider>(): androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider<#A> // androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope.motionDataProvider.<get-motionDataProvider>|<get-motionDataProvider>(){}[0]
abstract val motionProgress // androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope.motionProgress|{}motionProgress[0]
abstract fun <get-motionProgress>(): kotlin/Float // androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope.motionProgress.<get-motionProgress>|<get-motionProgress>(){}[0]
abstract val scaffoldStateTransition // androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope.scaffoldStateTransition|{}scaffoldStateTransition[0]
abstract fun <get-scaffoldStateTransition>(): androidx.compose.animation.core/Transition<#B> // androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope.scaffoldStateTransition.<get-scaffoldStateTransition>|<get-scaffoldStateTransition>(){}[0]
}
sealed interface <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole> androidx.compose.material3.adaptive.layout/PaneScaffoldHorizontalOrder { // androidx.compose.material3.adaptive.layout/PaneScaffoldHorizontalOrder|null[0]
abstract val size // androidx.compose.material3.adaptive.layout/PaneScaffoldHorizontalOrder.size|{}size[0]
abstract fun <get-size>(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneScaffoldHorizontalOrder.size.<get-size>|<get-size>(){}[0]
abstract fun forEach(kotlin/Function1<#A, kotlin/Unit>) // androidx.compose.material3.adaptive.layout/PaneScaffoldHorizontalOrder.forEach|forEach(kotlin.Function1<1:0,kotlin.Unit>){}[0]
abstract fun forEachIndexed(kotlin/Function2<kotlin/Int, #A, kotlin/Unit>) // androidx.compose.material3.adaptive.layout/PaneScaffoldHorizontalOrder.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,1:0,kotlin.Unit>){}[0]
abstract fun forEachIndexedReversed(kotlin/Function2<kotlin/Int, #A, kotlin/Unit>) // androidx.compose.material3.adaptive.layout/PaneScaffoldHorizontalOrder.forEachIndexedReversed|forEachIndexedReversed(kotlin.Function2<kotlin.Int,1:0,kotlin.Unit>){}[0]
abstract fun indexOf(#A): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneScaffoldHorizontalOrder.indexOf|indexOf(1:0){}[0]
}
sealed interface <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole> androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider { // androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider|null[0]
abstract val count // androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider.count|{}count[0]
abstract fun <get-count>(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider.count.<get-count>|<get-count>(){}[0]
abstract val scaffoldSize // androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider.scaffoldSize|{}scaffoldSize[0]
abstract fun <get-scaffoldSize>(): androidx.compose.ui.unit/IntSize // androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider.scaffoldSize.<get-scaffoldSize>|<get-scaffoldSize>(){}[0]
abstract fun get(#A): androidx.compose.material3.adaptive.layout/PaneMotionData // androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider.get|get(1:0){}[0]
abstract fun get(kotlin/Int): androidx.compose.material3.adaptive.layout/PaneMotionData // androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider.get|get(kotlin.Int){}[0]
abstract fun getRoleAt(kotlin/Int): #A // androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider.getRoleAt|getRoleAt(kotlin.Int){}[0]
}
sealed interface <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole> androidx.compose.material3.adaptive.layout/PaneScaffoldPaneScope { // androidx.compose.material3.adaptive.layout/PaneScaffoldPaneScope|null[0]
abstract val isInteractable // androidx.compose.material3.adaptive.layout/PaneScaffoldPaneScope.isInteractable|{}isInteractable[0]
abstract fun <get-isInteractable>(): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneScaffoldPaneScope.isInteractable.<get-isInteractable>|<get-isInteractable>(){}[0]
abstract val paneMotion // androidx.compose.material3.adaptive.layout/PaneScaffoldPaneScope.paneMotion|{}paneMotion[0]
abstract fun <get-paneMotion>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneScaffoldPaneScope.paneMotion.<get-paneMotion>|<get-paneMotion>(){}[0]
abstract val paneRole // androidx.compose.material3.adaptive.layout/PaneScaffoldPaneScope.paneRole|{}paneRole[0]
abstract fun <get-paneRole>(): #A // androidx.compose.material3.adaptive.layout/PaneScaffoldPaneScope.paneRole.<get-paneRole>|<get-paneRole>(){}[0]
}
sealed interface <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole> androidx.compose.material3.adaptive.layout/PaneScaffoldValue { // androidx.compose.material3.adaptive.layout/PaneScaffoldValue|null[0]
abstract fun get(#A): androidx.compose.material3.adaptive.layout/PaneAdaptedValue // androidx.compose.material3.adaptive.layout/PaneScaffoldValue.get|get(1:0){}[0]
}
sealed interface androidx.compose.material3.adaptive.layout/AdaptStrategy { // androidx.compose.material3.adaptive.layout/AdaptStrategy|null[0]
open fun adapt(): androidx.compose.material3.adaptive.layout/PaneAdaptedValue // androidx.compose.material3.adaptive.layout/AdaptStrategy.adapt|adapt(){}[0]
final class Levitate : androidx.compose.material3.adaptive.layout/AdaptStrategy { // androidx.compose.material3.adaptive.layout/AdaptStrategy.Levitate|null[0]
constructor <init>(androidx.compose.ui/Alignment = ..., kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>? = ...) // androidx.compose.material3.adaptive.layout/AdaptStrategy.Levitate.<init>|<init>(androidx.compose.ui.Alignment;kotlin.Function2<androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?){}[0]
constructor <init>(androidx.compose.ui/Alignment = ..., kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>? = ..., androidx.compose.material3.adaptive.layout/DragToResizeState? = ...) // androidx.compose.material3.adaptive.layout/AdaptStrategy.Levitate.<init>|<init>(androidx.compose.ui.Alignment;kotlin.Function2<androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.DragToResizeState?){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/AdaptStrategy.Levitate.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/AdaptStrategy.Levitate.hashCode|hashCode(){}[0]
final fun onlyIf(kotlin/Boolean, androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.material3.adaptive.layout/AdaptStrategy // androidx.compose.material3.adaptive.layout/AdaptStrategy.Levitate.onlyIf|onlyIf(kotlin.Boolean;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun onlyIfSinglePane(androidx.compose.material3.adaptive.layout/PaneScaffoldDirective, androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.material3.adaptive.layout/AdaptStrategy // androidx.compose.material3.adaptive.layout/AdaptStrategy.Levitate.onlyIfSinglePane|onlyIfSinglePane(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/AdaptStrategy.Levitate.toString|toString(){}[0]
}
final class Reflow : androidx.compose.material3.adaptive.layout/AdaptStrategy { // androidx.compose.material3.adaptive.layout/AdaptStrategy.Reflow|null[0]
constructor <init>(androidx.compose.material3.adaptive.layout/PaneScaffoldRole) // androidx.compose.material3.adaptive.layout/AdaptStrategy.Reflow.<init>|<init>(androidx.compose.material3.adaptive.layout.PaneScaffoldRole){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/AdaptStrategy.Reflow.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/AdaptStrategy.Reflow.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/AdaptStrategy.Reflow.toString|toString(){}[0]
}
final object Companion { // androidx.compose.material3.adaptive.layout/AdaptStrategy.Companion|null[0]
final val Hide // androidx.compose.material3.adaptive.layout/AdaptStrategy.Companion.Hide|{}Hide[0]
final fun <get-Hide>(): androidx.compose.material3.adaptive.layout/AdaptStrategy // androidx.compose.material3.adaptive.layout/AdaptStrategy.Companion.Hide.<get-Hide>|<get-Hide>(){}[0]
}
}
sealed interface androidx.compose.material3.adaptive.layout/AnimatedPaneScope : androidx.compose.animation/AnimatedVisibilityScope { // androidx.compose.material3.adaptive.layout/AnimatedPaneScope|null[0]
final object Companion { // androidx.compose.material3.adaptive.layout/AnimatedPaneScope.Companion|null[0]
final fun create(androidx.compose.animation/AnimatedVisibilityScope): androidx.compose.material3.adaptive.layout/AnimatedPaneScope // androidx.compose.material3.adaptive.layout/AnimatedPaneScope.Companion.create|create(androidx.compose.animation.AnimatedVisibilityScope){}[0]
}
}
sealed interface androidx.compose.material3.adaptive.layout/PaneAdaptedValue { // androidx.compose.material3.adaptive.layout/PaneAdaptedValue|null[0]
final class Levitated : androidx.compose.material3.adaptive.layout/PaneAdaptedValue { // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Levitated|null[0]
constructor <init>(androidx.compose.ui/Alignment, kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>? = ...) // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Levitated.<init>|<init>(androidx.compose.ui.Alignment;kotlin.Function2<androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?){}[0]
constructor <init>(androidx.compose.ui/Alignment, kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>? = ..., androidx.compose.material3.adaptive.layout/DragToResizeState? = ...) // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Levitated.<init>|<init>(androidx.compose.ui.Alignment;kotlin.Function2<androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.DragToResizeState?){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Levitated.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Levitated.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Levitated.toString|toString(){}[0]
}
final class Reflowed : androidx.compose.material3.adaptive.layout/PaneAdaptedValue { // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Reflowed|null[0]
constructor <init>(androidx.compose.material3.adaptive.layout/PaneScaffoldRole) // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Reflowed.<init>|<init>(androidx.compose.material3.adaptive.layout.PaneScaffoldRole){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Reflowed.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Reflowed.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Reflowed.toString|toString(){}[0]
}
final object Companion { // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Companion|null[0]
final val Expanded // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Companion.Expanded|{}Expanded[0]
final fun <get-Expanded>(): androidx.compose.material3.adaptive.layout/PaneAdaptedValue // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Companion.Expanded.<get-Expanded>|<get-Expanded>(){}[0]
final val Hidden // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Companion.Hidden|{}Hidden[0]
final fun <get-Hidden>(): androidx.compose.material3.adaptive.layout/PaneAdaptedValue // androidx.compose.material3.adaptive.layout/PaneAdaptedValue.Companion.Hidden.<get-Hidden>|<get-Hidden>(){}[0]
}
}
sealed interface androidx.compose.material3.adaptive.layout/PaneExpansionStateKey { // androidx.compose.material3.adaptive.layout/PaneExpansionStateKey|null[0]
final object Companion { // androidx.compose.material3.adaptive.layout/PaneExpansionStateKey.Companion|null[0]
final val Default // androidx.compose.material3.adaptive.layout/PaneExpansionStateKey.Companion.Default|{}Default[0]
final fun <get-Default>(): androidx.compose.material3.adaptive.layout/PaneExpansionStateKey // androidx.compose.material3.adaptive.layout/PaneExpansionStateKey.Companion.Default.<get-Default>|<get-Default>(){}[0]
}
}
sealed interface androidx.compose.material3.adaptive.layout/PaneExpansionStateKeyProvider { // androidx.compose.material3.adaptive.layout/PaneExpansionStateKeyProvider|null[0]
abstract val paneExpansionStateKey // androidx.compose.material3.adaptive.layout/PaneExpansionStateKeyProvider.paneExpansionStateKey|{}paneExpansionStateKey[0]
abstract fun <get-paneExpansionStateKey>(): androidx.compose.material3.adaptive.layout/PaneExpansionStateKey // androidx.compose.material3.adaptive.layout/PaneExpansionStateKeyProvider.paneExpansionStateKey.<get-paneExpansionStateKey>|<get-paneExpansionStateKey>(){}[0]
}
sealed interface androidx.compose.material3.adaptive.layout/PaneMargins { // androidx.compose.material3.adaptive.layout/PaneMargins|null[0]
open fun (androidx.compose.ui.layout/Placeable.PlacementScope).getPaneBottom(kotlin/Int, kotlin/Int): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneMargins.getPaneBottom|getPaneBottom@androidx.compose.ui.layout.Placeable.PlacementScope(kotlin.Int;kotlin.Int){}[0]
open fun (androidx.compose.ui.layout/Placeable.PlacementScope).getPaneLeft(kotlin/Int): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneMargins.getPaneLeft|getPaneLeft@androidx.compose.ui.layout.Placeable.PlacementScope(kotlin.Int){}[0]
open fun (androidx.compose.ui.layout/Placeable.PlacementScope).getPaneRight(kotlin/Int, kotlin/Int): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneMargins.getPaneRight|getPaneRight@androidx.compose.ui.layout.Placeable.PlacementScope(kotlin.Int;kotlin.Int){}[0]
open fun (androidx.compose.ui.layout/Placeable.PlacementScope).getPaneTop(kotlin/Int): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneMargins.getPaneTop|getPaneTop@androidx.compose.ui.layout.Placeable.PlacementScope(kotlin.Int){}[0]
final object Companion { // androidx.compose.material3.adaptive.layout/PaneMargins.Companion|null[0]
final val Unspecified // androidx.compose.material3.adaptive.layout/PaneMargins.Companion.Unspecified|{}Unspecified[0]
final fun <get-Unspecified>(): androidx.compose.material3.adaptive.layout/PaneMargins // androidx.compose.material3.adaptive.layout/PaneMargins.Companion.Unspecified.<get-Unspecified>|<get-Unspecified>(){}[0]
}
}
sealed interface androidx.compose.material3.adaptive.layout/PaneMotion { // androidx.compose.material3.adaptive.layout/PaneMotion|null[0]
abstract val type // androidx.compose.material3.adaptive.layout/PaneMotion.type|{}type[0]
abstract fun <get-type>(): androidx.compose.material3.adaptive.layout/PaneMotion.Type // androidx.compose.material3.adaptive.layout/PaneMotion.type.<get-type>|<get-type>(){}[0]
final value class Type { // androidx.compose.material3.adaptive.layout/PaneMotion.Type|null[0]
final val value // androidx.compose.material3.adaptive.layout/PaneMotion.Type.value|{}value[0]
final fun <get-value>(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneMotion.Type.value.<get-value>|<get-value>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneMotion.Type.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneMotion.Type.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/PaneMotion.Type.toString|toString(){}[0]
final object Companion { // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion|null[0]
final val Entering // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.Entering|{}Entering[0]
final fun <get-Entering>(): androidx.compose.material3.adaptive.layout/PaneMotion.Type // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.Entering.<get-Entering>|<get-Entering>(){}[0]
final val EnteringModal // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.EnteringModal|{}EnteringModal[0]
final fun <get-EnteringModal>(): androidx.compose.material3.adaptive.layout/PaneMotion.Type // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.EnteringModal.<get-EnteringModal>|<get-EnteringModal>(){}[0]
final val Exiting // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.Exiting|{}Exiting[0]
final fun <get-Exiting>(): androidx.compose.material3.adaptive.layout/PaneMotion.Type // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.Exiting.<get-Exiting>|<get-Exiting>(){}[0]
final val ExitingModal // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.ExitingModal|{}ExitingModal[0]
final fun <get-ExitingModal>(): androidx.compose.material3.adaptive.layout/PaneMotion.Type // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.ExitingModal.<get-ExitingModal>|<get-ExitingModal>(){}[0]
final val Hidden // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.Hidden|{}Hidden[0]
final fun <get-Hidden>(): androidx.compose.material3.adaptive.layout/PaneMotion.Type // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.Hidden.<get-Hidden>|<get-Hidden>(){}[0]
final val Shown // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.Shown|{}Shown[0]
final fun <get-Shown>(): androidx.compose.material3.adaptive.layout/PaneMotion.Type // androidx.compose.material3.adaptive.layout/PaneMotion.Type.Companion.Shown.<get-Shown>|<get-Shown>(){}[0]
}
}
final object Companion { // androidx.compose.material3.adaptive.layout/PaneMotion.Companion|null[0]
final val AnimateBounds // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.AnimateBounds|{}AnimateBounds[0]
final fun <get-AnimateBounds>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.AnimateBounds.<get-AnimateBounds>|<get-AnimateBounds>(){}[0]
final val EnterAsModal // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterAsModal|{}EnterAsModal[0]
final fun <get-EnterAsModal>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterAsModal.<get-EnterAsModal>|<get-EnterAsModal>(){}[0]
final val EnterFromLeft // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterFromLeft|{}EnterFromLeft[0]
final fun <get-EnterFromLeft>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterFromLeft.<get-EnterFromLeft>|<get-EnterFromLeft>(){}[0]
final val EnterFromLeftDelayed // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterFromLeftDelayed|{}EnterFromLeftDelayed[0]
final fun <get-EnterFromLeftDelayed>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterFromLeftDelayed.<get-EnterFromLeftDelayed>|<get-EnterFromLeftDelayed>(){}[0]
final val EnterFromRight // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterFromRight|{}EnterFromRight[0]
final fun <get-EnterFromRight>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterFromRight.<get-EnterFromRight>|<get-EnterFromRight>(){}[0]
final val EnterFromRightDelayed // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterFromRightDelayed|{}EnterFromRightDelayed[0]
final fun <get-EnterFromRightDelayed>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterFromRightDelayed.<get-EnterFromRightDelayed>|<get-EnterFromRightDelayed>(){}[0]
final val EnterWithExpand // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterWithExpand|{}EnterWithExpand[0]
final fun <get-EnterWithExpand>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.EnterWithExpand.<get-EnterWithExpand>|<get-EnterWithExpand>(){}[0]
final val ExitAsModal // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.ExitAsModal|{}ExitAsModal[0]
final fun <get-ExitAsModal>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.ExitAsModal.<get-ExitAsModal>|<get-ExitAsModal>(){}[0]
final val ExitToLeft // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.ExitToLeft|{}ExitToLeft[0]
final fun <get-ExitToLeft>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.ExitToLeft.<get-ExitToLeft>|<get-ExitToLeft>(){}[0]
final val ExitToRight // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.ExitToRight|{}ExitToRight[0]
final fun <get-ExitToRight>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.ExitToRight.<get-ExitToRight>|<get-ExitToRight>(){}[0]
final val ExitWithShrink // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.ExitWithShrink|{}ExitWithShrink[0]
final fun <get-ExitWithShrink>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.ExitWithShrink.<get-ExitWithShrink>|<get-ExitWithShrink>(){}[0]
final val NoMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.NoMotion|{}NoMotion[0]
final fun <get-NoMotion>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotion.Companion.NoMotion.<get-NoMotion>|<get-NoMotion>(){}[0]
}
}
sealed interface androidx.compose.material3.adaptive.layout/PaneScaffoldParentData { // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData|null[0]
abstract val isAnimatedPane // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.isAnimatedPane|{}isAnimatedPane[0]
abstract fun <get-isAnimatedPane>(): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.isAnimatedPane.<get-isAnimatedPane>|<get-isAnimatedPane>(){}[0]
abstract val minTouchTargetSize // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.minTouchTargetSize|{}minTouchTargetSize[0]
abstract fun <get-minTouchTargetSize>(): androidx.compose.ui.unit/Dp // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.minTouchTargetSize.<get-minTouchTargetSize>|<get-minTouchTargetSize>(){}[0]
abstract val paneMargins // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.paneMargins|{}paneMargins[0]
abstract fun <get-paneMargins>(): androidx.compose.material3.adaptive.layout/PaneMargins // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.paneMargins.<get-paneMargins>|<get-paneMargins>(){}[0]
abstract val preferredHeight // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.preferredHeight|{}preferredHeight[0]
abstract fun <get-preferredHeight>(): androidx.compose.ui.unit/Dp // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.preferredHeight.<get-preferredHeight>|<get-preferredHeight>(){}[0]
abstract val preferredHeightInProportion // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.preferredHeightInProportion|{}preferredHeightInProportion[0]
abstract fun <get-preferredHeightInProportion>(): kotlin/Float // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.preferredHeightInProportion.<get-preferredHeightInProportion>|<get-preferredHeightInProportion>(){}[0]
abstract val preferredWidth // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.preferredWidth|{}preferredWidth[0]
abstract fun <get-preferredWidth>(): androidx.compose.ui.unit/Dp // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.preferredWidth.<get-preferredWidth>|<get-preferredWidth>(){}[0]
abstract val preferredWidthInProportion // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.preferredWidthInProportion|{}preferredWidthInProportion[0]
abstract fun <get-preferredWidthInProportion>(): kotlin/Float // androidx.compose.material3.adaptive.layout/PaneScaffoldParentData.preferredWidthInProportion.<get-preferredWidthInProportion>|<get-preferredWidthInProportion>(){}[0]
}
sealed interface androidx.compose.material3.adaptive.layout/PaneScaffoldScope { // androidx.compose.material3.adaptive.layout/PaneScaffoldScope|null[0]
abstract val saveableStateHolder // androidx.compose.material3.adaptive.layout/PaneScaffoldScope.saveableStateHolder|{}saveableStateHolder[0]
abstract fun <get-saveableStateHolder>(): androidx.compose.runtime.saveable/SaveableStateHolder // androidx.compose.material3.adaptive.layout/PaneScaffoldScope.saveableStateHolder.<get-saveableStateHolder>|<get-saveableStateHolder>(){}[0]
abstract fun (androidx.compose.ui/Modifier).paneExpansionDraggable(androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.ui.unit/Dp, androidx.compose.foundation.interaction/MutableInteractionSource, kotlin/Function1<androidx.compose.ui.semantics/SemanticsPropertyReceiver, kotlin/Unit>? = ...): androidx.compose.ui/Modifier // androidx.compose.material3.adaptive.layout/PaneScaffoldScope.paneExpansionDraggable|paneExpansionDraggable@androidx.compose.ui.Modifier(androidx.compose.material3.adaptive.layout.PaneExpansionState;androidx.compose.ui.unit.Dp;androidx.compose.foundation.interaction.MutableInteractionSource;kotlin.Function1<androidx.compose.ui.semantics.SemanticsPropertyReceiver,kotlin.Unit>?){}[0]
abstract fun (androidx.compose.ui/Modifier).paneMargins(androidx.compose.foundation.layout/PaddingValues, kotlin/Array<out androidx.compose.ui.layout/RectRulers>..., androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.ui/Modifier // androidx.compose.material3.adaptive.layout/PaneScaffoldScope.paneMargins|paneMargins@androidx.compose.ui.Modifier(androidx.compose.foundation.layout.PaddingValues;kotlin.Array<out|androidx.compose.ui.layout.RectRulers>...;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
abstract fun (androidx.compose.ui/Modifier).paneMargins(kotlin/Array<out androidx.compose.ui.layout/RectRulers>..., androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.ui/Modifier // androidx.compose.material3.adaptive.layout/PaneScaffoldScope.paneMargins|paneMargins@androidx.compose.ui.Modifier(kotlin.Array<out|androidx.compose.ui.layout.RectRulers>...;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
abstract fun (androidx.compose.ui/Modifier).preferredHeight(androidx.compose.ui.unit/Dp): androidx.compose.ui/Modifier // androidx.compose.material3.adaptive.layout/PaneScaffoldScope.preferredHeight|preferredHeight@androidx.compose.ui.Modifier(androidx.compose.ui.unit.Dp){}[0]
abstract fun (androidx.compose.ui/Modifier).preferredHeight(kotlin/Float): androidx.compose.ui/Modifier // androidx.compose.material3.adaptive.layout/PaneScaffoldScope.preferredHeight|preferredHeight@androidx.compose.ui.Modifier(kotlin.Float){}[0]
abstract fun (androidx.compose.ui/Modifier).preferredWidth(androidx.compose.ui.unit/Dp): androidx.compose.ui/Modifier // androidx.compose.material3.adaptive.layout/PaneScaffoldScope.preferredWidth|preferredWidth@androidx.compose.ui.Modifier(androidx.compose.ui.unit.Dp){}[0]
abstract fun (androidx.compose.ui/Modifier).preferredWidth(kotlin/Float): androidx.compose.ui/Modifier // androidx.compose.material3.adaptive.layout/PaneScaffoldScope.preferredWidth|preferredWidth@androidx.compose.ui.Modifier(kotlin.Float){}[0]
}
sealed interface androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope : androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldPaneScope<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue>, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope|null[0]
sealed interface androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope : androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldScope<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue> // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope|null[0]
abstract class androidx.compose.material3.adaptive.layout/DragToResizeState : androidx.compose.foundation.gestures/DraggableState { // androidx.compose.material3.adaptive.layout/DragToResizeState|null[0]
open fun dispatchRawDelta(kotlin/Float) // androidx.compose.material3.adaptive.layout/DragToResizeState.dispatchRawDelta|dispatchRawDelta(kotlin.Float){}[0]
open suspend fun drag(androidx.compose.foundation/MutatePriority, kotlin.coroutines/SuspendFunction1<androidx.compose.foundation.gestures/DragScope, kotlin/Unit>) // androidx.compose.material3.adaptive.layout/DragToResizeState.drag|drag(androidx.compose.foundation.MutatePriority;kotlin.coroutines.SuspendFunction1<androidx.compose.foundation.gestures.DragScope,kotlin.Unit>){}[0]
final object Companion // androidx.compose.material3.adaptive.layout/DragToResizeState.Companion|null[0]
}
final class <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole, #B: androidx.compose.material3.adaptive.layout/PaneScaffoldValue<#A>> androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope { // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope|null[0]
final val boundsAnimationSpec // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.boundsAnimationSpec|{}boundsAnimationSpec[0]
final fun <get-boundsAnimationSpec>(): androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntRect> // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.boundsAnimationSpec.<get-boundsAnimationSpec>|<get-boundsAnimationSpec>(){}[0]
final val content // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.content|{}content[0]
final fun <get-content>(): kotlin/Function3<androidx.compose.material3.adaptive.layout/AnimatedPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit> // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.content.<get-content>|<get-content>(){}[0]
final val dragToResizeHandle // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.dragToResizeHandle|{}dragToResizeHandle[0]
final fun <get-dragToResizeHandle>(): kotlin/Function3<androidx.compose.material3.adaptive.layout/DragToResizeState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>? // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.dragToResizeHandle.<get-dragToResizeHandle>|<get-dragToResizeHandle>(){}[0]
final val enterTransition // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.enterTransition|{}enterTransition[0]
final fun <get-enterTransition>(): androidx.compose.animation/EnterTransition // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.enterTransition.<get-enterTransition>|<get-enterTransition>(){}[0]
final val exitTransition // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.exitTransition|{}exitTransition[0]
final fun <get-exitTransition>(): androidx.compose.animation/ExitTransition // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.exitTransition.<get-exitTransition>|<get-exitTransition>(){}[0]
final val modifier // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.modifier|{}modifier[0]
final fun <get-modifier>(): androidx.compose.ui/Modifier // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.modifier.<get-modifier>|<get-modifier>(){}[0]
final val scope // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.scope|{}scope[0]
final fun <get-scope>(): androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldPaneScope<#A, #B> // androidx.compose.material3.adaptive.layout/AnimatedPaneOverrideScope.scope.<get-scope>|<get-scope>(){}[0]
}
final class <#A: out kotlin/Any?> androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem { // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem|null[0]
constructor <init>(androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole, #A? = ...) // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem.<init>|<init>(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole;1:0?){}[0]
final val contentKey // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem.contentKey|{}contentKey[0]
final fun <get-contentKey>(): #A? // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem.contentKey.<get-contentKey>|<get-contentKey>(){}[0]
final val pane // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem.pane|{}pane[0]
final fun <get-pane>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem.pane.<get-pane>|<get-pane>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem.toString|toString(){}[0]
}
final class androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState : androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState { // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState|null[0]
constructor <init>(androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue) // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.<init>|<init>(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue){}[0]
final val currentState // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.currentState|{}currentState[0]
final fun <get-currentState>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.currentState.<get-currentState>|<get-currentState>(){}[0]
final val progressFraction // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.progressFraction|{}progressFraction[0]
final fun <get-progressFraction>(): kotlin/Float // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.progressFraction.<get-progressFraction>|<get-progressFraction>(){}[0]
final val targetState // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.targetState|{}targetState[0]
final fun <get-targetState>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.targetState.<get-targetState>|<get-targetState>(){}[0]
final var isPredictiveBackInProgress // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.isPredictiveBackInProgress|{}isPredictiveBackInProgress[0]
final fun <get-isPredictiveBackInProgress>(): kotlin/Boolean // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.isPredictiveBackInProgress.<get-isPredictiveBackInProgress>|<get-isPredictiveBackInProgress>(){}[0]
final suspend fun animateTo(androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue = ..., androidx.compose.animation.core/FiniteAnimationSpec<kotlin/Float>? = ..., kotlin/Boolean = ...) // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.animateTo|animateTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue;androidx.compose.animation.core.FiniteAnimationSpec<kotlin.Float>?;kotlin.Boolean){}[0]
final suspend fun seekTo(kotlin/Float, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue = ..., kotlin/Boolean = ...) // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.seekTo|seekTo(kotlin.Float;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue;kotlin.Boolean){}[0]
final suspend fun snapTo(androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue) // androidx.compose.material3.adaptive.layout/MutableThreePaneScaffoldState.snapTo|snapTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue){}[0]
}
final class androidx.compose.material3.adaptive.layout/PaneExpansionState { // androidx.compose.material3.adaptive.layout/PaneExpansionState|null[0]
final var currentAnchor // androidx.compose.material3.adaptive.layout/PaneExpansionState.currentAnchor|{}currentAnchor[0]
final fun <get-currentAnchor>(): androidx.compose.material3.adaptive.layout/PaneExpansionAnchor? // androidx.compose.material3.adaptive.layout/PaneExpansionState.currentAnchor.<get-currentAnchor>|<get-currentAnchor>(){}[0]
final fun clear() // androidx.compose.material3.adaptive.layout/PaneExpansionState.clear|clear(){}[0]
final fun isUnspecified(): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneExpansionState.isUnspecified|isUnspecified(){}[0]
final fun setFirstPaneProportion(kotlin/Float) // androidx.compose.material3.adaptive.layout/PaneExpansionState.setFirstPaneProportion|setFirstPaneProportion(kotlin.Float){}[0]
final fun setFirstPaneWidth(kotlin/Int) // androidx.compose.material3.adaptive.layout/PaneExpansionState.setFirstPaneWidth|setFirstPaneWidth(kotlin.Int){}[0]
final suspend fun animateTo(androidx.compose.material3.adaptive.layout/PaneExpansionAnchor, kotlin/Float = ...) // androidx.compose.material3.adaptive.layout/PaneExpansionState.animateTo|animateTo(androidx.compose.material3.adaptive.layout.PaneExpansionAnchor;kotlin.Float){}[0]
final object Companion { // androidx.compose.material3.adaptive.layout/PaneExpansionState.Companion|null[0]
final const val Unspecified // androidx.compose.material3.adaptive.layout/PaneExpansionState.Companion.Unspecified|{}Unspecified[0]
final fun <get-Unspecified>(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneExpansionState.Companion.Unspecified.<get-Unspecified>|<get-Unspecified>(){}[0]
}
}
final class androidx.compose.material3.adaptive.layout/PaneMotionData { // androidx.compose.material3.adaptive.layout/PaneMotionData|null[0]
final var motion // androidx.compose.material3.adaptive.layout/PaneMotionData.motion|{}motion[0]
final fun <get-motion>(): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/PaneMotionData.motion.<get-motion>|<get-motion>(){}[0]
final var originPosition // androidx.compose.material3.adaptive.layout/PaneMotionData.originPosition|{}originPosition[0]
final fun <get-originPosition>(): androidx.compose.ui.unit/IntOffset // androidx.compose.material3.adaptive.layout/PaneMotionData.originPosition.<get-originPosition>|<get-originPosition>(){}[0]
final var originSize // androidx.compose.material3.adaptive.layout/PaneMotionData.originSize|{}originSize[0]
final fun <get-originSize>(): androidx.compose.ui.unit/IntSize // androidx.compose.material3.adaptive.layout/PaneMotionData.originSize.<get-originSize>|<get-originSize>(){}[0]
final var targetPosition // androidx.compose.material3.adaptive.layout/PaneMotionData.targetPosition|{}targetPosition[0]
final fun <get-targetPosition>(): androidx.compose.ui.unit/IntOffset // androidx.compose.material3.adaptive.layout/PaneMotionData.targetPosition.<get-targetPosition>|<get-targetPosition>(){}[0]
final var targetSize // androidx.compose.material3.adaptive.layout/PaneMotionData.targetSize|{}targetSize[0]
final fun <get-targetSize>(): androidx.compose.ui.unit/IntSize // androidx.compose.material3.adaptive.layout/PaneMotionData.targetSize.<get-targetSize>|<get-targetSize>(){}[0]
}
final class androidx.compose.material3.adaptive.layout/PaneScaffoldDirective { // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective|null[0]
constructor <init>(kotlin/Int, androidx.compose.ui.unit/Dp, kotlin/Int, androidx.compose.ui.unit/Dp, androidx.compose.ui.unit/Dp, androidx.compose.ui.unit/Dp, kotlin.collections/List<androidx.compose.ui.geometry/Rect>) // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.<init>|<init>(kotlin.Int;androidx.compose.ui.unit.Dp;kotlin.Int;androidx.compose.ui.unit.Dp;androidx.compose.ui.unit.Dp;androidx.compose.ui.unit.Dp;kotlin.collections.List<androidx.compose.ui.geometry.Rect>){}[0]
constructor <init>(kotlin/Int, androidx.compose.ui.unit/Dp, kotlin/Int, androidx.compose.ui.unit/Dp, androidx.compose.ui.unit/Dp, androidx.compose.ui.unit/Dp, kotlin.collections/List<androidx.compose.ui.geometry/Rect>, kotlin/Boolean) // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.<init>|<init>(kotlin.Int;androidx.compose.ui.unit.Dp;kotlin.Int;androidx.compose.ui.unit.Dp;androidx.compose.ui.unit.Dp;androidx.compose.ui.unit.Dp;kotlin.collections.List<androidx.compose.ui.geometry.Rect>;kotlin.Boolean){}[0]
constructor <init>(kotlin/Int, androidx.compose.ui.unit/Dp, kotlin/Int, androidx.compose.ui.unit/Dp, androidx.compose.ui.unit/Dp, kotlin.collections/List<androidx.compose.ui.geometry/Rect>) // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.<init>|<init>(kotlin.Int;androidx.compose.ui.unit.Dp;kotlin.Int;androidx.compose.ui.unit.Dp;androidx.compose.ui.unit.Dp;kotlin.collections.List<androidx.compose.ui.geometry.Rect>){}[0]
final val defaultPanePreferredHeight // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.defaultPanePreferredHeight|{}defaultPanePreferredHeight[0]
final fun <get-defaultPanePreferredHeight>(): androidx.compose.ui.unit/Dp // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.defaultPanePreferredHeight.<get-defaultPanePreferredHeight>|<get-defaultPanePreferredHeight>(){}[0]
final val defaultPanePreferredWidth // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.defaultPanePreferredWidth|{}defaultPanePreferredWidth[0]
final fun <get-defaultPanePreferredWidth>(): androidx.compose.ui.unit/Dp // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.defaultPanePreferredWidth.<get-defaultPanePreferredWidth>|<get-defaultPanePreferredWidth>(){}[0]
final val excludedBounds // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.excludedBounds|{}excludedBounds[0]
final fun <get-excludedBounds>(): kotlin.collections/List<androidx.compose.ui.geometry/Rect> // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.excludedBounds.<get-excludedBounds>|<get-excludedBounds>(){}[0]
final val horizontalPartitionSpacerSize // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.horizontalPartitionSpacerSize|{}horizontalPartitionSpacerSize[0]
final fun <get-horizontalPartitionSpacerSize>(): androidx.compose.ui.unit/Dp // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.horizontalPartitionSpacerSize.<get-horizontalPartitionSpacerSize>|<get-horizontalPartitionSpacerSize>(){}[0]
final val maxHorizontalPartitions // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.maxHorizontalPartitions|{}maxHorizontalPartitions[0]
final fun <get-maxHorizontalPartitions>(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.maxHorizontalPartitions.<get-maxHorizontalPartitions>|<get-maxHorizontalPartitions>(){}[0]
final val maxVerticalPartitions // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.maxVerticalPartitions|{}maxVerticalPartitions[0]
final fun <get-maxVerticalPartitions>(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.maxVerticalPartitions.<get-maxVerticalPartitions>|<get-maxVerticalPartitions>(){}[0]
final val shouldAutoFocusCurrentDestination // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.shouldAutoFocusCurrentDestination|{}shouldAutoFocusCurrentDestination[0]
final fun <get-shouldAutoFocusCurrentDestination>(): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.shouldAutoFocusCurrentDestination.<get-shouldAutoFocusCurrentDestination>|<get-shouldAutoFocusCurrentDestination>(){}[0]
final val verticalPartitionSpacerSize // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.verticalPartitionSpacerSize|{}verticalPartitionSpacerSize[0]
final fun <get-verticalPartitionSpacerSize>(): androidx.compose.ui.unit/Dp // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.verticalPartitionSpacerSize.<get-verticalPartitionSpacerSize>|<get-verticalPartitionSpacerSize>(){}[0]
final fun copy(kotlin/Int = ..., androidx.compose.ui.unit/Dp = ..., kotlin/Int = ..., androidx.compose.ui.unit/Dp = ..., androidx.compose.ui.unit/Dp = ..., kotlin.collections/List<androidx.compose.ui.geometry/Rect> = ...): androidx.compose.material3.adaptive.layout/PaneScaffoldDirective // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.copy|copy(kotlin.Int;androidx.compose.ui.unit.Dp;kotlin.Int;androidx.compose.ui.unit.Dp;androidx.compose.ui.unit.Dp;kotlin.collections.List<androidx.compose.ui.geometry.Rect>){}[0]
final fun copy(kotlin/Int = ..., androidx.compose.ui.unit/Dp = ..., kotlin/Int = ..., androidx.compose.ui.unit/Dp = ..., androidx.compose.ui.unit/Dp = ..., kotlin.collections/List<androidx.compose.ui.geometry/Rect> = ..., androidx.compose.ui.unit/Dp = ...): androidx.compose.material3.adaptive.layout/PaneScaffoldDirective // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.copy|copy(kotlin.Int;androidx.compose.ui.unit.Dp;kotlin.Int;androidx.compose.ui.unit.Dp;androidx.compose.ui.unit.Dp;kotlin.collections.List<androidx.compose.ui.geometry.Rect>;androidx.compose.ui.unit.Dp){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.toString|toString(){}[0]
final object Companion { // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.Companion|null[0]
final val Default // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.Companion.Default|{}Default[0]
final fun <get-Default>(): androidx.compose.material3.adaptive.layout/PaneScaffoldDirective // androidx.compose.material3.adaptive.layout/PaneScaffoldDirective.Companion.Default.<get-Default>|<get-Default>(){}[0]
}
}
final class androidx.compose.material3.adaptive.layout/ThreePaneMotion { // androidx.compose.material3.adaptive.layout/ThreePaneMotion|null[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/ThreePaneMotion.equals|equals(kotlin.Any?){}[0]
final fun get(androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole): androidx.compose.material3.adaptive.layout/PaneMotion // androidx.compose.material3.adaptive.layout/ThreePaneMotion.get|get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/ThreePaneMotion.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/ThreePaneMotion.toString|toString(){}[0]
final object Companion { // androidx.compose.material3.adaptive.layout/ThreePaneMotion.Companion|null[0]
final val NoMotion // androidx.compose.material3.adaptive.layout/ThreePaneMotion.Companion.NoMotion|{}NoMotion[0]
final fun <get-NoMotion>(): androidx.compose.material3.adaptive.layout/ThreePaneMotion // androidx.compose.material3.adaptive.layout/ThreePaneMotion.Companion.NoMotion.<get-NoMotion>|<get-NoMotion>(){}[0]
}
}
final class androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies { // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies|null[0]
constructor <init>(androidx.compose.material3.adaptive.layout/AdaptStrategy, androidx.compose.material3.adaptive.layout/AdaptStrategy, androidx.compose.material3.adaptive.layout/AdaptStrategy) // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies.<init>|<init>(androidx.compose.material3.adaptive.layout.AdaptStrategy;androidx.compose.material3.adaptive.layout.AdaptStrategy;androidx.compose.material3.adaptive.layout.AdaptStrategy){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies.equals|equals(kotlin.Any?){}[0]
final fun get(androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole): androidx.compose.material3.adaptive.layout/AdaptStrategy // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies.get|get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies.hashCode|hashCode(){}[0]
}
final class androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder : androidx.compose.material3.adaptive.layout/PaneScaffoldHorizontalOrder<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole> { // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder|null[0]
final val size // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder.size|{}size[0]
final fun <get-size>(): kotlin/Int // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder.size.<get-size>|<get-size>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder.equals|equals(kotlin.Any?){}[0]
final fun forEach(kotlin/Function1<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole, kotlin/Unit>) // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder.forEach|forEach(kotlin.Function1<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,kotlin.Unit>){}[0]
final fun forEachIndexed(kotlin/Function2<kotlin/Int, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole, kotlin/Unit>) // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder.forEachIndexed|forEachIndexed(kotlin.Function2<kotlin.Int,androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,kotlin.Unit>){}[0]
final fun forEachIndexedReversed(kotlin/Function2<kotlin/Int, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole, kotlin/Unit>) // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder.forEachIndexedReversed|forEachIndexedReversed(kotlin.Function2<kotlin.Int,androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole,kotlin.Unit>){}[0]
final fun get(kotlin/Int): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder.get|get(kotlin.Int){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder.hashCode|hashCode(){}[0]
final fun indexOf(androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole): kotlin/Int // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder.indexOf|indexOf(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole){}[0]
}
final class androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope { // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope|null[0]
final val modifier // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.modifier|{}modifier[0]
final fun <get-modifier>(): androidx.compose.ui/Modifier // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.modifier.<get-modifier>|<get-modifier>(){}[0]
final val paneExpansionDragHandle // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.paneExpansionDragHandle|{}paneExpansionDragHandle[0]
final fun <get-paneExpansionDragHandle>(): kotlin/Function3<androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>? // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.paneExpansionDragHandle.<get-paneExpansionDragHandle>|<get-paneExpansionDragHandle>(){}[0]
final val paneExpansionState // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.paneExpansionState|{}paneExpansionState[0]
final fun <get-paneExpansionState>(): androidx.compose.material3.adaptive.layout/PaneExpansionState // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.paneExpansionState.<get-paneExpansionState>|<get-paneExpansionState>(){}[0]
final val paneOrder // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.paneOrder|{}paneOrder[0]
final fun <get-paneOrder>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldHorizontalOrder // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.paneOrder.<get-paneOrder>|<get-paneOrder>(){}[0]
final val primaryPane // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.primaryPane|{}primaryPane[0]
final fun <get-primaryPane>(): kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit> // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.primaryPane.<get-primaryPane>|<get-primaryPane>(){}[0]
final val scaffoldDirective // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.scaffoldDirective|{}scaffoldDirective[0]
final fun <get-scaffoldDirective>(): androidx.compose.material3.adaptive.layout/PaneScaffoldDirective // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.scaffoldDirective.<get-scaffoldDirective>|<get-scaffoldDirective>(){}[0]
final val scaffoldState // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.scaffoldState|{}scaffoldState[0]
final fun <get-scaffoldState>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.scaffoldState.<get-scaffoldState>|<get-scaffoldState>(){}[0]
final val secondaryPane // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.secondaryPane|{}secondaryPane[0]
final fun <get-secondaryPane>(): kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit> // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.secondaryPane.<get-secondaryPane>|<get-secondaryPane>(){}[0]
final val tertiaryPane // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.tertiaryPane|{}tertiaryPane[0]
final fun <get-tertiaryPane>(): kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>? // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverrideScope.tertiaryPane.<get-tertiaryPane>|<get-tertiaryPane>(){}[0]
}
final class androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue : androidx.compose.material3.adaptive.layout/PaneExpansionStateKeyProvider, androidx.compose.material3.adaptive.layout/PaneScaffoldValue<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole> { // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue|null[0]
constructor <init>(androidx.compose.material3.adaptive.layout/PaneAdaptedValue, androidx.compose.material3.adaptive.layout/PaneAdaptedValue, androidx.compose.material3.adaptive.layout/PaneAdaptedValue) // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.<init>|<init>(androidx.compose.material3.adaptive.layout.PaneAdaptedValue;androidx.compose.material3.adaptive.layout.PaneAdaptedValue;androidx.compose.material3.adaptive.layout.PaneAdaptedValue){}[0]
final val paneExpansionStateKey // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.paneExpansionStateKey|{}paneExpansionStateKey[0]
final fun <get-paneExpansionStateKey>(): androidx.compose.material3.adaptive.layout/PaneExpansionStateKey // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.paneExpansionStateKey.<get-paneExpansionStateKey>|<get-paneExpansionStateKey>(){}[0]
final val primary // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.primary|{}primary[0]
final fun <get-primary>(): androidx.compose.material3.adaptive.layout/PaneAdaptedValue // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.primary.<get-primary>|<get-primary>(){}[0]
final val secondary // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.secondary|{}secondary[0]
final fun <get-secondary>(): androidx.compose.material3.adaptive.layout/PaneAdaptedValue // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.secondary.<get-secondary>|<get-secondary>(){}[0]
final val tertiary // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.tertiary|{}tertiary[0]
final fun <get-tertiary>(): androidx.compose.material3.adaptive.layout/PaneAdaptedValue // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.tertiary.<get-tertiary>|<get-tertiary>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.equals|equals(kotlin.Any?){}[0]
final fun get(androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole): androidx.compose.material3.adaptive.layout/PaneAdaptedValue // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.get|get(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue.toString|toString(){}[0]
}
final value class androidx.compose.material3.adaptive.layout/HingePolicy { // androidx.compose.material3.adaptive.layout/HingePolicy|null[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/HingePolicy.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/HingePolicy.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/HingePolicy.toString|toString(){}[0]
final object Companion { // androidx.compose.material3.adaptive.layout/HingePolicy.Companion|null[0]
final val AlwaysAvoid // androidx.compose.material3.adaptive.layout/HingePolicy.Companion.AlwaysAvoid|{}AlwaysAvoid[0]
final fun <get-AlwaysAvoid>(): androidx.compose.material3.adaptive.layout/HingePolicy // androidx.compose.material3.adaptive.layout/HingePolicy.Companion.AlwaysAvoid.<get-AlwaysAvoid>|<get-AlwaysAvoid>(){}[0]
final val AvoidOccluding // androidx.compose.material3.adaptive.layout/HingePolicy.Companion.AvoidOccluding|{}AvoidOccluding[0]
final fun <get-AvoidOccluding>(): androidx.compose.material3.adaptive.layout/HingePolicy // androidx.compose.material3.adaptive.layout/HingePolicy.Companion.AvoidOccluding.<get-AvoidOccluding>|<get-AvoidOccluding>(){}[0]
final val AvoidSeparating // androidx.compose.material3.adaptive.layout/HingePolicy.Companion.AvoidSeparating|{}AvoidSeparating[0]
final fun <get-AvoidSeparating>(): androidx.compose.material3.adaptive.layout/HingePolicy // androidx.compose.material3.adaptive.layout/HingePolicy.Companion.AvoidSeparating.<get-AvoidSeparating>|<get-AvoidSeparating>(){}[0]
final val NeverAvoid // androidx.compose.material3.adaptive.layout/HingePolicy.Companion.NeverAvoid|{}NeverAvoid[0]
final fun <get-NeverAvoid>(): androidx.compose.material3.adaptive.layout/HingePolicy // androidx.compose.material3.adaptive.layout/HingePolicy.Companion.NeverAvoid.<get-NeverAvoid>|<get-NeverAvoid>(){}[0]
}
}
sealed class androidx.compose.material3.adaptive.layout/PaneExpansionAnchor { // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor|null[0]
abstract val description // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.description|{}description[0]
abstract fun <get-description>(androidx.compose.runtime/Composer?, kotlin/Int): kotlin/String // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.description.<get-description>|<get-description>(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
abstract class Offset : androidx.compose.material3.adaptive.layout/PaneExpansionAnchor { // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset|null[0]
final val direction // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.direction|{}direction[0]
final fun <get-direction>(): androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.direction.<get-direction>|<get-direction>(){}[0]
final val offset // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.offset|{}offset[0]
final fun <get-offset>(): androidx.compose.ui.unit/Dp // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.offset.<get-offset>|<get-offset>(){}[0]
open fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.equals|equals(kotlin.Any?){}[0]
open fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.hashCode|hashCode(){}[0]
open fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.toString|toString(){}[0]
final value class Direction { // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction|null[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction.toString|toString(){}[0]
final object Companion { // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction.Companion|null[0]
final val FromEnd // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction.Companion.FromEnd|{}FromEnd[0]
final fun <get-FromEnd>(): androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction.Companion.FromEnd.<get-FromEnd>|<get-FromEnd>(){}[0]
final val FromStart // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction.Companion.FromStart|{}FromStart[0]
final fun <get-FromStart>(): androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Direction.Companion.FromStart.<get-FromStart>|<get-FromStart>(){}[0]
}
}
final object Companion { // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Companion|null[0]
final fun fromEnd(androidx.compose.ui.unit/Dp): androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Companion.fromEnd|fromEnd(androidx.compose.ui.unit.Dp){}[0]
final fun fromStart(androidx.compose.ui.unit/Dp): androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Offset.Companion.fromStart|fromStart(androidx.compose.ui.unit.Dp){}[0]
}
}
final class Proportion : androidx.compose.material3.adaptive.layout/PaneExpansionAnchor { // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Proportion|null[0]
constructor <init>(kotlin/Float) // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Proportion.<init>|<init>(kotlin.Float){}[0]
final val description // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Proportion.description|{}description[0]
final fun <get-description>(androidx.compose.runtime/Composer?, kotlin/Int): kotlin/String // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Proportion.description.<get-description>|<get-description>(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final val proportion // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Proportion.proportion|{}proportion[0]
final fun <get-proportion>(): kotlin/Float // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Proportion.proportion.<get-proportion>|<get-proportion>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Proportion.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Proportion.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.layout/PaneExpansionAnchor.Proportion.toString|toString(){}[0]
}
}
sealed class androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState { // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState|null[0]
abstract val currentState // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState.currentState|{}currentState[0]
abstract fun <get-currentState>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState.currentState.<get-currentState>|<get-currentState>(){}[0]
abstract val isPredictiveBackInProgress // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState.isPredictiveBackInProgress|{}isPredictiveBackInProgress[0]
abstract fun <get-isPredictiveBackInProgress>(): kotlin/Boolean // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState.isPredictiveBackInProgress.<get-isPredictiveBackInProgress>|<get-isPredictiveBackInProgress>(){}[0]
abstract val progressFraction // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState.progressFraction|{}progressFraction[0]
abstract fun <get-progressFraction>(): kotlin/Float // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState.progressFraction.<get-progressFraction>|<get-progressFraction>(){}[0]
abstract val targetState // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState.targetState|{}targetState[0]
abstract fun <get-targetState>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue // androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState.targetState.<get-targetState>|<get-targetState>(){}[0]
}
final object androidx.compose.material3.adaptive.layout/LevitatedPaneScrimDefaults { // androidx.compose.material3.adaptive.layout/LevitatedPaneScrimDefaults|null[0]
final val Color // androidx.compose.material3.adaptive.layout/LevitatedPaneScrimDefaults.Color|{}Color[0]
final fun <get-Color>(): androidx.compose.ui.graphics/Color // androidx.compose.material3.adaptive.layout/LevitatedPaneScrimDefaults.Color.<get-Color>|<get-Color>(){}[0]
}
final object androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldDefaults { // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldDefaults|null[0]
final fun adaptStrategies(androidx.compose.material3.adaptive.layout/AdaptStrategy = ..., androidx.compose.material3.adaptive.layout/AdaptStrategy = ..., androidx.compose.material3.adaptive.layout/AdaptStrategy = ...): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldDefaults.adaptStrategies|adaptStrategies(androidx.compose.material3.adaptive.layout.AdaptStrategy;androidx.compose.material3.adaptive.layout.AdaptStrategy;androidx.compose.material3.adaptive.layout.AdaptStrategy){}[0]
}
final object androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldRole { // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldRole|null[0]
final val Detail // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldRole.Detail|{}Detail[0]
final fun <get-Detail>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldRole.Detail.<get-Detail>|<get-Detail>(){}[0]
final val Extra // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldRole.Extra|{}Extra[0]
final fun <get-Extra>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldRole.Extra.<get-Extra>|<get-Extra>(){}[0]
final val List // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldRole.List|{}List[0]
final fun <get-List>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffoldRole.List.<get-List>|<get-List>(){}[0]
}
final object androidx.compose.material3.adaptive.layout/PaneMotionDefaults { // androidx.compose.material3.adaptive.layout/PaneMotionDefaults|null[0]
final val AnimationSpec // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.AnimationSpec|{}AnimationSpec[0]
final fun <get-AnimationSpec>(): androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntRect> // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.AnimationSpec.<get-AnimationSpec>|<get-AnimationSpec>(){}[0]
final val DelayedAnimationSpec // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.DelayedAnimationSpec|{}DelayedAnimationSpec[0]
final fun <get-DelayedAnimationSpec>(): androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntRect> // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.DelayedAnimationSpec.<get-DelayedAnimationSpec>|<get-DelayedAnimationSpec>(){}[0]
final val DelayedOffsetAnimationSpec // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.DelayedOffsetAnimationSpec|{}DelayedOffsetAnimationSpec[0]
final fun <get-DelayedOffsetAnimationSpec>(): androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntOffset> // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.DelayedOffsetAnimationSpec.<get-DelayedOffsetAnimationSpec>|<get-DelayedOffsetAnimationSpec>(){}[0]
final val DelayedSizeAnimationSpec // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.DelayedSizeAnimationSpec|{}DelayedSizeAnimationSpec[0]
final fun <get-DelayedSizeAnimationSpec>(): androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntSize> // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.DelayedSizeAnimationSpec.<get-DelayedSizeAnimationSpec>|<get-DelayedSizeAnimationSpec>(){}[0]
final val OffsetAnimationSpec // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.OffsetAnimationSpec|{}OffsetAnimationSpec[0]
final fun <get-OffsetAnimationSpec>(): androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntOffset> // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.OffsetAnimationSpec.<get-OffsetAnimationSpec>|<get-OffsetAnimationSpec>(){}[0]
final val SizeAnimationSpec // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.SizeAnimationSpec|{}SizeAnimationSpec[0]
final fun <get-SizeAnimationSpec>(): androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntSize> // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.SizeAnimationSpec.<get-SizeAnimationSpec>|<get-SizeAnimationSpec>(){}[0]
final val VisibilityAnimationSpec // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.VisibilityAnimationSpec|{}VisibilityAnimationSpec[0]
final fun <get-VisibilityAnimationSpec>(): androidx.compose.animation.core/FiniteAnimationSpec<kotlin/Float> // androidx.compose.material3.adaptive.layout/PaneMotionDefaults.VisibilityAnimationSpec.<get-VisibilityAnimationSpec>|<get-VisibilityAnimationSpec>(){}[0]
}
final object androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldDefaults { // androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldDefaults|null[0]
final fun adaptStrategies(androidx.compose.material3.adaptive.layout/AdaptStrategy = ..., androidx.compose.material3.adaptive.layout/AdaptStrategy = ..., androidx.compose.material3.adaptive.layout/AdaptStrategy = ...): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies // androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldDefaults.adaptStrategies|adaptStrategies(androidx.compose.material3.adaptive.layout.AdaptStrategy;androidx.compose.material3.adaptive.layout.AdaptStrategy;androidx.compose.material3.adaptive.layout.AdaptStrategy){}[0]
}
final object androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldRole { // androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldRole|null[0]
final val Extra // androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldRole.Extra|{}Extra[0]
final fun <get-Extra>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole // androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldRole.Extra.<get-Extra>|<get-Extra>(){}[0]
final val Main // androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldRole.Main|{}Main[0]
final fun <get-Main>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole // androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldRole.Main.<get-Main>|<get-Main>(){}[0]
final val Supporting // androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldRole.Supporting|{}Supporting[0]
final fun <get-Supporting>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole // androidx.compose.material3.adaptive.layout/SupportingPaneScaffoldRole.Supporting.<get-Supporting>|<get-Supporting>(){}[0]
}
final val androidx.compose.material3.adaptive.layout/LocalAnimatedPaneOverride // androidx.compose.material3.adaptive.layout/LocalAnimatedPaneOverride|{}LocalAnimatedPaneOverride[0]
final fun <get-LocalAnimatedPaneOverride>(): androidx.compose.runtime/ProvidableCompositionLocal<androidx.compose.material3.adaptive.layout/AnimatedPaneOverride> // androidx.compose.material3.adaptive.layout/LocalAnimatedPaneOverride.<get-LocalAnimatedPaneOverride>|<get-LocalAnimatedPaneOverride>(){}[0]
final val androidx.compose.material3.adaptive.layout/LocalThreePaneScaffoldOverride // androidx.compose.material3.adaptive.layout/LocalThreePaneScaffoldOverride|{}LocalThreePaneScaffoldOverride[0]
final fun <get-LocalThreePaneScaffoldOverride>(): androidx.compose.runtime/ProvidableCompositionLocal<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldOverride> // androidx.compose.material3.adaptive.layout/LocalThreePaneScaffoldOverride.<get-LocalThreePaneScaffoldOverride>|<get-LocalThreePaneScaffoldOverride>(){}[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AdaptStrategy_Levitate$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AdaptStrategy_Levitate$stableprop|#static{}androidx_compose_material3_adaptive_layout_AdaptStrategy_Levitate$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AdaptStrategy_Reflow$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AdaptStrategy_Reflow$stableprop|#static{}androidx_compose_material3_adaptive_layout_AdaptStrategy_Reflow$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AnimatedPaneOverrideScope$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AnimatedPaneOverrideScope$stableprop|#static{}androidx_compose_material3_adaptive_layout_AnimatedPaneOverrideScope$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_DragToResizeState$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_DragToResizeState$stableprop|#static{}androidx_compose_material3_adaptive_layout_DragToResizeState$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_LevitatedPaneScrimDefaults$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_LevitatedPaneScrimDefaults$stableprop|#static{}androidx_compose_material3_adaptive_layout_LevitatedPaneScrimDefaults$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldDefaults$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldDefaults$stableprop|#static{}androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldDefaults$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldRole$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldRole$stableprop|#static{}androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldRole$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_MutableThreePaneScaffoldState$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_MutableThreePaneScaffoldState$stableprop|#static{}androidx_compose_material3_adaptive_layout_MutableThreePaneScaffoldState$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Levitated$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Levitated$stableprop|#static{}androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Levitated$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Reflowed$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Reflowed$stableprop|#static{}androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Reflowed$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor$stableprop|#static{}androidx_compose_material3_adaptive_layout_PaneExpansionAnchor$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Offset$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Offset$stableprop|#static{}androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Offset$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Proportion$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Proportion$stableprop|#static{}androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Proportion$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionState$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionState$stableprop|#static{}androidx_compose_material3_adaptive_layout_PaneExpansionState$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneMotionData$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneMotionData$stableprop|#static{}androidx_compose_material3_adaptive_layout_PaneMotionData$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneMotionDefaults$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneMotionDefaults$stableprop|#static{}androidx_compose_material3_adaptive_layout_PaneMotionDefaults$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneScaffoldDirective$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneScaffoldDirective$stableprop|#static{}androidx_compose_material3_adaptive_layout_PaneScaffoldDirective$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldDefaults$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldDefaults$stableprop|#static{}androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldDefaults$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldRole$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldRole$stableprop|#static{}androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldRole$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneMotion$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneMotion$stableprop|#static{}androidx_compose_material3_adaptive_layout_ThreePaneMotion$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldAdaptStrategies$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldAdaptStrategies$stableprop|#static{}androidx_compose_material3_adaptive_layout_ThreePaneScaffoldAdaptStrategies$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldDestinationItem$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldDestinationItem$stableprop|#static{}androidx_compose_material3_adaptive_layout_ThreePaneScaffoldDestinationItem$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldHorizontalOrder$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldHorizontalOrder$stableprop|#static{}androidx_compose_material3_adaptive_layout_ThreePaneScaffoldHorizontalOrder$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldOverrideScope$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldOverrideScope$stableprop|#static{}androidx_compose_material3_adaptive_layout_ThreePaneScaffoldOverrideScope$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldState$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldState$stableprop|#static{}androidx_compose_material3_adaptive_layout_ThreePaneScaffoldState$stableprop[0]
final val androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldValue$stableprop // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldValue$stableprop|#static{}androidx_compose_material3_adaptive_layout_ThreePaneScaffoldValue$stableprop[0]
final fun (androidx.compose.material3.adaptive.layout/PaneExpansionState).androidx.compose.material3.adaptive.layout/defaultDragHandleSemantics(androidx.compose.runtime/Composer?, kotlin/Int): kotlin/Function1<androidx.compose.ui.semantics/SemanticsPropertyReceiver, kotlin/Unit> // androidx.compose.material3.adaptive.layout/defaultDragHandleSemantics|defaultDragHandleSemantics@androidx.compose.material3.adaptive.layout.PaneExpansionState(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole, #B: androidx.compose.material3.adaptive.layout/PaneScaffoldValue<#A>> (androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldPaneScope<#A, #B>).androidx.compose.material3.adaptive.layout/AnimatedPane(androidx.compose.ui/Modifier?, androidx.compose.animation/EnterTransition?, androidx.compose.animation/ExitTransition?, androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntRect>?, kotlin/Function3<androidx.compose.material3.adaptive.layout/AnimatedPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // androidx.compose.material3.adaptive.layout/AnimatedPane|AnimatedPane@androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldPaneScope<0:0,0:1>(androidx.compose.ui.Modifier?;androidx.compose.animation.EnterTransition?;androidx.compose.animation.ExitTransition?;androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect>?;kotlin.Function3<androidx.compose.material3.adaptive.layout.AnimatedPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§<androidx.compose.material3.adaptive.layout.PaneScaffoldRole>;1§<androidx.compose.material3.adaptive.layout.PaneScaffoldValue<0:0>>}[0]
final fun <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole, #B: androidx.compose.material3.adaptive.layout/PaneScaffoldValue<#A>> (androidx.compose.material3.adaptive.layout/ExtendedPaneScaffoldPaneScope<#A, #B>).androidx.compose.material3.adaptive.layout/AnimatedPane(androidx.compose.ui/Modifier?, androidx.compose.animation/EnterTransition?, androidx.compose.animation/ExitTransition?, androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntRect>?, kotlin/Function3<androidx.compose.material3.adaptive.layout/DragToResizeState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, kotlin/Function3<androidx.compose.material3.adaptive.layout/AnimatedPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // androidx.compose.material3.adaptive.layout/AnimatedPane|AnimatedPane@androidx.compose.material3.adaptive.layout.ExtendedPaneScaffoldPaneScope<0:0,0:1>(androidx.compose.ui.Modifier?;androidx.compose.animation.EnterTransition?;androidx.compose.animation.ExitTransition?;androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect>?;kotlin.Function3<androidx.compose.material3.adaptive.layout.DragToResizeState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;kotlin.Function3<androidx.compose.material3.adaptive.layout.AnimatedPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§<androidx.compose.material3.adaptive.layout.PaneScaffoldRole>;1§<androidx.compose.material3.adaptive.layout.PaneScaffoldValue<0:0>>}[0]
final fun <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole> (androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider<#A>).androidx.compose.material3.adaptive.layout/calculateDefaultEnterTransition(#A): androidx.compose.animation/EnterTransition // androidx.compose.material3.adaptive.layout/calculateDefaultEnterTransition|calculateDefaultEnterTransition@androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<0:0>(0:0){0§<androidx.compose.material3.adaptive.layout.PaneScaffoldRole>}[0]
final fun <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole> (androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider<#A>).androidx.compose.material3.adaptive.layout/calculateDefaultExitTransition(#A): androidx.compose.animation/ExitTransition // androidx.compose.material3.adaptive.layout/calculateDefaultExitTransition|calculateDefaultExitTransition@androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<0:0>(0:0){0§<androidx.compose.material3.adaptive.layout.PaneScaffoldRole>}[0]
final fun androidx.compose.material3.adaptive.layout/LevitatedPaneScrim(androidx.compose.ui/Modifier?, kotlin/Function0<kotlin/Unit>?, androidx.compose.ui.graphics/Color, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // androidx.compose.material3.adaptive.layout/LevitatedPaneScrim|LevitatedPaneScrim(androidx.compose.ui.Modifier?;kotlin.Function0<kotlin.Unit>?;androidx.compose.ui.graphics.Color;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout/PaneScaffoldDirective, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, androidx.compose.ui/Modifier?, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, androidx.compose.material3.adaptive.layout/PaneExpansionState?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffold|ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;androidx.compose.ui.Modifier?;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;kotlin.Function4<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.material3.adaptive.layout.PaneExpansionState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.PaneExpansionState?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout/PaneScaffoldDirective, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, androidx.compose.ui/Modifier?, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, androidx.compose.material3.adaptive.layout/PaneExpansionState?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // androidx.compose.material3.adaptive.layout/ListDetailPaneScaffold|ListDetailPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;androidx.compose.ui.Modifier?;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;kotlin.Function4<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.material3.adaptive.layout.PaneExpansionState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.PaneExpansionState?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/SupportingPaneScaffold(androidx.compose.material3.adaptive.layout/PaneScaffoldDirective, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, androidx.compose.ui/Modifier?, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, androidx.compose.material3.adaptive.layout/PaneExpansionState?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // androidx.compose.material3.adaptive.layout/SupportingPaneScaffold|SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldState;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;androidx.compose.ui.Modifier?;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;kotlin.Function4<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.material3.adaptive.layout.PaneExpansionState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.PaneExpansionState?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/SupportingPaneScaffold(androidx.compose.material3.adaptive.layout/PaneScaffoldDirective, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, androidx.compose.ui/Modifier?, kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldPaneScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, androidx.compose.material3.adaptive.layout/PaneExpansionState?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // androidx.compose.material3.adaptive.layout/SupportingPaneScaffold|SupportingPaneScaffold(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;androidx.compose.ui.Modifier?;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldPaneScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;kotlin.Function4<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.material3.adaptive.layout.PaneExpansionState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.PaneExpansionState?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AdaptStrategy_Levitate$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AdaptStrategy_Levitate$stableprop_getter|androidx_compose_material3_adaptive_layout_AdaptStrategy_Levitate$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AdaptStrategy_Reflow$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AdaptStrategy_Reflow$stableprop_getter|androidx_compose_material3_adaptive_layout_AdaptStrategy_Reflow$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AnimatedPaneOverrideScope$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_AnimatedPaneOverrideScope$stableprop_getter|androidx_compose_material3_adaptive_layout_AnimatedPaneOverrideScope$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_DragToResizeState$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_DragToResizeState$stableprop_getter|androidx_compose_material3_adaptive_layout_DragToResizeState$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_LevitatedPaneScrimDefaults$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_LevitatedPaneScrimDefaults$stableprop_getter|androidx_compose_material3_adaptive_layout_LevitatedPaneScrimDefaults$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldDefaults$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldDefaults$stableprop_getter|androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldDefaults$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldRole$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldRole$stableprop_getter|androidx_compose_material3_adaptive_layout_ListDetailPaneScaffoldRole$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_MutableThreePaneScaffoldState$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_MutableThreePaneScaffoldState$stableprop_getter|androidx_compose_material3_adaptive_layout_MutableThreePaneScaffoldState$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Levitated$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Levitated$stableprop_getter|androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Levitated$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Reflowed$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Reflowed$stableprop_getter|androidx_compose_material3_adaptive_layout_PaneAdaptedValue_Reflowed$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor$stableprop_getter|androidx_compose_material3_adaptive_layout_PaneExpansionAnchor$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Offset$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Offset$stableprop_getter|androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Offset$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Proportion$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Proportion$stableprop_getter|androidx_compose_material3_adaptive_layout_PaneExpansionAnchor_Proportion$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionState$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneExpansionState$stableprop_getter|androidx_compose_material3_adaptive_layout_PaneExpansionState$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneMotionData$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneMotionData$stableprop_getter|androidx_compose_material3_adaptive_layout_PaneMotionData$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneMotionDefaults$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneMotionDefaults$stableprop_getter|androidx_compose_material3_adaptive_layout_PaneMotionDefaults$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneScaffoldDirective$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_PaneScaffoldDirective$stableprop_getter|androidx_compose_material3_adaptive_layout_PaneScaffoldDirective$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldDefaults$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldDefaults$stableprop_getter|androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldDefaults$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldRole$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldRole$stableprop_getter|androidx_compose_material3_adaptive_layout_SupportingPaneScaffoldRole$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneMotion$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneMotion$stableprop_getter|androidx_compose_material3_adaptive_layout_ThreePaneMotion$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldAdaptStrategies$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldAdaptStrategies$stableprop_getter|androidx_compose_material3_adaptive_layout_ThreePaneScaffoldAdaptStrategies$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldDestinationItem$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldDestinationItem$stableprop_getter|androidx_compose_material3_adaptive_layout_ThreePaneScaffoldDestinationItem$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldHorizontalOrder$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldHorizontalOrder$stableprop_getter|androidx_compose_material3_adaptive_layout_ThreePaneScaffoldHorizontalOrder$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldOverrideScope$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldOverrideScope$stableprop_getter|androidx_compose_material3_adaptive_layout_ThreePaneScaffoldOverrideScope$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldState$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldState$stableprop_getter|androidx_compose_material3_adaptive_layout_ThreePaneScaffoldState$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldValue$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.layout/androidx_compose_material3_adaptive_layout_ThreePaneScaffoldValue$stableprop_getter|androidx_compose_material3_adaptive_layout_ThreePaneScaffoldValue$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.layout/calculatePaneScaffoldDirective(androidx.compose.material3.adaptive/WindowAdaptiveInfo, androidx.compose.material3.adaptive.layout/HingePolicy = ...): androidx.compose.material3.adaptive.layout/PaneScaffoldDirective // androidx.compose.material3.adaptive.layout/calculatePaneScaffoldDirective|calculatePaneScaffoldDirective(androidx.compose.material3.adaptive.WindowAdaptiveInfo;androidx.compose.material3.adaptive.layout.HingePolicy){}[0]
final fun androidx.compose.material3.adaptive.layout/calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(androidx.compose.material3.adaptive/WindowAdaptiveInfo, androidx.compose.material3.adaptive.layout/HingePolicy = ...): androidx.compose.material3.adaptive.layout/PaneScaffoldDirective // androidx.compose.material3.adaptive.layout/calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth|calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(androidx.compose.material3.adaptive.WindowAdaptiveInfo;androidx.compose.material3.adaptive.layout.HingePolicy){}[0]
final fun androidx.compose.material3.adaptive.layout/calculateThreePaneScaffoldValue(kotlin/Int, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem<*>?, kotlin/Int = ...): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue // androidx.compose.material3.adaptive.layout/calculateThreePaneScaffoldValue|calculateThreePaneScaffoldValue(kotlin.Int;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<*>?;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/calculateThreePaneScaffoldValue(kotlin/Int, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies, kotlin.collections/List<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem<*>>, kotlin/Int = ...): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue // androidx.compose.material3.adaptive.layout/calculateThreePaneScaffoldValue|calculateThreePaneScaffoldValue(kotlin.Int;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies;kotlin.collections.List<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<*>>;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/rememberDragToResizeState(androidx.compose.material3.adaptive.layout/DockedEdge, androidx.compose.ui.unit/Dp, androidx.compose.ui.unit/Dp, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.layout/DragToResizeState // androidx.compose.material3.adaptive.layout/rememberDragToResizeState|rememberDragToResizeState(androidx.compose.material3.adaptive.layout.DockedEdge;androidx.compose.ui.unit.Dp;androidx.compose.ui.unit.Dp;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/rememberPaneExpansionState(androidx.compose.material3.adaptive.layout/PaneExpansionStateKey?, kotlin.collections/List<androidx.compose.material3.adaptive.layout/PaneExpansionAnchor>?, kotlin/Int, androidx.compose.animation.core/FiniteAnimationSpec<kotlin/Float>?, androidx.compose.foundation.gestures/FlingBehavior?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.layout/PaneExpansionState // androidx.compose.material3.adaptive.layout/rememberPaneExpansionState|rememberPaneExpansionState(androidx.compose.material3.adaptive.layout.PaneExpansionStateKey?;kotlin.collections.List<androidx.compose.material3.adaptive.layout.PaneExpansionAnchor>?;kotlin.Int;androidx.compose.animation.core.FiniteAnimationSpec<kotlin.Float>?;androidx.compose.foundation.gestures.FlingBehavior?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/rememberPaneExpansionState(androidx.compose.material3.adaptive.layout/PaneExpansionStateKey?, kotlin.collections/List<androidx.compose.material3.adaptive.layout/PaneExpansionAnchor>?, kotlin/Int, androidx.compose.animation.core/FiniteAnimationSpec<kotlin/Float>?, androidx.compose.foundation.gestures/FlingBehavior?, kotlin/Function1<kotlin/Float, kotlin/Float>?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.layout/PaneExpansionState // androidx.compose.material3.adaptive.layout/rememberPaneExpansionState|rememberPaneExpansionState(androidx.compose.material3.adaptive.layout.PaneExpansionStateKey?;kotlin.collections.List<androidx.compose.material3.adaptive.layout.PaneExpansionAnchor>?;kotlin.Int;androidx.compose.animation.core.FiniteAnimationSpec<kotlin.Float>?;androidx.compose.foundation.gestures.FlingBehavior?;kotlin.Function1<kotlin.Float,kotlin.Float>?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/rememberPaneExpansionState(androidx.compose.material3.adaptive.layout/PaneExpansionStateKeyProvider, kotlin.collections/List<androidx.compose.material3.adaptive.layout/PaneExpansionAnchor>?, kotlin/Int, androidx.compose.animation.core/FiniteAnimationSpec<kotlin/Float>?, androidx.compose.foundation.gestures/FlingBehavior?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.layout/PaneExpansionState // androidx.compose.material3.adaptive.layout/rememberPaneExpansionState|rememberPaneExpansionState(androidx.compose.material3.adaptive.layout.PaneExpansionStateKeyProvider;kotlin.collections.List<androidx.compose.material3.adaptive.layout.PaneExpansionAnchor>?;kotlin.Int;androidx.compose.animation.core.FiniteAnimationSpec<kotlin.Float>?;androidx.compose.foundation.gestures.FlingBehavior?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.layout/rememberPaneExpansionState(androidx.compose.material3.adaptive.layout/PaneExpansionStateKeyProvider, kotlin.collections/List<androidx.compose.material3.adaptive.layout/PaneExpansionAnchor>?, kotlin/Int, androidx.compose.animation.core/FiniteAnimationSpec<kotlin/Float>?, androidx.compose.foundation.gestures/FlingBehavior?, kotlin/Function1<kotlin/Float, kotlin/Float>?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.layout/PaneExpansionState // androidx.compose.material3.adaptive.layout/rememberPaneExpansionState|rememberPaneExpansionState(androidx.compose.material3.adaptive.layout.PaneExpansionStateKeyProvider;kotlin.collections.List<androidx.compose.material3.adaptive.layout.PaneExpansionAnchor>?;kotlin.Int;androidx.compose.animation.core.FiniteAnimationSpec<kotlin.Float>?;androidx.compose.foundation.gestures.FlingBehavior?;kotlin.Function1<kotlin.Float,kotlin.Float>?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final inline fun <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole> (androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider<#A>).androidx.compose.material3.adaptive.layout/forEach(kotlin/Function2<#A, androidx.compose.material3.adaptive.layout/PaneMotionData, kotlin/Unit>) // androidx.compose.material3.adaptive.layout/forEach|forEach@androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<0:0>(kotlin.Function2<0:0,androidx.compose.material3.adaptive.layout.PaneMotionData,kotlin.Unit>){0§<androidx.compose.material3.adaptive.layout.PaneScaffoldRole>}[0]
final inline fun <#A: androidx.compose.material3.adaptive.layout/PaneScaffoldRole> (androidx.compose.material3.adaptive.layout/PaneScaffoldMotionDataProvider<#A>).androidx.compose.material3.adaptive.layout/forEachReversed(kotlin/Function2<#A, androidx.compose.material3.adaptive.layout/PaneMotionData, kotlin/Unit>) // androidx.compose.material3.adaptive.layout/forEachReversed|forEachReversed@androidx.compose.material3.adaptive.layout.PaneScaffoldMotionDataProvider<0:0>(kotlin.Function2<0:0,androidx.compose.material3.adaptive.layout.PaneMotionData,kotlin.Unit>){0§<androidx.compose.material3.adaptive.layout.PaneScaffoldRole>}[0]
@@ -0,0 +1,52 @@
// :adaptive-layout - androidx.compose.material3.adaptive.layout.* (ListDetailPaneScaffold,
// SupportingPaneScaffold, ThreePaneScaffold + pane motion / expansion machinery), vendored
// VERBATIM from JetBrains/compose-multiplatform-core into src/vendor/. See :adaptive for why
// upstream ships no linux / mingw artifact.
//
// Upstream's `skikoMain` carries the l10n translation tables (~75 files) plus the Strings and
// drag-handle actuals; for this port every native target is a skiko target, so it lands in
// src/vendor/native/ alongside upstream's own nativeMain actuals. The two sets actualise
// disjoint expects, so they coexist in one source set.
//
// Provenance = compose-fork.txt + scripts/compose-fork/compose.properties. Never hand-edit
// src/vendor - change the manifest + `python scripts/compose-fork/sync.py compose/material3/adaptive/adaptive-layout`.
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.plugin.compose)
}
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
kotlin {
linuxArm64(); linuxX64(); macosArm64(); if (vHostSupportsMingw) mingwX64()
applyDefaultHierarchyTemplate()
sourceSets {
commonMain {
dependencies {
api(project(":compose:material3:adaptive:adaptive"))
api(project(":compose:ui:ui"))
api(project(":compose:animation:animation-core"))
api(libs.androidx.collection)
implementation(project(":compose:animation:animation"))
implementation(project(":compose:foundation:foundation"))
implementation(project(":compose:foundation:foundation-layout"))
implementation(project(":compose:ui:ui-geometry"))
implementation(libs.androidx.window.core)
}
kotlin.srcDir("src/vendor/common/kotlin")
}
nativeMain {
kotlin.srcDir("src/vendor/native/kotlin")
}
}
compilerOptions {
freeCompilerArgs.addAll(
"-Xexpect-actual-classes",
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
"-opt-in=androidx.compose.ui.InternalComposeUiApi",
"-opt-in=androidx.compose.animation.ExperimentalAnimationApi",
"-opt-in=androidx.compose.animation.ExperimentalSharedTransitionApi",
"-opt-in=androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi",
)
}
}
@@ -0,0 +1,133 @@
SET_REPO=https://github.com/JetBrains/compose-multiplatform-core@<COMPOSE_CORE_REF>
SET_FOLDER=compose/material3/adaptive/adaptive-layout/src
commonMain/kotlin/ -> src/vendor/common/kotlin/
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/AdaptStrategy.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/AdaptStrategy.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateBoundsModifier.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/AnimateBoundsModifier.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateModifierUtils.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/AnimateModifierUtils.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/AnimateWithFadingModifier.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/AnimateWithFadingModifier.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/DragToResizeState.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/DragToResizeState.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/LevitatedPaneScrim.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/LevitatedPaneScrim.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/ListDetailPaneScaffold.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/Pane.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/Pane.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneAdaptedValue.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PaneAdaptedValue.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionDraggableModifier.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionDraggableModifier.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionState.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionState.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneMargins.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PaneMargins.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneMotion.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PaneMotion.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldDirective.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldHorizontalOrder.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldHorizontalOrder.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldRole.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldRole.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldValue.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffoldValue.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/PredictiveBackScaleState.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/PredictiveBackScaleState.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/SupportingPaneScaffold.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneMotion.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneMotion.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffold.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldAdaptStrategies.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldAdaptStrategies.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldDestinationItem.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldDestinationItem.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldHorizontalOrder.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldHorizontalOrder.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldScope.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldScope.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldState.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldState.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldValue.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/ThreePaneScaffoldValue.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/WindowSizeClassHelper.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/WindowSizeClassHelper.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/internal/DelegableModifier.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/internal/DelegableModifier.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/internal/RememberUtils.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/internal/RememberUtils.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/internal/Strings.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/internal/Strings.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/layout/internal/System.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/layout/internal/System.kt
skikoMain/kotlin/ -> src/vendor/native/kotlin/
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Af.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Af.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Am.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Am.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ar.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ar.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/As.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/As.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Az.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Az.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Be.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Be.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Bg.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Bg.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Bn.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Bn.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Bs.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Bs.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ca.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ca.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Cs.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Cs.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Da.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Da.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/De.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/De.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/El.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/El.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/En.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/En.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Es.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Es.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Et.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Et.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Eu.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Eu.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Fa.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Fa.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Fi.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Fi.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Fr.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Fr.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Gl.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Gl.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Gu.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Gu.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Hi.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Hi.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Hr.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Hr.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Hu.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Hu.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Hy.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Hy.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/In.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/In.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Is.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Is.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/It.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/It.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Iw.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Iw.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ja.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ja.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ka.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ka.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Kk.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Kk.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Km.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Km.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Kn.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Kn.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ko.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ko.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ky.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ky.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Lo.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Lo.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Lt.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Lt.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Lv.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Lv.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Mk.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Mk.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ml.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ml.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Mn.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Mn.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Mr.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Mr.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ms.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ms.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/My.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/My.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Nb.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Nb.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ne.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ne.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Nl.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Nl.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Or.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Or.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Pa.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Pa.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Pl.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Pl.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Pt.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Pt.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ro.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ro.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ru.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ru.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Si.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Si.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Sk.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Sk.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Sl.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Sl.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Sq.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Sq.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Sr.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Sr.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Sv.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Sv.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Sw.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Sw.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ta.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ta.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Te.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Te.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Th.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Th.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Tl.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Tl.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Tr.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Tr.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Translations.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Translations.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Uk.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Uk.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Ur.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Ur.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Uz.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Uz.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Vi.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Vi.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Zh.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Zh.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/l10n/Zu.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/l10n/Zu.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionDragHandle.skiko.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionDragHandle.skiko.kt
# | skikoMain/kotlin/androidx/compose/material3/adaptive/layout/internal/Strings.skiko.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/layout/internal/Strings.skiko.kt
nativeMain/kotlin/ -> src/vendor/native/kotlin/
# | nativeMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.native.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.native.kt
# | nativeMain/kotlin/androidx/compose/material3/adaptive/layout/internal/System.native.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/layout/internal/System.native.kt
# >>> ---- DIAGNOSTIC GAPS ---- <<<<
# androidMain (2)
# | androidMain/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionDraggableModifier.android.kt -> src/vendor/android/kotlin/androidx/compose/material3/adaptive/layout/PaneExpansionDraggableModifier.android.kt
# | androidMain/kotlin/androidx/compose/material3/adaptive/layout/internal/Strings.android.kt -> src/vendor/android/kotlin/androidx/compose/material3/adaptive/layout/internal/Strings.android.kt
# jsMain (2)
# | jsMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.js.kt -> src/vendor/js/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.js.kt
# | jsMain/kotlin/androidx/compose/material3/adaptive/layout/internal/System.js.kt -> src/vendor/js/kotlin/androidx/compose/material3/adaptive/layout/internal/System.js.kt
# jvmAndAndroidMain (2)
# | jvmAndAndroidMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.jvmAndAndroid.kt -> src/vendor/jvmAndAndroid/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.jvmAndAndroid.kt
# | jvmAndAndroidMain/kotlin/androidx/compose/material3/adaptive/layout/internal/System.jvmAndAndroid.kt -> src/vendor/jvmAndAndroid/kotlin/androidx/compose/material3/adaptive/layout/internal/System.jvmAndAndroid.kt
# wasmJsMain (2)
# | wasmJsMain/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.wasm.kt -> src/vendor/wasmJs/kotlin/androidx/compose/material3/adaptive/layout/PaneScaffold.wasm.kt
# | wasmJsMain/kotlin/androidx/compose/material3/adaptive/layout/internal/System.wasm.kt -> src/vendor/wasmJs/kotlin/androidx/compose/material3/adaptive/layout/internal/System.wasm.kt
# <<< ---- DIAGNOSTIC GAPS ---- >>>>
@@ -0,0 +1,50 @@
// Klib ABI Dump
// Targets: [linuxArm64, linuxX64, macosArm64, mingwX64]
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: true
// - Show declarations: true
// Library unique name: <com.bitsycore.compose.material3.adaptive:adaptive-navigation>
abstract interface <#A: kotlin/Any?> androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator { // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator|null[0]
abstract val currentDestination // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.currentDestination|{}currentDestination[0]
abstract fun <get-currentDestination>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem<#A>? // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.currentDestination.<get-currentDestination>|<get-currentDestination>(){}[0]
abstract val scaffoldDirective // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.scaffoldDirective|{}scaffoldDirective[0]
abstract fun <get-scaffoldDirective>(): androidx.compose.material3.adaptive.layout/PaneScaffoldDirective // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.scaffoldDirective.<get-scaffoldDirective>|<get-scaffoldDirective>(){}[0]
abstract val scaffoldState // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.scaffoldState|{}scaffoldState[0]
abstract fun <get-scaffoldState>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldState // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.scaffoldState.<get-scaffoldState>|<get-scaffoldState>(){}[0]
abstract val scaffoldValue // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.scaffoldValue|{}scaffoldValue[0]
abstract fun <get-scaffoldValue>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.scaffoldValue.<get-scaffoldValue>|<get-scaffoldValue>(){}[0]
abstract var isDestinationHistoryAware // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.isDestinationHistoryAware|{}isDestinationHistoryAware[0]
abstract fun <get-isDestinationHistoryAware>(): kotlin/Boolean // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.isDestinationHistoryAware.<get-isDestinationHistoryAware>|<get-isDestinationHistoryAware>(){}[0]
abstract fun <set-isDestinationHistoryAware>(kotlin/Boolean) // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.isDestinationHistoryAware.<set-isDestinationHistoryAware>|<set-isDestinationHistoryAware>(kotlin.Boolean){}[0]
abstract fun canNavigateBack(androidx.compose.material3.adaptive.navigation/BackNavigationBehavior = ...): kotlin/Boolean // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.canNavigateBack|canNavigateBack(androidx.compose.material3.adaptive.navigation.BackNavigationBehavior){}[0]
abstract fun peekPreviousScaffoldValue(androidx.compose.material3.adaptive.navigation/BackNavigationBehavior = ...): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.peekPreviousScaffoldValue|peekPreviousScaffoldValue(androidx.compose.material3.adaptive.navigation.BackNavigationBehavior){}[0]
abstract suspend fun navigateBack(androidx.compose.material3.adaptive.navigation/BackNavigationBehavior = ...): kotlin/Boolean // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.navigateBack|navigateBack(androidx.compose.material3.adaptive.navigation.BackNavigationBehavior){}[0]
abstract suspend fun navigateTo(androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole, #A? = ...) // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.navigateTo|navigateTo(androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole;1:0?){}[0]
abstract suspend fun seekBack(androidx.compose.material3.adaptive.navigation/BackNavigationBehavior = ..., kotlin/Float = ...) // androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator.seekBack|seekBack(androidx.compose.material3.adaptive.navigation.BackNavigationBehavior;kotlin.Float){}[0]
}
final value class androidx.compose.material3.adaptive.navigation/BackNavigationBehavior { // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior|null[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.toString|toString(){}[0]
final object Companion { // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.Companion|null[0]
final val PopLatest // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.Companion.PopLatest|{}PopLatest[0]
final fun <get-PopLatest>(): androidx.compose.material3.adaptive.navigation/BackNavigationBehavior // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.Companion.PopLatest.<get-PopLatest>|<get-PopLatest>(){}[0]
final val PopUntilContentChange // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.Companion.PopUntilContentChange|{}PopUntilContentChange[0]
final fun <get-PopUntilContentChange>(): androidx.compose.material3.adaptive.navigation/BackNavigationBehavior // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.Companion.PopUntilContentChange.<get-PopUntilContentChange>|<get-PopUntilContentChange>(){}[0]
final val PopUntilCurrentDestinationChange // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.Companion.PopUntilCurrentDestinationChange|{}PopUntilCurrentDestinationChange[0]
final fun <get-PopUntilCurrentDestinationChange>(): androidx.compose.material3.adaptive.navigation/BackNavigationBehavior // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.Companion.PopUntilCurrentDestinationChange.<get-PopUntilCurrentDestinationChange>|<get-PopUntilCurrentDestinationChange>(){}[0]
final val PopUntilScaffoldValueChange // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.Companion.PopUntilScaffoldValueChange|{}PopUntilScaffoldValueChange[0]
final fun <get-PopUntilScaffoldValueChange>(): androidx.compose.material3.adaptive.navigation/BackNavigationBehavior // androidx.compose.material3.adaptive.navigation/BackNavigationBehavior.Companion.PopUntilScaffoldValueChange.<get-PopUntilScaffoldValueChange>|<get-PopUntilScaffoldValueChange>(){}[0]
}
}
final fun <#A: kotlin/Any?> androidx.compose.material3.adaptive.navigation/rememberListDetailPaneScaffoldNavigator(androidx.compose.material3.adaptive.layout/PaneScaffoldDirective?, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies?, kotlin/Boolean, kotlin.collections/List<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem<#A>>?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator<#A> // androidx.compose.material3.adaptive.navigation/rememberListDetailPaneScaffoldNavigator|rememberListDetailPaneScaffoldNavigator(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective?;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies?;kotlin.Boolean;kotlin.collections.List<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<0:0>>?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§<kotlin.Any?>}[0]
final fun <#A: kotlin/Any?> androidx.compose.material3.adaptive.navigation/rememberSupportingPaneScaffoldNavigator(androidx.compose.material3.adaptive.layout/PaneScaffoldDirective?, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies?, kotlin/Boolean, kotlin.collections/List<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldDestinationItem<#A>>?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator<#A> // androidx.compose.material3.adaptive.navigation/rememberSupportingPaneScaffoldNavigator|rememberSupportingPaneScaffoldNavigator(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective?;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies?;kotlin.Boolean;kotlin.collections.List<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldDestinationItem<0:0>>?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§<kotlin.Any?>}[0]
final fun androidx.compose.material3.adaptive.navigation/rememberListDetailPaneScaffoldNavigator(androidx.compose.material3.adaptive.layout/PaneScaffoldDirective?, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies?, kotlin/Boolean, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator<kotlin/Any> // androidx.compose.material3.adaptive.navigation/rememberListDetailPaneScaffoldNavigator|rememberListDetailPaneScaffoldNavigator(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective?;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies?;kotlin.Boolean;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive.navigation/rememberSupportingPaneScaffoldNavigator(androidx.compose.material3.adaptive.layout/PaneScaffoldDirective?, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies?, kotlin/Boolean, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.navigation/ThreePaneScaffoldNavigator<kotlin/Any> // androidx.compose.material3.adaptive.navigation/rememberSupportingPaneScaffoldNavigator|rememberSupportingPaneScaffoldNavigator(androidx.compose.material3.adaptive.layout.PaneScaffoldDirective?;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies?;kotlin.Boolean;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
@@ -0,0 +1,35 @@
// :adaptive-navigation - androidx.compose.material3.adaptive.navigation.*
// (ThreePaneScaffoldNavigator + back-navigation behaviour), vendored VERBATIM from
// JetBrains/compose-multiplatform-core into src/vendor/. See :adaptive for why upstream
// ships no linux / mingw artifact. commonMain only - upstream's platform legs are
// android-specific.
//
// Provenance = compose-fork.txt + scripts/compose-fork/compose.properties. Never hand-edit
// src/vendor - change the manifest + `python scripts/compose-fork/sync.py compose/material3/adaptive/adaptive-navigation`.
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.plugin.compose)
}
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
kotlin {
linuxArm64(); linuxX64(); macosArm64(); if (vHostSupportsMingw) mingwX64()
applyDefaultHierarchyTemplate()
sourceSets {
commonMain {
dependencies {
api(project(":compose:material3:adaptive:adaptive-layout"))
implementation(project(":compose:foundation:foundation"))
implementation(project(":compose:ui:ui-util"))
}
kotlin.srcDir("src/vendor/common/kotlin")
}
}
compilerOptions {
freeCompilerArgs.addAll(
"-Xexpect-actual-classes",
"-opt-in=androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi",
)
}
}
@@ -0,0 +1,12 @@
SET_REPO=https://github.com/JetBrains/compose-multiplatform-core@<COMPOSE_CORE_REF>
SET_FOLDER=compose/material3/adaptive/adaptive-navigation/src
commonMain/kotlin/ -> src/vendor/common/kotlin/
# | commonMain/kotlin/androidx/compose/material3/adaptive/navigation/BackNavigationBehavior.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/navigation/BackNavigationBehavior.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldNavigator.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldNavigator.kt
# >>> ---- DIAGNOSTIC GAPS ---- <<<<
# androidMain (2)
# | androidMain/kotlin/androidx/compose/material3/adaptive/navigation/AndroidThreePaneScaffold.android.kt -> src/vendor/android/kotlin/androidx/compose/material3/adaptive/navigation/AndroidThreePaneScaffold.android.kt
# | androidMain/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldPredictiveBackHandler.android.kt -> src/vendor/android/kotlin/androidx/compose/material3/adaptive/navigation/ThreePaneScaffoldPredictiveBackHandler.android.kt
# <<< ---- DIAGNOSTIC GAPS ---- >>>>
@@ -0,0 +1,93 @@
// Klib ABI Dump
// Targets: [linuxArm64, linuxX64, macosArm64, mingwX64]
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: true
// - Show declarations: true
// Library unique name: <com.bitsycore.compose.material3.adaptive:adaptive-navigation3>
sealed interface androidx.compose.material3.adaptive.navigation3/ListDetailSceneScope { // androidx.compose.material3.adaptive.navigation3/ListDetailSceneScope|null[0]
abstract val scaffoldTransitionScope // androidx.compose.material3.adaptive.navigation3/ListDetailSceneScope.scaffoldTransitionScope|{}scaffoldTransitionScope[0]
abstract fun <get-scaffoldTransitionScope>(): androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue> // androidx.compose.material3.adaptive.navigation3/ListDetailSceneScope.scaffoldTransitionScope.<get-scaffoldTransitionScope>|<get-scaffoldTransitionScope>(){}[0]
}
sealed interface androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneScope { // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneScope|null[0]
abstract val scaffoldTransitionScope // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneScope.scaffoldTransitionScope|{}scaffoldTransitionScope[0]
abstract fun <get-scaffoldTransitionScope>(): androidx.compose.material3.adaptive.layout/PaneScaffoldTransitionScope<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldRole, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldValue> // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneScope.scaffoldTransitionScope.<get-scaffoldTransitionScope>|<get-scaffoldTransitionScope>(){}[0]
}
final class <#A: kotlin/Any> androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy : androidx.navigation3.scene/SceneStrategy<#A> { // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy|null[0]
constructor <init>(kotlin/Boolean, androidx.compose.material3.adaptive.navigation/BackNavigationBehavior, androidx.compose.material3.adaptive.layout/PaneScaffoldDirective, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies, kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, androidx.compose.material3.adaptive.layout/PaneExpansionState?) // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.<init>|<init>(kotlin.Boolean;androidx.compose.material3.adaptive.navigation.BackNavigationBehavior;androidx.compose.material3.adaptive.layout.PaneScaffoldDirective;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies;kotlin.Function4<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.material3.adaptive.layout.PaneExpansionState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.PaneExpansionState?){}[0]
final val adaptStrategies // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.adaptStrategies|{}adaptStrategies[0]
final fun <get-adaptStrategies>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.adaptStrategies.<get-adaptStrategies>|<get-adaptStrategies>(){}[0]
final val backNavigationBehavior // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.backNavigationBehavior|{}backNavigationBehavior[0]
final fun <get-backNavigationBehavior>(): androidx.compose.material3.adaptive.navigation/BackNavigationBehavior // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.backNavigationBehavior.<get-backNavigationBehavior>|<get-backNavigationBehavior>(){}[0]
final val directive // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.directive|{}directive[0]
final fun <get-directive>(): androidx.compose.material3.adaptive.layout/PaneScaffoldDirective // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.directive.<get-directive>|<get-directive>(){}[0]
final val paneExpansionDragHandle // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.paneExpansionDragHandle|{}paneExpansionDragHandle[0]
final fun <get-paneExpansionDragHandle>(): kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>? // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.paneExpansionDragHandle.<get-paneExpansionDragHandle>|<get-paneExpansionDragHandle>(){}[0]
final val paneExpansionState // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.paneExpansionState|{}paneExpansionState[0]
final fun <get-paneExpansionState>(): androidx.compose.material3.adaptive.layout/PaneExpansionState? // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.paneExpansionState.<get-paneExpansionState>|<get-paneExpansionState>(){}[0]
final val shouldHandleSinglePaneLayout // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.shouldHandleSinglePaneLayout|{}shouldHandleSinglePaneLayout[0]
final fun <get-shouldHandleSinglePaneLayout>(): kotlin/Boolean // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.shouldHandleSinglePaneLayout.<get-shouldHandleSinglePaneLayout>|<get-shouldHandleSinglePaneLayout>(){}[0]
final fun (androidx.navigation3.scene/SceneStrategyScope<#A>).calculateScene(kotlin.collections/List<androidx.navigation3.runtime/NavEntry<#A>>): androidx.navigation3.scene/Scene<#A>? // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.calculateScene|calculateScene@androidx.navigation3.scene.SceneStrategyScope<1:0>(kotlin.collections.List<androidx.navigation3.runtime.NavEntry<1:0>>){}[0]
final object Companion { // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.Companion|null[0]
final fun detailPane(kotlin/Any = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.Companion.detailPane|detailPane(kotlin.Any){}[0]
final fun extraPane(kotlin/Any = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.Companion.extraPane|extraPane(kotlin.Any){}[0]
final fun listPane(kotlin/Any = ..., kotlin/Function3<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit> = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.Companion.listPane|listPane(kotlin.Any;kotlin.Function3<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>){}[0]
final fun paneAnimation(androidx.compose.animation/EnterTransition? = ..., androidx.compose.animation/ExitTransition? = ..., androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntRect>? = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.Companion.paneAnimation|paneAnimation(androidx.compose.animation.EnterTransition?;androidx.compose.animation.ExitTransition?;androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect>?){}[0]
final fun preferredPaneSize(androidx.compose.ui.unit/Dp = ..., androidx.compose.ui.unit/Dp = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.Companion.preferredPaneSize|preferredPaneSize(androidx.compose.ui.unit.Dp;androidx.compose.ui.unit.Dp){}[0]
final fun preferredPaneSize(kotlin/Float = ..., kotlin/Float = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy.Companion.preferredPaneSize|preferredPaneSize(kotlin.Float;kotlin.Float){}[0]
}
}
final class <#A: kotlin/Any> androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy : androidx.navigation3.scene/SceneStrategy<#A> { // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy|null[0]
constructor <init>(kotlin/Boolean, androidx.compose.material3.adaptive.navigation/BackNavigationBehavior, androidx.compose.material3.adaptive.layout/PaneScaffoldDirective, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies, kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, androidx.compose.material3.adaptive.layout/PaneExpansionState?) // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.<init>|<init>(kotlin.Boolean;androidx.compose.material3.adaptive.navigation.BackNavigationBehavior;androidx.compose.material3.adaptive.layout.PaneScaffoldDirective;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies;kotlin.Function4<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.material3.adaptive.layout.PaneExpansionState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.PaneExpansionState?){}[0]
final val adaptStrategies // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.adaptStrategies|{}adaptStrategies[0]
final fun <get-adaptStrategies>(): androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.adaptStrategies.<get-adaptStrategies>|<get-adaptStrategies>(){}[0]
final val backNavigationBehavior // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.backNavigationBehavior|{}backNavigationBehavior[0]
final fun <get-backNavigationBehavior>(): androidx.compose.material3.adaptive.navigation/BackNavigationBehavior // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.backNavigationBehavior.<get-backNavigationBehavior>|<get-backNavigationBehavior>(){}[0]
final val directive // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.directive|{}directive[0]
final fun <get-directive>(): androidx.compose.material3.adaptive.layout/PaneScaffoldDirective // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.directive.<get-directive>|<get-directive>(){}[0]
final val paneExpansionDragHandle // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.paneExpansionDragHandle|{}paneExpansionDragHandle[0]
final fun <get-paneExpansionDragHandle>(): kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>? // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.paneExpansionDragHandle.<get-paneExpansionDragHandle>|<get-paneExpansionDragHandle>(){}[0]
final val paneExpansionState // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.paneExpansionState|{}paneExpansionState[0]
final fun <get-paneExpansionState>(): androidx.compose.material3.adaptive.layout/PaneExpansionState? // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.paneExpansionState.<get-paneExpansionState>|<get-paneExpansionState>(){}[0]
final val shouldHandleSinglePaneLayout // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.shouldHandleSinglePaneLayout|{}shouldHandleSinglePaneLayout[0]
final fun <get-shouldHandleSinglePaneLayout>(): kotlin/Boolean // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.shouldHandleSinglePaneLayout.<get-shouldHandleSinglePaneLayout>|<get-shouldHandleSinglePaneLayout>(){}[0]
final fun (androidx.navigation3.scene/SceneStrategyScope<#A>).calculateScene(kotlin.collections/List<androidx.navigation3.runtime/NavEntry<#A>>): androidx.navigation3.scene/Scene<#A>? // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.calculateScene|calculateScene@androidx.navigation3.scene.SceneStrategyScope<1:0>(kotlin.collections.List<androidx.navigation3.runtime.NavEntry<1:0>>){}[0]
final object Companion { // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.Companion|null[0]
final fun extraPane(kotlin/Any = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.Companion.extraPane|extraPane(kotlin.Any){}[0]
final fun mainPane(kotlin/Any = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.Companion.mainPane|mainPane(kotlin.Any){}[0]
final fun paneAnimation(androidx.compose.animation/EnterTransition? = ..., androidx.compose.animation/ExitTransition? = ..., androidx.compose.animation.core/FiniteAnimationSpec<androidx.compose.ui.unit/IntRect>? = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.Companion.paneAnimation|paneAnimation(androidx.compose.animation.EnterTransition?;androidx.compose.animation.ExitTransition?;androidx.compose.animation.core.FiniteAnimationSpec<androidx.compose.ui.unit.IntRect>?){}[0]
final fun preferredPaneSize(androidx.compose.ui.unit/Dp = ..., androidx.compose.ui.unit/Dp = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.Companion.preferredPaneSize|preferredPaneSize(androidx.compose.ui.unit.Dp;androidx.compose.ui.unit.Dp){}[0]
final fun preferredPaneSize(kotlin/Float = ..., kotlin/Float = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.Companion.preferredPaneSize|preferredPaneSize(kotlin.Float;kotlin.Float){}[0]
final fun supportingPane(kotlin/Any = ...): kotlin.collections/Map<kotlin/String, kotlin/Any> // androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy.Companion.supportingPane|supportingPane(kotlin.Any){}[0]
}
}
final val androidx.compose.material3.adaptive.navigation3/LocalListDetailSceneScope // androidx.compose.material3.adaptive.navigation3/LocalListDetailSceneScope|{}LocalListDetailSceneScope[0]
final fun <get-LocalListDetailSceneScope>(): androidx.compose.runtime/ProvidableCompositionLocal<androidx.compose.material3.adaptive.navigation3/ListDetailSceneScope?> // androidx.compose.material3.adaptive.navigation3/LocalListDetailSceneScope.<get-LocalListDetailSceneScope>|<get-LocalListDetailSceneScope>(){}[0]
final val androidx.compose.material3.adaptive.navigation3/LocalSupportingPaneSceneScope // androidx.compose.material3.adaptive.navigation3/LocalSupportingPaneSceneScope|{}LocalSupportingPaneSceneScope[0]
final fun <get-LocalSupportingPaneSceneScope>(): androidx.compose.runtime/ProvidableCompositionLocal<androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneScope?> // androidx.compose.material3.adaptive.navigation3/LocalSupportingPaneSceneScope.<get-LocalSupportingPaneSceneScope>|<get-LocalSupportingPaneSceneScope>(){}[0]
final val androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ListDetailSceneStrategy$stableprop // androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ListDetailSceneStrategy$stableprop|#static{}androidx_compose_material3_adaptive_navigation3_ListDetailSceneStrategy$stableprop[0]
final val androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_SupportingPaneSceneStrategy$stableprop // androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_SupportingPaneSceneStrategy$stableprop|#static{}androidx_compose_material3_adaptive_navigation3_SupportingPaneSceneStrategy$stableprop[0]
final val androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldScene_OnBackResult$stableprop // androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldScene_OnBackResult$stableprop|#static{}androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldScene_OnBackResult$stableprop[0]
final val androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_ListDetail$stableprop // androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_ListDetail$stableprop|#static{}androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_ListDetail$stableprop[0]
final val androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_SupportingPane$stableprop // androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_SupportingPane$stableprop|#static{}androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_SupportingPane$stableprop[0]
final fun <#A: kotlin/Any> androidx.compose.material3.adaptive.navigation3/rememberListDetailSceneStrategy(kotlin/Boolean, androidx.compose.material3.adaptive.navigation/BackNavigationBehavior, androidx.compose.material3.adaptive.layout/PaneScaffoldDirective, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies, kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, androidx.compose.material3.adaptive.layout/PaneExpansionState?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy<#A> // androidx.compose.material3.adaptive.navigation3/rememberListDetailSceneStrategy|rememberListDetailSceneStrategy(kotlin.Boolean;androidx.compose.material3.adaptive.navigation.BackNavigationBehavior;androidx.compose.material3.adaptive.layout.PaneScaffoldDirective;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies;kotlin.Function4<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.material3.adaptive.layout.PaneExpansionState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.PaneExpansionState?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§<kotlin.Any>}[0]
final fun <#A: kotlin/Any> androidx.compose.material3.adaptive.navigation3/rememberListDetailSceneStrategy(kotlin/Boolean, androidx.compose.material3.adaptive.navigation/BackNavigationBehavior?, androidx.compose.material3.adaptive.layout/PaneScaffoldDirective?, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies?, kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, androidx.compose.material3.adaptive.layout/PaneExpansionState?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.navigation3/ListDetailSceneStrategy<#A> // androidx.compose.material3.adaptive.navigation3/rememberListDetailSceneStrategy|rememberListDetailSceneStrategy(kotlin.Boolean;androidx.compose.material3.adaptive.navigation.BackNavigationBehavior?;androidx.compose.material3.adaptive.layout.PaneScaffoldDirective?;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies?;kotlin.Function4<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.material3.adaptive.layout.PaneExpansionState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.PaneExpansionState?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§<kotlin.Any>}[0]
final fun <#A: kotlin/Any> androidx.compose.material3.adaptive.navigation3/rememberSupportingPaneSceneStrategy(kotlin/Boolean, androidx.compose.material3.adaptive.navigation/BackNavigationBehavior, androidx.compose.material3.adaptive.layout/PaneScaffoldDirective, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies, kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, androidx.compose.material3.adaptive.layout/PaneExpansionState?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy<#A> // androidx.compose.material3.adaptive.navigation3/rememberSupportingPaneSceneStrategy|rememberSupportingPaneSceneStrategy(kotlin.Boolean;androidx.compose.material3.adaptive.navigation.BackNavigationBehavior;androidx.compose.material3.adaptive.layout.PaneScaffoldDirective;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies;kotlin.Function4<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.material3.adaptive.layout.PaneExpansionState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.PaneExpansionState?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§<kotlin.Any>}[0]
final fun <#A: kotlin/Any> androidx.compose.material3.adaptive.navigation3/rememberSupportingPaneSceneStrategy(kotlin/Boolean, androidx.compose.material3.adaptive.navigation/BackNavigationBehavior?, androidx.compose.material3.adaptive.layout/PaneScaffoldDirective?, androidx.compose.material3.adaptive.layout/ThreePaneScaffoldAdaptStrategies?, kotlin/Function4<androidx.compose.material3.adaptive.layout/ThreePaneScaffoldScope, androidx.compose.material3.adaptive.layout/PaneExpansionState, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>?, androidx.compose.material3.adaptive.layout/PaneExpansionState?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive.navigation3/SupportingPaneSceneStrategy<#A> // androidx.compose.material3.adaptive.navigation3/rememberSupportingPaneSceneStrategy|rememberSupportingPaneSceneStrategy(kotlin.Boolean;androidx.compose.material3.adaptive.navigation.BackNavigationBehavior?;androidx.compose.material3.adaptive.layout.PaneScaffoldDirective?;androidx.compose.material3.adaptive.layout.ThreePaneScaffoldAdaptStrategies?;kotlin.Function4<androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope,androidx.compose.material3.adaptive.layout.PaneExpansionState,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>?;androidx.compose.material3.adaptive.layout.PaneExpansionState?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§<kotlin.Any>}[0]
final fun androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ListDetailSceneStrategy$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ListDetailSceneStrategy$stableprop_getter|androidx_compose_material3_adaptive_navigation3_ListDetailSceneStrategy$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_SupportingPaneSceneStrategy$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_SupportingPaneSceneStrategy$stableprop_getter|androidx_compose_material3_adaptive_navigation3_SupportingPaneSceneStrategy$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldScene_OnBackResult$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldScene_OnBackResult$stableprop_getter|androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldScene_OnBackResult$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_ListDetail$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_ListDetail$stableprop_getter|androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_ListDetail$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_SupportingPane$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive.navigation3/androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_SupportingPane$stableprop_getter|androidx_compose_material3_adaptive_navigation3_ThreePaneScaffoldType_SupportingPane$stableprop_getter(){}[0]
@@ -0,0 +1,40 @@
// :adaptive-navigation3 - androidx.compose.material3.adaptive.navigation3.*
// (ListDetail / SupportingPane SceneStrategy for Navigation 3), vendored VERBATIM from
// JetBrains/compose-multiplatform-core into src/vendor/. See :adaptive for why upstream
// ships no linux / mingw artifact. commonMain only.
//
// navigation3-ui is the PROJECT module: upstream's org.jetbrains.androidx.navigation3:
// navigation3-ui has no K/N desktop artifact either, which is why :navigation3:navigation3-ui
// exists. navigationevent-compose is a plain Maven dep (api-exposed by :ui).
//
// Provenance = compose-fork.txt + scripts/compose-fork/compose.properties. Never hand-edit
// src/vendor - change the manifest + `python scripts/compose-fork/sync.py compose/material3/adaptive/adaptive-navigation3`.
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.plugin.compose)
}
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
kotlin {
linuxArm64(); linuxX64(); macosArm64(); if (vHostSupportsMingw) mingwX64()
applyDefaultHierarchyTemplate()
sourceSets {
commonMain {
dependencies {
api(project(":compose:material3:adaptive:adaptive-navigation"))
api(project(":navigation3:navigation3-ui"))
implementation(libs.androidx.collection)
implementation(libs.androidx.navigationevent.compose)
}
kotlin.srcDir("src/vendor/common/kotlin")
}
}
compilerOptions {
freeCompilerArgs.addAll(
"-Xexpect-actual-classes",
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
"-opt-in=androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi",
)
}
}
@@ -0,0 +1,13 @@
SET_REPO=https://github.com/JetBrains/compose-multiplatform-core@<COMPOSE_CORE_REF>
SET_FOLDER=compose/material3/adaptive/adaptive-navigation3/src
commonMain/kotlin/ -> src/vendor/common/kotlin/
# | commonMain/kotlin/androidx/compose/material3/adaptive/navigation3/ListDetailSceneScope.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/navigation3/ListDetailSceneScope.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/navigation3/ListDetailSceneStrategy.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/navigation3/ListDetailSceneStrategy.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/navigation3/SupportingPaneSceneScope.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/navigation3/SupportingPaneSceneScope.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/navigation3/SupportingPaneSceneStrategy.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/navigation3/SupportingPaneSceneStrategy.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/navigation3/ThreePaneScaffoldScene.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/navigation3/ThreePaneScaffoldScene.kt
# >>> ---- DIAGNOSTIC GAPS ---- <<<<
# (none - every source set under SET_FOLDER is vendored)
# <<< ---- DIAGNOSTIC GAPS ---- >>>>
@@ -0,0 +1,85 @@
// Klib ABI Dump
// Targets: [linuxArm64, linuxX64, macosArm64, mingwX64]
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: true
// - Show declarations: true
// Library unique name: <com.bitsycore.compose.material3.adaptive:adaptive>
open annotation class androidx.compose.material3.adaptive/ExperimentalMaterial3AdaptiveApi : kotlin/Annotation { // androidx.compose.material3.adaptive/ExperimentalMaterial3AdaptiveApi|null[0]
constructor <init>() // androidx.compose.material3.adaptive/ExperimentalMaterial3AdaptiveApi.<init>|<init>(){}[0]
}
open annotation class androidx.compose.material3.adaptive/ExperimentalMaterial3AdaptiveComponentOverrideApi : kotlin/Annotation { // androidx.compose.material3.adaptive/ExperimentalMaterial3AdaptiveComponentOverrideApi|null[0]
constructor <init>() // androidx.compose.material3.adaptive/ExperimentalMaterial3AdaptiveComponentOverrideApi.<init>|<init>(){}[0]
}
final class androidx.compose.material3.adaptive/HingeInfo { // androidx.compose.material3.adaptive/HingeInfo|null[0]
constructor <init>(androidx.compose.ui.geometry/Rect, kotlin/Boolean, kotlin/Boolean, kotlin/Boolean, kotlin/Boolean) // androidx.compose.material3.adaptive/HingeInfo.<init>|<init>(androidx.compose.ui.geometry.Rect;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean){}[0]
final val bounds // androidx.compose.material3.adaptive/HingeInfo.bounds|{}bounds[0]
final fun <get-bounds>(): androidx.compose.ui.geometry/Rect // androidx.compose.material3.adaptive/HingeInfo.bounds.<get-bounds>|<get-bounds>(){}[0]
final val isFlat // androidx.compose.material3.adaptive/HingeInfo.isFlat|{}isFlat[0]
final fun <get-isFlat>(): kotlin/Boolean // androidx.compose.material3.adaptive/HingeInfo.isFlat.<get-isFlat>|<get-isFlat>(){}[0]
final val isOccluding // androidx.compose.material3.adaptive/HingeInfo.isOccluding|{}isOccluding[0]
final fun <get-isOccluding>(): kotlin/Boolean // androidx.compose.material3.adaptive/HingeInfo.isOccluding.<get-isOccluding>|<get-isOccluding>(){}[0]
final val isSeparating // androidx.compose.material3.adaptive/HingeInfo.isSeparating|{}isSeparating[0]
final fun <get-isSeparating>(): kotlin/Boolean // androidx.compose.material3.adaptive/HingeInfo.isSeparating.<get-isSeparating>|<get-isSeparating>(){}[0]
final val isVertical // androidx.compose.material3.adaptive/HingeInfo.isVertical|{}isVertical[0]
final fun <get-isVertical>(): kotlin/Boolean // androidx.compose.material3.adaptive/HingeInfo.isVertical.<get-isVertical>|<get-isVertical>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive/HingeInfo.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive/HingeInfo.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive/HingeInfo.toString|toString(){}[0]
}
final class androidx.compose.material3.adaptive/Posture { // androidx.compose.material3.adaptive/Posture|null[0]
constructor <init>(kotlin/Boolean = ..., kotlin.collections/List<androidx.compose.material3.adaptive/HingeInfo> = ...) // androidx.compose.material3.adaptive/Posture.<init>|<init>(kotlin.Boolean;kotlin.collections.List<androidx.compose.material3.adaptive.HingeInfo>){}[0]
final val hingeList // androidx.compose.material3.adaptive/Posture.hingeList|{}hingeList[0]
final fun <get-hingeList>(): kotlin.collections/List<androidx.compose.material3.adaptive/HingeInfo> // androidx.compose.material3.adaptive/Posture.hingeList.<get-hingeList>|<get-hingeList>(){}[0]
final val isTabletop // androidx.compose.material3.adaptive/Posture.isTabletop|{}isTabletop[0]
final fun <get-isTabletop>(): kotlin/Boolean // androidx.compose.material3.adaptive/Posture.isTabletop.<get-isTabletop>|<get-isTabletop>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive/Posture.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive/Posture.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive/Posture.toString|toString(){}[0]
}
final class androidx.compose.material3.adaptive/WindowAdaptiveInfo { // androidx.compose.material3.adaptive/WindowAdaptiveInfo|null[0]
constructor <init>(androidx.window.core.layout/WindowSizeClass, androidx.compose.material3.adaptive/Posture) // androidx.compose.material3.adaptive/WindowAdaptiveInfo.<init>|<init>(androidx.window.core.layout.WindowSizeClass;androidx.compose.material3.adaptive.Posture){}[0]
final val windowPosture // androidx.compose.material3.adaptive/WindowAdaptiveInfo.windowPosture|{}windowPosture[0]
final fun <get-windowPosture>(): androidx.compose.material3.adaptive/Posture // androidx.compose.material3.adaptive/WindowAdaptiveInfo.windowPosture.<get-windowPosture>|<get-windowPosture>(){}[0]
final val windowSizeClass // androidx.compose.material3.adaptive/WindowAdaptiveInfo.windowSizeClass|{}windowSizeClass[0]
final fun <get-windowSizeClass>(): androidx.window.core.layout/WindowSizeClass // androidx.compose.material3.adaptive/WindowAdaptiveInfo.windowSizeClass.<get-windowSizeClass>|<get-windowSizeClass>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // androidx.compose.material3.adaptive/WindowAdaptiveInfo.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // androidx.compose.material3.adaptive/WindowAdaptiveInfo.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // androidx.compose.material3.adaptive/WindowAdaptiveInfo.toString|toString(){}[0]
}
final val androidx.compose.material3.adaptive/allHorizontalHingeBounds // androidx.compose.material3.adaptive/allHorizontalHingeBounds|@androidx.compose.material3.adaptive.Posture{}allHorizontalHingeBounds[0]
final fun (androidx.compose.material3.adaptive/Posture).<get-allHorizontalHingeBounds>(): kotlin.collections/List<androidx.compose.ui.geometry/Rect> // androidx.compose.material3.adaptive/allHorizontalHingeBounds.<get-allHorizontalHingeBounds>|<get-allHorizontalHingeBounds>@androidx.compose.material3.adaptive.Posture(){}[0]
final val androidx.compose.material3.adaptive/allVerticalHingeBounds // androidx.compose.material3.adaptive/allVerticalHingeBounds|@androidx.compose.material3.adaptive.Posture{}allVerticalHingeBounds[0]
final fun (androidx.compose.material3.adaptive/Posture).<get-allVerticalHingeBounds>(): kotlin.collections/List<androidx.compose.ui.geometry/Rect> // androidx.compose.material3.adaptive/allVerticalHingeBounds.<get-allVerticalHingeBounds>|<get-allVerticalHingeBounds>@androidx.compose.material3.adaptive.Posture(){}[0]
final val androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_HingeInfo$stableprop // androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_HingeInfo$stableprop|#static{}androidx_compose_material3_adaptive_HingeInfo$stableprop[0]
final val androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_Posture$stableprop // androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_Posture$stableprop|#static{}androidx_compose_material3_adaptive_Posture$stableprop[0]
final val androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_WindowAdaptiveInfo$stableprop // androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_WindowAdaptiveInfo$stableprop|#static{}androidx_compose_material3_adaptive_WindowAdaptiveInfo$stableprop[0]
final val androidx.compose.material3.adaptive/occludingHorizontalHingeBounds // androidx.compose.material3.adaptive/occludingHorizontalHingeBounds|@androidx.compose.material3.adaptive.Posture{}occludingHorizontalHingeBounds[0]
final fun (androidx.compose.material3.adaptive/Posture).<get-occludingHorizontalHingeBounds>(): kotlin.collections/List<androidx.compose.ui.geometry/Rect> // androidx.compose.material3.adaptive/occludingHorizontalHingeBounds.<get-occludingHorizontalHingeBounds>|<get-occludingHorizontalHingeBounds>@androidx.compose.material3.adaptive.Posture(){}[0]
final val androidx.compose.material3.adaptive/occludingVerticalHingeBounds // androidx.compose.material3.adaptive/occludingVerticalHingeBounds|@androidx.compose.material3.adaptive.Posture{}occludingVerticalHingeBounds[0]
final fun (androidx.compose.material3.adaptive/Posture).<get-occludingVerticalHingeBounds>(): kotlin.collections/List<androidx.compose.ui.geometry/Rect> // androidx.compose.material3.adaptive/occludingVerticalHingeBounds.<get-occludingVerticalHingeBounds>|<get-occludingVerticalHingeBounds>@androidx.compose.material3.adaptive.Posture(){}[0]
final val androidx.compose.material3.adaptive/separatingHorizontalHingeBounds // androidx.compose.material3.adaptive/separatingHorizontalHingeBounds|@androidx.compose.material3.adaptive.Posture{}separatingHorizontalHingeBounds[0]
final fun (androidx.compose.material3.adaptive/Posture).<get-separatingHorizontalHingeBounds>(): kotlin.collections/List<androidx.compose.ui.geometry/Rect> // androidx.compose.material3.adaptive/separatingHorizontalHingeBounds.<get-separatingHorizontalHingeBounds>|<get-separatingHorizontalHingeBounds>@androidx.compose.material3.adaptive.Posture(){}[0]
final val androidx.compose.material3.adaptive/separatingVerticalHingeBounds // androidx.compose.material3.adaptive/separatingVerticalHingeBounds|@androidx.compose.material3.adaptive.Posture{}separatingVerticalHingeBounds[0]
final fun (androidx.compose.material3.adaptive/Posture).<get-separatingVerticalHingeBounds>(): kotlin.collections/List<androidx.compose.ui.geometry/Rect> // androidx.compose.material3.adaptive/separatingVerticalHingeBounds.<get-separatingVerticalHingeBounds>|<get-separatingVerticalHingeBounds>@androidx.compose.material3.adaptive.Posture(){}[0]
final fun androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_HingeInfo$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_HingeInfo$stableprop_getter|androidx_compose_material3_adaptive_HingeInfo$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_Posture$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_Posture$stableprop_getter|androidx_compose_material3_adaptive_Posture$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_WindowAdaptiveInfo$stableprop_getter(): kotlin/Int // androidx.compose.material3.adaptive/androidx_compose_material3_adaptive_WindowAdaptiveInfo$stableprop_getter|androidx_compose_material3_adaptive_WindowAdaptiveInfo$stableprop_getter(){}[0]
final fun androidx.compose.material3.adaptive/currentWindowAdaptiveInfo(androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.material3.adaptive/WindowAdaptiveInfo // androidx.compose.material3.adaptive/currentWindowAdaptiveInfo|currentWindowAdaptiveInfo(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive/currentWindowAdaptiveInfo(kotlin/Boolean, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): androidx.compose.material3.adaptive/WindowAdaptiveInfo // androidx.compose.material3.adaptive/currentWindowAdaptiveInfo|currentWindowAdaptiveInfo(kotlin.Boolean;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive/currentWindowAdaptiveInfoV2(androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.material3.adaptive/WindowAdaptiveInfo // androidx.compose.material3.adaptive/currentWindowAdaptiveInfoV2|currentWindowAdaptiveInfoV2(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive/currentWindowDpSize(androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.ui.unit/DpSize // androidx.compose.material3.adaptive/currentWindowDpSize|currentWindowDpSize(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun androidx.compose.material3.adaptive/currentWindowSize(androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.ui.unit/IntSize // androidx.compose.material3.adaptive/currentWindowSize|currentWindowSize(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
@@ -0,0 +1,47 @@
// :adaptive - androidx.compose.material3.adaptive.* (WindowAdaptiveInfo / Posture /
// window size class helpers), vendored VERBATIM from JetBrains/compose-multiplatform-core
// (compose/material3/adaptive/adaptive) into src/vendor/.
//
// Upstream org.jetbrains.compose.material3.adaptive:* publishes only ios / macosArm64 -
// no linux, no mingw (github.com/bitsycore/compose-desktop-native/issues/4). Nothing in
// the sources blocks those targets: the whole port is vendor-only, every actual the
// native targets need already exists upstream, and the one real dependency
// (androidx.window:window-core) ALREADY publishes linux + mingw + macos. The artifacts
// are missing purely because upstream did not enable the targets.
//
// Upstream's `nonAndroidMain` lands in src/vendor/native/ - for this port native IS the
// only non-android leg, same treatment coil's nonAndroid sets get.
//
// Provenance = compose-fork.txt + scripts/compose-fork/compose.properties. Never hand-edit
// src/vendor - change the manifest + `python scripts/compose-fork/sync.py compose/material3/adaptive/adaptive`.
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.kotlin.plugin.compose)
}
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
kotlin {
linuxArm64(); linuxX64(); macosArm64(); if (vHostSupportsMingw) mingwX64()
applyDefaultHierarchyTemplate()
sourceSets {
commonMain {
dependencies {
api(project(":compose:ui:ui"))
api(libs.androidx.window.core)
implementation(project(":compose:foundation:foundation"))
}
kotlin.srcDir("src/vendor/common/kotlin")
}
nativeMain {
kotlin.srcDir("src/vendor/native/kotlin")
}
}
compilerOptions {
freeCompilerArgs.addAll(
"-Xexpect-actual-classes",
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
"-opt-in=androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi",
)
}
}
@@ -0,0 +1,17 @@
SET_REPO=https://github.com/JetBrains/compose-multiplatform-core@<COMPOSE_CORE_REF>
SET_FOLDER=compose/material3/adaptive/adaptive/src
commonMain/kotlin/ -> src/vendor/common/kotlin/
# | commonMain/kotlin/androidx/compose/material3/adaptive/ExperimentalMaterial3AdaptiveApi.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/ExperimentalMaterial3AdaptiveApi.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/ExperimentalMaterial3AdaptiveComponentOverrideApi.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/ExperimentalMaterial3AdaptiveComponentOverrideApi.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/Posture.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/Posture.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/WindowAdaptiveInfo.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/WindowAdaptiveInfo.kt
# | commonMain/kotlin/androidx/compose/material3/adaptive/WindowSizeClassHelper.kt -> src/vendor/common/kotlin/androidx/compose/material3/adaptive/WindowSizeClassHelper.kt
nonAndroidMain/kotlin/ -> src/vendor/native/kotlin/
# | nonAndroidMain/kotlin/androidx/compose/material3/adaptive/WindowAdaptiveInfo.nonAndroid.kt -> src/vendor/native/kotlin/androidx/compose/material3/adaptive/WindowAdaptiveInfo.nonAndroid.kt
# >>> ---- DIAGNOSTIC GAPS ---- <<<<
# androidMain (2)
# | androidMain/kotlin/androidx/compose/material3/adaptive/AndroidPosture.android.kt -> src/vendor/android/kotlin/androidx/compose/material3/adaptive/AndroidPosture.android.kt
# | androidMain/kotlin/androidx/compose/material3/adaptive/AndroidWindowAdaptiveInfo.android.kt -> src/vendor/android/kotlin/androidx/compose/material3/adaptive/AndroidWindowAdaptiveInfo.android.kt
# <<< ---- DIAGNOSTIC GAPS ---- >>>>
@@ -341,18 +341,10 @@ abstract interface com.compose.sdl.graphics/NativeFinishableCanvas { // com.comp
abstract fun finish() // com.compose.sdl.graphics/NativeFinishableCanvas.finish|finish(){}[0]
}
abstract interface com.compose.sdl.graphics/NativePainterCanvas { // com.compose.sdl.graphics/NativePainterCanvas|null[0]
abstract fun drawNativePainter(kotlin/String, com.compose.sdl.res/ResourceKind, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float) // com.compose.sdl.graphics/NativePainterCanvas.drawNativePainter|drawNativePainter(kotlin.String;com.compose.sdl.res.ResourceKind;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
}
abstract interface com.compose.sdl.graphics/NativeShadowCanvas { // com.compose.sdl.graphics/NativeShadowCanvas|null[0]
abstract fun drawDropShadow(androidx.compose.ui.graphics/Outline, kotlin/Float, androidx.compose.ui.graphics/Color, androidx.compose.ui.graphics/Color) // com.compose.sdl.graphics/NativeShadowCanvas.drawDropShadow|drawDropShadow(androidx.compose.ui.graphics.Outline;kotlin.Float;androidx.compose.ui.graphics.Color;androidx.compose.ui.graphics.Color){}[0]
}
abstract interface com.compose.sdl.graphics/NativeShapeClipCanvas { // com.compose.sdl.graphics/NativeShapeClipCanvas|null[0]
abstract fun clipRoundRect(androidx.compose.ui.geometry/RoundRect) // com.compose.sdl.graphics/NativeShapeClipCanvas.clipRoundRect|clipRoundRect(androidx.compose.ui.geometry.RoundRect){}[0]
}
abstract interface com.compose.sdl.res/ImageLoader { // com.compose.sdl.res/ImageLoader|null[0]
abstract fun intrinsicSize(kotlin/String, com.compose.sdl.res/ResourceKind): androidx.compose.ui.geometry/Size // com.compose.sdl.res/ImageLoader.intrinsicSize|intrinsicSize(kotlin.String;com.compose.sdl.res.ResourceKind){}[0]
abstract fun readBytes(kotlin/String): kotlin/ByteArray? // com.compose.sdl.res/ImageLoader.readBytes|readBytes(kotlin.String){}[0]
@@ -364,62 +356,6 @@ sealed interface androidx.compose.ui.graphics.shadow/ShadowContext { // androidx
open fun createInnerShadowPainter(androidx.compose.ui.graphics/Shape, androidx.compose.ui.graphics.shadow/Shadow): androidx.compose.ui.graphics.shadow/InnerShadowPainter // androidx.compose.ui.graphics.shadow/ShadowContext.createInnerShadowPainter|createInnerShadowPainter(androidx.compose.ui.graphics.Shape;androidx.compose.ui.graphics.shadow.Shadow){}[0]
}
sealed interface com.compose.sdl.graphics/PathCommand { // com.compose.sdl.graphics/PathCommand|null[0]
final class CubicTo : com.compose.sdl.graphics/PathCommand { // com.compose.sdl.graphics/PathCommand.CubicTo|null[0]
constructor <init>(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float) // com.compose.sdl.graphics/PathCommand.CubicTo.<init>|<init>(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
final val c1x // com.compose.sdl.graphics/PathCommand.CubicTo.c1x|{}c1x[0]
final fun <get-c1x>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.CubicTo.c1x.<get-c1x>|<get-c1x>(){}[0]
final val c1y // com.compose.sdl.graphics/PathCommand.CubicTo.c1y|{}c1y[0]
final fun <get-c1y>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.CubicTo.c1y.<get-c1y>|<get-c1y>(){}[0]
final val c2x // com.compose.sdl.graphics/PathCommand.CubicTo.c2x|{}c2x[0]
final fun <get-c2x>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.CubicTo.c2x.<get-c2x>|<get-c2x>(){}[0]
final val c2y // com.compose.sdl.graphics/PathCommand.CubicTo.c2y|{}c2y[0]
final fun <get-c2y>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.CubicTo.c2y.<get-c2y>|<get-c2y>(){}[0]
final val x // com.compose.sdl.graphics/PathCommand.CubicTo.x|{}x[0]
final fun <get-x>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.CubicTo.x.<get-x>|<get-x>(){}[0]
final val y // com.compose.sdl.graphics/PathCommand.CubicTo.y|{}y[0]
final fun <get-y>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.CubicTo.y.<get-y>|<get-y>(){}[0]
}
final class LineTo : com.compose.sdl.graphics/PathCommand { // com.compose.sdl.graphics/PathCommand.LineTo|null[0]
constructor <init>(kotlin/Float, kotlin/Float) // com.compose.sdl.graphics/PathCommand.LineTo.<init>|<init>(kotlin.Float;kotlin.Float){}[0]
final val x // com.compose.sdl.graphics/PathCommand.LineTo.x|{}x[0]
final fun <get-x>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.LineTo.x.<get-x>|<get-x>(){}[0]
final val y // com.compose.sdl.graphics/PathCommand.LineTo.y|{}y[0]
final fun <get-y>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.LineTo.y.<get-y>|<get-y>(){}[0]
}
final class MoveTo : com.compose.sdl.graphics/PathCommand { // com.compose.sdl.graphics/PathCommand.MoveTo|null[0]
constructor <init>(kotlin/Float, kotlin/Float) // com.compose.sdl.graphics/PathCommand.MoveTo.<init>|<init>(kotlin.Float;kotlin.Float){}[0]
final val x // com.compose.sdl.graphics/PathCommand.MoveTo.x|{}x[0]
final fun <get-x>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.MoveTo.x.<get-x>|<get-x>(){}[0]
final val y // com.compose.sdl.graphics/PathCommand.MoveTo.y|{}y[0]
final fun <get-y>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.MoveTo.y.<get-y>|<get-y>(){}[0]
}
final class QuadTo : com.compose.sdl.graphics/PathCommand { // com.compose.sdl.graphics/PathCommand.QuadTo|null[0]
constructor <init>(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float) // com.compose.sdl.graphics/PathCommand.QuadTo.<init>|<init>(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
final val cx // com.compose.sdl.graphics/PathCommand.QuadTo.cx|{}cx[0]
final fun <get-cx>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.QuadTo.cx.<get-cx>|<get-cx>(){}[0]
final val cy // com.compose.sdl.graphics/PathCommand.QuadTo.cy|{}cy[0]
final fun <get-cy>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.QuadTo.cy.<get-cy>|<get-cy>(){}[0]
final val x // com.compose.sdl.graphics/PathCommand.QuadTo.x|{}x[0]
final fun <get-x>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.QuadTo.x.<get-x>|<get-x>(){}[0]
final val y // com.compose.sdl.graphics/PathCommand.QuadTo.y|{}y[0]
final fun <get-y>(): kotlin/Float // com.compose.sdl.graphics/PathCommand.QuadTo.y.<get-y>|<get-y>(){}[0]
}
final object Close : com.compose.sdl.graphics/PathCommand { // com.compose.sdl.graphics/PathCommand.Close|null[0]
final fun equals(kotlin/Any?): kotlin/Boolean // com.compose.sdl.graphics/PathCommand.Close.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // com.compose.sdl.graphics/PathCommand.Close.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // com.compose.sdl.graphics/PathCommand.Close.toString|toString(){}[0]
}
}
abstract class androidx.compose.ui.graphics.colorspace/Adaptation { // androidx.compose.ui.graphics.colorspace/Adaptation|null[0]
final object Companion { // androidx.compose.ui.graphics.colorspace/Adaptation.Companion|null[0]
final val Bradford // androidx.compose.ui.graphics.colorspace/Adaptation.Companion.Bradford|{}Bradford[0]
@@ -1111,19 +1047,6 @@ final class com.compose.sdl.renderer.skia/SkiaLeafDrawer { // com.compose.sdl.re
final fun <get-imageCache>(): com.compose.sdl.renderer.skia/SkiaImageCache // com.compose.sdl.renderer.skia/SkiaLeafDrawer.imageCache.<get-imageCache>|<get-imageCache>(){}[0]
}
final class com.compose.sdl.res/ResourcePainter : androidx.compose.ui.graphics.painter/Painter { // com.compose.sdl.res/ResourcePainter|null[0]
final val intrinsicSize // com.compose.sdl.res/ResourcePainter.intrinsicSize|{}intrinsicSize[0]
final fun <get-intrinsicSize>(): androidx.compose.ui.geometry/Size // com.compose.sdl.res/ResourcePainter.intrinsicSize.<get-intrinsicSize>|<get-intrinsicSize>(){}[0]
final val kind // com.compose.sdl.res/ResourcePainter.kind|{}kind[0]
final fun <get-kind>(): com.compose.sdl.res/ResourceKind // com.compose.sdl.res/ResourcePainter.kind.<get-kind>|<get-kind>(){}[0]
final val resourcePath // com.compose.sdl.res/ResourcePainter.resourcePath|{}resourcePath[0]
final fun <get-resourcePath>(): kotlin/String // com.compose.sdl.res/ResourcePainter.resourcePath.<get-resourcePath>|<get-resourcePath>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // com.compose.sdl.res/ResourcePainter.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // com.compose.sdl.res/ResourcePainter.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // com.compose.sdl.res/ResourcePainter.toString|toString(){}[0]
}
final value class androidx.compose.ui.graphics.colorspace/ColorModel { // androidx.compose.ui.graphics.colorspace/ColorModel|null[0]
final val componentCount // androidx.compose.ui.graphics.colorspace/ColorModel.componentCount|{}componentCount[0]
final fun <get-componentCount>(): kotlin/Int // androidx.compose.ui.graphics.colorspace/ColorModel.componentCount.<get-componentCount>|<get-componentCount>(){}[0]
@@ -2108,10 +2031,6 @@ final object com.compose.sdl.graphics/NativeReleaseQueue { // com.compose.sdl.gr
final fun enqueue(kotlin/Function0<kotlin/Unit>) // com.compose.sdl.graphics/NativeReleaseQueue.enqueue|enqueue(kotlin.Function0<kotlin.Unit>){}[0]
}
final object com.compose.sdl.res/AndroidVectorToSvg { // com.compose.sdl.res/AndroidVectorToSvg|null[0]
final fun convert(kotlin/String): kotlin/String // com.compose.sdl.res/AndroidVectorToSvg.convert|convert(kotlin.String){}[0]
}
final object com.compose.sdl.res/Res { // com.compose.sdl.res/Res|null[0]
final fun readBytes(kotlin/String): kotlin/ByteArray? // com.compose.sdl.res/Res.readBytes|readBytes(kotlin.String){}[0]
@@ -2261,11 +2180,9 @@ final val com.compose.sdl.graphics/gradientTileMode // com.compose.sdl.graphics/
final val com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaEncodedImageDecoder$stableprop // com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaEncodedImageDecoder$stableprop|#static{}com_compose_sdl_renderer_skia_SkiaEncodedImageDecoder$stableprop[0]
final val com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaImageCache$stableprop // com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaImageCache$stableprop|#static{}com_compose_sdl_renderer_skia_SkiaImageCache$stableprop[0]
final val com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaLeafDrawer$stableprop // com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaLeafDrawer$stableprop|#static{}com_compose_sdl_renderer_skia_SkiaLeafDrawer$stableprop[0]
final val com.compose.sdl.res/com_compose_sdl_res_AndroidVectorToSvg$stableprop // com.compose.sdl.res/com_compose_sdl_res_AndroidVectorToSvg$stableprop|#static{}com_compose_sdl_res_AndroidVectorToSvg$stableprop[0]
final val com.compose.sdl.res/com_compose_sdl_res_Res$stableprop // com.compose.sdl.res/com_compose_sdl_res_Res$stableprop|#static{}com_compose_sdl_res_Res$stableprop[0]
final val com.compose.sdl.res/com_compose_sdl_res_Res_drawable$stableprop // com.compose.sdl.res/com_compose_sdl_res_Res_drawable$stableprop|#static{}com_compose_sdl_res_Res_drawable$stableprop[0]
final val com.compose.sdl.res/com_compose_sdl_res_Res_files$stableprop // com.compose.sdl.res/com_compose_sdl_res_Res_files$stableprop|#static{}com_compose_sdl_res_Res_files$stableprop[0]
final val com.compose.sdl.res/com_compose_sdl_res_ResourcePainter$stableprop // com.compose.sdl.res/com_compose_sdl_res_ResourcePainter$stableprop|#static{}com_compose_sdl_res_ResourcePainter$stableprop[0]
final var androidx.compose.ui.graphics/alphaMultiplier // androidx.compose.ui.graphics/alphaMultiplier|@androidx.compose.ui.graphics.Canvas{}alphaMultiplier[0]
final fun (androidx.compose.ui.graphics/Canvas).<get-alphaMultiplier>(): kotlin/Float // androidx.compose.ui.graphics/alphaMultiplier.<get-alphaMultiplier>|<get-alphaMultiplier>@androidx.compose.ui.graphics.Canvas(){}[0]
@@ -2456,14 +2373,11 @@ final fun com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaEncode
final fun com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaImageCache$stableprop_getter(): kotlin/Int // com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaImageCache$stableprop_getter|com_compose_sdl_renderer_skia_SkiaImageCache$stableprop_getter(){}[0]
final fun com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaLeafDrawer$stableprop_getter(): kotlin/Int // com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaLeafDrawer$stableprop_getter|com_compose_sdl_renderer_skia_SkiaLeafDrawer$stableprop_getter(){}[0]
final fun com.compose.sdl.renderer.skia/readBackBgra(org.jetbrains.skia/Surface?, kotlin/Int, kotlin/Int): kotlin/Triple<kotlin/Int, kotlin/Int, kotlin/ByteArray>? // com.compose.sdl.renderer.skia/readBackBgra|readBackBgra(org.jetbrains.skia.Surface?;kotlin.Int;kotlin.Int){}[0]
final fun com.compose.sdl.res/com_compose_sdl_res_AndroidVectorToSvg$stableprop_getter(): kotlin/Int // com.compose.sdl.res/com_compose_sdl_res_AndroidVectorToSvg$stableprop_getter|com_compose_sdl_res_AndroidVectorToSvg$stableprop_getter(){}[0]
final fun com.compose.sdl.res/com_compose_sdl_res_Res$stableprop_getter(): kotlin/Int // com.compose.sdl.res/com_compose_sdl_res_Res$stableprop_getter|com_compose_sdl_res_Res$stableprop_getter(){}[0]
final fun com.compose.sdl.res/com_compose_sdl_res_Res_drawable$stableprop_getter(): kotlin/Int // com.compose.sdl.res/com_compose_sdl_res_Res_drawable$stableprop_getter|com_compose_sdl_res_Res_drawable$stableprop_getter(){}[0]
final fun com.compose.sdl.res/com_compose_sdl_res_Res_files$stableprop_getter(): kotlin/Int // com.compose.sdl.res/com_compose_sdl_res_Res_files$stableprop_getter|com_compose_sdl_res_Res_files$stableprop_getter(){}[0]
final fun com.compose.sdl.res/com_compose_sdl_res_ResourcePainter$stableprop_getter(): kotlin/Int // com.compose.sdl.res/com_compose_sdl_res_ResourcePainter$stableprop_getter|com_compose_sdl_res_ResourcePainter$stableprop_getter(){}[0]
final fun com.compose.sdl.res/painterResource(kotlin/String): androidx.compose.ui.graphics.painter/Painter // com.compose.sdl.res/painterResource|painterResource(kotlin.String){}[0]
final fun com.compose.sdl.res/painterResource(kotlin/String, com.compose.sdl.res/ResourceKind): androidx.compose.ui.graphics.painter/Painter // com.compose.sdl.res/painterResource|painterResource(kotlin.String;com.compose.sdl.res.ResourceKind){}[0]
final fun com.compose.sdl.res/resourceKindForPath(kotlin/String): com.compose.sdl.res/ResourceKind // com.compose.sdl.res/resourceKindForPath|resourceKindForPath(kotlin.String){}[0]
final inline fun (androidx.compose.ui.graphics.drawscope/DrawScope).androidx.compose.ui.graphics.drawscope/clipPath(androidx.compose.ui.graphics/Path, androidx.compose.ui.graphics/ClipOp = ..., kotlin/Function1<androidx.compose.ui.graphics.drawscope/DrawScope, kotlin/Unit>) // androidx.compose.ui.graphics.drawscope/clipPath|clipPath@androidx.compose.ui.graphics.drawscope.DrawScope(androidx.compose.ui.graphics.Path;androidx.compose.ui.graphics.ClipOp;kotlin.Function1<androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit>){}[0]
final inline fun (androidx.compose.ui.graphics.drawscope/DrawScope).androidx.compose.ui.graphics.drawscope/clipRect(kotlin/Float = ..., kotlin/Float = ..., kotlin/Float = ..., kotlin/Float = ..., androidx.compose.ui.graphics/ClipOp = ..., kotlin/Function1<androidx.compose.ui.graphics.drawscope/DrawScope, kotlin/Unit>) // androidx.compose.ui.graphics.drawscope/clipRect|clipRect@androidx.compose.ui.graphics.drawscope.DrawScope(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;androidx.compose.ui.graphics.ClipOp;kotlin.Function1<androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit>){}[0]
final inline fun (androidx.compose.ui.graphics.drawscope/DrawScope).androidx.compose.ui.graphics.drawscope/draw(androidx.compose.ui.unit/Density, androidx.compose.ui.unit/LayoutDirection, androidx.compose.ui.graphics/Canvas, androidx.compose.ui.geometry/Size, androidx.compose.ui.graphics.layer/GraphicsLayer? = ..., kotlin/Function1<androidx.compose.ui.graphics.drawscope/DrawScope, kotlin/Unit>) // androidx.compose.ui.graphics.drawscope/draw|draw@androidx.compose.ui.graphics.drawscope.DrawScope(androidx.compose.ui.unit.Density;androidx.compose.ui.unit.LayoutDirection;androidx.compose.ui.graphics.Canvas;androidx.compose.ui.geometry.Size;androidx.compose.ui.graphics.layer.GraphicsLayer?;kotlin.Function1<androidx.compose.ui.graphics.drawscope.DrawScope,kotlin.Unit>){}[0]
@@ -16,7 +16,7 @@ import com.compose.sdl.res.ResourceKind
the given rect, and this seam carries no ui-layout (ContentScale) dependency -
keeping :ui-graphics free of :ui.
*/
interface NativePainterCanvas {
internal interface NativePainterCanvas {
fun drawNativePainter(
inResourcePath: String,
inKind: ResourceKind,
@@ -10,7 +10,7 @@ package com.compose.sdl.graphics
carries a `commands: List<PathCommand>` that the SDL3 / Skia renderers walk
directly. No official equivalent, so lives in com.compose.sdl
per FIDELITY.md's relocate rule. */
sealed interface PathCommand {
internal sealed interface PathCommand {
class MoveTo(val x: Float, val y: Float) : PathCommand
class LineTo(val x: Float, val y: Float) : PathCommand
class QuadTo(val cx: Float, val cy: Float, val x: Float, val y: Float) : PathCommand
@@ -21,7 +21,7 @@ import androidx.compose.ui.geometry.RoundRect
`Canvas.restore()` - the backend composites and pops it on the matching
`restore()`.
*/
interface NativeShapeClipCanvas {
internal interface NativeShapeClipCanvas {
// Pushes a rounded-rect clip for subsequent draws until the matching
// Canvas.restore(). [inRoundRect] is in the CURRENT canvas-local coordinate
// space (the same space draw calls use after the enclosing translate).
@@ -16,7 +16,7 @@ package com.compose.sdl.res
Not supported: <group> transforms (paths are flattened, transforms ignored),
gradients, clip-paths, trim-path. This covers the common single-/multi-path
icon case such as exported Material icons. */
object AndroidVectorToSvg {
internal object AndroidVectorToSvg {
// Tag values never contain '>' (pathData is letters/digits/.,- /space) and
// quoted attribute values can't either, so [^>] within a tag is safe.
@@ -18,7 +18,7 @@ enum class ResourceKind {
}
/** Picks a ResourceKind from a file extension. */
fun resourceKindForPath(path: String): ResourceKind {
internal fun resourceKindForPath(path: String): ResourceKind {
val vExt = path.substringAfterLast('.', "").lowercase()
return when (vExt) {
"svg" -> ResourceKind.Svg
@@ -27,17 +27,17 @@ import com.compose.sdl.graphics.NativePainterCanvas
If the Canvas doesn't implement NativePainterCanvas (offscreen layer capture
for graphics-layer effects) the paint is skipped.
*/
class ResourcePainter internal constructor(
internal class ResourcePainter internal constructor(
val resourcePath: String,
val kind: ResourceKind,
) : Painter() {
private var fAlpha: Float = 1f
private var mAlpha: Float = 1f
override val intrinsicSize: Size
get() = currentImageLoader.intrinsicSize(resourcePath, kind)
override fun applyAlpha(alpha: Float): Boolean { fAlpha = alpha; return true }
override fun applyAlpha(alpha: Float): Boolean { mAlpha = alpha; return true }
override fun DrawScope.onDraw() {
drawIntoCanvas { vCanvas ->
@@ -45,7 +45,7 @@ class ResourcePainter internal constructor(
// translate + scaledSize, so we paint the resource FillBounds into the
// full DrawScope size. Alpha is threaded via applyAlpha.
(vCanvas as? NativePainterCanvas)?.drawNativePainter(
resourcePath, kind, 0f, 0f, size.width, size.height, fAlpha,
resourcePath, kind, 0f, 0f, size.width, size.height, mAlpha,
)
}
}
@@ -12,7 +12,7 @@ import kotlin.math.abs
// vendored Matrices.skiko.kt and would not be visible up here. Distinct name
// avoids clashing with that copy on the Skia leg. (RENDERER.md P1.5 will
// reverse this rename once B2 vendors Matrices.skiko onto the Skia leg.)
// VENDOR-BASE: compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/graphics/Matrices.skiko.kt @ v1.12.0
// VENDOR-BASE: compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/Matrices.skiko.kt @ v1.12.1
// Builds the layer transform (translation / rotation / scale / camera about a
// pivot) into [matrix]. Matches the skiko copy so hit-testing agrees across legs.
@@ -28,12 +28,12 @@ import kotlinx.atomicfu.locks.synchronized
*/
object NativeReleaseQueue {
private val fLock = SynchronizedObject()
private var fPending = ArrayList<() -> Unit>()
private val mLock = SynchronizedObject()
private var mPending = ArrayList<() -> Unit>()
/** Enqueue a release action. Safe to call from any thread. */
fun enqueue(action: () -> Unit) {
synchronized(fLock) { fPending.add(action) }
synchronized(mLock) { mPending.add(action) }
}
/**
@@ -41,10 +41,10 @@ object NativeReleaseQueue {
* renderer APIs that aren't thread-safe. Returns the number drained.
*/
fun drain(): Int {
val vBatch = synchronized(fLock) {
if (fPending.isEmpty()) return 0
val vTaken = fPending
fPending = ArrayList()
val vBatch = synchronized(mLock) {
if (mPending.isEmpty()) return 0
val vTaken = mPending
mPending = ArrayList()
vTaken
}
for (vAction in vBatch) {
@@ -15,7 +15,7 @@
*/
// DERIVED (copy + edit) from compose-multiplatform-core -- MANUAL VENDOR, NON-IDEMPOTENT.
// VENDOR-BASE: compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt @ v1.12.0
// VENDOR-BASE: compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt @ v1.12.1
// ONLY change vs upstream: this class also implements the port draw contracts
// (NativePainterCanvas / NativeShadowCanvas / NativeFinishableCanvas) so the
// resource-image cache + elevation shadows keep working on the Skia renderer,
@@ -17,7 +17,7 @@
*/
// DERIVED (copy + edit) from compose-multiplatform-core -- MANUAL VENDOR, NON-IDEMPOTENT.
// VENDOR-BASE: compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaImageAsset.skiko.kt @ v1.12.0
// VENDOR-BASE: compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaImageAsset.skiko.kt @ v1.12.1
// FLATTENED for our single-native-source-set layout: upstream splits Image.toBitmap /
// ByteArray.putBytesInto into expect (here) + actual (skikoExcludingWebMain / nativeMain).
// Those collapse into the same skikoRenderer source set here, which is illegal, so the two
@@ -38,7 +38,7 @@ class SkiaImageCache {
// (registerMemoryResource - e.g. downloaded PNGs) from growing image memory
// without limit (issue #2). On-screen images stay hot; an evicted one just
// re-decodes on next use.
private val fCache = LinkedHashMap<String, Image?>()
private val mCache = LinkedHashMap<String, Image?>()
// SVG / Android-vector kinds are RESOLUTION-INDEPENDENT: instead of caching one
// intrinsic-size raster and letting drawImageRect upscale it (blurry when an icon
@@ -46,8 +46,8 @@ class SkiaImageCache {
// destination pixel size and cache that raster keyed by "path@WxH". Mirrors
// upstream's size-driven DrawCache for SVGPainter. Intrinsic dims are cached
// separately so the layout pass (intrinsicSize) doesn't force a raster.
private val fSvgRasterCache = LinkedHashMap<String, Image?>()
private val fSvgIntrinsic = HashMap<String, androidx.compose.ui.geometry.Size>()
private val mSvgRasterCache = LinkedHashMap<String, Image?>()
private val mSvgIntrinsic = HashMap<String, androidx.compose.ui.geometry.Size>()
fun intrinsicSize(inPath: String, inKind: ResourceKind): androidx.compose.ui.geometry.Size {
if (inKind == ResourceKind.Svg || inKind == ResourceKind.AndroidVector) {
@@ -58,18 +58,18 @@ class SkiaImageCache {
}
private fun get(inPath: String, inKind: ResourceKind): Image? {
if (fCache.containsKey(inPath)) {
if (mCache.containsKey(inPath)) {
// Re-insert to mark most-recently-used (LinkedHashMap keeps first =
// least-recently-used for eviction below).
val vExisting = fCache.remove(inPath)
fCache[inPath] = vExisting
val vExisting = mCache.remove(inPath)
mCache[inPath] = vExisting
return vExisting
}
val vImage = decode(inPath, inKind)
fCache[inPath] = vImage
if (fCache.size > MAX_CACHED_IMAGES) {
val vEldest = fCache.keys.firstOrNull()
if (vEldest != null) fCache.remove(vEldest)?.close()
mCache[inPath] = vImage
if (mCache.size > MAX_CACHED_IMAGES) {
val vEldest = mCache.keys.firstOrNull()
if (vEldest != null) mCache.remove(vEldest)?.close()
}
return vImage
}
@@ -136,7 +136,7 @@ class SkiaImageCache {
/** Intrinsic (viewport) size of the vector, parsed once and cached. */
private fun svgIntrinsicSize(inPath: String, inKind: ResourceKind): androidx.compose.ui.geometry.Size {
fSvgIntrinsic[inPath]?.let { return it }
mSvgIntrinsic[inPath]?.let { return it }
val vBytes = svgBytes(inPath, inKind) ?: return androidx.compose.ui.geometry.Size.Unspecified
val vSize = runCatching {
val vDom = SVGDOM(Data.makeFromBytes(vBytes))
@@ -146,7 +146,7 @@ class SkiaImageCache {
vDom.close()
androidx.compose.ui.geometry.Size(vW, vH)
}.getOrDefault(androidx.compose.ui.geometry.Size.Unspecified)
fSvgIntrinsic[inPath] = vSize
mSvgIntrinsic[inPath] = vSize
return vSize
}
@@ -157,9 +157,9 @@ class SkiaImageCache {
val vWpx = inW.roundToInt().coerceAtLeast(1)
val vHpx = inH.roundToInt().coerceAtLeast(1)
val vKey = "$inPath@${vWpx}x$vHpx"
val vImg = if (fSvgRasterCache.containsKey(vKey)) {
val vExisting = fSvgRasterCache.remove(vKey)
fSvgRasterCache[vKey] = vExisting
val vImg = if (mSvgRasterCache.containsKey(vKey)) {
val vExisting = mSvgRasterCache.remove(vKey)
mSvgRasterCache[vKey] = vExisting
vExisting
} else {
val vIntrinsic = svgIntrinsicSize(inPath, inKind)
@@ -167,9 +167,9 @@ class SkiaImageCache {
val vRaster = if (vBytes != null && vIntrinsic.isSpecified) {
rasterizeSvgAt(vBytes, vIntrinsic.width, vIntrinsic.height, vWpx, vHpx)
} else null
fSvgRasterCache[vKey] = vRaster
if (fSvgRasterCache.size > MAX_CACHED_IMAGES) {
fSvgRasterCache.keys.firstOrNull()?.let { fSvgRasterCache.remove(it)?.close() }
mSvgRasterCache[vKey] = vRaster
if (mSvgRasterCache.size > MAX_CACHED_IMAGES) {
mSvgRasterCache.keys.firstOrNull()?.let { mSvgRasterCache.remove(it)?.close() }
}
vRaster
} ?: return
@@ -183,11 +183,11 @@ class SkiaImageCache {
}
fun destroy() {
for (vImg in fCache.values) vImg?.close()
fCache.clear()
for (vImg in fSvgRasterCache.values) vImg?.close()
fSvgRasterCache.clear()
fSvgIntrinsic.clear()
for (vImg in mCache.values) vImg?.close()
mCache.clear()
for (vImg in mSvgRasterCache.values) vImg?.close()
mSvgRasterCache.clear()
mSvgIntrinsic.clear()
}
}
@@ -6,7 +6,7 @@
* compose/ui/compose-fork.txt is commented out so this file survives
* `scripts/compose-fork/sync.sh`.
*/
// VENDOR-BASE: compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/platform/Synchronization.kt @ v1.12.0
// VENDOR-BASE: compose/ui/ui-text/src/commonMain/kotlin/androidx/compose/ui/text/platform/Synchronization.kt @ v1.12.1
package androidx.compose.ui.text.platform
@@ -18,35 +18,35 @@ package com.compose.sdl.icons
the renderer where to find the bytes. */
object IconFont {
private val fFonts = mutableMapOf<String, ByteArray>()
private val mFonts = mutableMapOf<String, ByteArray>()
// Families that render as single variable-axis glyphs (Material Symbols),
// vs ordinary text fonts - icon families are drawn one codepoint at a time,
// while text families (e.g. a bundled monospace) go through normal
// full-string rendering.
private val fIconFamilies = mutableSetOf<String>()
private val mIconFamilies = mutableSetOf<String>()
/** Register a TEXT font family's bytes (rendered as full strings; e.g. a
bundled monospace). Idempotent - a second call with the same family
replaces the previous bytes. */
fun register(inFamily: String, inBytes: ByteArray) {
fFonts[inFamily] = inBytes
mFonts[inFamily] = inBytes
}
/** Register an ICON font family (Material Symbols et al.) - variable-axis.
Same byte store as register(), but also flags the family as an icon
font. */
fun registerIcon(inFamily: String, inBytes: ByteArray) {
fFonts[inFamily] = inBytes
fIconFamilies += inFamily
mFonts[inFamily] = inBytes
mIconFamilies += inFamily
}
/** Bytes for a registered family, or null if not registered. Renderers
fall back to the default font when this returns null. */
fun bytesFor(inFamily: String): ByteArray? = fFonts[inFamily]
fun bytesFor(inFamily: String): ByteArray? = mFonts[inFamily]
/** True only for families registered via registerIcon() - icon fonts that
need single-glyph (variable-axis) rendering, not text fonts. */
fun isIconFamily(inFamily: String): Boolean = inFamily in fIconFamilies
fun isIconFamily(inFamily: String): Boolean = inFamily in mIconFamilies
val families: Set<String> get() = fFonts.keys
val families: Set<String> get() = mFonts.keys
}
@@ -2,7 +2,7 @@ package androidx.compose.ui.text
import kotlin.experimental.ExperimentalNativeApi
// VENDOR-BASE: compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/FontRasterizationSettings.skiko.kt @ v1.12.0
// VENDOR-BASE: compose/ui/ui-text/src/skikoMain/kotlin/androidx/compose/ui/text/FontRasterizationSettings.skiko.kt @ v1.12.1
// ==================
// MARK: FontRasterizationSettings native actual (Skia-free)
+111 -146
View File
@@ -1370,17 +1370,6 @@ abstract interface androidx.compose.ui/UiMediaScope { // androidx.compose.ui/UiM
}
}
abstract interface com.compose.sdl.renderer.skia/SkiaBridge { // com.compose.sdl.renderer.skia/SkiaBridge|null[0]
abstract val canvas // com.compose.sdl.renderer.skia/SkiaBridge.canvas|{}canvas[0]
abstract fun <get-canvas>(): org.jetbrains.skia/Canvas // com.compose.sdl.renderer.skia/SkiaBridge.canvas.<get-canvas>|<get-canvas>(){}[0]
abstract fun destroy() // com.compose.sdl.renderer.skia/SkiaBridge.destroy|destroy(){}[0]
abstract fun ensureSize(kotlin/Int, kotlin/Int): kotlin/Boolean // com.compose.sdl.renderer.skia/SkiaBridge.ensureSize|ensureSize(kotlin.Int;kotlin.Int){}[0]
abstract fun present() // com.compose.sdl.renderer.skia/SkiaBridge.present|present(){}[0]
abstract fun snapshot(): org.jetbrains.skia/Image? // com.compose.sdl.renderer.skia/SkiaBridge.snapshot|snapshot(){}[0]
abstract fun snapshotBgra(): kotlin/Triple<kotlin/Int, kotlin/Int, kotlin/ByteArray>? // com.compose.sdl.renderer.skia/SkiaBridge.snapshotBgra|snapshotBgra(){}[0]
}
abstract interface com.compose.sdl/ComposeWindowScope { // com.compose.sdl/ComposeWindowScope|null[0]
abstract val window // com.compose.sdl/ComposeWindowScope.window|{}window[0]
abstract fun <get-window>(): com.compose.sdl/ComposeNativeWindow // com.compose.sdl/ComposeWindowScope.window.<get-window>|<get-window>(){}[0]
@@ -1392,10 +1381,10 @@ abstract interface com.compose.sdl/RenderBackend { // com.compose.sdl/RenderBack
abstract fun beginFrame(kotlin/Float) // com.compose.sdl/RenderBackend.beginFrame|beginFrame(kotlin.Float){}[0]
abstract fun destroy() // com.compose.sdl/RenderBackend.destroy|destroy(){}[0]
abstract fun drawRoot(kotlin/Function1<androidx.compose.ui.graphics/Canvas, kotlin/Unit>) // com.compose.sdl/RenderBackend.drawRoot|drawRoot(kotlin.Function1<androidx.compose.ui.graphics.Canvas,kotlin.Unit>){}[0]
abstract fun endFrame() // com.compose.sdl/RenderBackend.endFrame|endFrame(){}[0]
abstract fun ensureSize(kotlin/Int, kotlin/Int): kotlin/Boolean // com.compose.sdl/RenderBackend.ensureSize|ensureSize(kotlin.Int;kotlin.Int){}[0]
abstract fun snapshotBgra(): kotlin/Triple<kotlin/Int, kotlin/Int, kotlin/ByteArray>? // com.compose.sdl/RenderBackend.snapshotBgra|snapshotBgra(){}[0]
open fun drawRoot(kotlin/Function1<androidx.compose.ui.graphics/Canvas, kotlin/Unit>) // com.compose.sdl/RenderBackend.drawRoot|drawRoot(kotlin.Function1<androidx.compose.ui.graphics.Canvas,kotlin.Unit>){}[0]
}
sealed interface androidx.compose.ui.autofill/ContentDataType { // androidx.compose.ui.autofill/ContentDataType|null[0]
@@ -2788,23 +2777,6 @@ final class androidx.compose.ui/CombinedModifier : androidx.compose.ui/Modifier
final fun toString(): kotlin/String // androidx.compose.ui/CombinedModifier.toString|toString(){}[0]
}
final class com.compose.sdl.input/LegacyPointerEvent { // com.compose.sdl.input/LegacyPointerEvent|null[0]
constructor <init>(kotlin/Float, kotlin/Float, androidx.compose.ui.input.pointer/PointerEventType, androidx.compose.ui.input.pointer/PointerButton = ...) // com.compose.sdl.input/LegacyPointerEvent.<init>|<init>(kotlin.Float;kotlin.Float;androidx.compose.ui.input.pointer.PointerEventType;androidx.compose.ui.input.pointer.PointerButton){}[0]
final val button // com.compose.sdl.input/LegacyPointerEvent.button|{}button[0]
final fun <get-button>(): androidx.compose.ui.input.pointer/PointerButton // com.compose.sdl.input/LegacyPointerEvent.button.<get-button>|<get-button>(){}[0]
final val type // com.compose.sdl.input/LegacyPointerEvent.type|{}type[0]
final fun <get-type>(): androidx.compose.ui.input.pointer/PointerEventType // com.compose.sdl.input/LegacyPointerEvent.type.<get-type>|<get-type>(){}[0]
final val x // com.compose.sdl.input/LegacyPointerEvent.x|{}x[0]
final fun <get-x>(): kotlin/Float // com.compose.sdl.input/LegacyPointerEvent.x.<get-x>|<get-x>(){}[0]
final val y // com.compose.sdl.input/LegacyPointerEvent.y|{}y[0]
final fun <get-y>(): kotlin/Float // com.compose.sdl.input/LegacyPointerEvent.y.<get-y>|<get-y>(){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // com.compose.sdl.input/LegacyPointerEvent.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // com.compose.sdl.input/LegacyPointerEvent.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // com.compose.sdl.input/LegacyPointerEvent.toString|toString(){}[0]
}
final class com.compose.sdl.node/ComposeRootHost { // com.compose.sdl.node/ComposeRootHost|null[0]
constructor <init>(kotlin/Float = ...) // com.compose.sdl.node/ComposeRootHost.<init>|<init>(kotlin.Float){}[0]
@@ -2850,37 +2822,12 @@ final class com.compose.sdl.node/ComposeRootHost { // com.compose.sdl.node/Compo
final fun setWindowFocused(kotlin/Boolean) // com.compose.sdl.node/ComposeRootHost.setWindowFocused|setWindowFocused(kotlin.Boolean){}[0]
}
final class com.compose.sdl.renderer.skia/SkiaSurfaceBridge : com.compose.sdl.renderer.skia/SkiaBridge { // com.compose.sdl.renderer.skia/SkiaSurfaceBridge|null[0]
constructor <init>(com.compose.sdl/SDL3Backend) // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.<init>|<init>(com.compose.sdl.SDL3Backend){}[0]
final val canvas // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.canvas|{}canvas[0]
final fun <get-canvas>(): org.jetbrains.skia/Canvas // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.canvas.<get-canvas>|<get-canvas>(){}[0]
final val height // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.height|{}height[0]
final fun <get-height>(): kotlin/Int // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.height.<get-height>|<get-height>(){}[0]
final val width // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.width|{}width[0]
final fun <get-width>(): kotlin/Int // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.width.<get-width>|<get-width>(){}[0]
final fun destroy() // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.destroy|destroy(){}[0]
final fun ensureSize(kotlin/Int, kotlin/Int): kotlin/Boolean // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.ensureSize|ensureSize(kotlin.Int;kotlin.Int){}[0]
final fun present() // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.present|present(){}[0]
final fun snapshot(): org.jetbrains.skia/Image? // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.snapshot|snapshot(){}[0]
final fun snapshotBgra(): kotlin/Triple<kotlin/Int, kotlin/Int, kotlin/ByteArray>? // com.compose.sdl.renderer.skia/SkiaSurfaceBridge.snapshotBgra|snapshotBgra(){}[0]
}
final class com.compose.sdl.window/PopupExitHandle { // com.compose.sdl.window/PopupExitHandle|null[0]
final val isExiting // com.compose.sdl.window/PopupExitHandle.isExiting|{}isExiting[0]
final fun <get-isExiting>(): androidx.compose.runtime/State<kotlin/Boolean> // com.compose.sdl.window/PopupExitHandle.isExiting.<get-isExiting>|<get-isExiting>(){}[0]
final fun enableExitTransition() // com.compose.sdl.window/PopupExitHandle.enableExitTransition|enableExitTransition(){}[0]
final fun finish() // com.compose.sdl.window/PopupExitHandle.finish|finish(){}[0]
}
final class com.compose.sdl.window/PopupHostState { // com.compose.sdl.window/PopupHostState|null[0]
final fun notifyOutsidePress(kotlin/Int, kotlin/Int) // com.compose.sdl.window/PopupHostState.notifyOutsidePress|notifyOutsidePress(kotlin.Int;kotlin.Int){}[0]
}
final class com.compose.sdl/ComposeNativeWindow { // com.compose.sdl/ComposeNativeWindow|null[0]
constructor <init>(com.compose.sdl/SDL3Backend, com.compose.sdl/GpuMode, kotlin/String) // com.compose.sdl/ComposeNativeWindow.<init>|<init>(com.compose.sdl.SDL3Backend;com.compose.sdl.GpuMode;kotlin.String){}[0]
constructor <init>(com.compose.sdl/Sdl3Backend, com.compose.sdl/GpuMode, kotlin/String) // com.compose.sdl/ComposeNativeWindow.<init>|<init>(com.compose.sdl.Sdl3Backend;com.compose.sdl.GpuMode;kotlin.String){}[0]
final val fps // com.compose.sdl/ComposeNativeWindow.fps|{}fps[0]
final fun <get-fps>(): kotlin/Int // com.compose.sdl/ComposeNativeWindow.fps.<get-fps>|<get-fps>(){}[0]
@@ -2888,14 +2835,24 @@ final class com.compose.sdl/ComposeNativeWindow { // com.compose.sdl/ComposeNati
final fun <get-gpuMode>(): com.compose.sdl/GpuMode // com.compose.sdl/ComposeNativeWindow.gpuMode.<get-gpuMode>|<get-gpuMode>(){}[0]
final val height // com.compose.sdl/ComposeNativeWindow.height|{}height[0]
final fun <get-height>(): kotlin/Int // com.compose.sdl/ComposeNativeWindow.height.<get-height>|<get-height>(){}[0]
final val isAlwaysOnTop // com.compose.sdl/ComposeNativeWindow.isAlwaysOnTop|{}isAlwaysOnTop[0]
final fun <get-isAlwaysOnTop>(): kotlin/Boolean // com.compose.sdl/ComposeNativeWindow.isAlwaysOnTop.<get-isAlwaysOnTop>|<get-isAlwaysOnTop>(){}[0]
final val isCloseRequested // com.compose.sdl/ComposeNativeWindow.isCloseRequested|{}isCloseRequested[0]
final fun <get-isCloseRequested>(): kotlin/Boolean // com.compose.sdl/ComposeNativeWindow.isCloseRequested.<get-isCloseRequested>|<get-isCloseRequested>(){}[0]
final val isEnabled // com.compose.sdl/ComposeNativeWindow.isEnabled|{}isEnabled[0]
final fun <get-isEnabled>(): kotlin/Boolean // com.compose.sdl/ComposeNativeWindow.isEnabled.<get-isEnabled>|<get-isEnabled>(){}[0]
final val isFocusable // com.compose.sdl/ComposeNativeWindow.isFocusable|{}isFocusable[0]
final fun <get-isFocusable>(): kotlin/Boolean // com.compose.sdl/ComposeNativeWindow.isFocusable.<get-isFocusable>|<get-isFocusable>(){}[0]
final val isFullscreen // com.compose.sdl/ComposeNativeWindow.isFullscreen|{}isFullscreen[0]
final fun <get-isFullscreen>(): kotlin/Boolean // com.compose.sdl/ComposeNativeWindow.isFullscreen.<get-isFullscreen>|<get-isFullscreen>(){}[0]
final val isMaximized // com.compose.sdl/ComposeNativeWindow.isMaximized|{}isMaximized[0]
final fun <get-isMaximized>(): kotlin/Boolean // com.compose.sdl/ComposeNativeWindow.isMaximized.<get-isMaximized>|<get-isMaximized>(){}[0]
final val isMinimized // com.compose.sdl/ComposeNativeWindow.isMinimized|{}isMinimized[0]
final fun <get-isMinimized>(): kotlin/Boolean // com.compose.sdl/ComposeNativeWindow.isMinimized.<get-isMinimized>|<get-isMinimized>(){}[0]
final val isResizable // com.compose.sdl/ComposeNativeWindow.isResizable|{}isResizable[0]
final fun <get-isResizable>(): kotlin/Boolean // com.compose.sdl/ComposeNativeWindow.isResizable.<get-isResizable>|<get-isResizable>(){}[0]
final val isUndecorated // com.compose.sdl/ComposeNativeWindow.isUndecorated|{}isUndecorated[0]
final fun <get-isUndecorated>(): kotlin/Boolean // com.compose.sdl/ComposeNativeWindow.isUndecorated.<get-isUndecorated>|<get-isUndecorated>(){}[0]
final val pixelDensity // com.compose.sdl/ComposeNativeWindow.pixelDensity|{}pixelDensity[0]
final fun <get-pixelDensity>(): kotlin/Float // com.compose.sdl/ComposeNativeWindow.pixelDensity.<get-pixelDensity>|<get-pixelDensity>(){}[0]
final val pixelHeight // com.compose.sdl/ComposeNativeWindow.pixelHeight|{}pixelHeight[0]
@@ -2915,50 +2872,79 @@ final class com.compose.sdl/ComposeNativeWindow { // com.compose.sdl/ComposeNati
final fun minimize() // com.compose.sdl/ComposeNativeWindow.minimize|minimize(){}[0]
final fun onResized() // com.compose.sdl/ComposeNativeWindow.onResized|onResized(){}[0]
final fun raise() // com.compose.sdl/ComposeNativeWindow.raise|raise(){}[0]
final fun rawSdlHandles(): com.compose.sdl/RawSdlHandles // com.compose.sdl/ComposeNativeWindow.rawSdlHandles|rawSdlHandles(){}[0]
final fun requestCloseFromUser(): kotlin/Boolean // com.compose.sdl/ComposeNativeWindow.requestCloseFromUser|requestCloseFromUser(){}[0]
final fun restore() // com.compose.sdl/ComposeNativeWindow.restore|restore(){}[0]
final fun setAlwaysOnTop(kotlin/Boolean) // com.compose.sdl/ComposeNativeWindow.setAlwaysOnTop|setAlwaysOnTop(kotlin.Boolean){}[0]
final fun setEnabled(kotlin/Boolean) // com.compose.sdl/ComposeNativeWindow.setEnabled|setEnabled(kotlin.Boolean){}[0]
final fun setFocusable(kotlin/Boolean) // com.compose.sdl/ComposeNativeWindow.setFocusable|setFocusable(kotlin.Boolean){}[0]
final fun setFullscreen(kotlin/Boolean) // com.compose.sdl/ComposeNativeWindow.setFullscreen|setFullscreen(kotlin.Boolean){}[0]
final fun setOnCloseRequest(kotlin/Function0<kotlin/Boolean>?) // com.compose.sdl/ComposeNativeWindow.setOnCloseRequest|setOnCloseRequest(kotlin.Function0<kotlin.Boolean>?){}[0]
final fun setOnKeyShortcut(kotlin/Function1<androidx.compose.ui.input.key/KeyEvent, kotlin/Boolean>?) // com.compose.sdl/ComposeNativeWindow.setOnKeyShortcut|setOnKeyShortcut(kotlin.Function1<androidx.compose.ui.input.key.KeyEvent,kotlin.Boolean>?){}[0]
final fun setOpacity(kotlin/Float) // com.compose.sdl/ComposeNativeWindow.setOpacity|setOpacity(kotlin.Float){}[0]
final fun setResizable(kotlin/Boolean) // com.compose.sdl/ComposeNativeWindow.setResizable|setResizable(kotlin.Boolean){}[0]
final fun setSize(kotlin/Int, kotlin/Int) // com.compose.sdl/ComposeNativeWindow.setSize|setSize(kotlin.Int;kotlin.Int){}[0]
final fun setTitle(kotlin/String) // com.compose.sdl/ComposeNativeWindow.setTitle|setTitle(kotlin.String){}[0]
final fun setUndecorated(kotlin/Boolean) // com.compose.sdl/ComposeNativeWindow.setUndecorated|setUndecorated(kotlin.Boolean){}[0]
final fun toggleFullscreen() // com.compose.sdl/ComposeNativeWindow.toggleFullscreen|toggleFullscreen(){}[0]
final fun updateFps(kotlin/Int) // com.compose.sdl/ComposeNativeWindow.updateFps|updateFps(kotlin.Int){}[0]
}
final class com.compose.sdl/SDL3Backend { // com.compose.sdl/SDL3Backend|null[0]
constructor <init>(kotlin/String = ..., kotlin/Int = ..., kotlin/Int = ..., com.compose.sdl/GpuMode = ..., kotlin.collections/List<kotlin/String> = ..., kotlin.collections/List<kotlin/String> = ...) // com.compose.sdl/SDL3Backend.<init>|<init>(kotlin.String;kotlin.Int;kotlin.Int;com.compose.sdl.GpuMode;kotlin.collections.List<kotlin.String>;kotlin.collections.List<kotlin.String>){}[0]
final class com.compose.sdl/RawSdlHandles { // com.compose.sdl/RawSdlHandles|null[0]
constructor <init>(kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>?, kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>?, kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>?, kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>?) // com.compose.sdl/RawSdlHandles.<init>|<init>(kotlinx.cinterop.CPointer<out|kotlinx.cinterop.CPointed>?;kotlinx.cinterop.CPointer<out|kotlinx.cinterop.CPointed>?;kotlinx.cinterop.CPointer<out|kotlinx.cinterop.CPointed>?;kotlinx.cinterop.CPointer<out|kotlinx.cinterop.CPointed>?){}[0]
final val gpuMode // com.compose.sdl/SDL3Backend.gpuMode|{}gpuMode[0]
final fun <get-gpuMode>(): com.compose.sdl/GpuMode // com.compose.sdl/SDL3Backend.gpuMode.<get-gpuMode>|<get-gpuMode>(){}[0]
final val pixelDensity // com.compose.sdl/SDL3Backend.pixelDensity|{}pixelDensity[0]
final fun <get-pixelDensity>(): kotlin/Float // com.compose.sdl/SDL3Backend.pixelDensity.<get-pixelDensity>|<get-pixelDensity>(){}[0]
final val windowId // com.compose.sdl/SDL3Backend.windowId|{}windowId[0]
final fun <get-windowId>(): kotlin/UInt // com.compose.sdl/SDL3Backend.windowId.<get-windowId>|<get-windowId>(){}[0]
final val glContext // com.compose.sdl/RawSdlHandles.glContext|{}glContext[0]
final fun <get-glContext>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/RawSdlHandles.glContext.<get-glContext>|<get-glContext>(){}[0]
final val metalView // com.compose.sdl/RawSdlHandles.metalView|{}metalView[0]
final fun <get-metalView>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/RawSdlHandles.metalView.<get-metalView>|<get-metalView>(){}[0]
final val renderer // com.compose.sdl/RawSdlHandles.renderer|{}renderer[0]
final fun <get-renderer>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/RawSdlHandles.renderer.<get-renderer>|<get-renderer>(){}[0]
final val window // com.compose.sdl/RawSdlHandles.window|{}window[0]
final fun <get-window>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/RawSdlHandles.window.<get-window>|<get-window>(){}[0]
final var glContext // com.compose.sdl/SDL3Backend.glContext|{}glContext[0]
final fun <get-glContext>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/SDL3Backend.glContext.<get-glContext>|<get-glContext>(){}[0]
final var metalView // com.compose.sdl/SDL3Backend.metalView|{}metalView[0]
final fun <get-metalView>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/SDL3Backend.metalView.<get-metalView>|<get-metalView>(){}[0]
final var pixelHeight // com.compose.sdl/SDL3Backend.pixelHeight|{}pixelHeight[0]
final fun <get-pixelHeight>(): kotlin/Int // com.compose.sdl/SDL3Backend.pixelHeight.<get-pixelHeight>|<get-pixelHeight>(){}[0]
final var pixelWidth // com.compose.sdl/SDL3Backend.pixelWidth|{}pixelWidth[0]
final fun <get-pixelWidth>(): kotlin/Int // com.compose.sdl/SDL3Backend.pixelWidth.<get-pixelWidth>|<get-pixelWidth>(){}[0]
final var renderer // com.compose.sdl/SDL3Backend.renderer|{}renderer[0]
final fun <get-renderer>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/SDL3Backend.renderer.<get-renderer>|<get-renderer>(){}[0]
final var vsyncEnabled // com.compose.sdl/SDL3Backend.vsyncEnabled|{}vsyncEnabled[0]
final fun <get-vsyncEnabled>(): kotlin/Boolean // com.compose.sdl/SDL3Backend.vsyncEnabled.<get-vsyncEnabled>|<get-vsyncEnabled>(){}[0]
final var window // com.compose.sdl/SDL3Backend.window|{}window[0]
final fun <get-window>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/SDL3Backend.window.<get-window>|<get-window>(){}[0]
final var windowHeight // com.compose.sdl/SDL3Backend.windowHeight|{}windowHeight[0]
final fun <get-windowHeight>(): kotlin/Int // com.compose.sdl/SDL3Backend.windowHeight.<get-windowHeight>|<get-windowHeight>(){}[0]
final var windowWidth // com.compose.sdl/SDL3Backend.windowWidth|{}windowWidth[0]
final fun <get-windowWidth>(): kotlin/Int // com.compose.sdl/SDL3Backend.windowWidth.<get-windowWidth>|<get-windowWidth>(){}[0]
final fun component1(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/RawSdlHandles.component1|component1(){}[0]
final fun component2(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/RawSdlHandles.component2|component2(){}[0]
final fun component3(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/RawSdlHandles.component3|component3(){}[0]
final fun component4(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/RawSdlHandles.component4|component4(){}[0]
final fun copy(kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? = ..., kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? = ..., kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? = ..., kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? = ...): com.compose.sdl/RawSdlHandles // com.compose.sdl/RawSdlHandles.copy|copy(kotlinx.cinterop.CPointer<out|kotlinx.cinterop.CPointed>?;kotlinx.cinterop.CPointer<out|kotlinx.cinterop.CPointed>?;kotlinx.cinterop.CPointer<out|kotlinx.cinterop.CPointed>?;kotlinx.cinterop.CPointer<out|kotlinx.cinterop.CPointed>?){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // com.compose.sdl/RawSdlHandles.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // com.compose.sdl/RawSdlHandles.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // com.compose.sdl/RawSdlHandles.toString|toString(){}[0]
}
final fun applyThemeIcon() // com.compose.sdl/SDL3Backend.applyThemeIcon|applyThemeIcon(){}[0]
final fun destroy(kotlin/Boolean = ...) // com.compose.sdl/SDL3Backend.destroy|destroy(kotlin.Boolean){}[0]
final fun init(): kotlin/Boolean // com.compose.sdl/SDL3Backend.init|init(){}[0]
final fun updateWindowSize() // com.compose.sdl/SDL3Backend.updateWindowSize|updateWindowSize(){}[0]
final class com.compose.sdl/Sdl3Backend { // com.compose.sdl/Sdl3Backend|null[0]
constructor <init>(kotlin/String = ..., kotlin/Int = ..., kotlin/Int = ..., com.compose.sdl/GpuMode = ..., kotlin.collections/List<kotlin/String> = ..., kotlin.collections/List<kotlin/String> = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ...) // com.compose.sdl/Sdl3Backend.<init>|<init>(kotlin.String;kotlin.Int;kotlin.Int;com.compose.sdl.GpuMode;kotlin.collections.List<kotlin.String>;kotlin.collections.List<kotlin.String>;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean){}[0]
final val gpuMode // com.compose.sdl/Sdl3Backend.gpuMode|{}gpuMode[0]
final fun <get-gpuMode>(): com.compose.sdl/GpuMode // com.compose.sdl/Sdl3Backend.gpuMode.<get-gpuMode>|<get-gpuMode>(){}[0]
final val pixelDensity // com.compose.sdl/Sdl3Backend.pixelDensity|{}pixelDensity[0]
final fun <get-pixelDensity>(): kotlin/Float // com.compose.sdl/Sdl3Backend.pixelDensity.<get-pixelDensity>|<get-pixelDensity>(){}[0]
final val windowId // com.compose.sdl/Sdl3Backend.windowId|{}windowId[0]
final fun <get-windowId>(): kotlin/UInt // com.compose.sdl/Sdl3Backend.windowId.<get-windowId>|<get-windowId>(){}[0]
final var glContext // com.compose.sdl/Sdl3Backend.glContext|{}glContext[0]
final fun <get-glContext>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/Sdl3Backend.glContext.<get-glContext>|<get-glContext>(){}[0]
final var metalView // com.compose.sdl/Sdl3Backend.metalView|{}metalView[0]
final fun <get-metalView>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/Sdl3Backend.metalView.<get-metalView>|<get-metalView>(){}[0]
final var pixelHeight // com.compose.sdl/Sdl3Backend.pixelHeight|{}pixelHeight[0]
final fun <get-pixelHeight>(): kotlin/Int // com.compose.sdl/Sdl3Backend.pixelHeight.<get-pixelHeight>|<get-pixelHeight>(){}[0]
final var pixelWidth // com.compose.sdl/Sdl3Backend.pixelWidth|{}pixelWidth[0]
final fun <get-pixelWidth>(): kotlin/Int // com.compose.sdl/Sdl3Backend.pixelWidth.<get-pixelWidth>|<get-pixelWidth>(){}[0]
final var renderer // com.compose.sdl/Sdl3Backend.renderer|{}renderer[0]
final fun <get-renderer>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/Sdl3Backend.renderer.<get-renderer>|<get-renderer>(){}[0]
final var vsyncEnabled // com.compose.sdl/Sdl3Backend.vsyncEnabled|{}vsyncEnabled[0]
final fun <get-vsyncEnabled>(): kotlin/Boolean // com.compose.sdl/Sdl3Backend.vsyncEnabled.<get-vsyncEnabled>|<get-vsyncEnabled>(){}[0]
final var window // com.compose.sdl/Sdl3Backend.window|{}window[0]
final fun <get-window>(): kotlinx.cinterop/CPointer<out kotlinx.cinterop/CPointed>? // com.compose.sdl/Sdl3Backend.window.<get-window>|<get-window>(){}[0]
final var windowHeight // com.compose.sdl/Sdl3Backend.windowHeight|{}windowHeight[0]
final fun <get-windowHeight>(): kotlin/Int // com.compose.sdl/Sdl3Backend.windowHeight.<get-windowHeight>|<get-windowHeight>(){}[0]
final var windowWidth // com.compose.sdl/Sdl3Backend.windowWidth|{}windowWidth[0]
final fun <get-windowWidth>(): kotlin/Int // com.compose.sdl/Sdl3Backend.windowWidth.<get-windowWidth>|<get-windowWidth>(){}[0]
final fun applyThemeIcon() // com.compose.sdl/Sdl3Backend.applyThemeIcon|applyThemeIcon(){}[0]
final fun destroy(kotlin/Boolean = ...) // com.compose.sdl/Sdl3Backend.destroy|destroy(kotlin.Boolean){}[0]
final fun init(): kotlin/Boolean // com.compose.sdl/Sdl3Backend.init|init(){}[0]
final fun updateWindowSize() // com.compose.sdl/Sdl3Backend.updateWindowSize|updateWindowSize(){}[0]
}
final value class <#A: kotlin/Any?> androidx.compose.ui/SessionMutex { // androidx.compose.ui/SessionMutex|null[0]
@@ -4161,16 +4147,25 @@ sealed class com.compose.sdl/AppEvent { // com.compose.sdl/AppEvent|null[0]
}
final class Pointer : com.compose.sdl/AppEvent { // com.compose.sdl/AppEvent.Pointer|null[0]
constructor <init>(com.compose.sdl.input/LegacyPointerEvent, kotlin/UInt = ...) // com.compose.sdl/AppEvent.Pointer.<init>|<init>(com.compose.sdl.input.LegacyPointerEvent;kotlin.UInt){}[0]
constructor <init>(kotlin/Float, kotlin/Float, androidx.compose.ui.input.pointer/PointerEventType, androidx.compose.ui.input.pointer/PointerButton = ..., kotlin/UInt = ...) // com.compose.sdl/AppEvent.Pointer.<init>|<init>(kotlin.Float;kotlin.Float;androidx.compose.ui.input.pointer.PointerEventType;androidx.compose.ui.input.pointer.PointerButton;kotlin.UInt){}[0]
final val event // com.compose.sdl/AppEvent.Pointer.event|{}event[0]
final fun <get-event>(): com.compose.sdl.input/LegacyPointerEvent // com.compose.sdl/AppEvent.Pointer.event.<get-event>|<get-event>(){}[0]
final val button // com.compose.sdl/AppEvent.Pointer.button|{}button[0]
final fun <get-button>(): androidx.compose.ui.input.pointer/PointerButton // com.compose.sdl/AppEvent.Pointer.button.<get-button>|<get-button>(){}[0]
final val type // com.compose.sdl/AppEvent.Pointer.type|{}type[0]
final fun <get-type>(): androidx.compose.ui.input.pointer/PointerEventType // com.compose.sdl/AppEvent.Pointer.type.<get-type>|<get-type>(){}[0]
final val windowId // com.compose.sdl/AppEvent.Pointer.windowId|{}windowId[0]
final fun <get-windowId>(): kotlin/UInt // com.compose.sdl/AppEvent.Pointer.windowId.<get-windowId>|<get-windowId>(){}[0]
final val x // com.compose.sdl/AppEvent.Pointer.x|{}x[0]
final fun <get-x>(): kotlin/Float // com.compose.sdl/AppEvent.Pointer.x.<get-x>|<get-x>(){}[0]
final val y // com.compose.sdl/AppEvent.Pointer.y|{}y[0]
final fun <get-y>(): kotlin/Float // com.compose.sdl/AppEvent.Pointer.y.<get-y>|<get-y>(){}[0]
final fun component1(): com.compose.sdl.input/LegacyPointerEvent // com.compose.sdl/AppEvent.Pointer.component1|component1(){}[0]
final fun component2(): kotlin/UInt // com.compose.sdl/AppEvent.Pointer.component2|component2(){}[0]
final fun copy(com.compose.sdl.input/LegacyPointerEvent = ..., kotlin/UInt = ...): com.compose.sdl/AppEvent.Pointer // com.compose.sdl/AppEvent.Pointer.copy|copy(com.compose.sdl.input.LegacyPointerEvent;kotlin.UInt){}[0]
final fun component1(): kotlin/Float // com.compose.sdl/AppEvent.Pointer.component1|component1(){}[0]
final fun component2(): kotlin/Float // com.compose.sdl/AppEvent.Pointer.component2|component2(){}[0]
final fun component3(): androidx.compose.ui.input.pointer/PointerEventType // com.compose.sdl/AppEvent.Pointer.component3|component3(){}[0]
final fun component4(): androidx.compose.ui.input.pointer/PointerButton // com.compose.sdl/AppEvent.Pointer.component4|component4(){}[0]
final fun component5(): kotlin/UInt // com.compose.sdl/AppEvent.Pointer.component5|component5(){}[0]
final fun copy(kotlin/Float = ..., kotlin/Float = ..., androidx.compose.ui.input.pointer/PointerEventType = ..., androidx.compose.ui.input.pointer/PointerButton = ..., kotlin/UInt = ...): com.compose.sdl/AppEvent.Pointer // com.compose.sdl/AppEvent.Pointer.copy|copy(kotlin.Float;kotlin.Float;androidx.compose.ui.input.pointer.PointerEventType;androidx.compose.ui.input.pointer.PointerButton;kotlin.UInt){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // com.compose.sdl/AppEvent.Pointer.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // com.compose.sdl/AppEvent.Pointer.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // com.compose.sdl/AppEvent.Pointer.toString|toString(){}[0]
@@ -4293,22 +4288,23 @@ sealed class com.compose.sdl/AppEvent { // com.compose.sdl/AppEvent|null[0]
}
sealed class com.compose.sdl/GpuMode { // com.compose.sdl/GpuMode|null[0]
sealed class Skia : com.compose.sdl/GpuMode { // com.compose.sdl/GpuMode.Skia|null[0]
final object Metal : com.compose.sdl/GpuMode.Skia { // com.compose.sdl/GpuMode.Skia.Metal|null[0]
final fun toString(): kotlin/String // com.compose.sdl/GpuMode.Skia.Metal.toString|toString(){}[0]
}
final object OpenGL : com.compose.sdl/GpuMode.Skia { // com.compose.sdl/GpuMode.Skia.OpenGL|null[0]
final fun toString(): kotlin/String // com.compose.sdl/GpuMode.Skia.OpenGL.toString|toString(){}[0]
}
}
final val isGpuAccelerated // com.compose.sdl/GpuMode.isGpuAccelerated|{}isGpuAccelerated[0]
final fun <get-isGpuAccelerated>(): kotlin/Boolean // com.compose.sdl/GpuMode.isGpuAccelerated.<get-isGpuAccelerated>|<get-isGpuAccelerated>(){}[0]
final object Auto : com.compose.sdl/GpuMode { // com.compose.sdl/GpuMode.Auto|null[0]
final fun toString(): kotlin/String // com.compose.sdl/GpuMode.Auto.toString|toString(){}[0]
}
final object Software : com.compose.sdl/GpuMode { // com.compose.sdl/GpuMode.Software|null[0]
final fun toString(): kotlin/String // com.compose.sdl/GpuMode.Software.toString|toString(){}[0]
final object CpuRaster : com.compose.sdl/GpuMode { // com.compose.sdl/GpuMode.CpuRaster|null[0]
final fun toString(): kotlin/String // com.compose.sdl/GpuMode.CpuRaster.toString|toString(){}[0]
}
final object Metal : com.compose.sdl/GpuMode { // com.compose.sdl/GpuMode.Metal|null[0]
final fun toString(): kotlin/String // com.compose.sdl/GpuMode.Metal.toString|toString(){}[0]
}
final object OpenGL : com.compose.sdl/GpuMode { // com.compose.sdl/GpuMode.OpenGL|null[0]
final fun toString(): kotlin/String // com.compose.sdl/GpuMode.OpenGL.toString|toString(){}[0]
}
}
@@ -4520,27 +4516,6 @@ final object androidx.compose.ui/ComposeUiFlags { // androidx.compose.ui/Compose
final fun <set-isViewFocusFixEnabled>(kotlin/Boolean) // androidx.compose.ui/ComposeUiFlags.isViewFocusFixEnabled.<set-isViewFocusFixEnabled>|<set-isViewFocusFixEnabled>(kotlin.Boolean){}[0]
}
final object com.compose.sdl.platform/DefaultViewConfiguration : androidx.compose.ui.platform/ViewConfiguration { // com.compose.sdl.platform/DefaultViewConfiguration|null[0]
final val doubleTapMinTimeMillis // com.compose.sdl.platform/DefaultViewConfiguration.doubleTapMinTimeMillis|{}doubleTapMinTimeMillis[0]
final fun <get-doubleTapMinTimeMillis>(): kotlin/Long // com.compose.sdl.platform/DefaultViewConfiguration.doubleTapMinTimeMillis.<get-doubleTapMinTimeMillis>|<get-doubleTapMinTimeMillis>(){}[0]
final val doubleTapTimeoutMillis // com.compose.sdl.platform/DefaultViewConfiguration.doubleTapTimeoutMillis|{}doubleTapTimeoutMillis[0]
final fun <get-doubleTapTimeoutMillis>(): kotlin/Long // com.compose.sdl.platform/DefaultViewConfiguration.doubleTapTimeoutMillis.<get-doubleTapTimeoutMillis>|<get-doubleTapTimeoutMillis>(){}[0]
final val handwritingGestureLineMargin // com.compose.sdl.platform/DefaultViewConfiguration.handwritingGestureLineMargin|{}handwritingGestureLineMargin[0]
final fun <get-handwritingGestureLineMargin>(): kotlin/Float // com.compose.sdl.platform/DefaultViewConfiguration.handwritingGestureLineMargin.<get-handwritingGestureLineMargin>|<get-handwritingGestureLineMargin>(){}[0]
final val handwritingSlop // com.compose.sdl.platform/DefaultViewConfiguration.handwritingSlop|{}handwritingSlop[0]
final fun <get-handwritingSlop>(): kotlin/Float // com.compose.sdl.platform/DefaultViewConfiguration.handwritingSlop.<get-handwritingSlop>|<get-handwritingSlop>(){}[0]
final val longPressTimeoutMillis // com.compose.sdl.platform/DefaultViewConfiguration.longPressTimeoutMillis|{}longPressTimeoutMillis[0]
final fun <get-longPressTimeoutMillis>(): kotlin/Long // com.compose.sdl.platform/DefaultViewConfiguration.longPressTimeoutMillis.<get-longPressTimeoutMillis>|<get-longPressTimeoutMillis>(){}[0]
final val maximumFlingVelocity // com.compose.sdl.platform/DefaultViewConfiguration.maximumFlingVelocity|{}maximumFlingVelocity[0]
final fun <get-maximumFlingVelocity>(): kotlin/Float // com.compose.sdl.platform/DefaultViewConfiguration.maximumFlingVelocity.<get-maximumFlingVelocity>|<get-maximumFlingVelocity>(){}[0]
final val minimumFlingVelocity // com.compose.sdl.platform/DefaultViewConfiguration.minimumFlingVelocity|{}minimumFlingVelocity[0]
final fun <get-minimumFlingVelocity>(): kotlin/Float // com.compose.sdl.platform/DefaultViewConfiguration.minimumFlingVelocity.<get-minimumFlingVelocity>|<get-minimumFlingVelocity>(){}[0]
final val minimumTouchTargetSize // com.compose.sdl.platform/DefaultViewConfiguration.minimumTouchTargetSize|{}minimumTouchTargetSize[0]
final fun <get-minimumTouchTargetSize>(): androidx.compose.ui.unit/DpSize // com.compose.sdl.platform/DefaultViewConfiguration.minimumTouchTargetSize.<get-minimumTouchTargetSize>|<get-minimumTouchTargetSize>(){}[0]
final val touchSlop // com.compose.sdl.platform/DefaultViewConfiguration.touchSlop|{}touchSlop[0]
final fun <get-touchSlop>(): kotlin/Float // com.compose.sdl.platform/DefaultViewConfiguration.touchSlop.<get-touchSlop>|<get-touchSlop>(){}[0]
}
final object com.compose.sdl.text.input/ImeBridge { // com.compose.sdl.text.input/ImeBridge|null[0]
final var onSessionActiveChange // com.compose.sdl.text.input/ImeBridge.onSessionActiveChange|{}onSessionActiveChange[0]
final fun <get-onSessionActiveChange>(): kotlin/Function1<kotlin/Boolean, kotlin/Unit>? // com.compose.sdl.text.input/ImeBridge.onSessionActiveChange.<get-onSessionActiveChange>|<get-onSessionActiveChange>(){}[0]
@@ -4843,6 +4818,8 @@ final val androidx.compose.ui.semantics/androidx_compose_ui_semantics_SemanticsP
final val androidx.compose.ui.spatial/androidx_compose_ui_spatial_RelativeLayoutBounds$stableprop // androidx.compose.ui.spatial/androidx_compose_ui_spatial_RelativeLayoutBounds$stableprop|#static{}androidx_compose_ui_spatial_RelativeLayoutBounds$stableprop[0]
final val androidx.compose.ui.window/androidx_compose_ui_window_DialogProperties$stableprop // androidx.compose.ui.window/androidx_compose_ui_window_DialogProperties$stableprop|#static{}androidx_compose_ui_window_DialogProperties$stableprop[0]
final val androidx.compose.ui.window/androidx_compose_ui_window_PopupProperties$stableprop // androidx.compose.ui.window/androidx_compose_ui_window_PopupProperties$stableprop|#static{}androidx_compose_ui_window_PopupProperties$stableprop[0]
final val androidx.compose.ui/LocalSystemTheme // androidx.compose.ui/LocalSystemTheme|{}LocalSystemTheme[0]
final fun <get-LocalSystemTheme>(): androidx.compose.runtime/ProvidableCompositionLocal<androidx.compose.ui/SystemTheme> // androidx.compose.ui/LocalSystemTheme.<get-LocalSystemTheme>|<get-LocalSystemTheme>(){}[0]
final val androidx.compose.ui/LocalUiMediaScope // androidx.compose.ui/LocalUiMediaScope|{}LocalUiMediaScope[0]
final fun <get-LocalUiMediaScope>(): androidx.compose.runtime/ProvidableCompositionLocal<androidx.compose.ui/UiMediaScope> // androidx.compose.ui/LocalUiMediaScope.<get-LocalUiMediaScope>|<get-LocalUiMediaScope>(){}[0]
final val androidx.compose.ui/androidx_compose_ui_AbsoluteAlignment$stableprop // androidx.compose.ui/androidx_compose_ui_AbsoluteAlignment$stableprop|#static{}androidx_compose_ui_AbsoluteAlignment$stableprop[0]
@@ -4854,7 +4831,6 @@ final val androidx.compose.ui/androidx_compose_ui_BiasAlignment_Vertical$stablep
final val androidx.compose.ui/androidx_compose_ui_CombinedModifier$stableprop // androidx.compose.ui/androidx_compose_ui_CombinedModifier$stableprop|#static{}androidx_compose_ui_CombinedModifier$stableprop[0]
final val androidx.compose.ui/androidx_compose_ui_ComposeUiFlags$stableprop // androidx.compose.ui/androidx_compose_ui_ComposeUiFlags$stableprop|#static{}androidx_compose_ui_ComposeUiFlags$stableprop[0]
final val androidx.compose.ui/androidx_compose_ui_Modifier_Node$stableprop // androidx.compose.ui/androidx_compose_ui_Modifier_Node$stableprop|#static{}androidx_compose_ui_Modifier_Node$stableprop[0]
final val com.compose.sdl.input/com_compose_sdl_input_LegacyPointerEvent$stableprop // com.compose.sdl.input/com_compose_sdl_input_LegacyPointerEvent$stableprop|#static{}com_compose_sdl_input_LegacyPointerEvent$stableprop[0]
final val com.compose.sdl.layout/intOffset // com.compose.sdl.layout/intOffset|@androidx.compose.ui.layout.LayoutCoordinates{}intOffset[0]
final fun (androidx.compose.ui.layout/LayoutCoordinates).<get-intOffset>(): androidx.compose.ui.unit/IntOffset // com.compose.sdl.layout/intOffset.<get-intOffset>|<get-intOffset>@androidx.compose.ui.layout.LayoutCoordinates(){}[0]
final val com.compose.sdl.layout/x // com.compose.sdl.layout/x|@androidx.compose.ui.layout.LayoutCoordinates{}x[0]
@@ -4862,14 +4838,9 @@ final val com.compose.sdl.layout/x // com.compose.sdl.layout/x|@androidx.compose
final val com.compose.sdl.layout/y // com.compose.sdl.layout/y|@androidx.compose.ui.layout.LayoutCoordinates{}y[0]
final fun (androidx.compose.ui.layout/LayoutCoordinates).<get-y>(): kotlin/Int // com.compose.sdl.layout/y.<get-y>|<get-y>@androidx.compose.ui.layout.LayoutCoordinates(){}[0]
final val com.compose.sdl.node/com_compose_sdl_node_ComposeRootHost$stableprop // com.compose.sdl.node/com_compose_sdl_node_ComposeRootHost$stableprop|#static{}com_compose_sdl_node_ComposeRootHost$stableprop[0]
final val com.compose.sdl.platform/com_compose_sdl_platform_DefaultViewConfiguration$stableprop // com.compose.sdl.platform/com_compose_sdl_platform_DefaultViewConfiguration$stableprop|#static{}com_compose_sdl_platform_DefaultViewConfiguration$stableprop[0]
final val com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaSurfaceBridge$stableprop // com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaSurfaceBridge$stableprop|#static{}com_compose_sdl_renderer_skia_SkiaSurfaceBridge$stableprop[0]
final val com.compose.sdl.text.input/com_compose_sdl_text_input_ImeBridge$stableprop // com.compose.sdl.text.input/com_compose_sdl_text_input_ImeBridge$stableprop|#static{}com_compose_sdl_text_input_ImeBridge$stableprop[0]
final val com.compose.sdl.window/LocalPopupExitHandle // com.compose.sdl.window/LocalPopupExitHandle|{}LocalPopupExitHandle[0]
final fun <get-LocalPopupExitHandle>(): androidx.compose.runtime/ProvidableCompositionLocal<com.compose.sdl.window/PopupExitHandle?> // com.compose.sdl.window/LocalPopupExitHandle.<get-LocalPopupExitHandle>|<get-LocalPopupExitHandle>(){}[0]
final val com.compose.sdl.window/LocalPopupHost // com.compose.sdl.window/LocalPopupHost|{}LocalPopupHost[0]
final fun <get-LocalPopupHost>(): androidx.compose.runtime/ProvidableCompositionLocal<com.compose.sdl.window/PopupHostState> // com.compose.sdl.window/LocalPopupHost.<get-LocalPopupHost>|<get-LocalPopupHost>(){}[0]
final val com.compose.sdl.window/com_compose_sdl_window_PopupExitHandle$stableprop // com.compose.sdl.window/com_compose_sdl_window_PopupExitHandle$stableprop|#static{}com_compose_sdl_window_PopupExitHandle$stableprop[0]
final val com.compose.sdl.window/com_compose_sdl_window_PopupHostState$stableprop // com.compose.sdl.window/com_compose_sdl_window_PopupHostState$stableprop|#static{}com_compose_sdl_window_PopupHostState$stableprop[0]
final val com.compose.sdl/LocalComposeNativeWindow // com.compose.sdl/LocalComposeNativeWindow|{}LocalComposeNativeWindow[0]
final fun <get-LocalComposeNativeWindow>(): androidx.compose.runtime/ProvidableCompositionLocal<com.compose.sdl/ComposeNativeWindow> // com.compose.sdl/LocalComposeNativeWindow.<get-LocalComposeNativeWindow>|<get-LocalComposeNativeWindow>(){}[0]
@@ -4890,11 +4861,11 @@ final val com.compose.sdl/com_compose_sdl_AppEvent_WindowResized$stableprop // c
final val com.compose.sdl/com_compose_sdl_ComposeNativeWindow$stableprop // com.compose.sdl/com_compose_sdl_ComposeNativeWindow$stableprop|#static{}com_compose_sdl_ComposeNativeWindow$stableprop[0]
final val com.compose.sdl/com_compose_sdl_GpuMode$stableprop // com.compose.sdl/com_compose_sdl_GpuMode$stableprop|#static{}com_compose_sdl_GpuMode$stableprop[0]
final val com.compose.sdl/com_compose_sdl_GpuMode_Auto$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_Auto$stableprop|#static{}com_compose_sdl_GpuMode_Auto$stableprop[0]
final val com.compose.sdl/com_compose_sdl_GpuMode_Skia$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_Skia$stableprop|#static{}com_compose_sdl_GpuMode_Skia$stableprop[0]
final val com.compose.sdl/com_compose_sdl_GpuMode_Skia_Metal$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_Skia_Metal$stableprop|#static{}com_compose_sdl_GpuMode_Skia_Metal$stableprop[0]
final val com.compose.sdl/com_compose_sdl_GpuMode_Skia_OpenGL$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_Skia_OpenGL$stableprop|#static{}com_compose_sdl_GpuMode_Skia_OpenGL$stableprop[0]
final val com.compose.sdl/com_compose_sdl_GpuMode_Software$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_Software$stableprop|#static{}com_compose_sdl_GpuMode_Software$stableprop[0]
final val com.compose.sdl/com_compose_sdl_SDL3Backend$stableprop // com.compose.sdl/com_compose_sdl_SDL3Backend$stableprop|#static{}com_compose_sdl_SDL3Backend$stableprop[0]
final val com.compose.sdl/com_compose_sdl_GpuMode_CpuRaster$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_CpuRaster$stableprop|#static{}com_compose_sdl_GpuMode_CpuRaster$stableprop[0]
final val com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop|#static{}com_compose_sdl_GpuMode_Metal$stableprop[0]
final val com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop // com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop|#static{}com_compose_sdl_GpuMode_OpenGL$stableprop[0]
final val com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop // com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop|#static{}com_compose_sdl_RawSdlHandles$stableprop[0]
final val com.compose.sdl/com_compose_sdl_Sdl3Backend$stableprop // com.compose.sdl/com_compose_sdl_Sdl3Backend$stableprop|#static{}com_compose_sdl_Sdl3Backend$stableprop[0]
final val com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop // com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop|#static{}com_compose_sdl_TextLayoutConfig$stableprop[0]
final var androidx.compose.ui.platform/isDebugInspectorInfoEnabled // androidx.compose.ui.platform/isDebugInspectorInfoEnabled|{}isDebugInspectorInfoEnabled[0]
@@ -5389,16 +5360,10 @@ final fun androidx.compose.ui/androidx_compose_ui_CombinedModifier$stableprop_ge
final fun androidx.compose.ui/androidx_compose_ui_ComposeUiFlags$stableprop_getter(): kotlin/Int // androidx.compose.ui/androidx_compose_ui_ComposeUiFlags$stableprop_getter|androidx_compose_ui_ComposeUiFlags$stableprop_getter(){}[0]
final fun androidx.compose.ui/androidx_compose_ui_Modifier_Node$stableprop_getter(): kotlin/Int // androidx.compose.ui/androidx_compose_ui_Modifier_Node$stableprop_getter|androidx_compose_ui_Modifier_Node$stableprop_getter(){}[0]
final fun androidx.compose.ui/isUiSystemInDarkTheme(androidx.compose.runtime/Composer?, kotlin/Int): kotlin/Boolean // androidx.compose.ui/isUiSystemInDarkTheme|isUiSystemInDarkTheme(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun com.compose.sdl.input/com_compose_sdl_input_LegacyPointerEvent$stableprop_getter(): kotlin/Int // com.compose.sdl.input/com_compose_sdl_input_LegacyPointerEvent$stableprop_getter|com_compose_sdl_input_LegacyPointerEvent$stableprop_getter(){}[0]
final fun com.compose.sdl.node/com_compose_sdl_node_ComposeRootHost$stableprop_getter(): kotlin/Int // com.compose.sdl.node/com_compose_sdl_node_ComposeRootHost$stableprop_getter|com_compose_sdl_node_ComposeRootHost$stableprop_getter(){}[0]
final fun com.compose.sdl.platform/com_compose_sdl_platform_DefaultViewConfiguration$stableprop_getter(): kotlin/Int // com.compose.sdl.platform/com_compose_sdl_platform_DefaultViewConfiguration$stableprop_getter|com_compose_sdl_platform_DefaultViewConfiguration$stableprop_getter(){}[0]
final fun com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaSurfaceBridge$stableprop_getter(): kotlin/Int // com.compose.sdl.renderer.skia/com_compose_sdl_renderer_skia_SkiaSurfaceBridge$stableprop_getter|com_compose_sdl_renderer_skia_SkiaSurfaceBridge$stableprop_getter(){}[0]
final fun com.compose.sdl.text.input/com_compose_sdl_text_input_ImeBridge$stableprop_getter(): kotlin/Int // com.compose.sdl.text.input/com_compose_sdl_text_input_ImeBridge$stableprop_getter|com_compose_sdl_text_input_ImeBridge$stableprop_getter(){}[0]
final fun com.compose.sdl.text/registerGenericFonts() // com.compose.sdl.text/registerGenericFonts|registerGenericFonts(){}[0]
final fun com.compose.sdl.window/PopupLayer(com.compose.sdl.window/PopupHostState, androidx.compose.runtime/Composer?, kotlin/Int) // com.compose.sdl.window/PopupLayer|PopupLayer(com.compose.sdl.window.PopupHostState;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun com.compose.sdl.window/PopupOutsideDismiss(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Function0<kotlin/Unit>, androidx.compose.runtime/Composer?, kotlin/Int) // com.compose.sdl.window/PopupOutsideDismiss|PopupOutsideDismiss(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Function0<kotlin.Unit>;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun com.compose.sdl.window/PositionedPopup(kotlin/Int, kotlin/Int, kotlin/Function0<kotlin/Unit>, kotlin/Function2<androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>, androidx.compose.runtime/Composer?, kotlin/Int) // com.compose.sdl.window/PositionedPopup|PositionedPopup(kotlin.Int;kotlin.Int;kotlin.Function0<kotlin.Unit>;kotlin.Function2<androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>;androidx.compose.runtime.Composer?;kotlin.Int){}[0]
final fun com.compose.sdl.window/com_compose_sdl_window_PopupExitHandle$stableprop_getter(): kotlin/Int // com.compose.sdl.window/com_compose_sdl_window_PopupExitHandle$stableprop_getter|com_compose_sdl_window_PopupExitHandle$stableprop_getter(){}[0]
final fun com.compose.sdl.window/com_compose_sdl_window_PopupHostState$stableprop_getter(): kotlin/Int // com.compose.sdl.window/com_compose_sdl_window_PopupHostState$stableprop_getter|com_compose_sdl_window_PopupHostState$stableprop_getter(){}[0]
final fun com.compose.sdl.window/createPopupHostState(): com.compose.sdl.window/PopupHostState // com.compose.sdl.window/createPopupHostState|createPopupHostState(){}[0]
final fun com.compose.sdl/appDataDir(kotlin/String, kotlin/String): kotlin/String? // com.compose.sdl/appDataDir|appDataDir(kotlin.String;kotlin.String){}[0]
@@ -5420,13 +5385,13 @@ final fun com.compose.sdl/com_compose_sdl_AppEvent_WindowResized$stableprop_gett
final fun com.compose.sdl/com_compose_sdl_ComposeNativeWindow$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_ComposeNativeWindow$stableprop_getter|com_compose_sdl_ComposeNativeWindow$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_GpuMode$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode$stableprop_getter|com_compose_sdl_GpuMode$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_GpuMode_Auto$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_Auto$stableprop_getter|com_compose_sdl_GpuMode_Auto$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_GpuMode_Skia$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_Skia$stableprop_getter|com_compose_sdl_GpuMode_Skia$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_GpuMode_Skia_Metal$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_Skia_Metal$stableprop_getter|com_compose_sdl_GpuMode_Skia_Metal$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_GpuMode_Skia_OpenGL$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_Skia_OpenGL$stableprop_getter|com_compose_sdl_GpuMode_Skia_OpenGL$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_GpuMode_Software$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_Software$stableprop_getter|com_compose_sdl_GpuMode_Software$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_SDL3Backend$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_SDL3Backend$stableprop_getter|com_compose_sdl_SDL3Backend$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_GpuMode_CpuRaster$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_CpuRaster$stableprop_getter|com_compose_sdl_GpuMode_CpuRaster$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_Metal$stableprop_getter|com_compose_sdl_GpuMode_Metal$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_GpuMode_OpenGL$stableprop_getter|com_compose_sdl_GpuMode_OpenGL$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_RawSdlHandles$stableprop_getter|com_compose_sdl_RawSdlHandles$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_Sdl3Backend$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_Sdl3Backend$stableprop_getter|com_compose_sdl_Sdl3Backend$stableprop_getter(){}[0]
final fun com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop_getter(): kotlin/Int // com.compose.sdl/com_compose_sdl_TextLayoutConfig$stableprop_getter|com_compose_sdl_TextLayoutConfig$stableprop_getter(){}[0]
final fun com.compose.sdl/createRenderBackend(com.compose.sdl/SDL3Backend, com.compose.sdl/GpuMode): com.compose.sdl/RenderBackend? // com.compose.sdl/createRenderBackend|createRenderBackend(com.compose.sdl.SDL3Backend;com.compose.sdl.GpuMode){}[0]
final fun com.compose.sdl/createRenderBackend(com.compose.sdl/Sdl3Backend, com.compose.sdl/GpuMode): com.compose.sdl/RenderBackend? // com.compose.sdl/createRenderBackend|createRenderBackend(com.compose.sdl.Sdl3Backend;com.compose.sdl.GpuMode){}[0]
final fun com.compose.sdl/fileManagerName(): kotlin/String // com.compose.sdl/fileManagerName|fileManagerName(){}[0]
final fun com.compose.sdl/hasComposeResource(kotlin/String): kotlin/Boolean // com.compose.sdl/hasComposeResource|hasComposeResource(kotlin.String){}[0]
final fun com.compose.sdl/injectKey(kotlin/Int, kotlin/Boolean) // com.compose.sdl/injectKey|injectKey(kotlin.Int;kotlin.Boolean){}[0]
@@ -10,7 +10,7 @@
* Line-for-line kept in sync; the entry in compose/ui/compose-fork.txt is
* commented out so this file survives `scripts/compose-fork/sync.sh`.
*/
// VENDOR-BASE: compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Synchronization.kt @ v1.12.0
// VENDOR-BASE: compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/platform/Synchronization.kt @ v1.12.1
package androidx.compose.ui.platform
@@ -14,7 +14,7 @@ import androidx.compose.ui.unit.DpSize
* StubOwner). Constant values match the upstream `PlatformContext.DefaultViewConfiguration`
* (longPressTimeout=500ms, doubleTapTimeout=300ms, doubleTapMinTime=40ms, touchSlop=8f).
*/
object DefaultViewConfiguration : ViewConfiguration {
internal object DefaultViewConfiguration : ViewConfiguration {
override val longPressTimeoutMillis: Long = 500
override val doubleTapTimeoutMillis: Long = 300
override val doubleTapMinTimeMillis: Long = 40
@@ -98,23 +98,23 @@ class PopupHostState internal constructor() {
fun contains(inX: Int, inY: Int) = inX >= x && inX < x + w && inY >= y && inY < y + h
}
private val fDismissers = mutableListOf<Dismisser>()
private val mDismissers = mutableListOf<Dismisser>()
internal fun setDismisser(inId: Any, inX: Int, inY: Int, inW: Int, inH: Int, inOnDismiss: () -> Unit) {
val vD = fDismissers.firstOrNull { it.id === inId } ?: Dismisser(inId).also { fDismissers.add(it) }
val vD = mDismissers.firstOrNull { it.id === inId } ?: Dismisser(inId).also { mDismissers.add(it) }
vD.x = inX; vD.y = inY; vD.w = inW; vD.h = inH; vD.onDismiss = inOnDismiss
}
internal fun removeDismisser(inId: Any) {
fDismissers.removeAll { it.id === inId }
mDismissers.removeAll { it.id === inId }
}
/** Dismiss every registered popup whose content rect does NOT contain the
press. Called from the window's press dispatch; never consumes the press. */
fun notifyOutsidePress(inX: Int, inY: Int) {
if (fDismissers.isEmpty()) return
if (mDismissers.isEmpty()) return
// Copy: an onDismiss typically removes the dismisser (state write).
for (vD in fDismissers.toList()) {
for (vD in mDismissers.toList()) {
if (vD.w > 0 && vD.h > 0 && !vD.contains(inX, inY)) vD.onDismiss()
}
}
@@ -139,26 +139,26 @@ fun createPopupHostState(): PopupHostState = PopupHostState()
the entry. The hosted content (still composed - it lives in the HOST's
composition, not the owner's) observes that, plays its animation, and MUST
end with [finish], which actually removes the entry. */
class PopupExitHandle internal constructor(
private val fHost: PopupHostState, // owning host - target of finish()
private val fEntry: PopupHostState.Entry, // the entry this handle controls
internal class PopupExitHandle internal constructor(
private val mHost: PopupHostState, // owning host - target of finish()
private val mEntry: PopupHostState.Entry, // the entry this handle controls
) {
/** True once the owning Popup left the composition and the host is waiting
for this entry's exit animation. Observable - recomposes the content. */
val isExiting: State<Boolean> get() = fEntry.exiting
val isExiting: State<Boolean> get() = mEntry.exiting
/** Opt in to exit deferral. Idempotent; call from a SideEffect. */
fun enableExitTransition() { fEntry.hasExitTransition = true }
fun enableExitTransition() { mEntry.hasExitTransition = true }
/** Ends the deferral - the entry is removed from the host for real. */
fun finish() { fHost.forceRemove(fEntry.id) }
fun finish() { mHost.forceRemove(mEntry.id) }
}
/** Per-entry handle, provided by PopupLayer around each hosted content. Null
outside a popup layer. NB: safe to expose through the caller-locals rewrap
in Popup (CompositionLocalProvider(callerContext)) - the caller never
provides this local, so the layer's per-entry value stays visible. */
val LocalPopupExitHandle = staticCompositionLocalOf<PopupExitHandle?> { null }
internal val LocalPopupExitHandle = staticCompositionLocalOf<PopupExitHandle?> { null }
// ==================
// MARK: PopupLayer
@@ -187,7 +187,7 @@ fun PopupLayer(inHost: PopupHostState) {
popup host so the dismissing press is NOT consumed (it still reaches whatever
is under it - no dead first click). */
@Composable
fun PopupOutsideDismiss(inX: Int, inY: Int, inW: Int, inH: Int, onDismissRequest: () -> Unit) {
internal fun PopupOutsideDismiss(inX: Int, inY: Int, inW: Int, inH: Int, onDismissRequest: () -> Unit) {
val vHost = LocalPopupHost.current
val vId = remember { Any() }
SideEffect { vHost.setDismisser(vId, inX, inY, inW, inH, onDismissRequest) }
@@ -203,7 +203,7 @@ fun PopupOutsideDismiss(inX: Int, inY: Int, inW: Int, inH: Int, onDismissRequest
`x`/`y` are LAYOUT PIXELS (matches `LayoutCoordinates.positionInRoot`), not
`Dp` - layout runs in physical pixels under the Option-B density flow. */
@Composable
fun PositionedPopup(
internal fun PositionedPopup(
x: Int,
y: Int,
onDismissRequest: () -> Unit,
@@ -8,7 +8,7 @@ import androidx.compose.ui.geometry.Offset
/* Actuals for the vendored `DragAndDrop.kt` expect classes.
* Backed by SDL3's SDL_EVENT_DROP_* stream (file / text / begin / position /
* complete) - see Sdl3DragAndDropOwner + SDL3EventMapper for the pump side.
* complete) - see Sdl3DragAndDropOwner + AppEvent.kt for the pump side.
*
* DragAndDropTransferData carries the payload a drop into the window
* delivered: zero or more file paths (SDL_EVENT_DROP_FILE) plus an optional
@@ -10,7 +10,7 @@ import androidx.compose.ui.node.ModifierNodeElement
/** The per-window DragAndDropManager backing ComposeOwner. Holds a root
* DragAndDropNode installed on the root LayoutNode via [modifier]; the SDL
* event pump (see SDL3EventMapper + ComposeWindow) drives [handleDrop] which
* event pump (see AppEvent.kt + ComposeWindow) drives [handleDrop] which
* accumulates BEGIN → FILE / TEXT / POSITION → COMPLETE into a single
* DragAndDropEvent and dispatches through the node tree.
*
@@ -8,8 +8,8 @@ package androidx.compose.ui.input.key
/**
* Project [Key] actual - assigns a unique sequential Long to each named
* key. SDL3EventMapper picks the right `Key.X` by switch on SDL scancode
* (`kKeyForScancode` in `SDL3EventMapper.kt`). Comparison works because
* key. The SDL event mapper picks the right `Key.X` by switch on SDL scancode
* (`kKeyForScancode` in `AppEvent.kt`). Comparison works because
* Key is a value class - `Key(42L) == Key(42L)`.
*/
@kotlin.jvm.JvmInline
@@ -16,7 +16,7 @@
// MANUALLY VENDORED (NON-IDEMPOTENT) from compose-multiplatform-core:
// compose/ui/ui/src/skikoMain/.../node/GraphicsLayerOwnerLayer.skiko.kt
// VENDOR-BASE: compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/node/GraphicsLayerOwnerLayer.skiko.kt @ v1.12.0
// VENDOR-BASE: compose/ui/ui/src/skikoMain/kotlin/androidx/compose/ui/node/GraphicsLayerOwnerLayer.skiko.kt @ v1.12.1
// Change vs upstream: dropped the trailing `SkiaGraphicsContext.setLightingInfo`
// extension + LIGHT_* constants (skiko-only shadow lighting; our shadows go via
// NativeShadowCanvas). The class body is unchanged - it is the real OwnedLayer
@@ -103,14 +103,14 @@ private const val kImageMime = "image/png"
// call with an image). Held on nativeHeap because SDL keeps the callback alive
// until the clipboard is cleared or another SDL_SetClipboardData replaces us -
// a Kotlin heap reference would break as soon as the caller's coroutine
// returned. Read by [fClipboardDataCallback], freed by [fClipboardCleanupCallback].
private var fOutgoingImagePtr: CPointer<ByteVar>? = null
private var fOutgoingImageSize: Int = 0
// returned. Read by [mClipboardDataCallback], freed by [mClipboardCleanupCallback].
private var mOutgoingImagePtr: CPointer<ByteVar>? = null
private var mOutgoingImageSize: Int = 0
// SDL fires this on the main event thread when another app asks the clipboard
// for a MIME we advertised. We serve back the pointer + size we stashed at
// SDL_SetClipboardData time. Return NULL if the MIME doesn't match.
private val fClipboardDataCallback = staticCFunction {
private val mClipboardDataCallback = staticCFunction {
userdata: COpaquePointer?,
mimeType: CPointer<ByteVar>?,
sizePtr: CPointer<platform.posix.size_tVar>?,
@@ -118,20 +118,20 @@ private val fClipboardDataCallback = staticCFunction {
userdata.hashCode() // unused
val vRequested = mimeType?.toKString() ?: return@staticCFunction null
if (vRequested != kImageMime) return@staticCFunction null
val vPtr = fOutgoingImagePtr ?: return@staticCFunction null
sizePtr?.pointed?.value = fOutgoingImageSize.toULong()
val vPtr = mOutgoingImagePtr ?: return@staticCFunction null
sizePtr?.pointed?.value = mOutgoingImageSize.toULong()
vPtr as COpaquePointer
}
// SDL calls this when the clipboard we set is cleared or overwritten (by us
// or another app). Frees the nativeHeap allocation.
private val fClipboardCleanupCallback = staticCFunction {
private val mClipboardCleanupCallback = staticCFunction {
userdata: COpaquePointer?,
->
userdata.hashCode() // unused
fOutgoingImagePtr?.let { nativeHeap.free(it.rawValue) }
fOutgoingImagePtr = null
fOutgoingImageSize = 0
mOutgoingImagePtr?.let { nativeHeap.free(it.rawValue) }
mOutgoingImagePtr = null
mOutgoingImageSize = 0
Unit
}
@@ -172,19 +172,19 @@ private fun sdlReadImage(): ByteArray? = memScoped {
private fun sdlWriteImage(bytes: ByteArray) {
// Free any previously-held buffer proactively (the cleanup callback also
// fires from SDL, but doing it eagerly here handles same-thread re-set).
fOutgoingImagePtr?.let { nativeHeap.free(it.rawValue) }
mOutgoingImagePtr?.let { nativeHeap.free(it.rawValue) }
val vPtr = nativeHeap.allocArray<ByteVar>(bytes.size)
for (i in bytes.indices) vPtr[i] = bytes[i]
fOutgoingImagePtr = vPtr
fOutgoingImageSize = bytes.size
mOutgoingImagePtr = vPtr
mOutgoingImageSize = bytes.size
memScoped {
val vMimeArray = allocArray<CPointerVar<ByteVar>>(1)
vMimeArray[0] = kImageMime.cstr.ptr
SDL_SetClipboardData(
fClipboardDataCallback,
fClipboardCleanupCallback,
mClipboardDataCallback,
mClipboardCleanupCallback,
null,
vMimeArray,
1.toULong(),
@@ -213,10 +213,10 @@ private fun sdlWriteEntry(entry: ClipEntry?) {
// The SDL3-backed Clipboard implementation. Singleton because SDL's clipboard
// is process-global - allocating a new one per Window() would only waste
// memory. Consumed by :desktop-native-window when seeding LocalClipboard.
private object SDL3Clipboard : Clipboard {
private object Sdl3Clipboard : Clipboard {
override suspend fun getClipEntry(): ClipEntry? = sdlReadEntry()
override suspend fun setClipEntry(clipEntry: ClipEntry?) { sdlWriteEntry(clipEntry) }
override val nativeClipboard: NativeClipboard get() = nativeClipboardSentinel
}
fun platformClipboard(): Clipboard = SDL3Clipboard
fun platformClipboard(): Clipboard = Sdl3Clipboard
@@ -18,12 +18,12 @@ import androidx.compose.ui.unit.IntRect
* no-ops.
*/
private class StubSemanticsRegion : SemanticsRegion {
private var fBounds: IntRect = IntRect.Zero
override fun set(rect: IntRect) { fBounds = rect }
private var mBounds: IntRect = IntRect.Zero
override fun set(rect: IntRect) { mBounds = rect }
override fun intersect(region: SemanticsRegion): Boolean = false
override fun difference(rect: IntRect): Boolean = false
override val bounds: IntRect get() = fBounds
override val isEmpty: Boolean get() = fBounds == IntRect.Zero
override val bounds: IntRect get() = mBounds
override val isEmpty: Boolean get() = mBounds == IntRect.Zero
}
internal actual fun SemanticsRegion(): SemanticsRegion = StubSemanticsRegion()
@@ -5,19 +5,27 @@ import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.pointer.PointerButton
import androidx.compose.ui.input.pointer.PointerEventType
import com.compose.sdl.input.LegacyPointerEvent
import kotlinx.cinterop.*
import sdl3.*
// ==================
// MARK: SDL3EventMapper
// MARK: AppEvent + the SDL3 event mapper
// ==================
/** Events carry the SDL window id they belong to (0 = unknown - injected test
events; the loop routes those to its first window). Quit is app-level. */
sealed class AppEvent {
data object Quit : AppEvent()
data class Pointer(val event: LegacyPointerEvent, val windowId: UInt = 0u) : AppEvent()
/** A single-pointer SDL mouse event. Flat like its MouseWheel sibling: the
fields used to sit behind a `LegacyPointerEvent` wrapper that the mapper
built and the window loop immediately took apart again. */
data class Pointer(
val x: Float,
val y: Float,
val type: PointerEventType,
val button: PointerButton = PointerButton.Primary,
val windowId: UInt = 0u,
) : AppEvent()
data class Key(val event: KeyEvent, val windowId: UInt = 0u) : AppEvent()
data class TextInput(val text: String, val windowId: UInt = 0u) : AppEvent()
/** IME preedit / composition (SDL_EVENT_TEXT_EDITING). Empty text = composition
@@ -75,34 +83,36 @@ fun pollEvents(): List<AppEvent> {
}
private fun mapEvent(e: SDL_Event): AppEvent? {
val type = e.type
return when (type) {
return when (e.type) {
SDL_EVENT_QUIT -> AppEvent.Quit
SDL_EVENT_MOUSE_BUTTON_DOWN -> {
val mb = e.button
AppEvent.Pointer(LegacyPointerEvent(
AppEvent.Pointer(
x = mb.x, y = mb.y,
type = PointerEventType.Press,
button = mapButton(mb.button)
), mb.windowID)
button = mapButton(mb.button),
windowId = mb.windowID,
)
}
SDL_EVENT_MOUSE_BUTTON_UP -> {
val mb = e.button
AppEvent.Pointer(LegacyPointerEvent(
AppEvent.Pointer(
x = mb.x, y = mb.y,
type = PointerEventType.Release,
button = mapButton(mb.button)
), mb.windowID)
button = mapButton(mb.button),
windowId = mb.windowID,
)
}
SDL_EVENT_MOUSE_MOTION -> {
val mm = e.motion
AppEvent.Pointer(LegacyPointerEvent(
AppEvent.Pointer(
x = mm.x, y = mm.y,
type = PointerEventType.Move
), mm.windowID)
type = PointerEventType.Move,
windowId = mm.windowID,
)
}
SDL_EVENT_KEY_DOWN -> mapKey(e.key, KeyEventType.KeyDown)
@@ -5,14 +5,18 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.input.key.KeyEvent
import kotlinx.cinterop.COpaquePointer
import kotlinx.cinterop.reinterpret
import kotlinx.cinterop.toKString
import sdl3.SDL_GetRendererName
import sdl3.SDL_MaximizeWindow
import sdl3.SDL_MinimizeWindow
import sdl3.SDL_RaiseWindow
import sdl3.SDL_RestoreWindow
import sdl3.SDL_SetWindowAlwaysOnTop
import sdl3.SDL_SetWindowBordered
import sdl3.SDL_SetWindowFocusable
import sdl3.SDL_SetWindowFullscreen
import sdl3.SDL_SetWindowOpacity
import sdl3.SDL_SetWindowResizable
import sdl3.SDL_SetWindowSize
import sdl3.SDL_SetWindowTitle
@@ -30,46 +34,46 @@ import sdl3.SDL_SetWindowTitle
them subscribes the caller to recomposition. The setter methods
call into SDL and also push the new value into the snapshot. */
class ComposeNativeWindow constructor(
private val backend: SDL3Backend,
private val backend: Sdl3Backend,
val gpuMode: GpuMode,
initialTitle: String,
) {
private var fWidth by mutableStateOf(backend.windowWidth)
private var fHeight by mutableStateOf(backend.windowHeight)
private var fPixelWidth by mutableStateOf(backend.pixelWidth)
private var fPixelHeight by mutableStateOf(backend.pixelHeight)
private var fTitle by mutableStateOf(initialTitle)
private var fMinimized by mutableStateOf(false)
private var fMaximized by mutableStateOf(false)
private var fFullscreen by mutableStateOf(false)
private var fCloseRequested by mutableStateOf(false)
private var fFps by mutableStateOf(0)
private var mWidth by mutableStateOf(backend.windowWidth)
private var mHeight by mutableStateOf(backend.windowHeight)
private var mPixelWidth by mutableStateOf(backend.pixelWidth)
private var mPixelHeight by mutableStateOf(backend.pixelHeight)
private var mTitle by mutableStateOf(initialTitle)
private var mMinimized by mutableStateOf(false)
private var mMaximized by mutableStateOf(false)
private var mFullscreen by mutableStateOf(false)
private var mCloseRequested by mutableStateOf(false)
private var mFps by mutableStateOf(0)
// ============
// State
/** Logical (point) width - the same units layout uses. */
val width: Int get() = fWidth
val height: Int get() = fHeight
val width: Int get() = mWidth
val height: Int get() = mHeight
/** Physical (pixel) back-buffer size; on Retina this is `width * pixelDensity`. */
val pixelWidth: Int get() = fPixelWidth
val pixelHeight: Int get() = fPixelHeight
val title: String get() = fTitle
val isMinimized: Boolean get() = fMinimized
val isMaximized: Boolean get() = fMaximized
val isFullscreen: Boolean get() = fFullscreen
val pixelWidth: Int get() = mPixelWidth
val pixelHeight: Int get() = mPixelHeight
val title: String get() = mTitle
val isMinimized: Boolean get() = mMinimized
val isMaximized: Boolean get() = mMaximized
val isFullscreen: Boolean get() = mFullscreen
val pixelDensity: Float get() = backend.pixelDensity
/** Frames per second, refreshed ~once a second by the render loop. Snapshot-
backed, so reading it in a composable recomposes when it changes. */
val fps: Int get() = fFps
val fps: Int get() = mFps
/** Human-readable name of the rendering pipeline:
"Skia / Metal", "Skia / OpenGL", "Skia / CPU raster". */
val rendererName: String
get() = when (gpuMode) {
is GpuMode.Skia.Metal -> "Skia / Metal"
is GpuMode.Skia.OpenGL -> "Skia / OpenGL"
is GpuMode.Software -> "Skia / CPU raster"
is GpuMode.Metal -> "Skia / Metal"
is GpuMode.OpenGL -> "Skia / OpenGL"
is GpuMode.CpuRaster -> "Skia / CPU raster"
is GpuMode.Auto -> "Auto (unresolved)"
}
@@ -79,7 +83,7 @@ class ComposeNativeWindow constructor(
fun setTitle(inTitle: String) {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_SetWindowTitle(vWindow, inTitle)
fTitle = inTitle
mTitle = inTitle
}
/** Logical size in points - SDL fires AppEvent.WindowResized which
@@ -91,56 +95,111 @@ class ComposeNativeWindow constructor(
fun minimize() {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_MinimizeWindow(vWindow); fMinimized = true
SDL_MinimizeWindow(vWindow); mMinimized = true
}
fun maximize() {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_MaximizeWindow(vWindow); fMaximized = true; fMinimized = false
SDL_MaximizeWindow(vWindow); mMaximized = true; mMinimized = false
}
fun restore() {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_RestoreWindow(vWindow); fMaximized = false; fMinimized = false
SDL_RestoreWindow(vWindow); mMaximized = false; mMinimized = false
}
fun setFullscreen(inFullscreen: Boolean) {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_SetWindowFullscreen(vWindow, inFullscreen); fFullscreen = inFullscreen
SDL_SetWindowFullscreen(vWindow, inFullscreen); mFullscreen = inFullscreen
}
fun toggleFullscreen() = setFullscreen(!fFullscreen)
fun toggleFullscreen() = setFullscreen(!mFullscreen)
fun raise() {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_RaiseWindow(vWindow)
}
// ============
// Mutable window attributes (Compose Desktop's Window() parameters)
//
// Window() re-applies these from the composition whenever they change, so
// app code normally passes them as parameters rather than calling these.
// `transparent` is absent on purpose: SDL needs SDL_WINDOW_TRANSPARENT at
// creation and offers no setter, so it is a construction-time attribute.
private var mUndecorated = false
private var mResizable = true
private var mAlwaysOnTop = false
private var mFocusable = true
private var mEnabled = true
val isUndecorated: Boolean get() = mUndecorated
val isResizable: Boolean get() = mResizable
val isAlwaysOnTop: Boolean get() = mAlwaysOnTop
val isFocusable: Boolean get() = mFocusable
/** False hides the window's title bar and border. */
fun setUndecorated(inUndecorated: Boolean) {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_SetWindowBordered(vWindow, !inUndecorated); mUndecorated = inUndecorated
}
fun setResizable(inResizable: Boolean) {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_SetWindowResizable(vWindow, inResizable); mResizable = inResizable
}
fun setAlwaysOnTop(inAlwaysOnTop: Boolean) {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_SetWindowAlwaysOnTop(vWindow, inAlwaysOnTop); mAlwaysOnTop = inAlwaysOnTop
}
/** False stops the window taking keyboard focus when clicked or raised. */
fun setFocusable(inFocusable: Boolean) {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_SetWindowFocusable(vWindow, inFocusable); mFocusable = inFocusable
}
/** Window opacity, 0f (fully transparent) to 1f. Needs a compositing window
manager; SDL reports failure on platforms without one. */
fun setOpacity(inOpacity: Float) {
val vWindow = backend.window?.reinterpret<cnames.structs.SDL_Window>() ?: return
SDL_SetWindowOpacity(vWindow, inOpacity.coerceIn(0f, 1f))
}
/** True while the window accepts input. A disabled window still renders and
repaints; it just drops pointer / keyboard / text / wheel / drop events,
which is how Compose Desktop's `enabled = false` behaves. */
val isEnabled: Boolean get() = mEnabled
fun setEnabled(inEnabled: Boolean) { mEnabled = inEnabled }
/** Asks composeWindow's main loop to break out at the next frame.
Same effect as the user closing the window via the OS. Bypasses any
onCloseRequest handler - this is the "really quit now" path. */
fun close() { fCloseRequested = true }
fun close() { mCloseRequested = true }
// ============
// Close interception
private var fOnCloseRequest: (() -> Boolean)? = null
private var mOnCloseRequest: (() -> Boolean)? = null
/** Register a handler invoked when the user tries to close the window (OS
close button / Quit). Return true to let the close proceed, false to veto
it (e.g. to show an "unsaved changes" dialog first, then call close()
once the user confirms). Pass null to clear. */
fun setOnCloseRequest(inHandler: (() -> Boolean)?) { fOnCloseRequest = inHandler }
fun setOnCloseRequest(inHandler: (() -> Boolean)?) { mOnCloseRequest = inHandler }
// ============
// Global key shortcuts
private var fOnKeyShortcut: ((KeyEvent) -> Boolean)? = null
private var mOnKeyShortcut: ((KeyEvent) -> Boolean)? = null
/** Register a handler for key events the focused node didn't consume - for
app-wide shortcuts (Ctrl+S, etc.). Receives every unconsumed key (and all
keys when nothing is focused). Return true if handled. Pass null to clear. */
fun setOnKeyShortcut(inHandler: ((KeyEvent) -> Boolean)?) { fOnKeyShortcut = inHandler }
fun setOnKeyShortcut(inHandler: ((KeyEvent) -> Boolean)?) { mOnKeyShortcut = inHandler }
// ============
// Framework hooks - driven by composeWindow's main loop (which lives in
@@ -148,26 +207,71 @@ class ComposeNativeWindow constructor(
// intended for app code.
fun onResized() {
fWidth = backend.windowWidth
fHeight = backend.windowHeight
fPixelWidth = backend.pixelWidth
fPixelHeight = backend.pixelHeight
mWidth = backend.windowWidth
mHeight = backend.windowHeight
mPixelWidth = backend.pixelWidth
mPixelHeight = backend.pixelHeight
}
/** Called by the main loop ~once a second with the measured frame rate. */
fun updateFps(inFps: Int) { fFps = inFps }
fun updateFps(inFps: Int) { mFps = inFps }
val isCloseRequested: Boolean get() = fCloseRequested
val isCloseRequested: Boolean get() = mCloseRequested
/** Driven by composeWindow's main loop on an OS close / Quit event. Returns
true if the close should proceed (no handler, or the handler allowed it). */
fun requestCloseFromUser(): Boolean = fOnCloseRequest?.invoke() ?: true
fun requestCloseFromUser(): Boolean = mOnCloseRequest?.invoke() ?: true
/** Driven by composeWindow's main loop for keys the focused node didn't
consume. Returns true if the shortcut handler handled it. */
fun dispatchKeyShortcut(inEvent: KeyEvent): Boolean = fOnKeyShortcut?.invoke(inEvent) ?: false
fun dispatchKeyShortcut(inEvent: KeyEvent): Boolean = mOnKeyShortcut?.invoke(inEvent) ?: false
// ============
// Raw SDL escape hatch
/**
The live SDL handles behind this window, for calling the `sdl3.*` cinterop
directly when the Compose-level API doesn't cover something.
Returns a snapshot: the pointers are only valid while the window is alive,
so re-read it rather than caching, and never use it after the window closes.
Whatever you do through it is outside the port's control - resizing,
destroying or re-creating the window or its renderer behind Compose's back
will desync the renderer state.
*/
fun rawSdlHandles(): RawSdlHandles = RawSdlHandles(
window = backend.window,
renderer = backend.renderer,
glContext = backend.glContext,
metalView = backend.metalView,
)
}
/**
Opaque SDL pointers for one window, as handed out by
[ComposeNativeWindow.rawSdlHandles].
`reinterpret()` each to the `cnames.structs.*` type the SDL function expects:
val vHandles = window.rawSdlHandles()
vHandles.window?.let { sdl3.SDL_FlashWindow(it.reinterpret(), FLASH_BRIEFLY) }
Which pointers are non-null depends on the resolved [GpuMode]: `renderer` is
set only for the CPU-raster (Software) backend, `glContext` only for Skia
OpenGL, and `metalView` only for Skia Metal. `window` is always set on a
successfully created window.
*/
data class RawSdlHandles(
/** `SDL_Window*` - always present while the window lives. */
val window: COpaquePointer?,
/** `SDL_Renderer*` - CPU-raster (Software) backend only. */
val renderer: COpaquePointer?,
/** `SDL_GLContext` - Skia OpenGL backend only. */
val glContext: COpaquePointer?,
/** `SDL_MetalView` - Skia Metal backend only. */
val metalView: COpaquePointer?,
)
// ==================
// MARK: Scope + CompositionLocal
// ==================
@@ -15,7 +15,7 @@ import sdl3.SDL_ShowSaveFileDialog
The C callback can't capture Kotlin state, so the result handler is parked in
a StableRef passed as the dialog's `userdata` and disposed inside the callback. */
private val fDialogCallback = staticCFunction { inUserData: COpaquePointer?, inFileList: CPointer<CPointerVar<ByteVar>>?, inFilter: Int ->
private val mDialogCallback = staticCFunction { inUserData: COpaquePointer?, inFileList: CPointer<CPointerVar<ByteVar>>?, inFilter: Int ->
inFilter.hashCode() // unused (selected filter index)
val vRef = inUserData?.asStableRef<(String?) -> Unit>() ?: return@staticCFunction
val vHandler = vRef.get()
@@ -31,7 +31,7 @@ private val fDialogCallback = staticCFunction { inUserData: COpaquePointer?, inF
fun showSaveFileDialog(inDefaultName: String? = null, inOnResult: (String?) -> Unit) {
val vRef = StableRef.create(inOnResult)
SDL_ShowSaveFileDialog(
fDialogCallback,
mDialogCallback,
vRef.asCPointer(),
null, // parent window (null = unparented)
null, // no file-type filters
@@ -45,7 +45,7 @@ fun showSaveFileDialog(inDefaultName: String? = null, inOnResult: (String?) -> U
fun showOpenFileDialog(inOnResult: (String?) -> Unit) {
val vRef = StableRef.create(inOnResult)
SDL_ShowOpenFileDialog(
fDialogCallback,
mDialogCallback,
vRef.asCPointer(),
null, // parent window
null, // filters
@@ -4,28 +4,38 @@ package com.compose.sdl
// MARK: GpuMode
// ==================
/** Which GPU path the Skia renderer uses.
*
* ┌─ Auto - pick the best for this platform (Metal on macOS, OpenGL on
* │ Linux, OpenGL on Windows). If the GPU context/bridge can't be
* │ created, ComposeWindow falls back to Software.
* ├─ Software - Skia CPU raster: paints a host pixel buffer that SDL_Renderer
* │ uploads as a texture each frame. No GPU context needed.
* └─ Skia.* - Skia GPU bridges
* ├─ OpenGL - Skia GL backend on an SDL OpenGL (WGL/GLX) context
* └─ Metal - Skia Metal on a CAMetalLayer via SDL_Metal_CreateView (macOS)
*
* AUTO resolves via rendererPreferredGpuMode() per target, at composeWindow entry. */
/**
Which drawing path the renderer uses.
Every mode is Skia - the port renders through Skia on all targets. The axis
here is only how Skia's output reaches the screen:
Auto resolve per platform at window creation (Metal on macOS, OpenGL
on Linux and Windows). If the GPU context can't be created, the
window falls back to CpuRaster rather than failing to open.
Metal Skia's Metal backend on a CAMetalLayer (SDL_Metal_CreateView), macOS.
OpenGL Skia's GL backend on an SDL OpenGL (WGL/GLX) context.
CpuRaster Skia paints a host pixel buffer that SDL_Renderer uploads as a
texture each frame. No GPU context needed.
This used to read `Software` vs a nested `Skia.*` group, which dated from when
the alternative to Skia was SDL's own renderer. With SDL reduced to windowing
that split was actively misleading - "Software" was always Skia too.
*/
sealed class GpuMode {
/** Let the platform pick: Skia.Metal on macOS, Skia.OpenGL on Linux/Windows. */
object Auto : GpuMode() { override fun toString() = "Auto" }
/** Let the platform pick: [Metal] on macOS, [OpenGL] on Linux and Windows. */
object Auto : GpuMode() { override fun toString() = "Auto" }
/** Skia CPU raster - a host pixel buffer uploaded via SDL_Renderer each frame. */
object Software : GpuMode() { override fun toString() = "Software" }
/** Skia Metal on a CAMetalLayer via SDL_Metal_CreateView (macOS only). */
object Metal : GpuMode() { override fun toString() = "Metal" }
/** Skia GPU bridges. */
sealed class Skia : GpuMode() {
object OpenGL : Skia() { override fun toString() = "Skia.OpenGL" }
object Metal : Skia() { override fun toString() = "Skia.Metal" }
}
/** Skia GL on an SDL OpenGL context (WGL on Windows, GLX on Linux). */
object OpenGL : GpuMode() { override fun toString() = "OpenGL" }
/** Skia CPU raster - a host pixel buffer uploaded via SDL_Renderer each frame. */
object CpuRaster : GpuMode() { override fun toString() = "CpuRaster" }
/** True for the GPU-backed modes. [Auto] is false because it is unresolved:
ask after the window has resolved it, via ComposeNativeWindow.gpuMode. */
val isGpuAccelerated: Boolean get() = this is Metal || this is OpenGL
}
@@ -22,7 +22,7 @@ import sdl3.SDL_OpenURL
UI until the file manager was up. None of the three platform backends
requires the main thread, so a small background scope launches them and
(optionally) posts the result back to Main. */
private val fOpenExternalScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private val mOpenExternalScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
/** Opens a URL or file:// URI with the OS's default handler (browser, file
manager, …) via SDL_OpenURL - asynchronously, so the UI keeps pumping while
@@ -30,7 +30,7 @@ private val fOpenExternalScope = CoroutineScope(SupervisorJob() + Dispatchers.De
thread with SDL_OpenURL's success flag. */
@OptIn(ExperimentalForeignApi::class)
fun openUrl(inUrl: String, inOnResult: ((Boolean) -> Unit)? = null) {
fOpenExternalScope.launch {
mOpenExternalScope.launch {
val vOk = SDL_OpenURL(inUrl)
if (inOnResult != null) withContext(Dispatchers.Main) { inOnResult(vOk) }
}
@@ -10,6 +10,21 @@ import com.compose.sdl.res.ImageLoader
(official Skiko on macOS/Linux, the fork on mingwX64); text measurement +
drawing go through the skiko paragraph engine (androidx.compose.ui.text
Paragraph actuals), so there is no renderer-owned measurer here. */
/*
WHY THIS INTERFACE EXISTS WITH ONE IMPLEMENTATION - do not "simplify" it away.
SkiaRenderBackend is its only implementor, which reads like leftover
dual-backend scaffolding from when SDL's own renderer was the alternative. It
is not. SkiaRenderBackend lives in skikoRendererMain, BELOW nativeMain, so
nativeMain-level code cannot name it: :desktop-native-window's WindowInstance
holds the renderer in nativeMain and would not compile against the concrete
type. Verified by trying it - compileNativeMainKotlinMetadata fails with
"Unresolved reference 'renderer'".
So this interface is the nativeMain-visible boundary for an implementation
that must live lower down, exactly like the expect fun createRenderBackend
that returns it. Same reasoning, same constraint.
*/
interface RenderBackend {
/** Loader the shared ImageMeasurePolicy / Res.readBytes plug into. Backed
by the same decode cache the renderer uses to paint images, so a
@@ -30,8 +45,13 @@ interface RenderBackend {
DrawModifierNode pipeline. Taking a (Canvas)->Unit instead of the host keeps
RenderBackend + its implementations independent of the node/host layer - the
decoupling that lets the renderers live in :ui-graphics (no ui-graphics→ui
cycle). Default no-op. */
fun drawRoot(inDraw: (canvas: androidx.compose.ui.graphics.Canvas) -> Unit) {}
cycle).
ABSTRACT, not a no-op default: the empty body dates from when a second,
non-Skia backend could opt out of it, and with the Skia backend now the
only implementation an unimplemented drawRoot would silently render
nothing rather than fail to compile. */
fun drawRoot(inDraw: (canvas: androidx.compose.ui.graphics.Canvas) -> Unit)
/** Flush + present whatever was just drawn. */
fun endFrame()
@@ -16,7 +16,7 @@ package com.compose.sdl
/** Create the render backend for the selected [GpuMode]; null when the
backend can't initialise (caller falls back / reports). */
expect fun createRenderBackend(inSdl: SDL3Backend, inGpu: GpuMode): RenderBackend?
expect fun createRenderBackend(inSdl: Sdl3Backend, inGpu: GpuMode): RenderBackend?
/** The renderer module's per-OS default GpuMode (used for GpuMode.Auto). */
expect fun rendererPreferredGpuMode(): GpuMode
@@ -43,7 +43,7 @@ import sdl3.SDL_GetBasePath
// no zlib/gzip wrapper). ZIP64 is not supported - the resource set is
// small enough that the standard 4 GB / 65535-entry limits don't apply.
private class ComposeResourceArchive(private val fHandle: FileHandle) {
private class ComposeResourceArchive(private val mHandle: FileHandle) {
// One entry's location + sizing + compression method from the central
// directory. method 0 = stored, 8 = deflated.
@@ -53,18 +53,18 @@ private class ComposeResourceArchive(private val fHandle: FileHandle) {
val uncompressedSize: Long,
val method: Int,
)
private val fEntries: Map<String, Entry>
private val mEntries: Map<String, Entry>
init {
fEntries = readCentralDirectory()
mEntries = readCentralDirectory()
}
fun has(inPath: String): Boolean = fEntries.containsKey(inPath)
fun has(inPath: String): Boolean = mEntries.containsKey(inPath)
/** Reads an entry's bytes (inflated if needed) or null if the entry is
missing / uses an unsupported compression method. */
fun readBytes(inPath: String): ByteArray? {
val vEntry = fEntries[inPath] ?: return null
val vEntry = mEntries[inPath] ?: return null
if (vEntry.uncompressedSize <= 0L) return ByteArray(0)
// Local file header layout: 30 fixed bytes, then filename_len + extra_len.
@@ -126,7 +126,7 @@ private class ComposeResourceArchive(private val fHandle: FileHandle) {
// End of Central Directory record: 22 bytes minimum, with up to 65535
// bytes of comment trailing. Read the last 64 KiB + 22 and scan backward
// for the EOCD signature.
val vFileLen: Long = fHandle.size()
val vFileLen: Long = mHandle.size()
if (vFileLen < 22L) return emptyMap()
val vTailLen = minOf(vFileLen, 65557L).toInt()
val vTail = ByteArray(vTailLen)
@@ -182,14 +182,14 @@ private class ComposeResourceArchive(private val fHandle: FileHandle) {
if (outBuf.isEmpty()) return true
var vRead = 0
while (vRead < outBuf.size) {
val vN = fHandle.read(inOffset + vRead, outBuf, vRead, outBuf.size - vRead)
val vN = mHandle.read(inOffset + vRead, outBuf, vRead, outBuf.size - vRead)
if (vN <= 0) return false
vRead += vN
}
return true
}
fun close() = fHandle.close()
fun close() = mHandle.close()
private fun le16(inBuf: ByteArray, inOff: Int): Int =
(inBuf[inOff].toInt() and 0xFF) or
@@ -4,23 +4,32 @@ import kotlinx.cinterop.*
import sdl3.*
// ==================
// MARK: SDL3Backend
// MARK: Sdl3Backend
// ==================
class SDL3Backend(
class Sdl3Backend(
private val title: String = "ComposeNativeSDL3",
private val width: Int = 800,
private val height: Int = 600,
val gpuMode: GpuMode = GpuMode.Software,
val gpuMode: GpuMode = GpuMode.CpuRaster,
// Resource paths (inside data.kres) of the pre-decoded .rgba icon blobs.
// The first-listed size is irrelevant - the largest becomes the base and
// the rest become alternate resolutions. Dark set falls back to light.
private val iconLightResourcePaths: List<String> = emptyList(),
private val iconDarkResourcePaths: List<String> = emptyList(),
// Creation-time window attributes, mirroring Compose Desktop's Window().
// `undecorated`, `resizable`, `alwaysOnTop` and `focusable` can also be
// changed later (see ComposeNativeWindow); `transparent` cannot - SDL needs
// SDL_WINDOW_TRANSPARENT at creation, so it is honoured here only.
private val undecorated: Boolean = false,
private val transparent: Boolean = false,
private val resizable: Boolean = true,
private val alwaysOnTop: Boolean = false,
private val focusable: Boolean = true,
) {
init {
require(gpuMode !is GpuMode.Auto) {
"SDL3Backend received GpuMode.Auto - resolve via preferredGpuMode() first"
"Sdl3Backend received GpuMode.Auto - resolve via preferredGpuMode() first"
}
}
@@ -66,7 +75,7 @@ class SDL3Backend(
com.compose.sdl.res.preferredLocaleProvider = ::sdlPreferredLocale
com.compose.sdl.res.preferredLocaleTagsProvider = { com.compose.sdl.text.systemPreferredLocaleTags() }
if (gpuMode is GpuMode.Skia.OpenGL) {
if (gpuMode is GpuMode.OpenGL) {
SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MAJOR_VERSION, 3)
SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_MINOR_VERSION, 2)
SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_CONTEXT_PROFILE_MASK,
@@ -75,10 +84,16 @@ class SDL3Backend(
SDL_GL_SetAttribute(SDL_GLAttr.SDL_GL_STENCIL_SIZE, 8)
}
val flags = SDL_WINDOW_RESIZABLE or SDL_WINDOW_HIGH_PIXEL_DENSITY or when (gpuMode) {
is GpuMode.Skia.OpenGL -> SDL_WINDOW_OPENGL
is GpuMode.Skia.Metal -> SDL_WINDOW_METAL
is GpuMode.Software -> 0UL
var attrFlags = SDL_WINDOW_HIGH_PIXEL_DENSITY
if (resizable) attrFlags = attrFlags or SDL_WINDOW_RESIZABLE
if (undecorated) attrFlags = attrFlags or SDL_WINDOW_BORDERLESS
if (transparent) attrFlags = attrFlags or SDL_WINDOW_TRANSPARENT
if (alwaysOnTop) attrFlags = attrFlags or SDL_WINDOW_ALWAYS_ON_TOP
if (!focusable) attrFlags = attrFlags or SDL_WINDOW_NOT_FOCUSABLE
val flags = attrFlags or when (gpuMode) {
is GpuMode.OpenGL -> SDL_WINDOW_OPENGL
is GpuMode.Metal -> SDL_WINDOW_METAL
is GpuMode.CpuRaster -> 0UL
is GpuMode.Auto -> error("unreachable")
}
window = SDL_CreateWindow(title, width, height, flags)
@@ -88,7 +103,7 @@ class SDL3Backend(
}
when (gpuMode) {
is GpuMode.Skia.OpenGL -> {
is GpuMode.OpenGL -> {
glContext = SDL_GL_CreateContext(window?.reinterpret())
if (glContext == null) {
println("SDL_GL_CreateContext failed: ${SDL_GetError()?.toKString()}")
@@ -101,7 +116,7 @@ class SDL3Backend(
// keeps its fallback frame cap.
vsyncEnabled = SDL_GL_SetSwapInterval(1)
}
is GpuMode.Skia.Metal -> {
is GpuMode.Metal -> {
metalView = SDL_Metal_CreateView(window?.reinterpret())
if (metalView == null) {
println("SDL_Metal_CreateView failed: ${SDL_GetError()?.toKString()}")
@@ -111,7 +126,7 @@ class SDL3Backend(
// blocks - so Metal is vsync-paced and the loop skips its delay.
vsyncEnabled = true
}
is GpuMode.Software -> {
is GpuMode.CpuRaster -> {
// Skia CPU raster: SkiaSurfaceBridge paints a host buffer that
// this SDL_Renderer uploads as a texture each frame.
renderer = SDL_CreateRenderer(window?.reinterpret(), null)
@@ -1,35 +0,0 @@
package com.compose.sdl.input
import androidx.compose.ui.input.pointer.PointerButton
import androidx.compose.ui.input.pointer.PointerEventType
// ==================
// MARK: LegacyPointerEvent (project-only)
// ==================
/** Project-only legacy event class used by the SDL3 input pipeline
(`SDL3EventMapper` → `AppEvent.Pointer` → `ComposeWindow`'s event loop).
The name `PointerEvent` is now reserved for upstream's multi-touch
vendored event (in androidx.compose.ui.input.pointer.PointerEvent);
this single-pointer (x, y, type, button) shape is the bridge between
raw SDL events and upstream-shaped PointerInputChange dispatch.
ComposeWindow constructs an upstream PointerEvent from this for
delivery to Modifier.pointerInput blocks. */
class LegacyPointerEvent(
// Kept as Float: SDL delivers sub-pixel cursor coordinates, and on HiDPI the
// value is multiplied by DPR before dispatch, so truncating to Int here would
// quantize the caret to whole-DPR steps (2px on a 2x display) near glyph edges.
val x: Float,
val y: Float,
val type: PointerEventType,
val button: PointerButton = PointerButton.Primary,
) {
override fun equals(other: Any?): Boolean = other is LegacyPointerEvent &&
other.x == x && other.y == y && other.type == type && other.button == button
override fun hashCode(): Int {
var h = x.hashCode(); h = 31 * h + y.hashCode(); h = 31 * h + type.hashCode(); h = 31 * h + button.hashCode(); return h
}
override fun toString(): String =
"LegacyPointerEvent(x=$x, y=$y, type=$type, button=$button)"
}
@@ -29,12 +29,12 @@ class ComposeRootHost(inDensity: Float = 1f) {
// the root keeps LayoutNode's ErrorMeasurePolicy ("Undefined measure").
measurePolicy = androidx.compose.ui.layout.RootMeasurePolicy
}
private val fOwner = ComposeOwner(rootNode, Density(inDensity), LayoutDirection.Ltr)
private val fApplier = NodeApplier(rootNode)
private val mOwner = ComposeOwner(rootNode, Density(inDensity), LayoutDirection.Ltr)
private val mApplier = NodeApplier(rootNode)
// Upcast to the public supertype so the internal NodeApplier / LayoutNode
// don't leak across the module boundary; Composition accepts Applier<*>.
val applier: Applier<*> get() = fApplier
val applier: Applier<*> get() = mApplier
fun attach() {
// Install the focus root + drag-and-drop root on the root LayoutNode
@@ -43,8 +43,8 @@ class ComposeRootHost(inDensity: Float = 1f) {
// focus root the focus tree has no origin and requestFocus never
// sticks; without the DnD root the root DragAndDropNode never receives
// events from Sdl3DragAndDropOwner's dispatch calls.
rootNode.modifier = fOwner.focusOwner.modifier.then(fOwner.dragAndDropManager.modifier)
fOwner.attach()
rootNode.modifier = mOwner.focusOwner.modifier.then(mOwner.dragAndDropManager.modifier)
mOwner.attach()
// Turn on snapshot observation so writes to `mutableStateOf` (ScrollState.value,
// LazyList firstVisibleItemIndex, etc.) fire the appropriate invalidation
// callbacks - requestRelayout / requestRemeasure - routing through the vendored
@@ -52,53 +52,53 @@ class ComposeRootHost(inDensity: Float = 1f) {
// state but the placement lambda inside ScrollNode.measure never re-runs, so
// the child layer's move(IntOffset(0, -scroll)) never happens → scroll offset
// is invisible even though the state is updating.
fOwner.snapshotObserver.startObserving()
mOwner.snapshotObserver.startObserving()
}
fun setConstraints(inWidth: Int, inHeight: Int) {
fOwner.setRootConstraints(Constraints.fixed(inWidth, inHeight))
mOwner.setRootConstraints(Constraints.fixed(inWidth, inHeight))
}
// Pump the owner's node-animation frame clock (scroll fling / animateScrollToItem / node
// Animatables). Called once per frame by ComposeWindow with a monotonic nanos timestamp.
fun sendAnimationFrame(inNanos: Long) {
fOwner.animationFrameClock.sendFrame(inNanos)
mOwner.animationFrameClock.sendFrame(inNanos)
}
// True while a node-level animation (scroll fling, node Animatable) awaits the next
// animation frame - the node half of the window's quiescence signal (render-to-quiescence
// screenshot capture asks the window whether ANY work is still pending).
fun hasAnimationAwaiters(): Boolean = fOwner.animationFrameClock.hasAwaiters
fun hasAnimationAwaiters(): Boolean = mOwner.animationFrameClock.hasAwaiters
fun measureAndLayout() {
// Flush deferred end-of-apply work (focus invalidation etc.) before measuring, so
// requestFocus() from composition OR from a pointer event this frame takes effect -
// e.g. focus-on-click, which schedules a focus invalidation during event dispatch.
fOwner.onEndApplyChanges()
fOwner.measureAndLayout()
mOwner.onEndApplyChanges()
mOwner.measureAndLayout()
// Drop snapshot observations whose scope is no longer valid (detached nodes,
// destroyed layers, disposed draw scopes). Upstream RootNodeOwner does this after
// the measure pass; without it, every disposed subtree's measure/layout/DRAW
// observation scopes linger in the OwnerSnapshotObserver forever - each pins its
// observed object graph (a leak the P2.2 soak caught on ripple/indication draws).
fOwner.snapshotObserver.clearInvalidObservations()
mOwner.snapshotObserver.clearInvalidObservations()
}
// Draw the composed tree into [canvas] - re-records dirty layers, then walks
// the root so clean layers replay their cached content. Backends call this
// instead of rootNode.draw so retained-layer bookkeeping runs each frame.
fun drawRoot(canvas: androidx.compose.ui.graphics.Canvas) {
fOwner.renderRoot(canvas)
mOwner.renderRoot(canvas)
}
// The window wires this to set needsFrame - a layer whose content changed
// (OwnedLayer.invalidate) schedules a frame even with nothing else pending.
fun setInvalidationCallback(callback: () -> Unit) {
fOwner.onInvalidate = callback
mOwner.onInvalidate = callback
}
// The owner's FocusOwner IS a FocusManager - the window provides it as LocalFocusManager.
val focusManager: androidx.compose.ui.focus.FocusManager get() = fOwner.focusOwner
val focusManager: androidx.compose.ui.focus.FocusManager get() = mOwner.focusOwner
// ==================
// MARK: Owner-backed values for the composition-locals seed
@@ -107,19 +107,19 @@ class ComposeRootHost(inDensity: Float = 1f) {
// Provider; ComposeWindow wraps setContent in a CompositionLocalProvider whose
// values come from the ComposeOwner attached below. These accessors expose the
// right fields without leaking the `internal Owner` interface across modules.
val density: androidx.compose.ui.unit.Density get() = fOwner.density
val layoutDirection: androidx.compose.ui.unit.LayoutDirection get() = fOwner.layoutDirection
val viewConfiguration: androidx.compose.ui.platform.ViewConfiguration get() = fOwner.viewConfiguration
val graphicsContext: androidx.compose.ui.graphics.GraphicsContext get() = fOwner.graphicsContext
val inputModeManager: androidx.compose.ui.input.InputModeManager get() = fOwner.inputModeManager
val hapticFeedback: androidx.compose.ui.hapticfeedback.HapticFeedback get() = fOwner.hapticFeedBack
val textToolbar: androidx.compose.ui.platform.TextToolbar get() = fOwner.textToolbar
val windowInfo: androidx.compose.ui.platform.WindowInfo get() = fOwner.windowInfo
val density: androidx.compose.ui.unit.Density get() = mOwner.density
val layoutDirection: androidx.compose.ui.unit.LayoutDirection get() = mOwner.layoutDirection
val viewConfiguration: androidx.compose.ui.platform.ViewConfiguration get() = mOwner.viewConfiguration
val graphicsContext: androidx.compose.ui.graphics.GraphicsContext get() = mOwner.graphicsContext
val inputModeManager: androidx.compose.ui.input.InputModeManager get() = mOwner.inputModeManager
val hapticFeedback: androidx.compose.ui.hapticfeedback.HapticFeedback get() = mOwner.hapticFeedBack
val textToolbar: androidx.compose.ui.platform.TextToolbar get() = mOwner.textToolbar
val windowInfo: androidx.compose.ui.platform.WindowInfo get() = mOwner.windowInfo
// Push SDL window focus into windowInfo.isWindowFocused (focus-reactive UI).
fun setWindowFocused(inFocused: Boolean) = fOwner.setWindowFocused(inFocused)
fun setWindowFocused(inFocused: Boolean) = mOwner.setWindowFocused(inFocused)
val softwareKeyboardController: androidx.compose.ui.platform.SoftwareKeyboardController
get() = fOwner.softwareKeyboardController
get() = mOwner.softwareKeyboardController
// PointerIconService and its composition local are :ui-internal, so ComposeWindow
// can't provide the local directly. This wrapper seeds it from the owner so
@@ -127,7 +127,7 @@ class ComposeRootHost(inDensity: Float = 1f) {
@androidx.compose.runtime.Composable
fun ProvidePointerIconService(content: @androidx.compose.runtime.Composable () -> Unit) {
androidx.compose.runtime.CompositionLocalProvider(
androidx.compose.ui.platform.LocalPointerIconService provides fOwner.pointerIconService,
androidx.compose.ui.platform.LocalPointerIconService provides mOwner.pointerIconService,
content = content,
)
}
@@ -140,7 +140,7 @@ class ComposeRootHost(inDensity: Float = 1f) {
onKeyEvent) - drives text-field editing keys (backspace / arrows / enter) and
clickable Enter/Space activation. Returns true if some node consumed it. */
fun dispatchKeyEvent(inEvent: androidx.compose.ui.input.key.KeyEvent): Boolean =
runCatching { fOwner.focusOwner.dispatchKeyEvent(inEvent) }.getOrDefault(false)
runCatching { mOwner.focusOwner.dispatchKeyEvent(inEvent) }.getOrDefault(false)
// ==================
// MARK: Input - feed the vendored PointerInputEventProcessor
@@ -152,13 +152,13 @@ class ComposeRootHost(inDensity: Float = 1f) {
// `expect PointerInputEvent` has no commonMain constructor, so the actual build+dispatch
// lives in nativeMain (feedPointerToProcessor).
fun onPointerRaw(inX: Float, inY: Float, inType: Int, inButton: Int, inUptime: Long) {
feedPointerToProcessor(fOwner, inType, inButton, inUptime, inX, inY)
feedPointerToProcessor(mOwner, inType, inButton, inUptime, inX, inY)
}
// Mouse wheel - feed a scroll PointerInputEvent to the processor so the vendored
// Modifier.scrollable (MouseWheelScrollingLogic) handles it, exactly like upstream.
fun onWheel(inX: Float, inY: Float, inDeltaX: Float, inDeltaY: Float, inUptime: Long) {
feedScrollToProcessor(fOwner, inX, inY, inDeltaX, inDeltaY, inUptime)
feedScrollToProcessor(mOwner, inX, inY, inDeltaX, inDeltaY, inUptime)
}
// ==================
@@ -169,11 +169,11 @@ class ComposeRootHost(inDensity: Float = 1f) {
// COMPLETE to these methods; Sdl3DragAndDropOwner accumulates the drop
// session and dispatches through the root DragAndDropNode on COMPLETE.
fun onDropBegin() = fOwner.dragAndDropManager.dropBegin()
fun onDropPosition(inX: Float, inY: Float) = fOwner.dragAndDropManager.dropPosition(inX, inY)
fun onDropFile(inPath: String) = fOwner.dragAndDropManager.dropFile(inPath)
fun onDropText(inText: String) = fOwner.dragAndDropManager.dropText(inText)
fun onDropComplete() = fOwner.dragAndDropManager.dropComplete()
fun onDropBegin() = mOwner.dragAndDropManager.dropBegin()
fun onDropPosition(inX: Float, inY: Float) = mOwner.dragAndDropManager.dropPosition(inX, inY)
fun onDropFile(inPath: String) = mOwner.dragAndDropManager.dropFile(inPath)
fun onDropText(inText: String) = mOwner.dragAndDropManager.dropText(inText)
fun onDropComplete() = mOwner.dragAndDropManager.dropComplete()
}
// NOTE: feedPointerToProcessor / feedScrollToProcessor are plain nativeMain
@@ -18,9 +18,9 @@ import com.compose.sdl.node.impl.ComposeOwner
mouse down unless `buttons.isPrimaryPressed`, and drag/scroll gestures need the held button to
persist across Move events, so the synthesized PointerInputEvent must carry a live button mask -
not just the changed button. A single mouse => module-level flags are enough. */
private var fPrimaryDown = false
private var fSecondaryDown = false
private var fTertiaryDown = false
private var mPrimaryDown = false
private var mSecondaryDown = false
private var mTertiaryDown = false
/** Builds the internal PointerInputEvent (its constructor is native-only) from a single mouse
pointer and drives the owner's PointerInputEventProcessor. inType: 0=Move 1=Press 2=Release 3=Exit;
@@ -34,14 +34,14 @@ internal fun feedPointerToProcessor(
inY: Float,
) {
when (inType) {
1 -> when (inButton) { 0 -> fPrimaryDown = true; 1 -> fSecondaryDown = true; 2 -> fTertiaryDown = true }
2 -> when (inButton) { 0 -> fPrimaryDown = false; 1 -> fSecondaryDown = false; 2 -> fTertiaryDown = false }
1 -> when (inButton) { 0 -> mPrimaryDown = true; 1 -> mSecondaryDown = true; 2 -> mTertiaryDown = true }
2 -> when (inButton) { 0 -> mPrimaryDown = false; 1 -> mSecondaryDown = false; 2 -> mTertiaryDown = false }
}
val vButtons = PointerButtons(
isPrimaryPressed = fPrimaryDown,
isSecondaryPressed = fSecondaryDown,
isTertiaryPressed = fTertiaryDown,
isPrimaryPressed = mPrimaryDown,
isSecondaryPressed = mSecondaryDown,
isTertiaryPressed = mTertiaryDown,
)
val vButton = if (inType == 1 || inType == 2) {
when (inButton) {
@@ -57,7 +57,7 @@ internal fun feedPointerToProcessor(
uptime = inUptime,
positionOnScreen = vPos,
position = vPos,
down = fPrimaryDown || fSecondaryDown || fTertiaryDown,
down = mPrimaryDown || mSecondaryDown || mTertiaryDown,
pressure = 1f,
type = PointerType.Mouse,
activeHover = inType == 0, // Exit (3) also reports no active hover
@@ -80,28 +80,28 @@ internal class ComposeOwner(
) : Owner, OwnedLayerManager {
// The vendored measure/layout state machine, rooted at [root].
private val fDelegate = MeasureAndLayoutDelegate(root)
private val mDelegate = MeasureAndLayoutDelegate(root)
// The vendored pointer-input dispatcher. Routes a PointerInputEvent through
// HitPathTracker to the tree's PointerInputModifierNodes (hover / gesture),
// synthesizing Enter/Exit. Fed from ComposeWindow via ComposeRootHost.
private val fPointerProcessor = androidx.compose.ui.input.pointer.PointerInputEventProcessor(root)
private val mPointerProcessor = androidx.compose.ui.input.pointer.PointerInputEventProcessor(root)
// Window focus + container size backing [windowInfo], snapshot-backed so
// focus-reactive UI and LocalWindowInfo.containerSize readers recompose. Fed by
// the window: focus via setWindowFocused (SDL activation), size from setRootConstraints.
private val fWindowFocused = androidx.compose.runtime.mutableStateOf(true)
private val fContainerSize = androidx.compose.runtime.mutableStateOf(androidx.compose.ui.unit.IntSize.Zero)
private val mWindowFocused = androidx.compose.runtime.mutableStateOf(true)
private val mContainerSize = androidx.compose.runtime.mutableStateOf(androidx.compose.ui.unit.IntSize.Zero)
// Called by the window on SDL focus gain/loss.
internal fun setWindowFocused(inFocused: Boolean) { fWindowFocused.value = inFocused }
internal fun setWindowFocused(inFocused: Boolean) { mWindowFocused.value = inFocused }
// Dispatch one pointer event to the upstream PointerInputModifierNode tree.
// [this] is the PositionCalculator (Owner : PositionCalculator).
internal fun processPointerInput(inEvent: androidx.compose.ui.input.pointer.PointerInputEvent): Boolean {
val vResult = fPointerProcessor.process(inEvent, this)
val vResult = mPointerProcessor.process(inEvent, this)
// Apply the cursor the hover pipeline just set for this event (deduped in SdlCursors).
com.compose.sdl.SdlCursors.apply(fPointerIcon)
com.compose.sdl.SdlCursors.apply(mPointerIcon)
return vResult.dispatchedToAPointerInputModifier
}
@@ -115,11 +115,11 @@ internal class ComposeOwner(
// Sets the constraints the root is measured against (window pixel size in
// logical points). Call each frame before [measureAndLayout].
fun setRootConstraints(inConstraints: Constraints) {
fDelegate.updateRootConstraints(inConstraints)
mDelegate.updateRootConstraints(inConstraints)
// Report the container size (physical px, the port's layout coord space) to
// windowInfo; updates on every resize since the window re-measures per frame.
if (inConstraints.hasBoundedWidth && inConstraints.hasBoundedHeight) {
fContainerSize.value =
mContainerSize.value =
androidx.compose.ui.unit.IntSize(inConstraints.maxWidth, inConstraints.maxHeight)
}
}
@@ -138,8 +138,8 @@ internal class ComposeOwner(
forceRequest: Boolean,
scheduleMeasureAndLayout: Boolean,
) {
if (affectsLookahead) fDelegate.requestLookaheadRemeasure(layoutNode, forceRequest)
else fDelegate.requestRemeasure(layoutNode, forceRequest)
if (affectsLookahead) mDelegate.requestLookaheadRemeasure(layoutNode, forceRequest)
else mDelegate.requestRemeasure(layoutNode, forceRequest)
}
override fun onRequestRelayout(
@@ -147,25 +147,25 @@ internal class ComposeOwner(
affectsLookahead: Boolean,
forceRequest: Boolean,
) {
if (affectsLookahead) fDelegate.requestLookaheadRelayout(layoutNode, forceRequest)
else fDelegate.requestRelayout(layoutNode, forceRequest)
if (affectsLookahead) mDelegate.requestLookaheadRelayout(layoutNode, forceRequest)
else mDelegate.requestRelayout(layoutNode, forceRequest)
}
override fun measureAndLayout(sendPointerUpdate: Boolean) {
fDelegate.measureAndLayout()
fDelegate.dispatchOnPositionedCallbacks()
mDelegate.measureAndLayout()
mDelegate.dispatchOnPositionedCallbacks()
}
override fun measureAndLayout(layoutNode: LayoutNode, constraints: Constraints) {
fDelegate.measureAndLayout(layoutNode, constraints)
mDelegate.measureAndLayout(layoutNode, constraints)
}
override fun forceMeasureTheSubtree(layoutNode: LayoutNode, affectsLookahead: Boolean) {
fDelegate.forceMeasureTheSubtree(layoutNode, affectsLookahead)
mDelegate.forceMeasureTheSubtree(layoutNode, affectsLookahead)
}
override fun onDetach(node: LayoutNode) {
fDelegate.onNodeDetached(node)
mDelegate.onNodeDetached(node)
// Drop the detached node's read-observation scopes, exactly as upstream
// RootNodeOwner.onDetach does. Without this, every disposed node's measure/
// layout/draw observation scopes accumulate in the snapshot observer forever
@@ -174,7 +174,7 @@ internal class ComposeOwner(
}
override fun registerOnLayoutCompletedListener(listener: Owner.OnLayoutCompletedListener) {
fDelegate.registerOnLayoutCompletedListener(listener)
mDelegate.registerOnLayoutCompletedListener(listener)
}
override fun requestOnPositionedCallback(layoutNode: LayoutNode) = Unit
@@ -345,12 +345,12 @@ internal class ComposeOwner(
}
// Desired hover cursor, set by the vendored HoverIconModifierNode
// (Modifier.pointerHoverIcon) and applied via SDL in processPointerInput. null = default.
private var fPointerIcon: androidx.compose.ui.input.pointer.PointerIcon =
private var mPointerIcon: androidx.compose.ui.input.pointer.PointerIcon =
androidx.compose.ui.input.pointer.PointerIcon.Default
override val pointerIconService: PointerIconService = object : PointerIconService {
override fun getIcon(): androidx.compose.ui.input.pointer.PointerIcon = fPointerIcon
override fun getIcon(): androidx.compose.ui.input.pointer.PointerIcon = mPointerIcon
override fun setIcon(value: androidx.compose.ui.input.pointer.PointerIcon?) {
fPointerIcon = value ?: androidx.compose.ui.input.pointer.PointerIcon.Default
mPointerIcon = value ?: androidx.compose.ui.input.pointer.PointerIcon.Default
}
override fun getStylusHoverIcon(): androidx.compose.ui.input.pointer.PointerIcon? = null
override fun setStylusHoverIcon(value: androidx.compose.ui.input.pointer.PointerIcon?) {}
@@ -368,13 +368,13 @@ internal class ComposeOwner(
owner = this,
)
override val windowInfo: WindowInfo = object : WindowInfo {
override val isWindowFocused: Boolean get() = fWindowFocused.value
override val containerSize: androidx.compose.ui.unit.IntSize get() = fContainerSize.value
override val isWindowFocused: Boolean get() = mWindowFocused.value
override val containerSize: androidx.compose.ui.unit.IntSize get() = mContainerSize.value
override val containerDpSize: androidx.compose.ui.unit.DpSize
get() = with(density) {
androidx.compose.ui.unit.DpSize(
fContainerSize.value.width.toDp(),
fContainerSize.value.height.toDp(),
mContainerSize.value.width.toDp(),
mContainerSize.value.height.toDp(),
)
}
}
@@ -412,16 +412,16 @@ internal class ComposeOwner(
// End-of-apply-changes listeners - the focus system (FocusInvalidationManager) and other
// engine parts register here to defer work until changes are applied; without this the
// focus invalidation never flushes and requestFocus() (e.g. focus-on-click) never sticks.
private val fEndApplyChangesListeners = mutableListOf<() -> Unit>()
private val mEndApplyChangesListeners = mutableListOf<() -> Unit>()
override fun registerOnEndApplyChangesListener(listener: () -> Unit) {
if (listener !in fEndApplyChangesListeners) fEndApplyChangesListeners.add(listener)
if (listener !in mEndApplyChangesListeners) mEndApplyChangesListeners.add(listener)
}
override fun onEndApplyChanges() {
// Drain in order; listeners may re-register, so loop until empty.
while (fEndApplyChangesListeners.isNotEmpty()) {
val vListener = fEndApplyChangesListeners.removeAt(0)
while (mEndApplyChangesListeners.isNotEmpty()) {
val vListener = mEndApplyChangesListeners.removeAt(0)
vListener()
}
}
@@ -17,10 +17,10 @@ import com.compose.sdl.loadComposeResourceBytes
*
* Idempotent; called from ComposeWindow.installGlobals (data.kres is loadable by then).
*/
private var fRegistered = false
private var mRegistered = false
fun registerGenericFonts() {
if (fRegistered) return
fRegistered = true
if (mRegistered) return
mRegistered = true
loadComposeResourceBytes("font/NotoSansMono.ttf")?.let { IconFont.register("generic:monospace", it) }
}
@@ -6,4 +6,4 @@ import com.compose.sdl.renderer.skia.SkiaBridge
// MARK: Linux GPU defaults
// ==================
internal actual fun makeMetalBridge(backend: SDL3Backend): SkiaBridge? = null
internal actual fun makeMetalBridge(backend: Sdl3Backend): SkiaBridge? = null
@@ -3,7 +3,7 @@ package com.compose.sdl
import com.compose.sdl.renderer.skia.SkiaBridge
import com.compose.sdl.renderer.skia.SkiaMetalBridge
internal actual fun makeMetalBridge(backend: SDL3Backend): SkiaBridge? {
internal actual fun makeMetalBridge(backend: Sdl3Backend): SkiaBridge? {
val bridge = SkiaMetalBridge(backend)
return if (bridge.init()) bridge else null
}
@@ -37,16 +37,16 @@ import sdl3.SDL_Metal_GetLayer
per frame, so we tear the BackendRenderTarget + Surface down every frame
(cheap - the DirectContext + device + queue persist). */
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
internal class SkiaMetalBridge(private val backend: SDL3Backend) : SkiaBridge {
private var fDevice: MTLDeviceProtocol? = null
private var fQueue: MTLCommandQueueProtocol? = null
private var fLayer: CAMetalLayer? = null
private var fContext: DirectContext? = null
private var fSurface: Surface? = null
private var fRT: BackendRenderTarget? = null
private var fDrawable: CAMetalDrawableProtocol? = null
private var fWidth = 0
private var fHeight = 0
internal class SkiaMetalBridge(private val backend: Sdl3Backend) : SkiaBridge {
private var mDevice: MTLDeviceProtocol? = null
private var mQueue: MTLCommandQueueProtocol? = null
private var mLayer: CAMetalLayer? = null
private var mContext: DirectContext? = null
private var mSurface: Surface? = null
private var mRT: BackendRenderTarget? = null
private var mDrawable: CAMetalDrawableProtocol? = null
private var mWidth = 0
private var mHeight = 0
fun init(): Boolean {
val vView = backend.metalView ?: run {
@@ -84,22 +84,22 @@ internal class SkiaMetalBridge(private val backend: SDL3Backend) : SkiaBridge {
return false
}
fDevice = vDevice
fQueue = vQueue
fLayer = vLayer
fContext = vContext
mDevice = vDevice
mQueue = vQueue
mLayer = vLayer
mContext = vContext
return true
}
override val canvas: Canvas
get() = requireNotNull(fSurface) { "SkiaMetalBridge not initialised" }.canvas
get() = requireNotNull(mSurface) { "SkiaMetalBridge not initialised" }.canvas
override fun ensureSize(inWidth: Int, inHeight: Int): Boolean {
if (inWidth <= 0 || inHeight <= 0) return false
val vLayer = fLayer ?: return false
val vContext = fContext ?: return false
val vLayer = mLayer ?: return false
val vContext = mContext ?: return false
if (inWidth != fWidth || inHeight != fHeight) {
if (inWidth != mWidth || inHeight != mHeight) {
vLayer.drawableSize = CGSizeMake(inWidth.toDouble(), inHeight.toDouble())
// Keep contentsScale in lock-step with the live backing density.
// Setting it only in init() can leave it stale at 1.0 when the
@@ -107,18 +107,18 @@ internal class SkiaMetalBridge(private val backend: SDL3Backend) : SkiaBridge {
// drawable then gets downscaled to the layer's logical backing and
// re-upscaled by the display, softening text and aliasing edges.
vLayer.contentsScale = backend.pixelDensity.toDouble()
fWidth = inWidth
fHeight = inHeight
mWidth = inWidth
mHeight = inHeight
println("SkiaMetalBridge: drawable ${inWidth}x${inHeight} @ contentsScale ${backend.pixelDensity}")
}
// Per-frame: drop the previous frame's drawable wrappers and grab a
// fresh one. nextDrawable() may block (~16ms vsync) when 3 drawables
// are already in flight - that's the natural pacing mechanism.
fSurface?.close()
fRT?.close()
fSurface = null
fRT = null
mSurface?.close()
mRT?.close()
mSurface = null
mRT = null
val vDrawable = vLayer.nextDrawable() ?: run {
println("SkiaMetalBridge: nextDrawable returned null")
@@ -142,38 +142,38 @@ internal class SkiaMetalBridge(private val backend: SDL3Backend) : SkiaBridge {
println("SkiaMetalBridge: Surface.makeFromBackendRenderTarget returned null")
return false
}
fRT = vRT
fSurface = vSurface
fDrawable = vDrawable
mRT = vRT
mSurface = vSurface
mDrawable = vDrawable
return true
}
override fun present() {
val vSurface = fSurface ?: return
val vQueue = fQueue ?: return
val vDrawable = fDrawable ?: return
val vSurface = mSurface ?: return
val vQueue = mQueue ?: return
val vDrawable = mDrawable ?: return
vSurface.flushAndSubmit()
val vCommandBuffer = vQueue.commandBuffer() ?: return
vCommandBuffer.presentDrawable(vDrawable)
vCommandBuffer.commit()
fDrawable = null
mDrawable = null
}
override fun snapshot(): Image? = fSurface?.makeImageSnapshot()
override fun snapshot(): Image? = mSurface?.makeImageSnapshot()
override fun snapshotBgra(): Triple<Int, Int, ByteArray>? =
readBackBgra(fSurface, fWidth, fHeight)
readBackBgra(mSurface, mWidth, mHeight)
override fun destroy() {
fSurface?.close()
fRT?.close()
fContext?.close()
fSurface = null
fRT = null
fContext = null
fLayer = null
fQueue = null
fDevice = null
fDrawable = null
mSurface?.close()
mRT?.close()
mContext?.close()
mSurface = null
mRT = null
mContext = null
mLayer = null
mQueue = null
mDevice = null
mDrawable = null
}
}
@@ -7,17 +7,17 @@ import kotlin.experimental.ExperimentalNativeApi
// MARK: Metal bridge factory (skia-only)
// ==================
internal expect fun makeMetalBridge(backend: SDL3Backend): SkiaBridge?
internal expect fun makeMetalBridge(backend: Sdl3Backend): SkiaBridge?
actual fun rendererPreferredGpuMode(): GpuMode {
@OptIn(ExperimentalNativeApi::class)
return when (Platform.osFamily) {
OsFamily.MACOSX -> GpuMode.Skia.Metal
OsFamily.LINUX -> GpuMode.Skia.OpenGL
OsFamily.MACOSX -> GpuMode.Metal
OsFamily.LINUX -> GpuMode.OpenGL
// Windows (mingwX64, Route 1a): GPU via Skia's GL backend on an SDL WGL
// context (SkiaGLBridge, shared with Linux). CPU raster stays available
// as --gpu=software. (A native D3D12 bridge is the milestone-2 upgrade.)
OsFamily.WINDOWS -> GpuMode.Skia.OpenGL
else -> GpuMode.Software
OsFamily.WINDOWS -> GpuMode.OpenGL
else -> GpuMode.CpuRaster
}
}
@@ -10,7 +10,7 @@ import com.compose.sdl.renderer.skia.SkiaRenderBackend
actual delegates here when Skia is the selected renderer). Mirrors the
SDL3 module's createRenderBackend / rendererPreferredGpuMode in the same
package; exactly one renderer module is included per target. */
actual fun createRenderBackend(inSdl: SDL3Backend, inGpu: GpuMode): RenderBackend? {
actual fun createRenderBackend(inSdl: Sdl3Backend, inGpu: GpuMode): RenderBackend? {
val vResolved = if (inGpu is GpuMode.Auto) rendererPreferredGpuMode() else inGpu
return try {
SkiaRenderBackend(inSdl, vResolved)
@@ -13,7 +13,7 @@ import org.jetbrains.skia.Image
per frame; the concrete implementation owns whatever GPU / CPU
resources it needs (raster buffer + SDL_Texture, or a GL/Metal backend
render target). */
interface SkiaBridge {
internal interface SkiaBridge {
val canvas: Canvas
fun ensureSize(inWidth: Int, inHeight: Int): Boolean
fun present()
@@ -4,16 +4,13 @@ import com.compose.sdl.*
import kotlinx.cinterop.*
import org.jetbrains.skia.BackendRenderTarget
import org.jetbrains.skia.Bitmap
import org.jetbrains.skia.Canvas
import org.jetbrains.skia.ColorAlphaType
import org.jetbrains.skia.ColorType
import org.jetbrains.skia.DirectContext
import org.jetbrains.skia.Image
import org.jetbrains.skia.ImageInfo
import org.jetbrains.skia.Surface
import org.jetbrains.skia.SurfaceColorFormat
import org.jetbrains.skia.SurfaceOrigin
import sdl3.SDL_GL_MakeCurrent
import sdl3.SDL_GL_SwapWindow
// ==================
@@ -27,17 +24,38 @@ import sdl3.SDL_GL_SwapWindow
Resize: tear down and rebuild the BackendRenderTarget + Surface; the
DirectContext is reused. The default GL framebuffer (id 0) is wrapped
directly; we don't manage an offscreen FBO. */
internal class SkiaGLBridge(private val backend: SDL3Backend) : SkiaBridge {
private var fContext: DirectContext? = null
private var fRT: BackendRenderTarget? = null
private var fSurface: Surface? = null
private var fWidth = 0
private var fHeight = 0
internal class SkiaGLBridge(private val backend: Sdl3Backend) : SkiaBridge {
private var mContext: DirectContext? = null
private var mRT: BackendRenderTarget? = null
private var mSurface: Surface? = null
private var mWidth = 0
private var mHeight = 0
/**
Binds THIS window's GL context to the calling thread.
A GL context is per-window but "current" is per-THREAD state, so with more
than one window open the last one to touch GL owns the thread. SDL makes a
context current once at creation (Sdl3Backend.init) and nothing re-bound it
per frame, so window A issued its draw and swap against window B's context:
silent corruption while both live, and a hard crash (SIGSEGV in present)
the moment B is destroyed and the current context is left dangling.
Every entry point that touches GL calls this first. Cheap when already
current - SDL short-circuits a redundant bind, so the single-window path
pays nothing.
*/
private fun makeCurrent() {
val vWindow = backend.window ?: return
val vContext = backend.glContext ?: return
SDL_GL_MakeCurrent(vWindow.reinterpret(), vContext.reinterpret())
}
fun init(): Boolean {
return try {
fContext = DirectContext.makeGL()
fContext != null
makeCurrent()
mContext = DirectContext.makeGL()
mContext != null
} catch (t: Throwable) {
println("SkiaGLBridge: DirectContext.makeGL failed: ${t.message}")
false
@@ -45,17 +63,20 @@ internal class SkiaGLBridge(private val backend: SDL3Backend) : SkiaBridge {
}
override val canvas: Canvas
get() = requireNotNull(fSurface) { "SkiaGLBridge not initialised" }.canvas
get() = requireNotNull(mSurface) { "SkiaGLBridge not initialised" }.canvas
override fun ensureSize(inWidth: Int, inHeight: Int): Boolean {
if (inWidth == fWidth && inHeight == fHeight && fSurface != null) return true
// BEFORE the unchanged-size early return: this runs once per frame and is
// what re-binds this window's context for the draw phase that follows.
makeCurrent()
if (inWidth == mWidth && inHeight == mHeight && mSurface != null) return true
if (inWidth <= 0 || inHeight <= 0) return false
val vContext = fContext ?: return false
val vContext = mContext ?: return false
fSurface?.close()
fRT?.close()
fSurface = null
fRT = null
mSurface?.close()
mRT?.close()
mSurface = null
mRT = null
// GL_RGBA8 = 0x8058. fbId = 0 binds the default framebuffer.
// sampleCnt = 0 means no MSAA; stencilBits = 8 is what Skia recommends.
@@ -77,31 +98,35 @@ internal class SkiaGLBridge(private val backend: SDL3Backend) : SkiaBridge {
println("SkiaGLBridge: Surface.makeFromBackendRenderTarget returned null")
return false
}
fRT = vRT
fSurface = vSurface
fWidth = inWidth
fHeight = inHeight
mRT = vRT
mSurface = vSurface
mWidth = inWidth
mHeight = inHeight
return true
}
override fun present() {
val vSurface = fSurface ?: return
val vSurface = mSurface ?: return
val vWindow = backend.window ?: return
makeCurrent()
vSurface.flushAndSubmit()
SDL_GL_SwapWindow(vWindow.reinterpret())
}
override fun snapshot(): Image? = fSurface?.makeImageSnapshot()
override fun snapshot(): Image? = mSurface?.makeImageSnapshot()
override fun snapshotBgra(): Triple<Int, Int, ByteArray>? =
readBackBgra(fSurface, fWidth, fHeight)
readBackBgra(mSurface, mWidth, mHeight)
override fun destroy() {
fSurface?.close()
fRT?.close()
fContext?.close()
fSurface = null
fRT = null
fContext = null
// Free this window's GPU objects against its OWN context, not whichever
// window happens to be current.
makeCurrent()
mSurface?.close()
mRT?.close()
mContext?.close()
mSurface = null
mRT = null
mContext = null
}
}
@@ -25,13 +25,13 @@ import org.jetbrains.skia.Color as SkColor
elevation shadows ride behind it via skiaLeafDrawer + the SkiaBackedCanvas
manual-vendor. */
internal class SkiaRenderBackend(
private val sdl: SDL3Backend,
private val sdl: Sdl3Backend,
private val gpuMode: GpuMode,
) : RenderBackend {
private val fBridge: SkiaBridge = buildBridge()
private val fSkiaImageCache = SkiaImageCache()
private var fCurrentCanvas: Canvas? = null
private val mBridge: SkiaBridge = buildBridge()
private val mSkiaImageCache = SkiaImageCache()
private var mCurrentCanvas: Canvas? = null
init {
// skiko's paragraph engine always supports variable-font axes (Material
@@ -60,40 +60,40 @@ internal class SkiaRenderBackend(
// Setting it once in the ctor left it dangling at a CLOSED window's (destroyed)
// image cache after a multi-window teardown → crash on the surviving window's
// next frame, so it is re-pointed at THIS backend before every frame.
private val fLeafDrawer = SkiaLeafDrawer(fSkiaImageCache)
private val mLeafDrawer = SkiaLeafDrawer(mSkiaImageCache)
override val imageLoader: ImageLoader = object : ImageLoader {
override fun intrinsicSize(inPath: String, inKind: ResourceKind): Size =
fSkiaImageCache.intrinsicSize(inPath, inKind)
mSkiaImageCache.intrinsicSize(inPath, inKind)
override fun readBytes(inPath: String): ByteArray? =
loadComposeResourceBytes(inPath)
}
private fun buildBridge(): SkiaBridge = when (gpuMode) {
is GpuMode.Skia.Metal -> makeMetalBridge(sdl) ?: error("Skia.Metal isn't supported on this target")
is GpuMode.Skia.OpenGL -> SkiaGLBridge(sdl).also { require(it.init()) { "Skia.OpenGL init failed" } }
is GpuMode.Software -> SkiaSurfaceBridge(sdl)
is GpuMode.Metal -> makeMetalBridge(sdl) ?: error("Skia.Metal isn't supported on this target")
is GpuMode.OpenGL -> SkiaGLBridge(sdl).also { require(it.init()) { "Skia.OpenGL init failed" } }
is GpuMode.CpuRaster -> SkiaSurfaceBridge(sdl)
is GpuMode.Auto -> error("SkiaRenderBackend received non-Skia gpuMode $gpuMode")
}
override fun ensureSize(inPixelWidth: Int, inPixelHeight: Int): Boolean =
fBridge.ensureSize(inPixelWidth, inPixelHeight)
mBridge.ensureSize(inPixelWidth, inPixelHeight)
override fun beginFrame(inDpr: Float) {
val canvas = fBridge.canvas
val canvas = mBridge.canvas
// Clear the surface to Material dark. Without this the Metal back
// buffer shows uninitialized GPU memory (pink). Clearing before scale
// so it covers the whole physical target.
canvas.clear(SkColor.makeARGB(0xFF, 0x12, 0x12, 0x12))
canvas.save()
if (inDpr != 1f) canvas.scale(inDpr, inDpr)
fCurrentCanvas = canvas
mCurrentCanvas = canvas
}
override fun drawRoot(inDraw: (androidx.compose.ui.graphics.Canvas) -> Unit) {
val canvas = fCurrentCanvas ?: return
val canvas = mCurrentCanvas ?: return
// Point the leaf-draw global at THIS window's renderers before drawing.
skiaLeafDrawer = fLeafDrawer
skiaLeafDrawer = mLeafDrawer
// Wrap the live skia canvas as upstream's SkiaBackedCanvas (real gradients/
// paint/shader). Its port draw contracts forward to skiaLeafDrawer. The
// ImageBitmap-backed offscreen (VectorPainter / DrawCache) now goes through
@@ -104,18 +104,18 @@ internal class SkiaRenderBackend(
}
override fun endFrame() {
fCurrentCanvas?.restore()
fCurrentCanvas = null
fBridge.present()
mCurrentCanvas?.restore()
mCurrentCanvas = null
mBridge.present()
}
override fun snapshotBgra(): Triple<Int, Int, ByteArray>? = fBridge.snapshotBgra()
override fun snapshotBgra(): Triple<Int, Int, ByteArray>? = mBridge.snapshotBgra()
override fun destroy() {
// Drop the global if it still points at this (about-to-be-destroyed) backend,
// so a stray draw before the next window's drawRoot can't hit freed renderers.
if (skiaLeafDrawer === fLeafDrawer) skiaLeafDrawer = null
fSkiaImageCache.destroy()
fBridge.destroy()
if (skiaLeafDrawer === mLeafDrawer) skiaLeafDrawer = null
mSkiaImageCache.destroy()
mBridge.destroy()
}
}
@@ -18,21 +18,21 @@ import sdl3.*
/** Owns the CPU pixel buffer Skia draws into, plus the streaming SDL_Texture
used to push that buffer onto the window. All three (buffer, Skia surface,
SDL texture) get reallocated when the window resizes. */
class SkiaSurfaceBridge(private val backend: SDL3Backend) : SkiaBridge {
private var fWidth = 0
private var fHeight = 0
private var fPixels: CPointer<UByteVar>? = null
private var fSurface: Surface? = null
private var fTexture: COpaquePointer? = null
internal class SkiaSurfaceBridge(private val backend: Sdl3Backend) : SkiaBridge {
private var mWidth = 0
private var mHeight = 0
private var mPixels: CPointer<UByteVar>? = null
private var mSurface: Surface? = null
private var mTexture: COpaquePointer? = null
override val canvas: Canvas
get() = requireNotNull(fSurface) { "SkiaSurfaceBridge not initialised" }.canvas
get() = requireNotNull(mSurface) { "SkiaSurfaceBridge not initialised" }.canvas
val width: Int get() = fWidth
val height: Int get() = fHeight
val width: Int get() = mWidth
val height: Int get() = mHeight
override fun ensureSize(inWidth: Int, inHeight: Int): Boolean {
if (inWidth == fWidth && inHeight == fHeight && fSurface != null) return true
if (inWidth == mWidth && inHeight == mHeight && mSurface != null) return true
if (inWidth <= 0 || inHeight <= 0) return false
destroy()
@@ -60,34 +60,34 @@ class SkiaSurfaceBridge(private val backend: SDL3Backend) : SkiaBridge {
}
SDL_SetTextureBlendMode(vTexture.reinterpret(), SDL_BLENDMODE_NONE)
fPixels = vPixels
fSurface = vSurface
fTexture = vTexture
fWidth = inWidth
fHeight = inHeight
mPixels = vPixels
mSurface = vSurface
mTexture = vTexture
mWidth = inWidth
mHeight = inHeight
return true
}
/** Skia → SDL_Texture → screen, once per frame. */
override fun present() {
val vRenderer = backend.renderer ?: return
val vTexture = fTexture ?: return
val vSurface = fSurface ?: return
val vPixels = fPixels ?: return
val vTexture = mTexture ?: return
val vSurface = mSurface ?: return
val vPixels = mPixels ?: return
vSurface.flushAndSubmit()
SDL_UpdateTexture(vTexture.reinterpret(), null, vPixels, fWidth * 4)
SDL_UpdateTexture(vTexture.reinterpret(), null, vPixels, mWidth * 4)
SDL_RenderTexture(vRenderer.reinterpret(), vTexture.reinterpret(), null, null)
SDL_RenderPresent(vRenderer.reinterpret())
}
override fun snapshot(): Image? = fSurface?.makeImageSnapshot()
override fun snapshot(): Image? = mSurface?.makeImageSnapshot()
/** CPU bridge already has the raw pixels in fPixels (RGBA8888 premul).
/** CPU bridge already has the raw pixels in mPixels (RGBA8888 premul).
Repack to BGRA so we share one BMP writer with the GPU bridges. */
override fun snapshotBgra(): Triple<Int, Int, ByteArray>? {
val vPixels = fPixels ?: return null
val vCount = fWidth * fHeight
val vPixels = mPixels ?: return null
val vCount = mWidth * mHeight
val vBytes = ByteArray(vCount * 4)
for (i in 0 until vCount) {
val vR = vPixels[i * 4 + 0]
@@ -99,17 +99,17 @@ class SkiaSurfaceBridge(private val backend: SDL3Backend) : SkiaBridge {
vBytes[i * 4 + 2] = vR.toByte()
vBytes[i * 4 + 3] = vA.toByte()
}
return Triple(fWidth, fHeight, vBytes)
return Triple(mWidth, mHeight, vBytes)
}
override fun destroy() {
fTexture?.let { SDL_DestroyTexture(it.reinterpret()) }
fSurface?.close()
fPixels?.let { nativeHeap.free(it) }
fTexture = null
fSurface = null
fPixels = null
fWidth = 0
fHeight = 0
mTexture?.let { SDL_DestroyTexture(it.reinterpret()) }
mSurface?.close()
mPixels?.let { nativeHeap.free(it) }
mTexture = null
mSurface = null
mPixels = null
mWidth = 0
mHeight = 0
}
}
@@ -6,7 +6,7 @@ import com.compose.sdl.renderer.skia.SkiaBridge
// MARK: Windows (mingwX64) GPU defaults
// ==================
// No Metal on Windows. The default Skia bridge is CPU raster (GpuMode.Software →
// No Metal on Windows. The default Skia bridge is CPU raster (GpuMode.CpuRaster →
// SkiaSurfaceBridge), so no GPU-context bridge is provided here yet. A D3D/Vulkan
// bridge can be added later (Route 1a milestone 2).
internal actual fun makeMetalBridge(backend: SDL3Backend): SkiaBridge? = null
internal actual fun makeMetalBridge(backend: Sdl3Backend): SkiaBridge? = null
+10 -1
View File
@@ -45,6 +45,7 @@ kotlin {
dependencies {
val vComposeJvmVersion = libs.versions.compose.get()
val vComposeM3JvmVersion = libs.versions.composeMaterial3.get()
val vAdaptiveJvmVersion = libs.versions.composeMaterial3Adaptive.get()
// Official Maven coords for everything the shared UI touches; native
// configurations substitute them for the port modules automatically
@@ -64,13 +65,21 @@ kotlin {
implementation("org.jetbrains.compose.components:components-resources:${vComposeJvmVersion}")
implementation("org.jetbrains.compose.material3:material3:$vComposeM3JvmVersion")
implementation("org.jetbrains.compose.material3.adaptive:adaptive:$vAdaptiveJvmVersion")
implementation("org.jetbrains.compose.material3.adaptive:adaptive-layout:$vAdaptiveJvmVersion")
implementation("org.jetbrains.compose.material3.adaptive:adaptive-navigation:$vAdaptiveJvmVersion")
// adaptive api-exposes window-core (WindowSizeClass). Because `adaptive`
// is SUBSTITUTED on native configs, KGP's granular-metadata visibility
// check drops its transitives from the commonMain classpath - so the
// shared screen only sees WindowSizeClass if we declare it DIRECTLY.
implementation(libs.androidx.window.core)
// Common Material Symbols Utility
implementation(project(":utils:material-symbols"))
// nav3 + lifecycle: real KMP Maven artifacts on every target
implementation(libs.androidx.navigation3.runtime)
implementation("org.jetbrains.androidx.navigation3:navigation3-ui:1.2.0-alpha02")
implementation("org.jetbrains.androidx.navigation3:navigation3-ui:1.1.2")
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
}
}
@@ -81,6 +81,7 @@ val Material3Screens: List<DemoScreen> = listOf(
DemoScreen("Pickers") { M3PickersScreen() },
DemoScreen("Carousel") { M3CarouselScreen() },
DemoScreen("M3Misc") { M3MiscScreen() },
DemoScreen("Adaptive") { AdaptiveScreen() },
)
val Navigation3Screens: List<DemoScreen> = listOf(
@@ -0,0 +1,241 @@
package screens
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.Posture
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowDpSize
import androidx.compose.material3.adaptive.layout.AnimatedPane
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffold
import androidx.compose.material3.adaptive.layout.ListDetailPaneScaffoldRole
import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirective
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.window.core.layout.WindowSizeClass
import androidx.window.core.layout.WindowSizeClass.Companion.HEIGHT_DP_EXPANDED_LOWER_BOUND
import androidx.window.core.layout.WindowSizeClass.Companion.HEIGHT_DP_MEDIUM_LOWER_BOUND
import androidx.window.core.layout.WindowSizeClass.Companion.WIDTH_DP_EXPANDED_LOWER_BOUND
import androidx.window.core.layout.WindowSizeClass.Companion.WIDTH_DP_MEDIUM_LOWER_BOUND
import kotlinx.coroutines.launch
/**
Probe seam: whether [AdaptiveScreen] last laid out two panes or one. The native
`--adaptivetest` probe resizes the window and asserts this flips, which is the
only way to prove the fold reacts to a LIVE resize rather than just to the size
the window happened to open at. Not part of the demo UI.
*/
internal var gAdaptiveTwoPane: Boolean = false
private set
/**
material3-adaptive running on desktop native - resize the window and watch it fold.
Upstream `org.jetbrains.compose.material3.adaptive:*` publishes ios + macosArm64
only, so this screen doubles as the runtime proof that the vendored
`:compose:material3:adaptive:*` modules work on linux and mingw too. It exercises
three of the four modules: `adaptive` ([currentWindowAdaptiveInfo]),
`adaptive-layout` ([ListDetailPaneScaffold]) and `adaptive-navigation` (the
navigator that drives the fold).
**Why the directive is computed here instead of being left to default.**
[rememberListDetailPaneScaffoldNavigator] defaults its directive to the WINDOW
size class, but this screen does not get the whole window - the demo shell spends
190dp on the sidebar plus 48dp of padding. Defaulting would fold ~240dp too late:
the panes would still be side by side in a region already too cramped for them.
So the size class is computed from the width this screen actually gets
([BoxWithConstraints]), which is what a real pane-hosting app should do too. The
header prints both numbers so the difference is visible while resizing.
*/
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun AdaptiveScreen() {
val vItems = remember { List(24) { "Item ${it + 1}" } }
var vSelected by remember { mutableStateOf(vItems.first()) }
val vScope = rememberCoroutineScope()
val vWindowDpSize = currentWindowDpSize()
val vWindowClass = currentWindowAdaptiveInfo().windowSizeClass
Column(
Modifier.fillMaxWidth().padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text("material3-adaptive", style = MaterialTheme.typography.titleLarge)
BoxWithConstraints(Modifier.fillMaxWidth()) {
// The space THIS screen was handed, not the window. maxHeight is
// infinite here (the shell hosts every screen in a verticalScroll), so
// the pane height is derived from the window instead - see below.
val vPaneWidth = maxWidth
val vPaneHeight = (vWindowDpSize.height - 200.dp).coerceIn(260.dp, 760.dp)
val vPaneClass = sizeClassFor(vPaneWidth, vPaneHeight)
val vNavigator = rememberListDetailPaneScaffoldNavigator<Nothing>(
scaffoldDirective = calculatePaneScaffoldDirective(
WindowAdaptiveInfo(vPaneClass, Posture()),
),
)
val vTwoPane = vNavigator.scaffoldDirective.maxHorizontalPartitions > 1
SideEffect { gAdaptiveTwoPane = vTwoPane }
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
ReadoutCard(
vWindowLine = "window ${vWindowDpSize.width.value.toInt()}x" +
"${vWindowDpSize.height.value.toInt()}dp -> " +
"minWidth ${vWindowClass.minWidthDp}dp",
vPaneLine = "this screen ${vPaneWidth.value.toInt()}x" +
"${vPaneHeight.value.toInt()}dp -> " +
"minWidth ${vPaneClass.minWidthDp}dp",
vVerdict = if (vTwoPane) {
"Wide enough: list and detail sit side by side. " +
"Narrow the window to fold them."
} else {
"Folded: one pane at a time. Pick an item to open the " +
"detail, then Back. Widen the window to unfold."
},
)
ListDetailPaneScaffold(
modifier = Modifier.fillMaxWidth().height(vPaneHeight),
directive = vNavigator.scaffoldDirective,
value = vNavigator.scaffoldValue,
listPane = {
AnimatedPane {
Card(Modifier.fillMaxSize()) {
LazyColumn {
items(vItems) { vItem ->
val vIsSelected = vItem == vSelected
ListItem(
headlineContent = { Text(vItem) },
colors = if (vIsSelected) {
ListItemDefaults.colors(
containerColor =
MaterialTheme.colorScheme.secondaryContainer,
)
} else {
ListItemDefaults.colors()
},
modifier = Modifier.fillMaxWidth().clickable {
vSelected = vItem
vScope.launch {
vNavigator.navigateTo(
ListDetailPaneScaffoldRole.Detail,
)
}
},
)
}
}
}
}
},
detailPane = {
AnimatedPane {
Card(Modifier.fillMaxSize()) {
Column(
Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
vSelected,
style = MaterialTheme.typography.headlineSmall,
)
Text(
if (vTwoPane) {
"Detail pane, side by side with the list " +
"because there is room for both."
} else {
"Detail pane, filling the scaffold because " +
"there is only room for one."
},
style = MaterialTheme.typography.bodyMedium,
)
// Only reachable when folded - with both panes up
// there is nothing to go back to.
if (vNavigator.canNavigateBack()) {
OutlinedButton(
onClick = {
vScope.launch { vNavigator.navigateBack() }
},
) { Text("Back to list") }
}
}
}
}
},
)
}
}
}
}
/** The live size readout above the scaffold - the whole point of the screen is
watching these three lines change as the window is dragged. */
@Composable
private fun ReadoutCard(vWindowLine: String, vPaneLine: String, vVerdict: String) {
Card(
Modifier.fillMaxWidth(),
colors = CardDefaults.outlinedCardColors(),
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant),
) {
Column(
Modifier.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(2.dp),
) {
Text(vWindowLine, style = MaterialTheme.typography.bodySmall)
Text(vPaneLine, style = MaterialTheme.typography.bodySmall)
Text(
vVerdict,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.primary,
)
}
}
}
/**
The [WindowSizeClass] for an arbitrary region.
window-core only offers this for the window itself; the breakpoint constants are
public, so applying them to a sub-region is just a table lookup.
*/
private fun sizeClassFor(inWidth: Dp, inHeight: Dp): WindowSizeClass {
val vMinWidth = when {
inWidth.value >= WIDTH_DP_EXPANDED_LOWER_BOUND -> WIDTH_DP_EXPANDED_LOWER_BOUND
inWidth.value >= WIDTH_DP_MEDIUM_LOWER_BOUND -> WIDTH_DP_MEDIUM_LOWER_BOUND
else -> 0
}
val vMinHeight = when {
inHeight.value >= HEIGHT_DP_EXPANDED_LOWER_BOUND -> HEIGHT_DP_EXPANDED_LOWER_BOUND
inHeight.value >= HEIGHT_DP_MEDIUM_LOWER_BOUND -> HEIGHT_DP_MEDIUM_LOWER_BOUND
else -> 0
}
return WindowSizeClass(vMinWidth, vMinHeight)
}
@@ -36,7 +36,7 @@ internal fun AnimationScreen() {
"animation.core",
"animate*AsState, AnimationSpec (tween / spring / repeatable / infiniteRepeatable), " +
"Easing (Linear, FastOutSlowIn, CubicBezier), rememberInfiniteTransition. " +
"All driven by the SDL3FrameClock via withFrameNanos.",
"All driven by the Sdl3FrameClock via withFrameNanos.",
)
// ============
@@ -31,7 +31,7 @@ internal fun GestureScreen() {
ScreenTitle(
"pointerInput + gesture DSL",
"Suspending Modifier.pointerInput { } with detectTapGestures / detectDragGestures " +
"on top. Awaits resume on the SDL3FrameClock; no per-event polling.",
"on top. Awaits resume on the Sdl3FrameClock; no per-event polling.",
)
// ============
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,81 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.Modifier
import com.compose.sdl.ComposeNativeWindow
import com.compose.sdl.nativeComposeWindow
/**
`--winattrtest`: opens a window with the non-default Compose Desktop attributes
set, then checks four things and exits -
1. the attributes reach SDL and read back off the facade,
2. the raw SDL escape hatch hands out the pointers the resolved renderer owns,
3. onPreviewKeyEvent sees a key BEFORE the focused content does,
4. `enabled = false` drops input entirely.
*/
fun runWindowAttrTest() {
var vPreviewSaw = 0
var vKeySaw = 0
var vFacade: ComposeNativeWindow? = null
var vFrames = 0
nativeComposeWindow(
title = "attr probe",
width = 420,
height = 240,
undecorated = true,
resizable = false,
alwaysOnTop = true,
focusable = true,
enabled = true,
// Returning false lets the event continue down the chain, so this also
// proves preview runs first without swallowing anything.
onPreviewKeyEvent = { vPreviewSaw++; false },
onKeyEvent = { vKeySaw++; false },
onFrame = { _, _ ->
vFrames++
// Let the first frames settle so SDL has applied everything.
if (vFrames < 3) return@nativeComposeWindow true
if (vFrames == 3) {
com.compose.sdl.injectKey(41, true) // Escape down
com.compose.sdl.injectKey(41, false)
return@nativeComposeWindow true
}
if (vFrames == 4) {
// Nothing is focused, so the content declines and BOTH window
// handlers must have seen the key.
println("KEYCHAIN preview=$vPreviewSaw onKey=$vKeySaw")
// Now disable the window and inject again: neither may fire.
vFacade?.setEnabled(false)
com.compose.sdl.injectKey(41, true)
com.compose.sdl.injectKey(41, false)
return@nativeComposeWindow true
}
if (vFrames == 5) {
println("DISABLED preview=$vPreviewSaw onKey=$vKeySaw (must be unchanged)")
vFacade?.setEnabled(true)
}
val vW = vFacade
if (vW == null) {
println("ATTR FAIL: window facade never captured")
} else {
println(
"ATTR undecorated=${vW.isUndecorated} resizable=${vW.isResizable} " +
"alwaysOnTop=${vW.isAlwaysOnTop} focusable=${vW.isFocusable} " +
"enabled=${vW.isEnabled}"
)
val vRaw = vW.rawSdlHandles()
println(
"RAW gpu=${vW.gpuMode} window=${vRaw.window != null} " +
"renderer=${vRaw.renderer != null} glContext=${vRaw.glContext != null} " +
"metalView=${vRaw.metalView != null}"
)
}
false
},
) {
SideEffect { vFacade = window }
Box(Modifier.fillMaxSize()) { Text("attr probe") }
}
}
@@ -0,0 +1,91 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.compose.sdl.ComposeNativeWindow
import com.compose.sdl.nativeComposeWindow
import screens.AdaptiveScreen
import screens.gAdaptiveTwoPane
/**
`--adaptivetest`: proves material3-adaptive reacts to a LIVE window resize.
The `--screen=Adaptive --width=…` screenshots only ever prove the fold is right
for the size the window OPENED at - each run is a fresh process. This drives one
window through wide → narrow → wide with `setSize`, and asserts the scaffold
folds and unfolds along the way. That exercises the whole chain: SDL resize →
the owner's snapshot-backed containerSize → recomposition → `BoxWithConstraints`
re-measure → a new `PaneScaffoldDirective` on the navigator → a new scaffold value.
The screen is hosted in the same `verticalScroll` + padding wrapper the demo
shell uses for its content pane, so the constraints match the real thing
(notably the infinite max height).
*/
internal fun runAdaptiveTest() {
var vFrames = 0
var vFacade: ComposeNativeWindow? = null
var vWide: Boolean? = null
var vFolded: Boolean? = null
var vUnfolded: Boolean? = null
// Frames to settle between each resize - SDL delivers the size change as an
// event, so the new constraints only reach layout on a later frame.
val kSettle = 6
nativeComposeWindow(
title = "adaptive probe",
width = 1400,
height = 760,
onFrame = { _, _ ->
vFrames++
when (vFrames) {
kSettle -> {
vWide = gAdaptiveTwoPane
vFacade?.setSize(760, 760)
true
}
kSettle * 2 -> {
vFolded = gAdaptiveTwoPane
vFacade?.setSize(1400, 760)
true
}
kSettle * 3 -> {
vUnfolded = gAdaptiveTwoPane
val vPass = vWide == true && vFolded == false && vUnfolded == true
println("adaptivetest: wide=$vWide folded=$vFolded unfolded=$vUnfolded")
println(
if (vPass) {
"adaptivetest: PASS (scaffold folded on resize to 760dp " +
"and unfolded again at 1400dp)"
} else {
"adaptivetest: FAIL (expected wide=true folded=false " +
"unfolded=true)"
}
)
false
}
else -> true
}
},
) {
SideEffect { vFacade = window }
MaterialTheme(colorScheme = darkColorScheme()) {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.verticalScroll(rememberScrollState())
.padding(24.dp),
) {
AdaptiveScreen()
}
}
}
}
@@ -0,0 +1,207 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.unit.dp
import com.compose.sdl.nativeComposeWindow
import utils.encodeBmpBgra32
import utils.writeFile
// ==================
// MARK: Animation probes - shared transitions, AnimatedVisibility, dialog entry.
// ==================
//
// Split out of MainNative.kt, which held the entry point plus 20 scenarios in
// 1240 lines. Behaviour is unchanged; each probe is `internal` so main()'s
// argument dispatch still reaches it.
/** Drives a SharedTransitionLayout shared-element morph both directions from
the frame counter (no clicks) - it requires the LOOKAHEAD pass, which dies
with "LookaheadDelegate has not been measured yet" if the owner drops
affectsLookahead measure/relayout requests. Reaching the end frame = PASS. */
@OptIn(androidx.compose.animation.ExperimentalSharedTransitionApi::class)
internal fun runSharedTest() {
val vBig = mutableStateOf(false)
nativeComposeWindow(
title = "sharedtest",
width = 300,
height = 300,
onFrame = { _, vFrame ->
when (vFrame) {
20 -> { vBig.value = true; true } // small → big morph
70 -> { vBig.value = false; true } // big → small morph
130 -> {
println("sharedtest: PASS (shared-element morph ran both directions)")
false
}
else -> true
}
},
) {
MaterialTheme(colorScheme = darkColorScheme()) {
androidx.compose.animation.SharedTransitionLayout {
Column {
androidx.compose.animation.AnimatedVisibility(visible = !vBig.value) {
Box(
modifier = Modifier
.sharedElement(
sharedContentState = rememberSharedContentState(key = "box"),
animatedVisibilityScope = this@AnimatedVisibility,
)
.size(48.dp)
.background(Color(0xFF7C4DFF), RoundedCornerShape(8.dp)),
)
}
androidx.compose.animation.AnimatedVisibility(visible = vBig.value) {
Box(
modifier = Modifier
.sharedElement(
sharedContentState = rememberSharedContentState(key = "box"),
animatedVisibilityScope = this@AnimatedVisibility,
)
.size(160.dp)
.background(Color(0xFF26A69A), RoundedCornerShape(24.dp)),
)
}
}
}
}
}
}
/** Frame-by-frame trace of AnimatedVisibility(fadeIn+expandVertically / fadeOut+
shrinkVertically): logs the AV container's animated size and the window-Y of a
marker Box below it every frame, toggling visibility three times. Diagnoses
(a) a size snap between just-before-end and end of the animation and (b)
instant (non-animated) transitions on subsequent toggles.
Expected healthy trace: ~24 smooth frames per toggle ending exactly at 0/60,
PLUS one 16px marker jump when the fully-shrunk node unmounts (exit end) or
mounts (enter start) - that's the parent's spacedBy(16) collapsing, inherent
upstream behaviour (spacing applies to zero-height children too), NOT a bug. */
internal fun runAnimVisTest() {
val vShown = mutableStateOf(true)
var vAvSize = androidx.compose.ui.unit.IntSize(-1, -1)
var vMarkerY = -1f
var vLastLog = ""
nativeComposeWindow(
title = "animvistest",
width = 600,
height = 500,
onFrame = { _, vFrame ->
// Log only when something moved - keeps the trace readable.
val vLine = "size=$vAvSize markerY=$vMarkerY shown=${vShown.value}"
if (vLine != vLastLog) {
println("animvistest: f=$vFrame $vLine")
vLastLog = vLine
}
when (vFrame) {
60 -> { println("animvistest: === HIDE 1 ==="); vShown.value = false; true }
140 -> { println("animvistest: === SHOW 2 ==="); vShown.value = true; true }
220 -> { println("animvistest: === HIDE 3 ==="); vShown.value = false; true }
300 -> false
else -> true
}
},
) {
MaterialTheme(colorScheme = darkColorScheme()) {
// spacedBy mirrors the demo's Section column - the end-of-exit jump
// reported on the FoundationExtra screen involves the spacing around
// the AnimatedVisibility node collapsing when the node unmounts.
Column(
modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Box(Modifier.size(30.dp).background(androidx.compose.ui.graphics.Color(0xFFC07040)))
androidx.compose.animation.AnimatedVisibility(
visible = vShown.value,
modifier = Modifier.onSizeChanged { vAvSize = it },
enter = androidx.compose.animation.fadeIn() + androidx.compose.animation.expandVertically(),
exit = androidx.compose.animation.fadeOut() + androidx.compose.animation.shrinkVertically(),
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.background(androidx.compose.ui.graphics.Color(0xFF7040C0)),
) {
Text("Animated content", modifier = Modifier.padding(14.dp))
}
}
Box(
modifier = Modifier
.size(20.dp)
.background(androidx.compose.ui.graphics.Color(0xFF40C070))
.onGloballyPositioned { vMarkerY = it.positionInRoot().y },
)
}
}
}
}
/** Boots a window whose full-size surface opens an m3 AlertDialog on click (the real
material3 → ui.window.Dialog path, default DialogProperties ⇒ animateTransition on),
injects the click, then dumps screenshots during the 0.2s appearance animation and
after it settles; then injects Escape and dumps mid-disappearance (0.1s reverse,
via the popup host's exit deferral) + after. Visual check: mid shots must show the
dialog semi-transparent, slightly scaled-down and shifted down vs the settled shot,
and exit_end must show no dialog at all - skiko-parity animation both ways. */
internal fun runDialogAnimTest() {
fun snap(inBridge: com.compose.sdl.RenderBackend, inName: String) {
val vSnap = inBridge.snapshotBgra() ?: return
val (vW, vH, vBgra) = vSnap
writeFile(inName, encodeBmpBgra32(vW, vH, vBgra))
println("dialoganimtest: wrote $inName")
}
nativeComposeWindow(
title = "dialoganimtest",
width = 1000,
height = 700,
onFrame = { vBridge, vFrame ->
when (vFrame) {
30 -> { com.compose.sdl.injectMouseEvent(1, 500f, 350f); true }
32 -> { com.compose.sdl.injectMouseEvent(2, 500f, 350f); true }
36 -> { snap(vBridge, "dialog_mid1.bmp"); true }
40 -> { snap(vBridge, "dialog_mid2.bmp"); true }
90 -> { snap(vBridge, "dialog_end.bmp"); true }
92 -> {
com.compose.sdl.injectKey(41, true) // Escape → dismiss
com.compose.sdl.injectKey(41, false)
true
}
97 -> { snap(vBridge, "dialog_exit_mid.bmp"); true }
140 -> { snap(vBridge, "dialog_exit_end.bmp"); false }
else -> true
}
},
) {
MaterialTheme(colorScheme = darkColorScheme()) {
var vShow by remember { mutableStateOf(false) }
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.clickable { vShow = true },
)
if (vShow) {
androidx.compose.material3.AlertDialog(
onDismissRequest = { vShow = false },
title = { Text("Animated dialog") },
text = { Text("Appearance animation parity with the JVM (skiko) Dialog.") },
confirmButton = {
androidx.compose.material3.TextButton(onClick = { vShow = false }) { Text("OK") }
},
)
}
}
}
}
@@ -0,0 +1,258 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.compose.sdl.nativeComposeWindow
// ==================
// MARK: Input probes - pointer, keyboard, wheel, cursor and IME.
// ==================
//
// Split out of MainNative.kt, which held the entry point plus 20 scenarios in
// 1240 lines. Behaviour is unchanged; each probe is `internal` so main()'s
// argument dispatch still reaches it.
/** Boots a real nativeComposeWindow with a full-size clickable box, then injects
move→press→release SDL mouse events a few frames apart (giving the upstream
clickable's gesture coroutine time to launch + await between frames), and prints
PASS/FAIL based on whether onClick fired. Proves the whole vendored interaction
pipeline works under the real Sdl3MainDispatcher + frame loop. */
internal fun runClickTest() {
var vClicks = 0
nativeComposeWindow(
title = "clicktest",
width = 400,
height = 300,
onFrame = { _, frameIndex ->
when (frameIndex) {
20 -> { com.compose.sdl.injectMouseEvent(0, 200f, 150f); true }
26 -> { com.compose.sdl.injectMouseEvent(1, 200f, 150f); true }
32 -> { com.compose.sdl.injectMouseEvent(2, 200f, 150f); true }
70 -> {
println(
if (vClicks > 0) "clicktest: PASS ($vClicks click(s) via upstream clickable)"
else "clicktest: FAIL (0 clicks - upstream clickable did not fire)"
)
false
}
else -> true
}
},
) {
MaterialTheme(colorScheme = darkColorScheme()) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFF303040))
.clickable { vClicks++; println("clicktest: onClick fired -> $vClicks") },
) {
Text("Click test", color = Color.White, fontSize = 16.sp)
}
}
}
}
/** Same injection harness as clicktest, but the target is a Material Switch (Modifier.toggleable
from the vendored foundation.selection). Asserts onCheckedChange flipped the state - proving
toggleable rides the same verified upstream interaction path as clickable. */
internal fun runToggleTest() {
var vChecked = false
var vChanges = 0
nativeComposeWindow(
title = "toggletest",
width = 400,
height = 300,
onFrame = { _, frameIndex ->
when (frameIndex) {
20 -> { com.compose.sdl.injectMouseEvent(0, 200f, 150f); true }
26 -> { com.compose.sdl.injectMouseEvent(1, 200f, 150f); true }
32 -> { com.compose.sdl.injectMouseEvent(2, 200f, 150f); true }
70 -> {
println(
if (vChanges > 0 && vChecked) "toggletest: PASS (switch toggled to $vChecked via toggleable)"
else "toggletest: FAIL (changes=$vChanges checked=$vChecked)"
)
false
}
else -> true
}
},
) {
MaterialTheme(colorScheme = darkColorScheme()) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
androidx.compose.material3.Switch(
checked = vChecked,
onCheckedChange = { vChecked = it; vChanges++; println("toggletest: onCheckedChange -> $it") },
)
}
}
}
}
/** Boots a window with a real BasicTextField, clicks it to focus (focus-on-click via the
FocusOwner), then injects TEXT_INPUT ("A","B") and a Backspace key through the live SDL path.
Asserts the field edits to "A" - proving click-to-focus + typing + editing keys route to the
focused field via ComposeRootHost.dispatchKeyEvent + the synthesised typed-key path. */
internal fun runKeyTest() {
val vText = mutableStateOf("")
nativeComposeWindow(
title = "keytest",
width = 400,
height = 200,
onFrame = { _, frameIndex ->
when (frameIndex) {
12 -> { com.compose.sdl.injectMouseEvent(1, 200f, 100f); true } // click field to focus
14 -> { com.compose.sdl.injectMouseEvent(2, 200f, 100f); true }
24 -> { com.compose.sdl.injectTextInput("A"); true }
28 -> { com.compose.sdl.injectTextInput("B"); true }
32 -> { com.compose.sdl.injectKey(42, true); com.compose.sdl.injectKey(42, false); true } // Backspace → "A"
70 -> {
println("keytest: real BasicTextField value='${vText.value}'")
println(
if (vText.value == "A") "keytest: PASS (click-to-focus + type 'AB' + backspace = 'A')"
else "keytest: FAIL (expected 'A')"
)
false
}
else -> true
}
},
) {
// The exact regression path: a real BasicTextField, focused by clicking it,
// receiving typed text (SDL TEXT_INPUT) + editing keys (Backspace).
MaterialTheme(colorScheme = darkColorScheme()) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
androidx.compose.foundation.text.BasicTextField(
value = vText.value,
onValueChange = { vText.value = it },
)
}
}
}
}
/** Boots a tall Column wrapped in the vendored Modifier.verticalScroll, injects several wheel-down
events through the live SDL path (→ processor → MouseWheelScrollingLogic), and asserts the
ScrollState offset advanced - proving upstream scrolling works end-to-end. */
internal fun runScrollTest() {
val vScroll = androidx.compose.foundation.ScrollState(0)
nativeComposeWindow(
title = "scrolltest",
width = 400,
height = 300,
onFrame = { _, frameIndex ->
when {
frameIndex in 20..40 && frameIndex % 2 == 0 -> {
com.compose.sdl.injectWheel(200f, 150f, 0f, -3f) // wheel down
true
}
frameIndex == 90 -> {
println("scrolltest: ScrollState.value=${vScroll.value} maxValue=${vScroll.maxValue}")
println(
if (vScroll.value > 0) "scrolltest: PASS (scrolled to ${vScroll.value}px)"
else "scrolltest: FAIL (did not scroll)"
)
false
}
else -> true
}
},
) {
MaterialTheme(colorScheme = darkColorScheme()) {
Column(modifier = Modifier.fillMaxSize().verticalScroll(vScroll)) {
repeat(40) { i ->
Text("Scroll row $i - lorem ipsum dolor sit", color = Color.White, fontSize = 16.sp)
}
}
}
}
}
/** --cursortest: injects hover moves over pointerHoverIcon(Text) / (Hand) regions
and a bare background, printing the applied SDL system cursor after each. Proves
PointerIcon -> SDL_SetCursor end-to-end through the live hover pipeline. */
internal fun runCursorTest() {
var vBg = "?"; var vText = "?"; var vHand = "?"
nativeComposeWindow(
title = "cursortest",
width = 400,
height = 400,
onFrame = { _, vFrame ->
when (vFrame) {
10 -> { com.compose.sdl.injectMouseEvent(0, 200f, 300f); true } // background (below both boxes)
14 -> { vBg = com.compose.sdl.appliedCursorName(); true }
20 -> { com.compose.sdl.injectMouseEvent(0, 200f, 50f); true } // Text box
24 -> { vText = com.compose.sdl.appliedCursorName(); true }
30 -> { com.compose.sdl.injectMouseEvent(0, 200f, 150f); true } // Hand box
34 -> {
vHand = com.compose.sdl.appliedCursorName()
println("cursortest: background=$vBg text=$vText hand=$vHand")
val vOk = vBg.endsWith("DEFAULT") && vText.endsWith("TEXT") && vHand.endsWith("POINTER")
println(if (vOk) "cursortest: PASS" else "cursortest: FAIL")
false
}
else -> true
}
},
) {
MaterialTheme(colorScheme = darkColorScheme()) {
Column(Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background)) {
Box(Modifier.fillMaxWidth().height(100.dp).pointerHoverIcon(PointerIcon.Text))
Box(Modifier.fillMaxWidth().height(100.dp).pointerHoverIcon(PointerIcon.Hand))
}
}
}
}
/** --imetest: focuses a BasicTextField, injects an IME composition (SDL TEXT_EDITING
"ni"), then a commit (SDL TEXT_INPUT "に"). Verifies the preedit shows a
composing region and the commit REPLACES it (not appends) - the real IME path. */
internal fun runImeTest() {
val vField = mutableStateOf(androidx.compose.ui.text.input.TextFieldValue(""))
nativeComposeWindow(
title = "imetest",
width = 500,
height = 200,
onFrame = { _, vFrame ->
when (vFrame) {
12 -> { com.compose.sdl.injectMouseEvent(1, 250f, 100f); true } // focus centered field
14 -> { com.compose.sdl.injectMouseEvent(2, 250f, 100f); true }
34 -> { com.compose.sdl.injectTextEditing("ni"); true } // composing (preedit)
40 -> { println("imetest: composing text='${vField.value.text}' composition=${vField.value.composition}"); true }
50 -> { com.compose.sdl.injectTextInput("に"); true } // commit "に"
60 -> {
println("imetest: committed text='${vField.value.text}' composition=${vField.value.composition}")
val vOk = vField.value.text == "に" && vField.value.composition == null
println(if (vOk) "imetest: PASS" else "imetest: FAIL")
false
}
else -> true
}
},
) {
MaterialTheme(colorScheme = darkColorScheme()) {
Box(
modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background),
contentAlignment = Alignment.Center,
) {
androidx.compose.foundation.text.BasicTextField(
value = vField.value,
onValueChange = { vField.value = it },
textStyle = androidx.compose.ui.text.TextStyle(color = Color.White, fontSize = 24.sp),
cursorBrush = androidx.compose.ui.graphics.SolidColor(Color.White),
)
}
}
}
}
@@ -0,0 +1,125 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.compose.sdl.nativeComposeWindow
import utils.encodeBmpBgra32
import utils.writeFile
// ==================
// MARK: Lifecycle probes - back navigation and Navigation3.
// ==================
//
// Split out of MainNative.kt, which held the entry point plus 20 scenarios in
// 1240 lines. Behaviour is unchanged; each probe is `internal` so main()'s
// argument dispatch still reaches it.
/** Boots a window with a BackHandler, injects an Escape key through the live SDL
path, and asserts the handler fired - proving ComposeWindow's
BackNavigationInput drives the NavigationEventDispatcher (the mechanism that
collapses an expanded m3 SearchBar on Escape). */
@OptIn(androidx.compose.ui.ExperimentalComposeUiApi::class)
internal fun runBackTest() {
val vBackNoFocus = mutableStateOf(false)
val vBackFocused = mutableStateOf(false)
val vText = mutableStateOf("")
nativeComposeWindow(
title = "backtest",
width = 400,
height = 200,
onFrame = { _, frameIndex ->
when (frameIndex) {
// Phase 1: Escape with NOTHING focused.
20 -> {
com.compose.sdl.injectKey(41, true) // SDL_SCANCODE_ESCAPE
com.compose.sdl.injectKey(41, false)
true
}
// Phase 2: click the text field to focus it, then Escape -
// the user-facing SearchBar scenario (field focused while
// the back handler should collapse the bar).
30 -> { com.compose.sdl.injectMouseEvent(1, 200f, 100f); true }
32 -> { com.compose.sdl.injectMouseEvent(2, 200f, 100f); true }
44 -> {
com.compose.sdl.injectKey(41, true)
com.compose.sdl.injectKey(41, false)
true
}
70 -> {
println("backtest: noFocus=${vBackNoFocus.value} focused=${vBackFocused.value}")
println(
if (vBackNoFocus.value && vBackFocused.value)
"backtest: PASS (Escape completed back navigation with and without a focused field)"
else
"backtest: FAIL (noFocus=${vBackNoFocus.value} focusedField=${vBackFocused.value})"
)
false
}
else -> true
}
},
) {
@Suppress("DEPRECATION")
androidx.compose.ui.backhandler.BackHandler(enabled = true) {
if (!vBackNoFocus.value) vBackNoFocus.value = true else vBackFocused.value = true
}
MaterialTheme(colorScheme = darkColorScheme()) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
// The NEW state-based field - the one m3 SearchBar's InputField
// uses - so the probe exercises its key handler, not the legacy
// value-based one.
androidx.compose.foundation.text.BasicTextField(
state = androidx.compose.foundation.text.input.rememberTextFieldState(),
)
}
}
}
}
internal fun runNav3Test() {
val vShow = mutableStateOf(false)
nativeComposeWindow(
title = "nav3test",
width = 1000,
height = 700,
onFrame = { vBridge, vFrame ->
when (vFrame) {
30 -> { vShow.value = true; true }
// Push Detail #1 (click its card) → per-entry lifecycle goes
// CREATED/STARTED during the slide, RESUMED once settled.
60 -> { com.compose.sdl.injectMouseEvent(1, 500f, 216f); true }
62 -> { com.compose.sdl.injectMouseEvent(2, 500f, 216f); true }
// Pop with ESC → ON_PAUSE, then CREATED while animating out,
// then the entry disposes.
120 -> {
com.compose.sdl.injectKey(41, true)
com.compose.sdl.injectKey(41, false)
true
}
180 -> {
val vSnap = vBridge.snapshotBgra()
if (vSnap != null) {
val (vW, vH, vBgra) = vSnap
writeFile("nav3late.bmp", encodeBmpBgra32(vW, vH, vBgra))
println("nav3test: wrote nav3late.bmp (${vW}x${vH})")
println("nav3test: PASS (Navigation3 composed at RESUMED; push + pop ran)")
} else println("nav3test: FAIL (no snapshot)")
false
}
else -> true
}
},
) {
MaterialTheme(colorScheme = darkColorScheme()) {
Box(modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background)) {
if (vShow.value) {
Box(modifier = Modifier.padding(24.dp)) { screens.Navigation3Screen() }
}
}
}
}
}
@@ -0,0 +1,151 @@
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.decodeToImageBitmap
import androidx.compose.ui.unit.dp
import com.compose.sdl.nativeComposeWindow
import demo.registry.allCategories
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.toKString
// ==================
// MARK: Renderer probes - the soak run and encoded-image decoding.
// ==================
//
// Split out of MainNative.kt, which held the entry point plus 20 scenarios in
// 1240 lines. Behaviour is unchanged; each probe is `internal` so main()'s
// argument dispatch still reaches it.
/** P2.2 soak - cycle through EVERY registered screen kCycles times in ONE process,
disposing each via a changing key() so composition + layer (skiko RenderNode)
allocate-and-release is exercised repeatedly. After each full cycle, GC then
record peak RSS (getrusage ru_maxrss). Peak RSS is monotonic, so after cycle 1 it
already reflects visiting every screen once; if there is NO leak it plateaus, if
there IS one it keeps climbing each cycle. PASS iff last-cycle peak stays within a
ceiling of the first-cycle peak. */
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class, ExperimentalForeignApi::class)
internal fun runSoakTest() {
// Disable never-settling animations so RSS reflects composition/layer lifetime,
// not live animation-state churn (same seed the screenshot path uses).
com.compose.sdl.disableInfiniteAnimations = true
com.compose.sdl.useVirtualFrameTime = true
val vAll = allCategories().flatMap { it.screens }
// CDN_SOAK_SCREEN=<name> repeats ONE screen 40x/cycle (bisect a specific screen);
// default cycles through every screen.
val vTarget = platform.posix.getenv("CDN_SOAK_SCREEN")?.toKString()
val vScreens = if (vTarget != null) { val vS = vAll.first { it.name.equals(vTarget, ignoreCase = true) }; List(40) { vS } }
else vAll
val vIndex = mutableStateOf(0)
// CDN_SOAK_STATIC=1: mount ONE screen once, never remount - measure RSS every 120 frames.
// Isolates a per-FRAME leak (RSS climbs with no remounts) from a per-MOUNT leak.
val vStatic = platform.posix.getenv("CDN_SOAK_STATIC")?.toKString() == "1"
val kFramesPerScreen = if (vStatic) 120 else 3
val kCycles = platform.posix.getenv("CDN_SOAK_CYCLES")?.toKString()?.toIntOrNull() ?: 3
val vRssPerCycleMb = mutableListOf<Long>()
var vShown = 0
nativeComposeWindow(
title = "soaktest",
width = 1000,
height = 700,
onFrame = { _, vFrame ->
if (vFrame > 0 && vFrame % kFramesPerScreen == 0) {
if (vStatic) {
// No remount - just pump frames on the one mounted screen.
kotlin.native.runtime.GC.collect()
vRssPerCycleMb.add(currentResidentMb())
println("soaktest[static]: measure ${vRssPerCycleMb.size}: currentRSS=${vRssPerCycleMb.last()}MB")
} else {
vShown++
vIndex.value = vShown % vScreens.size
if (vShown % vScreens.size == 0) {
kotlin.native.runtime.GC.collect()
vRssPerCycleMb.add(currentResidentMb())
println("soaktest: cycle ${vRssPerCycleMb.size}: currentRSS=${vRssPerCycleMb.last()}MB")
}
}
}
if (vRssPerCycleMb.size >= kCycles) {
val vFirst = vRssPerCycleMb.first()
val vLast = vRssPerCycleMb.last()
val vGrowth = vLast - vFirst
val vCeiling = maxOf(48L, vFirst / 4) // 25% or 48MB, whichever larger
println("soaktest: currentRSS/cycle(MB)=$vRssPerCycleMb (${vScreens.size} screens x $kCycles cycles)")
if (vGrowth <= vCeiling) {
println("soaktest: PASS (current RSS grew ${vGrowth}MB over cycles 1->$kCycles, within ${vCeiling}MB ceiling)")
} else {
println("soaktest: FAIL (current RSS grew ${vGrowth}MB > ${vCeiling}MB ceiling - possible leak)")
}
false
} else true
},
) {
MaterialTheme(colorScheme = darkColorScheme()) {
val vScroll = rememberScrollState()
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.verticalScroll(vScroll)
.padding(24.dp),
) {
// key(vShown) forces a FULL dispose+recompose each advance (even when the
// target screen repeats) - the allocate/release churn we soak. Reading
// vIndex.value (a State) is what triggers the recomposition each mount.
key(vShown) {
vScreens[vIndex.value].content()
}
}
}
}
}
/** CURRENT resident set (MB): current RSS can DROP after GC, so it distinguishes a
true leak (ratchets up) from K/N allocator high-water, unlike getrusage's
monotonic peak. posix reads it from `ps`; mingw has no `ps`/popen and returns
-1 (the soak gate runs on macOS/Linux - see scripts/verify-mac.sh). */
internal expect fun currentResidentMb(): Long
/** --imagebytestest: decodes an in-memory 2x2 BMP via ByteArray.decodeToImageBitmap()
(routes to createImageBitmap(bytes)) through Skia's image decoder. Needs a window
so the render backend installs the encoded-image decoder. */
internal fun runImageBytesTest() {
nativeComposeWindow(
title = "imagebytestest",
width = 200,
height = 200,
onFrame = { _, vFrame ->
if (vFrame >= 2) {
try {
val vBitmap = tinyRedBmp().decodeToImageBitmap()
println("imagebytestest: decoded ${vBitmap.width}x${vBitmap.height}")
println(if (vBitmap.width == 2 && vBitmap.height == 2) "imagebytestest: PASS" else "imagebytestest: FAIL (wrong size)")
} catch (e: Throwable) {
println("imagebytestest: FAIL (${e::class.simpleName}: ${e.message})")
}
false
} else true
},
) {}
}
// A minimal 2x2 24bpp red BMP (no compression) - valid decoder input.
internal fun tinyRedBmp(): ByteArray {
fun le16(v: Int) = byteArrayOf((v and 0xFF).toByte(), ((v shr 8) and 0xFF).toByte())
fun le32(v: Int) = byteArrayOf(
(v and 0xFF).toByte(), ((v shr 8) and 0xFF).toByte(),
((v shr 16) and 0xFF).toByte(), ((v shr 24) and 0xFF).toByte(),
)
val vPixel = byteArrayOf(0, 0, 0xFF.toByte()) // BGR red
val vRow = vPixel + vPixel + byteArrayOf(0, 0) // 2px + 2 pad = 8 bytes (4-byte aligned)
val vPixels = vRow + vRow // 2 rows = 16 bytes
val vFileHeader = byteArrayOf('B'.code.toByte(), 'M'.code.toByte()) + le32(70) + le32(0) + le32(54)
val vDib = le32(40) + le32(2) + le32(2) + le16(1) + le16(24) + le32(0) + le32(16) +
le32(2835) + le32(2835) + le32(0) + le32(0)
return vFileHeader + vDib + vPixels
}

Some files were not shown because too many files have changed in this diff Show More