Skip to content

Fill remaining MAUI property-mapper keys#277

Open
jonathanpeppers wants to merge 3 commits into
mainfrom
jonathanpeppers/misc-handler-keys
Open

Fill remaining MAUI property-mapper keys#277
jonathanpeppers wants to merge 3 commits into
mainfrom
jonathanpeppers/misc-handler-keys

Conversation

@jonathanpeppers

Copy link
Copy Markdown
Owner

Wires up two of the five short-list missing property-mapper keys on the .NET MAUI ⇄ Compose backend and sharpens the inline TODOs for the three remaining keys that genuinely can't be wired without major new work or a new dependency.

Per-handler changes

RefreshViewHandler — now 35 / 35 (100%)

Wires IRefreshView.RefreshColor to the Material 3 PullToRefreshDefaults.Indicator spinner colour:

  • Null (or non-SolidPaint) keeps the existing ComposePullToRefreshBox facade path.
  • A non-null solid colour switches to a hand-written ColoredIndicatorBox ComposableNode that calls PullToRefreshKt.PullToRefreshBox directly with a custom indicator IFunction3 invoking PullToRefreshDefaults.Instance.Indicator(...) with the packed colour.
  • Adds [InternalsVisibleTo("Microsoft.AndroidX.Compose.Maui")] to the Compose facade assembly so the Maui project can reach ComposableLambdas, ComposableLambda0, and BuildModifier.

Sample: RefreshPage.xaml now ships RefreshColor="#FF6B35".

SliderHandler — now 38 / 38 (100%)

