From ad2cc63ff497e166a96c5c7f512b5d4db24b3f05 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Sun, 7 Jun 2026 14:11:12 -0500 Subject: [PATCH] Refresh README and docs to reflect current facade state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many tracked issues have shipped since these docs were last touched — update README, docs/architecture.md, samples/README.md, and the Jetchat sample README to reflect what's actually in the repo today. README.md: - Soften the "27 lines is the entire MainActivity.cs" claim — the sample is now a tabbed kitchen-sink demo. Point readers at samples/Jetchat for a single-screen real-app example. - Rebuild the "What's wrapped today" table: add Pager / FlowRow / BoxWithConstraints / LazyStaggeredGrid, Carousels, PullToRefreshBox, full Button + IconButton + FAB variants, Text styling primitives, SecureTextField, ExposedDropdownMenuBox, DockedSearchBar, NavHost / NavController, Animation (AnimatedVisibility / AnimatedContent / Crossfade), Effects (LaunchedEffect / DisposableEffect / SideEffect), expanded Modifier surface, Color value type + parameterized MaterialTheme + theme reads, SuspendBridge async path, full state-holder list. - Update Status to mention the kitchen-sink demo and theme params. docs/architecture.md: - Add ComposeFacadeGenerator alongside ComposeBridgeGenerator and ComposeDefaultsGenerator (third generator). - New "Compose value types" section covering Color / Dp / Sp / FontWeight / TextAlign and the ComposeValueTypes registry. - Rewrite Known issues to call out theming reads (#61), effects (#57/#128), suspend bridges (#97), Compose Navigation (#60) as shipped, with the remaining caveats. Add a "Still missing (tracked)" list pointing at the open issues (#59 CompositionLocal, #64 drawing, #69 WindowInsets, #54/#103 Expressive M3, drawer Open/Close suspend bridges). samples/README.md: - Drop closed issues (#51 / #53 / #58 / #61 / #62 / #63 / #65 / #70) from "Tracked facade gaps" and list them as a one-line "previously appeared here" summary instead. Add #59 CompositionLocal to the open list. - Restore the accidentally-truncated Attribution paragraph. samples/Jetchat/README.md: - Recast the "Hard-coded colors/typography" implementation notes as "feasible — not yet wired" now that MaterialTheme.CurrentColorScheme/Typography(composer) reads exist (#61 / PR #133). - Update the hamburger-nav-icon note to reflect that the DrawerStateHolder wrapper landed (PR #131, Phase 10) and only the suspend Open()/Close() bridges remain. - Wire the user-profile-screen omission to NavHost / NavController (now bound via #60). - Move the asymmetric RoundedCornerShape omission from #65 to the still-open #64 drawing primitives. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 35 +++++++------ docs/architecture.md | 106 +++++++++++++++++++++++++++++++++++--- samples/Jetchat/README.md | 43 +++++++++------- samples/README.md | 22 ++++---- 4 files changed, 153 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 588cc10d..45ef22ae 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ The translation is mechanical — `new` instead of bare calls, commas instead of | `count++` | `count++` (operator on `MutableNumberState`) | | `"Count: $count"` | `$"Count: {count}"` (via `MutableState.ToString`) | -That's the entire [`MainActivity.cs`](src/ComposeNet.Sample/MainActivity.cs) — ~27 lines including ceremony, 13 for the composition itself. +That's an end-to-end Material 3 counter app in ~13 lines of composition — start from this shape when adding a new screen. The actual [`src/ComposeNet.Sample/MainActivity.cs`](src/ComposeNet.Sample/MainActivity.cs) in the repo is a much larger **kitchen-sink demo** that exercises every facade in a tabbed `Scaffold`; for a single-screen real-app example see [`samples/Jetchat`](samples/Jetchat). ## What's wrapped today @@ -107,24 +107,29 @@ The facade [`ComposeNet.Compose`](src/ComposeNet.Compose) covers the common Mate | Category | Composables | | ----------------------- | ----------- | -| Theme & layout | `MaterialTheme`, `Column`, `Row`, `Box`, `Spacer`, `Scaffold`, `HorizontalDivider`, `VerticalDivider` | -| Lazy lists | `LazyColumn`, `LazyRow`, `LazyVerticalGrid`, `LazyHorizontalGrid` (+ `GridCells`) | +| Theme & layout | `MaterialTheme` (parameterizable `ColorScheme`/`Typography`/`Shapes`/`Dark`/`UseDynamicColor`, plus `CurrentColorScheme`/`CurrentTypography`/`CurrentShapes` reads), `Column`, `Row` (`Arrangement`), `Box`, `Spacer`, `Scaffold`, `HorizontalDivider`, `VerticalDivider`, `BoxWithConstraints` | +| Lazy lists & paging | `LazyColumn`, `LazyRow`, `LazyVerticalGrid`, `LazyHorizontalGrid`, `LazyVerticalStaggeredGrid`, `LazyHorizontalStaggeredGrid` (+ `GridCells`/`StaggeredGridCells`), `HorizontalPager`, `VerticalPager` (+ `PagerState`), `FlowRow`, `FlowColumn` | +| Carousels & pull | `HorizontalMultiBrowseCarousel`, `HorizontalCenteredHeroCarousel`, `HorizontalUncontainedCarousel`, `PullToRefreshBox` (+ `PullToRefreshState`) | | Surfaces | `Surface`, `Card`, `ElevatedCard`, `OutlinedCard` | -| App bars | `TopAppBar` family (Center/Medium/Large/Flexible), `BottomAppBar`, `FlexibleBottomAppBar` | +| App bars | `TopAppBar` family (Center/Medium/Large/Flexible — with optional subtitles via Phase 9 branching), `BottomAppBar`, `FlexibleBottomAppBar` | | Tabs | `TabRow` family (Primary/Secondary, scrollable variants), `Tab`, `LeadingIconTab`, `CustomTab` | -| Buttons | `Button`, `IconButton`, `FloatingActionButton` | -| Text & input | `Text`, `TextField`, `OutlinedTextField` | -| Media | `Image`, `Icon` | +| Buttons | `Button`, `OutlinedButton`, `TextButton`, `ElevatedButton`, `FilledTonalButton`, `IconButton`, `OutlinedIconButton`, `FilledIconButton`, `FilledTonalIconButton`, full `IconToggleButton` family, `FloatingActionButton` (+ `Small`/`Large`/`Extended` variants) | +| Text & input | `Text` (`TextStyle`/`FontWeight`/`FontStyle`/`FontFamily`/`TextDecoration`/`TextAlign`/`TextOverflow`), `TextField`, `OutlinedTextField`, `SecureTextField`, `OutlinedSecureTextField` | +| Media | `Image`, `Icon` (drawable-resource and `ImageVector` overloads) | | Chips | `AssistChip`, `FilterChip`, `InputChip`, `SuggestionChip` (+ `Elevated*` variants) | -| Selection | `Checkbox`, `TriStateCheckbox`, `RadioButton`, `Switch`, `Slider`, `RangeSlider`, `SegmentedButton` | +| Selection | `Checkbox`, `TriStateCheckbox`, `RadioButton`, `Switch`, `Slider`, `RangeSlider`, `SegmentedButton`, `SingleChoiceSegmentedButtonRow`, `MultiChoiceSegmentedButtonRow` | | Progress & feedback | `CircularProgressIndicator`, `LinearProgressIndicator`, `ListItem`, `Badge`, `BadgedBox` | -| Menus & search | `DropdownMenu` + `DropdownMenuItem`, `SearchBar` family (Top, ExpandedDocked, ExpandedFullScreen) | -| Navigation | `NavigationBar`, `NavigationRail`, `WideNavigationRail`, `ModalWideNavigationRail` (+ items) | -| Drawers | `ModalNavigationDrawer`, `DismissibleNavigationDrawer`, `PermanentNavigationDrawer` | -| Sheets & pickers | `ModalBottomSheet`, `BottomSheetScaffold`, `DatePicker`/`DatePickerDialog`, `TimePicker`/`TimePickerDialog` | +| Menus & search | `DropdownMenu` + `DropdownMenuItem`, `ExposedDropdownMenuBox` + `ExposedDropdownMenu`, `SearchBar` family (Top, ExpandedDocked, ExpandedFullScreen, `DockedSearchBar`) | +| Navigation | `NavHost`, `NavController`, `NavBackStackEntry`, `NavigationBar`, `NavigationRail`, `WideNavigationRail`, `ModalWideNavigationRail` (+ items) | +| Drawers | `ModalNavigationDrawer`, `DismissibleNavigationDrawer`, `PermanentNavigationDrawer` (+ matching sheets, generated via Phase 10 `[ConfirmStateChange]`) | +| Sheets & pickers | `ModalBottomSheet`, `BottomSheetScaffold`, `DatePicker`/`DatePickerDialog`, `DateRangePicker`/`DateRangePickerDialog`, `TimePicker`/`TimeInput`/`TimePickerDialog` | | Overlays | `AlertDialog`, `Snackbar` + `SnackbarHost`, `Tooltip` | -| Modifier chains | `Padding`, `FillMaxWidth/Height/Size`, `Width`, `Height`, `Size`, `SafeDrawingPadding`, `SystemBarsPadding` | -| State | `Remember` (+ keyed `Remember(factory, key1, …)`, `RememberKeyed`), `RememberSaveable` (+ keyed), `MutableState`, `MutableNumberState`, `MutableStateList`, `MutableStateMap`, `DerivedStateOf`, `ProduceState`, plus `DatePickerState`, `TimePickerState`, `SearchBarState`, `SnackbarHostState` | +| Animation | `AnimatedVisibility`, `AnimatedContent`, `Crossfade` | +| Effects | `Compose.LaunchedEffect`, `Compose.DisposableEffect`, `Compose.SideEffect` | +| Modifier chains | `Padding`, `FillMaxWidth/Height/Size`, `Width`, `Height`, `Size`, `AspectRatio`, `Offset`, `Alpha`, `Background`, `Border`, `Clip`, `Clickable`, `Weight`, `VerticalScroll`/`HorizontalScroll` (+ `ScrollState`), `Draggable` (+ `DraggableState`), focus/semantics/gestures, `SafeDrawingPadding`, `SystemBarsPadding` | +| Value types | `Color` (+ `FromRgb`/`FromArgb`/`FromHex` and theme reads), `Dp`, `Sp`, `FontWeight`, `TextAlign`, `Shape` | +| State | `Remember` (+ keyed `Remember(factory, key1, …)`, `RememberKeyed`), `RememberSaveable` (+ keyed), `MutableState`, `MutableNumberState`, `MutableStateList`, `MutableStateMap`, `DerivedStateOf`, `ProduceState`, plus `DatePickerState`, `DateRangePickerState`, `TimePickerState`, `SearchBarState`, `SnackbarHostState`, `ScrollState`, `PagerState`, `PullToRefreshState`, `DraggableState`, `DrawerStateHolder`, `WideNavigationRailState`, `FocusRequester`/`FocusState` | +| Async | `SuspendBridge` — Kotlin `suspend` functions surfaced as C# `Task` / `Task` (drives `ScrollState.ScrollToAsync`, `LazyListState.AnimateScrollToItemAsync`, `SnackbarHostState.ShowSnackbarAsync`, etc.) | ## Samples @@ -132,7 +137,7 @@ The facade [`ComposeNet.Compose`](src/ComposeNet.Compose) covers the common Mate ## Status -The sample builds, deploys to an Android 16 (API 36) emulator, and renders a real Material 3 UI end-to-end: dynamic Material You colors, edge-to-edge layout, an interactive `Button` that increments `MutableNumberState` and recomposes the count. +The sample builds, deploys to an Android 16 (API 36) emulator, and renders a real Material 3 UI end-to-end: dynamic Material You colors via parameterizable `MaterialTheme`, edge-to-edge layout, an interactive `Button` that increments `MutableNumberState` and recomposes the count. The kitchen-sink demo in [`src/ComposeNet.Sample`](src/ComposeNet.Sample) exercises the full facade across a tabbed `Scaffold` (text styling, lists, pickers, dialogs, sheets, navigation, animation, effects, search, dropdowns, draggable modifiers, …). The facade and sample reference the official `Xamarin.AndroidX.Compose.*` 1.11.2.x and `Xamarin.AndroidX.Compose.Material3` 1.4.0.x NuGets directly — the per-binding projects this repo originally needed have been deleted. diff --git a/docs/architecture.md b/docs/architecture.md index a9b0086f..50ffc0e9 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -26,8 +26,14 @@ boilerplate (cached `IntPtr` class/method handles, signature constants, `try { Call… } finally { GC.KeepAlive(…) }` around every managed wrapper whose `.Handle` was read into a `JValue`, and `DeleteLocalRef` for local string refs) is emitted by `ComposeBridgeGenerator` in -[`ComposeNet.SourceGenerators`](../src/ComposeNet.SourceGenerators). Only -two outliers stay hand-written: `ModifierHandle` (a managed +[`ComposeNet.SourceGenerators`](../src/ComposeNet.SourceGenerators). For +the simplest facade shapes, the entire `Render` body itself is also +generated by `ComposeFacadeGenerator` from a one-line `[ComposeFacade]` +attribute stacked on the bridge — see +[`.github/copilot-instructions.md`](../.github/copilot-instructions.md) +("Facade generator — `[ComposeFacade]`") for the ~10 phases of facade +shapes the generator covers. Only two outliers stay hand-written at +the JNI layer: `ModifierHandle` (a managed `IModifier? → IntPtr` conversion that none of the bridge shapes fit) and `ModifierCompanionInstance` (a `$$INSTANCE` static field lookup, not a method invocation). The user never sees any of this; when @@ -59,9 +65,30 @@ default." Hand-writing those bitmasks at every call site is illegible (`_changed: 0b0111`); writing the `[Flags]` enum by hand is tedious and bit-rots when the Kotlin signature changes. -[`ComposeNet.SourceGenerators`](../src/ComposeNet.SourceGenerators) is a -small Roslyn incremental generator triggered by an assembly-level -attribute. It supports two forms. +[`ComposeNet.SourceGenerators`](../src/ComposeNet.SourceGenerators) hosts +three Roslyn incremental generators that together eliminate the +boilerplate: + +- **`ComposeDefaultsGenerator`** — emits a `[Flags] enum` per + composable, one bit per Kotlin parameter, driven by + `[ComposeDefaults]` (described below). All declarations live in + [`ComposeDefaults.cs`](../src/ComposeNet.Compose/ComposeDefaults.cs). +- **`ComposeBridgeGenerator`** — emits the JNI body of each bridge + partial method declared in + [`ComposeBridges.cs`](../src/ComposeNet.Compose/ComposeBridges.cs) + from a single `[ComposeBridge(Class=…, JvmName=…, Signature=…)]` + attribute. +- **`ComposeFacadeGenerator`** — emits the full + `ComposableNode`-derived facade class (ctor + `Render` body) for + bridges whose shape it recognizes, driven by `[ComposeFacade]` + stacked on the bridge declaration. See + [`.github/copilot-instructions.md`](../.github/copilot-instructions.md) + for the ~10 facade-shape "phases" (containers, callbacks, named + slots, painter resources, state holders with parameterised + Remember, default-from-theme color slots, bridge branching, JCW + confirm-state-change adapters, …). + +The `$default` generator supports two forms. **Generic form** — when the binder exposes the Kt method: @@ -102,6 +129,26 @@ all of these declarations — every composable in the facade gets its pin the emitted output. When the upstream binder fix lands, each declarative attribute can be swapped one-for-one to the generic form. +## Compose value types + +The Kotlin `@JvmInline value class` types that surface as primitives +across JNI (`Color`, `Dp`, `Sp`, `FontWeight`, `TextAlign`) are +mirrored as typed C# structs in +[`ComposeNet.Compose`](../src/ComposeNet.Compose). The bridge generator +keeps a tiny registry in +[`ComposeValueTypes.cs`](../src/ComposeNet.SourceGenerators/ComposeValueTypes.cs) +that maps each value type to its JNI slot (`F` / `J` / `I`) and a +`Pack(T?)` helper, so a bridge can declare `Dp? width` or `Sp? size` +and the generator emits the correct primitive lowering plus the +`$default`-bit clear on non-null. `Color` is a 64-bit packed ULong with +an implicit conversion to `long`, so call sites pass `Color.FromRgb(…)` +or one of the named constants straight through to bridges and bound +binding methods that already take a packed color — no per-call +conversion required. Reference-typed wrappers (`FontWeight`, +`TextDecoration`, `Shape`) go through the generic +"reference-type → handle with null check" path the bridge generator +already supports. + ## What's missing on the C# side (and why) | Kotlin | C# today | Cost | @@ -164,6 +211,15 @@ class. `Modifier` class via a one-time JNI fetch of the `$$INSTANCE` field — invisible to callers. See [NOTES.md](NOTES.md) open issue #1 for the upstream-friendly fix. +- **Theming reads landed (#61).** Use + `MaterialTheme.CurrentColorScheme(composer)`, + `CurrentTypography(composer)`, and `CurrentShapes(composer)` from + inside `Render` to read the active theme; mirror of Kotlin's + `MaterialTheme.colorScheme / typography / shapes` reads. + `MaterialTheme` itself takes `ColorScheme`/`Typography`/`Shapes`/`Dark`/`UseDynamicColor` + as settable properties — see `MaterialTheme.LightColorScheme()` / + `DarkColorScheme()` / `DynamicLightColorScheme(...)` / + `DynamicDarkColorScheme(...)` factories. - **`remember(keys, …)` is supported.** Use the keyed overloads `Remember(factory, key1)`, `Remember(factory, key1, key2)`, `Remember(factory, key1, key2, key3)`, or @@ -181,6 +237,40 @@ class. available. `ProduceState` is implemented purely in C# via an `IRememberObserver` JCW — the producer is a plain `Func, CancellationToken, Task>`, not a Kotlin - suspend lambda. Tracked in #62. Still missing: - `snapshotFlow { … }` (Flow → `IAsyncEnumerable` bridge) and custom - `Saver` (only `autoSaver` is exposed today). + suspend lambda. Still missing: `snapshotFlow { … }` + (Flow → `IAsyncEnumerable` bridge) and custom `Saver` (only + `autoSaver` is exposed today). +- **Effects.** `Compose.LaunchedEffect`, `Compose.DisposableEffect`, + and `Compose.SideEffect` are bound (#57 / #128). `LaunchedEffect` + takes a plain `Func` and a key list + (rather than a Kotlin suspend lambda); cancellation happens on key + change / leaving composition just like Kotlin. +- **Suspend functions.** `SuspendBridge` (PR #97) lets a + hand-written bridge return Kotlin's `COROUTINE_SUSPENDED` sentinel + and complete a `Task` from the eventual resume. Used by + `ScrollState.ScrollToAsync` / `LazyListState.AnimateScrollToItemAsync` + / `SnackbarHostState.ShowSnackbarAsync`. The drawer family + (`ModalNavigationDrawer` etc.) exposes a `DrawerStateHolder` + wrapper but programmatic `open()` / `close()` suspend bridges are + not yet wired up. +- **Compose Navigation.** `NavHost` / `NavController` / + `NavBackStackEntry` are bound (#60). Pass route lambdas via + `NavGraphBuilderLambda`; deep links are not yet exposed. + +## Still missing (tracked) + +- `CompositionLocal` / `CompositionLocalProvider` — see + [#59](https://github.com/jonathanpeppers/compose-net/issues/59). +- Drawing primitives: `Canvas`, `Modifier.drawBehind`, `Brush`, + `Path`, `Shape` factories — see + [#64](https://github.com/jonathanpeppers/compose-net/issues/64). +- WindowInsets padding modifiers (`imePadding`, + `navigationBarsPadding`, `statusBarsPadding`, …) — see + [#69](https://github.com/jonathanpeppers/compose-net/issues/69). +- M3 Expressive newcomers (SplitButton, ButtonGroup, + LoadingIndicator, FAB menus) — see + [#54](https://github.com/jonathanpeppers/compose-net/issues/54) / + [#103](https://github.com/jonathanpeppers/compose-net/issues/103). +- Programmatic drawer / bottom-sheet open via `suspend` — the JCW + veto-adapter pattern is in place (Phase 10 `[ConfirmStateChange]`) + but the suspend-bridge `Open()`/`Close()` aren't surfaced yet. diff --git a/samples/Jetchat/README.md b/samples/Jetchat/README.md index 1a00e82d..30f583f7 100644 --- a/samples/Jetchat/README.md +++ b/samples/Jetchat/README.md @@ -79,19 +79,20 @@ dotnet build samples/Jetchat -t:Run ## What's omitted Each row links to the upstream issue tracking the missing facade -feature: +feature (or, for items where the facade primitive landed but Jetchat +hasn't been ported to it yet, notes "feasible — not yet wired"): | Upstream feature | Tracking issue | |-------------------------------------------|----------------| -| Hamburger nav icon that programmatically opens the drawer | needs a `DrawerState` wrapper + suspend bridges (the `SuspendBridge` plumbing landed in #97 but the drawer-state type isn't exposed yet) — edge-swipe still works | +| Hamburger nav icon that programmatically opens the drawer | the `DrawerStateHolder` wrapper exists and `SuspendBridge` plumbing (#97) is in place, but the suspend `Open()`/`Close()` haven't been surfaced yet — edge-swipe still works | | Multi-channel message lists (the drawer changes the title but not the messages — we only seed one conversation) | requires extending the sample's seed data, not blocked by facade | | `LazyColumn(reverseLayout = true)` so newest messages sit at the bottom | not yet exposed on the `LazyColumn` facade | | Image / sticker / file message attachments inside bubbles | requires composable image-loader plumbing | -| User profile screen | depends on `androidx.navigation.compose` binding | +| User profile screen | wire up via `NavHost` / `NavController` (now bound via [#60](https://github.com/jonathanpeppers/compose-net/issues/60)) — not yet wired | | IME-synchronized scroll-to-bottom | [#69](https://github.com/jonathanpeppers/compose-net/issues/69) (`imePadding`) | -| `MaterialTheme.colorScheme.primary` reads for the "me" bubble + drawer selection + tonal text colors (`onSurfaceVariant`) | [#61](https://github.com/jonathanpeppers/compose-net/issues/61) | -| `MaterialTheme.typography.*` reads (we approximate the M3 sp/weight values directly until these land) | [#58](https://github.com/jonathanpeppers/compose-net/issues/58) | -| Asymmetric `RoundedCornerShape(topStart, …)` on bubbles | `Modifier.Clip(Dp)` only takes a single radius; full `Shape` API tracked under [#65](https://github.com/jonathanpeppers/compose-net/issues/65)'s follow-up surface | +| `MaterialTheme.colorScheme.*` reads for the "me" bubble + drawer selection + tonal text colors (`onSurfaceVariant`) | **feasible — not yet wired**. `MaterialTheme.CurrentColorScheme(composer)` landed in [#61](https://github.com/jonathanpeppers/compose-net/issues/61) (PR #133); Jetchat still uses hardcoded `Color.FromRgb(...)` values | +| `MaterialTheme.typography.*` reads | **feasible — not yet wired**. `MaterialTheme.CurrentTypography(composer)` landed alongside #61; Jetchat still hard-codes M3 sp/weight values | +| Asymmetric `RoundedCornerShape(topStart, …)` on bubbles | `Modifier.Clip(Dp)` only takes a single radius; full asymmetric `Shape` API still needs [#64](https://github.com/jonathanpeppers/compose-net/issues/64) drawing primitives | | Search / info popups behind the action icons (`FunctionalityNotAvailablePopup`) | requires popup APIs not yet bound; the icons are wired but the onClick is a no-op | ## Facade features added for this port @@ -139,11 +140,10 @@ the C# source stays close in shape to the Kotlin original. ### Hard-coded colors instead of `MaterialTheme.colorScheme.*` reads — [#61] -User code can't read `MaterialTheme.colorScheme.primary` / -`primaryContainer` / `surfaceVariant` / `onSurfaceVariant` etc. yet — -only the source generator's `[ComposeFacade(DefaultColorFromTheme=…)]` -path consumes them, and it's internal. Until #61 lands the sample -hard-codes: +`MaterialTheme.CurrentColorScheme(composer)` shipped in PR #133 and +the color reads (`.Primary`, `.PrimaryContainer`, `.SurfaceVariant`, +`.OnSurfaceVariant`, …) are now usable from any `Render` body. +Jetchat hasn't been migrated yet — until then the sample hard-codes: | Slot | Hex | Approximates upstream's | |-------------------------------|--------------|--------------------------| @@ -156,9 +156,10 @@ color instead of `onSurfaceVariant`. ### Hard-coded sp / weight values instead of `MaterialTheme.typography.*` reads — [#58] -Same shape as the color gap: typography reads aren't exposed yet. -The sample sets `FontSize` / `FontWeight` directly to the M3 spec -values for each role: +`MaterialTheme.CurrentTypography(composer)` is available alongside the +color reads above. Jetchat hasn't been migrated to it yet — the sample +still sets `FontSize` / `FontWeight` directly to the M3 spec values +for each role: | Upstream role | Where used | sp | Weight | Letter-spacing | |-------------------|--------------------------------------|----|--------------|----------------| @@ -212,12 +213,14 @@ icons are real affordances (correct drawables, real ripple via ### No hamburger nav icon — drawer opens via edge-swipe only -Programmatic drawer open requires a `DrawerState` C# wrapper plus -suspend bridges around `DrawerState.open()` / `close()`. The -`SuspendBridge` plumbing landed in #97 and is already used by -`ScrollState.scrollTo` + `animateScrollToItem`, so this is the next -state-holder to expose. A no-op hamburger button would be a misleading -affordance, so it's omitted entirely until the wrapper lands. +Programmatic drawer open requires a C# wrapper around +`DrawerState.open()` / `close()`. The `DrawerStateHolder` wrapper +type is in place (PR #131, Phase 10 `[ConfirmStateChange]` migration +of the drawer family) and `SuspendBridge` plumbing (#97) already +powers other `*Async` calls — what's still missing is the suspend +bridge exposing `DrawerState.open` / `close` to C#. A no-op +hamburger button would be a misleading affordance, so it's omitted +entirely until the wrapper lands. ### Static builder instead of `ComposableNode` subclass diff --git a/samples/README.md b/samples/README.md index 1e2b6646..c35d482d 100644 --- a/samples/README.md +++ b/samples/README.md @@ -40,23 +40,25 @@ that needs the same primitive. | Issue | Area | Blocks (in samples) | |------:|-----------------------------|--------------------| -| [#51](https://github.com/jonathanpeppers/compose-net/issues/51) | `Pager`, `FlowRow`/`FlowColumn`, `BoxWithConstraints`, remaining Lazy variants | **JetNews** carousel, **Jetsnack** chip wrapping. | -| [#53](https://github.com/jonathanpeppers/compose-net/issues/53) | `PullToRefreshBox` | News-feed refresh in **JetNews** / **Reply**. | -| [#58](https://github.com/jonathanpeppers/compose-net/issues/58) | `Text` styling — `TextStyle`, `FontWeight`, `AnnotatedString`, `KeyboardOptions`, `supportingText`, leading/trailing icons | **Jetchat** bold author names; every sample's typography. | -| [#61](https://github.com/jonathanpeppers/compose-net/issues/61) | Theming — parameterize `MaterialTheme`, `ColorScheme` / `Typography` / `Shapes`, `MaterialTheme.colorScheme.*` reads, Material Icons | **Jetchat** primary-tinted "me" bubble, every sample's app-bar icons. | -| [#62](https://github.com/jonathanpeppers/compose-net/issues/62) | State primitives — `rememberSaveable`, `mutableStateListOf`/`MapOf`, `derivedStateOf` | Rotation-stable state in every sample. | -| [#63](https://github.com/jonathanpeppers/compose-net/issues/63) | `Modifier` surface — gestures, focus, semantics, `weight` / `align` extras (`verticalScroll` / `horizontalScroll` ✅ landed in #94 and exercised by **Jetchat**'s drawer panel) | Drag/drop everywhere. | -| [#64](https://github.com/jonathanpeppers/compose-net/issues/64) | Drawing primitives — `Canvas`, `drawBehind`, `Brush`, `Path`, `Shape` factories | Custom visuals in **JetLagged**. | -| [#65](https://github.com/jonathanpeppers/compose-net/issues/65) | C# value types for inline classes (`Color`, `Dp`, `Sp`, `FontWeight`, `TextAlign`, `Shape`) | Asymmetric `RoundedCornerShape(topStart, topEnd, …)` on **Jetchat** bubbles; ergonomic API everywhere. | +| [#64](https://github.com/jonathanpeppers/compose-net/issues/64) | Drawing primitives — `Canvas`, `drawBehind`, `Brush`, `Path`, `Shape` factories | Custom visuals in **JetLagged**; asymmetric `RoundedCornerShape(topStart, topEnd, …)` on **Jetchat** bubbles. | | [#69](https://github.com/jonathanpeppers/compose-net/issues/69) | WindowInsets padding modifiers (`imePadding`, `navigationBarsPadding`, `statusBarsPadding`, …) | IME-synced input row in **Jetchat**. | -| [#70](https://github.com/jonathanpeppers/compose-net/issues/70) | `Row` / `Column` `Arrangement` parameter (`Start`, `End`, `Center`, `SpaceBetween`, `SpaceAround`, `spacedBy`) | Right-aligned "me" bubbles in **Jetchat** (currently faked with `Spacer().Weight(1f)`). | +| [#59](https://github.com/jonathanpeppers/compose-net/issues/59) | `CompositionLocal` / `CompositionLocalProvider` (`LocalContext`, `LocalDensity`, `LocalContentColor`, `LocalTextStyle`, …) | Idiomatic theming and density reads across every sample. | +| [#20](https://github.com/jonathanpeppers/compose-net/issues/20) | Edge-to-edge bootstrapping | Status/nav-bar overlap on every sample. | + +Closed gaps that previously appeared here (now usable in samples): +**#51** Pager / FlowRow / FlowColumn / BoxWithConstraints / LazyStaggeredGrid, +**#53** PullToRefreshBox, **#58** Text styling + TextField config, +**#61** Theming reads + `Color` value type + parameterized `MaterialTheme`, +**#62** State primitives (`RememberSaveable` / `mutableStateListOf` / `mutableStateMapOf` / `derivedStateOf`), +**#63** Modifier surface (Background/Border/Clickable/Size/Width/Height/AspectRatio/Offset/Alpha/Clip/Weight + scroll + focus + semantics + Draggable), +**#65** Compose value types (`Color`/`Dp`/`Sp`/`FontWeight`/`TextAlign`), +**#70** Row/Column `Arrangement`. ## Attribution These samples are C# ports inspired by Google's [android/compose-samples](https://github.com/android/compose-samples), which is licensed under the [Apache License 2.0](https://github.com/android/compose-samples/blob/main/LICENSE). No upstream Kotlin source code is copied into this repo — the ports re-implement the same UI in C# against this repo's `ComposeNet.Compose` facade. The four per-author avatar PNGs in [`samples/Jetchat/Resources/drawable-nodpi/`](Jetchat/Resources/drawable-nodpi/) (`avatar_ali.png`, `avatar_aubrey.png`, `avatar_taylor.png`, `avatar_jordan.png`) were generated with [DiceBear](https://www.dicebear.com)'s `lorelei` style and are released under [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) (no attribution required, but credit appreciated). All other sample drawables and string content under each `samples//` folder are original to this repo. -| [#20](https://github.com/jonathanpeppers/compose-net/issues/20) | Edge-to-edge bootstrapping | Status/nav-bar overlap on every sample. | ## Conventions