Wire RadioButton stroke color (and document corner-radius/thickness limits)#276
Open
jonathanpeppers wants to merge 1 commit into
Open
Wire RadioButton stroke color (and document corner-radius/thickness limits)#276jonathanpeppers wants to merge 1 commit into
jonathanpeppers wants to merge 1 commit into
Conversation
…imits)
Routes `IButtonStroke.StrokeColor` (XAML's `BorderColor` on
`Microsoft.Maui.Controls.RadioButton`) through the Compose Material 3
`RadioButtonColors` ring slots, and explicitly holds back
`CornerRadius` / `StrokeThickness` with a rationale comment.
What's wired:
- `ComposeBridges.RadioButton` accepts `RadioButtonColors? colors = null`
and forwards to `RadioButtonKt.RadioButton(colors: ...)`.
- New `composer.RadioButtonColors(...)` extension factory on
`IComposer` mirrors `CheckboxColors` / `SwitchColors` — calls the
parameterless `RadioButtonDefaults.colors(composer, _changed)` and
threads non-null overrides through `RadioButtonColors.copy(...)`
(the only four-long entry point that survives the binder strip).
- `RadioButtonColors` added to the source-generator reference-type
allow-list so `[ComposeFacade]` recognises it as an OptionalValue
slot (auto-property + auto-mask).
- `RadioButtonHandler` adds `[nameof(IButtonStroke.StrokeColor)] =
MapStrokeColor` and applies the colour to both `selectedColor` and
`unselectedColor` so the user's chosen colour is visible regardless
of check state — matches stock MAUI's `UpdateStrokeColor` semantic.
What's held back (with rationale in the handler's mapper dictionary):
- `IButtonStroke.StrokeThickness` and `CornerRadius`. Material 3's
`RadioButton` draws a fixed 20.dp circle whose ring thickness and
shape are baked into the composable's internal Canvas (`drawCircle`
with a hard-coded `RadioStrokeWidth = 2.dp`); the public surface
(`RadioButtonColors`) only exposes the ring colour, not its
geometry. Honouring these would require replacing
`RadioButtonKt.RadioButton` with a hand-rolled
`Canvas { drawCircle / drawArc }` composable, which loses Material
3's ripple, state-layer, focus indicator, and accessibility chrome.
XAML callers reaching for `CornerRadius` on a radio button are
typically trying to make a control that is no longer recognisable
as a radio button.
Coverage:
- `RadioButtonHandler`: 36/39 (92%) -> 37/39 (95%). Missing keys
drop from {CornerRadius, StrokeColor, StrokeThickness} to
{CornerRadius, StrokeThickness}.
- Repo-wide property-mapper coverage: 884/1224 (72.2%).
Sample:
- TogglesPage adds a fourth `<RadioButton BorderColor="#D32F2F">`
(Dragonfruit) so the change is visible on device.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR wires IButtonStroke.StrokeColor (MAUI's BorderColor on RadioButton) through Compose Material 3's RadioButtonColors ring slots, following the same colors-factory-and-copy pattern already established for CheckboxColors and SwitchColors. It also explicitly documents why StrokeThickness and CornerRadius are intentionally held back (Material 3's RadioButton composable hardcodes its ring geometry).
Changes:
- Adds
ComposeExtensions.RadioButtonColors(...)factory onIComposerthat callsRadioButtonDefaults.Instance.Colors(composer, 0)and applies overrides viaRadioButtonColors.copy(...), plus registersRadioButtonColorsin the source-generator's reference-type allow-list so[ComposeFacade]recognizes it as anOptionalValueslot. - Updates
ComposeBridges.RadioButtonto accept and forwardRadioButtonColors? colors = nullto the underlyingRadioButtonKt.RadioButtoncall. - Adds
MapStrokeColorinRadioButtonHandlerto convertIButtonStroke.StrokeColorto a packedlong?and apply it to bothselectedColorandunselectedColorring slots so the user-chosen color is visible in all check states.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
src/Microsoft.AndroidX.Compose/ComposeExtensions.RadioButtonDefaults.cs |
New IComposer.RadioButtonColors(...) extension mirroring RadioButtonDefaults.colors(), using the theme-defaults-then-copy pattern from CheckboxColors/SwitchColors. |
src/Microsoft.AndroidX.Compose/ComposeBridges.cs |
RadioButton bridge gains RadioButtonColors? colors = null parameter forwarded to the bound RadioButtonKt.RadioButton. |
src/Microsoft.AndroidX.Compose.SourceGenerators/ComposeReferenceTypes.cs |
Adds RadioButtonColors to the recognized reference-type list for [ComposeFacade] auto-property generation. |
src/Microsoft.AndroidX.Compose.Maui/Handlers/RadioButtonHandler.cs |
Adds MutableState<long?> _strokeColor, MapStrokeColor mapper, and BuildNode wiring that builds RadioButtonColors when stroke is set. Expands TODO comment for held-back properties. |
src/Microsoft.AndroidX.Compose/PublicAPI.Unshipped.txt |
Registers new RadioButton.Colors property and ComposeExtensions.RadioButtonColors(...) method. |
src/Microsoft.AndroidX.Compose.Maui.Sample/Pages/TogglesPage.xaml |
Adds a "Dragonfruit" RadioButton with BorderColor="#D32F2F" for on-device visual verification. |
docs/maui-coverage.md |
Regenerated: RadioButtonHandler coverage moves from 36/39 (92%) to 37/39 (95%); StrokeColor marked covered. |
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.
Routes
IButtonStroke.StrokeColor(XAML'sBorderColoronMicrosoft.Maui.Controls.RadioButton) through Compose Material 3'sRadioButtonColorsring slots, and explicitly holds backCornerRadius/StrokeThicknesswith a rationale comment.What's wired
ComposeBridges.RadioButtonnow acceptsRadioButtonColors? colors = nulland forwards toRadioButtonKt.RadioButton(colors: ...).composer.RadioButtonColors(...)extension factory onIComposermirrorsCheckboxColors/SwitchColors— calls the parameterlessRadioButtonDefaults.colors(composer, _changed)and threads non-null overrides throughRadioButtonColors.copy(...)(the only four-longentry point that survives the binder strip from@JvmInline value class Color).RadioButtonColorsadded to the source-generator reference-type allow-list (ComposeReferenceTypes.cs) so[ComposeFacade]recognises it as anOptionalValueslot (auto-property + auto-mask). The generatedRadioButton.Colors { get; set; }becomes a normal facade property.RadioButtonHandleradds[nameof(IButtonStroke.StrokeColor)] = MapStrokeColorand applies the colour to bothselectedColorandunselectedColorso the user's chosen colour is visible regardless of check state — matches stock MAUI'sUpdateStrokeColorsemantic, which tints the ring drawable in every check state.What's held back (intentionally)
IButtonStroke.StrokeThicknessandCornerRadiusare documented in the handler's mapper dictionary as deliberately unimplemented. Rationale (paraphrased from the new TODO block inRadioButtonHandler.cs):Material 3's
RadioButtondraws a fixed 20.dp circle whose ring thickness and shape are baked into the composable's internalCanvas(drawCirclewith a hard-codedRadioStrokeWidth = 2.dp); the public surface (RadioButtonColors) only exposes the ring colour, not its geometry. Honouring these would require replacing the call toRadioButtonKt.RadioButtonwith a hand-rolledCanvas { drawCircle / drawArc }composable, which loses M3's ripple, state-layer, focus indicator, and accessibility chrome. ACornerRadiusoverride is also semantically off — XAML callers reaching for it on aRadioButtonare typically trying to make a control that is no longer recognisable as a radio button.Coverage
RadioButtonHandler: 36/39 (92%) → 37/39 (95%). Missing-key list shrinks from{CornerRadius, StrokeColor, StrokeThickness}to{CornerRadius, StrokeThickness}.Repo-wide property-mapper coverage stays at 884/1224 (72.2%) since the new
StrokeColorslot replaces an already-covered key in the regenerateddocs/maui-coverage.md.Sample
TogglesPage.xamlgains a fourth<RadioButton BorderColor="#D32F2F">(Dragonfruit) in the existing radio group, so the change is visually verifiable on device.Verified
dotnet build src/Microsoft.AndroidX.Compose✅ (0 errors)dotnet test src/Microsoft.AndroidX.Compose.SourceGenerators.Tests✅ (155/155 pass)dotnet build src/Microsoft.AndroidX.Compose.Maui✅ (0 errors)dotnet build src/Microsoft.AndroidX.Compose.Maui.Sample✅ (0 errors)dotnet run scripts/maui-coverage.csregeneratesdocs/maui-coverage.mdcleanly.