Wires ISlider.ThumbImageSource through the shared ImageSourceLoader:

  • Small SliderThumbImageSourcePart adapter wraps ISlider as an IImageSourcePart (the slider itself doesn't implement it — only IImage / IImageButton do).
  • When the loader resolves a painter or drawable id, BuildNode swaps to a hand-written ImageThumbSlider ComposableNode calling SliderKt.Slider(value, onValueChange, ..., thumb = { ... }) directly with a ComposeImage of the resolved source in the thumb slot. Material 3's stock Slider draws its thumb as a fixed circle and has no public way to inject a custom drawable, so the lower-level overload is required.

Sample: SlidersPage.xaml now ships a third slider with ThumbImageSource="dotnet_bot.png".

Held back with sharpened TODO reasons

🟡 ScrollViewHandler.{Horizontal,Vertical}ScrollBarVisibility — still 33 / 35 (94%)

Compose Foundation 1.9 / 1.10 / 1.11 (and the Material3 layer on top) don't expose any public Scrollbar / scrollbar API on Android. The androidx.compose.foundation.v2.scrollbar extension exists only on the Multiplatform desktop target; Modifier.verticalScroll / horizontalScroll have no scrollbar-visibility flag at all. Honouring even the Default / Always / Never enum requires a hand-built ScrollState + animated overlay drawn into a sibling Box — meaningful surgery and a non-trivial new public surface. TODO comment refined to spell this out.

🟡 ImageHandler.IsAnimationPlaying / ImageButtonHandler.IsAnimationPlaying — still 33 / 34 (97%) and 37 / 38 (97%)

ImageSourceLoader rasterises the resolved Drawable to a static Bitmap (via DrawableKt.ToBitmap) before wrapping it as a BitmapPainter. The Android-native AnimatedImageDrawable (API 28+) keeps animating, but the snapshot we hand to Compose doesn't — its first frame is what gets sampled. Fixing this needs either:

  • a hand-rolled DrawablePainter JCW subclass of androidx.compose.ui.graphics.painter.Painter whose DrawScope.onDraw forwards into Drawable.draw(Canvas) and toggles start() / stop() based on the slot, or
  • routing source resolution through coil-compose's AsyncImagePainter.

Both are larger surgery + a real new dependency. TODO comments refined to spell this out.

Headline coverage

Metric Before After
Property-mapper keys covered 883 / 1224 (72.1%) 885 / 1224 (72.3%)
Containers category 165 / 174 (95%) 167 / 174 (96%)
Leaves category 683 / 695 (98%) 685 / 695 (99%)
Handlers at ✅ 100% (before: RefreshView 97%, Slider 97% — both 🟡) RefreshView and Slider now

Per-handler delta: RefreshViewHandler 34/35 → 35/35, SliderHandler 37/38 → 38/38, both moving from 🟡 to ✅.

Verifying

dotnet build src/Microsoft.AndroidX.Compose.Maui                    # facade only
dotnet build src/Microsoft.AndroidX.Compose.Maui.Sample             # full sample, deploys gallery
dotnet run scripts/maui-coverage.cs                                 # regenerate docs/maui-coverage.md

Each commit is independently revertable: Wire RefreshColor on RefreshViewHandler, Wire ThumbImageSource on SliderHandler, Sharpen TODO reasons for unmapped handler keys.

jonathanpeppers and others added 3 commits June 15, 2026 18:09
Maps Microsoft.Maui.Graphics.SolidPaint on IRefreshView.RefreshColor
to the Compose Material 3 PullToRefreshDefaults.Indicator spinner
color. Null (or non-solid) RefreshColor keeps the existing
ComposePullToRefreshBox facade path; a non-null solid color switches
to a hand-written ColoredIndicatorBox ComposableNode that calls
PullToRefreshKt.PullToRefreshBox directly with a custom indicator
IFunction3 invoking PullToRefreshDefaults.Instance.Indicator(...)
with the packed color.

Adds [InternalsVisibleTo("Microsoft.AndroidX.Compose.Maui")] to the
Compose facade assembly so the Maui project can reach the internal
ComposableLambdas/ComposableLambda0/BuildModifier helpers needed by
ColoredIndicatorBox.

RefreshViewHandler is now at 35 / 35 (100%) per docs/maui-coverage.md.

The sample's RefreshPage now ships RefreshColor="#FF6B35" so the
behaviour is exercised on-device.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Maps ISlider.ThumbImageSource through the shared ImageSourceLoader
(same helper backing ImageHandler / ImageButtonHandler). A small
SliderThumbImageSourcePart adapter exposes the slider as an
IImageSourcePart so the loader's standard MAUI pipeline resolves the
source to either a drawable id (fast path) or a runtime-decoded
BitmapPainter (general path).

When the loader resolves a painter or drawable id, BuildNode swaps to
a hand-written ImageThumbSlider ComposableNode that calls the
lower-level SliderKt.Slider(value, onValueChange, ..., thumb=...)
overload directly with an Image of the resolved source in the thumb
slot. Material 3's stock Slider draws its thumb as a fixed circle and
has no public way to inject a custom drawable, so we have to go
through the bound overload rather than the existing Slider facade.

SliderHandler is now at 38 / 38 (100%) per docs/maui-coverage.md.

The sample's SlidersPage now ships a third slider with
ThumbImageSource="dotnet_bot.png" so the path is exercised on-device.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rewrites the inline TODOs for the three properties that genuinely
can't be wired without major new work, so a future contributor sees
the concrete blocker instead of a one-liner:

* ScrollViewHandler.{Horizontal,Vertical}ScrollBarVisibility —
  Compose Foundation 1.9 / 1.10 / 1.11 has no public Scrollbar API
  on Android (the androidx.compose.foundation.v2.scrollbar extension
  is desktop-only). Implementing the Default/Always/Never enum needs
  a hand-built ScrollState + animated overlay drawn into a sibling
  Box.

* ImageHandler.IsAnimationPlaying /
  ImageButtonHandler.IsAnimationPlaying — ImageSourceLoader
  rasterises the resolved Drawable into a Bitmap before wrapping as a
  BitmapPainter, so even AnimatedImageDrawable sources don't animate
  through our Compose path. Fixing it needs either a hand-rolled
  DrawablePainter JCW (forward DrawScope.onDraw -> Drawable.draw +
  gate start/stop on the slot) or routing source resolution through
  coil-compose.

No behaviour change.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 15, 2026 23:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fills two of the five remaining unmapped MAUI property-mapper keys on the Compose backend and refines the inline TODO comments for the three keys that cannot be wired without major new work or dependencies.

Changes:

  • Wires IRefreshView.RefreshColor by introducing a hand-written ColoredIndicatorBox ComposableNode that calls the bound PullToRefreshKt.PullToRefreshBox directly with a custom indicator slot piping the packed color through PullToRefreshDefaults.Indicator. Adds [InternalsVisibleTo("Microsoft.AndroidX.Compose.Maui")] to expose internal helpers like ComposableLambdas.Wrap3, ComposableLambda0, and Modifier.Build().
  • Wires ISlider.ThumbImageSource through the shared ImageSourceLoader via a SliderThumbImageSourcePart adapter, with an ImageThumbSlider ComposableNode that calls the lower-level SliderKt.Slider(..., thumb = {...}) overload directly to render a custom thumb image.
  • Sharpens TODO comments for ScrollViewHandler.{Horizontal,Vertical}ScrollBarVisibility and ImageHandler/ImageButtonHandler.IsAnimationPlaying with concrete technical reasons why they can't be wired yet.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/Microsoft.AndroidX.Compose/AssemblyInfo.cs New [InternalsVisibleTo("Microsoft.AndroidX.Compose.Maui")] to expose internal helpers to the MAUI handler assembly.
src/Microsoft.AndroidX.Compose.Maui/Handlers/RefreshViewHandler.cs Adds MapRefreshColor mapper, _refreshColor state, and ColoredIndicatorBox inner class for custom-color spinner rendering.
src/Microsoft.AndroidX.Compose.Maui/Handlers/SliderHandler.cs Adds MapThumbImageSource mapper, lazy ImageSourceLoader, SliderThumbImageSourcePart adapter, and ImageThumbSlider inner class for custom thumb image rendering.
src/Microsoft.AndroidX.Compose.Maui/Handlers/ScrollViewHandler.cs Refines TODO comment explaining why scrollbar visibility can't be wired (no public Scrollbar API on Android Compose).
src/Microsoft.AndroidX.Compose.Maui/Handlers/ImageHandler.cs Refines TODO comment explaining why IsAnimationPlaying can't be wired (static BitmapPainter, needs DrawablePainter or coil-compose).
src/Microsoft.AndroidX.Compose.Maui/Handlers/ImageButtonHandler.cs Same TODO refinement as ImageHandler for IsAnimationPlaying.
src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/SlidersPage.xaml Adds a third slider demo with ThumbImageSource="dotnet_bot.png".
src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/SlidersPage.xaml.cs Adds value-change handler and reset logic for the thumb-image slider.
src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/RefreshPage.xaml Adds RefreshColor="#FF6B35" and updates labels to describe the new coverage.
docs/maui-coverage.md Auto-generated coverage report reflecting the two new mappings (883→885 keys, RefreshView and Slider now ✅).

Comment on lines +298 to +308
PullToRefreshKt.PullToRefreshBox(
isRefreshing: isRefreshing,
onRefresh: onRefresh,
modifier: modifier.Build(),
state: state,
contentAlignment: null,
indicator: indicator,
content: content,
_composer: composer,
p8: boxMask,
_changed: 0);
Comment on lines +266 to +275
PullToRefreshDefaults.Instance.Indicator(
state: state,
isRefreshing: isRefreshing,
modifier: null,
containerColor: 0L,
color: _packedColor,
maxDistance: 0f,
_composer: c,
p7: indicatorMask,
_changed: 0);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants