mirror of
https://github.com/bitsycore/compose-desktop-native.git
synced 2026-10-05 18:57:26 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4272cec614 | ||
|
|
3f4b3a23be | ||
|
|
d058f1b6a2 | ||
|
|
924ebe9776 | ||
|
|
5dc4b21867 | ||
|
|
accd74c9ee | ||
|
|
30cc39606a | ||
|
|
099211b015 | ||
|
|
37a44ffb46 | ||
|
|
045c734eef | ||
|
|
83c1902b08 | ||
|
|
076d60460c | ||
|
|
93b7ff28e1 | ||
|
|
08968fe820 | ||
|
|
3a57e96c80 | ||
|
|
332ba757fe | ||
|
|
09fe92fc95 | ||
|
|
ec2451aad1 | ||
|
|
ff32d68a72 | ||
|
|
1a56b7dcaf | ||
|
|
7c5abc2705 |
@@ -103,16 +103,13 @@ jobs:
|
|||||||
# intrinsic K/N's LLD can't resolve is aliased to msvcrt's _setjmpex at
|
# intrinsic K/N's LLD can't resolve is aliased to msvcrt's _setjmpex at
|
||||||
# link time via --defsym in sdl3.def.
|
# link time via --defsym in sdl3.def.
|
||||||
|
|
||||||
# Build the static native deps from scripts/build-sdl/build-sdl.properties.
|
# Build the static SDL3 lib (scripts/build-sdl/build-sdl.properties). Every
|
||||||
# Only Windows ships the SDL3 renderer (no Skiko on mingw), so only it needs
|
# target renders through Skia now, so all platforms need only base SDL3 for
|
||||||
# SDL3_ttf / SDL3_image / FreeType (SDL3_ttf text, SDL3_image decode, FreeType
|
# the main loop (windowing / input / audio / filesystem) — the from-scratch
|
||||||
# icon-axis rasterisation). macOS / Linux publish the Skia renderer — Skiko
|
# SDL renderer and its SDL3_ttf / SDL3_image / FreeType deps were removed.
|
||||||
# handles text / images / icons — so they only need base SDL3 for the main
|
# Cached against the manifest + scripts so version bumps invalidate cleanly;
|
||||||
# loop (windowing / input / audio / filesystem). Matches the cinterop guard
|
# bump the `v3` key when the toolchain (runner OS, compiler flavour, K/N
|
||||||
# in compose/ui/ui/build.gradle.kts, which skips those three cinterops on the
|
# sysroot) changes without a scripts/build-sdl/ edit.
|
||||||
# Skia targets. Cached against the manifest + scripts so version bumps
|
|
||||||
# invalidate cleanly; bump the `v3` key when the toolchain (runner OS,
|
|
||||||
# compiler flavour, K/N sysroot) changes without a scripts/build-sdl/ edit.
|
|
||||||
- name: Cache libs/
|
- name: Cache libs/
|
||||||
id: cache-libs
|
id: cache-libs
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
@@ -120,16 +117,10 @@ jobs:
|
|||||||
path: libs
|
path: libs
|
||||||
key: sdl-libs-v3-${{ matrix.host }}-${{ hashFiles('scripts/build-sdl/**') }}
|
key: sdl-libs-v3-${{ matrix.host }}-${{ hashFiles('scripts/build-sdl/**') }}
|
||||||
|
|
||||||
# Windows: full SDL3 renderer stack. macOS / Linux (Skia): base SDL3 only.
|
|
||||||
- name: Build static native deps
|
- name: Build static native deps
|
||||||
if: steps.cache-libs.outputs.cache-hit != 'true'
|
if: steps.cache-libs.outputs.cache-hit != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: python3 scripts/build-sdl/build-all.py
|
||||||
if [ "${{ matrix.host }}" = "windows" ]; then
|
|
||||||
python3 scripts/build-sdl/build-all.py
|
|
||||||
else
|
|
||||||
python3 scripts/build-sdl/build-all.py sdl3
|
|
||||||
fi
|
|
||||||
|
|
||||||
# src/vendor/ trees are gitignored — a fresh checkout has zero vendored
|
# src/vendor/ trees are gitignored — a fresh checkout has zero vendored
|
||||||
# androidx.compose.* files. Sync copies them in from the upstream refs
|
# androidx.compose.* files. Sync copies them in from the upstream refs
|
||||||
@@ -160,16 +151,35 @@ jobs:
|
|||||||
VER="${PUBLISH_VERSION#v}"
|
VER="${PUBLISH_VERSION#v}"
|
||||||
OWNER="${GITHUB_REPOSITORY%%/*}"
|
OWNER="${GITHUB_REPOSITORY%%/*}"
|
||||||
# Keep in sync with the module list in settings.gradle.kts.
|
# Keep in sync with the module list in settings.gradle.kts.
|
||||||
MODULES="ui ui-util ui-geometry ui-unit ui-backhandler ui-tooling-preview animation animation-core animation-graphics foundation foundation-layout material3 material-ripple window material-symbols navigation3-ui components-resources"
|
MODULES="sdl-core ui ui-graphics ui-text ui-util ui-geometry ui-unit ui-backhandler ui-tooling-preview animation animation-core animation-graphics foundation foundation-layout material3 material-ripple desktop-native-window material-symbols navigation3-ui components-resources"
|
||||||
case "${{ matrix.host }}" in
|
case "${{ matrix.host }}" in
|
||||||
macos) SUFFIXES="-macosarm64" ;;
|
macos) SUFFIXES="-macosarm64" ;;
|
||||||
linux) SUFFIXES="-linuxx64 -linuxarm64" ;;
|
linux) SUFFIXES="-linuxx64 -linuxarm64" ;;
|
||||||
windows) SUFFIXES="_ROOT_ -mingwx64" ;;
|
windows) SUFFIXES="_ROOT_ -mingwx64" ;;
|
||||||
esac
|
esac
|
||||||
|
# Published group per module — mirrors the per-area com.bitsycore groups
|
||||||
|
# in root build.gradle.kts. Project-only modules (sdl-core, material-symbols)
|
||||||
|
# fall through to com.bitsycore.compose.sdl; the GitHub Packages Maven
|
||||||
|
# package name is "<group>.<artifact>".
|
||||||
|
group_of() {
|
||||||
|
case "$1" in
|
||||||
|
ui|ui-graphics|ui-text|ui-util|ui-geometry|ui-unit|ui-backhandler|ui-tooling-preview)
|
||||||
|
echo "com.bitsycore.compose.ui" ;;
|
||||||
|
foundation|foundation-layout) echo "com.bitsycore.compose.foundation" ;;
|
||||||
|
animation|animation-core|animation-graphics) echo "com.bitsycore.compose.animation" ;;
|
||||||
|
material3) echo "com.bitsycore.compose.material3" ;;
|
||||||
|
material-ripple) echo "com.bitsycore.compose.material" ;;
|
||||||
|
components-resources) echo "com.bitsycore.compose.components" ;;
|
||||||
|
navigation3-ui) echo "com.bitsycore.navigation3" ;;
|
||||||
|
desktop-native-window) echo "com.bitsycore.compose" ;;
|
||||||
|
*) echo "com.bitsycore.compose.sdl" ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
host_packages() {
|
host_packages() {
|
||||||
for m in $MODULES; do
|
for m in $MODULES; do
|
||||||
|
g=$(group_of "$m")
|
||||||
for s in $SUFFIXES; do
|
for s in $SUFFIXES; do
|
||||||
if [ "$s" = "_ROOT_" ]; then echo "com.bitsycore.compose.sdl.$m"; else echo "com.bitsycore.compose.sdl.$m$s"; fi
|
if [ "$s" = "_ROOT_" ]; then echo "$g.$m"; else echo "$g.$m$s"; fi
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
if [ "${{ matrix.host }}" = "windows" ]; then
|
if [ "${{ matrix.host }}" = "windows" ]; then
|
||||||
@@ -242,8 +252,21 @@ jobs:
|
|||||||
for app in demo apidemo; do
|
for app in demo apidemo; do
|
||||||
src="$app/build/bin/$target_dir/releaseExecutable"
|
src="$app/build/bin/$target_dir/releaseExecutable"
|
||||||
zip_path="$(pwd)/dist/$app-$version-$target_dir.zip"
|
zip_path="$(pwd)/dist/$app-$version-$target_dir.zip"
|
||||||
|
files=("$src/$app.${{ matrix.exe_ext }}" "$src/data.kres")
|
||||||
|
# Windows Skia leg ships two runtime sidecars next to the exe — the
|
||||||
|
# skiko fork DLL and Skia's ICU data (icudtl.dat) — provisioned by the
|
||||||
|
# bridge plugin after linkReleaseExecutableMingwX64. The exe cannot run
|
||||||
|
# without them, so include them (and fail loudly if a provision regressed).
|
||||||
|
if [ "${{ matrix.exe_ext }}" = "exe" ]; then
|
||||||
|
for sidecar in skiko-windows-x64.dll icudtl.dat; do
|
||||||
|
if [ ! -f "$src/$sidecar" ]; then
|
||||||
|
echo "::error::Missing Windows runtime sidecar: $src/$sidecar"; exit 1
|
||||||
|
fi
|
||||||
|
files+=("$src/$sidecar")
|
||||||
|
done
|
||||||
|
fi
|
||||||
python3 -c 'import sys,os,zipfile; z=zipfile.ZipFile(sys.argv[1],"w",zipfile.ZIP_DEFLATED,compresslevel=9); [z.write(f,os.path.basename(f)) for f in sys.argv[2:]]; z.close()' \
|
python3 -c 'import sys,os,zipfile; z=zipfile.ZipFile(sys.argv[1],"w",zipfile.ZIP_DEFLATED,compresslevel=9); [z.write(f,os.path.basename(f)) for f in sys.argv[2:]]; z.close()' \
|
||||||
"$zip_path" "$src/$app.${{ matrix.exe_ext }}" "$src/data.kres"
|
"$zip_path" "${files[@]}"
|
||||||
done
|
done
|
||||||
ls -la dist/
|
ls -la dist/
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,16 @@ context — read it first, then look at the files it points to.
|
|||||||
on SDL3, no JVM. Compiles to native binaries for macOS (arm64), Linux
|
on SDL3, no JVM. Compiles to native binaries for macOS (arm64), Linux
|
||||||
(x64/arm64), Windows (mingwX64).
|
(x64/arm64), Windows (mingwX64).
|
||||||
|
|
||||||
Rendering is pluggable behind one `RenderBackend`:
|
Rendering is **Skia everywhere** behind one `RenderBackend` — Metal / OpenGL
|
||||||
|
/ CPU raster:
|
||||||
|
|
||||||
- **Skia** (via Skiko klibs) on macOS + Linux — Metal / OpenGL / CPU raster.
|
- **macOS + Linux** link the OFFICIAL Skiko klibs from Maven.
|
||||||
- **SDL3** (`SDL3_ttf` + `SDL_RenderGeometry`) on Windows, and on macOS/Linux
|
- **Windows (mingwX64)** links the bitsycore skiko FORK — skiko+Skia compiled
|
||||||
when `-Prenderer=sdl3` is passed.
|
into `skiko-windows-x64.dll` with a flat extern-C surface, bound from K/N via
|
||||||
|
an embedded GNU import lib, published to GitHub Packages as
|
||||||
|
`com.bitsycore.skiko:skiko:0.150.1-mingw.1` (override with
|
||||||
|
`-PskikoMingwVersion`). The runtime DLL is auto-provisioned next to the exe by
|
||||||
|
the bridge plugin (`installWindowsSkiaDll`). See SKIKO-MINGW-FEASIBILITY.md.
|
||||||
|
|
||||||
Windowing, input, audio, filesystem access, and the OS-integration surface
|
Windowing, input, audio, filesystem access, and the OS-integration surface
|
||||||
(file dialogs, clipboard, "open in Finder/Explorer"…) all go through
|
(file dialogs, clipboard, "open in Finder/Explorer"…) all go through
|
||||||
@@ -41,8 +46,11 @@ re-implements the layers on top (`androidx.compose.ui.*`, `.foundation.*`,
|
|||||||
## Module layout
|
## Module layout
|
||||||
|
|
||||||
Library modules mirror upstream Compose Multiplatform's `compose/` tree.
|
Library modules mirror upstream Compose Multiplatform's `compose/` tree.
|
||||||
Only `:window` is the SDL integration layer ("Compose SDL" — SDL3 main loop)
|
The SDL layer is two modules: `:sdl-core` (the NAKED sdl3 cinterop + platform
|
||||||
and lives under `compose/sdl/`.
|
primitives — zero Compose dep, like `skiko`) at `sdl/sdl-core/`, and
|
||||||
|
`:desktop-native-window` (the SDL3 main-loop shell + app entry point) at
|
||||||
|
`compose/desktop/native/window/`. `:ui` depends on `:sdl-core` and its renderer
|
||||||
|
+ SDL↔Compose bridges pick the cinterop from it.
|
||||||
|
|
||||||
One Gradle module per upstream artifact; the directory mirrors the upstream
|
One Gradle module per upstream artifact; the directory mirrors the upstream
|
||||||
`compose/` path, the gradle path is kept short (redirected via `projectDir`).
|
`compose/` path, the gradle path is kept short (redirected via `projectDir`).
|
||||||
@@ -50,10 +58,18 @@ One Gradle module per upstream artifact; the directory mirrors the upstream
|
|||||||
```
|
```
|
||||||
compose/
|
compose/
|
||||||
├── ui/
|
├── ui/
|
||||||
│ ├── ui/ → :ui — androidx.compose.ui.* (ui + ui-graphics + ui-text) +
|
│ ├── ui/ → :ui — androidx.compose.ui.* CORE (Modifier, LayoutNode,
|
||||||
│ │ com.compose.sdl.* — cinterops + BOTH renderer pipelines live
|
│ │ composition, semantics, input, focus) + com.compose.sdl.* —
|
||||||
│ │ here. ui-graphics/ui-text can't split off: their Canvas /
|
│ │ the Skia RenderBackend + GPU bridges + the SDL↔Compose
|
||||||
│ │ Paragraph `expect`s are the renderers' `actual`s (same-module).
|
│ │ bridges (events / clipboard / cursors / window). Depends on
|
||||||
|
│ │ :ui-graphics, :ui-text, :sdl-core. (ui-graphics + ui-text +
|
||||||
|
│ │ the sdl3 cinterop were split OUT — upstream layout.)
|
||||||
|
│ ├── ui-graphics/ → :ui-graphics — androidx.compose.ui.graphics.* + the Skia actuals
|
||||||
|
│ │ (SkiaBackedCanvas/Path/Paint, GraphicsLayer, SkiaImageCache,
|
||||||
|
│ │ painter/image + resource seams). → skiko; SDL-free.
|
||||||
|
│ ├── ui-text/ → :ui-text — androidx.compose.ui.text.* + the skiko text engine
|
||||||
|
│ │ (SkiaParagraph → NativeParagraphOps → SkiaFonts, IconFont,
|
||||||
|
│ │ NamedFont). → :ui-graphics, skiko; SDL-free.
|
||||||
│ ├── ui-util/ → :ui-util — androidx.compose.ui.util.* (+ Experimental/InternalComposeUiApi)
|
│ ├── ui-util/ → :ui-util — androidx.compose.ui.util.* (+ Experimental/InternalComposeUiApi)
|
||||||
│ ├── ui-geometry/ → :ui-geometry — androidx.compose.ui.geometry.*
|
│ ├── ui-geometry/ → :ui-geometry — androidx.compose.ui.geometry.*
|
||||||
│ ├── ui-unit/ → :ui-unit — androidx.compose.ui.unit.*
|
│ ├── ui-unit/ → :ui-unit — androidx.compose.ui.unit.*
|
||||||
@@ -74,16 +90,21 @@ compose/
|
|||||||
│ └── material3/ → :material3 — androidx.compose.material3.*
|
│ └── material3/ → :material3 — androidx.compose.material3.*
|
||||||
├── material/
|
├── material/
|
||||||
│ └── material-ripple/ → :material-ripple — androidx.compose.material.ripple.*
|
│ └── material-ripple/ → :material-ripple — androidx.compose.material.ripple.*
|
||||||
└── sdl/ ("Compose SDL" — project modules, not upstream CMP artifacts)
|
└── desktop/native/window/ → :desktop-native-window — nativeComposeApp { Window(...) {} }
|
||||||
└── window/ → :window — nativeComposeApp { Window(...) {} } multi-window
|
multi-window shell + SDL3 main loop; nativeComposeWindow()
|
||||||
shell + SDL3 main loop; nativeComposeWindow() wrapper
|
wrapper (project app-shell, not an upstream CMP artifact).
|
||||||
|
Published as com.bitsycore.compose:desktop-native-window.
|
||||||
|
|
||||||
|
sdl/
|
||||||
|
└── sdl-core/ → :sdl-core — the NAKED sdl3 cinterop + platform primitives
|
||||||
|
(zero Compose dep, like skiko); the single `sdl3` cinterop
|
||||||
|
lives here. :ui depends on it. com.bitsycore.compose.sdl:sdl-core.
|
||||||
|
|
||||||
utils/
|
utils/
|
||||||
└── material-symbols/ → :material-symbols — codepoints + all three style objects
|
└── material-symbols/ → :material-symbols — codepoints + all three style objects
|
||||||
(Outlined / Rounded / Sharp). COMMON API (usable from
|
(Outlined / Rounded / Sharp). COMMON API (usable from
|
||||||
shared app code) + per-stack actuals: native renders
|
shared app code) + per-stack actuals: native renders
|
||||||
via :foundation IconFontIcon (IconFont handles the
|
via :foundation IconFontIcon (Skia), jvm() via Skiko directly
|
||||||
SDL3/Skia split), jvm() via Skiko directly
|
|
||||||
(Typeface.makeClone per axes — upstream's FontCache
|
(Typeface.makeClone per axes — upstream's FontCache
|
||||||
drops variationSettings from its key). Its commonMain
|
drops variationSettings from its key). Its commonMain
|
||||||
declares official Maven compose coords — the root
|
declares official Maven compose coords — the root
|
||||||
@@ -106,8 +127,8 @@ components/
|
|||||||
no mingwX64/linux klibs. Platform layer is project
|
no mingwX64/linux klibs. Platform layer is project
|
||||||
code: data.kres ResourceReader, pure-Kotlin
|
code: data.kres ResourceReader, pure-Kotlin
|
||||||
DomXmlParser (upstream's is Darwin NSXMLParser),
|
DomXmlParser (upstream's is Darwin NSXMLParser),
|
||||||
SDL3_image decode via the :ui EncodedImageDecoder
|
image decode via the :ui EncodedImageDecoder hook
|
||||||
hook, NamedFont registration, SDL locale/theme env.
|
(Skia), NamedFont registration, SDL locale/theme env.
|
||||||
Apps' JVM targets keep the Maven artifact — the
|
Apps' JVM targets keep the Maven artifact — the
|
||||||
generated Res accessors work against BOTH.
|
generated Res accessors work against BOTH.
|
||||||
|
|
||||||
@@ -173,11 +194,11 @@ scripts/ → vendor-sync + python helper scripts (compose-coverage =
|
|||||||
coverage/fidelity vs upstream, material-symbols generate/subset)
|
coverage/fidelity vs upstream, material-symbols generate/subset)
|
||||||
+ compose-fork/;
|
+ compose-fork/;
|
||||||
scripts/build-sdl/ = static-lib build script (python)
|
scripts/build-sdl/ = static-lib build script (python)
|
||||||
libs/ → gitignored per-host static SDL3 / SDL3_ttf / SDL3_image / FreeType
|
libs/ → gitignored per-host static SDL3 output of
|
||||||
output of scripts/build-sdl/build-all.py on Windows
|
scripts/build-sdl/build-all.py on Windows
|
||||||
```
|
```
|
||||||
|
|
||||||
Module PATHS stay short (`:ui`, `:foundation`, `:window`, …) —
|
Module PATHS stay short (`:ui`, `:foundation`, `:desktop-native-window`, …) —
|
||||||
`settings.gradle.kts` redirects `projectDir` for each so build files across
|
`settings.gradle.kts` redirects `projectDir` for each so build files across
|
||||||
the repo stay terse. `androidx.collection` is a plain Maven dependency
|
the repo stay terse. `androidx.collection` is a plain Maven dependency
|
||||||
(`androidx.collection:collection`), not a module — same as other simple
|
(`androidx.collection:collection`), not a module — same as other simple
|
||||||
@@ -189,23 +210,27 @@ androidx KMP libs.
|
|||||||
:ui ← :animation-core ← :animation ← :foundation ← :material3 ← :demo, :apidemo
|
:ui ← :animation-core ← :animation ← :foundation ← :material3 ← :demo, :apidemo
|
||||||
:ui ← :foundation-layout ←──────────────────────┘ ↑ ↑
|
:ui ← :foundation-layout ←──────────────────────┘ ↑ ↑
|
||||||
:material-ripple ←────────────┘──────────────┘
|
:material-ripple ←────────────┘──────────────┘
|
||||||
:foundation, :animation-core ← :window ; :foundation, :material3 ← :material-symbols
|
:foundation, :animation-core ← :desktop-native-window ; :foundation, :material3 ← :material-symbols
|
||||||
```
|
```
|
||||||
|
|
||||||
All edges are `api`, so a consumer of `:foundation` / `:material3` transitively
|
All edges are `api`, so a consumer of `:foundation` / `:material3` transitively
|
||||||
sees the split modules. Full DAG: `:ui-util → collection`; `:ui-geometry → :ui-util`;
|
sees the split modules. Full DAG: `:ui-util → collection`; `:ui-geometry → :ui-util`;
|
||||||
`:ui-unit → :ui-geometry, :ui-util`; `:ui-backhandler → :ui-util, navigationevent`;
|
`:ui-unit → :ui-geometry, :ui-util`; `:ui-backhandler → :ui-util, navigationevent`;
|
||||||
`:ui → :ui-util, :ui-geometry, :ui-unit, :ui-backhandler`; `:animation-core → :ui`;
|
`:sdl-core → sdl3 cinterop (NAKED — no Compose)`;
|
||||||
|
`:ui-graphics → :ui-geometry, :ui-unit, :ui-util, skiko`;
|
||||||
|
`:ui-text → :ui-graphics, :ui-unit, :ui-util, skiko`;
|
||||||
|
`:ui → :ui-graphics, :ui-text, :sdl-core, :ui-util, :ui-geometry, :ui-unit, :ui-backhandler`; `:animation-core → :ui`;
|
||||||
`:foundation-layout → :ui`; `:animation → :animation-core, :foundation-layout`;
|
`:foundation-layout → :ui`; `:animation → :animation-core, :foundation-layout`;
|
||||||
`:foundation → :animation, :foundation-layout, :animation-core, :ui`;
|
`:foundation → :animation, :foundation-layout, :animation-core, :ui`;
|
||||||
`:material-ripple → :foundation, :animation-core`;
|
`:material-ripple → :foundation, :animation-core`;
|
||||||
`:material3 → :foundation, :material-ripple, :animation-core, :foundation-layout`.
|
`:material3 → :foundation, :material-ripple, :animation-core, :foundation-layout`.
|
||||||
|
|
||||||
`:ui` is the renderer/cinterop hub (the ui-graphics + ui-text `expect`s resolve to
|
`:ui` is the Compose core + the Skia RenderBackend + the SDL↔Compose bridges; it
|
||||||
its SDL/Skia renderer `actual`s). The pure lower artifacts (`:ui-util`,
|
sits on `:ui-graphics` / `:ui-text` (the graphics/text primitives + their skiko
|
||||||
`:ui-geometry`, `:ui-unit`, `:ui-backhandler`) are split out below it. Everything
|
`actual`s, SDL-free) and the naked `:sdl-core` (sdl3 cinterop). The pure lower
|
||||||
above `:ui` can only touch renderer / cinterop internals via its public surface.
|
artifacts (`:ui-util`, `:ui-geometry`, `:ui-unit`, `:ui-backhandler`) are below.
|
||||||
`:window` depends on `:ui` + `:foundation` (needs `LazyList`-style scaffolding to
|
Everything above `:ui` touches renderer internals only via its public surface.
|
||||||
|
`:desktop-native-window` depends on `:ui` + `:foundation` (needs `LazyList`-style scaffolding to
|
||||||
install the popup / scaffold layer at the composition root).
|
install the popup / scaffold layer at the composition root).
|
||||||
|
|
||||||
## Vendoring philosophy — read this before writing any `androidx.compose.*` code
|
## Vendoring philosophy — read this before writing any `androidx.compose.*` code
|
||||||
@@ -222,7 +247,7 @@ compose-multiplatform umbrella repo that `:components-resources` vendors from).
|
|||||||
There is no implicit default — `SET_REPO` is required. `scripts/compose-fork/sync.sh`
|
There is no implicit default — `SET_REPO` is required. `scripts/compose-fork/sync.sh`
|
||||||
walks all manifests and copies each selected file byte-for-byte from the pinned
|
walks all manifests and copies each selected file byte-for-byte from the pinned
|
||||||
checkout (each distinct repo sparse-cloned to `../cmp-ref[-<name>]`) into
|
checkout (each distinct repo sparse-cloned to `../cmp-ref[-<name>]`) into
|
||||||
`<module>/src/vendor/{common,native,skikoRenderer,sdlRenderer}/kotlin/`. The
|
`<module>/src/vendor/{common,native,skikoRenderer}/kotlin/`. The
|
||||||
`src/vendor/` tree is **gitignored** — you don't check it in, you re-sync
|
`src/vendor/` tree is **gitignored** — you don't check it in, you re-sync
|
||||||
on demand.
|
on demand.
|
||||||
|
|
||||||
@@ -247,8 +272,8 @@ Two categories of code live in each module:
|
|||||||
Never hand-edit these. If upstream diverges, adjust the manifest or the
|
Never hand-edit these. If upstream diverges, adjust the manifest or the
|
||||||
pinned ref and re-sync. This is the bulk of the codebase (~1500 files
|
pinned ref and re-sync. This is the bulk of the codebase (~1500 files
|
||||||
across the modules).
|
across the modules).
|
||||||
2. **Project code (in `src/commonMain/`, `src/nativeMain/`, `src/skikoRendererMain/`,
|
2. **Project code (in `src/commonMain/`, `src/nativeMain/`,
|
||||||
`src/sdlRendererMain/`)** — code we author (project actuals, glue between
|
`src/skikoRendererMain/`)** — code we author (project actuals, glue between
|
||||||
Compose and SDL3, project-specific extensions).
|
Compose and SDL3, project-specific extensions).
|
||||||
|
|
||||||
### The 5 rules for adding upstream Compose surface
|
### The 5 rules for adding upstream Compose surface
|
||||||
@@ -288,14 +313,13 @@ Two categories of code live in each module:
|
|||||||
reconciled by hand. This is fine; do it when the edit is small and
|
reconciled by hand. This is fine; do it when the edit is small and
|
||||||
the file is unlikely to churn upstream.
|
the file is unlikely to churn upstream.
|
||||||
|
|
||||||
4. **Skiko-specific things go in `:ui/src/skikoRendererMain/`, with
|
4. **Skiko-specific things go in a `skikoRendererMain` source set.** When
|
||||||
an SDL3 equivalent in `:ui/src/sdlRendererMain/`.** When upstream
|
upstream ships a `.skiko.kt` file that uses Skiko's Canvas / Paragraph / …,
|
||||||
ships a `.skiko.kt` file that uses Skiko's Canvas / Paragraph / …,
|
the `.skiko.kt` variant is fine to vendor into `skikoRendererMain` — graphics
|
||||||
the `.skiko.kt` variant is fine to vendor into `skikoRendererMain`.
|
into `:ui-graphics` (`SkiaBackedCanvas.skiko.kt`, `SkiaImageCache.kt`, …),
|
||||||
Then hand-roll (or minimally vendor) the SDL3 counterpart in
|
text into `:ui-text` (`SkiaParagraphEngine.kt`), the render backend into
|
||||||
`sdlRendererMain` — `SkiaCanvas.kt` ↔ `Sdl3Canvas.kt`,
|
`:ui` (`SkiaRenderBackend.kt`). All native targets attach this source set;
|
||||||
`SkiaTextRenderer.kt` ↔ `Sdl3TextRenderer.kt` (using SDL3_ttf +
|
mingwX64 layers its fork actuals on top under `skikoRendererMingwMain`.
|
||||||
FreeType), `SkiaImageCache.kt` ↔ `Sdl3ImageCache.kt` (using SDL3_image).
|
|
||||||
|
|
||||||
5. **Multi-OS project code goes through SDL3, not hand-rolled per-target
|
5. **Multi-OS project code goes through SDL3, not hand-rolled per-target
|
||||||
ifdefs.** SDL3 already handles the platform differences for filesystem
|
ifdefs.** SDL3 already handles the platform differences for filesystem
|
||||||
@@ -313,52 +337,41 @@ call sites don't care.
|
|||||||
|
|
||||||
## Source-set hierarchy (:ui only)
|
## Source-set hierarchy (:ui only)
|
||||||
|
|
||||||
`:ui` owns cinterops + both renderer pipelines. Its source-set tree:
|
`:ui` owns the Skia RenderBackend + GPU bridges. The SAME source-set tree is used
|
||||||
|
by `:ui-graphics` / `:ui-text` for their skiko `actual`s (only the deps differ);
|
||||||
|
the `sdl3` cinterop lives in `:sdl-core` (see below). `:ui`'s tree:
|
||||||
|
|
||||||
```
|
```
|
||||||
commonMain
|
commonMain
|
||||||
└── nativeMain (vendored .native.kt + project native code)
|
└── nativeMain (vendored .native.kt + project native code)
|
||||||
├── skikoRendererMain (Skia drawing pipeline; Skiko on classpath)
|
├── skikoRendererMain (Skia drawing pipeline; official Skiko on classpath)
|
||||||
│ ├── skikoRendererMacosMain (macOS-only Skia actuals — Metal bridge)
|
│ ├── skikoRendererMacosMain (macOS-only Skia actuals — Metal bridge)
|
||||||
│ └── skikoRendererLinuxMain (Linux-only Skia actuals — OpenGL)
|
│ └── skikoRendererLinuxMain (Linux-only Skia actuals — OpenGL)
|
||||||
│ attached to: macosArm64Main / linuxX64Main / linuxArm64Main
|
│ attached to: macosArm64Main / linuxX64Main / linuxArm64Main.
|
||||||
│ ONLY when the Skia renderer is active for the target.
|
└── skikoRendererMingwSharedMain
|
||||||
└── sdlRendererMain (SDL3 drawing pipeline + TTF/image/FreeType)
|
└── skikoRendererMingwMain (mingwX64-only Skia actuals — the bitsycore
|
||||||
├── sdlRendererMacosMain (macOS-only SDL3 driver hint)
|
skiko FORK: skiko-windows-x64.dll bound via
|
||||||
├── sdlRendererLinuxMain (Linux-only SDL3 driver hint)
|
an embedded GNU import lib; OpenGL context)
|
||||||
└── sdlRendererMingwMain (mingwX64-only SDL3 driver hint)
|
attached to: mingwX64Main.
|
||||||
attached to: mingwX64Main always; macOS/Linux when -Prenderer=sdl3.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`createRenderBackend(…)` + `rendererPreferredGpuMode()` are `expect`s in
|
`createRenderBackend(…)` + `rendererPreferredGpuMode()` are `expect`s in
|
||||||
`:ui`'s nativeMain with `actual`s in BOTH `skikoRendererMain` and
|
`:ui`'s nativeMain with `actual`s in `skikoRendererMain` (shared by every native
|
||||||
`sdlRendererMain` — unambiguous because **only one of the two renderer source
|
target). `rendererPreferredGpuMode()` picks Metal on macOS, OpenGL on
|
||||||
sets is attached to a given target**. (They used to be plain duplicate
|
Linux + Windows, with a Software (CPU raster) auto-fallback if the GPU context
|
||||||
declarations with no expect; that compiled per-target but shared nativeMain
|
fails to come up.
|
||||||
METADATA couldn't see them on a host whose targets span both renderers, which
|
|
||||||
blocked the WINDOWS host from producing :window's KotlinMultiplatform
|
|
||||||
publication — and Windows must publish the root modules, see the publish
|
|
||||||
workflow.) Under `-Prenderer=sdl3`, the `skikoRenderer*` source sets are
|
|
||||||
**not even created**, so Gradle has nothing to warn about and Skiko is never
|
|
||||||
pulled in.
|
|
||||||
|
|
||||||
### Cinterop sibling-dependency gotcha
|
### Cinterop
|
||||||
|
|
||||||
`:ui` owns four cinterops in `src/nativeInterop/cinterop/`:
|
The `sdl3` cinterop lives in the NAKED `:sdl-core` module
|
||||||
`sdl3`, `sdl3_ttf`, `sdl3_image`, `freetype`. The `.def` files for `sdl3_ttf` /
|
(`sdl/sdl-core/src/nativeInterop/cinterop/sdl3.def`: SDL_Window / SDL_Event /
|
||||||
`sdl3_image` carry `depends = sdl3` so their `SDL_Surface` / `SDL_Color`
|
SDL_GetBasePath / clipboard / dialogs / GL+Metal context / SDL_Renderer-for-CPU-
|
||||||
references resolve to the *same* types `sdl3` produces (not duplicates inside
|
raster). `:ui` depends on `:sdl-core` (`api`), so `:ui`'s renderer + SDL↔Compose
|
||||||
`sdl3_image.SDL_Surface`). **Gradle does not automatically add a sibling
|
bridges — and `:desktop-native-window` downstream — see `sdl3.*` and inherit SDL3's static-lib +
|
||||||
cinterop's klib to a cinterop task's `-library` list**, so the manifest
|
linker-opt propagation (the `.def` bakes in `staticLibraries = libSDL3.a` + the
|
||||||
directive silently fails and you get cryptic
|
per-OS `linkerOpts`, carried through the klib chain to the final exe, so apps
|
||||||
`expected CPointer<sdl3.SDL_Surface>?, actual CPointer<sdl3_image.SDL_Surface>?`
|
link SDL just by depending on `:ui`/`:desktop-native-window`). `:sdl-core` has NO Compose
|
||||||
errors.
|
dependency, so `:ui → :sdl-core` is cycle-free — the compose way (like `ui → skiko`).
|
||||||
|
|
||||||
`:ui/build.gradle.kts` works around this by passing the sdl3 cinterop output
|
|
||||||
klib path explicitly via `extraOpts("-library", vSdl3Klib)` on `sdl3_ttf` /
|
|
||||||
`sdl3_image`, plus a task dependency
|
|
||||||
(`cinteropSdl3_ttf*Target.dependsOn(cinteropSdl3*Target)`). If you ever add
|
|
||||||
another `depends = sdl3` cinterop, add it to that list too.
|
|
||||||
|
|
||||||
## Density flow (Option B — layout in physical pixels)
|
## Density flow (Option B — layout in physical pixels)
|
||||||
|
|
||||||
@@ -392,13 +405,13 @@ Passing raw `px.dp` will double-scale on Retina.
|
|||||||
./gradlew :demo:runDebugExecutableLinuxX64
|
./gradlew :demo:runDebugExecutableLinuxX64
|
||||||
./gradlew :apidemo:runDebugExecutableLinuxX64
|
./gradlew :apidemo:runDebugExecutableLinuxX64
|
||||||
|
|
||||||
# Windows (from Windows — mingw cross-build from macOS/Linux fails at cinterop)
|
# Windows (from Windows — mingw cross-build from macOS/Linux fails at cinterop).
|
||||||
|
# Links the bitsycore skiko FORK from GitHub Packages (com.bitsycore.skiko:skiko:
|
||||||
|
# 0.150.1-mingw.1, override -PskikoMingwVersion); the bridge plugin drops
|
||||||
|
# skiko-windows-x64.dll next to the exe (installWindowsSkiaDll).
|
||||||
gradlew.bat :demo:runDebugExecutableMingwX64
|
gradlew.bat :demo:runDebugExecutableMingwX64
|
||||||
gradlew.bat :apidemo:runDebugExecutableMingwX64
|
gradlew.bat :apidemo:runDebugExecutableMingwX64
|
||||||
|
|
||||||
# Skiko-free build on macOS/Linux — SDL3 renderer everywhere
|
|
||||||
./gradlew :demo:runDebugExecutableMacosArm64 -Prenderer=sdl3
|
|
||||||
|
|
||||||
# Stock JVM Compose Desktop (any host) — the parity reference: the SAME shared
|
# Stock JVM Compose Desktop (any host) — the parity reference: the SAME shared
|
||||||
# screens on upstream Compose; differences vs the native build = port bugs.
|
# screens on upstream Compose; differences vs the native build = port bugs.
|
||||||
./gradlew :demo:run
|
./gradlew :demo:run
|
||||||
@@ -407,12 +420,16 @@ gradlew.bat :apidemo:runDebugExecutableMingwX64
|
|||||||
|
|
||||||
### System dependencies
|
### System dependencies
|
||||||
|
|
||||||
SDL3 + SDL3_ttf + SDL3_image + FreeType (+ image codecs) are **built from
|
SDL3 is **built from source as a static library on every OS** and linked
|
||||||
source as static libraries on every OS** and linked straight into the
|
straight into the executable — no brew/apt SDL packages. Build it once per host
|
||||||
executable — no brew/apt SDL packages, no runtime .dll/.so/.dylib; a
|
with `python scripts/build-sdl/build-all.py`. Per-host toolchain requirements
|
||||||
distributable is `<app>` + `data.kres`. Build them once per host with
|
and the step breakdown are in [TOOLING.md](TOOLING.md#native-libraries).
|
||||||
`python scripts/build-sdl/build-all.py`. Per-host toolchain requirements and
|
|
||||||
the step breakdown are in [TOOLING.md](TOOLING.md#native-libraries).
|
Skia comes in through the Skiko klibs: the official Maven ones on macOS/Linux
|
||||||
|
(no runtime .so/.dylib — Skia is statically inside the klib), and the bitsycore
|
||||||
|
fork on Windows, whose `skiko-windows-x64.dll` the bridge plugin provisions next
|
||||||
|
to the exe. So a macOS/Linux distributable is `<app>` + `data.kres`; a Windows
|
||||||
|
one is `<app>.exe` + `data.kres` + `skiko-windows-x64.dll`.
|
||||||
|
|
||||||
## Runtime bundling — data.kres
|
## Runtime bundling — data.kres
|
||||||
|
|
||||||
@@ -456,15 +473,15 @@ drift / vendor-clean guardrails are in
|
|||||||
## Key files by area — start here when you need to find something
|
## Key files by area — start here when you need to find something
|
||||||
|
|
||||||
### Renderer + main loop
|
### Renderer + main loop
|
||||||
- `compose/sdl/window/src/nativeMain/…/ComposeWindow.kt` — main loop,
|
- `compose/desktop/native/window/src/nativeMain/…/ComposeWindow.kt` — main loop,
|
||||||
recomposer lifecycle, SDL event dispatch, composition-local seeding.
|
recomposer lifecycle, SDL event dispatch, composition-local seeding.
|
||||||
- `compose/ui/ui/src/nativeMain/…/RenderBackend.kt` — the interface.
|
- `compose/ui/ui/src/nativeMain/…/RenderBackend.kt` — the interface.
|
||||||
- `compose/ui/ui/src/nativeMain/…/GpuMode.kt` — sealed renderer / driver picker.
|
- `compose/ui/ui/src/nativeMain/…/GpuMode.kt` — sealed driver picker
|
||||||
- `compose/ui/ui/src/skikoRendererMain/…/renderer/skia/SkiaRenderBackend.kt`.
|
(`Auto` / `Software` / `Skia.OpenGL` / `Skia.Metal`).
|
||||||
- `compose/ui/ui/src/sdlRendererMain/…/renderer/sdl/Sdl3RenderBackend.kt`.
|
- `compose/ui/ui/src/skikoRendererMain/…/renderer/skia/SkiaRenderBackend.kt` —
|
||||||
- `compose/ui/ui/src/sdlRendererMain/…/renderer/sdl/FreeTypeIcons.kt` —
|
the Skia render backend (shared by every native target).
|
||||||
variable-font axis rasterisation (SDL3_ttf has no axis-set API; we go
|
- `compose/ui/ui/src/skikoRendererMingwMain/…` — mingwX64-only Skia actuals
|
||||||
to FreeType directly for icon families).
|
bound against the skiko fork's flat extern-C surface.
|
||||||
|
|
||||||
### Layout / composition wiring
|
### Layout / composition wiring
|
||||||
- `compose/ui/ui/src/commonMain/…/node/ComposeRootHost.kt` — root LayoutNode
|
- `compose/ui/ui/src/commonMain/…/node/ComposeRootHost.kt` — root LayoutNode
|
||||||
@@ -474,15 +491,15 @@ drift / vendor-clean guardrails are in
|
|||||||
alpha bridge).
|
alpha bridge).
|
||||||
- `compose/ui/ui/src/commonMain/…/node/NodeApplier.kt`.
|
- `compose/ui/ui/src/commonMain/…/node/NodeApplier.kt`.
|
||||||
|
|
||||||
### Text
|
### Text (all in `:ui-text` now)
|
||||||
- `compose/ui/ui/src/nativeMain/…/ui/text/SdlParagraph.native.kt` — the
|
- `compose/ui/ui-text/src/nativeMain/…/ui/text/SkiaParagraph.native.kt` — the
|
||||||
bridged `Paragraph` implementation (measurement, hit-test, line metrics,
|
`Paragraph` actual (skiko-free; the 30 interface methods over plain data),
|
||||||
span painting).
|
driving the skiko engine through the `NativeParagraphOps` seam.
|
||||||
- `compose/ui/ui/src/nativeMain/…/ui/text/ParagraphFactories.native.kt` —
|
- `compose/ui/ui-text/src/skikoRendererMain/…/renderer/skia/SkiaParagraphEngine.kt`
|
||||||
|
— `SkiaParagraphOps` (builds/queries the real skiko `skparagraph`) + `SkiaFonts`
|
||||||
|
(data.kres bytes → skiko Typeface; the port's family/variable-axis model).
|
||||||
|
- `compose/ui/ui-text/src/nativeMain/…/ui/text/ParagraphFactories.native.kt` —
|
||||||
actuals for the `Paragraph(…)` / `ParagraphIntrinsics(…)` factory family.
|
actuals for the `Paragraph(…)` / `ParagraphIntrinsics(…)` factory family.
|
||||||
- `compose/ui/ui/src/commonMain/…/text/TextMeasurer.kt` — `NativeTextMeasurer`
|
|
||||||
interface (per-renderer implementations: `SkiaTextRenderer` /
|
|
||||||
`Sdl3TextRenderer`).
|
|
||||||
|
|
||||||
### Icons
|
### Icons
|
||||||
- `compose/foundation/foundation/src/nativeMain/…/icons/IconFontIcon.kt` —
|
- `compose/foundation/foundation/src/nativeMain/…/icons/IconFontIcon.kt` —
|
||||||
@@ -516,7 +533,7 @@ profiler. Quick rules of thumb:
|
|||||||
the broad net.
|
the broad net.
|
||||||
- Chasing one reported interaction → run the **probe** (`scripts/probe/`), targeted.
|
- Chasing one reported interaction → run the **probe** (`scripts/probe/`), targeted.
|
||||||
- Slow frame → **profiler** first (`CDN_PROFILE=1`), optimize second.
|
- Slow frame → **profiler** first (`CDN_PROFILE=1`), optimize second.
|
||||||
- Any renderer change → the **`verify-mac`** runbook gates both legs before commit.
|
- Any renderer change → the **`verify-mac`** runbook gates it before commit.
|
||||||
|
|
||||||
See [RENDERER.md](RENDERER.md) for the renderer work these support.
|
See [RENDERER.md](RENDERER.md) for the renderer work these support.
|
||||||
|
|
||||||
@@ -565,12 +582,6 @@ that should surface in tooling.
|
|||||||
`Modifier.alpha(x)` desugars to `graphicsLayer(alpha = x, clip = true)`.
|
`Modifier.alpha(x)` desugars to `graphicsLayer(alpha = x, clip = true)`.
|
||||||
For a drag ghost that also translates, put `alpha` and `translationX` on
|
For a drag ghost that also translates, put `alpha` and `translationX` on
|
||||||
the SAME `graphicsLayer(...)` so clip stays false.
|
the SAME `graphicsLayer(...)` so clip stays false.
|
||||||
- **Cinterop `depends = sdl3`** — silently doesn't propagate; see the
|
|
||||||
gotcha section above.
|
|
||||||
- **Configuration cache + `-Prenderer=`** — Gradle caches configuration;
|
|
||||||
toggling the renderer property may not invalidate it. Delete
|
|
||||||
`.gradle/configuration-cache/` between switches if you see weird
|
|
||||||
"couldn't find sdl3_ttf" errors.
|
|
||||||
- **Substituted Maven modules hide their transitives from common metadata** —
|
- **Substituted Maven modules hide their transitives from common metadata** —
|
||||||
when the bridge swaps an official coord for a project module on native
|
when the bridge swaps an official coord for a project module on native
|
||||||
configs, KGP's granular-metadata visibility check drops that Maven module's
|
configs, KGP's granular-metadata visibility check drops that Maven module's
|
||||||
@@ -583,17 +594,11 @@ that should surface in tooling.
|
|||||||
declares every target, so only its .module files carry the full variant
|
declares every target, so only its .module files carry the full variant
|
||||||
table; macOS-published roots left v0.1.15 without mingwX64 variants) —
|
table; macOS-published roots left v0.1.15 without mingwX64 variants) —
|
||||||
test with `gradlew :<module>:compileCommonMainKotlinMetadata` before tagging.
|
test with `gradlew :<module>:compileCommonMainKotlinMetadata` before tagging.
|
||||||
- **`Path()` in commonMain returns different actuals per renderer** —
|
|
||||||
the Skia renderer produces a `SkiaBackedPath` (wraps
|
|
||||||
`org.jetbrains.skia.Path`), the SDL renderer produces a project
|
|
||||||
`ProjectPath` (command-list based). `SkiaCanvas.toSkiaPath` handles
|
|
||||||
both — never assume the type without checking.
|
|
||||||
|
|
||||||
## Useful Gradle tricks
|
## Useful Gradle tricks
|
||||||
|
|
||||||
- `--args="--gpu=sdl3.opengl --screen=Buttons"` — pass CLI to the demo.
|
- `--args="--gpu=skia.opengl --screen=Buttons"` — pass CLI to the demo.
|
||||||
- `--info` — see cinterop classpath + include paths actually used.
|
- `--info` — see cinterop classpath + include paths actually used.
|
||||||
- `--rerun-tasks` — force rebuild after toggling `-Prenderer=`.
|
|
||||||
- After a module rename or IC-cache mismatch: nuke
|
- After a module rename or IC-cache mismatch: nuke
|
||||||
`demo/build/kotlin-native-ic-cache` (or `apidemo/build/…`). Kotlin/Native
|
`demo/build/kotlin-native-ic-cache` (or `apidemo/build/…`). Kotlin/Native
|
||||||
pins module IDs into its klib metadata; a stale cache surfaces as
|
pins module IDs into its klib metadata; a stale cache surfaces as
|
||||||
|
|||||||
@@ -18,20 +18,21 @@ executable for macOS (arm64), Linux (x64/arm64), and Windows (mingwX64), using
|
|||||||
- **The UI layers are vendored upstream.** `androidx.compose.ui`, `foundation`,
|
- **The UI layers are vendored upstream.** `androidx.compose.ui`, `foundation`,
|
||||||
`animation`, and `material3` are copied from Compose Multiplatform verbatim
|
`animation`, and `material3` are copied from Compose Multiplatform verbatim
|
||||||
wherever they compile as is, with project code filling in only the native glue.
|
wherever they compile as is, with project code filling in only the native glue.
|
||||||
- **Rendering is pluggable** behind one `RenderBackend`: Skia (via Skiko) on
|
- **Rendering is Skia everywhere.** macOS and Linux link the official Skiko
|
||||||
macOS and Linux, and a from-scratch SDL3 renderer (`SDL3_ttf` +
|
klibs (Metal / OpenGL); Windows links the bitsycore Skiko fork, which ships
|
||||||
`SDL_RenderGeometry`) on Windows, or anywhere with `-Prenderer=sdl3`.
|
Skiko and Skia together in `skiko-windows-x64.dll`.
|
||||||
- **The platform is SDL3.** Windowing, input, audio, filesystem, file dialogs,
|
- **The platform is SDL3.** Windowing, input, audio, filesystem, file dialogs,
|
||||||
and clipboard all go through SDL3, so one code path covers every OS.
|
and clipboard all go through SDL3, so one code path covers every OS.
|
||||||
- **No runtime dependencies.** SDL3 and its codecs are built as static
|
- **Lean distributables.** SDL3 is built as a static library and linked in. On
|
||||||
libraries and linked in. A distributable is just the executable plus a
|
macOS and Linux a distributable is just the executable plus a `data.kres`
|
||||||
`data.kres` resource bundle.
|
resource bundle; on Windows it also ships `skiko-windows-x64.dll` next to the
|
||||||
|
exe (auto-provisioned by the bridge plugin).
|
||||||
|
|
||||||
| Platform | Gradle target | Default renderer |
|
| Platform | Gradle target | Renderer |
|
||||||
|----------|---------------|------------------|
|
|----------|---------------|----------|
|
||||||
| macOS arm64 | `macosArm64` | Skia (Metal) |
|
| macOS arm64 | `macosArm64` | Skia (Metal) — official Skiko |
|
||||||
| Linux x64 / arm64 | `linuxX64` / `linuxArm64` | Skia (OpenGL) |
|
| Linux x64 / arm64 | `linuxX64` / `linuxArm64` | Skia (OpenGL) — official Skiko |
|
||||||
| Windows | `mingwX64` | SDL3 |
|
| Windows | `mingwX64` | Skia — bitsycore Skiko fork |
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ saved state behave as they do on Android.
|
|||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
implementation(project(":window")) // window shell + main loop
|
implementation(project(":desktop-native-window")) // window shell + main loop
|
||||||
implementation(project(":material3")) // Material 3 widgets
|
implementation(project(":material3")) // Material 3 widgets
|
||||||
implementation(project(":material-symbols")) // icon-font composables (optional)
|
implementation(project(":material-symbols")) // icon-font composables (optional)
|
||||||
}
|
}
|
||||||
@@ -122,7 +123,7 @@ dialogs, canvas, graphics layers, animation, and gestures.
|
|||||||
```bash
|
```bash
|
||||||
./gradlew :demo:runDebugExecutableMacosArm64 # macOS (Skia / Metal)
|
./gradlew :demo:runDebugExecutableMacosArm64 # macOS (Skia / Metal)
|
||||||
./gradlew :demo:runDebugExecutableLinuxX64 # Linux (Skia / OpenGL)
|
./gradlew :demo:runDebugExecutableLinuxX64 # Linux (Skia / OpenGL)
|
||||||
gradlew.bat :demo:runDebugExecutableMingwX64 # Windows (SDL3)
|
gradlew.bat :demo:runDebugExecutableMingwX64 # Windows (Skia / Skiko fork)
|
||||||
./gradlew :demo:run # JVM Compose Desktop (reference)
|
./gradlew :demo:run # JVM Compose Desktop (reference)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -138,14 +139,11 @@ client certificates.
|
|||||||
./gradlew :apidemo:run # JVM Compose Desktop (reference)
|
./gradlew :apidemo:run # JVM Compose Desktop (reference)
|
||||||
```
|
```
|
||||||
|
|
||||||
Pass `-Prenderer=sdl3` on macOS or Linux to drop Skiko and use the pure SDL3
|
|
||||||
renderer everywhere.
|
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
One Gradle module per upstream Compose artifact, mirroring the upstream
|
One Gradle module per upstream Compose artifact, mirroring the upstream
|
||||||
`compose/` tree. The renderer and cinterops live in `:ui`; `:window` is the SDL
|
`compose/` tree. The renderer lives in `:ui` and the `sdl3` cinterop in
|
||||||
integration layer. `:material-symbols`, `:components-resources`, and
|
`:sdl-core`; `:desktop-native-window` is the SDL integration layer. `:material-symbols`, `:components-resources`, and
|
||||||
`:navigation3-ui` are project or vendored modules that the official Maven
|
`:navigation3-ui` are project or vendored modules that the official Maven
|
||||||
artifacts do not cover for Kotlin/Native desktop.
|
artifacts do not cover for Kotlin/Native desktop.
|
||||||
|
|
||||||
@@ -159,7 +157,7 @@ compatible artifacts are in [CLAUDE.md](CLAUDE.md).
|
|||||||
Build the native libraries once per machine, then build any app target:
|
Build the native libraries once per machine, then build any app target:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 scripts/build-sdl/build-all.py # SDL3, SDL3_ttf, SDL3_image, FreeType (static)
|
python3 scripts/build-sdl/build-all.py # SDL3 (static)
|
||||||
./gradlew :demo:runDebugExecutableMacosArm64
|
./gradlew :demo:runDebugExecutableMacosArm64
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+65
-114
@@ -8,37 +8,39 @@ actuals, or the layer engine.
|
|||||||
For build and verification commands see [TOOLING.md](TOOLING.md). For module
|
For build and verification commands see [TOOLING.md](TOOLING.md). For module
|
||||||
layout, source-set hierarchy, and vendoring rules see [CLAUDE.md](CLAUDE.md).
|
layout, source-set hierarchy, and vendoring rules see [CLAUDE.md](CLAUDE.md).
|
||||||
|
|
||||||
## 1. The two renderer legs
|
## 1. The renderer
|
||||||
|
|
||||||
Rendering is pluggable behind one `RenderBackend` interface. Exactly one leg is
|
> **This branch is Skia-only.** The from-scratch SDL renderer leg
|
||||||
active per target:
|
> (`sdlRendererMain`: `Sdl3Canvas` / `Sdl3TextRenderer` / `FreeTypeIcons` / the
|
||||||
|
> SDL render nodes, ~3,800 lines) and its `SDL3_ttf` / `SDL3_image` / FreeType
|
||||||
|
> cinterops were removed once Skiko-on-mingwX64 landed (Route 1a — see
|
||||||
|
> [SKIKO-MINGW-FEASIBILITY.md](SKIKO-MINGW-FEASIBILITY.md)). SDL3 remains as the
|
||||||
|
> windowing / input / platform layer only. Below, references to "the SDL leg", a
|
||||||
|
> second `RenderBackend` actual, `-Prenderer=sdl3`, and the SDL-leg fidelity work
|
||||||
|
> are **historical** — kept because the convergence decisions still explain the
|
||||||
|
> Skia leg's shape.
|
||||||
|
|
||||||
- **Skia leg** (`skikoRendererMain`, macOS + Linux). Draws through upstream
|
Rendering goes through one `RenderBackend`, implemented once: the **Skia leg**
|
||||||
Compose Multiplatform's own Skia stack, vendored verbatim: `SkiaBackedCanvas`,
|
(`skikoRendererMain`). It draws through upstream Compose Multiplatform's own Skia
|
||||||
`SkiaBackedPaint`, `SkiaShader`, `actual class GraphicsLayer` backed by
|
stack, vendored verbatim: `SkiaBackedCanvas`, `SkiaBackedPaint`, `SkiaShader`,
|
||||||
`org.jetbrains.skiko.node.RenderNode`, `SkiaGraphicsContext`. On this leg the
|
`actual class GraphicsLayer` backed by `org.jetbrains.skiko.node.RenderNode`,
|
||||||
layer and draw engine internals are literally upstream.
|
`SkiaGraphicsContext` — the layer and draw engine internals are literally
|
||||||
- **SDL leg** (`sdlRendererMain`, Windows always; macOS/Linux under
|
upstream. macOS/Linux link the official Skiko klibs; mingwX64 links the
|
||||||
`-Prenderer=sdl3`). A from-scratch renderer on `SDL_RenderGeometry` +
|
bitsycore skiko **fork** (`skikoRendererMingwMain`, Route 1a).
|
||||||
`SDL3_ttf` + FreeType. SDL is a triangle blitter, so this leg carries a
|
|
||||||
permanent bespoke surface (about 3,800 lines): `Sdl3Canvas`,
|
|
||||||
`Sdl3TextRenderer`, `FreeTypeIcons`, and the SDL render nodes.
|
|
||||||
|
|
||||||
Both legs share one project-owned text and image pipeline (`SdlParagraph`,
|
The text pipeline is now upstream's own `SkiaParagraph` (skparagraph, vendored
|
||||||
`NativeTextMeasurer`, `IconFont`). Text layout, line-breaking, metrics, and
|
verbatim); glyph rasterization is Skia. GPU path per platform: Metal (macOS),
|
||||||
hit-test are the port's engine on both legs; only glyph rasterization differs
|
OpenGL (Linux + Windows), with a CPU-raster `Software` fallback.
|
||||||
(Skia vs SDL3_ttf/FreeType). This is a deliberate axiom, not a gap: see the
|
|
||||||
text decision in section 4.
|
|
||||||
|
|
||||||
The seam is kept as narrow and low as possible. Code flows
|
The seam is kept as narrow and low as possible. Code flows
|
||||||
`Common (upstream) -> shared native engine -> Skia actual / SDL actual`. What we
|
`Common (upstream) -> shared native engine -> Skia actual`. What we
|
||||||
minimize is hand-rolled actual surface, not actual-side line count: a fat
|
minimize is hand-rolled actual surface, not actual-side line count: a fat
|
||||||
vendored-upstream actual (the Skia leg carrying upstream's whole GraphicsLayer
|
vendored-upstream actual (the Skia leg carrying upstream's whole GraphicsLayer
|
||||||
stack) is preferred over a thin hand-written one.
|
stack) is preferred over a thin hand-written one.
|
||||||
|
|
||||||
## 2. The retained-layer engine (the model we copied)
|
## 2. The retained-layer engine (the model we copied)
|
||||||
|
|
||||||
Upstream skiko skips work at three levels. The port has all three.
|
Upstream skiko skips work at three levels. The Skia leg has all three.
|
||||||
|
|
||||||
- **L1 frame scheduling.** A frame schedules the next only if still dirty.
|
- **L1 frame scheduling.** A frame schedules the next only if still dirty.
|
||||||
Ours: `ComposeWindow.shouldRender()` gates `renderFrame()`; the loop blocks on
|
Ours: `ComposeWindow.shouldRender()` gates `renderFrame()`; the loop blocks on
|
||||||
@@ -58,8 +60,7 @@ dirty-region present; the entire win is not re-recording clean layers, not
|
|||||||
drawing less screen. Dirty-region rendering is therefore an explicit non-goal.
|
drawing less screen. Dirty-region rendering is therefore an explicit non-goal.
|
||||||
|
|
||||||
The compositing-strategy contract (`requiresLayer()`, from
|
The compositing-strategy contract (`requiresLayer()`, from
|
||||||
`SkiaGraphicsLayer.skiko.kt`) must match on both legs. It decides when a layer
|
`SkiaGraphicsLayer.skiko.kt`) decides when a layer needs an offscreen:
|
||||||
needs an offscreen:
|
|
||||||
|
|
||||||
| Condition | Auto | Offscreen | ModulateAlpha |
|
| Condition | Auto | Offscreen | ModulateAlpha |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
@@ -76,80 +77,46 @@ needs an offscreen:
|
|||||||
vendored verbatim. The Skia leg gets real display-list caching and correct
|
vendored verbatim. The Skia leg gets real display-list caching and correct
|
||||||
clip/shadow/renderEffect for free from upstream. After this landed, Skia
|
clip/shadow/renderEffect for free from upstream. After this landed, Skia
|
||||||
`draw` on LazyColumn fell from 1.75 ms to 0.2 ms.
|
`draw` on LazyColumn fell from 1.75 ms to 0.2 ms.
|
||||||
- **SDL leg ships the geo node as the Windows default.** `SdlDisplayListRenderNode`
|
- **Parity is a golden-master against JVM** (about 2% median, dominated by the
|
||||||
records a leaf's layer-local tessellated geometry, text runs, and icon glyphs
|
shared text engine's small metric delta). See TOOLING.md for how to read it.
|
||||||
by params (not texture pointers), then re-emits through the layer transform.
|
|
||||||
Crisp under any transform, bit-exact, deterministic (no render-target state),
|
|
||||||
and eviction-safe (replay re-looks-up via the run LRU / FreeType glyph cache).
|
|
||||||
0.000% across a 57-screen self-consistency sweep bar a sub-0.06% rotated-edge
|
|
||||||
AA fringe. LazyColumn steady-state `draw` fell 57% versus the un-cached
|
|
||||||
baseline. Fallbacks: `CDN_LAYERCACHE=off|texture`.
|
|
||||||
- **Parity is a golden-master on the Skia leg** (about 2% median, dominated by
|
|
||||||
the shared text engine's small metric delta) and a bounded, ranked signal on
|
|
||||||
the SDL leg. See TOOLING.md for how to read it.
|
|
||||||
- **Memory is stable.** The historical composition leak is fixed (see section 6)
|
- **Memory is stable.** The historical composition leak is fixed (see section 6)
|
||||||
and guarded by the `--soaktest` gate in the verify runbook.
|
and guarded by the `--soaktest` gate in the verify runbook.
|
||||||
|
|
||||||
## 4. Decisions to remember
|
## 4. Decisions to remember
|
||||||
|
|
||||||
- **Goal is G1, cheap upstream-tracking.** The target is low per-bump
|
- **Goal is G1, cheap upstream-tracking.** The target is low per-bump
|
||||||
reconciliation cost when following upstream, not Windows pixel-parity or
|
reconciliation cost when following upstream. All platforms now render through
|
||||||
feature completeness. The JVM Compose Desktop target (`:demo:run`) is the
|
real Skia — mingwX64 via the bitsycore skiko fork (Route 1a; see
|
||||||
documented Windows fidelity/feature tier: real Skia, full fidelity, on any
|
[SKIKO-MINGW-FEASIBILITY.md](SKIKO-MINGW-FEASIBILITY.md)) — so there is no
|
||||||
host. That makes SDL-leg fidelity work (B3) and real-Skia-on-Windows (Track A)
|
fidelity tier below the JVM cross-check. The JVM Compose Desktop target
|
||||||
optional polish, not obligations.
|
(`:demo:run`) remains the fidelity/parity reference on any host.
|
||||||
- **Vendor, do not hand-roll.** Copy upstream verbatim wherever it compiles.
|
- **Vendor, do not hand-roll.** Copy upstream verbatim wherever it compiles.
|
||||||
Edit-to-compile becomes a manual vendor with a `// VENDOR-BASE:` header so the
|
Edit-to-compile becomes a manual vendor with a `// VENDOR-BASE:` header so the
|
||||||
drift tripwire can track it. The litmus test for any divergence: "Is this what
|
drift tripwire can track it. The litmus test for any divergence: "Is this what
|
||||||
upstream does? If not, what real platform constraint forces the difference?"
|
upstream does? If not, what real platform constraint forces the difference?"
|
||||||
Valid answers name a constraint (no Skiko on Windows K/N; SDL is a triangle
|
Valid answers name a constraint (e.g. no windowing/input toolkit in K/N ->
|
||||||
blitter). "It was easier" is not valid.
|
SDL3). "It was easier" is not valid.
|
||||||
- **B2 convergence is complete.** The Skia leg was migrated off the port's
|
- **Text is now upstream's engine (B6.3 done).** Skia-leg text draws through
|
||||||
hand-rolled GraphicsLayer onto upstream's own. This was a source-set migration,
|
upstream's own `SkiaParagraph` (skparagraph), vendored verbatim — the
|
||||||
not a file-flip: the SDL node cluster relocated `nativeMain -> sdlRendererMain`,
|
font-subsystem replacement landed: upstream `PlatformFont`/`FontCache`/
|
||||||
`GraphicsContext` forked per-leg behind a `createGraphicsContext()` seam, and
|
`FontCollection` back a `data.kres`-fed font supply, replacing the port's
|
||||||
the upstream skiko files were un-refused in `compose-fork.txt`. Measured
|
name-to-bytes engine. glyph rasterization stays Skia. This subsumes the P3.1
|
||||||
sync-tax reduction on the skiko path: about 8.5x (roughly 1240 hand-rolled
|
metrics work (measurement is now upstream's, so the numbers match by
|
||||||
lines down to about 145 lines of drift-tracked edits; roughly 1006 lines now
|
construction).
|
||||||
auto-sync verbatim on a ref bump). The beta02 -> beta03 bump proved it: zero
|
- **Module split (`:ui-graphics` / `:ui-text`) is done.** The blocker — the
|
||||||
reconciliation, all manual-vendor bases unchanged.
|
`sdl3` cinterop being a shared substrate that Kotlin/Native can't cleanly
|
||||||
- **Text stays the port's engine (B6.3 skipped).** Making Skia-leg text truly
|
share across a module boundary — was resolved by extracting a non-upstream
|
||||||
upstream (`SkiaParagraph`) is a font-subsystem replacement, not a canvas swap:
|
`:sdl-core` base module that owns the cinterop and api-exposes it. With that
|
||||||
two coexisting font-identity models (the port's name-to-bytes `IconFont` +
|
base in place, `androidx.compose.ui.graphics.*` / `.text.*` split off into
|
||||||
no-op resolver vs upstream `PlatformFont`/`FontCache`/`FontCollection`), about
|
`:ui-graphics` / `:ui-text` (matching upstream artifacts), leaving `:ui` as
|
||||||
13 skiko files to re-vendor, about 10 shared actuals to split, a
|
the Compose core + Skia renderer + SDL bridges. `RenderBackend.drawRoot` takes
|
||||||
`data.kres`-to-`FontCache` bridge, no green intermediate, and it discards the
|
`(Canvas) -> Unit`, decoupling the backends from `ComposeRootHost`.
|
||||||
P3.1 metrics work. ROI is low: the Skia leg already measures text with skiko
|
- **Real Skia on Windows K/N shipped (Route 1a).** mingwX64 links the bitsycore
|
||||||
`Font` metrics (via `currentTextMeasurer` -> `SkiaTextRenderer`), which is how
|
skiko fork against `skiko-windows-x64.dll` (with an embedded GNU import lib),
|
||||||
parity reached about 2%. Revisit only if complex-script/bidi fidelity on the
|
published to GitHub Packages and auto-provisioned by the bridge plugin. This
|
||||||
native Skia leg becomes a hard requirement.
|
replaced the SDL renderer as the Windows path. See
|
||||||
- **`GraphicsLayerOwnerLayer` stays shared.** The fork point is `GraphicsLayer` /
|
[SKIKO-MINGW-FEASIBILITY.md](SKIKO-MINGW-FEASIBILITY.md) for the ABI details
|
||||||
`GraphicsContext`, not the owner layer. The shared owner layer compiles
|
and the route trade-offs.
|
||||||
against both `GraphicsLayer` actuals (upstream skiko + port SDL) only if both
|
|
||||||
satisfy the commonMain `expect class GraphicsLayer` API. That API-parity
|
|
||||||
invariant is enforced by `:ui:compileCommonMainKotlinMetadata` on every build.
|
|
||||||
- **Lifetime model.** The SDL leg uses GC / release-queue for layer lifetime and
|
|
||||||
does NOT vendor `ChildLayerDependenciesTracker`. The Skia leg uses upstream's
|
|
||||||
`SkiaGraphicsLayer`, which inherently uses the tracker (already vendored in
|
|
||||||
commonMain). A per-leg lifetime divergence is the class of bug that caused an
|
|
||||||
early navigation crash, so any change here goes to both legs plus a soak gate.
|
|
||||||
- **Module split (`:ui-graphics` / `:ui-text`) is shelved as infeasible.** Not
|
|
||||||
cosmetic churn as originally scoped: the `sdl3` cinterop is a shared substrate
|
|
||||||
used by 11 graphics-side files AND 12 platform/windowing/node/resources files
|
|
||||||
that stay in `:ui`. Kotlin/Native cannot cleanly share a cinterop klib across a
|
|
||||||
module boundary, which is exactly why they were merged. A real split needs a
|
|
||||||
new non-upstream `:ui-cinterop` base module plus cross-module cinterop
|
|
||||||
api-exposure, which is bigger and more divergent than the "match upstream
|
|
||||||
artifacts" goal it was meant to serve. The one keeper from the attempt:
|
|
||||||
`RenderBackend.drawRoot` now takes `(Canvas) -> Unit`, decoupling the backends
|
|
||||||
from `ComposeRootHost` (a genuine improvement, retained).
|
|
||||||
- **Track A (real Skia on Windows K/N) is shelved.** Kotlin/Native mingwX64 is
|
|
||||||
GNU-ABI; skia-pack Windows is MSVC-ABI, which cannot be statically fused into a
|
|
||||||
GNU-ABI binary. Building current C++20 Skia to a GNU/mingw static archive is an
|
|
||||||
open-ended fork (only abandoned Mozilla-era precedent). The only working route
|
|
||||||
is a runtime DLL, which breaks the no-DLL invariant and adds tens of MB on the
|
|
||||||
one platform it targets. The SDL leg is the permanent Windows renderer; JVM is
|
|
||||||
the fidelity escape hatch.
|
|
||||||
|
|
||||||
## 5. Convergence status
|
## 5. Convergence status
|
||||||
|
|
||||||
@@ -159,24 +126,23 @@ needs an offscreen:
|
|||||||
| B2: Skia leg on upstream GraphicsLayer/GraphicsContext | Done |
|
| B2: Skia leg on upstream GraphicsLayer/GraphicsContext | Done |
|
||||||
| B6.1: Skia leg on upstream `SkiaBackedCanvas` + paint/shader | Done |
|
| B6.1: Skia leg on upstream `SkiaBackedCanvas` + paint/shader | Done |
|
||||||
| B6.2: upstream `GraphicsLayer` + delete transient port cluster | Done |
|
| B6.2: upstream `GraphicsLayer` + delete transient port cluster | Done |
|
||||||
| B6.3: upstream text (`SkiaParagraph`) on the Skia leg | Skipped (decision) |
|
| B6.3: upstream text (`SkiaParagraph`) on the Skia leg | Done |
|
||||||
| B5: engine-convergence deltas audit | Done (clean wins spent by B6) |
|
| B5: engine-convergence deltas audit | Done (clean wins spent by B6) |
|
||||||
| P2.2: composition memory leak | Fixed + soak-gated |
|
| P2.2: composition memory leak | Fixed + soak-gated |
|
||||||
| P2.3: outsets / blur / renderEffect | Done via upstream `SkiaGraphicsLayer` |
|
| P2.3: outsets / blur / renderEffect | Done via upstream `SkiaGraphicsLayer` |
|
||||||
| P3.1: SDL text metrics parity (17% median -> 2%) | Done |
|
| P3.1: text metrics parity (17% median -> 2%) | Done |
|
||||||
| B3: further SDL fidelity | Capped (parity-ranked wins only) |
|
| Module split (`:ui-graphics` / `:ui-text` via `:sdl-core`) | Done |
|
||||||
| Module split | Shelved (infeasible as specified) |
|
| Track A: real Skia on Windows (Route 1a) | Done |
|
||||||
| Track A: real Skia on Windows | Shelved |
|
|
||||||
|
|
||||||
## 6. Remaining and future work
|
## 6. Remaining and future work
|
||||||
|
|
||||||
Nothing here blocks day-to-day work. These are the open threads worth
|
Nothing here blocks day-to-day work. These are the open threads worth
|
||||||
remembering.
|
remembering.
|
||||||
|
|
||||||
- **WIN-SMOKE (pre-ship, Windows only).** The Mac runbook covers both renderers
|
- **WIN-SMOKE (pre-ship, Windows only).** The Mac runbook cannot cover the
|
||||||
but cannot cover the shipped mingwX64 binary, the Windows-only `PrintWindow`
|
shipped mingwX64 binary, the Windows-only `PrintWindow` probe, or the
|
||||||
probe, or the common-metadata publish job. Run these on a Windows host before
|
common-metadata publish job. Run these on a Windows host before any release.
|
||||||
any release. This is the only outstanding verification.
|
This is the only outstanding verification.
|
||||||
- **Stabilization at Compose 1.12.0 stable.** The vendored refs are pinned to
|
- **Stabilization at Compose 1.12.0 stable.** The vendored refs are pinned to
|
||||||
`v1.12.0-beta03+dev4483` (no clean beta03 tag exists yet, and it is not on
|
`v1.12.0-beta03+dev4483` (no clean beta03 tag exists yet, and it is not on
|
||||||
Maven). The native side leads the JVM parity leg (forced to beta02, the latest
|
Maven). The native side leads the JVM parity leg (forced to beta02, the latest
|
||||||
@@ -192,17 +158,12 @@ remembering.
|
|||||||
shadows already match JVM.
|
shadows already match JVM.
|
||||||
- D3: dedupe `LayerTransformationMatrix.kt` against the now-vendored
|
- D3: dedupe `LayerTransformationMatrix.kt` against the now-vendored
|
||||||
`Matrices.skiko`. Blocked on D2 (the shared owner-layer hit-test needs a
|
`Matrices.skiko`. Blocked on D2 (the shared owner-layer hit-test needs a
|
||||||
both-legs matrix fn).
|
matrix fn usable from the owner layer).
|
||||||
- D4/D5: `SemanticsRegion` intersect/difference are stubs, and `CharHelpers`
|
- D4/D5: `SemanticsRegion` intersect/difference are stubs, and `CharHelpers`
|
||||||
is naive grapheme/bidi vs upstream ICU. Real fidelity gaps, gated on an
|
is naive grapheme/bidi vs upstream ICU. Real fidelity gaps, gated on an
|
||||||
accessibility or complex-script roadmap.
|
accessibility or complex-script roadmap.
|
||||||
- D6: `Focusability` / `PlatformVelocityTracker` are byte-equal to upstream;
|
- D6: `Focusability` / `PlatformVelocityTracker` are byte-equal to upstream;
|
||||||
vendoring them saves nothing.
|
vendoring them saves nothing.
|
||||||
- **SDL performance (opportunistic).** Shared glyph atlas to replace per-run
|
|
||||||
textures; `drawImageRect` capture in the geo node (journal rates it low-ROI).
|
|
||||||
- **SDL_GPU backend (long-term).** Real stencil clipping, pipelined batching,
|
|
||||||
shader gradients. The `NativeRenderNode` seam makes a GPU node a clean
|
|
||||||
drop-in.
|
|
||||||
- **Native-resource lifecycle.** Wire `GraphicsLayer`/RenderNode +
|
- **Native-resource lifecycle.** Wire `GraphicsLayer`/RenderNode +
|
||||||
`SdlImageBitmap.close()` fully into cache eviction and the renderer
|
`SdlImageBitmap.close()` fully into cache eviction and the renderer
|
||||||
`destroy()` chain; demote the periodic GC nudge once ownership covers it.
|
`destroy()` chain; demote the periodic GC nudge once ownership covers it.
|
||||||
@@ -225,13 +186,6 @@ remembering.
|
|||||||
Found by exact live-counters, static-mode isolation, macOS `leaks`/`heap`/
|
Found by exact live-counters, static-mode isolation, macOS `leaks`/`heap`/
|
||||||
`vmmap`, and component bisection. RSS alone cannot pinpoint a referenced leak;
|
`vmmap`, and component bisection. RSS alone cannot pinpoint a referenced leak;
|
||||||
budget heap tooling for this class.
|
budget heap tooling for this class.
|
||||||
- **Offscreen-texture caching is timing-nondeterministic on complex screens.**
|
|
||||||
This is why the geo (no-render-target) node is the robust default, not the
|
|
||||||
texture node.
|
|
||||||
- **Rounded/path layer clips must be applied or deferred on the fast path.** The
|
|
||||||
geo path submits raw geometry, which clips only to a rect; rounded clips are a
|
|
||||||
lazy offscreen mask the fast replay bypasses. Rect clip on the fast path;
|
|
||||||
rounded/generic clips fall back to block-replay.
|
|
||||||
- **Screenshots miss crashes and settle-timing.** Free-running screenshots on
|
- **Screenshots miss crashes and settle-timing.** Free-running screenshots on
|
||||||
animated/settling screens give false signals. Use render-to-quiescence +
|
animated/settling screens give false signals. Use render-to-quiescence +
|
||||||
virtual frame time for parity, the probe for interaction/crash coverage.
|
virtual frame time for parity, the probe for interaction/crash coverage.
|
||||||
@@ -244,7 +198,7 @@ remembering.
|
|||||||
is not re-recording, not redrawing less.
|
is not re-recording, not redrawing less.
|
||||||
- **A custom `cacheKey` API.** Superseded by the real per-node display list.
|
- **A custom `cacheKey` API.** Superseded by the real per-node display list.
|
||||||
- **Vendoring `RootNodeOwner` / the `ComposeScene` stack.** Coupled to skiko's
|
- **Vendoring `RootNodeOwner` / the `ComposeScene` stack.** Coupled to skiko's
|
||||||
`SkiaLayer` and windowing; it fights the `:window` SDL loop. The port borrows
|
`SkiaLayer` and windowing; it fights the `:desktop-native-window` SDL loop. The port borrows
|
||||||
only the layer engine.
|
only the layer engine.
|
||||||
|
|
||||||
## 9. Key files
|
## 9. Key files
|
||||||
@@ -252,12 +206,9 @@ remembering.
|
|||||||
- `compose/ui/ui/src/nativeMain/.../RenderBackend.kt`: the interface.
|
- `compose/ui/ui/src/nativeMain/.../RenderBackend.kt`: the interface.
|
||||||
- `compose/ui/ui/src/nativeMain/.../GpuMode.kt`: renderer / driver picker.
|
- `compose/ui/ui/src/nativeMain/.../GpuMode.kt`: renderer / driver picker.
|
||||||
- `compose/ui/ui/src/skikoRendererMain/.../renderer/skia/SkiaRenderBackend.kt`.
|
- `compose/ui/ui/src/skikoRendererMain/.../renderer/skia/SkiaRenderBackend.kt`.
|
||||||
- `compose/ui/ui/src/sdlRendererMain/.../renderer/sdl/Sdl3RenderBackend.kt`.
|
|
||||||
- `compose/ui/ui/src/sdlRendererMain/.../renderer/sdl/FreeTypeIcons.kt`:
|
|
||||||
variable-font axis rasterization.
|
|
||||||
- `compose/ui/ui/src/commonMain/.../node/ComposeRootHost.kt`: root host,
|
- `compose/ui/ui/src/commonMain/.../node/ComposeRootHost.kt`: root host,
|
||||||
hit-test, event dispatch, snapshot observer sweep.
|
hit-test, event dispatch, snapshot observer sweep.
|
||||||
- `compose/ui/ui/src/commonMain/.../node/impl/ComposeOwner.kt`: the project
|
- `compose/ui/ui/src/commonMain/.../node/impl/ComposeOwner.kt`: the project
|
||||||
`Owner` + `GraphicsLayerOwnerLayer` bridge.
|
`Owner` + `GraphicsLayerOwnerLayer` bridge.
|
||||||
- `compose/ui/ui/src/nativeMain/.../ui/text/SdlParagraph.native.kt`: the bridged
|
- `compose/ui/ui-text/src/nativeMain/.../ui/text/SkiaParagraph.native.kt`: the
|
||||||
`Paragraph` (measurement, hit-test, line metrics, span painting).
|
upstream `Paragraph` actual (measurement, hit-test, line metrics, span painting).
|
||||||
|
|||||||
@@ -0,0 +1,402 @@
|
|||||||
|
# Skiko on mingwX64 — Feasibility & Effort Assessment
|
||||||
|
|
||||||
|
> **STATUS: SHIPPED (Route 1a).** The real Skia leg now runs on mingwX64 via the
|
||||||
|
> bitsycore skiko fork + `skiko-windows-x64.dll`. This document is retained as
|
||||||
|
> **historical design context** — the feasibility findings and route trade-offs
|
||||||
|
> that led to the shipped implementation.
|
||||||
|
|
||||||
|
Investigation of the effort to run the real Skia leg (`org.jetbrains.skiko`)
|
||||||
|
on the Kotlin/Native **mingwX64** target, under the relaxed constraint that a
|
||||||
|
**DLL is acceptable** (the static / no-DLL invariant may be dropped). Companion
|
||||||
|
to [RENDERER.md](RENDERER.md) §4 "Track A" — which this assessment **partly
|
||||||
|
supersedes** (see [Corrections](#corrections-to-renderermd-4) below).
|
||||||
|
|
||||||
|
Pins at time of writing: skiko `0.150.1` (Skia m150), kotlin `2.4.0`,
|
||||||
|
compose `1.12.0-beta02`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bottom line
|
||||||
|
|
||||||
|
Feasible — and **more tractable than RENDERER.md §4 claims**. Two of that
|
||||||
|
section's load-bearing premises are now factually stale:
|
||||||
|
|
||||||
|
1. **"Building C++20 Skia to a GNU/mingw archive is an open-ended fork (only
|
||||||
|
abandoned Mozilla-era precedent)."** — **False.** MSYS2 maintains
|
||||||
|
`mingw-w64-skia` at **Skia milestone 143 (C++20)** for `mingw64` (GCC),
|
||||||
|
`ucrt64`, and `clang64` (clang → `x86_64-w64-windows-gnu`), producing
|
||||||
|
`libskia.dll` + a GNU import lib `libskia.dll.a`. Last rebuilt **2026-07-24**.
|
||||||
|
The GNU-ABI recipe is ~8 small GN patches (the key one adds
|
||||||
|
`is_mingw = is_win && (cxx=="g++"||cxx=="clang++")`).
|
||||||
|
2. **"The only working route is a runtime DLL, which breaks the no-DLL
|
||||||
|
invariant."** — The DLL route is real, but the reason it works is **not**
|
||||||
|
what the section implies. See the crux below.
|
||||||
|
|
||||||
|
**Effort:** ~**1–2 weeks** to a proven spike + a CPU-raster Skia-on-Windows
|
||||||
|
prototype; ~**1–2 months** to a tested, GPU-accelerated Windows Skia leg with
|
||||||
|
published fork klibs and repo wiring — **plus** permanent
|
||||||
|
`bitsycore/skiko`-fork maintenance (re-sync at every skiko bump). Difficulty:
|
||||||
|
**research-grade / high**, because no working prior art exists (the two GitHub
|
||||||
|
`skiko-mingw64` forks are vaporware — 0 commits ahead of upstream, no mingw
|
||||||
|
source set). Confidence: **medium** (high on mechanism, medium on timeline).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Spike result — EXECUTED, GREEN ✅ (2026-07-24)
|
||||||
|
|
||||||
|
The core de-risking experiment has been **run on this machine** and passed. The
|
||||||
|
whole Route 1a link mechanism works on the exact target toolchain (Kotlin
|
||||||
|
**2.4.0**, mingwX64, LLVM **21** `ld.lld`, `x86_64-pc-windows-gnu`).
|
||||||
|
|
||||||
|
**What was built** (`spike-skiko-mingw/` in this repo):
|
||||||
|
- A tiny DLL reproducing skiko's binding shape — an **opaque handle over a real
|
||||||
|
C++/STL object** (`std::string`), **alloc+free paired inside the DLL**, flat
|
||||||
|
`extern "C"` surface. Compiled **MSVC-ABI via `clang-cl /LD /MT /EHsc`**
|
||||||
|
(static CRT). Exports verified undecorated: `spike_make/size/free`.
|
||||||
|
- A **GNU import library** (`libspike.dll.a`) generated with `dlltool`.
|
||||||
|
- A **Kotlin/Native mingwX64 executable** binding it via cinterop (plain C
|
||||||
|
header) + `linkerOpts("-L… -lspike")`.
|
||||||
|
|
||||||
|
**Results:**
|
||||||
|
- `linkReleaseExecutableMingwX64` **linked clean** — **KT-65671 did NOT bite**
|
||||||
|
(the DLL path puts zero C++ objects in the K/N link graph, so the
|
||||||
|
`--allow-multiple-definition` / `.drectve __clang_call_terminate` failures
|
||||||
|
never arise). LLVM 21 `ld.lld` is also far newer than the 1.9.x-era reports.
|
||||||
|
- Runtime: `SPIKE-RESULT size=7 expected=7 ok=true`, exit 0 — the C++
|
||||||
|
`std::string(7,'x').size()` round-trips through the opaque handle correctly.
|
||||||
|
- `objdump -p` confirms the exe imports `spike.dll`, and `spike.dll` depends on
|
||||||
|
**`KERNEL32.dll` only** → **self-contained, no VC++ redist needed**. The exe
|
||||||
|
carries K/N's `msvcrt.dll` while the DLL carries its own static MSVC CRT —
|
||||||
|
**two CRTs coexist in one process** with no issue (ownership paired inside the
|
||||||
|
DLL), empirically confirming the CRT-boundary discipline.
|
||||||
|
|
||||||
|
**Implication:** the one load-bearing unknown (does the DLL route link on
|
||||||
|
2.4.0?) is resolved **in favour**. Route 1a is de-risked at the mechanism level;
|
||||||
|
remaining effort is engineering the real `skiko.dll` (compile skiko's shim +
|
||||||
|
skia-pack MSVC Skia into one DLL) and repo wiring — not proving feasibility.
|
||||||
|
Feasibility confidence is now **high**; residual medium confidence is on
|
||||||
|
timeline only.
|
||||||
|
|
||||||
|
Reproduce: `./gradlew -p spike-skiko-mingw linkReleaseExecutableMingwX64`, then
|
||||||
|
run the exe with `spike-skiko-mingw/native` on `PATH`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The crux — and why "just use a DLL" is only half the story
|
||||||
|
|
||||||
|
**The blocker is a C++ ABI mismatch at the binding boundary, and it is
|
||||||
|
orthogonal to static-vs-dynamic linkage.**
|
||||||
|
|
||||||
|
- **K/N mingwX64 is GNU/Itanium ABI.** `konan.properties`:
|
||||||
|
`targetTriple.mingw_x64 = x86_64-pc-windows-gnu`,
|
||||||
|
`linker.mingw_x64 = ld.lld.exe`,
|
||||||
|
`linkerKonanFlags.mingw_x64 = -static-libgcc -static-libstdc++ … -lwinpthread`
|
||||||
|
— GCC libstdc++/libgcc, Itanium mangling (`_Z3fooi`).
|
||||||
|
- **skia-pack's only Windows Skia is MSVC-ABI** — built with `clang-cl`,
|
||||||
|
`is_trivial_abi=false`, shipped as MSVC `.lib`; C++ exports are MSVC-mangled
|
||||||
|
(`?foo@@YAHH@Z`).
|
||||||
|
|
||||||
|
LLVM issue #60847 ("LLD cannot link against MSVC libraries in MinGW toolchain",
|
||||||
|
closed *not planned*) confirms the two mangling schemes never reconcile.
|
||||||
|
**Wrapping the same MSVC C++ Skia in a DLL does not change this** — a DLL's C++
|
||||||
|
exports are still MSVC-mangled. So dynamic-vs-static is a red herring; **the C++
|
||||||
|
ABI at whatever boundary the mangled C++ crosses is the wall.**
|
||||||
|
|
||||||
|
What makes it tractable is *where that boundary sits in skiko*:
|
||||||
|
|
||||||
|
- **skiko's Kotlin↔native boundary is already a flat, unmangled C symbol
|
||||||
|
boundary.** Kotlin declares
|
||||||
|
`@ExternalSymbolName("org_jetbrains_skia_Data__1nSize") external fun _nSize(…)`;
|
||||||
|
`ExternalSymbolName` is a typealias for `kotlin.native.SymbolName`, which
|
||||||
|
resolves to a *plain named symbol* at link. The generated `.def` is
|
||||||
|
header-less (only `linkerOpts`). **No C++ type ever crosses into Kotlin.**
|
||||||
|
- **The C++ ABI coupling lives one layer down**, at the *shim↔Skia* link:
|
||||||
|
~78 `extern "C"` (`SKIKO_EXPORT`) `.cc` files under `src/nativeJsMain/cpp/`
|
||||||
|
that `reinterpret_cast<SkData*>(ptr)->size()` etc. This shim **must share one
|
||||||
|
C++ ABI with Skia** — today they're one GNU-ABI island fused via
|
||||||
|
`-include-binary`.
|
||||||
|
|
||||||
|
**Consequence:** a DLL is viable **iff** the *entire* C++ island (shim + Skia)
|
||||||
|
is one consistent ABI internally, and only the **flat extern-C export surface**
|
||||||
|
crosses to K/N via a generated import library. On x86_64 there is a single
|
||||||
|
Windows calling convention and extern-C exports are undecorated on both MSVC and
|
||||||
|
mingw, so `ld.lld` links such a DLL via `gendef`+`dlltool` (or directly, LLD
|
||||||
|
13+). **The repo already does exactly this for system DLLs** (`sdl3.def`:
|
||||||
|
`linkerOpts.mingw_x64 = -lkernel32 -luser32 -lgdi32 …`).
|
||||||
|
|
||||||
|
Crucially the extern-C façade **already exists** (the `nativeJsMain/cpp` shim
|
||||||
|
exports precisely the `org_jetbrains_skia_*` symbols the K/N side binds) — it is
|
||||||
|
**reused, not authored anew**. The catch: no ownership / exceptions / STL may
|
||||||
|
cross the C line — skiko is already handle/RefCnt-based, so this holds if
|
||||||
|
authored carefully. (The JVM `jvmMain/cpp` bridge exports `Java_…` JNI symbols
|
||||||
|
and is **not** reusable — the *native* shim is the right one.)
|
||||||
|
|
||||||
|
The non-negotiable that survives all of this: **upstream skiko ships no
|
||||||
|
mingwX64 K/N target at all** (`throw GradleException("$os not yet supported")`
|
||||||
|
for non-{Mac,iOS,tvOS,Linux}; "Windows" in skiko means the JVM/AWT clang-cl
|
||||||
|
path). So this is a **from-scratch skiko build-system port**, not a flag flip.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The dominant risk: KT-65671
|
||||||
|
|
||||||
|
`KT-65671` ("Kotlin/Native: Failed linking cinterop static library for
|
||||||
|
mingwX64", **Open**, filed against 1.9.x, status on 2.4.0 unverified): K/N's
|
||||||
|
mingw link step chokes when **fusing an external C++ static archive** —
|
||||||
|
`ld.lld` rejects `--allow-multiple-definition`, and
|
||||||
|
`-exclude-symbols:__clang_call_terminate` in the objects is rejected in
|
||||||
|
`.drectve`. This is skiko's **exact static-fusion pattern**.
|
||||||
|
|
||||||
|
Key implication for route choice: a **pure extern-C DLL boundary sidesteps this
|
||||||
|
bug class entirely** — no C++ objects enter the K/N link graph; only a flat
|
||||||
|
import surface crosses. So, counter-intuitively, the **DLL route is the
|
||||||
|
risk-*reducing* route** w.r.t. KT-65671, and it is exactly what the user is
|
||||||
|
willing to accept.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Routes
|
||||||
|
|
||||||
|
### ★ Recommended — DLL with a flat extern-C export, K/N binds via import lib
|
||||||
|
|
||||||
|
Put **all** the C++ (skiko's existing shim + Skia) inside one DLL; export only
|
||||||
|
the flat `org_jetbrains_skia_*` symbols; bind from a new mingwX64 skiko target
|
||||||
|
via a generated import lib. K/N resolves at link; the Windows loader binds
|
||||||
|
`skiko.dll` at process start — **no dlopen/`staticLoad` code needed** (the
|
||||||
|
shipped `staticLoad()` no-op is fine for the import-lib path).
|
||||||
|
|
||||||
|
Two sub-choices for the DLL internals (K/N neither knows nor cares which):
|
||||||
|
|
||||||
|
- **1a — MSVC internals (minimal fork).** Recompile skiko's `nativeJsMain/cpp`
|
||||||
|
shim with `clang-cl` against **skia-pack's existing MSVC Windows `.lib`**
|
||||||
|
(`windows-x64` archives are already published). **Zero Skia fork.** Net-new:
|
||||||
|
add `__declspec(dllexport)` to `SKIKO_EXPORT` on this build, a mingw/DLL
|
||||||
|
branch in skiko's build logic, import-lib generation. CRT discipline (MSVC CRT
|
||||||
|
in the DLL vs K/N's static libgcc) is a correctness constraint on the C API
|
||||||
|
(handles + primitives only), not a linkability blocker.
|
||||||
|
- **1b — GNU internals.** Build the shim + Skia GNU-ABI (reuse MSYS2's
|
||||||
|
`mingw-w64-skia` patch set) into the DLL. Slightly more Skia-side work, but a
|
||||||
|
single toolchain end-to-end.
|
||||||
|
|
||||||
|
**Effort:** ~2–4 weeks once the spike is green. **Risk: medium** (sidesteps
|
||||||
|
KT-65671). **"Just works" preserved:** none — ships `skiko.dll` (tens of MB)
|
||||||
|
next to `data.kres` (accepted). **Sub-choice 1a is the least-fork option.**
|
||||||
|
|
||||||
|
### Route 2 — GNU-ABI static Skia + real mingwX64 skiko target (purist)
|
||||||
|
|
||||||
|
Fork skia-pack (or reuse MSYS2's patches) to emit **GNU-ABI static** Skia;
|
||||||
|
fork skiko to add a mingwX64 target that recompiles the existing extern-C shim
|
||||||
|
GNU-ABI and **static-fuses** exactly as macOS/Linux do today.
|
||||||
|
|
||||||
|
**Effort:** Skia build itself is now **days** (MSYS2 proves it). skiko port +
|
||||||
|
link reconciliation ~3–6 weeks. **Risk: medium-high** — this route hits
|
||||||
|
**KT-65671 head-on** (static C++ archive fusion). **Preserves the static /
|
||||||
|
no-DLL invariant** — the only reason to prefer it, and the user said they don't
|
||||||
|
need it.
|
||||||
|
|
||||||
|
### Route 3 — CPU-raster-only (a scope reduction, not an ABI shortcut)
|
||||||
|
|
||||||
|
The repo's `SkiaSurfaceBridge` (`Surface.makeRasterDirect` → `SDL_UpdateTexture`
|
||||||
|
→ `SDL_RenderTexture`) needs **no GPU context** and compiles on mingwX64 the
|
||||||
|
moment a skiko klib exists. Use it to **skip authoring the Windows GPU bridge**
|
||||||
|
for a first cut. Still requires solving Route 1/2's binding problem first.
|
||||||
|
**Saves ~1–2 weeks** of GPU-bridge work → the natural **milestone 1** of
|
||||||
|
whichever route you pick.
|
||||||
|
|
||||||
|
### Baseline — do nothing (already shipping)
|
||||||
|
|
||||||
|
Windows renders via the from-scratch **SDL leg** (`SDL_RenderGeometry` +
|
||||||
|
SDL3_ttf + FreeType); the **JVM parity target** is the full-fidelity Skia
|
||||||
|
reference. The bar any Skia-on-Windows work must clear is "materially better
|
||||||
|
than the SDL leg's fidelity, worth the DLL + maintenance cost."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommended work breakdown (Route 1a, CPU-raster first)
|
||||||
|
|
||||||
|
**A. Skia — none (1a) / days (1b).**
|
||||||
|
Route 1a reuses skia-pack's published `windows-x64` MSVC `.lib`. (1b: reuse the
|
||||||
|
MSYS2 `mingw-w64-skia` PKGBUILD + patches at skiko's pinned milestone; backends
|
||||||
|
CPU-raster + `skia_use_gl`, `skia_use_freetype`.)
|
||||||
|
|
||||||
|
**B. `bitsycore/skiko` fork — the bulk, medium risk.**
|
||||||
|
1. Add `mingwX64()` + a `configureNativeTarget(OS.Windows, Arch.X64, …)` branch;
|
||||||
|
replace the `throw GradleException("$os not yet supported")` / empty
|
||||||
|
`linkerFlags` fall-throughs in `NativeTasksConfiguration.kt` with a Windows
|
||||||
|
branch (PE/COFF, `llvm-ar`).
|
||||||
|
2. Compile the **existing** `nativeJsMain/cpp` shim (not the JVM JNI bridge)
|
||||||
|
into `skiko.dll`; make `SKIKO_EXPORT` export the flat symbols
|
||||||
|
(`__declspec(dllexport)` or `--export-all-symbols`/def file).
|
||||||
|
3. Generate the GNU import lib (`gendef` + `dlltool` → `libskiko.dll.a`).
|
||||||
|
4. cinterop `.def` stays header-less (`linkerOpts`/`libraryPaths`), matching the
|
||||||
|
repo's own idiom. Publish mingwX64 klibs + the DLL.
|
||||||
|
|
||||||
|
**C. Windows context actual — deferred behind milestone 1.**
|
||||||
|
5. **Milestone 1:** wire only the CPU-raster `SkiaSurfaceBridge` (already
|
||||||
|
Windows-ready).
|
||||||
|
6. **Milestone 2:** author `SkiaD3D11Bridge` / `SkiaVulkanBridge` — extract HWND
|
||||||
|
via `SDL_GetWindowProperties`, wrap `DirectContext.makeD3D11`/`makeVulkan`,
|
||||||
|
mirror the Metal/GL bridges' per-frame acquire/present.
|
||||||
|
|
||||||
|
**D. This repo's wiring — small, low risk (integration points confirmed).**
|
||||||
|
7. `compose/ui/ui/build.gradle.kts` — flip `isSkiaTarget("mingwX64")` to `true`
|
||||||
|
(currently hardcoded `false`, ~line 58); create a `skikoRendererMingwMain`
|
||||||
|
and `mingwX64Main.dependsOn(...)` it (today mingw attaches only to
|
||||||
|
`sdlRendererMingwMain`, lines ~203–206; the skiko tree, lines ~220–235, is
|
||||||
|
created only under `!useSdl3Everywhere` and never for mingw). Add the fork's
|
||||||
|
coords to that source set.
|
||||||
|
8. **No new expect/actual needed:** `createRenderBackend` /
|
||||||
|
`rendererPreferredGpuMode` are `expect`s in `nativeMain` with `actual`s in
|
||||||
|
`skikoRendererMain` and `sdlRendererMain`; attaching mingwX64 to the skiko
|
||||||
|
source set makes the Skia actual resolve for Windows (one renderer source set
|
||||||
|
per target — the existing invariant).
|
||||||
|
9. Add a Windows GPU-bridge branch to `rendererPreferredGpuMode()`; verify with
|
||||||
|
the demo/apidemo (`--gpu`, `--screenshot`) and the parity harness.
|
||||||
|
|
||||||
|
Repo surface to cover is narrow: **~67 `org.jetbrains.skia` classes + 5
|
||||||
|
`org.jetbrains.skiko.node` APIs** (`RenderNode`, `RenderNodeContext`,
|
||||||
|
`SkikoRenderDelegate`, `SystemTheme`, `currentSystemTheme`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The one spike to run first (~1–2 days)
|
||||||
|
|
||||||
|
Before any fork work, **empirically test the K/N-mingw link path**: build a
|
||||||
|
trivial `extern "C"` C++ DLL (one function returning a handle + one that uses
|
||||||
|
it) — or consume MSYS2's `mingw-w64-x86_64-skia` DLL + `libskia.dll.a`
|
||||||
|
directly — from a throwaway mingwX64 K/N project via cinterop + import lib, and
|
||||||
|
**run it**. This answers, in a day instead of a month:
|
||||||
|
|
||||||
|
- Does K/N mingw link + call a GNU-ABI DLL via an import lib at all?
|
||||||
|
- Does **KT-65671** reproduce on Kotlin 2.4.0 (import-lib vs static fusion)?
|
||||||
|
- DLL vs static-fusion behaviour on this toolchain.
|
||||||
|
|
||||||
|
If green, Routes 1/2 are de-risked. (Caveat: MSYS2 Skia exports Skia's *mangled
|
||||||
|
C++* symbols, so a direct cinterop of it tests only the *link mechanism*, not
|
||||||
|
the skiko API — for the API you still need the extern-C shim. The trivial-C-DLL
|
||||||
|
variant is the cleaner mechanism test.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Biggest risks / unknowns
|
||||||
|
|
||||||
|
1. **KT-65671** — whether K/N's mingw link works on 2.4.0 for external C++;
|
||||||
|
the DLL route sidesteps it, the static route hits it. *The single fact that
|
||||||
|
swings the effort.*
|
||||||
|
2. **Toolchain matching** — Konan bundles LLVM 16 clang + `ld.lld`; the C++20
|
||||||
|
Skia + shim must be built by a toolchain whose objects/imports LLD accepts.
|
||||||
|
3. **CRT / ownership discipline** across the C boundary (only matters for 1a).
|
||||||
|
4. **Maintenance** — SKIKO-446 / SKIKO-611 are Open, unassigned → almost
|
||||||
|
certainly a **permanent `bitsycore/skiko` fork** with per-bump re-sync tax.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommendation
|
||||||
|
|
||||||
|
Pursue **Route 1a** (skiko fork producing a DLL with a flat extern-C export over
|
||||||
|
skia-pack's existing MSVC Windows Skia), **scoped CPU-raster first** (Route 3
|
||||||
|
milestone), GPU bridge second. It matches the user's accepted DLL tolerance,
|
||||||
|
requires **zero Skia fork**, and **dodges KT-65671**. Keep Route 2 (static,
|
||||||
|
GNU-ABI) in reserve only if reclaiming the no-DLL invariant later becomes
|
||||||
|
valuable. **Run the MSYS2/trivial-DLL spike before committing to any fork.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation status — WORKING END-TO-END ✅ (2026-07-24)
|
||||||
|
|
||||||
|
**`demo.exe` (mingwX64) renders the Material 3 Buttons screen through real Skia**
|
||||||
|
and wrote a 1000×700 screenshot ("settled at frame 3"), using the CPU-raster
|
||||||
|
`SkiaSurfaceBridge` backed by the forked `skiko-windows-x64.dll`. Route 1a is
|
||||||
|
complete. Build/run:
|
||||||
|
|
||||||
|
```
|
||||||
|
# fork (C:/Dev/skiko), one-time env: SKIKO_VSBT_PATH=<VS BuildTools dir>
|
||||||
|
./gradlew :skiko:publishKotlinMultiplatformPublicationToMavenLocal \
|
||||||
|
:skiko:publishMingwX64PublicationToMavenLocal -Pskiko.native.windows.enabled=true
|
||||||
|
# compose repo
|
||||||
|
./gradlew :demo:linkDebugExecutableMingwX64 -PwindowsSkia=true
|
||||||
|
cp <fork>/skiko/build/out/link/Release-windows-native-x64/skiko-windows-x64.dll <demo exe dir>/
|
||||||
|
<demo exe dir>/demo.exe --screenshot=out.bmp --screen=Buttons
|
||||||
|
```
|
||||||
|
|
||||||
|
Fork fixes needed beyond the DLL build (skiko uses a CUSTOM hierarchy template,
|
||||||
|
`applyDefaultHierarchyTemplate=false`):
|
||||||
|
1. `sourceHierarchy.kt` — add `group("windows"){ withMingwX64() }` under `native`
|
||||||
|
(else the mingwX64 klib compiles EMPTY — missing all of `org.jetbrains.skia`).
|
||||||
|
2. `SkikoProjectContext` — add `supportNativeWindows` into `supportAnyNative` so
|
||||||
|
the `@SymbolName` opt-in reaches the mingw compile.
|
||||||
|
3. `Resources.native.kt` — `ftell` is 32-bit `Int` on Windows (LLP64); `==` fix.
|
||||||
|
4. `windowsMain` actuals for `SkiaLayer` / `currentSystemTheme` (linux is a stub).
|
||||||
|
5. Force-export the 2 ICU symbols (`uloc_getDefault_skiko`,
|
||||||
|
`uloc_toLanguageTag_skiko`) that Kotlin binds via `@SymbolName`.
|
||||||
|
|
||||||
|
Compose-repo wiring (all behind `-PwindowsSkia=true`): `mavenLocal()` in
|
||||||
|
settings, `isSkiaTarget("mingwX64")`, a separate `skikoRendererMingwMain` tree
|
||||||
|
on the fork's **root** coord (`org.jetbrains.skiko:skiko:0.0.0-SNAPSHOT` — the
|
||||||
|
platform artifact alone doesn't expose api-elements), `PlatformGpu` Windows →
|
||||||
|
`Software`, and a one-line `PlatformGpu.mingw.kt`.
|
||||||
|
|
||||||
|
## Earlier milestone — fork BUILDS ✅ (2026-07-24)
|
||||||
|
|
||||||
|
Route 1a is not just designed — the `bitsycore/skiko` fork now **builds the
|
||||||
|
native Windows Skia DLL + a mingwX64 klib** on this machine (`C:/Dev/skiko`,
|
||||||
|
skiko `v0.150.1`).
|
||||||
|
|
||||||
|
Edits made (all in the fork):
|
||||||
|
- `src/nativeJsMain/cpp/common.h` — `SKIKO_EXPORT` gains `__declspec(dllexport)`
|
||||||
|
under a `SKIKO_WINDOWS_DLL` guard.
|
||||||
|
- `skiko/build.gradle.kts` — `configureNativeTarget(OS.Windows, Arch.X64,
|
||||||
|
mingwX64())`, gated behind `-Pskiko.native.windows.enabled=true`.
|
||||||
|
- `buildSrc/.../NativeTasksConfiguration.kt` — a Windows compile branch
|
||||||
|
(clang-cl + `-DSKIKO_WINDOWS_DLL`) and a new `configureWindowsNativeTarget`
|
||||||
|
that links `skiko-windows-x64.dll` via `lld-link` (reusing the JVM template +
|
||||||
|
`resolveBinaryInputs(…, TargetEnv.JVM, …)`), auto-generates the GNU import lib
|
||||||
|
(`dumpbin`→`.def`→`dlltool`), and wires the K/N cinterop `linkerOpts`.
|
||||||
|
|
||||||
|
Results:
|
||||||
|
- The native bridge (`nativeJsMain/cpp`) **compiled on Windows with clang-cl,
|
||||||
|
zero source changes.** The only link fix was adding `d3d12.lib
|
||||||
|
d3dcompiler.lib dxgi.lib` for Skia's `SK_DIRECT3D` backend.
|
||||||
|
- Produced **`skiko-windows-x64.dll` (14.2 MB, 1005 exported
|
||||||
|
`org_jetbrains_skia_*` symbols)**, `libskiko-windows-x64.dll.a` (GNU import
|
||||||
|
lib), and the **mingwX64 klib** (`build/classes/kotlin/mingwX64/main/klib`).
|
||||||
|
|
||||||
|
Prerequisite for any skiko Windows build on this box:
|
||||||
|
`SKIKO_VSBT_PATH='C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools'`
|
||||||
|
(Gradle's VS locator doesn't auto-find VS 18 BuildTools).
|
||||||
|
|
||||||
|
Remaining: publish the fork to `mavenLocal` → a tiny K/N mingw consumer that
|
||||||
|
calls a real Skia function (links the import lib, runs with the DLL on `PATH`)
|
||||||
|
for the definitive end-to-end proof → then wire the compose repo (flip
|
||||||
|
`isSkiaTarget("mingwX64")`, add `skikoRendererMingwMain`, ship the DLL next to
|
||||||
|
the exe, CPU-raster `SkiaSurfaceBridge` first). One known TODO: the klib's
|
||||||
|
`linkerOpts` use an absolute import-lib path (fine same-machine; make relative /
|
||||||
|
bundled for distribution).
|
||||||
|
|
||||||
|
## Corrections to RENDERER.md §4
|
||||||
|
|
||||||
|
The shelving note should be updated — its two central technical claims are stale
|
||||||
|
as of 2026-07:
|
||||||
|
|
||||||
|
- "GNU-ABI Skia is an open-ended fork / only abandoned Mozilla-era precedent" —
|
||||||
|
**false**: MSYS2 maintains a modern (m143, C++20) GNU-ABI Skia, rebuilt
|
||||||
|
routinely.
|
||||||
|
- The accurate residual blocker is **the K/N-side link/binding port**
|
||||||
|
(KT-65671 + authoring the mingwX64 skiko target), **not** building Skia, and
|
||||||
|
the DLL route is *risk-reducing*, not merely "the only working route."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sources (primary, load-bearing)
|
||||||
|
|
||||||
|
- skiko v0.150.1: `skiko/build.gradle.kts`, `buildSrc/.../NativeTasksConfiguration.kt`,
|
||||||
|
`src/commonMain/kotlin/org/jetbrains/skia/Data.kt`,
|
||||||
|
`src/nativeMain/kotlin/org/jetbrains/skia/Actuals.native.kt`,
|
||||||
|
`src/nativeJsMain/cpp/common.h`, `.../impl/Library.native.kt`
|
||||||
|
- skia-pack `script/build.py`; skia.org build docs
|
||||||
|
- Kotlin `konan.properties` (v2.2.0); kotlinlang native-c-interop / target-support
|
||||||
|
- LLVM issue #60847; **KT-65671**; **SKIKO-446**, **SKIKO-611**
|
||||||
|
- MSYS2 `mingw-w64-skia` (packages.msys2.org; MINGW-packages PKGBUILD + patches)
|
||||||
|
- Vaporware forks: github.com/Cdm2883/skiko-mingw64, crowforkotlin/skiko-mingw64
|
||||||
|
- Repo: `compose/ui/ui/build.gradle.kts`, the `SkiaMetalBridge` / `SkiaGLBridge`
|
||||||
|
/ `SkiaSurfaceBridge` / `RenderBackendFactory` seam
|
||||||
@@ -2,9 +2,8 @@
|
|||||||
|
|
||||||
Everything the port currently leaves as a no-op, stub, hardcode, or partial
|
Everything the port currently leaves as a no-op, stub, hardcode, or partial
|
||||||
implementation, plus the gaps worth closing before a stable 1.12 release. This
|
implementation, plus the gaps worth closing before a stable 1.12 release. This
|
||||||
is an audit of PROJECT code only (the `*.native.kt` / `*.sdl.kt` /
|
is an audit of PROJECT code only (the `*.native.kt` / `com.compose.sdl.*`
|
||||||
`com.compose.sdl.*` actuals); vendored upstream under `src/vendor/` is out of
|
actuals); vendored upstream under `src/vendor/` is out of scope.
|
||||||
scope.
|
|
||||||
|
|
||||||
Severity is a rough guide, not a mandate:
|
Severity is a rough guide, not a mandate:
|
||||||
|
|
||||||
@@ -14,10 +13,10 @@ Severity is a rough guide, not a mandate:
|
|||||||
- **Cosmetic**: minor fidelity or edge-case behavior.
|
- **Cosmetic**: minor fidelity or edge-case behavior.
|
||||||
|
|
||||||
Context for prioritizing: the project goal is **G1, cheap upstream-tracking**
|
Context for prioritizing: the project goal is **G1, cheap upstream-tracking**
|
||||||
(see [RENDERER.md](RENDERER.md)). The **JVM Compose Desktop target is the
|
(see [RENDERER.md](RENDERER.md)). There is now ONE renderer — **Skia**
|
||||||
documented Windows fidelity/feature tier**, so SDL-leg visual gaps (section H)
|
(macOS/Linux official Skiko; mingwX64 the bitsycore skiko fork), so Windows
|
||||||
are lower priority than the cross-cutting platform gaps that affect every
|
renders through real Skia. The priorities below are the cross-cutting platform
|
||||||
backend including Skia on macOS/Linux.
|
gaps that affect every OS. JVM Compose Desktop remains a parity cross-check.
|
||||||
|
|
||||||
Renderer-internal cleanups (the deferred D2 shadow-lighting split, D3 matrix
|
Renderer-internal cleanups (the deferred D2 shadow-lighting split, D3 matrix
|
||||||
dedupe) live in [RENDERER.md](RENDERER.md#6-remaining-and-future-work), not
|
dedupe) live in [RENDERER.md](RENDERER.md#6-remaining-and-future-work), not
|
||||||
@@ -37,28 +36,13 @@ Platform / cross-cutting:
|
|||||||
via SDL (I-beam over text, hand over links).
|
via SDL (I-beam over text, hand over links).
|
||||||
- **WindowInfo** (`bad23073`) — `isWindowFocused` + `containerSize` / `containerDpSize`
|
- **WindowInfo** (`bad23073`) — `isWindowFocused` + `containerSize` / `containerDpSize`
|
||||||
report real values (were hardcoded `true` / `Zero`).
|
report real values (were hardcoded `true` / `Zero`).
|
||||||
- **ImageBitmap from bytes** (SDL leg) — `ByteArray.decodeToImageBitmap()` /
|
- **ImageBitmap from bytes** — `ByteArray.decodeToImageBitmap()` /
|
||||||
`createImageBitmap(bytes)` decodes via SDL3_image; was
|
`createImageBitmap(bytes)` decodes encoded image bytes; was
|
||||||
`UnsupportedOperationException`. Verified `demo --imagebytestest` on both legs.
|
`UnsupportedOperationException`. Verified `demo --imagebytestest`.
|
||||||
- **`FontFamily.Monospace`** — generic families map to `"generic:<name>"`;
|
- **`FontFamily.Monospace`** — generic families map to `"generic:<name>"`;
|
||||||
`registerGenericFonts` registers bundled NotoSansMono under `generic:monospace`
|
`registerGenericFonts` registers bundled NotoSansMono under `generic:monospace`
|
||||||
(bundled only when the app references it). Verified `demo --fonttest`.
|
(bundled only when the app references it). Verified `demo --fonttest`.
|
||||||
|
|
||||||
SDL renderer fidelity (all verified against the Skia leg via `demo --<name>test`):
|
|
||||||
|
|
||||||
- **DashPathEffect** (`--dashtest`), **gradient TileMode** repeat/mirror/decal
|
|
||||||
(`--tilemodetest`), **drawPoints/drawRawPoints** Points/Lines/Polygon
|
|
||||||
(`--pointstest`), **rotated images** via textured `SDL_RenderGeometry`
|
|
||||||
(`--rotimgtest`).
|
|
||||||
- **BlendMode** (`774271eb`, `--blendtest`) — Plus/Modulate/Multiply/Src via SDL
|
|
||||||
hardware blend (`withBlend`); other separable modes fall back to SrcOver.
|
|
||||||
- **Stroke joins + Square cap** (`3518521c`, `--jointest`) — miter/bevel/round
|
|
||||||
(`emitJoin`) + round/square end caps (`emitCap`), pixel-identical to Skia.
|
|
||||||
- **ColorFilter on shapes** (`169a51f0`, `--filtertest`) — tint / colorMatrix
|
|
||||||
(grayscale/saturation) / lighting per-vertex; pixel-exact vs Skia.
|
|
||||||
- **Stroked elliptical ovals** (`efaf20d9`, `--ovaltest`) — true ellipse band, not
|
|
||||||
a circular ring on the averaged radius.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Release-blocking shortlist for stable 1.12
|
## Release-blocking shortlist for stable 1.12
|
||||||
@@ -70,7 +54,7 @@ The cross-cutting items to weigh first (all detailed below):
|
|||||||
menu, upstream TODO CMP-7819).
|
menu, upstream TODO CMP-7819).
|
||||||
2. **Accessibility is entirely absent** (section A). Decide whether stable
|
2. **Accessibility is entirely absent** (section A). Decide whether stable
|
||||||
requires any screen-reader support at all.
|
requires any screen-reader support at all.
|
||||||
3. Fonts (section D): compose-resources `Font(Res.font.x)` works on both legs, and
|
3. Fonts (section D): compose-resources `Font(Res.font.x)` works, and
|
||||||
`FontFamily.Monospace` now renders NotoSansMono. Remaining: `Serif`/`Cursive`
|
`FontFamily.Monospace` now renders NotoSansMono. Remaining: `Serif`/`Cursive`
|
||||||
generics (no bundled font) and the androidx `Font(bytes)`/`PlatformFontLoader`
|
generics (no bundled font) and the androidx `Font(bytes)`/`PlatformFontLoader`
|
||||||
path. Nice-to-have, not a blocker.
|
path. Nice-to-have, not a blocker.
|
||||||
@@ -110,15 +94,14 @@ calendar/clock/number prefs). These require a bundled CLDR subset or a K/N i18n
|
|||||||
- `:62` `getDateInputFormat()` hardcoded `yyyy-MM-dd` / `'-'`. **Nice-to-have.**
|
- `:62` `getDateInputFormat()` hardcoded `yyyy-MM-dd` / `'-'`. **Nice-to-have.**
|
||||||
- `:65` `is24HourFormat()` hardcoded `true`. **Cosmetic.**
|
- `:65` `is24HourFormat()` hardcoded `true`. **Cosmetic.**
|
||||||
- `compose/ui/ui/src/nativeMain/.../text/platform/NativeStringDelegate.native.kt:17` · `toUpperCase`/`toLowerCase` use locale-independent stdlib casing; the `locale` param is ignored (wrong for Turkish dotted/dotless i, etc.). **Cosmetic.**
|
- `compose/ui/ui/src/nativeMain/.../text/platform/NativeStringDelegate.native.kt:17` · `toUpperCase`/`toLowerCase` use locale-independent stdlib casing; the `locale` param is ignored (wrong for Turkish dotted/dotless i, etc.). **Cosmetic.**
|
||||||
- `compose/ui/ui/src/nativeMain/.../text/intl/Locale.native.kt` · `Locale.isRtl()` not implemented; the pipeline is LTR-only (`SdlParagraph.native.kt` `getBidiRunDirection` always returns `Ltr`; owners/DrawScopes default `LayoutDirection.Ltr`). No RTL shaping. **Nice-to-have.**
|
- `compose/ui/ui/src/nativeMain/.../text/intl/Locale.native.kt` · `Locale.isRtl()` not implemented; the pipeline is LTR-only (`SkiaParagraph.native.kt` hardcodes `textDirection = Ltr`, so `getParagraphDirection` always returns `Ltr`; owners/DrawScopes default `LayoutDirection.Ltr`). No RTL shaping. **Nice-to-have.**
|
||||||
- No `NumberFormat` / currency / decimal-grouping actual exists; numbers render via `toString()` with `.` decimal and no grouping regardless of locale. **Cosmetic.**
|
- No `NumberFormat` / currency / decimal-grouping actual exists; numbers render via `toString()` with `.` decimal and no grouping regardless of locale. **Cosmetic.**
|
||||||
|
|
||||||
## D. Font resolution
|
## D. Font resolution
|
||||||
|
|
||||||
**Custom fonts via compose-resources work.** `org.jetbrains.compose.resources.Font(Res.font.x)`
|
**Custom fonts via compose-resources work.** `org.jetbrains.compose.resources.Font(Res.font.x)`
|
||||||
loads the bytes and registers them with the project font registry (IconFont → NamedFont)
|
loads the bytes and registers them with the project font registry (IconFont → NamedFont),
|
||||||
on both renderer legs (`FontResources.sdl.kt`, shared by the skiko leg via a srcDir
|
so the standard CMP way to bundle a font renders correctly.
|
||||||
alias), so the standard CMP way to bundle a font renders correctly.
|
|
||||||
|
|
||||||
- `FontFamily.Serif` / `FontFamily.Cursive` still collapse to the default sans — no bundled
|
- `FontFamily.Serif` / `FontFamily.Cursive` still collapse to the default sans — no bundled
|
||||||
serif/cursive font yet (`downloadNotoFonts` fetches Sans + SansMono only). Register one under
|
serif/cursive font yet (`downloadNotoFonts` fetches Sans + SansMono only). Register one under
|
||||||
@@ -129,7 +112,7 @@ alias), so the standard CMP way to bundle a font renders correctly.
|
|||||||
|
|
||||||
## E. Text rendering details
|
## E. Text rendering details
|
||||||
|
|
||||||
- `compose/ui/ui/src/nativeMain/.../text/SdlParagraph.native.kt:376` · `drawNativeText` accepts and ignores text `Shadow`, stroke `DrawStyle`, and non-`SrcOver` blend, so those never render on text. **Cosmetic.**
|
- `compose/ui/ui-text/src/nativeMain/.../text/SkiaParagraph.native.kt` · text `Shadow` + `TextDecoration` now render (the `paint(...)` overloads forward them to `ops.rebuildAndPaint`), but stroke `DrawStyle` and non-`SrcOver` `blendMode` are still accepted and ignored, so those never render on text. **Cosmetic.**
|
||||||
- `compose/foundation/.../text/StringHelpers.native.kt:31` and `compose/ui/ui/src/nativeMain/.../text/CharHelpers.native.kt:14` · `findPrecedingBreak`/`findFollowingBreak` (and `offsetByCodePoints`) do a codepoint/surrogate walk only, with no ICU grapheme clusters, so caret movement and backspace split combining marks and emoji ZWJ sequences. **Nice-to-have.**
|
- `compose/foundation/.../text/StringHelpers.native.kt:31` and `compose/ui/ui/src/nativeMain/.../text/CharHelpers.native.kt:14` · `findPrecedingBreak`/`findFollowingBreak` (and `offsetByCodePoints`) do a codepoint/surrogate walk only, with no ICU grapheme clusters, so caret movement and backspace split combining marks and emoji ZWJ sequences. **Nice-to-have.**
|
||||||
|
|
||||||
## F. Drag and drop
|
## F. Drag and drop
|
||||||
@@ -149,56 +132,29 @@ Drop INTO the window (files + text) is fully wired and works. Drag OUT does not.
|
|||||||
- `compose/ui/ui/src/nativeMain/.../node/impl/ComposeOwner.kt:287` · deprecated `clipboardManager` `setText`/`getText` NOP, and `clipboard.getClipEntry` null. Harmless: the real `LocalClipboard` (text + PNG image via SDL3) works. **Cosmetic.**
|
- `compose/ui/ui/src/nativeMain/.../node/impl/ComposeOwner.kt:287` · deprecated `clipboardManager` `setText`/`getText` NOP, and `clipboard.getClipEntry` null. Harmless: the real `LocalClipboard` (text + PNG image via SDL3) works. **Cosmetic.**
|
||||||
- `compose/foundation/.../text/input/internal/selection/TextFieldSelectionState.native.kt:44` · `ClipboardPasteState.hasClip` aliased to `hasText`, so image-only clipboard is not detected for the paste affordance. **Cosmetic.**
|
- `compose/foundation/.../text/input/internal/selection/TextFieldSelectionState.native.kt:44` · `ClipboardPasteState.hasClip` aliased to `hasText`, so image-only clipboard is not detected for the paste affordance. **Cosmetic.**
|
||||||
|
|
||||||
## H. SDL renderer graphics (Windows / `-Prenderer=sdl3` only)
|
## H. Skia renderer anomalies
|
||||||
|
|
||||||
These affect only the SDL leg. On macOS/Linux the Skia leg implements them
|
Skia is the renderer everywhere, so its bugs are rarely tracked here. This one
|
||||||
correctly, and JVM is the documented Windows fidelity tier, so these are lower
|
surfaced while adding BlendMode coverage (`demo --blendtest`):
|
||||||
priority under G1. Listed because SDL is the shipped Windows renderer. (Several
|
|
||||||
SDL fidelity gaps are now closed — see **Completed** above.)
|
|
||||||
|
|
||||||
The biggest remaining value here is the **offscreen-layer work** (blur + real
|
- `BlendMode.Multiply` renders wrong. A filled `drawRect` with opaque
|
||||||
`saveLayer`); the two are the same infrastructure and warrant a dedicated pass
|
`Color.Cyan` over opaque `Color.Yellow` reads back `(0,0,255)` blue; the
|
||||||
with the full parity + verify-mac gate, not a probe.
|
multiply formula can only yield `(0,255,0)` green for opaque cyan×yellow.
|
||||||
|
`Plus` and `Modulate` composite correctly on the same path, so
|
||||||
- **Blur / RenderEffect** — `compose/ui/ui/src/sdlRendererMain/.../graphics/RenderEffect.sdl.kt:16` `RenderEffect.isSupported()` is `false`; `Modifier.blur()` and `graphicsLayer{renderEffect=...}` do nothing; `.../graphics/shadow/Blur.native.kt:33` `Paint.setBlurFilter` NOP. Needs an offscreen render target + a blur pass (box-blur infra already exists in `Sdl3ShadowCache`). **Nice-to-have (Windows visual parity).**
|
`paint.blendMode` IS applied — the fault is specific to `MULTIPLY` (likely a
|
||||||
- **`saveLayer` has no offscreen buffer** — `Sdl3Canvas.kt:436`: layer alpha is multiplied into each primitive, so overlapping content double-composites (wrong group opacity) and the layer paint's colorFilter/blendMode/renderEffect are dropped. Same offscreen infra as blur. **Nice-to-have (blocker for correct group-alpha over overlapping content).**
|
Metal-backend / premultiply interaction in the graphics-layer flatten, not
|
||||||
- **`clipPath` degrades to a bounding box** — `Sdl3Canvas.kt:509`: an arbitrary path clip collapses to its AABB; a rotated/sheared `clipRect` also collapses to an AABB, so non-rect clips leak. (Rounded/difference clips are real, with feathered AA.) **Nice-to-have.**
|
the `BlendMode.toSkia()` map, which is correct). Needs isolating on the Skia
|
||||||
- **`ActualImageShader` / `ActualCompositeShader` are stubs** — `CanvasPaintActuals.native.kt:106`: an image/composite `ShaderBrush` degrades to a solid white/black fill (the tessellator emits per-vertex colours, not UVs). **Nice-to-have.**
|
draw path. **Nice-to-have.**
|
||||||
- **`PathFillType` (NonZero vs EvenOdd)** — mostly ignored beyond the 2-contour border-ring case; interior holes and self-intersections are not cut out, and concave fills self-overlap (fan triangulation) (`Sdl3DrawScope.kt:366,717`). Needs a scanline/tessellation fill. **Nice-to-have.**
|
|
||||||
- **`drawVertices`** (custom vertex meshes) is still a NOP in the DrawScope. **Nice-to-have.**
|
|
||||||
- **ColorFilter/blur on IMAGE blits** — image blits honor only `BlendModeColorFilter` tint (`Sdl3Canvas.kt:1281`); a `ColorMatrix`/lighting filter or blur on a bitmap needs per-pixel work. Capture-mode (`graphicsLayer`) block-replay also drops the shape colorFilter. **Nice-to-have.**
|
|
||||||
- **`SdlImageBitmap.readPixels` is a no-op** (`Sdl3Offscreen.kt:117`) — can't read back an offscreen-rendered bitmap. **Cosmetic / nice-to-have.**
|
|
||||||
- **Drop shadow is approximated** (`Sdl3Canvas.kt:785`) — 9-slice / stacked rings, not a true gaussian; ambient vs spot largely collapsed; AA is a ~1px geometry fringe, not analytic coverage. **Cosmetic.**
|
|
||||||
- `DashPathEffect` `corner`/`chain`/`stamped` variants remain NOP (only the interval dash is implemented). **Cosmetic.**
|
|
||||||
|
|
||||||
## I. Skia renderer (macOS/Linux default) anomalies
|
|
||||||
|
|
||||||
The Skia leg is normally the fidelity reference, so its bugs are rarely tracked
|
|
||||||
here. This one surfaced while adding SDL BlendMode parity (`demo --blendtest`):
|
|
||||||
|
|
||||||
- `BlendMode.Multiply` renders wrong on the Skia leg. A filled `drawRect` with
|
|
||||||
opaque `Color.Cyan` over opaque `Color.Yellow` reads back `(0,0,255)` blue;
|
|
||||||
the multiply formula can only yield `(0,255,0)` green for opaque cyan×yellow,
|
|
||||||
and the SDL leg produces exactly that. `Plus` and `Modulate` composite
|
|
||||||
correctly on the same path, so `paint.blendMode` IS applied — the fault is
|
|
||||||
specific to `MULTIPLY` (likely a Metal-backend / premultiply interaction in
|
|
||||||
the graphics-layer flatten, not the `BlendMode.toSkia()` map, which is
|
|
||||||
correct). Needs isolating on the Skia draw path. **Nice-to-have (Skia leg).**
|
|
||||||
|
|
||||||
## J. Needs further review (2026-07 bug audit)
|
## J. Needs further review (2026-07 bug audit)
|
||||||
|
|
||||||
An audit (3 parallel passes over the renderer, recent SDL features, and the
|
An audit (parallel passes over the renderer and the text/input/event actuals)
|
||||||
text/input/event actuals) surfaced these. The clear, contained bugs were fixed
|
surfaced these. The clear, contained bugs were fixed in the same pass; the items
|
||||||
in the same pass (see below); the items here are left open because they are
|
here are left open because they are either invasive, risky, or a judgement call.
|
||||||
either invasive, risky, or a cosmetic-AA judgement call.
|
(The renderer-side fixes from that pass predate the single-Skia consolidation and
|
||||||
|
are no longer tracked here.)
|
||||||
|
|
||||||
Fixed in the audit (for reference, not open work): the `roundRectCore`
|
Fixed in the audit (for reference, not open work): `Locale.region` no longer
|
||||||
inverted-rect crash; `drawArc(useCenter=false)` now fills the segment (was always
|
|
||||||
a pie sector); `BlendMode.Src` no longer punches the AA fringe out via
|
|
||||||
`SDL_BLENDMODE_NONE`; the cover-fill fast-path no longer drops the paint's
|
|
||||||
`colorFilter`; `saveLayer` in capture mode keeps the save/restore stack balanced;
|
|
||||||
stroked oval / round-rect inner radius is clamped ≥ 0; the per-vertex
|
|
||||||
`ColorMatrix` copy is hoisted out of the sample loop; `Locale.region` no longer
|
|
||||||
returns the language for a single-subtag tag; and a `MOUSE_LEAVE` now clears
|
returns the language for a single-subtag tag; and a `MOUSE_LEAVE` now clears
|
||||||
hover so a widget doesn't stay highlighted after the cursor exits.
|
hover so a widget doesn't stay highlighted after the cursor exits.
|
||||||
|
|
||||||
@@ -208,10 +164,3 @@ Open — platform / input:
|
|||||||
- **A window created unfocused is still promoted to RESUMED** (`ComposeWindow.kt:575-576, 713-719`). `windowFocused`/`windowVisible` default `true`, so a second `Window {}` opened while another holds focus (or one opened minimized) reports `RESUMED` until SDL later delivers a focus/minimize event. Should query `SDL_GetWindowFlags` at creation. **Review.**
|
- **A window created unfocused is still promoted to RESUMED** (`ComposeWindow.kt:575-576, 713-719`). `windowFocused`/`windowVisible` default `true`, so a second `Window {}` opened while another holds focus (or one opened minimized) reports `RESUMED` until SDL later delivers a focus/minimize event. Should query `SDL_GetWindowFlags` at creation. **Review.**
|
||||||
- **IME text-input area is only pushed on the first `TEXT_EDITING`** (`ComposeWindow.kt:803`; `updateImeArea()` not called on focus gain / first `StartTextInput` / caret move). The first candidate popup can appear at (0,0) before correcting. The rect math itself is correct. **Review.**
|
- **IME text-input area is only pushed on the first `TEXT_EDITING`** (`ComposeWindow.kt:803`; `updateImeArea()` not called on focus gain / first `StartTextInput` / caret move). The first candidate popup can appear at (0,0) before correcting. The rect math itself is correct. **Review.**
|
||||||
- **`Snapshot.sendApplyNotifications()` is called synchronously inside the global write observer** (`ComposeWindow.kt:143-146`). Compose Desktop's `GlobalSnapshotManager` deliberately defers it to a separate dispatch; calling it per-write is redundant (the main loop already calls it each frame) and risks re-entrancy during snapshot application. Verify against the runtime's guard before changing — behaviour-sensitive. **Review (risky).**
|
- **`Snapshot.sendApplyNotifications()` is called synchronously inside the global write observer** (`ComposeWindow.kt:143-146`). Compose Desktop's `GlobalSnapshotManager` deliberately defers it to a separate dispatch; calling it per-write is redundant (the main loop already calls it each frame) and risks re-entrancy during snapshot application. Verify against the runtime's guard before changing — behaviour-sensitive. **Review (risky).**
|
||||||
|
|
||||||
Open — SDL renderer (cosmetic AA / draw-time):
|
|
||||||
|
|
||||||
- **Stroke joins are not antialiased and reach ~0.5px past the band** (`Sdl3DrawScope.kt` `emitJoin`). The segment band feathers to `halfW − kAaHalf` solid + fringe, but joins fill solid to full `halfW` with a hard edge, so corners read as slightly darker/crisper nubs — visible on thin or translucent strokes. Needs feathered join geometry. **Review (cosmetic).**
|
|
||||||
- **Round join over-draws the stroke body** (`Sdl3DrawScope.kt` `emitJoin`, `StrokeJoin.Round`): a full disc overlaps the adjacent segments, so a semi-transparent stroke double-blends darker at each corner. Fill only the convex wedge. **Review (cosmetic).**
|
|
||||||
- **Text / icon draws ignore the draw-time `colorFilter`** — `activeColorFilter` is consulted only by the tessellation sampler; glyph/icon runs blit through the text renderer with a pre-resolved colour, so a `BlendModeColorFilter` tint applied at draw time to a `Text`/icon node isn't honored (acceptable if callers pre-fold the tint into the glyph colour). **Review.**
|
|
||||||
- **Rounded-clip containment ignores stroke half-width** (`Sdl3Canvas.kt` `admitDraw`/`tryDrawRectUnderPendingClips`): a stroked shape whose fill-box is inside a pending rounded clip but whose stroke pokes past it skips mask realization, so the stroke's outer half is bounded only by the AABB, not the rounded outline. Marginal (only bites at the exact clip boundary). **Review.**
|
|
||||||
|
|||||||
+24
-17
@@ -8,18 +8,18 @@ For architecture, source-set layout, and vendoring rules, see
|
|||||||
|
|
||||||
## Native libraries
|
## Native libraries
|
||||||
|
|
||||||
SDL3, SDL3_ttf, SDL3_image, and FreeType are built from source as static
|
SDL3 is built from source as a static library and linked straight into the
|
||||||
libraries and linked straight into the executable. One script does it on every
|
executable — the windowing, input, and platform-integration layer. One script
|
||||||
OS. Output lands in the gitignored `libs/`; versions are pinned in
|
does it on every OS. Output lands in the gitignored `libs/`; the version is
|
||||||
`scripts/build-sdl/build-sdl.properties`.
|
pinned in `scripts/build-sdl/build-sdl.properties`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python3 scripts/build-sdl/build-all.py # freetype, sdl3, sdl3-image, sdl3-ttf
|
python3 scripts/build-sdl/build-all.py # sdl3
|
||||||
python3 scripts/build-sdl/build-all.py sdl3-ttf # rebuild one step
|
python3 scripts/build-sdl/build-all.py sdl3 # rebuild the step
|
||||||
```
|
```
|
||||||
|
|
||||||
Needs `git`, `cmake`, and Python 3 on every host (`ninja` is fetched when
|
Needs `git`, `cmake`, and Python 3 on every host (`ninja` is fetched when
|
||||||
absent). Run it once per machine, or after bumping the pinned versions.
|
absent). Run it once per machine, or after bumping the pinned version.
|
||||||
|
|
||||||
## Vendoring upstream Compose
|
## Vendoring upstream Compose
|
||||||
|
|
||||||
@@ -63,16 +63,16 @@ python3 scripts/compose-coverage.py --missing ui-text # list uncovered decl
|
|||||||
### One-command gate
|
### One-command gate
|
||||||
|
|
||||||
`verify-mac.sh` is the runbook to run after any renderer or layout change. On
|
`verify-mac.sh` is the runbook to run after any renderer or layout change. On
|
||||||
macOS or Linux it exercises BOTH renderer legs (Skia by default, then
|
macOS or Linux it exercises the Skia renderer and exits non-zero on any
|
||||||
`-Prenderer=sdl3`) and exits non-zero on any failure:
|
failure:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scripts/verify-mac.sh
|
scripts/verify-mac.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
It runs, per leg: the vendor drift and clean checks, a build of `:demo` and
|
It runs: the vendor drift and clean checks, a build of `:demo` and `:apidemo`,
|
||||||
`:apidemo`, the interaction probes, the parity sweep, a memory soak, and a
|
the interaction probes, the parity sweep, a memory soak, and a frame-time spot
|
||||||
frame-time spot check. The Windows target is verified separately (see below).
|
check. The Windows target is verified separately (see below).
|
||||||
|
|
||||||
### Parity: native vs JVM
|
### Parity: native vs JVM
|
||||||
|
|
||||||
@@ -135,6 +135,13 @@ gradlew.bat :demo:runDebugExecutableMingwX64
|
|||||||
gradlew.bat :apidemo:runDebugExecutableMingwX64
|
gradlew.bat :apidemo:runDebugExecutableMingwX64
|
||||||
```
|
```
|
||||||
|
|
||||||
|
mingwX64 renders through Skia via the **bitsycore skiko fork**, consumed from
|
||||||
|
GitHub Packages as `com.bitsycore.skiko:skiko:0.150.1-mingw.1` (macOS/Linux use
|
||||||
|
official Skiko). The runtime `skiko-windows-x64.dll` is auto-provisioned next
|
||||||
|
to the executable by the bridge plugin — no manual copy. The fork itself is
|
||||||
|
published by a separate GitHub Actions workflow in the fork repo, out of band
|
||||||
|
from this repo's release flow.
|
||||||
|
|
||||||
## Consuming the port
|
## Consuming the port
|
||||||
|
|
||||||
To build a third-party app against the published klibs, apply the bridge Gradle
|
To build a third-party app against the published klibs, apply the bridge Gradle
|
||||||
@@ -154,8 +161,8 @@ 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`. Group is `com.bitsycore.compose.sdl`. | Set by the tag, not edited by hand. A non-publish build is `0.0.0-SNAPSHOT`. |
|
| 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`. Group is `com.bitsycore.compose.sdl`. | 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 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. |
|
||||||
| JVM parity forcing | `vComposeJvmVersion` in `demo`, `apidemo`, and `material-symbols` `build.gradle.kts`. | Must be a version PUBLISHED to Maven Central. It may lag the vendored native ref (a documented skew) until the matching version is published. |
|
| JVM parity forcing | `vComposeJvmVersion` in `demo`, `apidemo`, and `material-symbols` `build.gradle.kts`. | Must be a version PUBLISHED to Maven Central. It may lag the vendored native ref (a documented skew) until the matching version is published. |
|
||||||
| Skiko | `skiko` in `gradle/libs.versions.toml`. | Must expose the `org.jetbrains.skiko.node` `RenderNode` / `GraphicsContext` API the vendored compose-core uses. Verify with a throwaway `skikoRendererMain` compile if unsure. |
|
| Skiko | `skiko` in `gradle/libs.versions.toml`. | macOS/Linux use official Skiko (`org.jetbrains.skiko`); mingwX64 uses the bitsycore fork (`com.bitsycore.skiko:skiko:0.150.1-mingw.1` from GitHub Packages), 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 / SDL3_ttf / SDL3_image / FreeType | `scripts/build-sdl/build-sdl.properties`. | Rebuild `libs/` with `build-all.py` after any change. |
|
| 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. |
|
| 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. |
|
||||||
|
|
||||||
### Bump the upstream Compose ref
|
### Bump the upstream Compose ref
|
||||||
@@ -168,8 +175,8 @@ Run this on each upstream bump; it is the flow that keeps the sync tax low.
|
|||||||
3. `python3 scripts/compose-fork/check-vendor-drift.py`. For any manual vendor
|
3. `python3 scripts/compose-fork/check-vendor-drift.py`. For any manual vendor
|
||||||
whose upstream base actually changed, reconcile by hand; otherwise re-stamp
|
whose upstream base actually changed, reconcile by hand; otherwise re-stamp
|
||||||
its `// VENDOR-BASE:` header to the new ref.
|
its `// VENDOR-BASE:` header to the new ref.
|
||||||
4. Build both legs and fix any fallout.
|
4. Build and fix any fallout.
|
||||||
5. `scripts/verify-mac.sh` (both legs green, including the soak and parity gates).
|
5. `scripts/verify-mac.sh` (green, including the soak and parity gates).
|
||||||
6. If a matching Compose version is now published to Maven, bump
|
6. If a matching Compose version is now published to Maven, bump
|
||||||
`vComposeJvmVersion` in demo/apidemo/material-symbols to close the skew.
|
`vComposeJvmVersion` in demo/apidemo/material-symbols to close the skew.
|
||||||
7. Run WIN-SMOKE on a Windows host.
|
7. Run WIN-SMOKE on a Windows host.
|
||||||
@@ -180,7 +187,7 @@ Run this on each upstream bump; it is the flow that keeps the sync tax low.
|
|||||||
|
|
||||||
1. Complete the ref-bump flow above and confirm it is green. For a STABLE
|
1. Complete the ref-bump flow above and confirm it is green. For a STABLE
|
||||||
release, `vComposeJvmVersion` should match the vendored ref (no skew).
|
release, `vComposeJvmVersion` should match the vendored ref (no skew).
|
||||||
2. On a Windows host, run `gradlew :window:compileCommonMainKotlinMetadata`.
|
2. On a Windows host, run `gradlew :desktop-native-window:compileCommonMainKotlinMetadata`.
|
||||||
Only Windows compiles the full common-metadata variant table, and the root
|
Only Windows compiles the full common-metadata variant table, and the root
|
||||||
KotlinMultiplatform publications live there; a macOS-only publish leaves the
|
KotlinMultiplatform publications live there; a macOS-only publish leaves the
|
||||||
roots without mingwX64 variants (this bit v0.1.15).
|
roots without mingwX64 variants (this bit v0.1.15).
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ kotlin {
|
|||||||
}
|
}
|
||||||
nativeMain {
|
nativeMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":window"))
|
implementation(project(":desktop-native-window"))
|
||||||
implementation(libs.ktor.client.curl)
|
implementation(libs.ktor.client.curl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -908,7 +908,7 @@ private fun buildBodyChunks(inText: String, inAnn: AnnotatedString, inLinesPerCh
|
|||||||
|
|
||||||
/** Offsets where each source line starts (line i spans [starts[ i], starts[i+1]-1),
|
/** Offsets where each source line starts (line i spans [starts[ i], starts[i+1]-1),
|
||||||
the -1 dropping the '\n'; the last line runs to the string end). One cheap O(n)
|
the -1 dropping the '\n'; the last line runs to the string end). One cheap O(n)
|
||||||
scan, memoised — replaces the old gutter pre-pass that ran measurer.wrap() on
|
scan, memoised — replaces the old gutter pre-pass that measured wrapping on
|
||||||
every one of N lines on each width change (the other half of the freeze). */
|
every one of N lines on each width change (the other half of the freeze). */
|
||||||
private fun lineStartOffsets(inText: String): IntArray {
|
private fun lineStartOffsets(inText: String): IntArray {
|
||||||
val vStarts = ArrayList<Int>(64)
|
val vStarts = ArrayList<Int>(64)
|
||||||
@@ -963,10 +963,10 @@ internal fun BodyView(
|
|||||||
val vGutterWidth = (vDigits * 7 + 4).dp
|
val vGutterWidth = (vDigits * 7 + 4).dp
|
||||||
// Body wrap width (physical px, from onSizeChanged), reported by the body
|
// Body wrap width (physical px, from onSizeChanged), reported by the body
|
||||||
// Box once it's laid out (0 on the first frame). The gutter numbering
|
// Box once it's laid out (0 on the first frame). The gutter numbering
|
||||||
// depends on it — see vNumbers. NativeTextMeasurer.wrap expects the font
|
// depends on it — see vNumbers. Wrap measurement expects the font
|
||||||
// size + max-width in the SAME unit (both physical px on Retina), so we
|
// size + max-width in the SAME unit (both physical px on Retina), so we
|
||||||
// scale 12.sp through the current density here, matching what SdlParagraph
|
// scale 12.sp through the current density here, matching what the paragraph
|
||||||
// does when it builds the body's real paragraph.
|
// engine does when it builds the body's real paragraph.
|
||||||
var vBodyWidthPx by remember { mutableStateOf(0) }
|
var vBodyWidthPx by remember { mutableStateOf(0) }
|
||||||
val vFontPx = with(LocalDensity.current) { 12.sp.toPx().toInt() }
|
val vFontPx = with(LocalDensity.current) { 12.sp.toPx().toInt() }
|
||||||
// Horizontal scroll for no-wrap mode (read-only body only); dormant when wrapping.
|
// Horizontal scroll for no-wrap mode (read-only body only); dormant when wrapping.
|
||||||
|
|||||||
@@ -16,7 +16,13 @@ import com.compose.sdl.res.painterResource
|
|||||||
import com.compose.sdl.revealInFileManager
|
import com.compose.sdl.revealInFileManager
|
||||||
import com.compose.sdl.showOpenFileDialog
|
import com.compose.sdl.showOpenFileDialog
|
||||||
import com.compose.sdl.showSaveFileDialog
|
import com.compose.sdl.showSaveFileDialog
|
||||||
import com.compose.sdl.text.currentTextMeasurer
|
import com.compose.sdl.text.namedFontFamily
|
||||||
|
import androidx.compose.ui.text.Paragraph
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
|
import androidx.compose.ui.text.font.createFontFamilyResolver
|
||||||
|
import androidx.compose.ui.unit.Constraints
|
||||||
|
import androidx.compose.ui.unit.Density
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.engine.curl.*
|
import io.ktor.client.engine.curl.*
|
||||||
import okio.FileSystem
|
import okio.FileSystem
|
||||||
@@ -49,8 +55,31 @@ actual fun removeMemoryImage(inKey: String) = removeMemoryResource(inKey)
|
|||||||
actual fun memoryImagePainter(inKey: String, inSvg: Boolean): Painter =
|
actual fun memoryImagePainter(inKey: String, inSvg: Boolean): Painter =
|
||||||
painterResource(inKey, if (inSvg) ResourceKind.Svg else ResourceKind.Raster)
|
painterResource(inKey, if (inSvg) ResourceKind.Svg else ResourceKind.Raster)
|
||||||
|
|
||||||
actual fun wrappedRowCount(inText: String, inFontPx: Int, inMaxWidthPx: Int, inFamilyName: String?): Int =
|
// Cached resolver for wrappedRowCount — the skiko engine resolves fonts by name
|
||||||
currentTextMeasurer.wrap(inText, inFontPx, inMaxWidthPx, inFamilyName).lines.size
|
// (projectFontName), so this is just the required non-null Paragraph argument.
|
||||||
|
private val mRowCountFontResolver = createFontFamilyResolver()
|
||||||
|
|
||||||
|
actual fun wrappedRowCount(inText: String, inFontPx: Int, inMaxWidthPx: Int, inFamilyName: String?): Int {
|
||||||
|
if (inText.isEmpty()) return 1
|
||||||
|
// inFontPx is already physical pixels, so measure at density 1 (fontSize.value
|
||||||
|
// * density = inFontPx) and wrap at inMaxWidthPx px by building a Paragraph.
|
||||||
|
// lineCount comes from skiko's paragraph layout.
|
||||||
|
val vStyle = TextStyle(
|
||||||
|
fontSize = inFontPx.sp,
|
||||||
|
fontFamily = inFamilyName?.let { namedFontFamily(it) },
|
||||||
|
)
|
||||||
|
return Paragraph(
|
||||||
|
text = inText,
|
||||||
|
style = vStyle,
|
||||||
|
constraints = Constraints(maxWidth = inMaxWidthPx.coerceAtLeast(0)),
|
||||||
|
density = Density(1f),
|
||||||
|
fontFamilyResolver = mRowCountFontResolver,
|
||||||
|
spanStyles = emptyList(),
|
||||||
|
placeholders = emptyList(),
|
||||||
|
maxLines = Int.MAX_VALUE,
|
||||||
|
ellipsis = false,
|
||||||
|
).lineCount
|
||||||
|
}
|
||||||
|
|
||||||
actual var editorTabWidth: Int
|
actual var editorTabWidth: Int
|
||||||
get() = TextLayoutConfig.tabWidth
|
get() = TextLayoutConfig.tabWidth
|
||||||
|
|||||||
+46
-14
@@ -18,8 +18,38 @@ apiValidation {
|
|||||||
// Version is driven by PUBLISH_VERSION (set from the git tag in the publish
|
// Version is driven by PUBLISH_VERSION (set from the git tag in the publish
|
||||||
// workflow, `v1.2.3` → `1.2.3`). Local dev / demo runs default to SNAPSHOT.
|
// workflow, `v1.2.3` → `1.2.3`). Local dev / demo runs default to SNAPSHOT.
|
||||||
val vPublishVersion = (System.getenv("PUBLISH_VERSION") ?: "0.0.0-SNAPSHOT").removePrefix("v")
|
val vPublishVersion = (System.getenv("PUBLISH_VERSION") ?: "0.0.0-SNAPSHOT").removePrefix("v")
|
||||||
|
|
||||||
|
// Published groups mirror the org.jetbrains.compose.<area> originals under the
|
||||||
|
// com.bitsycore fork name, so each module is the obvious 1:1 fork of its upstream
|
||||||
|
// coord (e.g. org.jetbrains.compose.ui:ui → com.bitsycore.compose.ui:ui). Modules
|
||||||
|
// with no upstream area (project-only: :sdl-core, :material-symbols) fall through
|
||||||
|
// to the default com.bitsycore.compose.sdl group; :desktop-native-window is the
|
||||||
|
// headline app-shell artifact, published under com.bitsycore.compose.
|
||||||
|
val kDefaultGroup = "com.bitsycore.compose.sdl"
|
||||||
|
val kAreaGroups = mapOf(
|
||||||
|
":ui" to "com.bitsycore.compose.ui",
|
||||||
|
":ui-graphics" to "com.bitsycore.compose.ui",
|
||||||
|
":ui-text" to "com.bitsycore.compose.ui",
|
||||||
|
":ui-unit" to "com.bitsycore.compose.ui",
|
||||||
|
":ui-geometry" to "com.bitsycore.compose.ui",
|
||||||
|
":ui-util" to "com.bitsycore.compose.ui",
|
||||||
|
":ui-backhandler" to "com.bitsycore.compose.ui",
|
||||||
|
":ui-tooling-preview" to "com.bitsycore.compose.ui",
|
||||||
|
":foundation" to "com.bitsycore.compose.foundation",
|
||||||
|
":foundation-layout" to "com.bitsycore.compose.foundation",
|
||||||
|
":animation" to "com.bitsycore.compose.animation",
|
||||||
|
":animation-core" to "com.bitsycore.compose.animation",
|
||||||
|
":animation-graphics" to "com.bitsycore.compose.animation",
|
||||||
|
":material3" to "com.bitsycore.compose.material3",
|
||||||
|
":material-ripple" to "com.bitsycore.compose.material",
|
||||||
|
":components-resources" to "com.bitsycore.compose.components",
|
||||||
|
":navigation3-ui" to "com.bitsycore.navigation3",
|
||||||
|
":desktop-native-window" to "com.bitsycore.compose",
|
||||||
|
)
|
||||||
|
fun groupFor(path: String): String = kAreaGroups[path] ?: kDefaultGroup
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = "com.bitsycore.compose.sdl"
|
group = groupFor(path)
|
||||||
version = vPublishVersion
|
version = vPublishVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,12 +67,13 @@ allprojects {
|
|||||||
|
|
||||||
val kAppModules = setOf(":demo", ":apidemo")
|
val kAppModules = setOf(":demo", ":apidemo")
|
||||||
val kPublishedLibs = setOf(
|
val kPublishedLibs = setOf(
|
||||||
":ui", ":ui-util", ":ui-geometry", ":ui-unit", ":ui-backhandler",
|
":sdl-core",
|
||||||
":ui-tooling-preview",
|
":ui", ":ui-util", ":ui-geometry", ":ui-graphics", ":ui-text",
|
||||||
|
":ui-unit", ":ui-backhandler", ":ui-tooling-preview",
|
||||||
":animation-core", ":animation", ":animation-graphics",
|
":animation-core", ":animation", ":animation-graphics",
|
||||||
":foundation", ":foundation-layout",
|
":foundation", ":foundation-layout",
|
||||||
":material3", ":material-ripple",
|
":material3", ":material-ripple",
|
||||||
":window", ":material-symbols",
|
":desktop-native-window", ":material-symbols",
|
||||||
":navigation3-ui", ":components-resources",
|
":navigation3-ui", ":components-resources",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -76,7 +107,7 @@ subprojects {
|
|||||||
kPublishedLibs.forEach { modulePath ->
|
kPublishedLibs.forEach { modulePath ->
|
||||||
val vArtifactId = modulePath.removePrefix(":")
|
val vArtifactId = modulePath.removePrefix(":")
|
||||||
substitute(project(modulePath))
|
substitute(project(modulePath))
|
||||||
.using(module("com.bitsycore.compose.sdl:$vArtifactId:$kConsumeVersion"))
|
.using(module("${groupFor(modulePath)}:$vArtifactId:$kConsumeVersion"))
|
||||||
.because("-PuseGithubPackages=true")
|
.because("-PuseGithubPackages=true")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,10 +166,11 @@ allprojects {
|
|||||||
if (vNativeTargetTokens.any { name.contains(it, ignoreCase = true) }) {
|
if (vNativeTargetTokens.any { name.contains(it, ignoreCase = true) }) {
|
||||||
resolutionStrategy.dependencySubstitution {
|
resolutionStrategy.dependencySubstitution {
|
||||||
substitute(module("org.jetbrains.compose.ui:ui")).using(project(":ui"))
|
substitute(module("org.jetbrains.compose.ui:ui")).using(project(":ui"))
|
||||||
// ui's split artifacts: ui-graphics + ui-text are merged INTO
|
// ui-graphics / ui-text are their own modules (split out of :ui,
|
||||||
// :ui in the port; ui-unit / ui-geometry have their own modules.
|
// upstream layout). Declare each DIRECTLY so the app commonMain sees
|
||||||
substitute(module("org.jetbrains.compose.ui:ui-graphics")).using(project(":ui"))
|
// them under the granular-metadata visibility rule.
|
||||||
substitute(module("org.jetbrains.compose.ui:ui-text")).using(project(":ui"))
|
substitute(module("org.jetbrains.compose.ui:ui-graphics")).using(project(":ui-graphics"))
|
||||||
|
substitute(module("org.jetbrains.compose.ui:ui-text")).using(project(":ui-text"))
|
||||||
substitute(module("org.jetbrains.compose.ui:ui-unit")).using(project(":ui-unit"))
|
substitute(module("org.jetbrains.compose.ui:ui-unit")).using(project(":ui-unit"))
|
||||||
substitute(module("org.jetbrains.compose.ui:ui-geometry")).using(project(":ui-geometry"))
|
substitute(module("org.jetbrains.compose.ui:ui-geometry")).using(project(":ui-geometry"))
|
||||||
substitute(module("org.jetbrains.compose.ui:ui-util")).using(project(":ui-util"))
|
substitute(module("org.jetbrains.compose.ui:ui-util")).using(project(":ui-util"))
|
||||||
@@ -160,11 +192,11 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Whether the current host can build the mingwX64 target. Kotlin/Native can
|
// Whether the current host can build the mingwX64 target. Kotlin/Native can
|
||||||
// only cross-compile mingwX64 cinterops from a Windows host — the sdl3_ttf /
|
// only cross-compile the mingwX64 sdl3 cinterop from a Windows host — it needs
|
||||||
// sdl3_image cinterops need Windows SDL3 headers under libs/ (produced by
|
// the Windows SDL3 headers under libs/ (produced by scripts/build-sdl/build-all.py
|
||||||
// scripts/build-sdl/build-all.py run on a Windows host). Declaring `mingwX64()` on
|
// run on a Windows host). Declaring `mingwX64()` on a non-Windows host is safe
|
||||||
// a non-Windows host is safe for pure-Kotlin modules but blows up the moment
|
// for pure-Kotlin modules but blows up the moment the sdl3 cinterop tries to
|
||||||
// a `depends = sdl3` cinterop tries to include SDL3_ttf/SDL_ttf.h.
|
// include SDL3's headers.
|
||||||
// Override with `-PforceMingw=true` if you actually have the headers wired.
|
// Override with `-PforceMingw=true` if you actually have the headers wired.
|
||||||
val vHostSupportsMingw = System.getProperty("os.name").startsWith("Windows") ||
|
val vHostSupportsMingw = System.getProperty("os.name").startsWith("Windows") ||
|
||||||
(findProperty("forceMingw") as? String)?.toBoolean() == true
|
(findProperty("forceMingw") as? String)?.toBoolean() == true
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:components-resources>
|
// Library unique name: <com.bitsycore.compose.components:components-resources>
|
||||||
open annotation class org.jetbrains.compose.resources/ExperimentalResourceApi : kotlin/Annotation { // org.jetbrains.compose.resources/ExperimentalResourceApi|null[0]
|
open annotation class org.jetbrains.compose.resources/ExperimentalResourceApi : kotlin/Annotation { // org.jetbrains.compose.resources/ExperimentalResourceApi|null[0]
|
||||||
constructor <init>() // org.jetbrains.compose.resources/ExperimentalResourceApi.<init>|<init>(){}[0]
|
constructor <init>() // org.jetbrains.compose.resources/ExperimentalResourceApi.<init>|<init>(){}[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
// Maven artifact ships no mingwX64/linux klibs. Public API is byte-for-byte
|
// Maven artifact ships no mingwX64/linux klibs. Public API is byte-for-byte
|
||||||
// upstream (painterResource / stringResource / Font / qualifiers / Res codegen
|
// upstream (painterResource / stringResource / Font / qualifiers / Res codegen
|
||||||
// compatibility); the platform actuals are this port's: data.kres reading,
|
// compatibility); the platform actuals are this port's: data.kres reading,
|
||||||
// SDL3_image decoding, SDL locale/theme environment. Apps' JVM targets keep
|
// image decode via :ui's Skia decoder, SDL locale/theme environment. Apps' JVM
|
||||||
// using the official Maven artifact — this module is native-only.
|
// targets keep using the official Maven artifact — this module is native-only.
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.kotlin.multiplatform)
|
alias(libs.plugins.kotlin.multiplatform)
|
||||||
@@ -17,7 +17,6 @@ repositories {
|
|||||||
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
|
||||||
}
|
}
|
||||||
|
|
||||||
val useSdl3Everywhere = (findProperty("renderer") as? String) == "sdl3"
|
|
||||||
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
|
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
@@ -41,52 +40,11 @@ kotlin {
|
|||||||
}
|
}
|
||||||
nativeMain {
|
nativeMain {
|
||||||
kotlin.srcDir("src/vendor/native/kotlin")
|
kotlin.srcDir("src/vendor/native/kotlin")
|
||||||
}
|
// src/nativeMain/kotlin also holds the Font / Image resource actuals —
|
||||||
|
// pure project code that delegates to the project font registry
|
||||||
// ============
|
// (IconFont / NamedFont) and the :ui-graphics Skia decoder
|
||||||
// Renderer roots — same conditional wiring as :ui: only the source
|
// (decodeEncodedImageBitmap). Skiko-free, so a single native set covers
|
||||||
// sets that will actually be attached are created.
|
// every target.
|
||||||
val sdlRendererMain = create("sdlRendererMain") {
|
|
||||||
dependsOn(nativeMain.get())
|
|
||||||
kotlin.srcDir("src/vendor/sdlRenderer/kotlin")
|
|
||||||
}
|
|
||||||
if (vHostSupportsMingw) {
|
|
||||||
val sdlRendererMingwMain = create("sdlRendererMingwMain") { dependsOn(sdlRendererMain) }
|
|
||||||
get("mingwX64Main").dependsOn(sdlRendererMingwMain)
|
|
||||||
}
|
|
||||||
|
|
||||||
val macosArm64Main = get("macosArm64Main")
|
|
||||||
val linuxX64Main = get("linuxX64Main")
|
|
||||||
val linuxArm64Main = get("linuxArm64Main")
|
|
||||||
|
|
||||||
if (useSdl3Everywhere) {
|
|
||||||
val sdlRendererMacosMain = create("sdlRendererMacosMain") { dependsOn(sdlRendererMain) }
|
|
||||||
val sdlRendererLinuxMain = create("sdlRendererLinuxMain") { dependsOn(sdlRendererMain) }
|
|
||||||
macosArm64Main.dependsOn(sdlRendererMacosMain)
|
|
||||||
linuxX64Main.dependsOn(sdlRendererLinuxMain)
|
|
||||||
linuxArm64Main.dependsOn(sdlRendererLinuxMain)
|
|
||||||
} else {
|
|
||||||
// The Font/Image actuals for this module are pure project code
|
|
||||||
// (:ui's IconFont / NamedFont / decodeEncodedImageBitmap in nativeMain)
|
|
||||||
// so they work identically under either renderer. Only one renderer
|
|
||||||
// source set is attached to a given target, so pointing skikoRenderer
|
|
||||||
// at sdlRendererMain's srcDir doesn't cause duplicate actuals.
|
|
||||||
// Upstream's skikoMain vendored files here referenced Compose-Desktop
|
|
||||||
// Skia extensions (SystemFont / toComposeImageBitmap / nativeCanvas)
|
|
||||||
// that this port's :ui doesn't expose — sharing the SDL actuals sidesteps
|
|
||||||
// that gap entirely.
|
|
||||||
val skikoRendererMain = create("skikoRendererMain") {
|
|
||||||
dependsOn(nativeMain.get())
|
|
||||||
kotlin.srcDir("src/sdlRendererMain/kotlin")
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.skiko)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val skikoRendererMacosMain = create("skikoRendererMacosMain") { dependsOn(skikoRendererMain) }
|
|
||||||
val skikoRendererLinuxMain = create("skikoRendererLinuxMain") { dependsOn(skikoRendererMain) }
|
|
||||||
macosArm64Main.dependsOn(skikoRendererMacosMain)
|
|
||||||
linuxX64Main.dependsOn(skikoRendererLinuxMain)
|
|
||||||
linuxArm64Main.dependsOn(skikoRendererLinuxMain)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
# mingwX64/linux variants, so the runtime library is vendored and the platform
|
# mingwX64/linux variants, so the runtime library is vendored and the platform
|
||||||
# actuals are project code:
|
# actuals are project code:
|
||||||
# src/nativeMain — ResourceReader (data.kres), getSystemEnvironment (SDL),
|
# src/nativeMain — ResourceReader (data.kres), getSystemEnvironment (SDL),
|
||||||
# pure-Kotlin DomXmlParser (upstream's is Darwin NSXMLParser)
|
# pure-Kotlin DomXmlParser (upstream's is Darwin NSXMLParser),
|
||||||
# src/sdlRendererMain — image decode via the :ui SDL3_image hook, SVG unsupported,
|
# and the Font/Image resource actuals: renderer-agnostic
|
||||||
# Font via the project font registry
|
# project code (image decode via the :ui-graphics Skia
|
||||||
# vendor/skikoRenderer — upstream skikoMain Skia-bound files, verbatim
|
# decoder, Font via the project font registry)
|
||||||
# Re-sync: python scripts/compose-fork/sync.py components/resources/library
|
# Re-sync: python scripts/compose-fork/sync.py components/resources/library
|
||||||
|
|
||||||
SET_REPO=https://github.com/JetBrains/compose-multiplatform@<COMPOSE_REF>
|
SET_REPO=https://github.com/JetBrains/compose-multiplatform@<COMPOSE_REF>
|
||||||
@@ -54,10 +54,9 @@ skikoMain/kotlin/org/jetbrains/compose/resources/ResourceReader.skiko.kt -> src/
|
|||||||
|
|
||||||
# Skia-bound pieces — upstream's skikoMain actuals rely on Compose-Desktop Skia
|
# Skia-bound pieces — upstream's skikoMain actuals rely on Compose-Desktop Skia
|
||||||
# extensions (SystemFont / toComposeImageBitmap / nativeCanvas) this port's :ui
|
# extensions (SystemFont / toComposeImageBitmap / nativeCanvas) this port's :ui
|
||||||
# doesn't expose. Since the sdlRenderer actuals (src/sdlRendererMain/kotlin/) use
|
# doesn't expose. The module's own Font/Image actuals (src/nativeMain/kotlin/)
|
||||||
# pure project APIs (IconFont / NamedFont / decodeEncodedImageBitmap) that work
|
# use pure project APIs (IconFont / NamedFont / decodeEncodedImageBitmap)
|
||||||
# on either renderer, the module's build shares those with skikoRendererMain
|
# instead, so opt the upstream Skia-bound files out.
|
||||||
# instead of vendoring the upstream files. Opt them out of the sync.
|
|
||||||
!skikoMain/kotlin/org/jetbrains/compose/resources/ImageResources.skiko.kt
|
!skikoMain/kotlin/org/jetbrains/compose/resources/ImageResources.skiko.kt
|
||||||
!skikoMain/kotlin/org/jetbrains/compose/resources/FontResources.skiko.kt
|
!skikoMain/kotlin/org/jetbrains/compose/resources/FontResources.skiko.kt
|
||||||
!skikoMain/kotlin/org/jetbrains/compose/resources/SvgPainter.kt
|
!skikoMain/kotlin/org/jetbrains/compose/resources/SvgPainter.kt
|
||||||
|
|||||||
+3
-3
@@ -15,16 +15,16 @@ import com.compose.sdl.icons.IconFont
|
|||||||
import com.compose.sdl.text.NamedFont
|
import com.compose.sdl.text.NamedFont
|
||||||
|
|
||||||
// ==================
|
// ==================
|
||||||
// MARK: Font actuals — SDL renderer
|
// MARK: Font actuals — Skia text stack via the project font registry
|
||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/** The SDL text stack resolves fonts by FAMILY NAME through the project font
|
/** The Skia text stack resolves fonts by FAMILY NAME through the project font
|
||||||
registry (IconFont.registerIcon — the general byte-font table, not just
|
registry (IconFont.registerIcon — the general byte-font table, not just
|
||||||
icons). A font resource therefore loads its bytes through the resource
|
icons). A font resource therefore loads its bytes through the resource
|
||||||
reader, registers them under a per-resource family name, and returns a
|
reader, registers them under a per-resource family name, and returns a
|
||||||
[NamedFont] — `FontFamily(Font(Res.font.x))` then renders through the
|
[NamedFont] — `FontFamily(Font(Res.font.x))` then renders through the
|
||||||
standard project pipeline. Weight/style are carried on the NamedFont;
|
standard project pipeline. Weight/style are carried on the NamedFont;
|
||||||
variation axes apply at draw time via the renderer's axis support. */
|
variation axes apply at draw time via Skia's axis support. */
|
||||||
|
|
||||||
private val emptyFont: Font = NamedFont("res-font:<loading>")
|
private val emptyFont: Font = NamedFont("res-font:<loading>")
|
||||||
|
|
||||||
+6
-7
@@ -12,21 +12,20 @@ import androidx.compose.ui.unit.Density
|
|||||||
import com.compose.sdl.graphics.decodeEncodedImageBitmap
|
import com.compose.sdl.graphics.decodeEncodedImageBitmap
|
||||||
|
|
||||||
// ==================
|
// ==================
|
||||||
// MARK: Image actuals — SDL renderer
|
// MARK: Image actuals — Skia decode via the :ui-graphics hook
|
||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/** Decoding goes through the :ui hook the SDL backend registers at init
|
/** Decoding goes through the :ui-graphics Skia hook the SDL backend registers
|
||||||
(SDL3_image, IMG auto-detect). resourceDensity/targetDensity are ignored:
|
at init (decodeEncodedImageBitmap). resourceDensity/targetDensity are ignored:
|
||||||
under this port's Option-B density flow layout runs in physical pixels and
|
under this port's Option-B density flow layout runs in physical pixels and
|
||||||
drawables ship at a single density, so no decode-time rescale applies. */
|
drawables ship at a single density, so no decode-time rescale applies. */
|
||||||
internal actual fun ByteArray.toImageBitmap(resourceDensity: Int, targetDensity: Int): ImageBitmap =
|
internal actual fun ByteArray.toImageBitmap(resourceDensity: Int, targetDensity: Int): ImageBitmap =
|
||||||
decodeEncodedImageBitmap(this)
|
decodeEncodedImageBitmap(this)
|
||||||
?: error("Image decode failed — is the render backend initialised before painterResource ran?")
|
?: error("Image decode failed — is the render backend initialised before painterResource ran?")
|
||||||
|
|
||||||
/** SVG on SDL: SDL3_image rasterises SVG at its intrinsic size (IMG_LoadSVG),
|
/** SVG: Skia rasterises it at its intrinsic size, so the "element" is just the
|
||||||
so the "element" is just the raw bytes and the painter is the rasterised
|
raw bytes and the painter is the rasterised bitmap. No DOM, no vector scaling —
|
||||||
bitmap. No DOM, no vector scaling — prefer XML vector drawables for
|
prefer XML vector drawables for resolution-independent art. */
|
||||||
resolution-independent art on this renderer. */
|
|
||||||
internal actual class SvgElement(val bytes: ByteArray)
|
internal actual class SvgElement(val bytes: ByteArray)
|
||||||
|
|
||||||
internal actual fun ByteArray.toSvgElement(): SvgElement = SvgElement(this)
|
internal actual fun ByteArray.toSvgElement(): SvgElement = SvgElement(this)
|
||||||
+12
-34
@@ -4,46 +4,24 @@ package org.jetbrains.compose.resources
|
|||||||
|
|
||||||
import org.jetbrains.compose.resources.InternalResourceApi
|
import org.jetbrains.compose.resources.InternalResourceApi
|
||||||
import org.jetbrains.compose.resources.ExperimentalResourceApi
|
import org.jetbrains.compose.resources.ExperimentalResourceApi
|
||||||
|
import com.compose.sdl.res.preferredLocaleProvider
|
||||||
import kotlinx.cinterop.alloc
|
import com.compose.sdl.res.systemThemeIsDarkProvider
|
||||||
import kotlinx.cinterop.get
|
|
||||||
import kotlinx.cinterop.memScoped
|
|
||||||
import kotlinx.cinterop.pointed
|
|
||||||
import kotlinx.cinterop.ptr
|
|
||||||
import kotlinx.cinterop.toKString
|
|
||||||
import kotlinx.cinterop.IntVar
|
|
||||||
import kotlinx.cinterop.value
|
|
||||||
import sdl3.SDL_GetPreferredLocales
|
|
||||||
import sdl3.SDL_GetSystemTheme
|
|
||||||
import sdl3.SDL_SystemTheme
|
|
||||||
import sdl3.SDL_free
|
|
||||||
|
|
||||||
// ==================
|
// ==================
|
||||||
// MARK: ResourceEnvironment — SDL actual
|
// MARK: ResourceEnvironment — platform-env-seam actual
|
||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/** Non-composable system environment for qualifier resolution (values-fr,
|
/** Non-composable system environment for qualifier resolution (values-fr,
|
||||||
drawable-dark, …): locale from SDL_GetPreferredLocales, theme from
|
drawable-dark, …): locale + theme come from the platform-env seams installed
|
||||||
SDL_GetSystemTheme. Density is reported as 1f — under this port's Option-B
|
by the SDL layer, so :components-resources carries no dependency on the sdl3
|
||||||
density flow layout runs in physical pixels and drawables are bundled at a
|
cinterop. Density is reported as 1f — under this port's Option-B density flow
|
||||||
single density, so the mdpi bucket is always the right one. The COMPOSABLE
|
layout runs in physical pixels and drawables are bundled at a single density,
|
||||||
path (rememberResourceEnvironment) doesn't use this: it reads
|
so the mdpi bucket is always the right one. The COMPOSABLE path
|
||||||
LocalDensity / isSystemInDarkTheme from the composition. */
|
(rememberResourceEnvironment) doesn't use this: it reads LocalDensity /
|
||||||
@OptIn(kotlinx.cinterop.ExperimentalForeignApi::class)
|
isSystemInDarkTheme from the composition. */
|
||||||
internal actual fun getSystemEnvironment(): ResourceEnvironment {
|
internal actual fun getSystemEnvironment(): ResourceEnvironment {
|
||||||
var vLanguage = ""
|
val (vLanguage, vRegion) = preferredLocaleProvider?.invoke() ?: ("" to "")
|
||||||
var vRegion = ""
|
val vDark = systemThemeIsDarkProvider?.invoke() ?: false
|
||||||
memScoped {
|
|
||||||
val vCount = alloc<IntVar>()
|
|
||||||
val vLocales = SDL_GetPreferredLocales(vCount.ptr)
|
|
||||||
if (vLocales != null && vCount.value > 0) {
|
|
||||||
val vFirst = vLocales[0]?.pointed
|
|
||||||
vLanguage = vFirst?.language?.toKString() ?: ""
|
|
||||||
vRegion = vFirst?.country?.toKString() ?: ""
|
|
||||||
SDL_free(vLocales)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val vDark = SDL_GetSystemTheme() == SDL_SystemTheme.SDL_SYSTEM_THEME_DARK
|
|
||||||
return ResourceEnvironment(
|
return ResourceEnvironment(
|
||||||
language = LanguageQualifier(vLanguage),
|
language = LanguageQualifier(vLanguage),
|
||||||
// SDL locales carry language+country only — no script subtag.
|
// SDL locales carry language+country only — no script subtag.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:animation-core>
|
// Library unique name: <com.bitsycore.compose.animation:animation-core>
|
||||||
open annotation class androidx.compose.animation.core/ExperimentalAnimationSpecApi : kotlin/Annotation { // androidx.compose.animation.core/ExperimentalAnimationSpecApi|null[0]
|
open annotation class androidx.compose.animation.core/ExperimentalAnimationSpecApi : kotlin/Annotation { // androidx.compose.animation.core/ExperimentalAnimationSpecApi|null[0]
|
||||||
constructor <init>() // androidx.compose.animation.core/ExperimentalAnimationSpecApi.<init>|<init>(){}[0]
|
constructor <init>() // androidx.compose.animation.core/ExperimentalAnimationSpecApi.<init>|<init>(){}[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
|||||||
|
|
||||||
// :animation-core — androidx.compose.animation.core.* vendored VERBATIM from
|
// :animation-core — androidx.compose.animation.core.* vendored VERBATIM from
|
||||||
// upstream. Renderer-agnostic (spring/tween specs, animatable, transition,
|
// upstream. Renderer-agnostic (spring/tween specs, animatable, transition,
|
||||||
// vector math). Split out of :core so :core stays about renderer+ui glue only.
|
// vector math). Split out of :ui so :ui stays about renderer+ui glue only.
|
||||||
//
|
//
|
||||||
// Provenance = animation-core/compose-fork.txt + scripts/compose-fork/compose.properties.
|
// Provenance = animation-core/compose-fork.txt + scripts/compose-fork/compose.properties.
|
||||||
// Never hand-edit files under animation-core/src/vendor/ — change the manifest and
|
// Never hand-edit files under animation-core/src/vendor/ — change the manifest and
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:animation-graphics>
|
// Library unique name: <com.bitsycore.compose.animation:animation-graphics>
|
||||||
open annotation class androidx.compose.animation.graphics/ExperimentalAnimationGraphicsApi : kotlin/Annotation { // androidx.compose.animation.graphics/ExperimentalAnimationGraphicsApi|null[0]
|
open annotation class androidx.compose.animation.graphics/ExperimentalAnimationGraphicsApi : kotlin/Annotation { // androidx.compose.animation.graphics/ExperimentalAnimationGraphicsApi|null[0]
|
||||||
constructor <init>() // androidx.compose.animation.graphics/ExperimentalAnimationGraphicsApi.<init>|<init>(){}[0]
|
constructor <init>() // androidx.compose.animation.graphics/ExperimentalAnimationGraphicsApi.<init>|<init>(){}[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:animation>
|
// Library unique name: <com.bitsycore.compose.animation:animation>
|
||||||
open annotation class androidx.compose.animation/ExperimentalAnimationApi : kotlin/Annotation { // androidx.compose.animation/ExperimentalAnimationApi|null[0]
|
open annotation class androidx.compose.animation/ExperimentalAnimationApi : kotlin/Annotation { // androidx.compose.animation/ExperimentalAnimationApi|null[0]
|
||||||
constructor <init>() // androidx.compose.animation/ExperimentalAnimationApi.<init>|<init>(){}[0]
|
constructor <init>() // androidx.compose.animation/ExperimentalAnimationApi.<init>|<init>(){}[0]
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -5,5 +5,5 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.native:window>
|
// Library unique name: <com.bitsycore.compose:desktop-native-window>
|
||||||
final fun com.compose.desktop.native/nativeComposeWindow(kotlin/String = ..., kotlin/Int = ..., kotlin/Int = ..., com.compose.desktop.native/GpuMode = ..., kotlin/Function2<com.compose.desktop.native/RenderBackend, kotlin/Int, kotlin/Boolean>? = ..., kotlin/Function3<com.compose.desktop.native/ComposeWindowScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>) // com.compose.desktop.native/nativeComposeWindow|nativeComposeWindow(kotlin.String;kotlin.Int;kotlin.Int;com.compose.desktop.native.GpuMode;kotlin.Function2<com.compose.desktop.native.RenderBackend,kotlin.Int,kotlin.Boolean>?;kotlin.Function3<com.compose.desktop.native.ComposeWindowScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>){}[0]
|
final fun com.compose.desktop.native/nativeComposeWindow(kotlin/String = ..., kotlin/Int = ..., kotlin/Int = ..., com.compose.desktop.native/GpuMode = ..., kotlin/Function2<com.compose.desktop.native/RenderBackend, kotlin/Int, kotlin/Boolean>? = ..., kotlin/Function3<com.compose.desktop.native/ComposeWindowScope, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>) // com.compose.desktop.native/nativeComposeWindow|nativeComposeWindow(kotlin.String;kotlin.Int;kotlin.Int;com.compose.desktop.native.GpuMode;kotlin.Function2<com.compose.desktop.native.RenderBackend,kotlin.Int,kotlin.Boolean>?;kotlin.Function3<com.compose.desktop.native.ComposeWindowScope,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>){}[0]
|
||||||
+9
-9
@@ -1,8 +1,8 @@
|
|||||||
// :window — the module apps depend on. Owns nativeComposeWindow() (main loop,
|
// :desktop-native-window — the module apps depend on. Owns nativeComposeWindow() (main loop,
|
||||||
// recomposer lifecycle, event dispatch, Snapshot apply notifications).
|
// recomposer lifecycle, event dispatch, Snapshot apply notifications).
|
||||||
// Renderer selection happens entirely inside :core via source-set wiring
|
// Renderer selection happens entirely inside :ui via source-set wiring
|
||||||
// (skikoRendererMain vs sdlRendererMain) — this module just calls
|
// (skikoRendererMain — Skia-only) — this module just calls
|
||||||
// `createRenderBackend(...)` and `rendererPreferredGpuMode()` from :core and
|
// `createRenderBackend(...)` and `rendererPreferredGpuMode()` from :ui and
|
||||||
// the right symbol resolves per target.
|
// the right symbol resolves per target.
|
||||||
// Publication artifactId (when set up): compose-desktop-native.
|
// Publication artifactId (when set up): compose-desktop-native.
|
||||||
|
|
||||||
@@ -23,15 +23,15 @@ kotlin {
|
|||||||
|
|
||||||
applyDefaultHierarchyTemplate()
|
applyDefaultHierarchyTemplate()
|
||||||
|
|
||||||
// sdl3.* types are api-exposed via :ui's cinterop klib — no separate
|
// sdl3.* types are api-exposed via :ui → :sdl-core's cinterop klib — no
|
||||||
// sdl3 cinterop here.
|
// separate sdl3 cinterop here.
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain.dependencies {
|
commonMain.dependencies {
|
||||||
// api so apps depending on :window also get the compose re-impl,
|
// api so apps depending on :desktop-native-window also get the compose re-impl,
|
||||||
// Res/resources, GpuMode, and the renderer pipeline from :core, plus
|
// Res/resources, GpuMode, and the renderer pipeline from :ui, plus
|
||||||
// the foundation / animation-core / animation modules that were split
|
// the foundation / animation-core / animation modules that were split
|
||||||
// out of :core (upstream Compose layout).
|
// out of :ui (upstream Compose layout).
|
||||||
// Material widgets used to be re-exported from :material here; the
|
// Material widgets used to be re-exported from :material here; the
|
||||||
// module was retired when :apidemo and :demo migrated to :material3.
|
// module was retired when :apidemo and :demo migrated to :material3.
|
||||||
// Apps that want Material 3 widgets pull `implementation(project(":material3"))`
|
// Apps that want Material 3 widgets pull `implementation(project(":material3"))`
|
||||||
+45
-16
@@ -25,7 +25,6 @@ import androidx.compose.ui.input.pointer.PointerEventType
|
|||||||
import androidx.lifecycle.enableSavedStateHandles
|
import androidx.lifecycle.enableSavedStateHandles
|
||||||
import com.compose.sdl.node.ComposeRootHost
|
import com.compose.sdl.node.ComposeRootHost
|
||||||
import com.compose.sdl.res.currentImageLoader
|
import com.compose.sdl.res.currentImageLoader
|
||||||
import com.compose.sdl.text.currentTextMeasurer
|
|
||||||
import com.compose.sdl.window.LocalPopupHost
|
import com.compose.sdl.window.LocalPopupHost
|
||||||
import com.compose.sdl.window.PopupLayer
|
import com.compose.sdl.window.PopupLayer
|
||||||
import com.compose.sdl.window.createPopupHostState
|
import com.compose.sdl.window.createPopupHostState
|
||||||
@@ -65,7 +64,7 @@ import sdl3.SDL_WaitEventTimeout
|
|||||||
routes events per SDL window id.
|
routes events per SDL window id.
|
||||||
|
|
||||||
Per-window pumping order matters: the render-bridge globals
|
Per-window pumping order matters: the render-bridge globals
|
||||||
(currentTextMeasurer / currentImageLoader / viewport) are per-renderer, so the
|
(currentImageLoader / viewport) are per-renderer, so the
|
||||||
loop installs a window's globals before recomposing / laying out / drawing it.
|
loop installs a window's globals before recomposing / laying out / drawing it.
|
||||||
|
|
||||||
`nativeComposeWindow(...)` remains as the single-window wrapper (all demo
|
`nativeComposeWindow(...)` remains as the single-window wrapper (all demo
|
||||||
@@ -530,12 +529,15 @@ internal class WindowInstance(
|
|||||||
private val onFrame: ((RenderBackend, Int) -> Boolean)?,
|
private val onFrame: ((RenderBackend, Int) -> Boolean)?,
|
||||||
private val contentHolder: () -> (@Composable ComposeWindowScope.() -> Unit),
|
private val contentHolder: () -> (@Composable ComposeWindowScope.() -> Unit),
|
||||||
) {
|
) {
|
||||||
private val gpuMode = if (inGpu is GpuMode.Auto) rendererPreferredGpuMode() else inGpu
|
// Resolved renderer mode. `var` so init() can fall back GPU → CPU raster when
|
||||||
val backend = SDL3Backend(
|
// a Skia GPU context can't be created (RDP / headless / missing GL driver).
|
||||||
initialTitle, inWidth, inHeight, gpuMode = gpuMode,
|
private var gpuMode = if (inGpu is GpuMode.Auto) rendererPreferredGpuMode() else inGpu
|
||||||
iconLightResourcePaths = inIcon?.light ?: emptyList(),
|
private val initialWidth = inWidth
|
||||||
iconDarkResourcePaths = inIcon?.dark ?: emptyList(),
|
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
|
private var renderBackend: RenderBackend? = null
|
||||||
lateinit var host: ComposeRootHost
|
lateinit var host: ComposeRootHost
|
||||||
private set
|
private set
|
||||||
@@ -606,14 +608,42 @@ internal class WindowInstance(
|
|||||||
needsFrame = true
|
needsFrame = true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun init(inScope: CoroutineScope): Boolean {
|
// Creates + initialises an SDL3Backend for [mode] and its RenderBackend. On
|
||||||
if (!backend.init()) return false
|
// success commits `backend` and returns the renderer; else tears everything
|
||||||
backend.updateWindowSize()
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
val vRender = createRenderBackend(backend, gpuMode)
|
fun init(inScope: CoroutineScope): Boolean {
|
||||||
if (vRender == null || !vRender.ensureSize(backend.pixelWidth, backend.pixelHeight)) {
|
// Try the resolved gpuMode; if it's a Skia GPU mode whose context/bridge
|
||||||
println("Failed to init render backend for $gpuMode")
|
// can't come up (RDP / headless / missing GL driver), fall back once to
|
||||||
backend.destroy(inQuitSdl = false)
|
// 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
|
return false
|
||||||
}
|
}
|
||||||
renderBackend = vRender
|
renderBackend = vRender
|
||||||
@@ -740,7 +770,6 @@ internal class WindowInstance(
|
|||||||
measuring, or drawing this window's tree. */
|
measuring, or drawing this window's tree. */
|
||||||
fun installGlobals() {
|
fun installGlobals() {
|
||||||
val vRender = renderBackend ?: return
|
val vRender = renderBackend ?: return
|
||||||
currentTextMeasurer = vRender.textMeasurer
|
|
||||||
currentImageLoader = vRender.imageLoader
|
currentImageLoader = vRender.imageLoader
|
||||||
com.compose.sdl.text.currentViewportWidth = backend.pixelWidth
|
com.compose.sdl.text.currentViewportWidth = backend.pixelWidth
|
||||||
com.compose.sdl.text.currentViewportHeight = backend.pixelHeight
|
com.compose.sdl.text.currentViewportHeight = backend.pixelHeight
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:foundation-layout>
|
// Library unique name: <com.bitsycore.compose.foundation:foundation-layout>
|
||||||
open annotation class androidx.compose.foundation.layout/ExperimentalFlexBoxApi : kotlin/Annotation { // androidx.compose.foundation.layout/ExperimentalFlexBoxApi|null[0]
|
open annotation class androidx.compose.foundation.layout/ExperimentalFlexBoxApi : kotlin/Annotation { // androidx.compose.foundation.layout/ExperimentalFlexBoxApi|null[0]
|
||||||
constructor <init>() // androidx.compose.foundation.layout/ExperimentalFlexBoxApi.<init>|<init>(){}[0]
|
constructor <init>() // androidx.compose.foundation.layout/ExperimentalFlexBoxApi.<init>|<init>(){}[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:foundation>
|
// Library unique name: <com.bitsycore.compose.foundation:foundation>
|
||||||
open annotation class androidx.compose.foundation.lazy.grid/LazyGridScopeMarker : kotlin/Annotation { // androidx.compose.foundation.lazy.grid/LazyGridScopeMarker|null[0]
|
open annotation class androidx.compose.foundation.lazy.grid/LazyGridScopeMarker : kotlin/Annotation { // androidx.compose.foundation.lazy.grid/LazyGridScopeMarker|null[0]
|
||||||
constructor <init>() // androidx.compose.foundation.lazy.grid/LazyGridScopeMarker.<init>|<init>(){}[0]
|
constructor <init>() // androidx.compose.foundation.lazy.grid/LazyGridScopeMarker.<init>|<init>(){}[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||||
|
|
||||||
// :foundation — androidx.compose.foundation.* AND androidx.compose.animation.*
|
// :foundation — androidx.compose.foundation.* AND androidx.compose.animation.*
|
||||||
// (non-core) vendored VERBATIM from upstream. Sits on :core (which still owns
|
// (non-core) vendored VERBATIM from upstream. Sits on :ui (which still owns
|
||||||
// androidx.compose.ui.*, both renderers, cinterops) + :animation-core.
|
// androidx.compose.ui.*, the Skia renderer, cinterops) + :animation-core.
|
||||||
//
|
//
|
||||||
// Why the two upstream modules (`:animation` and `:foundation`) are combined
|
// Why the two upstream modules (`:animation` and `:foundation`) are combined
|
||||||
// here: upstream splits `foundation-layout` off from `foundation`, then has
|
// here: upstream splits `foundation-layout` off from `foundation`, then has
|
||||||
@@ -20,9 +20,9 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
|||||||
// Note on DarkTheme: foundation declares `internal expect fun _isSystemInDarkTheme()`,
|
// Note on DarkTheme: foundation declares `internal expect fun _isSystemInDarkTheme()`,
|
||||||
// and its actual lives here as a plain nativeMain kt file (see
|
// and its actual lives here as a plain nativeMain kt file (see
|
||||||
// src/nativeMain/kotlin/androidx/compose/foundation/DarkTheme.native.kt) — a single
|
// src/nativeMain/kotlin/androidx/compose/foundation/DarkTheme.native.kt) — a single
|
||||||
// stub returning false. The upstream skiko/sdl per-renderer actuals were dropped
|
// stub returning false. The upstream skiko per-renderer actuals were dropped
|
||||||
// when :foundation was split from :core: expect/actual must live in the same
|
// when :foundation was split from :ui: expect/actual must live in the same
|
||||||
// module, and duplicating the skikoRenderer/sdlRenderer source-set hierarchy on
|
// module, and duplicating the skikoRenderer source-set hierarchy on
|
||||||
// :foundation just to read `LocalSystemTheme` on the Skia path was more build
|
// :foundation just to read `LocalSystemTheme` on the Skia path was more build
|
||||||
// wiring than is worth it for one function no in-repo app calls.
|
// wiring than is worth it for one function no in-repo app calls.
|
||||||
//
|
//
|
||||||
|
|||||||
+24
-43
@@ -1,38 +1,31 @@
|
|||||||
package com.compose.sdl.text
|
package com.compose.sdl.text
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.text.BasicText
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontVariation
|
import androidx.compose.ui.text.font.FontVariation
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.Constraints
|
|
||||||
import androidx.compose.ui.unit.TextUnit
|
import androidx.compose.ui.unit.TextUnit
|
||||||
|
|
||||||
// ==================
|
// ==================
|
||||||
// MARK: IconText — project-only text composable for icon fonts
|
// MARK: IconText — icon-font text on the standard text path
|
||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A minimal project text composable that renders a codepoint through a named
|
A minimal project text composable that renders a codepoint through a named
|
||||||
icon font with optional variable-font axis settings (Material Symbols).
|
icon font with optional variable-font axis settings (Material Symbols FILL /
|
||||||
|
wght / GRAD / opsz).
|
||||||
|
|
||||||
Sits outside `androidx.compose.foundation.text.BasicText` so BasicText can
|
Now just a [BasicText]: the icon font family and its variable axes are threaded
|
||||||
remain a byte-identical match of upstream. Icons need:
|
through [namedFontFamily] (`axes = …`), which the skiko text engine reads via
|
||||||
- `fontFamily: String` — the registered icon font family name (project's
|
`FontFamily.projectFontVariations()` and applies to the typeface. So icons
|
||||||
`FontFamily.Named`, not upstream `FontFamily.Resolver`).
|
measure + draw through the same skiko `skparagraph` path as ordinary text — no
|
||||||
- `fontVariationSettings` — per-usage variable-font axis values (FILL,
|
separate renderer/measurer seam.
|
||||||
wght, GRAD, opsz for Material Symbols).
|
|
||||||
|
|
||||||
Upstream `TextStyle` has neither: fontFamily routes through the
|
Material `Icon(codepoint = …, fontFamily = …)` uses this; Text / BasicText /
|
||||||
`FontFamily.Resolver`, and axis values are set per-Font at construction
|
TextField go through the upstream-shaped path already.
|
||||||
time. Rather than fight that abstraction for one composable, icons use
|
|
||||||
this project path — the TextDrawElement modifier feeds directly into
|
|
||||||
SdlParagraph / Sdl3Canvas.drawNativeText.
|
|
||||||
|
|
||||||
Material `Icon(codepoint = ..., fontFamily = ...)` uses this. Everything
|
|
||||||
else (Text, BasicText, TextField) goes through the upstream-shaped path.
|
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun IconText(
|
fun IconText(
|
||||||
@@ -44,28 +37,16 @@ fun IconText(
|
|||||||
textAlign: TextAlign = TextAlign.Start,
|
textAlign: TextAlign = TextAlign.Start,
|
||||||
fontVariationSettings: List<FontVariation.Setting>? = null,
|
fontVariationSettings: List<FontVariation.Setting>? = null,
|
||||||
) {
|
) {
|
||||||
// Layout runs in physical pixels (LocalDensity = DPR), so the icon font
|
BasicText(
|
||||||
// size must also convert `sp → px`. Matches SdlParagraph's `fontSize.value
|
text = text,
|
||||||
// * density`. Without this the icons render at half size on Retina.
|
modifier = modifier,
|
||||||
val vDensity = LocalDensity.current.density
|
style = TextStyle(
|
||||||
val vFontPx = (fontSize.value * vDensity).toInt().coerceAtLeast(1)
|
color = if (color == Color.Unspecified) Color.Black else color,
|
||||||
Box(modifier = modifier) {
|
fontSize = fontSize,
|
||||||
androidx.compose.ui.layout.Layout(
|
fontFamily = namedFontFamily(fontFamily, axes = fontVariationSettings),
|
||||||
modifier = TextDrawElement(
|
textAlign = textAlign,
|
||||||
text = text,
|
),
|
||||||
spans = null,
|
softWrap = false,
|
||||||
color = if (color == Color.Unspecified) Color.Black else color,
|
maxLines = 1,
|
||||||
fontSizePx = vFontPx,
|
)
|
||||||
textAlign = textAlign,
|
|
||||||
softWrap = false,
|
|
||||||
fontFamily = fontFamily,
|
|
||||||
fontVariations = fontVariationSettings,
|
|
||||||
),
|
|
||||||
) { _, constraints ->
|
|
||||||
val vSize = currentTextMeasurer.measure(text, vFontPx, Int.MAX_VALUE, fontFamily, fontVariationSettings)
|
|
||||||
val w = vSize.width.coerceIn(constraints.minWidth, constraints.maxWidth)
|
|
||||||
val h = vSize.height.coerceIn(constraints.minHeight, constraints.maxHeight)
|
|
||||||
layout(w, h) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -2,9 +2,10 @@ package androidx.compose.foundation
|
|||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.ReadOnlyComposable
|
import androidx.compose.runtime.ReadOnlyComposable
|
||||||
import sdl3.SDL_GetSystemTheme
|
import com.compose.sdl.res.systemThemeIsDarkProvider
|
||||||
import sdl3.SDL_SystemTheme
|
|
||||||
|
|
||||||
|
// System dark-theme flag via the platform-env seam (installed by the SDL layer),
|
||||||
|
// so :foundation carries no dependency on the sdl3 cinterop.
|
||||||
@Composable
|
@Composable
|
||||||
@ReadOnlyComposable
|
@ReadOnlyComposable
|
||||||
internal actual fun _isSystemInDarkTheme(): Boolean = SDL_GetSystemTheme() == SDL_SystemTheme.SDL_SYSTEM_THEME_DARK
|
internal actual fun _isSystemInDarkTheme(): Boolean = systemThemeIsDarkProvider?.invoke() ?: false
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ import androidx.compose.ui.unit.sp
|
|||||||
|
|
||||||
The four Material Symbols variable-font axes (fill / weight / grade /
|
The four Material Symbols variable-font axes (fill / weight / grade /
|
||||||
opticalSize) come in via `fontVariationSettings`; use `MaterialIconAxes(…)`
|
opticalSize) come in via `fontVariationSettings`; use `MaterialIconAxes(…)`
|
||||||
to build the list. Skia honours the axes; SDL3_ttf 3.2 ignores them. */
|
to build the list. Skia honours the axes. */
|
||||||
@Composable
|
@Composable
|
||||||
fun IconFontIcon(
|
fun IconFontIcon(
|
||||||
codepoint: Int,
|
codepoint: Int,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:material-ripple>
|
// Library unique name: <com.bitsycore.compose.material:material-ripple>
|
||||||
abstract interface androidx.compose.material.ripple/RippleTheme { // androidx.compose.material.ripple/RippleTheme|null[0]
|
abstract interface androidx.compose.material.ripple/RippleTheme { // androidx.compose.material.ripple/RippleTheme|null[0]
|
||||||
abstract fun defaultColor(androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.ui.graphics/Color // androidx.compose.material.ripple/RippleTheme.defaultColor|defaultColor(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
|
abstract fun defaultColor(androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.ui.graphics/Color // androidx.compose.material.ripple/RippleTheme.defaultColor|defaultColor(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
|
||||||
abstract fun rippleAlpha(androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.material.ripple/RippleAlpha // androidx.compose.material.ripple/RippleTheme.rippleAlpha|rippleAlpha(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
|
abstract fun rippleAlpha(androidx.compose.runtime/Composer?, kotlin/Int): androidx.compose.material.ripple/RippleAlpha // androidx.compose.material.ripple/RippleTheme.rippleAlpha|rippleAlpha(androidx.compose.runtime.Composer?;kotlin.Int){}[0]
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:material3>
|
// Library unique name: <com.bitsycore.compose.material3:material3>
|
||||||
open annotation class androidx.compose.material3/ExperimentalMaterial3Api : kotlin/Annotation { // androidx.compose.material3/ExperimentalMaterial3Api|null[0]
|
open annotation class androidx.compose.material3/ExperimentalMaterial3Api : kotlin/Annotation { // androidx.compose.material3/ExperimentalMaterial3Api|null[0]
|
||||||
constructor <init>() // androidx.compose.material3/ExperimentalMaterial3Api.<init>|<init>(){}[0]
|
constructor <init>() // androidx.compose.material3/ExperimentalMaterial3Api.<init>|<init>(){}[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ kotlin {
|
|||||||
api(project(":animation-core"))
|
api(project(":animation-core"))
|
||||||
api(project(":material-ripple"))
|
api(project(":material-ripple"))
|
||||||
// androidx.collection is used by material3 internals (MutableIntObjectMap, …).
|
// androidx.collection is used by material3 internals (MutableIntObjectMap, …).
|
||||||
// Already on the classpath via :core's runtime; declare here for clarity.
|
// Already on the classpath via :ui's runtime; declare here for clarity.
|
||||||
implementation("androidx.collection:collection:1.5.0")
|
implementation("androidx.collection:collection:1.5.0")
|
||||||
// androidx.graphics.shapes.* — MaterialShapes (RoundedPolygon /
|
// androidx.graphics.shapes.* — MaterialShapes (RoundedPolygon /
|
||||||
// Morph / CornerRounding) that WavyProgress / LoadingIndicator use.
|
// Morph / CornerRounding) that WavyProgress / LoadingIndicator use.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:ui-backhandler>
|
// Library unique name: <com.bitsycore.compose.ui:ui-backhandler>
|
||||||
final class androidx.compose.ui.backhandler/BackEventCompat { // androidx.compose.ui.backhandler/BackEventCompat|null[0]
|
final class androidx.compose.ui.backhandler/BackEventCompat { // androidx.compose.ui.backhandler/BackEventCompat|null[0]
|
||||||
constructor <init>(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Int) // androidx.compose.ui.backhandler/BackEventCompat.<init>|<init>(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Int){}[0]
|
constructor <init>(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Int) // androidx.compose.ui.backhandler/BackEventCompat.<init>|<init>(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Int){}[0]
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:ui-geometry>
|
// Library unique name: <com.bitsycore.compose.ui:ui-geometry>
|
||||||
final class androidx.compose.ui.geometry/MutableRect { // androidx.compose.ui.geometry/MutableRect|null[0]
|
final class androidx.compose.ui.geometry/MutableRect { // androidx.compose.ui.geometry/MutableRect|null[0]
|
||||||
constructor <init>(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float) // androidx.compose.ui.geometry/MutableRect.<init>|<init>(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
|
constructor <init>(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float) // androidx.compose.ui.geometry/MutableRect.<init>|<init>(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float){}[0]
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,75 @@
|
|||||||
|
// :ui-graphics — androidx.compose.ui.graphics.* split out of :ui (upstream layout).
|
||||||
|
// Canvas / Paint / Path / Brush / Color / ImageBitmap / Shader / GraphicsLayer +
|
||||||
|
// the Skia actuals (SkiaBackedCanvas, SkiaImageCache, …). SDL-free — image bytes
|
||||||
|
// come through the composeResourceReader seam, so it depends only on skiko +
|
||||||
|
// the low ui primitives, NOT on :sdl-core.
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlin.multiplatform)
|
||||||
|
alias(libs.plugins.kotlin.plugin.compose)
|
||||||
|
alias(libs.plugins.compose.multiplatform)
|
||||||
|
}
|
||||||
|
|
||||||
|
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
linuxArm64()
|
||||||
|
linuxX64()
|
||||||
|
macosArm64()
|
||||||
|
if (vHostSupportsMingw) mingwX64()
|
||||||
|
|
||||||
|
applyDefaultHierarchyTemplate()
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
kotlin.srcDir("src/vendor/common/kotlin")
|
||||||
|
dependencies {
|
||||||
|
api(project(":ui-geometry"))
|
||||||
|
api(project(":ui-unit"))
|
||||||
|
api(project(":ui-util"))
|
||||||
|
api("org.jetbrains.compose.runtime:runtime:${libs.versions.composeRuntime.get()}")
|
||||||
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
|
// androidx.collection comes transitively via :ui-util (api).
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nativeMain {
|
||||||
|
kotlin.srcDir("src/vendor/native/kotlin")
|
||||||
|
}
|
||||||
|
|
||||||
|
val skikoRendererMain = create("skikoRendererMain") {
|
||||||
|
dependsOn(nativeMain.get())
|
||||||
|
kotlin.srcDir("src/vendor/skikoRenderer/kotlin")
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.skiko)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val skikoRendererMacosMain = create("skikoRendererMacosMain") { dependsOn(skikoRendererMain) }
|
||||||
|
val skikoRendererLinuxMain = create("skikoRendererLinuxMain") { dependsOn(skikoRendererMain) }
|
||||||
|
get("macosArm64Main").dependsOn(skikoRendererMacosMain)
|
||||||
|
get("linuxX64Main").dependsOn(skikoRendererLinuxMain)
|
||||||
|
get("linuxArm64Main").dependsOn(skikoRendererLinuxMain)
|
||||||
|
|
||||||
|
if (vHostSupportsMingw) {
|
||||||
|
val skikoRendererMingwSharedMain = create("skikoRendererMingwSharedMain") {
|
||||||
|
dependsOn(nativeMain.get())
|
||||||
|
kotlin.srcDir("src/skikoRendererMain/kotlin")
|
||||||
|
kotlin.srcDir("src/vendor/skikoRenderer/kotlin")
|
||||||
|
dependencies {
|
||||||
|
implementation("com.bitsycore.skiko:skiko:${providers.gradleProperty("skikoMingwVersion").getOrElse("0.150.1-mingw.1")}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val skikoRendererMingwMain = create("skikoRendererMingwMain") { dependsOn(skikoRendererMingwSharedMain) }
|
||||||
|
get("mingwX64Main").dependsOn(skikoRendererMingwMain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compilerOptions {
|
||||||
|
freeCompilerArgs.addAll(
|
||||||
|
"-Xcollection-literals",
|
||||||
|
"-Xexpect-actual-classes",
|
||||||
|
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
|
||||||
|
"-opt-in=androidx.compose.ui.InternalComposeUiApi",
|
||||||
|
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
SET_REPO=https://github.com/JetBrains/compose-multiplatform-core@<COMPOSE_CORE_REF>
|
||||||
|
SET_FOLDER=compose/ui/ui-graphics/src
|
||||||
|
|
||||||
|
commonMain/kotlin/ -> src/vendor/common/kotlin/
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Bezier.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/BlendMode.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/BlendMode.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Brush.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Canvas.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Canvas.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/ClipOp.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/ClipOp.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Color.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Color.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/ColorFilter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/ColorFilter.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/ColorMatrix.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/ColorMatrix.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Degrees.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Degrees.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/ExperimentalGraphicsApi.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/ExperimentalGraphicsApi.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/FilterQuality.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/FilterQuality.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Float16.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Float16.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/GraphicsContext.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/GraphicsContext.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/ImageBitmap.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/InlineClassHelper.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/InlineClassHelper.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Interpolatable.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Interpolatable.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/IntervalTree.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/IntervalTree.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/LayerOutsets.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/LayerOutsets.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Matrix.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Matrix.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Outline.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Outline.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Paint.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Paint.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PaintingStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PaintingStyle.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Path.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Path.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PathEffect.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathEffect.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PathFillType.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathFillType.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PathGeometry.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathGeometry.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PathHitTester.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathHitTester.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PathIterator.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathIterator.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PathMeasure.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathMeasure.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PathOperation.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathOperation.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PathSegment.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathSegment.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PathSvg.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathSvg.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PixelMap.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PixelMap.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/PointMode.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PointMode.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/RectangleShape.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/RectangleShape.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/RenderEffect.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/RenderEffect.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Shader.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Shadow.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Shadow.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Shape.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Shape.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/StrokeCap.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/StrokeCap.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/StrokeJoin.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/StrokeJoin.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/TileMode.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/TileMode.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/VertexMode.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/VertexMode.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/Vertices.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Vertices.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Adaptation.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Adaptation.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/ColorModel.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/ColorModel.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpace.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpace.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpaces.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpaces.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Connector.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Connector.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Illuminant.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Illuminant.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Lab.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Lab.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Oklab.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Oklab.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/RenderIntent.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/RenderIntent.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Rgb.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Rgb.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/TransferParameters.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/TransferParameters.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/WhitePoint.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/WhitePoint.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Xyz.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Xyz.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/ContentDrawScope.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/ContentDrawScope.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawContext.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/DrawContext.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeMarker.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeMarker.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawTransform.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/DrawTransform.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/EmptyCanvas.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/EmptyCanvas.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/layer/ChildLayerDependenciesTracker.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/layer/ChildLayerDependenciesTracker.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/layer/CompositingStrategy.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/layer/CompositingStrategy.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayer.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayer.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/painter/BitmapPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/painter/BitmapPainter.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/painter/BrushPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/painter/BrushPainter.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/painter/ColorPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/painter/ColorPainter.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/painter/Painter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/painter/Painter.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/Blur.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/Blur.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/DropShadowPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/DropShadowPainter.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/InnerShadowPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/InnerShadowPainter.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/Shadow.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/Shadow.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/ShadowContext.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/ShadowContext.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/ShadowRenderer.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/ShadowRenderer.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/vector/FastFloatParser.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/vector/FastFloatParser.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/vector/PathBuilder.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/vector/PathBuilder.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/vector/PathNode.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/vector/PathNode.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
|
||||||
|
|
||||||
|
nonJvmMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.nonJvm.kt -> src/vendor/native/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.native.kt
|
||||||
|
|
||||||
|
skikoMain/kotlin/ -> src/vendor/skikoRenderer/kotlin/
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/BlendMode.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/BlendMode.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/ImageBitmap.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/Matrices.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/Matrices.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/Rects.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/Rects.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPaint.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedPaint.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPathEffect.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedPathEffect.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPathMeasure.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedPathMeasure.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedRenderEffect.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedRenderEffect.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaColorFilter.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaColorFilter.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaGraphicsContext.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaGraphicsContext.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaImageAsset.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaImageAsset.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaPathIterator.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaPathIterator.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaShader.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaShader.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaTileMode.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaTileMode.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaVertexMode.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaVertexMode.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/layer/SkiaGraphicsLayer.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/layer/SkiaGraphicsLayer.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/graphics/shadow/Blur.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/shadow/Blur.skiko.kt
|
||||||
|
# B6.1 (2026-07-16): un-refused — skiko leg adopts upstream graphics actuals.
|
||||||
|
# SkiaBackedCanvas is then MANUAL-VENDORED (moved to src/skikoRendererMain, re-refused
|
||||||
|
# below) to also implement the port Native* draw contracts. See RENDERER_TASKS.md B6.1.
|
||||||
|
# B6.2: un-refused — skiko leg adopts upstream GraphicsLayer/GraphicsContext (real
|
||||||
|
# skiko RenderNode) now the canvas is SkiaBackedCanvas. See RENDERER_TASKS.md B6.2.
|
||||||
|
!skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt
|
||||||
|
# B6.1: MANUAL-VENDORED (flattened toBitmap/putBytesInto expect-actuals — see the file).
|
||||||
|
!skikoMain/kotlin/androidx/compose/ui/graphics/SkiaImageAsset.skiko.kt
|
||||||
|
|
||||||
|
# >>> ---- DIAGNOSTIC GAPS ---- <<<<
|
||||||
|
# androidMain (28)
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidBlendMode.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidBlendMode.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidCanvas.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidCanvas.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidColor.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidColor.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorFilter.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidColorFilter.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidColorSpace.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidGraphicsContext.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidImageBitmap.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidMatrixConversions.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidMatrixConversions.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidPaint.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidPaint.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidPath.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidPathEffect.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidPathEffect.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidPathIterator.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidPathIterator.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidPathMeasure.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidPathMeasure.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidRenderEffect.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidRenderEffect.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidShader.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidShader.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidTileMode.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidTileMode.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/AndroidVertexMode.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/AndroidVertexMode.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/CanvasUtils.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/CanvasUtils.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/RectHelper.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/RectHelper.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/layer/AndroidGraphicsLayer.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV23.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV23.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV29.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayerV29.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsViewLayer.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/layer/GraphicsViewLayer.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/layer/LayerSnapshot.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/layer/LayerSnapshot.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/layer/view/PlaceholderHardwareCanvas.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/layer/view/PlaceholderHardwareCanvas.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/layer/view/ViewLayerContainer.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/layer/view/ViewLayerContainer.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/shadow/AndroidShadowContext.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/shadow/AndroidShadowContext.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/graphics/shadow/Blur.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/graphics/shadow/Blur.android.kt
|
||||||
|
# desktopMain (7)
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/graphics/DesktopColorFilter.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/graphics/DesktopColorFilter.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/graphics/DesktopImageAsset.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/graphics/DesktopImageAsset.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/graphics/DesktopImageConverters.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/graphics/DesktopImageConverters.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPath.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/graphics/DesktopPath.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/graphics/DesktopPathEffect.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/graphics/DesktopPathEffect.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/graphics/RenderEffect.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/graphics/RenderEffect.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/graphics/awt/AwtColor.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/graphics/awt/AwtColor.desktop.kt
|
||||||
|
# jvmAndAndroidMain (1)
|
||||||
|
# | jvmAndAndroidMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.jvmAndroidAndroid.kt -> src/vendor/jvmAndAndroid/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.jvmAndroidAndroid.kt
|
||||||
|
# nativeMain (1)
|
||||||
|
# | nativeMain/kotlin/androidx/compose/ui/graphics/NativeImageAsset.native.kt -> src/vendor/native/kotlin/androidx/compose/ui/graphics/NativeImageAsset.native.kt
|
||||||
|
# skikoExcludingWebMain (1)
|
||||||
|
# | skikoExcludingWebMain/kotlin/androidx/compose/ui/graphics/Actuals.skikoExcludingWeb.kt -> src/vendor/skikoExcludingWeb/kotlin/androidx/compose/ui/graphics/Actuals.skikoExcludingWeb.kt
|
||||||
|
# webMain (2)
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/graphics/Actuals.web.kt -> src/vendor/web/kotlin/androidx/compose/ui/graphics/Actuals.web.kt
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/graphics/ImageAsset.web.kt -> src/vendor/web/kotlin/androidx/compose/ui/graphics/ImageAsset.web.kt
|
||||||
|
# <<< ---- DIAGNOSTIC GAPS ---- >>>>
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package com.compose.sdl.graphics
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// MARK: NativeFinishableCanvas
|
||||||
|
// ==================
|
||||||
|
|
||||||
|
/**
|
||||||
|
A renderer canvas that must be flushed once drawing into it is done — some backends
|
||||||
|
batch geometry and only submit on flush; an offscreen canvas must flush before its
|
||||||
|
backing texture is read. Implemented by SkiaBackedCanvas (no-op — Skia save/restore
|
||||||
|
is balanced per call). Lets renderer-agnostic code (e.g. GraphicsLayer.toImageBitmap)
|
||||||
|
commit an offscreen render without knowing the concrete canvas type.
|
||||||
|
*/
|
||||||
|
interface NativeFinishableCanvas {
|
||||||
|
fun finish()
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.compose.sdl.graphics
|
||||||
|
|
||||||
|
import com.compose.sdl.res.ResourceKind
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// MARK: NativePainterCanvas bridge
|
||||||
|
// ==================
|
||||||
|
|
||||||
|
/**
|
||||||
|
Image drawing bridge. Renderer Canvas backends (SkiaBackedCanvas) implement this
|
||||||
|
to paint a bundled resource by path + kind. [com.compose.sdl.res.ResourcePainter]
|
||||||
|
(an upstream `Painter` subclass in the res pipeline) casts the current Canvas to
|
||||||
|
this and calls drawNativePainter from inside its `Painter.onDraw()` — reached via
|
||||||
|
upstream `Modifier.paint(...)` (PainterModifier), which pre-scales the
|
||||||
|
destination size per ContentScale. So the painter always draws fill-bounds into
|
||||||
|
the given rect, and this seam carries no ui-layout (ContentScale) dependency —
|
||||||
|
keeping :ui-graphics free of :ui.
|
||||||
|
*/
|
||||||
|
interface NativePainterCanvas {
|
||||||
|
fun drawNativePainter(
|
||||||
|
inResourcePath: String,
|
||||||
|
inKind: ResourceKind,
|
||||||
|
inX: Float,
|
||||||
|
inY: Float,
|
||||||
|
inWidth: Float,
|
||||||
|
inHeight: Float,
|
||||||
|
inAlpha: Float,
|
||||||
|
)
|
||||||
|
}
|
||||||
+2
-4
@@ -7,7 +7,7 @@ import androidx.compose.ui.graphics.Outline
|
|||||||
// MARK: NativeShadowCanvas (render bridge)
|
// MARK: NativeShadowCanvas (render bridge)
|
||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/** Drop-shadow bridge, same shape as NativeTextCanvas / NativePainterCanvas:
|
/** Drop-shadow bridge, same shape as NativePainterCanvas:
|
||||||
the render nodes call it when a layer carries shadowElevation > 0
|
the render nodes call it when a layer carries shadowElevation > 0
|
||||||
(Modifier.shadow, m3 Surface tonal+shadow elevation). The outline is the
|
(Modifier.shadow, m3 Surface tonal+shadow elevation). The outline is the
|
||||||
layer's resolved shape in LAYER-LOCAL pixels — the canvas is already
|
layer's resolved shape in LAYER-LOCAL pixels — the canvas is already
|
||||||
@@ -15,9 +15,7 @@ import androidx.compose.ui.graphics.Outline
|
|||||||
paint BEFORE the layer's clip (it lives outside the bounds).
|
paint BEFORE the layer's clip (it lives outside the bounds).
|
||||||
|
|
||||||
Implementations:
|
Implementations:
|
||||||
- SkiaBackedCanvas — a real Gaussian blur MaskFilter on the outline.
|
- SkiaBackedCanvas — a real Gaussian blur MaskFilter on the outline. */
|
||||||
- Sdl3Canvas — cached shadow tiles (Sdl3ShadowCache) blitted via 9-slice
|
|
||||||
(the SDL 2D renderer has no shader/blur primitive). */
|
|
||||||
interface NativeShadowCanvas {
|
interface NativeShadowCanvas {
|
||||||
fun drawDropShadow(
|
fun drawDropShadow(
|
||||||
inOutline: Outline,
|
inOutline: Outline,
|
||||||
+4
-4
@@ -11,10 +11,10 @@ import androidx.compose.ui.geometry.RoundRect
|
|||||||
a rounded-rect / circle OUTLINE (not just its bounding box).
|
a rounded-rect / circle OUTLINE (not just its bounding box).
|
||||||
|
|
||||||
The Skia backend does NOT implement this: Skia's `Canvas.clipPath` already
|
The Skia backend does NOT implement this: Skia's `Canvas.clipPath` already
|
||||||
clips to a rounded path natively. The SDL3 backend has no path-clip primitive
|
clips to a rounded path natively. This side-channel exists for any backend
|
||||||
(only a rectangular render clip), so `Sdl3Canvas` implements this by rendering
|
that has no path-clip primitive (only a rectangular render clip) and must
|
||||||
the clipped subtree into an offscreen target and masking it to the rounded shape
|
emulate it by rendering the clipped subtree into an offscreen target and
|
||||||
(lazily — see the pending-clip machinery there).
|
masking it to the rounded shape.
|
||||||
|
|
||||||
The render nodes check `canvas is NativeShapeClipCanvas` when a layer has an
|
The render nodes check `canvas is NativeShapeClipCanvas` when a layer has an
|
||||||
`Outline.Rounded` clip and route here; otherwise they fall back to `clipPath`. The pushed clip is scoped to the enclosing `Canvas.save()` /
|
`Outline.Rounded` clip and route here; otherwise they fall back to `clipPath`. The pushed clip is scoped to the enclosing `Canvas.save()` /
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.compose.sdl.res
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// MARK: Platform-environment seams
|
||||||
|
// ==================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System-environment seams the platform layer (the SDL module) installs at
|
||||||
|
* startup, so `:foundation` (`isSystemInDarkTheme`) and `:components-resources`
|
||||||
|
* (locale / theme resource qualifiers) can read them WITHOUT a dependency on the
|
||||||
|
* sdl3 cinterop — the cinterop lives in the SDL platform module, but these
|
||||||
|
* consumers sit below it. null until installed → callers fall back to the light
|
||||||
|
* theme / empty locale (the sensible headless default).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** True when the OS is in dark mode. Installed from SDL_GetSystemTheme. */
|
||||||
|
var systemThemeIsDarkProvider: (() -> Boolean)? = null
|
||||||
|
|
||||||
|
/** The OS preferred locale as (language, region) — e.g. ("fr", "FR"); either may
|
||||||
|
be empty. Installed from SDL_GetPreferredLocales. */
|
||||||
|
var preferredLocaleProvider: (() -> Pair<String, String>)? = null
|
||||||
|
|
||||||
|
/** OS preferred-locale BCP47 tags (language[-REGION]), most-preferred first —
|
||||||
|
backs androidx.compose.ui.text.intl Locale.current / LocaleList.current (in
|
||||||
|
:ui-text). Installed from SDL_GetPreferredLocales. */
|
||||||
|
var preferredLocaleTagsProvider: (() -> List<String>)? = null
|
||||||
+9
-2
@@ -34,8 +34,8 @@ fun resourceKindForPath(path: String): ResourceKind {
|
|||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/** Shared bridge the commonMain layout pass uses to size images and read raw
|
/** Shared bridge the commonMain layout pass uses to size images and read raw
|
||||||
bytes. The active RenderBackend installs an implementation at startup
|
bytes. The active RenderBackend installs an implementation at startup.
|
||||||
(parallel to currentTextMeasurer). Actual drawing is done by the renderer
|
Actual drawing is done by the renderer
|
||||||
directly off the LayoutNode painter leaf, reusing the backend's decode
|
directly off the LayoutNode painter leaf, reusing the backend's decode
|
||||||
cache — so a decoded texture is created once and shared by measure + draw. */
|
cache — so a decoded texture is created once and shared by measure + draw. */
|
||||||
interface ImageLoader {
|
interface ImageLoader {
|
||||||
@@ -56,6 +56,13 @@ private val kFallbackImageLoader = object : ImageLoader {
|
|||||||
/** Installed by ComposeWindow from renderBackend.imageLoader. */
|
/** Installed by ComposeWindow from renderBackend.imageLoader. */
|
||||||
var currentImageLoader: ImageLoader = kFallbackImageLoader
|
var currentImageLoader: ImageLoader = kFallbackImageLoader
|
||||||
|
|
||||||
|
/** Raw resource-bytes reader (reads an entry from the bundled data.kres). The
|
||||||
|
platform layer installs the impl (SDL's ResourceIO via SDL_GetBasePath); the
|
||||||
|
Skia font + image actuals read through this instead of the SDL loader directly,
|
||||||
|
so ui-graphics / ui-text carry no dependency on the sdl3 cinterop. null until
|
||||||
|
installed → callers treat that as "resource unavailable". */
|
||||||
|
var composeResourceReader: ((String) -> ByteArray?)? = null
|
||||||
|
|
||||||
// ==================
|
// ==================
|
||||||
// MARK: painterResource
|
// MARK: painterResource
|
||||||
// ==================
|
// ==================
|
||||||
+2
-3
@@ -23,7 +23,7 @@ import com.compose.sdl.graphics.NativePainterCanvas
|
|||||||
the renderer, so this reduced type carries just the resource pointer.
|
the renderer, so this reduced type carries just the resource pointer.
|
||||||
|
|
||||||
[onDraw] casts the current Canvas to [NativePainterCanvas] (implemented by
|
[onDraw] casts the current Canvas to [NativePainterCanvas] (implemented by
|
||||||
Sdl3Canvas / SkiaCanvas) and delegates paint to the renderer's image cache.
|
SkiaBackedCanvas) and delegates paint to the renderer's image cache.
|
||||||
If the Canvas doesn't implement NativePainterCanvas (offscreen layer capture
|
If the Canvas doesn't implement NativePainterCanvas (offscreen layer capture
|
||||||
for graphics-layer effects) the paint is skipped.
|
for graphics-layer effects) the paint is skipped.
|
||||||
*/
|
*/
|
||||||
@@ -45,8 +45,7 @@ class ResourcePainter internal constructor(
|
|||||||
// translate + scaledSize, so we paint the resource FillBounds into the
|
// translate + scaledSize, so we paint the resource FillBounds into the
|
||||||
// full DrawScope size. Alpha is threaded via applyAlpha.
|
// full DrawScope size. Alpha is threaded via applyAlpha.
|
||||||
(vCanvas as? NativePainterCanvas)?.drawNativePainter(
|
(vCanvas as? NativePainterCanvas)?.drawNativePainter(
|
||||||
resourcePath, kind, 0f, 0f, size.width, size.height,
|
resourcePath, kind, 0f, 0f, size.width, size.height, fAlpha,
|
||||||
androidx.compose.ui.layout.ContentScale.FillBounds, fAlpha,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -11,4 +11,4 @@ import androidx.compose.ui.graphics.GraphicsContext
|
|||||||
(the createRenderBackend trick). SDL → the project ProjectGraphicsContext (record/
|
(the createRenderBackend trick). SDL → the project ProjectGraphicsContext (record/
|
||||||
replay via the SDL NativeRenderNode); Skia → upstream's SkiaGraphicsContext (owns a
|
replay via the SDL NativeRenderNode); Skia → upstream's SkiaGraphicsContext (owns a
|
||||||
skiko RenderNodeContext). See RENDERER.md §4 (B2). */
|
skiko RenderNodeContext). See RENDERER.md §4 (B2). */
|
||||||
internal expect fun createGraphicsContext(): GraphicsContext
|
expect fun createGraphicsContext(): GraphicsContext
|
||||||
+2
-4
@@ -8,10 +8,9 @@ import androidx.compose.ui.graphics.ImageBitmap
|
|||||||
|
|
||||||
/** Decodes an ENCODED image (png / jpg / webp / bmp / gif / svg bytes) into a
|
/** Decodes an ENCODED image (png / jpg / webp / bmp / gif / svg bytes) into a
|
||||||
drawable [ImageBitmap]. The active renderer registers its implementation at
|
drawable [ImageBitmap]. The active renderer registers its implementation at
|
||||||
backend construction — the SDL backend goes through SDL3_image
|
backend construction — the Skia backend goes through Image.makeFromEncoded /
|
||||||
(Sdl3EncodedImageDecoder), the Skia backend through Image.makeFromEncoded /
|
|
||||||
SVGDOM (SkiaEncodedImageDecoder). :components-resources' actuals share this
|
SVGDOM (SkiaEncodedImageDecoder). :components-resources' actuals share this
|
||||||
hook under BOTH renderers (its skikoRendererMain reuses the SDL actuals).
|
hook.
|
||||||
|
|
||||||
Consumed by `ByteArray.toImageBitmap` / `SvgElement.toSvgPainter` so
|
Consumed by `ByteArray.toImageBitmap` / `SvgElement.toSvgPainter` so
|
||||||
painterResource(Res.drawable.x) works without that module reaching into
|
painterResource(Res.drawable.x) works without that module reaching into
|
||||||
@@ -24,7 +23,6 @@ interface EncodedImageDecoder {
|
|||||||
resources pipeline on Dispatchers.Default workers. */
|
resources pipeline on Dispatchers.Default workers. */
|
||||||
@kotlin.concurrent.Volatile
|
@kotlin.concurrent.Volatile
|
||||||
var encodedImageDecoder: EncodedImageDecoder? = null
|
var encodedImageDecoder: EncodedImageDecoder? = null
|
||||||
internal set
|
|
||||||
|
|
||||||
/** Decode via the active renderer's registered decoder — null when no renderer
|
/** Decode via the active renderer's registered decoder — null when no renderer
|
||||||
* has initialised yet or the bytes aren't a supported image. */
|
* has initialised yet or the bytes aren't a supported image. */
|
||||||
+1
-1
@@ -16,7 +16,7 @@ import kotlin.math.abs
|
|||||||
|
|
||||||
// Builds the layer transform (translation / rotation / scale / camera about a
|
// Builds the layer transform (translation / rotation / scale / camera about a
|
||||||
// pivot) into [matrix]. Matches the skiko copy so hit-testing agrees across legs.
|
// pivot) into [matrix]. Matches the skiko copy so hit-testing agrees across legs.
|
||||||
internal fun prepareLayerTransformationMatrix(
|
fun prepareLayerTransformationMatrix(
|
||||||
matrix: Matrix,
|
matrix: Matrix,
|
||||||
pivotX: Float,
|
pivotX: Float,
|
||||||
pivotY: Float,
|
pivotY: Float,
|
||||||
+5
-51
@@ -17,11 +17,11 @@
|
|||||||
// DERIVED (copy + edit) from compose-multiplatform-core -- MANUAL VENDOR, NON-IDEMPOTENT.
|
// 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-beta03+dev4483
|
// VENDOR-BASE: compose/ui/ui-graphics/src/skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt @ v1.12.0-beta03+dev4483
|
||||||
// ONLY change vs upstream: this class also implements the port draw contracts
|
// ONLY change vs upstream: this class also implements the port draw contracts
|
||||||
// (NativeTextCanvas / NativePainterCanvas / NativeShadowCanvas / NativeFinishableCanvas) so
|
// (NativePainterCanvas / NativeShadowCanvas / NativeFinishableCanvas) so the
|
||||||
// the port text engine (SkiaTextRenderer, P3.1 metrics) + resource-image cache + elevation
|
// resource-image cache + elevation shadows keep working on the Skia renderer,
|
||||||
// shadows keep working on the Skia leg, which now draws through THIS canvas instead of the
|
// which draws through THIS canvas instead of the retired project SkiaCanvas.
|
||||||
// retired project SkiaCanvas. Bridged methods forward to `skiaLeafDrawer` using
|
// Bridged methods forward to `skiaLeafDrawer` using `internalSkiaCanvas`.
|
||||||
// `internalSkiaCanvas`. See RENDERER.md (B6.1, section 4).
|
// See RENDERER.md (B6.1, section 4).
|
||||||
|
|
||||||
package androidx.compose.ui.graphics
|
package androidx.compose.ui.graphics
|
||||||
|
|
||||||
@@ -103,7 +103,6 @@ var Canvas.alphaMultiplier: Float
|
|||||||
internal class SkiaBackedCanvas(
|
internal class SkiaBackedCanvas(
|
||||||
internal val internalSkiaCanvas: SkCanvas,
|
internal val internalSkiaCanvas: SkCanvas,
|
||||||
) : Canvas,
|
) : Canvas,
|
||||||
com.compose.sdl.text.NativeTextCanvas,
|
|
||||||
com.compose.sdl.graphics.NativePainterCanvas,
|
com.compose.sdl.graphics.NativePainterCanvas,
|
||||||
com.compose.sdl.graphics.NativeShadowCanvas,
|
com.compose.sdl.graphics.NativeShadowCanvas,
|
||||||
com.compose.sdl.graphics.NativeFinishableCanvas {
|
com.compose.sdl.graphics.NativeFinishableCanvas {
|
||||||
@@ -497,49 +496,6 @@ internal class SkiaBackedCanvas(
|
|||||||
|
|
||||||
override fun finish() { /* Skia save/restore is balanced per call */ }
|
override fun finish() { /* Skia save/restore is balanced per call */ }
|
||||||
|
|
||||||
override fun drawNativeText(
|
|
||||||
inText: String,
|
|
||||||
inSpans: List<androidx.compose.ui.text.AnnotatedString.Range<androidx.compose.ui.text.SpanStyle>>?,
|
|
||||||
inX: Float,
|
|
||||||
inY: Float,
|
|
||||||
inBoxWidth: Float,
|
|
||||||
inBoxHeight: Float,
|
|
||||||
inColor: Color,
|
|
||||||
inFontSizePx: Int,
|
|
||||||
inTextAlign: androidx.compose.ui.text.style.TextAlign,
|
|
||||||
inSoftWrap: Boolean,
|
|
||||||
inFontFamily: String?,
|
|
||||||
inFontVariations: List<androidx.compose.ui.text.font.FontVariation.Setting>?,
|
|
||||||
inBaseItalic: Boolean,
|
|
||||||
inTextDecoration: androidx.compose.ui.text.style.TextDecoration?,
|
|
||||||
inLineHeightPx: Float,
|
|
||||||
inTrimFirstLine: Boolean,
|
|
||||||
) {
|
|
||||||
val drawer = skiaLeafDrawer ?: return
|
|
||||||
val vUnderline = inTextDecoration?.contains(androidx.compose.ui.text.style.TextDecoration.Underline) == true
|
|
||||||
val vLineThrough = inTextDecoration?.contains(androidx.compose.ui.text.style.TextDecoration.LineThrough) == true
|
|
||||||
drawer.textRenderer.drawText(
|
|
||||||
inCanvas = internalSkiaCanvas,
|
|
||||||
inText = inText,
|
|
||||||
inX = inX,
|
|
||||||
inY = inY,
|
|
||||||
inBoxWidth = inBoxWidth.toInt(),
|
|
||||||
inBoxHeight = inBoxHeight.toInt(),
|
|
||||||
inColor = inColor,
|
|
||||||
inFontSize = inFontSizePx,
|
|
||||||
inAlign = inTextAlign,
|
|
||||||
inSoftWrap = inSoftWrap,
|
|
||||||
inFontFamily = inFontFamily,
|
|
||||||
inFontVariations = inFontVariations,
|
|
||||||
inSpans = inSpans,
|
|
||||||
inBaseItalic = inBaseItalic,
|
|
||||||
inBaseUnderline = vUnderline,
|
|
||||||
inBaseLineThrough = vLineThrough,
|
|
||||||
inLineHeightPx = inLineHeightPx,
|
|
||||||
inTrimFirstLine = inTrimFirstLine,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun drawNativePainter(
|
override fun drawNativePainter(
|
||||||
inResourcePath: String,
|
inResourcePath: String,
|
||||||
inKind: com.compose.sdl.res.ResourceKind,
|
inKind: com.compose.sdl.res.ResourceKind,
|
||||||
@@ -547,7 +503,6 @@ internal class SkiaBackedCanvas(
|
|||||||
inY: Float,
|
inY: Float,
|
||||||
inWidth: Float,
|
inWidth: Float,
|
||||||
inHeight: Float,
|
inHeight: Float,
|
||||||
inContentScale: androidx.compose.ui.layout.ContentScale,
|
|
||||||
inAlpha: Float,
|
inAlpha: Float,
|
||||||
) {
|
) {
|
||||||
val drawer = skiaLeafDrawer ?: return
|
val drawer = skiaLeafDrawer ?: return
|
||||||
@@ -559,7 +514,6 @@ internal class SkiaBackedCanvas(
|
|||||||
inY = inY,
|
inY = inY,
|
||||||
inW = inWidth,
|
inW = inWidth,
|
||||||
inH = inHeight,
|
inH = inHeight,
|
||||||
inScale = inContentScale,
|
|
||||||
inAlpha = inAlpha,
|
inAlpha = inAlpha,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+1
-1
@@ -10,4 +10,4 @@ import androidx.compose.ui.graphics.SkiaGraphicsContext
|
|||||||
/** The Skia leg uses upstream's SkiaGraphicsContext, which owns a skiko
|
/** The Skia leg uses upstream's SkiaGraphicsContext, which owns a skiko
|
||||||
RenderNodeContext and creates upstream GraphicsLayer(skiko.RenderNode) instances.
|
RenderNodeContext and creates upstream GraphicsLayer(skiko.RenderNode) instances.
|
||||||
See RENDERER.md (B6.2, section 4). */
|
See RENDERER.md (B6.2, section 4). */
|
||||||
internal actual fun createGraphicsContext(): GraphicsContext = SkiaGraphicsContext()
|
actual fun createGraphicsContext(): GraphicsContext = SkiaGraphicsContext()
|
||||||
+1
-1
@@ -18,7 +18,7 @@ import org.jetbrains.skia.Surface
|
|||||||
Returns null if anything along the way fails: the Surface may not yet
|
Returns null if anything along the way fails: the Surface may not yet
|
||||||
exist (first frame), the GPU readback may fail, or bitmap.readPixels()
|
exist (first frame), the GPU readback may fail, or bitmap.readPixels()
|
||||||
may not be exposed in this Skiko build. */
|
may not be exposed in this Skiko build. */
|
||||||
internal fun readBackBgra(inSurface: Surface?, inWidth: Int, inHeight: Int): Triple<Int, Int, ByteArray>? {
|
fun readBackBgra(inSurface: Surface?, inWidth: Int, inHeight: Int): Triple<Int, Int, ByteArray>? {
|
||||||
val vSurface = inSurface ?: return null
|
val vSurface = inSurface ?: return null
|
||||||
if (inWidth <= 0 || inHeight <= 0) return null
|
if (inWidth <= 0 || inHeight <= 0) return null
|
||||||
val vBitmap = Bitmap()
|
val vBitmap = Bitmap()
|
||||||
+6
-33
@@ -1,11 +1,8 @@
|
|||||||
package com.compose.sdl.renderer.skia
|
package com.compose.sdl.renderer.skia
|
||||||
|
|
||||||
import com.compose.sdl.*
|
|
||||||
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
import com.compose.sdl.res.AndroidVectorToSvg
|
import com.compose.sdl.res.AndroidVectorToSvg
|
||||||
import com.compose.sdl.res.ResourceKind
|
import com.compose.sdl.res.ResourceKind
|
||||||
import androidx.compose.ui.unit.IntSize
|
import com.compose.sdl.res.composeResourceReader
|
||||||
import org.jetbrains.skia.Canvas
|
import org.jetbrains.skia.Canvas
|
||||||
import org.jetbrains.skia.Color
|
import org.jetbrains.skia.Color
|
||||||
import org.jetbrains.skia.Data
|
import org.jetbrains.skia.Data
|
||||||
@@ -14,8 +11,6 @@ import org.jetbrains.skia.Paint
|
|||||||
import org.jetbrains.skia.Rect
|
import org.jetbrains.skia.Rect
|
||||||
import org.jetbrains.skia.Surface
|
import org.jetbrains.skia.Surface
|
||||||
import org.jetbrains.skia.svg.SVGDOM
|
import org.jetbrains.skia.svg.SVGDOM
|
||||||
import kotlin.math.max
|
|
||||||
import kotlin.math.min
|
|
||||||
|
|
||||||
// ==================
|
// ==================
|
||||||
// MARK: SkiaImageCache
|
// MARK: SkiaImageCache
|
||||||
@@ -31,7 +26,7 @@ import kotlin.math.min
|
|||||||
NOTE: this source set isn't compiled on the mingwX64 host, so it is built
|
NOTE: this source set isn't compiled on the mingwX64 host, so it is built
|
||||||
only on macOS / Linux — keep it to the Skiko APIs already used elsewhere in
|
only on macOS / Linux — keep it to the Skiko APIs already used elsewhere in
|
||||||
this module. */
|
this module. */
|
||||||
internal class SkiaImageCache {
|
class SkiaImageCache {
|
||||||
|
|
||||||
// Value is null when a decode failed — cached to avoid retrying each frame.
|
// Value is null when a decode failed — cached to avoid retrying each frame.
|
||||||
private val fCache = HashMap<String, Image?>()
|
private val fCache = HashMap<String, Image?>()
|
||||||
@@ -49,7 +44,7 @@ internal class SkiaImageCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun decode(inPath: String, inKind: ResourceKind): Image? {
|
private fun decode(inPath: String, inKind: ResourceKind): Image? {
|
||||||
val vBytes = loadComposeResourceBytes(inPath) ?: return null
|
val vBytes = composeResourceReader?.invoke(inPath) ?: return null
|
||||||
return when (inKind) {
|
return when (inKind) {
|
||||||
ResourceKind.Raster -> runCatching { Image.makeFromEncoded(vBytes) }.getOrNull()
|
ResourceKind.Raster -> runCatching { Image.makeFromEncoded(vBytes) }.getOrNull()
|
||||||
ResourceKind.Svg -> rasterizeSvg(vBytes)
|
ResourceKind.Svg -> rasterizeSvg(vBytes)
|
||||||
@@ -73,7 +68,6 @@ internal class SkiaImageCache {
|
|||||||
inY: Float,
|
inY: Float,
|
||||||
inW: Float,
|
inW: Float,
|
||||||
inH: Float,
|
inH: Float,
|
||||||
inScale: ContentScale,
|
|
||||||
inAlpha: Float,
|
inAlpha: Float,
|
||||||
) {
|
) {
|
||||||
if (inW <= 0f || inH <= 0f) return
|
if (inW <= 0f || inH <= 0f) return
|
||||||
@@ -85,30 +79,9 @@ internal class SkiaImageCache {
|
|||||||
val vPaint = Paint()
|
val vPaint = Paint()
|
||||||
vPaint.color = Color.makeARGB((inAlpha * 255f).toInt().coerceIn(0, 255), 255, 255, 255)
|
vPaint.color = Color.makeARGB((inAlpha * 255f).toInt().coerceIn(0, 255), 255, 255, 255)
|
||||||
|
|
||||||
when (inScale) {
|
// Upstream PainterModifier pre-scales the destination per ContentScale, so
|
||||||
ContentScale.FillBounds -> {
|
// draw fill-bounds into the given rect.
|
||||||
inCanvas.drawImageRect(vImg, Rect.makeWH(vIw, vIh), Rect.makeXYWH(inX, inY, inW, inH), vPaint)
|
inCanvas.drawImageRect(vImg, Rect.makeWH(vIw, vIh), Rect.makeXYWH(inX, inY, inW, inH), vPaint)
|
||||||
}
|
|
||||||
ContentScale.Crop -> {
|
|
||||||
val vScale = max(inW / vIw, inH / vIh)
|
|
||||||
val vSrcW = inW / vScale
|
|
||||||
val vSrcH = inH / vScale
|
|
||||||
val vSrc = Rect.makeXYWH((vIw - vSrcW) / 2f, (vIh - vSrcH) / 2f, vSrcW, vSrcH)
|
|
||||||
inCanvas.drawImageRect(vImg, vSrc, Rect.makeXYWH(inX, inY, inW, inH), vPaint)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val vScale = when (inScale) {
|
|
||||||
ContentScale.Fit -> min(inW / vIw, inH / vIh)
|
|
||||||
ContentScale.Inside -> min(1f, min(inW / vIw, inH / vIh))
|
|
||||||
else -> 1f // None
|
|
||||||
}
|
|
||||||
val vDw = vIw * vScale
|
|
||||||
val vDh = vIh * vScale
|
|
||||||
val vDx = inX + (inW - vDw) / 2f
|
|
||||||
val vDy = inY + (inH - vDh) / 2f
|
|
||||||
inCanvas.drawImageRect(vImg, Rect.makeWH(vIw, vIh), Rect.makeXYWH(vDx, vDy, vDw, vDh), vPaint)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vPaint.close()
|
vPaint.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package com.compose.sdl.renderer.skia
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// MARK: SkiaLeafDrawer — port text/image renderers behind the upstream canvas (B6.1)
|
||||||
|
// ==================
|
||||||
|
|
||||||
|
/** The Skia leg draws through upstream's `SkiaBackedCanvas` (real gradients/paint/
|
||||||
|
shader). Text now goes through the skiko paragraph engine (Paragraph.paint), so
|
||||||
|
this drawer only holds the resource-image cache (`SkiaImageCache`) that
|
||||||
|
`SkiaBackedCanvas`'s `NativePainterCanvas` contract forwards to. Set once by
|
||||||
|
`SkiaRenderBackend`; any `SkiaBackedCanvas` (frame, offscreen, or a GraphicsLayer
|
||||||
|
recording — B6.2) can then draw images onto its own `internalSkiaCanvas`. */
|
||||||
|
var skiaLeafDrawer: SkiaLeafDrawer? = null
|
||||||
|
|
||||||
|
class SkiaLeafDrawer(
|
||||||
|
val imageCache: SkiaImageCache,
|
||||||
|
)
|
||||||
+8
-8
@@ -9,18 +9,18 @@ import org.jetbrains.skia.Image
|
|||||||
// MARK: SkiaEncodedImageDecoder
|
// MARK: SkiaEncodedImageDecoder
|
||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/** Encoded-image decode hook impl for the Skia renderer — the counterpart of
|
/** Encoded-image decode hook impl for the Skia renderer, consumed by
|
||||||
Sdl3EncodedImageDecoder, consumed by :components-resources' actuals
|
:components-resources' actuals (painterResource / SVG). Raster formats go
|
||||||
(painterResource / SVG). Raster formats go through Image.makeFromEncoded;
|
through Image.makeFromEncoded; when that fails the bytes are retried as SVG
|
||||||
when that fails the bytes are retried as SVG through the same SVGDOM
|
through the same SVGDOM rasterisation SkiaImageCache uses. Pure CPU raster
|
||||||
rasterisation SkiaImageCache uses. Pure CPU raster (no GrContext), so it is
|
(no GrContext), so it is safe on the resources pipeline's Dispatchers.Default
|
||||||
safe on the resources pipeline's Dispatchers.Default workers.
|
workers.
|
||||||
|
|
||||||
B6.1: produces an UPSTREAM ImageBitmap (Image.toComposeImageBitmap) — the Skia
|
B6.1: produces an UPSTREAM ImageBitmap (Image.toComposeImageBitmap) — the Skia
|
||||||
leg now uses upstream's SkiaBackedCanvas/SkiaImageAsset, so the offscreen +
|
renderer uses upstream's SkiaBackedCanvas/SkiaImageAsset, so the offscreen +
|
||||||
ImageBitmap-backed paths go through upstream's own actuals (the project
|
ImageBitmap-backed paths go through upstream's own actuals (the project
|
||||||
SkiaImageBitmap / SkiaOffscreenRenderer are retired). */
|
SkiaImageBitmap / SkiaOffscreenRenderer are retired). */
|
||||||
internal class SkiaEncodedImageDecoder : EncodedImageDecoder {
|
class SkiaEncodedImageDecoder : EncodedImageDecoder {
|
||||||
|
|
||||||
override fun decode(inBytes: ByteArray): ImageBitmap? {
|
override fun decode(inBytes: ByteArray): ImageBitmap? {
|
||||||
if (inBytes.isEmpty()) return null
|
if (inBytes.isEmpty()) return null
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
|||||||
|
// :ui-text — androidx.compose.ui.text.* split out of :ui (upstream layout).
|
||||||
|
// AnnotatedString / TextStyle / Paragraph / FontFamily + the B6.3 skiko text
|
||||||
|
// engine (SkiaParagraph / SkiaParagraphOps / SkiaFonts, IconFont). SDL-free —
|
||||||
|
// depends on :ui-graphics (Canvas), skiko, and the low ui primitives.
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlin.multiplatform)
|
||||||
|
alias(libs.plugins.kotlin.plugin.compose)
|
||||||
|
alias(libs.plugins.compose.multiplatform)
|
||||||
|
}
|
||||||
|
|
||||||
|
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
linuxArm64()
|
||||||
|
linuxX64()
|
||||||
|
macosArm64()
|
||||||
|
if (vHostSupportsMingw) mingwX64()
|
||||||
|
|
||||||
|
applyDefaultHierarchyTemplate()
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
kotlin.srcDir("src/vendor/common/kotlin")
|
||||||
|
dependencies {
|
||||||
|
api(project(":ui-graphics"))
|
||||||
|
api(project(":ui-unit"))
|
||||||
|
api(project(":ui-util"))
|
||||||
|
api("org.jetbrains.compose.runtime:runtime:${libs.versions.composeRuntime.get()}")
|
||||||
|
api("org.jetbrains.compose.runtime:runtime-saveable:${libs.versions.composeRuntime.get()}")
|
||||||
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nativeMain {
|
||||||
|
kotlin.srcDir("src/vendor/native/kotlin")
|
||||||
|
}
|
||||||
|
|
||||||
|
val skikoRendererMain = create("skikoRendererMain") {
|
||||||
|
dependsOn(nativeMain.get())
|
||||||
|
kotlin.srcDir("src/vendor/skikoRenderer/kotlin")
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.skiko)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val skikoRendererMacosMain = create("skikoRendererMacosMain") { dependsOn(skikoRendererMain) }
|
||||||
|
val skikoRendererLinuxMain = create("skikoRendererLinuxMain") { dependsOn(skikoRendererMain) }
|
||||||
|
get("macosArm64Main").dependsOn(skikoRendererMacosMain)
|
||||||
|
get("linuxX64Main").dependsOn(skikoRendererLinuxMain)
|
||||||
|
get("linuxArm64Main").dependsOn(skikoRendererLinuxMain)
|
||||||
|
|
||||||
|
if (vHostSupportsMingw) {
|
||||||
|
val skikoRendererMingwSharedMain = create("skikoRendererMingwSharedMain") {
|
||||||
|
dependsOn(nativeMain.get())
|
||||||
|
kotlin.srcDir("src/skikoRendererMain/kotlin")
|
||||||
|
kotlin.srcDir("src/vendor/skikoRenderer/kotlin")
|
||||||
|
dependencies {
|
||||||
|
implementation("com.bitsycore.skiko:skiko:${providers.gradleProperty("skikoMingwVersion").getOrElse("0.150.1-mingw.1")}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val skikoRendererMingwMain = create("skikoRendererMingwMain") { dependsOn(skikoRendererMingwSharedMain) }
|
||||||
|
get("mingwX64Main").dependsOn(skikoRendererMingwMain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compilerOptions {
|
||||||
|
freeCompilerArgs.addAll(
|
||||||
|
"-Xcollection-literals",
|
||||||
|
"-Xexpect-actual-classes",
|
||||||
|
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
|
||||||
|
"-opt-in=androidx.compose.ui.InternalComposeUiApi",
|
||||||
|
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
SET_REPO=https://github.com/JetBrains/compose-multiplatform-core@<COMPOSE_CORE_REF>
|
||||||
|
SET_FOLDER=compose/ui/ui-text/src
|
||||||
|
|
||||||
|
commonMain/kotlin/ -> src/vendor/common/kotlin/
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/AnnotatedString.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/AtomicReference.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/AtomicReference.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/Bullet.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/Bullet.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/CharHelpers.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/CharHelpers.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/ExperimentalTextApi.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/ExperimentalTextApi.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/InternalTextApi.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/InternalTextApi.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/LinkAnnotation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/LinkAnnotation.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/LinkInteractionListener.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/LinkInteractionListener.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/MultiParagraph.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/MultiParagraph.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/MultiParagraphIntrinsics.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/MultiParagraphIntrinsics.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/Paragraph.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/Paragraph.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/ParagraphIntrinsics.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/ParagraphIntrinsics.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/Placeholder.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/PlatformTextStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/PlatformTextStyle.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/Savers.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/Savers.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/SpanStyle.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/String.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/String.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/StringAnnotation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/StringAnnotation.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/TextGranularity.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextGranularity.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/TextInclusionStrategy.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextInclusionStrategy.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/TextLayoutResult.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextLayoutResult.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/TextLinkStyles.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextLinkStyles.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextMeasurer.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextPainter.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/TextRange.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextRange.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextStyle.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/TtsAnnotation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TtsAnnotation.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/UrlAnnotation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/UrlAnnotation.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/Font.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/Font.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/FontFamily.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontFamily.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyTypefaceAdapter.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontFamilyTypefaceAdapter.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/FontLoadingStrategy.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontLoadingStrategy.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/FontMatcher.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontMatcher.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/FontStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontStyle.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontSynthesis.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/FontVariation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontVariation.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/FontWeight.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontWeight.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/font/Typeface.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/Typeface.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/EditCommand.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/EditCommand.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/EditProcessor.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/EditProcessor.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/EditingBuffer.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/EditingBuffer.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/GapBuffer.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/GapBuffer.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/ImeAction.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/ImeAction.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/ImeOptions.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/ImeOptions.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/InputEventCallback.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/InputEventCallback.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/KeyboardCapitalization.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/KeyboardCapitalization.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/KeyboardType.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/KeyboardType.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/MathUtils.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/MathUtils.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/OffsetMapping.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/OffsetMapping.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/TextInputService.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/input/VisualTransformation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/VisualTransformation.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/internal/InlineClassHelper.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/internal/InlineClassHelper.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/intl/Locale.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/intl/Locale.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/intl/LocaleList.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/intl/LocaleList.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/intl/PlatformLocale.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/intl/PlatformLocale.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/platform/Dispatcher.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/platform/Dispatcher.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/platform/PlatformMultiParagraphDraw.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/platform/PlatformMultiParagraphDraw.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/platform/PlatformString.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/platform/PlatformString.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/platform/Synchronization.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/platform/Synchronization.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/BaselineShift.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/BaselineShift.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/Hyphens.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/Hyphens.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/LineBreak.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/LineBreak.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/LineHeightStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/LineHeightStyle.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/ResolvedTextDirection.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/ResolvedTextDirection.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/TextAlign.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextAlign.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/TextDecoration.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextDecoration.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/TextDirection.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextDirection.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/TextForegroundStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextForegroundStyle.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/TextGeometricTransform.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextGeometricTransform.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/TextIndent.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextIndent.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/TextMotion.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextMotion.kt
|
||||||
|
# | commonMain/kotlin/androidx/compose/ui/text/style/TextOverflow.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextOverflow.kt
|
||||||
|
!commonMain/kotlin/androidx/compose/ui/text/platform/Synchronization.kt
|
||||||
|
|
||||||
|
nativeMain/kotlin/androidx/compose/ui/text/WeakReference.native.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/WeakReference.native.kt
|
||||||
|
nativeMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.native.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/font/FontSynthesis.native.kt
|
||||||
|
|
||||||
|
nonJvmMain/kotlin/ -> src/vendor/native/kotlin/
|
||||||
|
# | nonJvmMain/kotlin/androidx/compose/ui/text/AnnotatedString.nonJvm.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/AnnotatedString.nonJvm.kt
|
||||||
|
# | nonJvmMain/kotlin/androidx/compose/ui/text/AtomicReference.nonJvm.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/AtomicReference.nonJvm.kt
|
||||||
|
# | nonJvmMain/kotlin/androidx/compose/ui/text/CharHelpers.nonJvm.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/CharHelpers.nonJvm.kt
|
||||||
|
# | nonJvmMain/kotlin/androidx/compose/ui/text/input/GapBuffer.nonJvmMain.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/input/GapBuffer.nonJvmMain.kt
|
||||||
|
# | nonJvmMain/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.nonJvmNative.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.nonJvmNative.kt
|
||||||
|
!nonJvmMain/kotlin/androidx/compose/ui/text/CharHelpers.nonJvm.kt
|
||||||
|
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/Paragraph.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/Paragraph.native.kt
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/Savers.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/Savers.native.kt
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/TextDecorationLineStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextDecorationLineStyle.kt
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/TextStyle.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/TextStyle.native.kt
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/UnresolvedSymbolsRegistry.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/UnresolvedSymbolsRegistry.native.kt
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/WeakReference.skiko.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/WeakReference.kt
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.native.kt
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/input/PlatformTextInputService2.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/input/PlatformTextInputService2.kt
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/platform/SkiaMultiParagraphDraw.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/platform/SkiaMultiParagraphDraw.native.kt
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/style/LineBreak.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/style/LineBreak.native.kt
|
||||||
|
skikoMain/kotlin/androidx/compose/ui/text/style/TextMotion.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/style/TextMotion.native.kt
|
||||||
|
|
||||||
|
# >>> ---- DIAGNOSTIC GAPS ---- <<<<
|
||||||
|
# androidMain (77)
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/AndroidComposeUiTextFlags.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/AndroidComposeUiTextFlags.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/AndroidParagraph.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/AndroidTextStyle.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/AndroidTextStyle.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/CharHelpers.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/CharHelpers.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/EmojiSupportMatch.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/EmojiSupportMatch.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/Html.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/Html.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/Paragraph.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/Paragraph.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/ParagraphIntrinsics.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/ParagraphIntrinsics.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/Savers.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/Savers.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/BoringLayoutFactory.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/BoringLayoutFactory.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/CharSequenceCharacterIterator.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/CharSequenceCharacterIterator.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/InlineClassUtils.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/InlineClassUtils.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/InternalPlatformTextApi.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/InternalPlatformTextApi.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/LayoutCompat.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/LayoutCompat.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/LayoutHelper.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/LayoutHelper.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/LayoutIntrinsics.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/LayoutIntrinsics.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/ListUtils.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/ListUtils.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/PaintExtensions.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/PaintExtensions.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/SpannedExtensions.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/SpannedExtensions.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/StaticLayoutFactory.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/StaticLayoutFactory.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/TextAndroidCanvas.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/TextAndroidCanvas.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/TextLayout.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/TextLayout.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/TextLayoutGetRangeForRectExtensions.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/TextLayoutGetRangeForRectExtensions.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/selection/SegmentFinder.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/selection/SegmentFinder.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/selection/WordBoundary.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/selection/WordBoundary.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/selection/WordIterator.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/selection/WordIterator.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/BaselineShiftSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/BaselineShiftSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/FontFeatureSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/FontFeatureSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/IndentationFixSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/IndentationFixSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanEm.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/LetterSpacingSpanPx.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/LineHeightSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/LineHeightSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/LineHeightStyleSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/LineHeightStyleSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/PlaceholderSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/PlaceholderSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/ShadowSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/ShadowSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/SkewXSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/SkewXSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/TextDecorationSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/TextDecorationSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/android/style/TypefaceSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/android/style/TypefaceSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/AndroidFont.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/AndroidFont.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontLoader.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/AndroidFontLoader.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontResolveInterceptor.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/AndroidFontResolveInterceptor.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/AndroidFontUtils.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/AndroidFontUtils.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/AndroidPreloadedFont.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/AndroidPreloadedFont.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/AndroidTypeface.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/AndroidTypeface.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/DeviceFontFamilyNameFont.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/DeviceFontFamilyNameFont.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/FontSynthesis.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapter.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapter.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/PlatformFontVariationSettings.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/PlatformFontVariationSettings.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/font/PlatformTypefaces.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/font/PlatformTypefaces.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/intl/AndroidLocaleDelegate.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/intl/AndroidLocaleDelegate.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/intl/AndroidPlatformLocale.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/intl/AndroidPlatformLocale.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/AndroidAccessibilitySpannableString.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/AndroidAccessibilitySpannableString.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/AndroidDefaultTypeface.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/AndroidDefaultTypeface.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/AndroidFontListTypeface.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/AndroidFontListTypeface.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/AndroidGenericFontFamilyTypeface.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/AndroidGenericFontFamilyTypeface.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/AndroidMultiParagraphDraw.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/AndroidMultiParagraphDraw.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/AndroidParagraphHelper.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/AndroidStringDelegate.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/AndroidStringDelegate.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/AndroidTextPaint.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/AndroidTextPaint.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/AndroidTypeface.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/AndroidTypeface.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/AndroidTypefaceWrapper.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/AndroidTypefaceWrapper.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/EmojiCompatStatus.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/EmojiCompatStatus.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/Synchronization.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/Synchronization.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/URLSpanCache.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/URLSpanCache.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/extensions/LocaleExtensions.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/extensions/LocaleExtensions.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/extensions/PlaceholderExtensions.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/extensions/PlaceholderExtensions.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/extensions/SpannableExtensions.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/extensions/SpannableExtensions.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/extensions/TextPaintExtensions.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/extensions/TextPaintExtensions.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/extensions/TtsAnnotationExtensions.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/extensions/TtsAnnotationExtensions.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/style/BulletSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/style/BulletSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/style/DrawStyleSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/style/DrawStyleSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/platform/style/ShaderBrushSpan.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/platform/style/ShaderBrushSpan.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/style/LineBreak.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/style/LineBreak.android.kt
|
||||||
|
# | androidMain/kotlin/androidx/compose/ui/text/style/TextMotion.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/text/style/TextMotion.android.kt
|
||||||
|
# darwinMain (2)
|
||||||
|
# | darwinMain/kotlin/androidx/compose/ui/text/intl/PlatformLocale.darwin.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/intl/PlatformLocale.darwin.kt
|
||||||
|
# | darwinMain/kotlin/androidx/compose/ui/text/platform/NativeStringDelegate.darwin.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/platform/NativeStringDelegate.darwin.kt
|
||||||
|
# desktopMain (15)
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/Cache.jvm.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/Cache.jvm.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/CharHelpers.jvm.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/CharHelpers.jvm.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/WeakReference.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/WeakReference.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/font/FontFamily.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/font/FontFamily.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.skiko.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.skiko.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/font/FontSynthesis.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/intl/DesktopPlatformLocale.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/intl/DesktopPlatformLocale.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/platform/AwtFontInterop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/platform/AwtFontInterop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/platform/AwtFontUtils.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/platform/AwtFontUtils.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopFont.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/platform/DesktopFont.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/platform/DesktopStringDelegate.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/platform/DesktopStringDelegate.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/platform/InternalFontApiChecker.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/platform/InternalFontApiChecker.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/platform/JetBrainsRuntimeFontFamilies.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/platform/JetBrainsRuntimeFontFamilies.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/platform/ParagraphLayouter.desktop.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/platform/ParagraphLayouter.desktop.kt
|
||||||
|
# | desktopMain/kotlin/androidx/compose/ui/text/platform/ReflectionUtil.kt -> src/vendor/desktop/kotlin/androidx/compose/ui/text/platform/ReflectionUtil.kt
|
||||||
|
# iosMain (2)
|
||||||
|
# | iosMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.ios.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.ios.kt
|
||||||
|
# | iosMain/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.ios.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.ios.kt
|
||||||
|
# jsMain (1)
|
||||||
|
# | jsMain/kotlin/androidx/compose/ui/text/intl/Actuals.js.kt -> src/vendor/js/kotlin/androidx/compose/ui/text/intl/Actuals.js.kt
|
||||||
|
# jvmAndAndroidMain (5)
|
||||||
|
# | jvmAndAndroidMain/kotlin/androidx/compose/ui/text/ActualAtomicReferenceJvm.jvmAndAndroid.kt -> src/vendor/jvmAndAndroid/kotlin/androidx/compose/ui/text/ActualAtomicReferenceJvm.jvmAndAndroid.kt
|
||||||
|
# | jvmAndAndroidMain/kotlin/androidx/compose/ui/text/JvmAnnotatedString.jvmAndAndroid.kt -> src/vendor/jvmAndAndroid/kotlin/androidx/compose/ui/text/JvmAnnotatedString.jvmAndAndroid.kt
|
||||||
|
# | jvmAndAndroidMain/kotlin/androidx/compose/ui/text/input/GapBuffer.jvmAndAndroid.kt -> src/vendor/jvmAndAndroid/kotlin/androidx/compose/ui/text/input/GapBuffer.jvmAndAndroid.kt
|
||||||
|
# | jvmAndAndroidMain/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.jvmAndAndroid.kt -> src/vendor/jvmAndAndroid/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.jvmAndAndroid.kt
|
||||||
|
# | jvmAndAndroidMain/kotlin/androidx/compose/ui/text/intl/Locale.jvmAndAndroid.kt -> src/vendor/jvmAndAndroid/kotlin/androidx/compose/ui/text/intl/Locale.jvmAndAndroid.kt
|
||||||
|
# macosMain (1)
|
||||||
|
# | macosMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.skiko.macos.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.skiko.macos.kt
|
||||||
|
# nativeMain (4)
|
||||||
|
# | nativeMain/kotlin/androidx/compose/ui/text/Cache.native.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/Cache.native.kt
|
||||||
|
# | nativeMain/kotlin/androidx/compose/ui/text/platform/NativeFont.native.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/platform/NativeFont.native.kt
|
||||||
|
# | nativeMain/kotlin/androidx/compose/ui/text/platform/ParagraphLayouter.native.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/platform/ParagraphLayouter.native.kt
|
||||||
|
# | nativeMain/kotlin/androidx/compose/ui/text/platform/PlatformFont.native.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/platform/PlatformFont.native.kt
|
||||||
|
# skikoMain (17)
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/ActualParagraph.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/ActualParagraph.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/Cache.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/Cache.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/CharHelpers.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/CharHelpers.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/FontRasterizationSettings.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/FontRasterizationSettings.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/ParagraphIntrisics.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/ParagraphIntrisics.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/SkiaParagraph.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapter.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/font/PlatformFontFamilyTypefaceAdapter.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/font/SkiaFontLoader.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/font/SkiaFontLoader.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/intl/PlatformLocale.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/intl/PlatformLocale.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/platform/ParagraphBuilder.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/platform/ParagraphBuilder.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/platform/ParagraphLayouter.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/platform/ParagraphLayouter.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/platform/PlatformFont.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/platform/PlatformFont.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/platform/SkiaParagraphIntrinsics.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/platform/SkiaParagraphIntrinsics.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/platform/SkiaTextPaint.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/platform/SkiaTextPaint.skiko.kt
|
||||||
|
# | skikoMain/kotlin/androidx/compose/ui/text/platform/Synchronization.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/text/platform/Synchronization.skiko.kt
|
||||||
|
# wasmJsMain (1)
|
||||||
|
# | wasmJsMain/kotlin/androidx/compose/ui/text/intl/Actuals.wasm.kt -> src/vendor/wasmJs/kotlin/androidx/compose/ui/text/intl/Actuals.wasm.kt
|
||||||
|
# webMain (10)
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/text/Cache.web.kt -> src/vendor/web/kotlin/androidx/compose/ui/text/Cache.web.kt
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/text/WeakReference.web.kt -> src/vendor/web/kotlin/androidx/compose/ui/text/WeakReference.web.kt
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.skiko.web.kt -> src/vendor/web/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.skiko.web.kt
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.web.kt -> src/vendor/web/kotlin/androidx/compose/ui/text/font/FontSynthesis.web.kt
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/text/intl/PlatformLocale.web.kt -> src/vendor/web/kotlin/androidx/compose/ui/text/intl/PlatformLocale.web.kt
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/text/intl/PreferredLanguages.web.kt -> src/vendor/web/kotlin/androidx/compose/ui/text/intl/PreferredLanguages.web.kt
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/text/platform/ParagraphLayouter.web.kt -> src/vendor/web/kotlin/androidx/compose/ui/text/platform/ParagraphLayouter.web.kt
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/text/platform/PlatformFont.web.kt -> src/vendor/web/kotlin/androidx/compose/ui/text/platform/PlatformFont.web.kt
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/text/platform/WebFont.kt -> src/vendor/web/kotlin/androidx/compose/ui/text/platform/WebFont.kt
|
||||||
|
# | webMain/kotlin/androidx/compose/ui/text/platform/WebStringDelegate.kt -> src/vendor/web/kotlin/androidx/compose/ui/text/platform/WebStringDelegate.kt
|
||||||
|
# <<< ---- DIAGNOSTIC GAPS ---- >>>>
|
||||||
+3
-3
@@ -20,9 +20,9 @@ object IconFont {
|
|||||||
|
|
||||||
private val fFonts = mutableMapOf<String, ByteArray>()
|
private val fFonts = mutableMapOf<String, ByteArray>()
|
||||||
// Families that render as single variable-axis glyphs (Material Symbols),
|
// Families that render as single variable-axis glyphs (Material Symbols),
|
||||||
// vs ordinary text fonts. The SDL3 renderer routes only these through its
|
// vs ordinary text fonts — icon families are drawn one codepoint at a time,
|
||||||
// single-glyph FreeType icon path; text families (e.g. a bundled
|
// while text families (e.g. a bundled monospace) go through normal
|
||||||
// monospace) must go through normal full-string rendering.
|
// full-string rendering.
|
||||||
private val fIconFamilies = mutableSetOf<String>()
|
private val fIconFamilies = mutableSetOf<String>()
|
||||||
|
|
||||||
/** Register a TEXT font family's bytes (rendered as full strings; e.g. a
|
/** Register a TEXT font family's bytes (rendered as full strings; e.g. a
|
||||||
+12
-1
@@ -30,6 +30,10 @@ class NamedFont(
|
|||||||
override val weight: FontWeight = FontWeight.Normal,
|
override val weight: FontWeight = FontWeight.Normal,
|
||||||
override val style: FontStyle = FontStyle.Normal,
|
override val style: FontStyle = FontStyle.Normal,
|
||||||
val variationSettings: FontVariation.Settings = FontVariation.Settings(weight, style),
|
val variationSettings: FontVariation.Settings = FontVariation.Settings(weight, style),
|
||||||
|
// Raw variable-font axis settings (Material Symbols FILL/wght/GRAD/opsz),
|
||||||
|
// threaded through the family so the skiko text engine can apply them via
|
||||||
|
// FontFamily.projectFontVariations() — icons render on the standard text path.
|
||||||
|
val axes: List<FontVariation.Setting>? = null,
|
||||||
) : Font {
|
) : Font {
|
||||||
override val loadingStrategy: FontLoadingStrategy = FontLoadingStrategy.Blocking
|
override val loadingStrategy: FontLoadingStrategy = FontLoadingStrategy.Blocking
|
||||||
|
|
||||||
@@ -52,7 +56,8 @@ fun namedFontFamily(
|
|||||||
name: String,
|
name: String,
|
||||||
weight: FontWeight = FontWeight.Normal,
|
weight: FontWeight = FontWeight.Normal,
|
||||||
style: FontStyle = FontStyle.Normal,
|
style: FontStyle = FontStyle.Normal,
|
||||||
): FontFamily = FontFamily(NamedFont(name, weight, style))
|
axes: List<FontVariation.Setting>? = null,
|
||||||
|
): FontFamily = FontFamily(NamedFont(name, weight, style, axes = axes))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the renderer font-registry name for a `FontFamily`:
|
* Extract the renderer font-registry name for a `FontFamily`:
|
||||||
@@ -69,3 +74,9 @@ fun FontFamily?.projectFontName(): String? = when (this) {
|
|||||||
if (name == "sans-serif") null else "generic:$name"
|
if (name == "sans-serif") null else "generic:$name"
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Variable-font axis settings carried by a [namedFontFamily]'s [NamedFont]
|
||||||
|
* (Material Symbols FILL/wght/GRAD/opsz); null for every other family. The
|
||||||
|
* skiko text engine applies these on top of / instead of the paragraph weight. */
|
||||||
|
fun FontFamily?.projectFontVariations(): List<FontVariation.Setting>? =
|
||||||
|
((this as? FontListFontFamily)?.firstOrNull() as? NamedFont)?.axes
|
||||||
+2
-3
@@ -10,9 +10,8 @@ package com.compose.sdl.text
|
|||||||
something the renderer can't deliver. Today the only consumer is the
|
something the renderer can't deliver. Today the only consumer is the
|
||||||
Material Symbols install() path, which emits a one-shot warning when
|
Material Symbols install() path, which emits a one-shot warning when
|
||||||
variable-font axes won't be honoured. The Skia renderer sets
|
variable-font axes won't be honoured. The Skia renderer sets
|
||||||
supportsFontVariations = true via Typeface.makeClone; the SDL3 renderer
|
supportsFontVariations = true via Typeface.makeClone; a renderer that
|
||||||
leaves it false because SDL3_ttf 3.2 has no axis-set API (filename /
|
couldn't honour variable-font axes would leave it false.
|
||||||
iostream / size / face / dpi only).
|
|
||||||
|
|
||||||
`supportsFontVariations` is null until a renderer initialises — code
|
`supportsFontVariations` is null until a renderer initialises — code
|
||||||
that warns on install() ignores the null case so it doesn't fire when
|
that warns on install() ignores the null case so it doesn't fire when
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.compose.sdl.text
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// MARK: Viewport size (project-only)
|
||||||
|
// ==================
|
||||||
|
|
||||||
|
/** Logical size of the window, set by the render loop each frame. Lets commonMain
|
||||||
|
composables (selection highlights cull per-line work, DropdownMenu flips/clamps
|
||||||
|
itself) read the viewport without a hard dependency on the window layer.
|
||||||
|
0 until first set — callers treat that as "viewport unknown". */
|
||||||
|
var currentViewportHeight: Int = 0
|
||||||
|
var currentViewportWidth: Int = 0
|
||||||
+1
-1
@@ -20,7 +20,7 @@ import androidx.compose.ui.text.input.TextFieldValue
|
|||||||
SDL_TEXT_INPUT_EVENT), replace this with a real implementation.
|
SDL_TEXT_INPUT_EVENT), replace this with a real implementation.
|
||||||
*/
|
*/
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
internal object NoOpPlatformTextInputService : PlatformTextInputService {
|
object NoOpPlatformTextInputService : PlatformTextInputService {
|
||||||
override fun startInput(
|
override fun startInput(
|
||||||
value: TextFieldValue,
|
value: TextFieldValue,
|
||||||
imeOptions: ImeOptions,
|
imeOptions: ImeOptions,
|
||||||
+61
-27
@@ -7,22 +7,52 @@ import androidx.compose.ui.unit.Constraints
|
|||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.unit.Density
|
||||||
|
|
||||||
// ==================
|
// ==================
|
||||||
// MARK: ParagraphIntrinsics + Paragraph factory actuals (SDL)
|
// MARK: ParagraphIntrinsics + Paragraph factory actuals (skiko engine)
|
||||||
// ==================
|
// ==================
|
||||||
|
//
|
||||||
|
// The `Paragraph()` / `ParagraphIntrinsics()` factories (expects in vendored
|
||||||
|
// commonMain) resolve here to the skiko-backed engine. Because `:ui`'s native
|
||||||
|
// hierarchy is nativeMain -> skiko (inverted vs upstream skiko -> native), the
|
||||||
|
// real SkiaParagraph lives in the child skiko source set; these parent-level
|
||||||
|
// actuals bridge to it through `makeSkiaParagraph` / `paragraphIntrinsicWidths`,
|
||||||
|
// whose actuals sit in skikoRendererMain (compiled into both the official-skiko
|
||||||
|
// and mingw-fork siblings). Same pattern as createRenderBackend.
|
||||||
|
|
||||||
/** Carries text+style for the intrinsics-based Paragraph factories; intrinsic widths come from a
|
/** Construct the nativeMain [SkiaParagraph] over a skiko-backed ops seam. */
|
||||||
throwaway unbounded SdlParagraph. `density` here is the LocalDensity scalar (dpr on Retina); it
|
private fun makeSkiaParagraph(
|
||||||
converts sp → pixels so the intrinsic widths land in the same pixel space the layout tree measures
|
text: String,
|
||||||
itself in. */
|
style: TextStyle,
|
||||||
internal class SdlParagraphIntrinsics(
|
width: Float,
|
||||||
|
maxLines: Int,
|
||||||
|
ellipsize: Boolean,
|
||||||
|
density: Float,
|
||||||
|
spanStyles: List<AnnotatedString.Range<SpanStyle>>,
|
||||||
|
): Paragraph = SkiaParagraph(
|
||||||
|
text, style, width,
|
||||||
|
buildParagraphOps(text, style, width, maxLines, ellipsize, density, spanStyles),
|
||||||
|
)
|
||||||
|
|
||||||
|
/** [min, max] intrinsic width from a throwaway unbounded skiko layout. */
|
||||||
|
internal expect fun paragraphIntrinsicWidths(
|
||||||
|
text: String,
|
||||||
|
style: TextStyle,
|
||||||
|
density: Float,
|
||||||
|
spanStyles: List<AnnotatedString.Range<SpanStyle>>,
|
||||||
|
): FloatArray
|
||||||
|
|
||||||
|
/** Carries text+style for the intrinsics-based Paragraph factories; intrinsic
|
||||||
|
widths come from an unbounded skiko layout. `density` is the LocalDensity
|
||||||
|
scalar (dpr on Retina): it converts sp → pixels so intrinsic widths land in
|
||||||
|
the same physical-pixel space the layout tree measures in. */
|
||||||
|
internal class NativeParagraphIntrinsics(
|
||||||
val paragraphText: String,
|
val paragraphText: String,
|
||||||
val paragraphStyle: TextStyle,
|
val paragraphStyle: TextStyle,
|
||||||
val density: Float,
|
val density: Float,
|
||||||
val spanStyles: List<AnnotatedString.Range<SpanStyle>> = emptyList(),
|
val spanStyles: List<AnnotatedString.Range<SpanStyle>> = emptyList(),
|
||||||
) : ParagraphIntrinsics {
|
) : ParagraphIntrinsics {
|
||||||
private val probe = SdlParagraph(paragraphText, paragraphStyle, Float.POSITIVE_INFINITY, Int.MAX_VALUE, density, spanStyles)
|
private val widths = paragraphIntrinsicWidths(paragraphText, paragraphStyle, density, spanStyles)
|
||||||
override val minIntrinsicWidth: Float = probe.minIntrinsicWidth
|
override val minIntrinsicWidth: Float = widths[0]
|
||||||
override val maxIntrinsicWidth: Float = probe.maxIntrinsicWidth
|
override val maxIntrinsicWidth: Float = widths[1]
|
||||||
override val hasStaleResolvedFonts: Boolean = false
|
override val hasStaleResolvedFonts: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +69,7 @@ actual fun ParagraphIntrinsics(
|
|||||||
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
||||||
density: Density,
|
density: Density,
|
||||||
resourceLoader: Font.ResourceLoader,
|
resourceLoader: Font.ResourceLoader,
|
||||||
): ParagraphIntrinsics = SdlParagraphIntrinsics(text, style, density.density, spanStyles)
|
): ParagraphIntrinsics = NativeParagraphIntrinsics(text, style, density.density, spanStyles)
|
||||||
|
|
||||||
actual fun ParagraphIntrinsics(
|
actual fun ParagraphIntrinsics(
|
||||||
text: String,
|
text: String,
|
||||||
@@ -48,7 +78,7 @@ actual fun ParagraphIntrinsics(
|
|||||||
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
||||||
density: Density,
|
density: Density,
|
||||||
fontFamilyResolver: FontFamily.Resolver,
|
fontFamilyResolver: FontFamily.Resolver,
|
||||||
): ParagraphIntrinsics = SdlParagraphIntrinsics(text, style, density.density, spanStyles)
|
): ParagraphIntrinsics = NativeParagraphIntrinsics(text, style, density.density, spanStyles)
|
||||||
|
|
||||||
actual fun ParagraphIntrinsics(
|
actual fun ParagraphIntrinsics(
|
||||||
text: String,
|
text: String,
|
||||||
@@ -57,7 +87,7 @@ actual fun ParagraphIntrinsics(
|
|||||||
density: Density,
|
density: Density,
|
||||||
fontFamilyResolver: FontFamily.Resolver,
|
fontFamilyResolver: FontFamily.Resolver,
|
||||||
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
||||||
): ParagraphIntrinsics = SdlParagraphIntrinsics(text, style, density.density, annotations.filterSpanStyles())
|
): ParagraphIntrinsics = NativeParagraphIntrinsics(text, style, density.density, annotations.filterSpanStyles())
|
||||||
|
|
||||||
actual fun ParagraphIntrinsics(
|
actual fun ParagraphIntrinsics(
|
||||||
text: String,
|
text: String,
|
||||||
@@ -67,12 +97,11 @@ actual fun ParagraphIntrinsics(
|
|||||||
fontFamilyResolver: FontFamily.Resolver,
|
fontFamilyResolver: FontFamily.Resolver,
|
||||||
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
||||||
softWrap: Boolean,
|
softWrap: Boolean,
|
||||||
): ParagraphIntrinsics = SdlParagraphIntrinsics(text, style, density.density, annotations.filterSpanStyles())
|
): ParagraphIntrinsics = NativeParagraphIntrinsics(text, style, density.density, annotations.filterSpanStyles())
|
||||||
|
|
||||||
// AnnotatedString.Annotation is a sealed interface with subtypes SpanStyle,
|
// AnnotatedString.Annotation is a sealed interface (SpanStyle, ParagraphStyle,
|
||||||
// ParagraphStyle, LinkAnnotation, StringAnnotation, TtsAnnotation, etc. Only
|
// LinkAnnotation, StringAnnotation, TtsAnnotation, …). Only SpanStyle affects
|
||||||
// SpanStyle contributes to glyph rendering — the paint path knows how to
|
// glyph layout/painting.
|
||||||
// interpret it via drawNativeText's `inSpans` argument.
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun List<AnnotatedString.Range<out AnnotatedString.Annotation>>.filterSpanStyles():
|
private fun List<AnnotatedString.Range<out AnnotatedString.Annotation>>.filterSpanStyles():
|
||||||
List<AnnotatedString.Range<SpanStyle>> =
|
List<AnnotatedString.Range<SpanStyle>> =
|
||||||
@@ -91,7 +120,7 @@ actual fun Paragraph(
|
|||||||
width: Float,
|
width: Float,
|
||||||
density: Density,
|
density: Density,
|
||||||
resourceLoader: Font.ResourceLoader,
|
resourceLoader: Font.ResourceLoader,
|
||||||
): Paragraph = SdlParagraph(text, style, width, maxLines, density.density, spanStyles)
|
): Paragraph = makeSkiaParagraph(text, style, width, maxLines, ellipsis, density.density, spanStyles)
|
||||||
|
|
||||||
actual fun Paragraph(
|
actual fun Paragraph(
|
||||||
text: String,
|
text: String,
|
||||||
@@ -103,7 +132,7 @@ actual fun Paragraph(
|
|||||||
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
||||||
maxLines: Int,
|
maxLines: Int,
|
||||||
ellipsis: Boolean,
|
ellipsis: Boolean,
|
||||||
): Paragraph = SdlParagraph(text, style, width, maxLines, density.density, spanStyles)
|
): Paragraph = makeSkiaParagraph(text, style, width, maxLines, ellipsis, density.density, spanStyles)
|
||||||
|
|
||||||
actual fun Paragraph(
|
actual fun Paragraph(
|
||||||
text: String,
|
text: String,
|
||||||
@@ -115,7 +144,7 @@ actual fun Paragraph(
|
|||||||
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
||||||
maxLines: Int,
|
maxLines: Int,
|
||||||
ellipsis: Boolean,
|
ellipsis: Boolean,
|
||||||
): Paragraph = SdlParagraph(text, style, widthFrom(constraints), maxLines, density.density, spanStyles)
|
): Paragraph = makeSkiaParagraph(text, style, widthFrom(constraints), maxLines, ellipsis, density.density, spanStyles)
|
||||||
|
|
||||||
actual fun Paragraph(
|
actual fun Paragraph(
|
||||||
text: String,
|
text: String,
|
||||||
@@ -127,7 +156,9 @@ actual fun Paragraph(
|
|||||||
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
placeholders: List<AnnotatedString.Range<Placeholder>>,
|
||||||
maxLines: Int,
|
maxLines: Int,
|
||||||
overflow: TextOverflow,
|
overflow: TextOverflow,
|
||||||
): Paragraph = SdlParagraph(text, style, widthFrom(constraints), maxLines, density.density, spanStyles)
|
): Paragraph = makeSkiaParagraph(
|
||||||
|
text, style, widthFrom(constraints), maxLines, overflow == TextOverflow.Ellipsis, density.density, spanStyles,
|
||||||
|
)
|
||||||
|
|
||||||
actual fun Paragraph(
|
actual fun Paragraph(
|
||||||
paragraphIntrinsics: ParagraphIntrinsics,
|
paragraphIntrinsics: ParagraphIntrinsics,
|
||||||
@@ -135,8 +166,8 @@ actual fun Paragraph(
|
|||||||
ellipsis: Boolean,
|
ellipsis: Boolean,
|
||||||
width: Float,
|
width: Float,
|
||||||
): Paragraph {
|
): Paragraph {
|
||||||
val vI = paragraphIntrinsics as SdlParagraphIntrinsics
|
val i = paragraphIntrinsics as NativeParagraphIntrinsics
|
||||||
return SdlParagraph(vI.paragraphText, vI.paragraphStyle, width, maxLines, vI.density, vI.spanStyles)
|
return makeSkiaParagraph(i.paragraphText, i.paragraphStyle, width, maxLines, ellipsis, i.density, i.spanStyles)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun Paragraph(
|
actual fun Paragraph(
|
||||||
@@ -145,8 +176,8 @@ actual fun Paragraph(
|
|||||||
maxLines: Int,
|
maxLines: Int,
|
||||||
ellipsis: Boolean,
|
ellipsis: Boolean,
|
||||||
): Paragraph {
|
): Paragraph {
|
||||||
val vI = paragraphIntrinsics as SdlParagraphIntrinsics
|
val i = paragraphIntrinsics as NativeParagraphIntrinsics
|
||||||
return SdlParagraph(vI.paragraphText, vI.paragraphStyle, widthFrom(constraints), maxLines, vI.density, vI.spanStyles)
|
return makeSkiaParagraph(i.paragraphText, i.paragraphStyle, widthFrom(constraints), maxLines, ellipsis, i.density, i.spanStyles)
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun Paragraph(
|
actual fun Paragraph(
|
||||||
@@ -155,6 +186,9 @@ actual fun Paragraph(
|
|||||||
maxLines: Int,
|
maxLines: Int,
|
||||||
overflow: TextOverflow,
|
overflow: TextOverflow,
|
||||||
): Paragraph {
|
): Paragraph {
|
||||||
val vI = paragraphIntrinsics as SdlParagraphIntrinsics
|
val i = paragraphIntrinsics as NativeParagraphIntrinsics
|
||||||
return SdlParagraph(vI.paragraphText, vI.paragraphStyle, widthFrom(constraints), maxLines, vI.density, vI.spanStyles)
|
return makeSkiaParagraph(
|
||||||
|
i.paragraphText, i.paragraphStyle, widthFrom(constraints), maxLines,
|
||||||
|
overflow == TextOverflow.Ellipsis, i.density, i.spanStyles,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
+305
@@ -0,0 +1,305 @@
|
|||||||
|
package androidx.compose.ui.text
|
||||||
|
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Rect
|
||||||
|
import androidx.compose.ui.graphics.BlendMode
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Canvas
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Path
|
||||||
|
import androidx.compose.ui.graphics.Shadow
|
||||||
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.graphics.drawscope.DrawStyle
|
||||||
|
import androidx.compose.ui.text.style.ResolvedTextDirection
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// MARK: SkiaParagraph — nativeMain Paragraph over a skiko-backed ops seam
|
||||||
|
// ==================
|
||||||
|
//
|
||||||
|
// `Paragraph` is a sealed interface whose actual is declared in nativeMain, so
|
||||||
|
// its implementers must live here (skiko-tied code in the child skikoRenderer
|
||||||
|
// source set counts as a "different module" for the sealed check). This class
|
||||||
|
// therefore holds no skiko types: it drives the real skiko skparagraph through
|
||||||
|
// [NativeParagraphOps] (impl in skikoRendererMain), exchanging plain data
|
||||||
|
// ([TextBoxData] / [LineMetricData] / [RectData]). Cursor/selection/line-metric
|
||||||
|
// math is adapted from upstream `SkiaParagraph.skiko.kt` (@ CMP core v1.12.x).
|
||||||
|
|
||||||
|
/** Plain mirror of a skiko `TextBox` (a glyph-run rect + its direction). */
|
||||||
|
internal class TextBoxData(
|
||||||
|
val left: Float, val top: Float, val right: Float, val bottom: Float, val isRtl: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Plain mirror of a skiko paragraph `LineMetrics`. */
|
||||||
|
internal class LineMetricData(
|
||||||
|
val startIndex: Int,
|
||||||
|
val endIndex: Int,
|
||||||
|
val endExcludingWhitespaces: Int,
|
||||||
|
val endIncludingNewline: Int,
|
||||||
|
val isHardBreak: Boolean,
|
||||||
|
val ascent: Double,
|
||||||
|
val descent: Double,
|
||||||
|
val baseline: Double,
|
||||||
|
val left: Double,
|
||||||
|
val right: Double,
|
||||||
|
val width: Double,
|
||||||
|
val height: Double,
|
||||||
|
val lineNumber: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
/** Plain mirror of a Compose/skiko rect. */
|
||||||
|
internal class RectData(val left: Float, val top: Float, val right: Float, val bottom: Float) {
|
||||||
|
fun toComposeRect() = Rect(left, top, right, bottom)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skiko-free view of one laid-out skiko paragraph. Implemented in
|
||||||
|
* skikoRendererMain (SkiaParagraphOps) where skiko is on the classpath; all
|
||||||
|
* exchange types are plain so this interface can live in nativeMain beside the
|
||||||
|
* sealed [Paragraph]. [rebuildAndPaint] re-lays-out with paint-time overrides
|
||||||
|
* (Compose paints text with the resolved colour/shadow/decoration) then draws.
|
||||||
|
*/
|
||||||
|
internal interface NativeParagraphOps {
|
||||||
|
val height: Float
|
||||||
|
val lineNumber: Int
|
||||||
|
val minIntrinsicWidth: Float
|
||||||
|
val maxIntrinsicWidth: Float
|
||||||
|
val didExceedMaxLines: Boolean
|
||||||
|
val alphabeticBaseline: Float
|
||||||
|
val defaultAscentPx: Float // positive: -fontMetrics.ascent
|
||||||
|
val defaultDescentPx: Float
|
||||||
|
|
||||||
|
fun lineMetrics(): List<LineMetricData>
|
||||||
|
fun placeholderRects(): List<RectData?>
|
||||||
|
fun getRectsForRange(start: Int, end: Int, useMaxHeight: Boolean): List<TextBoxData>
|
||||||
|
fun glyphPositionAtCoordinate(x: Float, y: Float): Int
|
||||||
|
fun wordBoundary(offset: Int): IntArray // [start, end]
|
||||||
|
|
||||||
|
fun rebuildAndPaint(canvas: Canvas, color: Color, shadow: Shadow?, decoration: TextDecoration?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Bridge to the skiko ops impl (actual in skikoRendererMain). */
|
||||||
|
internal expect fun buildParagraphOps(
|
||||||
|
text: String,
|
||||||
|
style: TextStyle,
|
||||||
|
width: Float,
|
||||||
|
maxLines: Int,
|
||||||
|
ellipsize: Boolean,
|
||||||
|
density: Float,
|
||||||
|
spanStyles: List<AnnotatedString.Range<SpanStyle>>,
|
||||||
|
): NativeParagraphOps
|
||||||
|
|
||||||
|
internal class SkiaParagraph(
|
||||||
|
private val text: String,
|
||||||
|
private val style: TextStyle,
|
||||||
|
private val widthConstraint: Float,
|
||||||
|
private val ops: NativeParagraphOps,
|
||||||
|
) : Paragraph {
|
||||||
|
|
||||||
|
private val textDirection: ResolvedTextDirection = ResolvedTextDirection.Ltr
|
||||||
|
|
||||||
|
override val width: Float
|
||||||
|
get() = if (widthConstraint.isFinite()) widthConstraint else ops.maxIntrinsicWidth
|
||||||
|
override val height: Float get() = ops.height
|
||||||
|
override val minIntrinsicWidth: Float get() = ops.minIntrinsicWidth
|
||||||
|
override val maxIntrinsicWidth: Float get() = ops.maxIntrinsicWidth
|
||||||
|
override val firstBaseline: Float get() = lineMetrics.firstOrNull()?.baseline?.toFloat() ?: 0f
|
||||||
|
override val lastBaseline: Float get() = lineMetrics.lastOrNull()?.baseline?.toFloat() ?: 0f
|
||||||
|
override val didExceedMaxLines: Boolean get() = ops.didExceedMaxLines
|
||||||
|
|
||||||
|
override val lineCount: Int
|
||||||
|
get() = if (text == "" || ops.lineNumber < 1) 1 else ops.lineNumber
|
||||||
|
|
||||||
|
override val placeholderRects: List<Rect?>
|
||||||
|
get() = ops.placeholderRects().map { it?.toComposeRect() }
|
||||||
|
|
||||||
|
override fun getPathForRange(start: Int, end: Int): Path {
|
||||||
|
val path = Path()
|
||||||
|
for (b in ops.getRectsForRange(start, end, useMaxHeight = true)) {
|
||||||
|
path.addRect(Rect(b.left, b.top, b.right, b.bottom))
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCursorRect(offset: Int): Rect {
|
||||||
|
val horizontal = getHorizontalPosition(offset, true)
|
||||||
|
val line = lineMetricsForOffset(offset) ?: return Rect(horizontal, 0f, horizontal, ops.defaultDescentPx)
|
||||||
|
val isNewEmptyLine = offset - 1 == line.startIndex && offset == text.length
|
||||||
|
val asc = line.ascent.let { if (isNewEmptyLine) it.coerceAtMost(ops.defaultAscentPx.toDouble()) else it }
|
||||||
|
val desc = line.descent.let { if (isNewEmptyLine) it.coerceAtMost(ops.defaultDescentPx.toDouble()) else it }
|
||||||
|
return Rect(horizontal, (line.baseline - asc).toFloat(), horizontal, (line.baseline + desc).toFloat())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLineLeft(lineIndex: Int): Float = lineMetrics.getOrNull(lineIndex)?.left?.toFloat() ?: 0f
|
||||||
|
override fun getLineRight(lineIndex: Int): Float = lineMetrics.getOrNull(lineIndex)?.right?.toFloat() ?: 0f
|
||||||
|
override fun getLineTop(lineIndex: Int): Float =
|
||||||
|
lineMetrics.getOrNull(lineIndex)?.let { floor((it.baseline - it.ascent).toFloat()) } ?: 0f
|
||||||
|
override fun getLineBottom(lineIndex: Int): Float =
|
||||||
|
lineMetrics.getOrNull(lineIndex)?.let { floor((it.baseline + it.descent).toFloat()) } ?: 0f
|
||||||
|
override fun getLineBaseline(lineIndex: Int): Float = lineMetrics.getOrNull(lineIndex)?.baseline?.toFloat() ?: 0f
|
||||||
|
override fun getLineHeight(lineIndex: Int): Float = lineMetrics.getOrNull(lineIndex)?.height?.toFloat() ?: 0f
|
||||||
|
override fun getLineWidth(lineIndex: Int): Float = lineMetrics.getOrNull(lineIndex)?.width?.toFloat() ?: 0f
|
||||||
|
override fun getLineStart(lineIndex: Int): Int = lineMetrics.getOrNull(lineIndex)?.startIndex ?: 0
|
||||||
|
|
||||||
|
override fun getLineEnd(lineIndex: Int, visibleEnd: Boolean): Int {
|
||||||
|
val metrics = lineMetrics.getOrNull(lineIndex) ?: return 0
|
||||||
|
return if (visibleEnd) {
|
||||||
|
if (lineIndex > 0 && metrics.startIndex < lineMetrics[lineIndex - 1].endIndex) metrics.endIndex
|
||||||
|
else if (metrics.startIndex < text.length && text[metrics.startIndex] == '\n') metrics.startIndex
|
||||||
|
else metrics.endExcludingWhitespaces
|
||||||
|
} else metrics.endIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isLineEllipsized(lineIndex: Int): Boolean = false
|
||||||
|
|
||||||
|
override fun getLineForOffset(offset: Int): Int = when {
|
||||||
|
offset < 0 -> 0
|
||||||
|
offset > text.length -> lineCount - 1
|
||||||
|
else -> lineMetricsForOffset(offset)?.lineNumber ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLineForVerticalPosition(vertical: Float): Int =
|
||||||
|
lineMetricsForVerticalPosition(vertical)?.lineNumber ?: 0
|
||||||
|
|
||||||
|
override fun getHorizontalPosition(offset: Int, usePrimaryDirection: Boolean): Float {
|
||||||
|
val prevBox = getBoxBackwardByOffset(offset)
|
||||||
|
val nextBox = getBoxForwardByOffset(offset)
|
||||||
|
val isRtl = textDirection == ResolvedTextDirection.Rtl
|
||||||
|
return when {
|
||||||
|
prevBox == null && nextBox == null -> getAlignedStartingPosition(isRtl)
|
||||||
|
prevBox == null -> nextBox!!.cursorHorizontalPosition(true)
|
||||||
|
nextBox == null -> prevBox.cursorHorizontalPosition()
|
||||||
|
nextBox.isRtl == prevBox.isRtl -> nextBox.cursorHorizontalPosition(true)
|
||||||
|
!isRtl && !prevBox.isRtl -> nextBox.cursorHorizontalPosition(opposite = true)
|
||||||
|
isRtl && prevBox.isRtl -> nextBox.cursorHorizontalPosition(opposite = true)
|
||||||
|
usePrimaryDirection -> prevBox.cursorHorizontalPosition()
|
||||||
|
else -> nextBox.cursorHorizontalPosition(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAlignedStartingPosition(isRtl: Boolean): Float = when (style.textAlign) {
|
||||||
|
TextAlign.Left -> 0f
|
||||||
|
TextAlign.Right -> width
|
||||||
|
TextAlign.Center -> width / 2
|
||||||
|
TextAlign.Start -> if (isRtl) width else 0f
|
||||||
|
TextAlign.End -> if (isRtl) 0f else width
|
||||||
|
else -> 0f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getParagraphDirection(offset: Int): ResolvedTextDirection = textDirection
|
||||||
|
|
||||||
|
override fun getBidiRunDirection(offset: Int): ResolvedTextDirection =
|
||||||
|
if (getBoxForwardByOffset(offset)?.isRtl == true) ResolvedTextDirection.Rtl else ResolvedTextDirection.Ltr
|
||||||
|
|
||||||
|
override fun getOffsetForPosition(position: Offset): Int {
|
||||||
|
val glyphPosition = ops.glyphPositionAtCoordinate(position.x, position.y)
|
||||||
|
val expectedLine = lineMetricsForVerticalPosition(position.y) ?: return glyphPosition
|
||||||
|
if (position.x > expectedLine.left && position.x < expectedLine.right) return glyphPosition
|
||||||
|
val isNotEmptyLine = expectedLine.startIndex < expectedLine.endIndex
|
||||||
|
val rects = if (isNotEmptyLine) ops.getRectsForRange(
|
||||||
|
expectedLine.startIndex,
|
||||||
|
if (expectedLine.isHardBreak) expectedLine.endIndex else expectedLine.endIndex - 1,
|
||||||
|
useMaxHeight = false,
|
||||||
|
) else null
|
||||||
|
val leftX = rects?.firstOrNull()?.left ?: expectedLine.left.toFloat()
|
||||||
|
val rightX = rects?.lastOrNull()?.right ?: expectedLine.right.toFloat()
|
||||||
|
if (leftX == rightX) return glyphPosition
|
||||||
|
return when {
|
||||||
|
position.x <= leftX -> ops.glyphPositionAtCoordinate(leftX + 1f, position.y)
|
||||||
|
position.x >= rightX -> ops.glyphPositionAtCoordinate(rightX - 1f, position.y)
|
||||||
|
else -> glyphPosition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRangeForRect(rect: Rect, granularity: TextGranularity, inclusionStrategy: TextInclusionStrategy): TextRange =
|
||||||
|
TextRange.Zero
|
||||||
|
|
||||||
|
override fun getBoundingBox(offset: Int): Rect {
|
||||||
|
val box = getBoxForwardByOffset(offset) ?: getBoxBackwardByOffset(offset, text.length)
|
||||||
|
return box?.let { Rect(it.left, it.top, it.right, it.bottom) } ?: Rect(0f, 0f, 0f, 0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fillBoundingBoxes(range: TextRange, array: FloatArray, arrayStart: Int) {
|
||||||
|
// TODO(CMP-720): not supported by skiko paragraph yet.
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getWordBoundary(offset: Int): TextRange {
|
||||||
|
if (offset < text.length && text[offset].isWhitespace() || offset == text.length) {
|
||||||
|
return if (offset > 0 && !text[offset - 1].isWhitespace()) {
|
||||||
|
val b = ops.wordBoundary(offset - 1); TextRange(b[0], b[1])
|
||||||
|
} else TextRange(offset, offset)
|
||||||
|
}
|
||||||
|
val b = ops.wordBoundary(offset)
|
||||||
|
return TextRange(b[0], b[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun paint(canvas: Canvas, color: Color, shadow: Shadow?, textDecoration: TextDecoration?) {
|
||||||
|
ops.rebuildAndPaint(canvas, color, shadow ?: style.shadow, textDecoration ?: style.textDecoration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun paint(
|
||||||
|
canvas: Canvas, color: Color, shadow: Shadow?, textDecoration: TextDecoration?,
|
||||||
|
drawStyle: DrawStyle?, blendMode: BlendMode,
|
||||||
|
) {
|
||||||
|
ops.rebuildAndPaint(canvas, color, shadow ?: style.shadow, textDecoration ?: style.textDecoration)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun paint(
|
||||||
|
canvas: Canvas, brush: Brush, alpha: Float, shadow: Shadow?, textDecoration: TextDecoration?,
|
||||||
|
drawStyle: DrawStyle?, blendMode: BlendMode,
|
||||||
|
) {
|
||||||
|
val color = (brush as? SolidColor)?.value?.let {
|
||||||
|
if (alpha.isFinite()) it.copy(alpha = it.alpha * alpha) else it
|
||||||
|
} ?: style.color
|
||||||
|
ops.rebuildAndPaint(canvas, color, shadow ?: style.shadow, textDecoration ?: style.textDecoration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// Line-metric helpers
|
||||||
|
|
||||||
|
private val lineMetrics: List<LineMetricData> by lazy { ops.lineMetrics() }
|
||||||
|
|
||||||
|
private fun lineMetricsForOffset(offset: Int): LineMetricData? =
|
||||||
|
if (offset in 0..text.length) lineMetrics.binarySearchFirstMatchingOrLast { offset < it.endIncludingNewline } else null
|
||||||
|
|
||||||
|
private fun lineMetricsForVerticalPosition(vertical: Float): LineMetricData? =
|
||||||
|
lineMetrics.binarySearchFirstMatchingOrLast { vertical < it.baseline + it.descent }
|
||||||
|
|
||||||
|
private fun getBoxForwardByOffset(offset: Int): TextBoxData? {
|
||||||
|
if (offset !in 0..text.length) return null
|
||||||
|
var to = offset + 1
|
||||||
|
while (to <= text.length) {
|
||||||
|
ops.getRectsForRange(offset, to, useMaxHeight = false).firstOrNull()?.let { return it }
|
||||||
|
to += 1
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBoxBackwardByOffset(offset: Int, end: Int = offset): TextBoxData? {
|
||||||
|
if (offset !in 0..text.length) return null
|
||||||
|
var from = offset - 1
|
||||||
|
while (from >= 0) {
|
||||||
|
val box = ops.getRectsForRange(from, end, useMaxHeight = false).firstOrNull()
|
||||||
|
when {
|
||||||
|
box == null -> from -= 1
|
||||||
|
text[from] == '\n' -> {
|
||||||
|
val bottom = box.bottom + box.bottom - box.top
|
||||||
|
return TextBoxData(0f, box.bottom, 0f, bottom, box.isRtl)
|
||||||
|
}
|
||||||
|
else -> return box
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TextBoxData.cursorHorizontalPosition(opposite: Boolean = false): Float =
|
||||||
|
if (isRtl) (if (opposite) right else left) else (if (opposite) left else right)
|
||||||
|
|
||||||
|
private inline fun <T> List<T>.binarySearchFirstMatchingOrLast(crossinline predicate: (T) -> Boolean): T? {
|
||||||
|
if (isEmpty()) return null
|
||||||
|
val index = binarySearch { if (predicate(it)) 1 else -1 }
|
||||||
|
return this[(-index - 1).coerceAtMost(lastIndex)]
|
||||||
|
}
|
||||||
+1
-1
@@ -10,7 +10,7 @@ package androidx.compose.ui.text.font
|
|||||||
* that-isn't-a-FontListFontFamily" adapter. Upstream's skiko actual routes
|
* that-isn't-a-FontListFontFamily" adapter. Upstream's skiko actual routes
|
||||||
* `Default`, `SansSerif`, `Serif`, `Monospace`, `Cursive`, and `LoadedFontFamily`
|
* `Default`, `SansSerif`, `Serif`, `Monospace`, `Cursive`, and `LoadedFontFamily`
|
||||||
* through `SkiaFontLoader` to produce a real `SkTypeface`. Our text renderer
|
* through `SkiaFontLoader` to produce a real `SkTypeface`. Our text renderer
|
||||||
* (SdlParagraph / SkiaParagraph) reads `TextStyle.fontFamily` and
|
* (SkiaParagraph) reads `TextStyle.fontFamily` and
|
||||||
* [com.compose.sdl.text.NamedFont] directly to look the family up
|
* [com.compose.sdl.text.NamedFont] directly to look the family up
|
||||||
* against its registered font table, so the resolver's typeface is never
|
* against its registered font table, so the resolver's typeface is never
|
||||||
* consulted. A no-op adapter (returns an [TypefaceResult.Immutable] wrapping
|
* consulted. A no-op adapter (returns an [TypefaceResult.Immutable] wrapping
|
||||||
+1
-1
@@ -64,4 +64,4 @@ actual class Locale actual constructor(languageTag: String) {
|
|||||||
// The OS's most-preferred locale (SDL); en-US before SDL init or on a host that
|
// The OS's most-preferred locale (SDL); en-US before SDL init or on a host that
|
||||||
// reports none. Cheap: the tag list is cached after the first non-empty read.
|
// reports none. Cheap: the tag list is cached after the first non-empty read.
|
||||||
private fun systemCurrentLocale(): Locale =
|
private fun systemCurrentLocale(): Locale =
|
||||||
com.compose.sdl.text.systemPreferredLocaleTags().firstOrNull()?.let(::Locale) ?: Locale("en-US")
|
com.compose.sdl.res.preferredLocaleTagsProvider?.invoke()?.firstOrNull()?.let(::Locale) ?: Locale("en-US")
|
||||||
+1
-1
@@ -17,7 +17,7 @@ internal actual fun createPlatformLocaleDelegate(): PlatformLocaleDelegate =
|
|||||||
object : PlatformLocaleDelegate {
|
object : PlatformLocaleDelegate {
|
||||||
override val current: LocaleList
|
override val current: LocaleList
|
||||||
get() {
|
get() {
|
||||||
val tags = com.compose.sdl.text.systemPreferredLocaleTags()
|
val tags = com.compose.sdl.res.preferredLocaleTagsProvider?.invoke().orEmpty()
|
||||||
return if (tags.isEmpty()) LocaleList(listOf(Locale("en-US")))
|
return if (tags.isEmpty()) LocaleList(listOf(Locale("en-US")))
|
||||||
else LocaleList(tags.map(::Locale))
|
else LocaleList(tags.map(::Locale))
|
||||||
}
|
}
|
||||||
+88
@@ -0,0 +1,88 @@
|
|||||||
|
package com.compose.sdl.renderer.skia
|
||||||
|
|
||||||
|
import androidx.compose.ui.text.font.FontVariation as ComposeFontVariation
|
||||||
|
import com.compose.sdl.icons.IconFont
|
||||||
|
import com.compose.sdl.res.composeResourceReader
|
||||||
|
import org.jetbrains.skia.Data
|
||||||
|
import org.jetbrains.skia.FontMgr
|
||||||
|
import org.jetbrains.skia.FontVariation as SkiaFontVariation
|
||||||
|
import org.jetbrains.skia.Typeface
|
||||||
|
import org.jetbrains.skia.paragraph.FontCollection
|
||||||
|
import org.jetbrains.skia.paragraph.TypefaceFontProvider
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// MARK: SkiaFonts — font-resolution bridge for the skiko paragraph engine
|
||||||
|
// ==================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns the port's `(family-name, variable-axis)` font model into skiko
|
||||||
|
* [Typeface]s for the paragraph engine. Families come from
|
||||||
|
* [com.compose.sdl.text.NamedFont.projectFontName] (a bundled default, an
|
||||||
|
* [IconFont]-registered text/icon family, or `generic:*`); variable axes
|
||||||
|
* (Material Symbols `FILL`/`wght`/… and paragraph `FontWeight`→`wght`) are
|
||||||
|
* applied via [Typeface.makeClone].
|
||||||
|
*
|
||||||
|
* Every resolved typeface is registered in a [TypefaceFontProvider] under a
|
||||||
|
* UNIQUE alias, and [resolve] hands that alias back so the caller sets both
|
||||||
|
* `TextStyle.typeface` AND `TextStyle.fontFamilies = [alias]`. That matters:
|
||||||
|
* skiko's shaper maps codepoints→glyphs through `fontFamilies` (the
|
||||||
|
* FontCollection), so an icon font MUST be reachable by its alias — otherwise a
|
||||||
|
* bare `typeface` is ignored and private-use icon codepoints render as tofu.
|
||||||
|
* (Mirrors upstream FontCache: register alias + set fontFamilies + typeface.)
|
||||||
|
*/
|
||||||
|
internal object SkiaFonts {
|
||||||
|
private const val DEFAULT_ALIAS = "Noto Sans"
|
||||||
|
|
||||||
|
private val fontMgr = FontMgr.default
|
||||||
|
private val provider = TypefaceFontProvider()
|
||||||
|
|
||||||
|
/** Bundled default (NotoSans from data.kres); the fallback for every unresolved family. */
|
||||||
|
val defaultTypeface: Typeface? =
|
||||||
|
composeResourceReader?.invoke("font/NotoSans.ttf")
|
||||||
|
?.let { fontMgr.makeFromData(Data.makeFromBytes(it), 0) }
|
||||||
|
?.also { provider.registerTypeface(it, DEFAULT_ALIAS) }
|
||||||
|
|
||||||
|
/** Shared collection: the alias provider first (bundled + icon + varied fonts),
|
||||||
|
then the system FontMgr for glyph fallback (CJK/emoji where present). */
|
||||||
|
val fontCollection: FontCollection = FontCollection().apply {
|
||||||
|
setDefaultFontManager(fontMgr)
|
||||||
|
setAssetFontManager(provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
// family name (null = default) -> base typeface
|
||||||
|
private val baseCache = mutableMapOf<String?, Typeface?>()
|
||||||
|
// resolve key -> (typeface, registered alias)
|
||||||
|
private val resolveCache = mutableMapOf<String, Pair<Typeface?, String>>()
|
||||||
|
private val registered = mutableSetOf(DEFAULT_ALIAS)
|
||||||
|
|
||||||
|
private fun baseTypeface(family: String?): Typeface? =
|
||||||
|
baseCache.getOrPut(family) {
|
||||||
|
val bytes = family?.let { IconFont.bytesFor(it) }
|
||||||
|
if (bytes != null) fontMgr.makeFromData(Data.makeFromBytes(bytes), 0) else defaultTypeface
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun variationsKey(variations: List<ComposeFontVariation.Setting>): String =
|
||||||
|
variations.sortedBy { it.axisName }.joinToString(",") { "${it.axisName}=${it.toVariationValue(null)}" }
|
||||||
|
|
||||||
|
/** Resolve a family + optional variable-axis settings to a concrete typeface AND
|
||||||
|
a provider alias (register-on-first-use). Set `fontFamilies = [alias]` and
|
||||||
|
`typeface = first` on the skiko TextStyle. */
|
||||||
|
fun resolve(family: String?, variations: List<ComposeFontVariation.Setting>?): Pair<Typeface?, String> {
|
||||||
|
if (family == null && variations.isNullOrEmpty()) return defaultTypeface to DEFAULT_ALIAS
|
||||||
|
val varKey = variations?.takeUnless { it.isEmpty() }?.let { variationsKey(it) } ?: ""
|
||||||
|
val key = "${family ?: ""}#$varKey"
|
||||||
|
return resolveCache.getOrPut(key) {
|
||||||
|
val base = baseTypeface(family) ?: defaultTypeface
|
||||||
|
val typeface = if (varKey.isEmpty() || base == null) base
|
||||||
|
else runCatching {
|
||||||
|
base.makeClone(variations!!.map { SkiaFontVariation(it.axisName, it.toVariationValue(null)) }.toTypedArray())
|
||||||
|
}.getOrDefault(base)
|
||||||
|
val alias = if (family == null && varKey.isEmpty()) DEFAULT_ALIAS else "cdn-font:$key"
|
||||||
|
if (typeface != null && alias !in registered) {
|
||||||
|
provider.registerTypeface(typeface, alias)
|
||||||
|
registered += alias
|
||||||
|
}
|
||||||
|
typeface to alias
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+256
@@ -0,0 +1,256 @@
|
|||||||
|
package androidx.compose.ui.text
|
||||||
|
|
||||||
|
import androidx.compose.ui.graphics.Canvas
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shadow
|
||||||
|
import androidx.compose.ui.graphics.isSpecified
|
||||||
|
import androidx.compose.ui.graphics.skiaCanvas
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
import androidx.compose.ui.text.font.FontVariation
|
||||||
|
import androidx.compose.ui.text.style.ResolvedTextDirection
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
import androidx.compose.ui.unit.isSpecified
|
||||||
|
import com.compose.sdl.renderer.skia.SkiaFonts
|
||||||
|
import com.compose.sdl.text.projectFontName
|
||||||
|
import com.compose.sdl.text.projectFontVariations
|
||||||
|
import org.jetbrains.skia.Font as SkFont
|
||||||
|
import org.jetbrains.skia.FontStyle as SkFontStyle
|
||||||
|
import org.jetbrains.skia.paragraph.Alignment as SkAlignment
|
||||||
|
import org.jetbrains.skia.paragraph.DecorationLineStyle as SkDecorationLineStyle
|
||||||
|
import org.jetbrains.skia.paragraph.DecorationStyle as SkDecorationStyle
|
||||||
|
import org.jetbrains.skia.paragraph.Direction as SkDirection
|
||||||
|
import org.jetbrains.skia.paragraph.Paragraph as SkParagraph
|
||||||
|
import org.jetbrains.skia.paragraph.ParagraphBuilder as SkParagraphBuilder
|
||||||
|
import org.jetbrains.skia.paragraph.ParagraphStyle
|
||||||
|
import org.jetbrains.skia.paragraph.RectHeightMode
|
||||||
|
import org.jetbrains.skia.paragraph.RectWidthMode
|
||||||
|
import org.jetbrains.skia.paragraph.Shadow as SkShadow
|
||||||
|
import org.jetbrains.skia.paragraph.TextStyle as SkTextStyle
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// MARK: SkiaParagraphOps — skiko skparagraph impl behind the nativeMain seam
|
||||||
|
// ==================
|
||||||
|
//
|
||||||
|
// Lives in the skiko source set (skiko on classpath) and implements the
|
||||||
|
// skiko-free [NativeParagraphOps] the nativeMain [SkiaParagraph] drives. Builds
|
||||||
|
// a skiko `org.jetbrains.skia.paragraph` (HarfBuzz shaping + skunicode bidi +
|
||||||
|
// FontCollection fallback) from Compose style/spans and exposes plain-typed
|
||||||
|
// queries. Compose style/span -> skiko conversion is a reduced local version of
|
||||||
|
// upstream ParagraphBuilder.skiko.kt, keeping the port's family/variable-axis
|
||||||
|
// font model (SkiaFonts) so icons / monospace / custom fonts are unchanged.
|
||||||
|
|
||||||
|
private const val INTRINSIC_WIDTH = 100_000f
|
||||||
|
|
||||||
|
internal class SkiaParagraphOps(
|
||||||
|
private val text: String,
|
||||||
|
private val style: TextStyle,
|
||||||
|
private val widthConstraint: Float,
|
||||||
|
private val maxLines: Int,
|
||||||
|
private val density: Float,
|
||||||
|
private val spanStyles: List<AnnotatedString.Range<SpanStyle>>,
|
||||||
|
private val ellipsize: Boolean,
|
||||||
|
) : NativeParagraphOps {
|
||||||
|
|
||||||
|
private val fontPx: Float =
|
||||||
|
((if (style.fontSize.isSpecified) style.fontSize.value else 14f) * density).coerceAtLeast(1f)
|
||||||
|
private val baseFamily: String? = style.fontFamily.projectFontName()
|
||||||
|
// Icon families (Material Symbols) carry explicit axes on the family; ordinary
|
||||||
|
// text derives only the wght axis from the paragraph FontWeight.
|
||||||
|
private val baseVariations: List<FontVariation.Setting>? =
|
||||||
|
style.fontFamily.projectFontVariations()
|
||||||
|
?: style.fontWeight?.let { listOf(FontVariation.weight(it.weight)) }
|
||||||
|
private val baseTypeface = SkiaFonts.resolve(baseFamily, baseVariations).first
|
||||||
|
private val defaultFont = SkFont(baseTypeface ?: SkiaFonts.defaultTypeface, fontPx)
|
||||||
|
private val layoutWidth: Float =
|
||||||
|
if (widthConstraint.isFinite() && widthConstraint > 0f) widthConstraint else INTRINSIC_WIDTH
|
||||||
|
|
||||||
|
private var paragraph: SkParagraph = build(style.color, style.shadow, style.textDecoration)
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// NativeParagraphOps
|
||||||
|
|
||||||
|
override val height: Float get() = paragraph.height
|
||||||
|
override val lineNumber: Int get() = paragraph.lineNumber
|
||||||
|
override val minIntrinsicWidth: Float get() = paragraph.minIntrinsicWidth
|
||||||
|
override val maxIntrinsicWidth: Float get() = paragraph.maxIntrinsicWidth
|
||||||
|
override val didExceedMaxLines: Boolean get() = paragraph.didExceedMaxLines()
|
||||||
|
override val alphabeticBaseline: Float get() = paragraph.alphabeticBaseline
|
||||||
|
override val defaultAscentPx: Float get() = -defaultFont.metrics.ascent
|
||||||
|
override val defaultDescentPx: Float get() = defaultFont.metrics.descent
|
||||||
|
|
||||||
|
override fun lineMetrics(): List<LineMetricData> {
|
||||||
|
val metrics = if (text.isEmpty()) emptyList() else paragraph.lineMetrics.toList()
|
||||||
|
if (metrics.isEmpty()) {
|
||||||
|
val a = -defaultFont.metrics.ascent.toDouble()
|
||||||
|
val d = defaultFont.metrics.descent.toDouble()
|
||||||
|
return listOf(
|
||||||
|
LineMetricData(0, 0, 0, 0, true, a, d, paragraph.alphabeticBaseline.toDouble().takeIf { it > 0 } ?: a,
|
||||||
|
0.0, 0.0, 0.0, a + d, 0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return metrics.map {
|
||||||
|
LineMetricData(
|
||||||
|
startIndex = it.startIndex, endIndex = it.endIndex,
|
||||||
|
endExcludingWhitespaces = it.endExcludingWhitespaces, endIncludingNewline = it.endIncludingNewline,
|
||||||
|
isHardBreak = it.isHardBreak, ascent = it.ascent, descent = it.descent, baseline = it.baseline,
|
||||||
|
left = it.left, right = it.left + it.width, width = it.width, height = it.height,
|
||||||
|
lineNumber = it.lineNumber,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun placeholderRects(): List<RectData?> =
|
||||||
|
paragraph.rectsForPlaceholders.map { RectData(it.rect.left, it.rect.top, it.rect.right, it.rect.bottom) }
|
||||||
|
|
||||||
|
override fun getRectsForRange(start: Int, end: Int, useMaxHeight: Boolean): List<TextBoxData> =
|
||||||
|
paragraph.getRectsForRange(
|
||||||
|
start, end,
|
||||||
|
if (useMaxHeight) RectHeightMode.MAX else RectHeightMode.STRUT,
|
||||||
|
RectWidthMode.TIGHT,
|
||||||
|
).map { TextBoxData(it.rect.left, it.rect.top, it.rect.right, it.rect.bottom, it.direction == SkDirection.RTL) }
|
||||||
|
|
||||||
|
override fun glyphPositionAtCoordinate(x: Float, y: Float): Int =
|
||||||
|
paragraph.getGlyphPositionAtCoordinate(x, y).position
|
||||||
|
|
||||||
|
override fun wordBoundary(offset: Int): IntArray =
|
||||||
|
paragraph.getWordBoundary(offset).let { intArrayOf(it.start, it.end) }
|
||||||
|
|
||||||
|
override fun rebuildAndPaint(canvas: Canvas, color: Color, shadow: Shadow?, decoration: TextDecoration?) {
|
||||||
|
paragraph = build(color, shadow, decoration)
|
||||||
|
paragraph.paint(canvas.skiaCanvas, 0f, 0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============
|
||||||
|
// Compose style/span -> skiko build
|
||||||
|
|
||||||
|
private fun build(color: Color, shadow: Shadow?, decoration: TextDecoration?): SkParagraph {
|
||||||
|
val baseStyle = makeTextStyle(baseFamily, baseVariations, fontPx, color, style.fontStyle, decoration, shadow)
|
||||||
|
val pStyle = ParagraphStyle().apply {
|
||||||
|
alignment = style.textAlign.toSkAlignment()
|
||||||
|
direction = if (style.textDirection == androidx.compose.ui.text.style.TextDirection.Rtl) SkDirection.RTL else SkDirection.LTR
|
||||||
|
textStyle = baseStyle
|
||||||
|
if (maxLines != Int.MAX_VALUE) {
|
||||||
|
maxLinesCount = maxLines
|
||||||
|
ellipsis = if (ellipsize) "…" else ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val pb = SkParagraphBuilder(pStyle, SkiaFonts.fontCollection)
|
||||||
|
if (spanStyles.isEmpty()) {
|
||||||
|
pb.pushStyle(baseStyle)
|
||||||
|
pb.addText(text)
|
||||||
|
pb.popStyle()
|
||||||
|
} else {
|
||||||
|
appendWithSpans(pb, color, shadow, decoration)
|
||||||
|
}
|
||||||
|
return pb.build().also { it.layout(layoutWidth) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun appendWithSpans(pb: SkParagraphBuilder, color: Color, shadow: Shadow?, decoration: TextDecoration?) {
|
||||||
|
val points = buildList {
|
||||||
|
add(0); add(text.length)
|
||||||
|
spanStyles.forEach { add(it.start.coerceIn(0, text.length)); add(it.end.coerceIn(0, text.length)) }
|
||||||
|
}.distinct().sorted()
|
||||||
|
for (i in 0 until points.size - 1) {
|
||||||
|
val segStart = points[i]
|
||||||
|
val segEnd = points[i + 1]
|
||||||
|
if (segStart >= segEnd) continue
|
||||||
|
val active = spanStyles.filter { it.start <= segStart && it.end >= segEnd }
|
||||||
|
pb.pushStyle(segmentStyle(color, shadow, decoration, active))
|
||||||
|
pb.addText(text.substring(segStart, segEnd))
|
||||||
|
pb.popStyle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun segmentStyle(
|
||||||
|
color: Color, shadow: Shadow?, decoration: TextDecoration?,
|
||||||
|
active: List<AnnotatedString.Range<SpanStyle>>,
|
||||||
|
): SkTextStyle {
|
||||||
|
var family = baseFamily
|
||||||
|
var variations = baseVariations
|
||||||
|
var size = fontPx
|
||||||
|
var segColor = color
|
||||||
|
var fontStyle = style.fontStyle
|
||||||
|
var deco = decoration
|
||||||
|
active.forEach { range ->
|
||||||
|
val sp = range.item
|
||||||
|
if (sp.color.isSpecified) segColor = sp.color
|
||||||
|
sp.fontWeight?.let { variations = listOf(FontVariation.weight(it.weight)) }
|
||||||
|
if (sp.fontSize.isSpecified) size = (sp.fontSize.value * density).coerceAtLeast(1f)
|
||||||
|
sp.fontStyle?.let { fontStyle = it }
|
||||||
|
sp.textDecoration?.let { deco = it }
|
||||||
|
sp.fontFamily.projectFontName()?.let { family = it }
|
||||||
|
sp.fontFamily.projectFontVariations()?.let { variations = it }
|
||||||
|
}
|
||||||
|
return makeTextStyle(family, variations, size, segColor, fontStyle, deco, shadow)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun makeTextStyle(
|
||||||
|
family: String?, variations: List<FontVariation.Setting>?, sizePx: Float,
|
||||||
|
color: Color, fontStyle: FontStyle?, decoration: TextDecoration?, shadow: Shadow?,
|
||||||
|
): SkTextStyle {
|
||||||
|
val ts = SkTextStyle()
|
||||||
|
val argb = (if (color.isSpecified) color else Color.Black).toArgb()
|
||||||
|
ts.color = argb
|
||||||
|
ts.fontSize = sizePx
|
||||||
|
// Register + resolve to a provider alias so skiko's shaper maps codepoints
|
||||||
|
// through the exact typeface (icon fonts would otherwise fall back to
|
||||||
|
// Noto Sans and render private-use glyphs as tofu).
|
||||||
|
val (tf, alias) = SkiaFonts.resolve(family, variations)
|
||||||
|
tf?.let { ts.typeface = it }
|
||||||
|
ts.fontFamilies = arrayOf(alias)
|
||||||
|
if (fontStyle == FontStyle.Italic) ts.fontStyle = SkFontStyle.ITALIC
|
||||||
|
decoration?.takeUnless { it == TextDecoration.None }?.let {
|
||||||
|
ts.decorationStyle = SkDecorationStyle(
|
||||||
|
it.contains(TextDecoration.Underline), false, it.contains(TextDecoration.LineThrough),
|
||||||
|
false, argb, SkDecorationLineStyle.SOLID, 1f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val letter = style.letterSpacing
|
||||||
|
if (letter.isSpecified) {
|
||||||
|
ts.letterSpacing = if (letter.isEm) sizePx * letter.value else letter.value * density
|
||||||
|
}
|
||||||
|
shadow?.takeUnless { it == Shadow.None }?.let {
|
||||||
|
ts.addShadow(SkShadow(it.color.toArgb(), it.offset.x, it.offset.y, it.blurRadius.toDouble()))
|
||||||
|
}
|
||||||
|
return ts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun TextAlign.toSkAlignment(): SkAlignment = when (this) {
|
||||||
|
TextAlign.Left -> SkAlignment.LEFT
|
||||||
|
TextAlign.Right -> SkAlignment.RIGHT
|
||||||
|
TextAlign.Center -> SkAlignment.CENTER
|
||||||
|
TextAlign.Justify -> SkAlignment.JUSTIFY
|
||||||
|
TextAlign.End -> SkAlignment.END
|
||||||
|
else -> SkAlignment.START
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// MARK: factory actuals (skikoRendererMain)
|
||||||
|
// ==================
|
||||||
|
|
||||||
|
/** Actual for the nativeMain `expect fun buildParagraphOps`; runs in the skiko
|
||||||
|
* source set (compiled into both the official-skiko and fork siblings). */
|
||||||
|
internal actual fun buildParagraphOps(
|
||||||
|
text: String,
|
||||||
|
style: TextStyle,
|
||||||
|
width: Float,
|
||||||
|
maxLines: Int,
|
||||||
|
ellipsize: Boolean,
|
||||||
|
density: Float,
|
||||||
|
spanStyles: List<AnnotatedString.Range<SpanStyle>>,
|
||||||
|
): NativeParagraphOps = SkiaParagraphOps(text, style, width, maxLines, density, spanStyles, ellipsize)
|
||||||
|
|
||||||
|
/** Actual for `expect fun paragraphIntrinsicWidths` — [min, max] from an
|
||||||
|
* unbounded layout. */
|
||||||
|
internal actual fun paragraphIntrinsicWidths(
|
||||||
|
text: String,
|
||||||
|
style: TextStyle,
|
||||||
|
density: Float,
|
||||||
|
spanStyles: List<AnnotatedString.Range<SpanStyle>>,
|
||||||
|
): FloatArray {
|
||||||
|
val ops = SkiaParagraphOps(text, style, Float.POSITIVE_INFINITY, Int.MAX_VALUE, density, spanStyles, false)
|
||||||
|
return floatArrayOf(ops.minIntrinsicWidth, ops.maxIntrinsicWidth)
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:ui-tooling-preview>
|
// Library unique name: <com.bitsycore.compose.ui:ui-tooling-preview>
|
||||||
open annotation class androidx.compose.ui.tooling.preview/AndroidUiMode : kotlin/Annotation { // androidx.compose.ui.tooling.preview/AndroidUiMode|null[0]
|
open annotation class androidx.compose.ui.tooling.preview/AndroidUiMode : kotlin/Annotation { // androidx.compose.ui.tooling.preview/AndroidUiMode|null[0]
|
||||||
constructor <init>() // androidx.compose.ui.tooling.preview/AndroidUiMode.<init>|<init>(){}[0]
|
constructor <init>() // androidx.compose.ui.tooling.preview/AndroidUiMode.<init>|<init>(){}[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:ui-unit>
|
// Library unique name: <com.bitsycore.compose.ui:ui-unit>
|
||||||
open annotation class androidx.compose.ui.unit/ExperimentalUnitApi : kotlin/Annotation { // androidx.compose.ui.unit/ExperimentalUnitApi|null[0]
|
open annotation class androidx.compose.ui.unit/ExperimentalUnitApi : kotlin/Annotation { // androidx.compose.ui.unit/ExperimentalUnitApi|null[0]
|
||||||
constructor <init>() // androidx.compose.ui.unit/ExperimentalUnitApi.<init>|<init>(){}[0]
|
constructor <init>() // androidx.compose.ui.unit/ExperimentalUnitApi.<init>|<init>(){}[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// - Show manifest properties: true
|
// - Show manifest properties: true
|
||||||
// - Show declarations: true
|
// - Show declarations: true
|
||||||
|
|
||||||
// Library unique name: <com.bitsycore.compose.sdl:ui-util>
|
// Library unique name: <com.bitsycore.compose.ui:ui-util>
|
||||||
open annotation class androidx.compose.ui/ExperimentalComposeUiApi : kotlin/Annotation { // androidx.compose.ui/ExperimentalComposeUiApi|null[0]
|
open annotation class androidx.compose.ui/ExperimentalComposeUiApi : kotlin/Annotation { // androidx.compose.ui/ExperimentalComposeUiApi|null[0]
|
||||||
constructor <init>() // androidx.compose.ui/ExperimentalComposeUiApi.<init>|<init>(){}[0]
|
constructor <init>() // androidx.compose.ui/ExperimentalComposeUiApi.<init>|<init>(){}[0]
|
||||||
}
|
}
|
||||||
|
|||||||
+93
-5106
File diff suppressed because it is too large
Load Diff
+51
-176
@@ -1,25 +1,23 @@
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||||
|
|
||||||
// :core — the renderer-agnostic Compose base + both renderer pipelines.
|
// :ui — the Compose core + the Skia rendering pipeline + the SDL bridges
|
||||||
|
// (all native targets). androidx.compose.ui.graphics.* / .text.* were split out
|
||||||
|
// to :ui-graphics / :ui-text, and the sdl3 cinterop now lives in :sdl-core;
|
||||||
|
// :ui depends on both.
|
||||||
//
|
//
|
||||||
// Source-set hierarchy:
|
// Source-set hierarchy:
|
||||||
// commonMain
|
// commonMain
|
||||||
// └── nativeMain (vendored .native.kt + project SDL3 wrappers)
|
// └── nativeMain (vendored .native.kt + project SDL3 wrappers)
|
||||||
// ├── skikoRendererMain (Skia drawing pipeline; Skiko on classpath)
|
// ├── skikoRendererMain (Skia pipeline; OFFICIAL Skiko — macOS/Linux)
|
||||||
// │ ├── skikoRendererMacosMain (macOS-only Skia actuals — Metal bridge)
|
// │ ├── skikoRendererMacosMain (macOS Metal bridge) → macosArm64
|
||||||
// │ └── skikoRendererLinuxMain (Linux-only Skia actuals — OpenGL)
|
// │ └── skikoRendererLinuxMain (Linux OpenGL) → linuxX64/Arm64
|
||||||
// │ attached: macosArm64Main / linuxX64Main / linuxArm64Main
|
// └── skikoRendererMingwSharedMain (Skia pipeline; the bitsycore skiko FORK —
|
||||||
// │ only when Skia path is active (default on macOS/Linux)
|
// └── skikoRendererMingwMain mingwX64 has no official Skiko klib) → mingwX64
|
||||||
// └── sdlRendererMain (SDL3 drawing pipeline + TTF/IMG/FreeType)
|
|
||||||
// ├── sdlRendererMacosMain (macOS-only SDL3 driver hint)
|
|
||||||
// ├── sdlRendererLinuxMain (Linux-only SDL3 driver hint)
|
|
||||||
// └── sdlRendererMingwMain (mingwX64-only SDL3 driver hint)
|
|
||||||
// attached: mingwX64Main always; macOS/Linux when -Prenderer=sdl3
|
|
||||||
//
|
//
|
||||||
// `-Prenderer=sdl3` flips macOS/Linux targets onto the SDL3 path. The
|
// SDL3 stays as the windowing / input / platform layer (the single `sdl3`
|
||||||
// `:renderer-skia` and `:renderer-sdl3` sibling modules are gone — their code
|
// cinterop, now in :sdl-core). Every target renders through Skia.
|
||||||
// lives here. `:window` depends only on `:core`; `createRenderBackend` comes
|
// `:desktop-native-window` depends only on this module and calls createRenderBackend() /
|
||||||
// from whichever of the renderer source sets is active for the target.
|
// rendererPreferredGpuMode(), which resolve to the Skia actuals.
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.kotlin.multiplatform)
|
alias(libs.plugins.kotlin.multiplatform)
|
||||||
@@ -27,39 +25,6 @@ plugins {
|
|||||||
alias(libs.plugins.compose.multiplatform)
|
alias(libs.plugins.compose.multiplatform)
|
||||||
}
|
}
|
||||||
|
|
||||||
// -Prenderer=sdl3 flips macOS/Linux targets onto sdlRendererMain (Skiko-free build).
|
|
||||||
val useSdl3Everywhere = (findProperty("renderer") as? String) == "sdl3"
|
|
||||||
|
|
||||||
// All targets pull headers from the in-repo static build tree at <repo>/libs,
|
|
||||||
// populated by scripts/build-sdl/build-all.py (optionally one library at a time).
|
|
||||||
// Same paths on macOS / Linux / Windows — the .def files are pathless and
|
|
||||||
// rely on these -I injections. Cross-target cinterop indexing under
|
|
||||||
// kotlin.mpp.enableCInteropCommonization is fine: clang silently ignores -I
|
|
||||||
// dirs that don't exist, and the libs/ tree only has headers for the host
|
|
||||||
// that built them (they aren't cross-installed).
|
|
||||||
val vLibs = "${rootDir.invariantSeparatorsPath}/libs"
|
|
||||||
val vHostSdlInclude: String = "$vLibs/SDL3/include"
|
|
||||||
val vHostFtInclude: String = "$vLibs/FreeType/include/freetype2"
|
|
||||||
val vHostTtfInclude: String = "$vLibs/SDL3_ttf/include"
|
|
||||||
val vHostImageInclude: String = "$vLibs/SDL3_image/include"
|
|
||||||
|
|
||||||
// Static-archive dirs for cinterop's `staticLibraries = …` directive. Same
|
|
||||||
// paths on every host — cinterop only reads the one for the target being
|
|
||||||
// built, so cross-target indexing on a host that hasn't built libs/ for a
|
|
||||||
// foreign target is fine (cinterop skips the archive lookup).
|
|
||||||
val vSdlLibDir: String = "$vLibs/SDL3/lib"
|
|
||||||
val vFtLibDir: String = "$vLibs/FreeType/lib"
|
|
||||||
val vTtfLibDir: String = "$vLibs/SDL3_ttf/lib"
|
|
||||||
val vImageLibDir: String = "$vLibs/SDL3_image/lib"
|
|
||||||
|
|
||||||
// Renderer assignment per target. mingwX64 is always SDL3; macOS / Linux
|
|
||||||
// default to Skia, switch to SDL3 under -Prenderer=sdl3.
|
|
||||||
fun isSkiaTarget(targetName: String): Boolean = when (targetName) {
|
|
||||||
"mingwX64" -> false
|
|
||||||
"macosArm64", "linuxX64", "linuxArm64" -> !useSdl3Everywhere
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip mingwX64 on non-Windows hosts; see root build.gradle.kts.
|
// Skip mingwX64 on non-Windows hosts; see root build.gradle.kts.
|
||||||
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
|
val vHostSupportsMingw = rootProject.extra["vHostSupportsMingw"] as Boolean
|
||||||
|
|
||||||
@@ -71,88 +36,16 @@ kotlin {
|
|||||||
|
|
||||||
applyDefaultHierarchyTemplate()
|
applyDefaultHierarchyTemplate()
|
||||||
|
|
||||||
targets.withType<KotlinNativeTarget>().all {
|
|
||||||
val vTargetName = name
|
|
||||||
// Whether this target compiles the SDL3 drawing pipeline. mingwX64 is
|
|
||||||
// always SDL3 (no Skiko on Windows); macOS / Linux switch to it only
|
|
||||||
// under -Prenderer=sdl3. SDL3 on macOS/Linux is a DEBUG target we don't
|
|
||||||
// ship in releases, so the default release build (Skia) skips the
|
|
||||||
// sdl3_ttf / sdl3_image / freetype cinterops entirely — one fewer set
|
|
||||||
// of system headers to install in CI, and a smaller klib footprint.
|
|
||||||
val vSdlRenderer = vTargetName == "mingwX64" ||
|
|
||||||
((vTargetName == "macosArm64" || vTargetName == "linuxX64" || vTargetName == "linuxArm64") && useSdl3Everywhere)
|
|
||||||
|
|
||||||
// Path to the sdl3 cinterop output klib for this target — used to
|
|
||||||
// wire `depends = sdl3` in sdl3_ttf / sdl3_image / freetype below.
|
|
||||||
// Gradle does NOT auto-add the sdl3 klib to the dependent cinterop
|
|
||||||
// tasks' -library list, so cinterop generates its own SDL_Surface /
|
|
||||||
// SDL_Color inside sdl3_image / sdl3_ttf instead of reusing the sdl3
|
|
||||||
// ones. Passing the path explicitly via extraOpts forces the link.
|
|
||||||
val vSdl3Klib = layout.buildDirectory.dir(
|
|
||||||
"classes/kotlin/$vTargetName/main/cinterop/ui-cinterop-sdl3"
|
|
||||||
).get().asFile.absolutePath
|
|
||||||
|
|
||||||
compilations["main"].cinterops {
|
|
||||||
// sdl3 stays for every target — :window uses the SDL3 main-loop
|
|
||||||
// types (SDL_Window / SDL_Event / SDL_GetBasePath / …) regardless
|
|
||||||
// of the renderer choice.
|
|
||||||
create("sdl3") {
|
|
||||||
defFile(project.file("src/nativeInterop/cinterop/sdl3.def"))
|
|
||||||
packageName("sdl3")
|
|
||||||
extraOpts("-compiler-options", "-I$vHostSdlInclude")
|
|
||||||
extraOpts("-libraryPath", vSdlLibDir)
|
|
||||||
}
|
|
||||||
if (vSdlRenderer) {
|
|
||||||
create("sdl3_ttf") {
|
|
||||||
defFile(project.file("src/nativeInterop/cinterop/sdl3_ttf.def"))
|
|
||||||
packageName("sdl3_ttf")
|
|
||||||
extraOpts("-library", vSdl3Klib)
|
|
||||||
extraOpts("-compiler-options", "-I$vHostSdlInclude")
|
|
||||||
extraOpts("-compiler-options", "-I$vHostTtfInclude")
|
|
||||||
extraOpts("-libraryPath", vTtfLibDir)
|
|
||||||
}
|
|
||||||
create("sdl3_image") {
|
|
||||||
defFile(project.file("src/nativeInterop/cinterop/sdl3_image.def"))
|
|
||||||
packageName("sdl3_image")
|
|
||||||
extraOpts("-library", vSdl3Klib)
|
|
||||||
extraOpts("-compiler-options", "-I$vHostSdlInclude")
|
|
||||||
extraOpts("-compiler-options", "-I$vHostImageInclude")
|
|
||||||
extraOpts("-libraryPath", vImageLibDir)
|
|
||||||
}
|
|
||||||
// FreeType powers variable-font axis rendering (FILL / wght /
|
|
||||||
// GRAD / opsz) on Material Symbols icons in the SDL3 path.
|
|
||||||
create("freetype") {
|
|
||||||
defFile(project.file("src/nativeInterop/cinterop/freetype.def"))
|
|
||||||
packageName("freetype")
|
|
||||||
extraOpts("-compiler-options", "-I$vHostFtInclude")
|
|
||||||
extraOpts("-libraryPath", vFtLibDir)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wire the task graph so cinteropSdl3_ttf/_image*Target run AFTER
|
|
||||||
// cinteropSdl3*Target — the -library reference above only points at
|
|
||||||
// the klib path; without a task dependency Gradle might run the
|
|
||||||
// dependent cinterop first and the path wouldn't exist yet.
|
|
||||||
if (vSdlRenderer) {
|
|
||||||
val vT = vTargetName.replaceFirstChar { it.uppercase() }
|
|
||||||
tasks.matching { it.name == "cinteropSdl3_ttf$vT" || it.name == "cinteropSdl3_image$vT" }
|
|
||||||
.configureEach { dependsOn("cinteropSdl3$vT") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
// Files vendored VERBATIM from upstream Compose by
|
// Files vendored VERBATIM from upstream Compose by
|
||||||
// scripts/compose-fork/sync.sh. Kept in their own folder so it's
|
// scripts/compose-fork/sync.sh. Never hand-edit; re-run sync.
|
||||||
// obvious they are generated — never hand-edit; re-run sync instead.
|
|
||||||
kotlin.srcDir("src/vendor/common/kotlin")
|
kotlin.srcDir("src/vendor/common/kotlin")
|
||||||
dependencies {
|
dependencies {
|
||||||
// Split-out lower ui artifacts (CMP layout). The graphics/text/framework
|
|
||||||
// vendored code + renderers here import ui.util / ui.geometry / ui.unit /
|
|
||||||
// ui.backhandler from these modules.
|
|
||||||
api(project(":ui-util"))
|
api(project(":ui-util"))
|
||||||
api(project(":ui-geometry"))
|
api(project(":ui-geometry"))
|
||||||
|
api(project(":ui-graphics"))
|
||||||
|
api(project(":ui-text"))
|
||||||
api(project(":ui-unit"))
|
api(project(":ui-unit"))
|
||||||
api(project(":ui-backhandler"))
|
api(project(":ui-backhandler"))
|
||||||
api("org.jetbrains.compose.runtime:runtime:${libs.versions.composeRuntime.get()}")
|
api("org.jetbrains.compose.runtime:runtime:${libs.versions.composeRuntime.get()}")
|
||||||
@@ -163,11 +56,6 @@ kotlin {
|
|||||||
api("androidx.savedstate:savedstate-compose:1.5.0")
|
api("androidx.savedstate:savedstate-compose:1.5.0")
|
||||||
api("androidx.lifecycle:lifecycle-viewmodel-compose:2.11.0")
|
api("androidx.lifecycle:lifecycle-viewmodel-compose:2.11.0")
|
||||||
api("androidx.lifecycle:lifecycle-runtime-compose:2.11.0")
|
api("androidx.lifecycle:lifecycle-runtime-compose:2.11.0")
|
||||||
// Navigation 3 runtime — the backstack / NavEntry / scene model. Published for
|
|
||||||
// all our K/N targets (mingwX64 + macos/linux) and pulls only runtime / lifecycle /
|
|
||||||
// savedstate / collection / serialization (no androidx.compose.ui). The NavDisplay
|
|
||||||
// UI (androidx.navigation3:navigation3-ui) has NO K/N desktop artifact, so it's not
|
|
||||||
// here — a display is hand-rolled on the project's own ui/animation.
|
|
||||||
api("androidx.navigation3:navigation3-runtime:1.1.4")
|
api("androidx.navigation3:navigation3-runtime:1.1.4")
|
||||||
api("androidx.lifecycle:lifecycle-viewmodel-navigation3:2.11.0")
|
api("androidx.lifecycle:lifecycle-viewmodel-navigation3:2.11.0")
|
||||||
implementation(libs.kotlinx.coroutines.core)
|
implementation(libs.kotlinx.coroutines.core)
|
||||||
@@ -178,74 +66,61 @@ kotlin {
|
|||||||
// Vendored platform `actual`s + project SDL3 wrappers / Compose native code.
|
// Vendored platform `actual`s + project SDL3 wrappers / Compose native code.
|
||||||
nativeMain {
|
nativeMain {
|
||||||
kotlin.srcDir("src/vendor/native/kotlin")
|
kotlin.srcDir("src/vendor/native/kotlin")
|
||||||
|
dependencies {
|
||||||
|
// The sdl3 cinterop now lives in :sdl-core; expose it (api) so :ui's
|
||||||
|
// SDL platform + renderer code — and :desktop-native-window downstream — see sdl3.*
|
||||||
|
// and inherit SDL3's static-lib + linker-opt propagation.
|
||||||
|
api(project(":sdl-core"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============
|
// ============
|
||||||
// Renderer roots. Each is a child of nativeMain; per-platform
|
// Skia renderer. macOS/Linux use the OFFICIAL Skiko; mingwX64 uses the
|
||||||
// intermediates below attach to one of these. Only the renderer
|
// bitsycore skiko FORK (no official mingw klib), in a separate tree so
|
||||||
// source sets that will actually be attached are created, so Gradle
|
// only mingw gets the fork coord.
|
||||||
// doesn't warn about unused source sets when the build is asymmetric
|
|
||||||
// (e.g. -Prenderer=sdl3 wouldn't use any skikoRenderer* sets).
|
|
||||||
|
|
||||||
val sdlRendererMain = create("sdlRendererMain") {
|
val skikoRendererMain = create("skikoRendererMain") {
|
||||||
dependsOn(nativeMain.get())
|
dependsOn(nativeMain.get())
|
||||||
// src/vendor/sdlRenderer/kotlin holds files vendored verbatim
|
// src/vendor/skikoRenderer/kotlin — upstream `skikoMain` files
|
||||||
// from upstream's skikoMain that are SDL3-friendly (no Skia refs)
|
// (Skia-tied actuals like BlendMode.skiko.kt) vendored verbatim.
|
||||||
// or whose SDL3 actual we provide; same "never hand-edit" rule
|
kotlin.srcDir("src/vendor/skikoRenderer/kotlin")
|
||||||
// as the other vendor srcDirs.
|
dependencies {
|
||||||
kotlin.srcDir("src/vendor/sdlRenderer/kotlin")
|
implementation(libs.skiko)
|
||||||
// SDL3_ttf / SDL3_image / freetype cinterop bindings come from
|
}
|
||||||
// the per-target cinterop block above; no separate Gradle deps.
|
|
||||||
}
|
}
|
||||||
// mingwX64 is SDL3 always — attach its intermediate only when the
|
val skikoRendererMacosMain = create("skikoRendererMacosMain") { dependsOn(skikoRendererMain) }
|
||||||
// target itself was declared (host is Windows). Non-Windows hosts skip
|
val skikoRendererLinuxMain = create("skikoRendererLinuxMain") { dependsOn(skikoRendererMain) }
|
||||||
// both the target and its source-set wiring.
|
get("macosArm64Main").dependsOn(skikoRendererMacosMain)
|
||||||
|
get("linuxX64Main").dependsOn(skikoRendererLinuxMain)
|
||||||
|
get("linuxArm64Main").dependsOn(skikoRendererLinuxMain)
|
||||||
|
|
||||||
if (vHostSupportsMingw) {
|
if (vHostSupportsMingw) {
|
||||||
val sdlRendererMingwMain = create("sdlRendererMingwMain") { dependsOn(sdlRendererMain) }
|
// Route 1a: mingwX64 Skia leg on the fork. Two levels so the shared
|
||||||
mingwX64Main.get().dependsOn(sdlRendererMingwMain)
|
// PlatformGpu expect (Shared) has its mingw actual (Mingw), mirroring
|
||||||
}
|
// the macos/linux split. Depends on the fork ROOT coord (not the
|
||||||
|
// platform artifact) so KMP variant-resolution exposes api-elements.
|
||||||
val macosArm64Main = get("macosArm64Main")
|
val skikoRendererMingwSharedMain = create("skikoRendererMingwSharedMain") {
|
||||||
val linuxX64Main = get("linuxX64Main")
|
|
||||||
val linuxArm64Main = get("linuxArm64Main")
|
|
||||||
|
|
||||||
if (useSdl3Everywhere) {
|
|
||||||
// macOS / Linux flip to SDL3 — create the sdl intermediates.
|
|
||||||
val sdlRendererMacosMain = create("sdlRendererMacosMain") { dependsOn(sdlRendererMain) }
|
|
||||||
val sdlRendererLinuxMain = create("sdlRendererLinuxMain") { dependsOn(sdlRendererMain) }
|
|
||||||
macosArm64Main.dependsOn(sdlRendererMacosMain)
|
|
||||||
linuxX64Main.dependsOn(sdlRendererLinuxMain)
|
|
||||||
linuxArm64Main.dependsOn(sdlRendererLinuxMain)
|
|
||||||
} else {
|
|
||||||
// Default: macOS / Linux use Skia. Create the skiko tree.
|
|
||||||
val skikoRendererMain = create("skikoRendererMain") {
|
|
||||||
dependsOn(nativeMain.get())
|
dependsOn(nativeMain.get())
|
||||||
// src/vendor/skikoRenderer/kotlin holds upstream's `skikoMain`
|
kotlin.srcDir("src/skikoRendererMain/kotlin")
|
||||||
// files (Skia-tied actuals / helpers like BlendMode.skiko.kt)
|
|
||||||
// vendored verbatim. Same "never hand-edit" rule.
|
|
||||||
kotlin.srcDir("src/vendor/skikoRenderer/kotlin")
|
kotlin.srcDir("src/vendor/skikoRenderer/kotlin")
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.skiko)
|
// Published by the fork's CI to GitHub Packages; version
|
||||||
|
// overridable via -PskikoMingwVersion=.
|
||||||
|
implementation("com.bitsycore.skiko:skiko:${providers.gradleProperty("skikoMingwVersion").getOrElse("0.150.1-mingw.1")}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val skikoRendererMacosMain = create("skikoRendererMacosMain") { dependsOn(skikoRendererMain) }
|
val skikoRendererMingwMain = create("skikoRendererMingwMain") { dependsOn(skikoRendererMingwSharedMain) }
|
||||||
val skikoRendererLinuxMain = create("skikoRendererLinuxMain") { dependsOn(skikoRendererMain) }
|
get("mingwX64Main").dependsOn(skikoRendererMingwMain)
|
||||||
macosArm64Main.dependsOn(skikoRendererMacosMain)
|
|
||||||
linuxX64Main.dependsOn(skikoRendererLinuxMain)
|
|
||||||
linuxArm64Main.dependsOn(skikoRendererLinuxMain)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compilerOptions {
|
compilerOptions {
|
||||||
freeCompilerArgs.addAll(
|
freeCompilerArgs.addAll(
|
||||||
"-Xcollection-literals",
|
"-Xcollection-literals",
|
||||||
// Silence the expect/actual-classes Beta warning that vendored files
|
// Silence the expect/actual-classes Beta warning vendored files trip.
|
||||||
// (e.g. ImageBitmap / Paint / Canvas / Path / ClipEntry) tripped. See
|
|
||||||
// https://youtrack.jetbrains.com/issue/KT-61573.
|
// https://youtrack.jetbrains.com/issue/KT-61573.
|
||||||
"-Xexpect-actual-classes",
|
"-Xexpect-actual-classes",
|
||||||
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
|
"-opt-in=kotlinx.cinterop.ExperimentalForeignApi",
|
||||||
// Vendored code is upstream foundation/ui, which their build compiles with these
|
|
||||||
// module-level opt-ins (e.g. lazy-layout prefetch uses ExperimentalFoundationApi).
|
|
||||||
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
|
||||||
"-opt-in=androidx.compose.ui.InternalComposeUiApi",
|
"-opt-in=androidx.compose.ui.InternalComposeUiApi",
|
||||||
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi"
|
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi"
|
||||||
|
|||||||
@@ -406,230 +406,6 @@ skikoMain/kotlin/androidx/compose/ui/skiko/RecordDrawRectRenderDecorator.skiko.k
|
|||||||
!skikoMain/kotlin/androidx/compose/ui/window/WindowExceptionHandler.kt
|
!skikoMain/kotlin/androidx/compose/ui/window/WindowExceptionHandler.kt
|
||||||
skikoMain/kotlin/androidx/compose/ui/window/WindowExceptionHandler.kt -> src/vendor/common/kotlin/androidx/compose/ui/window/WindowExceptionHandler.kt
|
skikoMain/kotlin/androidx/compose/ui/window/WindowExceptionHandler.kt -> src/vendor/common/kotlin/androidx/compose/ui/window/WindowExceptionHandler.kt
|
||||||
|
|
||||||
SET_FOLDER=compose/ui/ui-graphics/src
|
|
||||||
|
|
||||||
commonMain/kotlin/ -> src/vendor/common/kotlin/
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Bezier.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Bezier.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/BlendMode.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/BlendMode.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Brush.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Brush.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Canvas.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Canvas.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/ClipOp.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/ClipOp.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Color.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Color.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/ColorFilter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/ColorFilter.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/ColorMatrix.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/ColorMatrix.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Degrees.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Degrees.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/ExperimentalGraphicsApi.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/ExperimentalGraphicsApi.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/FilterQuality.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/FilterQuality.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Float16.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Float16.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/GraphicsContext.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/GraphicsContext.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/ImageBitmap.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/InlineClassHelper.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/InlineClassHelper.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Interpolatable.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Interpolatable.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/IntervalTree.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/IntervalTree.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/LayerOutsets.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/LayerOutsets.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Matrix.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Matrix.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Outline.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Outline.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Paint.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Paint.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PaintingStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PaintingStyle.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Path.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Path.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PathEffect.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathEffect.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PathFillType.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathFillType.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PathGeometry.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathGeometry.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PathHitTester.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathHitTester.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PathIterator.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathIterator.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PathMeasure.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathMeasure.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PathOperation.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathOperation.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PathSegment.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathSegment.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PathSvg.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PathSvg.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PixelMap.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PixelMap.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/PointMode.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/PointMode.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/RectangleShape.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/RectangleShape.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/RenderEffect.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/RenderEffect.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Shader.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Shader.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Shadow.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Shadow.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Shape.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Shape.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/StrokeCap.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/StrokeCap.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/StrokeJoin.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/StrokeJoin.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/TileMode.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/TileMode.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/VertexMode.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/VertexMode.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/Vertices.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/Vertices.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Adaptation.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Adaptation.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/ColorModel.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/ColorModel.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpace.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpace.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpaces.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/ColorSpaces.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Connector.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Connector.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Illuminant.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Illuminant.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Lab.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Lab.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Oklab.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Oklab.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/RenderIntent.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/RenderIntent.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Rgb.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Rgb.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/TransferParameters.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/TransferParameters.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/WhitePoint.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/WhitePoint.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/colorspace/Xyz.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/colorspace/Xyz.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/CanvasDrawScope.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/ContentDrawScope.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/ContentDrawScope.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawContext.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/DrawContext.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/DrawScope.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeMarker.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/DrawScopeMarker.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/DrawTransform.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/DrawTransform.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/drawscope/EmptyCanvas.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/drawscope/EmptyCanvas.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/layer/ChildLayerDependenciesTracker.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/layer/ChildLayerDependenciesTracker.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/layer/CompositingStrategy.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/layer/CompositingStrategy.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayer.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/layer/GraphicsLayer.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/painter/BitmapPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/painter/BitmapPainter.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/painter/BrushPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/painter/BrushPainter.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/painter/ColorPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/painter/ColorPainter.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/painter/Painter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/painter/Painter.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/Blur.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/Blur.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/DropShadowPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/DropShadowPainter.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/InnerShadowPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/InnerShadowPainter.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/Shadow.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/Shadow.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/ShadowContext.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/ShadowContext.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/shadow/ShadowRenderer.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/shadow/ShadowRenderer.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/vector/FastFloatParser.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/vector/FastFloatParser.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/vector/PathBuilder.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/vector/PathBuilder.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/vector/PathNode.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/vector/PathNode.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt -> src/vendor/common/kotlin/androidx/compose/ui/graphics/vector/PathParser.kt
|
|
||||||
|
|
||||||
nonJvmMain/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.nonJvm.kt -> src/vendor/native/kotlin/androidx/compose/ui/graphics/internal/JvmDefaultWithCompatibility.native.kt
|
|
||||||
|
|
||||||
skikoMain/kotlin/ -> src/vendor/skikoRenderer/kotlin/
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/BlendMode.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/BlendMode.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/ImageBitmap.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/ImageBitmap.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/Matrices.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/Matrices.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/Rects.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/Rects.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPaint.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedPaint.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedPath.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPathEffect.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedPathEffect.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedPathMeasure.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedPathMeasure.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedRenderEffect.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaBackedRenderEffect.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaColorFilter.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaColorFilter.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaGraphicsContext.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaGraphicsContext.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaImageAsset.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaImageAsset.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaPathIterator.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaPathIterator.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaShader.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaShader.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaTileMode.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaTileMode.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/SkiaVertexMode.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/SkiaVertexMode.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/layer/SkiaGraphicsLayer.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/layer/SkiaGraphicsLayer.skiko.kt
|
|
||||||
# | skikoMain/kotlin/androidx/compose/ui/graphics/shadow/Blur.skiko.kt -> src/vendor/skikoRenderer/kotlin/androidx/compose/ui/graphics/shadow/Blur.skiko.kt
|
|
||||||
# B6.1 (2026-07-16): un-refused — skiko leg adopts upstream graphics actuals.
|
|
||||||
# SkiaBackedCanvas is then MANUAL-VENDORED (moved to src/skikoRendererMain, re-refused
|
|
||||||
# below) to also implement the port Native* draw contracts. See RENDERER_TASKS.md B6.1.
|
|
||||||
# B6.2: un-refused — skiko leg adopts upstream GraphicsLayer/GraphicsContext (real
|
|
||||||
# skiko RenderNode) now the canvas is SkiaBackedCanvas. See RENDERER_TASKS.md B6.2.
|
|
||||||
!skikoMain/kotlin/androidx/compose/ui/graphics/SkiaBackedCanvas.skiko.kt
|
|
||||||
# B6.1: MANUAL-VENDORED (flattened toBitmap/putBytesInto expect-actuals — see the file).
|
|
||||||
!skikoMain/kotlin/androidx/compose/ui/graphics/SkiaImageAsset.skiko.kt
|
|
||||||
|
|
||||||
SET_FOLDER=compose/ui/ui-text/src
|
|
||||||
|
|
||||||
commonMain/kotlin/ -> src/vendor/common/kotlin/
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/AnnotatedString.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/AnnotatedString.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/AtomicReference.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/AtomicReference.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/Bullet.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/Bullet.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/CharHelpers.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/CharHelpers.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/ExperimentalTextApi.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/ExperimentalTextApi.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/InternalTextApi.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/InternalTextApi.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/LinkAnnotation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/LinkAnnotation.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/LinkInteractionListener.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/LinkInteractionListener.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/MultiParagraph.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/MultiParagraph.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/MultiParagraphIntrinsics.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/MultiParagraphIntrinsics.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/Paragraph.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/Paragraph.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/ParagraphIntrinsics.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/ParagraphIntrinsics.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/ParagraphStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/ParagraphStyle.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/Placeholder.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/Placeholder.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/PlatformTextStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/PlatformTextStyle.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/Savers.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/Savers.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/SpanStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/SpanStyle.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/String.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/String.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/StringAnnotation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/StringAnnotation.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/TextGranularity.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextGranularity.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/TextInclusionStrategy.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextInclusionStrategy.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/TextLayoutResult.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextLayoutResult.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/TextLinkStyles.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextLinkStyles.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/TextMeasurer.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextMeasurer.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/TextPainter.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextPainter.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/TextRange.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextRange.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/TextStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextStyle.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/TtsAnnotation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TtsAnnotation.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/UrlAnnotation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/UrlAnnotation.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/DelegatingFontLoaderForDeprecatedUsage.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/Font.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/Font.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/FontFamily.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontFamily.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontFamilyResolver.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/FontFamilyTypefaceAdapter.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontFamilyTypefaceAdapter.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontListFontFamilyTypefaceAdapter.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/FontLoadingStrategy.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontLoadingStrategy.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/FontMatcher.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontMatcher.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/FontStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontStyle.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontSynthesis.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/FontVariation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontVariation.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/FontWeight.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/FontWeight.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/font/Typeface.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/font/Typeface.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/EditCommand.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/EditCommand.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/EditProcessor.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/EditProcessor.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/EditingBuffer.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/EditingBuffer.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/GapBuffer.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/GapBuffer.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/ImeAction.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/ImeAction.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/ImeOptions.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/ImeOptions.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/InputEventCallback.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/InputEventCallback.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/KeyboardCapitalization.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/KeyboardCapitalization.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/KeyboardType.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/KeyboardType.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/MathUtils.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/MathUtils.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/OffsetMapping.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/OffsetMapping.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/TextFieldValue.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/TextInputService.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/TextInputService.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/input/VisualTransformation.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/input/VisualTransformation.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/internal/InlineClassHelper.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/internal/InlineClassHelper.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/intl/Locale.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/intl/Locale.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/intl/LocaleList.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/intl/LocaleList.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/intl/PlatformLocale.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/intl/PlatformLocale.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/platform/Dispatcher.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/platform/Dispatcher.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/platform/PlatformMultiParagraphDraw.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/platform/PlatformMultiParagraphDraw.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/platform/PlatformString.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/platform/PlatformString.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/platform/Synchronization.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/platform/Synchronization.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/BaselineShift.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/BaselineShift.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/Hyphens.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/Hyphens.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/LineBreak.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/LineBreak.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/LineHeightStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/LineHeightStyle.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/ResolvedTextDirection.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/ResolvedTextDirection.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/TextAlign.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextAlign.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/TextDecoration.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextDecoration.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/TextDirection.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextDirection.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/TextForegroundStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextForegroundStyle.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/TextGeometricTransform.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextGeometricTransform.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/TextIndent.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextIndent.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/TextMotion.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextMotion.kt
|
|
||||||
# | commonMain/kotlin/androidx/compose/ui/text/style/TextOverflow.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/style/TextOverflow.kt
|
|
||||||
!commonMain/kotlin/androidx/compose/ui/text/platform/Synchronization.kt
|
|
||||||
|
|
||||||
nativeMain/kotlin/androidx/compose/ui/text/WeakReference.native.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/WeakReference.native.kt
|
|
||||||
nativeMain/kotlin/androidx/compose/ui/text/font/FontSynthesis.native.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/font/FontSynthesis.native.kt
|
|
||||||
|
|
||||||
nonJvmMain/kotlin/ -> src/vendor/native/kotlin/
|
|
||||||
# | nonJvmMain/kotlin/androidx/compose/ui/text/AnnotatedString.nonJvm.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/AnnotatedString.nonJvm.kt
|
|
||||||
# | nonJvmMain/kotlin/androidx/compose/ui/text/AtomicReference.nonJvm.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/AtomicReference.nonJvm.kt
|
|
||||||
# | nonJvmMain/kotlin/androidx/compose/ui/text/CharHelpers.nonJvm.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/CharHelpers.nonJvm.kt
|
|
||||||
# | nonJvmMain/kotlin/androidx/compose/ui/text/input/GapBuffer.nonJvmMain.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/input/GapBuffer.nonJvmMain.kt
|
|
||||||
# | nonJvmMain/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.nonJvmNative.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/internal/JvmDefaultWithCompatibility.nonJvmNative.kt
|
|
||||||
!nonJvmMain/kotlin/androidx/compose/ui/text/CharHelpers.nonJvm.kt
|
|
||||||
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/Paragraph.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/Paragraph.native.kt
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/Savers.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/Savers.native.kt
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/TextDecorationLineStyle.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/TextDecorationLineStyle.kt
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/TextStyle.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/TextStyle.native.kt
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/UnresolvedSymbolsRegistry.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/UnresolvedSymbolsRegistry.native.kt
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/WeakReference.skiko.kt -> src/vendor/common/kotlin/androidx/compose/ui/text/WeakReference.kt
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/input/PlatformImeOptions.native.kt
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/input/PlatformTextInputService2.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/input/PlatformTextInputService2.kt
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/platform/SkiaMultiParagraphDraw.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/platform/SkiaMultiParagraphDraw.native.kt
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/style/LineBreak.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/style/LineBreak.native.kt
|
|
||||||
skikoMain/kotlin/androidx/compose/ui/text/style/TextMotion.skiko.kt -> src/vendor/native/kotlin/androidx/compose/ui/text/style/TextMotion.native.kt
|
|
||||||
|
|
||||||
# >>> ---- DIAGNOSTIC GAPS ---- <<<<
|
# >>> ---- DIAGNOSTIC GAPS ---- <<<<
|
||||||
# androidMain (125)
|
# androidMain (125)
|
||||||
# | androidMain/kotlin/androidx/compose/ui/Actual.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/Actual.android.kt
|
# | androidMain/kotlin/androidx/compose/ui/Actual.android.kt -> src/vendor/android/kotlin/androidx/compose/ui/Actual.android.kt
|
||||||
|
|||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
package com.compose.sdl.graphics
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// MARK: NativeFinishableCanvas
|
|
||||||
// ==================
|
|
||||||
|
|
||||||
/**
|
|
||||||
A renderer canvas that must be flushed once drawing into it is done — SDL batches
|
|
||||||
geometry and only submits on flush; an offscreen SDL canvas must flush before its
|
|
||||||
backing texture is read. Implemented by Sdl3Canvas (real flush) and SkiaBackedCanvas
|
|
||||||
(no-op — Skia save/restore is balanced per call). Lets renderer-agnostic code
|
|
||||||
(e.g. GraphicsLayer.toImageBitmap) commit an offscreen render without knowing the
|
|
||||||
concrete canvas type.
|
|
||||||
*/
|
|
||||||
interface NativeFinishableCanvas {
|
|
||||||
fun finish()
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
package com.compose.sdl.graphics
|
|
||||||
|
|
||||||
import androidx.compose.ui.layout.ContentScale
|
|
||||||
import com.compose.sdl.res.ResourceKind
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// MARK: NativePainterCanvas bridge
|
|
||||||
// ==================
|
|
||||||
|
|
||||||
/**
|
|
||||||
Image drawing bridge. Renderer Canvas backends (Sdl3Canvas / SkiaBackedCanvas)
|
|
||||||
implement this interface to paint a bundled resource by
|
|
||||||
path + kind. [ResourcePainter] (upstream `Painter` subclass in the res
|
|
||||||
pipeline) casts the current Canvas to this and calls drawNativePainter from
|
|
||||||
inside its `Painter.onDraw()` — reached via upstream `Modifier.paint(...)`
|
|
||||||
(PainterModifier), so the resource render path is driven end-to-end by the
|
|
||||||
vendored upstream engine.
|
|
||||||
|
|
||||||
contentScale is FillBounds when called through PainterModifier (which
|
|
||||||
pre-scales the size); other values are supported for direct callers.
|
|
||||||
*/
|
|
||||||
interface NativePainterCanvas {
|
|
||||||
fun drawNativePainter(
|
|
||||||
inResourcePath: String,
|
|
||||||
inKind: ResourceKind,
|
|
||||||
inX: Float,
|
|
||||||
inY: Float,
|
|
||||||
inWidth: Float,
|
|
||||||
inHeight: Float,
|
|
||||||
inContentScale: ContentScale,
|
|
||||||
inAlpha: Float,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ import androidx.compose.ui.draganddrop.DragAndDropManager
|
|||||||
// MARK: DragAndDropPlatform — SDL event pump hooks
|
// MARK: DragAndDropPlatform — SDL event pump hooks
|
||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/** Platform hooks the :window layer's event loop uses to feed SDL_EVENT_DROP_*
|
/** Platform hooks the :desktop-native-window layer's event loop uses to feed SDL_EVENT_DROP_*
|
||||||
* into a per-window DragAndDropManager. The manager instance is created here
|
* into a per-window DragAndDropManager. The manager instance is created here
|
||||||
* (native actual: Sdl3DragAndDropOwner over the SDL drop stream); each dropX
|
* (native actual: Sdl3DragAndDropOwner over the SDL drop stream); each dropX
|
||||||
* extension casts to the concrete platform implementation and no-ops if the
|
* extension casts to the concrete platform implementation and no-ops if the
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import androidx.compose.ui.node.LayoutNode
|
|||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/** Composition applier over the vendored upstream LayoutNode.
|
/** Composition applier over the vendored upstream LayoutNode.
|
||||||
internal because LayoutNode is internal to :core; the :window layer drives
|
internal because LayoutNode is internal to :core; the :desktop-native-window layer drives
|
||||||
composition through the public ComposeRootHost facade (which upcasts this to
|
composition through the public ComposeRootHost facade (which upcasts this to
|
||||||
Applier<*>). */
|
Applier<*>). */
|
||||||
internal class NodeApplier(root: LayoutNode) : AbstractApplier<LayoutNode>(root) {
|
internal class NodeApplier(root: LayoutNode) : AbstractApplier<LayoutNode>(root) {
|
||||||
|
|||||||
@@ -1,234 +0,0 @@
|
|||||||
package com.compose.sdl.text
|
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.text.AnnotatedString.Range
|
|
||||||
import androidx.compose.ui.text.SpanStyle
|
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
|
||||||
import androidx.compose.ui.text.font.FontVariation
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
|
||||||
import androidx.compose.ui.unit.TextUnit
|
|
||||||
import androidx.compose.ui.unit.TextUnitType
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// MARK: Per-line style runs (renderer helper)
|
|
||||||
// ==================
|
|
||||||
|
|
||||||
/* Project render glue with no official Compose equivalent — the text renderers
|
|
||||||
turn an AnnotatedString's spans into per-line style runs (colour + weight +
|
|
||||||
italic + background + decoration + size). Lives in
|
|
||||||
`com.compose.sdl.text` rather than `androidx.compose.ui.text`. */
|
|
||||||
|
|
||||||
/** A run of one style within a wrapped line, line-local [start, end) cols.
|
|
||||||
`weight` is the OpenType wght axis (100..900, 400 = default), `italic` =
|
|
||||||
FontStyle.Italic (paragraph base or span), `background` = SpanStyle.background
|
|
||||||
(Unspecified = none), `underline`/`lineThrough` = TextDecoration flags
|
|
||||||
(paragraph base or span), `fontSize` = the span's size (Unspecified = the
|
|
||||||
paragraph size; Em scales it, Sp resolves through the density).
|
|
||||||
|
|
||||||
Measurement/wrapping is still base-style, so a bold/resized run's glyphs
|
|
||||||
paint into a line box wrapped at base metrics; that's a known simplification
|
|
||||||
(real fidelity needs a re-wrap per style run). Paint-time advances DO use
|
|
||||||
the run's own style, so runs push each other over correctly. */
|
|
||||||
class ColorRun(
|
|
||||||
val start: Int,
|
|
||||||
val end: Int,
|
|
||||||
val color: Color,
|
|
||||||
val weight: Int = 400,
|
|
||||||
val italic: Boolean = false,
|
|
||||||
val background: Color = Color.Unspecified,
|
|
||||||
val underline: Boolean = false,
|
|
||||||
val lineThrough: Boolean = false,
|
|
||||||
val fontSize: TextUnit = TextUnit.Unspecified,
|
|
||||||
)
|
|
||||||
|
|
||||||
/** Style runs for a single wrapped line, given the AnnotatedString spans (whose
|
|
||||||
start/end index the ORIGINAL text) and this line's start offset. Gaps use
|
|
||||||
the base style; overlapping spans compose colour/background/size last-wins,
|
|
||||||
weight max-wins (bold beats regular), italic/underline/lineThrough OR-wins
|
|
||||||
(also OR'd with the paragraph-level base flags — a span's explicit
|
|
||||||
TextDecoration.None can't clear a base decoration; simplification).
|
|
||||||
|
|
||||||
Spans are assumed sorted by start with non-decreasing end (true for the
|
|
||||||
tokenizers and for buildAnnotatedString appended in order). That lets us
|
|
||||||
binary-search to the first span reaching this line and stop at the first span
|
|
||||||
past it, so a visible line of a 20k-line highlighted body touches only its
|
|
||||||
own handful of spans instead of scanning the whole (tens-of-thousands) list
|
|
||||||
every frame — the difference between smooth and janky scrolling. */
|
|
||||||
fun lineColorRuns(
|
|
||||||
inLine: String,
|
|
||||||
inLineStart: Int,
|
|
||||||
inSpans: List<Range<SpanStyle>>,
|
|
||||||
inDefault: Color,
|
|
||||||
inBaseItalic: Boolean = false,
|
|
||||||
inBaseUnderline: Boolean = false,
|
|
||||||
inBaseLineThrough: Boolean = false,
|
|
||||||
): List<ColorRun> {
|
|
||||||
val vN = inLine.length
|
|
||||||
if (vN == 0) return emptyList()
|
|
||||||
val vLineEnd = inLineStart + vN
|
|
||||||
// Per-column style. Nullable arrays let us distinguish "explicit default" from
|
|
||||||
// "no span touched me" — the coalesce step uses `inDefault` when a column stayed null.
|
|
||||||
val vCols = arrayOfNulls<Color>(vN)
|
|
||||||
val vWgt = IntArray(vN)
|
|
||||||
val vIt = BooleanArray(vN)
|
|
||||||
val vBg = arrayOfNulls<Color>(vN)
|
|
||||||
val vUl = BooleanArray(vN)
|
|
||||||
val vSt = BooleanArray(vN)
|
|
||||||
val vSz = arrayOfNulls<TextUnit>(vN)
|
|
||||||
// First span whose end reaches into this line (skip the prefix before it).
|
|
||||||
var vIdx = firstSpanReaching(inSpans, inLineStart)
|
|
||||||
while (vIdx < inSpans.size) {
|
|
||||||
val vS = inSpans[vIdx]
|
|
||||||
// Sorted by start → once a span starts at/after the line end, so do all
|
|
||||||
// the rest; nothing more can overlap.
|
|
||||||
if (vS.start >= vLineEnd) break
|
|
||||||
vIdx++
|
|
||||||
val vColor = vS.item.color
|
|
||||||
val vSpanWeight = vS.item.fontWeight?.weight ?: 0
|
|
||||||
val vSpanItalic = vS.item.fontStyle == FontStyle.Italic
|
|
||||||
val vSpanBg = vS.item.background
|
|
||||||
val vDeco = vS.item.textDecoration
|
|
||||||
val vSpanUl = vDeco != null && vDeco.contains(TextDecoration.Underline)
|
|
||||||
val vSpanSt = vDeco != null && vDeco.contains(TextDecoration.LineThrough)
|
|
||||||
val vSpanSz = vS.item.fontSize
|
|
||||||
if (vColor == Color.Unspecified && vSpanWeight == 0 && !vSpanItalic &&
|
|
||||||
vSpanBg == Color.Unspecified && !vSpanUl && !vSpanSt && vSpanSz == TextUnit.Unspecified
|
|
||||||
) continue
|
|
||||||
val vA = vS.start - inLineStart
|
|
||||||
val vB = vS.end - inLineStart
|
|
||||||
if (vB <= 0 || vA >= vN) continue
|
|
||||||
var i = if (vA < 0) 0 else vA
|
|
||||||
val vEnd = if (vB > vN) vN else vB
|
|
||||||
while (i < vEnd) {
|
|
||||||
if (vColor != Color.Unspecified) vCols[i] = vColor
|
|
||||||
if (vSpanWeight > vWgt[i]) vWgt[i] = vSpanWeight
|
|
||||||
if (vSpanItalic) vIt[i] = true
|
|
||||||
if (vSpanBg != Color.Unspecified) vBg[i] = vSpanBg
|
|
||||||
if (vSpanUl) vUl[i] = true
|
|
||||||
if (vSpanSt) vSt[i] = true
|
|
||||||
if (vSpanSz != TextUnit.Unspecified) vSz[i] = vSpanSz
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Coalesce equal-style columns into runs. Two adjacent columns coalesce
|
|
||||||
// only when every dimension matches.
|
|
||||||
val vRuns = ArrayList<ColorRun>()
|
|
||||||
var i = 0
|
|
||||||
while (i < vN) {
|
|
||||||
val vC = vCols[i] ?: inDefault
|
|
||||||
val vW = if (vWgt[i] == 0) 400 else vWgt[i]
|
|
||||||
val vI = vIt[i] || inBaseItalic
|
|
||||||
val vB = vBg[i] ?: Color.Unspecified
|
|
||||||
val vU = vUl[i] || inBaseUnderline
|
|
||||||
val vS2 = vSt[i] || inBaseLineThrough
|
|
||||||
val vZ = vSz[i] ?: TextUnit.Unspecified
|
|
||||||
var j = i + 1
|
|
||||||
while (j < vN &&
|
|
||||||
(vCols[j] ?: inDefault) == vC &&
|
|
||||||
(if (vWgt[j] == 0) 400 else vWgt[j]) == vW &&
|
|
||||||
(vIt[j] || inBaseItalic) == vI &&
|
|
||||||
(vBg[j] ?: Color.Unspecified) == vB &&
|
|
||||||
(vUl[j] || inBaseUnderline) == vU &&
|
|
||||||
(vSt[j] || inBaseLineThrough) == vS2 &&
|
|
||||||
(vSz[j] ?: TextUnit.Unspecified) == vZ
|
|
||||||
) j++
|
|
||||||
vRuns.add(ColorRun(i, j, vC, vW, vI, vB, vU, vS2, vZ))
|
|
||||||
i = j
|
|
||||||
}
|
|
||||||
return vRuns
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// MARK: Style-aware metrics helpers
|
|
||||||
// ==================
|
|
||||||
// Shared by BOTH the renderers (paint-time advances) and SdlParagraph
|
|
||||||
// (layout-time widths/heights) so painted glyph runs land exactly inside the
|
|
||||||
// measured box — per-run fontSize / fontWeight change metrics, not just paint.
|
|
||||||
|
|
||||||
/** Resolves a run's SpanStyle.fontSize to pixels: Em scales the paragraph's
|
|
||||||
base size, Sp resolves through the density the base size was resolved with,
|
|
||||||
Unspecified inherits the base. */
|
|
||||||
fun resolveRunPx(inRun: ColorRun, inBasePx: Int, inDensity: Float): Int = when (inRun.fontSize.type) {
|
|
||||||
TextUnitType.Em -> (inBasePx * inRun.fontSize.value).roundToInt().coerceAtLeast(1)
|
|
||||||
TextUnitType.Sp -> (inRun.fontSize.value * inDensity).roundToInt().coerceAtLeast(1)
|
|
||||||
else -> inBasePx
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A run's font axes: its own weight when set (400 = "no run weight"), else
|
|
||||||
the paragraph's base axes. */
|
|
||||||
fun runVariations(
|
|
||||||
inRun: ColorRun,
|
|
||||||
inBaseVariations: List<FontVariation.Setting>?,
|
|
||||||
): List<FontVariation.Setting>? =
|
|
||||||
if (inRun.weight != 400) listOf(FontVariation.weight(inRun.weight)) else inBaseVariations
|
|
||||||
|
|
||||||
/** Whether any span carries a metric-affecting style (size / weight) — the
|
|
||||||
gate for the styled measurement paths, so plain/colour-only text keeps the
|
|
||||||
cheap single-measure route. */
|
|
||||||
fun spansAffectMetrics(inSpans: List<Range<SpanStyle>>?): Boolean =
|
|
||||||
inSpans != null && inSpans.any {
|
|
||||||
it.item.fontSize != TextUnit.Unspecified || it.item.fontWeight != null
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Width of a text slice, span-aware: sums each style run's advance at its
|
|
||||||
resolved size/weight through [inMeasurer]. `inGlobalStart` maps the slice
|
|
||||||
into the spans' index space. */
|
|
||||||
fun styledSliceWidth(
|
|
||||||
inSlice: String,
|
|
||||||
inGlobalStart: Int,
|
|
||||||
inSpans: List<Range<SpanStyle>>,
|
|
||||||
inBasePx: Int,
|
|
||||||
inDensity: Float,
|
|
||||||
inMeasurer: TextMeasurer,
|
|
||||||
inFontFamily: String?,
|
|
||||||
inBaseVariations: List<FontVariation.Setting>?,
|
|
||||||
): Float {
|
|
||||||
if (inSlice.isEmpty()) return 0f
|
|
||||||
var vW = 0f
|
|
||||||
for (vRun in lineColorRuns(inSlice, inGlobalStart, inSpans, Color.Unspecified)) {
|
|
||||||
val vPx = resolveRunPx(vRun, inBasePx, inDensity)
|
|
||||||
val vVars = runVariations(vRun, inBaseVariations)
|
|
||||||
vW += inMeasurer.measure(
|
|
||||||
inSlice.substring(vRun.start, vRun.end), vPx, Int.MAX_VALUE, inFontFamily, vVars,
|
|
||||||
).width
|
|
||||||
}
|
|
||||||
return vW
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Tallest run cell height on a line — the line's box height when spans carry
|
|
||||||
their own sizes; the base cell height when they don't (or the line is empty). */
|
|
||||||
fun styledLineCellHeight(
|
|
||||||
inLine: String,
|
|
||||||
inGlobalStart: Int,
|
|
||||||
inSpans: List<Range<SpanStyle>>,
|
|
||||||
inBasePx: Int,
|
|
||||||
inDensity: Float,
|
|
||||||
inMeasurer: TextMeasurer,
|
|
||||||
inFontFamily: String?,
|
|
||||||
inBaseVariations: List<FontVariation.Setting>?,
|
|
||||||
): Float {
|
|
||||||
var vH = inMeasurer.lineHeight(inBasePx, inFontFamily, inBaseVariations)
|
|
||||||
for (vRun in lineColorRuns(inLine, inGlobalStart, inSpans, Color.Unspecified)) {
|
|
||||||
val vPx = resolveRunPx(vRun, inBasePx, inDensity)
|
|
||||||
if (vPx != inBasePx) {
|
|
||||||
val vRunH = inMeasurer.lineHeight(vPx, inFontFamily, runVariations(vRun, inBaseVariations))
|
|
||||||
if (vRunH > vH) vH = vRunH
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return vH
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Index of the first span whose end is past inLineStart — i.e. the first that
|
|
||||||
can reach into a line starting there. Binary search; relies on spans being
|
|
||||||
sorted with non-decreasing end (see lineColorRuns). */
|
|
||||||
private fun firstSpanReaching(inSpans: List<Range<SpanStyle>>, inLineStart: Int): Int {
|
|
||||||
var vLo = 0
|
|
||||||
var vHi = inSpans.size
|
|
||||||
while (vLo < vHi) {
|
|
||||||
val vMid = (vLo + vHi) ushr 1
|
|
||||||
if (inSpans[vMid].end <= inLineStart) vLo = vMid + 1 else vHi = vMid
|
|
||||||
}
|
|
||||||
return vLo
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
package com.compose.sdl.text
|
|
||||||
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
|
|
||||||
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
|
||||||
import androidx.compose.ui.node.DrawModifierNode
|
|
||||||
import androidx.compose.ui.node.ModifierNodeElement
|
|
||||||
import androidx.compose.ui.text.AnnotatedString.Range
|
|
||||||
import androidx.compose.ui.text.SpanStyle
|
|
||||||
import androidx.compose.ui.text.font.FontVariation
|
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
|
||||||
|
|
||||||
// ==================
|
|
||||||
// MARK: Native text drawing bridge (B5)
|
|
||||||
// ==================
|
|
||||||
|
|
||||||
/**
|
|
||||||
Bridges commonMain text drawing to the platform renderer without a hard
|
|
||||||
dependency on it. A renderer's `Canvas` (Sdl3Canvas / SkiaBackedCanvas)
|
|
||||||
implements [NativeTextCanvas]; the text [DrawModifierNode] gets the live Canvas via
|
|
||||||
`drawIntoCanvas` and, if it's a NativeTextCanvas, asks it to paint the glyphs. Layout
|
|
||||||
sizing still goes through `currentTextMeasurer` so bounds match the drawn text.
|
|
||||||
|
|
||||||
This keeps text as a real upstream DrawModifierNode in the modifier chain — it draws
|
|
||||||
through the coordinator pipeline like any other draw node.
|
|
||||||
*/
|
|
||||||
interface NativeTextCanvas {
|
|
||||||
fun drawNativeText(
|
|
||||||
inText: String,
|
|
||||||
inSpans: List<Range<SpanStyle>>?,
|
|
||||||
inX: Float,
|
|
||||||
inY: Float,
|
|
||||||
inBoxWidth: Float,
|
|
||||||
inBoxHeight: Float,
|
|
||||||
inColor: Color,
|
|
||||||
inFontSizePx: Int,
|
|
||||||
inTextAlign: TextAlign,
|
|
||||||
inSoftWrap: Boolean,
|
|
||||||
inFontFamily: String?,
|
|
||||||
inFontVariations: List<FontVariation.Setting>?,
|
|
||||||
// Paragraph-level TextStyle.fontStyle == Italic + merged textDecoration
|
|
||||||
// (underline / line-through). Per-run overrides ride in via inSpans.
|
|
||||||
inBaseItalic: Boolean = false,
|
|
||||||
inTextDecoration: TextDecoration? = null,
|
|
||||||
// The paragraph's per-line band height in px (TextStyle.lineHeight when the
|
|
||||||
// style specifies one — SdlParagraph passes it so glyph rows stack exactly
|
|
||||||
// where layout put the line boxes). <= 0 → derive from font metrics (legacy
|
|
||||||
// behaviour; icon callers and TextDrawNode don't pass it).
|
|
||||||
inLineHeightPx: Float = 0f,
|
|
||||||
// Compat-trim (raw styles): the FIRST line keeps the tight font cell. False
|
|
||||||
// for M3's LineHeightStyle(Trim.None) — every line is the full band.
|
|
||||||
inTrimFirstLine: Boolean = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modifier element carrying the text paint params onto a LayoutNode's chain.
|
|
||||||
// Public (not internal) so :foundation's IconText.kt can reach it — same
|
|
||||||
// package, different module.
|
|
||||||
data class TextDrawElement(
|
|
||||||
val text: String,
|
|
||||||
val spans: List<Range<SpanStyle>>?,
|
|
||||||
val color: Color,
|
|
||||||
val fontSizePx: Int,
|
|
||||||
val textAlign: TextAlign,
|
|
||||||
val softWrap: Boolean,
|
|
||||||
val fontFamily: String?,
|
|
||||||
val fontVariations: List<FontVariation.Setting>?,
|
|
||||||
) : ModifierNodeElement<TextDrawNode>() {
|
|
||||||
|
|
||||||
override fun create(): TextDrawNode =
|
|
||||||
TextDrawNode(text, spans, color, fontSizePx, textAlign, softWrap, fontFamily, fontVariations)
|
|
||||||
|
|
||||||
override fun update(node: TextDrawNode) {
|
|
||||||
node.text = text
|
|
||||||
node.spans = spans
|
|
||||||
node.color = color
|
|
||||||
node.fontSizePx = fontSizePx
|
|
||||||
node.textAlign = textAlign
|
|
||||||
node.softWrap = softWrap
|
|
||||||
node.fontFamily = fontFamily
|
|
||||||
node.fontVariations = fontVariations
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DrawModifierNode that paints the text via the renderer Canvas at the node's origin.
|
|
||||||
class TextDrawNode(
|
|
||||||
var text: String,
|
|
||||||
var spans: List<Range<SpanStyle>>?,
|
|
||||||
var color: Color,
|
|
||||||
var fontSizePx: Int,
|
|
||||||
var textAlign: TextAlign,
|
|
||||||
var softWrap: Boolean,
|
|
||||||
var fontFamily: String?,
|
|
||||||
var fontVariations: List<FontVariation.Setting>?,
|
|
||||||
) : Modifier.Node(), DrawModifierNode {
|
|
||||||
|
|
||||||
override fun ContentDrawScope.draw() {
|
|
||||||
drawContent()
|
|
||||||
drawIntoCanvas { vCanvas ->
|
|
||||||
(vCanvas as? NativeTextCanvas)?.drawNativeText(
|
|
||||||
text, spans, 0f, 0f, size.width, size.height,
|
|
||||||
color, fontSizePx, textAlign, softWrap, fontFamily, fontVariations,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user