Fill remaining MAUI property-mapper keys#277
Open
jonathanpeppers wants to merge 3 commits into
Open
Conversation
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>
There was a problem hiding this comment.
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.RefreshColorby introducing a hand-writtenColoredIndicatorBoxComposableNodethat calls the boundPullToRefreshKt.PullToRefreshBoxdirectly with a custom indicator slot piping the packed color throughPullToRefreshDefaults.Indicator. Adds[InternalsVisibleTo("Microsoft.AndroidX.Compose.Maui")]to expose internal helpers likeComposableLambdas.Wrap3,ComposableLambda0, andModifier.Build(). - Wires
ISlider.ThumbImageSourcethrough the sharedImageSourceLoadervia aSliderThumbImageSourcePartadapter, with anImageThumbSliderComposableNodethat calls the lower-levelSliderKt.Slider(..., thumb = {...})overload directly to render a custom thumb image. - Sharpens TODO comments for
ScrollViewHandler.{Horizontal,Vertical}ScrollBarVisibilityandImageHandler/ImageButtonHandler.IsAnimationPlayingwith 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.RefreshColorto the Material 3PullToRefreshDefaults.Indicatorspinner colour:SolidPaint) keeps the existingComposePullToRefreshBoxfacade path.ColoredIndicatorBoxComposableNodethat callsPullToRefreshKt.PullToRefreshBoxdirectly with a custom indicatorIFunction3invokingPullToRefreshDefaults.Instance.Indicator(...)with the packed colour.[InternalsVisibleTo("Microsoft.AndroidX.Compose.Maui")]to the Compose facade assembly so the Maui project can reachComposableLambdas,ComposableLambda0, andBuildModifier.Sample:
RefreshPage.xamlnow shipsRefreshColor="#FF6B35".✅
SliderHandler— now 38 / 38 (100%)Wires
ISlider.ThumbImageSourcethrough the sharedImageSourceLoader:SliderThumbImageSourcePartadapter wrapsISlideras anIImageSourcePart(the slider itself doesn't implement it — onlyIImage/IImageButtondo).BuildNodeswaps to a hand-writtenImageThumbSliderComposableNodecallingSliderKt.Slider(value, onValueChange, ..., thumb = { ... })directly with aComposeImageof the resolved source in the thumb slot. Material 3's stockSliderdraws 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.xamlnow ships a third slider withThumbImageSource="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/scrollbarAPI on Android. Theandroidx.compose.foundation.v2.scrollbarextension exists only on the Multiplatform desktop target;Modifier.verticalScroll/horizontalScrollhave no scrollbar-visibility flag at all. Honouring even theDefault/Always/Neverenum requires a hand-builtScrollState+ animated overlay drawn into a siblingBox— 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%)ImageSourceLoaderrasterises the resolvedDrawableto a staticBitmap(viaDrawableKt.ToBitmap) before wrapping it as aBitmapPainter. The Android-nativeAnimatedImageDrawable(API 28+) keeps animating, but the snapshot we hand to Compose doesn't — its first frame is what gets sampled. Fixing this needs either:DrawablePainterJCW subclass ofandroidx.compose.ui.graphics.painter.PainterwhoseDrawScope.onDrawforwards intoDrawable.draw(Canvas)and togglesstart()/stop()based on the slot, orAsyncImagePainter.Both are larger surgery + a real new dependency. TODO comments refined to spell this out.
Headline coverage
Per-handler delta:
RefreshViewHandler34/35 → 35/35,SliderHandler37/38 → 38/38, both moving from 🟡 to ✅.Verifying
Each commit is independently revertable:
Wire RefreshColor on RefreshViewHandler,Wire ThumbImageSource on SliderHandler,Sharpen TODO reasons for unmapped handler keys.