From 51c75546fbaa322a2606f20af793a2b8ccd68bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?kusutori=E3=82=AF=E3=82=B9=E3=81=A8=E3=82=8A?= <100983934+kusutori@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:36:11 +0800 Subject: [PATCH 1/3] Add typed ResourceBuilder overloads for native styling values --- docs/_pipeline/templates/styling.md.dt | 13 ++- docs/guide/styling.md | 13 ++- .../reactor-dsl/references/reactor.api.txt | 6 + skills/reactor.api.txt | 6 + src/Reactor/Elements/ResourceBuilder.cs | 51 ++++++++- .../Fixtures/TypedResourceOverrideFixtures.cs | 105 ++++++++++++++++++ .../SelfTest/SelfTestFixtureRegistry.cs | 4 + .../Reactor.Tests/LightweightStylingTests.cs | 95 ++++++++++++++++ 8 files changed, 285 insertions(+), 8 deletions(-) create mode 100644 tests/Reactor.AppTests.Host/SelfTest/Fixtures/TypedResourceOverrideFixtures.cs diff --git a/docs/_pipeline/templates/styling.md.dt b/docs/_pipeline/templates/styling.md.dt index 4ad626662..7ce27fabb 100644 --- a/docs/_pipeline/templates/styling.md.dt +++ b/docs/_pipeline/templates/styling.md.dt @@ -206,9 +206,16 @@ pressed, disabled) automatically respect your overrides: ![Lightweight styling](screenshot://styling/lightweight-styling) -`ResourceBuilder` supports `.Set(key, ThemeRef)` for theme-reactive -overrides, `.Set(key, string)` for hex colors, `.Set(key, double)` for -numeric values, and `.Set(key, CornerRadius)` for corner radius. +`ResourceBuilder.Set` has explicit overloads for `ThemeRef`, `Brush`, +color strings, `double`, `CornerRadius`, `Thickness`, `FontWeight`, +`FontFamily`, `bool`, `Color`, and `GridLength`. Use `Thickness` for padding +or border thickness, and `FontWeight` / `FontFamily` for typography resources. +Literal values retain their types; `ThemeRef` overrides re-resolve on theme changes. + +The `Color` overload stores a color value, not a brush. The string overload +still parses a hex or named color into a brush; it does not store arbitrary +text. Integer literals continue to select the `double` overload, so pass the +value type expected by the WinUI resource key rather than relying on conversion. Overrides cascade to child elements through WinUI's resource dictionary hierarchy. diff --git a/docs/guide/styling.md b/docs/guide/styling.md index fa5bf8f82..3160d1fea 100644 --- a/docs/guide/styling.md +++ b/docs/guide/styling.md @@ -353,9 +353,16 @@ class LightweightStylingExample : Component ![Lightweight styling](images/styling/lightweight-styling.png) -`ResourceBuilder` supports `.Set(key, ThemeRef)` for theme-reactive -overrides, `.Set(key, string)` for hex colors, `.Set(key, double)` for -numeric values, and `.Set(key, CornerRadius)` for corner radius. +`ResourceBuilder.Set` has explicit overloads for `ThemeRef`, `Brush`, +color strings, `double`, `CornerRadius`, `Thickness`, `FontWeight`, +`FontFamily`, `bool`, `Color`, and `GridLength`. Use `Thickness` for padding +or border thickness, and `FontWeight` / `FontFamily` for typography resources. +Literal values retain their types; `ThemeRef` overrides re-resolve on theme changes. + +The `Color` overload stores a color value, not a brush. The string overload +still parses a hex or named color into a brush; it does not store arbitrary +text. Integer literals continue to select the `double` overload, so pass the +value type expected by the WinUI resource key rather than relying on conversion. Overrides cascade to child elements through WinUI's resource dictionary hierarchy. diff --git a/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt b/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt index fbf6b92ce..fb3c99790 100644 --- a/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt +++ b/plugins/reactor/skills/reactor-dsl/references/reactor.api.txt @@ -4901,8 +4901,14 @@ ToString() → string ### class ResourceBuilder new() Set(string key, Brush brush) → ResourceBuilder +Set(string key, Color value) → ResourceBuilder Set(string key, CornerRadius value) → ResourceBuilder +Set(string key, FontFamily value) → ResourceBuilder +Set(string key, FontWeight value) → ResourceBuilder +Set(string key, GridLength value) → ResourceBuilder Set(string key, ThemeRef themeRef) → ResourceBuilder +Set(string key, Thickness value) → ResourceBuilder +Set(string key, bool value) → ResourceBuilder Set(string key, double value) → ResourceBuilder Set(string key, string color) → ResourceBuilder diff --git a/skills/reactor.api.txt b/skills/reactor.api.txt index fbf6b92ce..fb3c99790 100644 --- a/skills/reactor.api.txt +++ b/skills/reactor.api.txt @@ -4901,8 +4901,14 @@ ToString() → string ### class ResourceBuilder new() Set(string key, Brush brush) → ResourceBuilder +Set(string key, Color value) → ResourceBuilder Set(string key, CornerRadius value) → ResourceBuilder +Set(string key, FontFamily value) → ResourceBuilder +Set(string key, FontWeight value) → ResourceBuilder +Set(string key, GridLength value) → ResourceBuilder Set(string key, ThemeRef themeRef) → ResourceBuilder +Set(string key, Thickness value) → ResourceBuilder +Set(string key, bool value) → ResourceBuilder Set(string key, double value) → ResourceBuilder Set(string key, string color) → ResourceBuilder diff --git a/src/Reactor/Elements/ResourceBuilder.cs b/src/Reactor/Elements/ResourceBuilder.cs index c45a049db..1477be76f 100644 --- a/src/Reactor/Elements/ResourceBuilder.cs +++ b/src/Reactor/Elements/ResourceBuilder.cs @@ -1,12 +1,14 @@ using Microsoft.UI.Reactor.Core; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media; +using Windows.UI; +using Windows.UI.Text; namespace Microsoft.UI.Reactor.Elements; /// /// Immutable snapshot of per-control resource overrides built by . -/// Contains both literal values (colors, doubles, corner radii) and theme-reactive +/// Contains both literal resource values and theme-reactive /// entries that are re-resolved on theme change. /// public sealed record ResourceOverrides( @@ -64,7 +66,7 @@ public ResourceBuilder Set(string key, ThemeRef themeRef) return this; } - /// Sets a numeric resource override (e.g., ButtonBorderThemeThickness). + /// Sets a numeric resource override (e.g., BreadcrumbBarItemThemeFontSize). public ResourceBuilder Set(string key, double value) { _literals[key] = value; @@ -78,6 +80,51 @@ public ResourceBuilder Set(string key, CornerRadius value) return this; } + /// Sets a thickness resource override (e.g., BreadcrumbBarChevronPadding). + public ResourceBuilder Set(string key, Thickness value) + { + _literals[key] = value; + return this; + } + + /// Sets a font weight resource override (e.g., BreadcrumbBarItemFontWeight). + public ResourceBuilder Set(string key, FontWeight value) + { + _literals[key] = value; + return this; + } + + /// Sets a font family resource override (e.g., ContentControlThemeFontFamily). + public ResourceBuilder Set(string key, FontFamily value) + { + _literals[key] = value; + return this; + } + + /// Sets a Boolean resource override (e.g., GridViewItemSelectionCheckMarkVisualEnabled). + public ResourceBuilder Set(string key, bool value) + { + _literals[key] = value; + return this; + } + + /// + /// Sets a literal color resource override, not a brush. Use the string or + /// overload for resources that expect a brush. + /// + public ResourceBuilder Set(string key, Color value) + { + _literals[key] = value; + return this; + } + + /// Sets a grid length resource override (e.g., ContentDialogButtonSpacing). + public ResourceBuilder Set(string key, GridLength value) + { + _literals[key] = value; + return this; + } + /// Builds an immutable snapshot. internal ResourceOverrides Build() { diff --git a/tests/Reactor.AppTests.Host/SelfTest/Fixtures/TypedResourceOverrideFixtures.cs b/tests/Reactor.AppTests.Host/SelfTest/Fixtures/TypedResourceOverrideFixtures.cs new file mode 100644 index 000000000..857948ed3 --- /dev/null +++ b/tests/Reactor.AppTests.Host/SelfTest/Fixtures/TypedResourceOverrideFixtures.cs @@ -0,0 +1,105 @@ +using Microsoft.UI.Reactor.Core; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Media; +using static Microsoft.UI.Reactor.Factories; + +namespace Microsoft.UI.Reactor.AppTests.Host.SelfTest.Fixtures; + +internal static class TypedResourceOverrideFixtures +{ + internal sealed class MountUpdateAndRemoval(Harness h) : SelfTestFixtureBase(h) + { + public override async Task RunAsync() + { + var family = new FontFamily("Consolas"); + var updatedFamily = new FontFamily("Segoe UI"); + Action? setPhase = null; + var host = H.CreateHost(); + host.Mount(ctx => + { + var (phase, update) = ctx.UseState(0); + setPhase = update; + var element = Button("typed resource overrides"); + if (phase < 2) + element = element.Resources(r => r + .Set("TypedPadding", Padding(phase)) + .Set("TypedWeight", new global::Windows.UI.Text.FontWeight((ushort)(phase == 0 ? 700 : 400))) + .Set("TypedFont", phase == 0 ? family : updatedFamily) + .Set("TypedBoolean", phase == 0) + .Set("TypedColor", Color(phase)) + .Set("TypedGridLength", Length(phase)) + .Set("TypedBrush", "#123456")); + return element; + }); + + await Harness.Render(); + var control = H.FindControl