Repository navigation
CmdPal: Fluent, theme-aware host config for Adaptive Cards - #51205
Conversation
Build the Adaptive Cards host config at runtime from theme tokens instead of two hard-coded JSON strings. Sizes and spacing follow the Fluent type ramp and 4px grid, the fonts are Segoe UI Variable and Cascadia Mono, the colors follow the Fluent theme resources and the system accent, and high contrast uses the system colors. Equal configs share one cached instance. ContentFormControl re-renders its card when the theme changes, or when it loads into a window with a different theme, replacing the theme TODO. Split out of #51125.
🧭 PR intakeVisual evidence: Not needed — The changed files do not indicate a visible UI change. Visual evidence was detected in the PR description. Recommendation
✅ Ready for reviewThis PR passed the automated intake checks and is ready for maintainer review. Automated PR intake; PowerToys maintainers make final decisions. |
|
The most recent pair of commits here (https://github.com/microsoft/PowerToys/pull/51205/changes/477afa0b8da02baf050a09a72086202e97547949..6aa86dae14898b5b1a67ea844381fd97ecb248d2) is kinda a big delta. Is this something that makes sense just to have as a separate commit to Like, are there other situations where the card might re-render and inputs would be lost? It just doesn't make sense to me to overload this PR with what feels like a totally secondary fix. |
|
Happy to make it a new pr and revert. You caught a legit bug. |
6aa86da to
477afa0
Compare
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request **Layer 2 of 13 in a stack that splits #51125** (native Adaptive Cards charts and redesigned Performance Monitor cards) into reviewable PRs. See #51125 for the full picture. This PR targets layer 1's branch (#51204), so its diff shows only this layer. It adds `Microsoft.CmdPal.AdaptiveCards.Polyfills`, a library that renders Adaptive Cards schema elements the WinUI 3 renderer doesn't support yet, with its first element, `Chart.Line`: - Smooth, filled lines with gridlines, axis titles, a legend, and a marker on the latest point. - New data updates the chart in place, through layer 1's patchable elements. When live data gains one sample, the lines scroll left instead of the card being rebuilt. - Narrower than 400 px, the chart draws as a sparkline, as in a dashboard tile. - Colors come from the schema's color names, in light and dark variants. In a contrast theme, series take the theme's colors and also differ by dash pattern. Screen readers get the chart's name and a summary of each series. Any extension can send `Chart.Line` as standard Adaptive Cards JSON. Today the renderer drops it. Nothing in Command Palette uses it yet; the Performance Monitor cards start in layer 5. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [ ] Closes: N/A. There's no issue; this is layer 2 of 13 splitting #51125 <!-- - [ ] Closes: #yyy (add separate lines for additional resolved issues) --> - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [x] **Localization:** All end-user-facing strings can be localized (7 new strings in Command Palette's `Resources.resw`; the library gets them from the host) - [x] **Dev docs:** Added/updated (the library's `README.md`) - [x] **New binaries:** Added on the required places. `Microsoft.CmdPal.AdaptiveCards.Polyfills.dll` ships inside Command Palette's MSIX, which is signed as a whole, like `Microsoft.CmdPal.AdaptiveCards.IncrementalRendering.dll`, so it only needs the solution entries. - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries (not needed: the MSIX is signed) - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder (not needed: it's in the MSIX) - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects (not needed: CI runs every `**\*UnitTest*.dll`) - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) (not needed) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments ### The library - **It fills gaps in the WinUI 3 renderer without a fork.** Elements are registered through the renderer's public custom element APIs: a parser in the element parser registration and a renderer in `ElementRenderers`. - **Nothing in it is specific to Command Palette.** Each element can move to the renderer in [microsoft/AdaptiveCards](https://github.com/microsoft/AdaptiveCards) and be deleted here. The README's "Upstream" section covers what moving one takes. - **It has no XAML or resources.** Controls are built in code, and the host passes its strings in: `RegisterRenderers(renderer, getString)` takes a lookup for the `AdaptiveChart_*` strings in Command Palette's `Resources.resw`. - **Each element is a model and a control.** Models parse the element JSON leniently (invalid values read as missing, with a warning) and don't use WinUI, so the unit tests cover them without a window. Models read only the properties on the element's page in the [Adaptive Cards element reference](https://adaptivecards.microsoft.com/). ### Wiring it in - `ContentFormControl` calls `AdaptiveCardPolyfills.RegisterParsers` and `RegisterRenderers`, and passes `AdaptiveCardPolyfills.PatchableElements` to the incremental updater. - `AdaptiveCustomElementJson` moves from `Microsoft.CmdPal.UI` into the library and becomes public, so the settings inputs and the new elements read the properties every element has (`id`, `isVisible`, `spacing`, `fallback`, `requires`, and so on) the same way. Its new optional `requireId` parameter defaults to `true`; charts don't need an `id`. The settings inputs don't change. ### The fallback workaround When a custom element renderer asks the WinUI 3 renderer to draw the element's `fallback`, `XamlBuilder::RenderAsUIElement` renders the fallback but returns an empty result, so nothing shows. `AdaptiveFallbackRenderer` works around that bug: the element renderer draws the fallback itself and returns it. `Chart.Line` always draws itself, so nothing uses this yet; `Icon` does in layer 3, for icon names without a glyph. ### Shared code for the next layers The theme, palette, scale, curve, shape, and number formatting code lands here whole, with its tests, so the later layers only add elements. Some of it isn't used by `Chart.Line`: - `ChartArc` and the arc, wedge, and ring helpers in `ChartShapes`: gauge and donut charts (layer 10). - `ChartDataPoint` and `ChartJson.GetDataPoints`: bar charts (layer 4) and gauge and donut charts. - The track, semantic, and contrasting text colors in `ChartTheme`, and high contrast outlines in `ChartPalette`: progress bars, badges, and icons (layer 3), bar charts, and gauge and donut charts. - The `AdaptiveVisualControl` base class and `AdaptiveFallbackRenderer`: layer 3 on. `LineChartControl` implements its own updates, so that it can animate a one-sample shift as a scroll. ### Screenshots Live line charts, captured with a local card harness (not in the repo): an unpackaged WinUI app that renders inline `Chart.Line` cards through the real renderer, this library, and `IncrementalAdaptiveCardUpdater`, at the dock window's content width (776 px), with `main`'s host config and data that gains one sample a second. | | Dark | Light | |---|---|---| | One series |  |  | | Two series |  |  | | Narrow columns, drawn as sparklines |  |  | The two-series chart in a simulated Aquatic contrast theme (the harness applies the theme's colors to the charts only). The series take the theme's colors and differ by dash pattern, which the legend shows:  <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed Every library and test file is identical to its version at #51125's head (`8858a9e0a7`), except `AdaptiveCardPolyfills.cs` and `README.md`, which are cut down to `Chart.Line`. In `Microsoft.CmdPal.UI`, `ContentFormControl.xaml.cs` takes only the polyfills lines; the host config lines are in #51205. ARM64 Debug builds, run from a `subst` drive (common build options: `-nologo -m -restore -t:Build -v:minimal -p:Configuration=Debug -p:Platform=ARM64 -p:RuntimeIdentifier=win-arm64 -p:CIBuild=false -p:GeneratePackageLocally=false -p:GenerateAppxPackageOnBuild=false -p:PublishAppxPackage=false -p:SkipLongPathsCheck=true`): - `MSBuild.exe src\modules\cmdpal\Tests\Microsoft.CmdPal.AdaptiveCards.Polyfills.UnitTests\Microsoft.CmdPal.AdaptiveCards.Polyfills.UnitTests.csproj <options>`: exit code 0, no warnings. It builds the library, which is `IsAotCompatible`, so the trim and AOT analyzers run. - `vstest.console.arm64.exe ARM64\Debug\WinUI3Apps\CmdPal\tests\Microsoft.CmdPal.AdaptiveCards.Polyfills.UnitTests.dll /Platform:ARM64`: 81 passed, 0 failed. - `MSBuild.exe src\modules\cmdpal\Microsoft.CmdPal.UI\Microsoft.CmdPal.UI.csproj <options>`: exit code 0. No warnings from C# projects; the only warnings are this machine's missing `NETFXSDK` LIB path (CS1668) and LNK4075 in the C++ projects, which this PR doesn't touch. - `MSBuild.exe src\modules\cmdpal\Tests\Microsoft.CmdPal.UI.UnitTests\Microsoft.CmdPal.UI.UnitTests.csproj <options>`: exit code 0, no warnings. - `vstest.console.arm64.exe ARM64\Debug\WinUI3Apps\CmdPal\tests\Microsoft.CmdPal.UI.UnitTests.dll /Platform:ARM64`: 715 passed, 0 failed. - `.pipelines\verifyCommonProps.ps1 -sourceDir <worktree>\src`: exit code 0. - Spelling: the repo's check-spelling workflow is disabled, so I ran a local approximation of it (its dictionaries plus `expect.txt`) over this PR's diff: no unknown words and no forbidden patterns. `sparkline` is added to `expect.txt`. The unit tests run under JIT, and release builds compile Command Palette with native AOT, so I also ran the card harness both ways. Each run renders the three cards in dark and light, updates them once a second, and counts the updates that replaced the whole card instead of patching the chart: | Harness build | Updates per card | Updates that replaced the whole card | |---|---|---| | JIT, library sources compiled in | 8 | 0 | | Native AOT (`PublishAot`, optimized), referencing the library assembly | 10 | 0 | The native AOT publish had no trim or AOT warnings, and its screenshots match the JIT ones.
…iles (#51209) <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request **Layer 3 of 13 in a stack that splits #51125** (native Adaptive Cards charts and redesigned Performance Monitor cards) into reviewable PRs. See #51125 for the full picture. This layer is based on layer 2 (`crutkas-stack-2-polyfills-chart-line`, the polyfills library with `Chart.Line`), so the diff shows only this layer. The WinUI 3 Adaptive Cards renderer drops elements it doesn't know, even with a `fallback`, and ignores container properties it doesn't know. This layer adds four more schema features to the polyfills library. Every redesigned Performance Monitor card in the later layers uses at least one of them. - **`ProgressBar`**, drawn with the native WinUI progress bar, so value changes animate and screen readers read the value. Without a `value`, it's indeterminate. - **`Badge`**, with the schema's `style`, `appearance`, `shape`, `size`, `icon`, `iconPosition`, and `tooltip`. - **`Icon`**, which maps Fluent System Icons names (the catalog Adaptive Cards uses) to Segoe Fluent Icons glyphs, the font Command Palette already uses. A name without a matching glyph draws the element's `fallback`. - **`roundedCorners` and `showBorder`** on containers, column sets, and columns. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [ ] Closes: N/A. There's no issue; this is layer 3 of 13 splitting #51125 <!-- - [ ] Closes: #yyy (add separate lines for additional resolved issues) --> - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [x] **Localization:** All end-user-facing strings can be localized (no new strings) - [x] **Dev docs:** Added/updated (the library's `README.md`) - [ ] **New binaries:** Added on the required places (N/A, no new binaries) - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments ### Elements Each element follows layer 2's pattern: a model that parses the element JSON leniently (invalid values read as missing and add a warning), and an `AdaptiveVisualControl` that draws it. All three are in `AdaptiveCardPolyfills`' element list, so they're registered as parsers, renderers, and `PatchableElements`, and data changes update them in place. - **`ProgressBarModel`, `ProgressBarControl`:** `max` defaults to 100; a `max` of zero or less warns and uses 100. The value is clamped to `0..max`. `color` takes the schema's accent (the default), good, warning, and attention. - **`BadgeModel`, `BadgeControl`:** the schema's defaults are filled, circular, and medium. A badge is neutral unless `style` names a semantic color. Filled badges use the color as the background, with contrasting text; tint badges use a translucent background, a border, and colored text. An icon-only badge is named by its `tooltip` for screen readers. An `icon` without a glyph is left out, with a warning. - **`IconModel`, `IconControl`, `FluentIconGlyphs`:** 130 names, each mapped to the Segoe Fluent Icons glyph that draws the same symbol, with the filled glyph where one exists. Sizes follow the Fluent icon sizes, from 12 to 48 px. Colors resolve like the host config's text colors; in high contrast, they use the contrast theme's colors. The schema gives icons no text alternative, so they're hidden from screen readers. - **Icons without a glyph:** `IconModel.HasGlyph` is the element type's `RendersItself` check, so the incremental updater knows that such an icon draws its fallback (layer 1's `IncrementalPatchableElements.Add(type, rendersItself)`), and layer 2's `AdaptiveFallbackRenderer` draws it. ### Rounded and bordered containers `AdaptiveContainerDecoratorRenderer` wraps the `Container`, `ColumnSet`, and `Column` renderers: whichever is registered, or the built-in one. After the inner renderer draws the element, it reads `roundedCorners` and `showBorder` from the element's additional properties, and applies a style to the element's background `Border`, including when a `selectAction` wraps it in a button. The styles are in `CardOverrideStyles` in `ContentFormControl.xaml`, so the host owns the look: - `CmdPal.Adaptive.Container.Rounded`: `OverlayCornerRadius`. - `CmdPal.Adaptive.Container.Bordered`: a 1 px `CardStrokeColorDefaultBrush` border, with 8 px padding. - `CmdPal.Adaptive.Container.RoundedBordered`: both. `AdaptiveCardPolyfills.RegisterRenderers` registers the decorator after the elements, so `ContentFormControl.xaml.cs` doesn't change. Elements without either property render as before. Under native AOT, the casts to `Border` and `Style` need their metadata, so `Render` keeps it with `DynamicDependency`, as layer 1 does for element tags. ### Docs The library's `README.md` now lists these elements, the container styles that the host provides, and the decorator. A new Icons section covers how names map to glyphs, the fallback, and how to add a name; it replaces layer 2's paragraph about elements that draw their fallback, since `Icon` is the first one. The upstream table adds both features: the object models for `ProgressBar`, `Badge`, and `Icon`, and the `roundedCorners` and `showBorder` parsing, already exist in the shared C++ model in [microsoft/AdaptiveCards-Mobile](https://github.com/microsoft/AdaptiveCards-Mobile). ### Screenshots From a local card harness (not in the repo) that renders cards through `IncrementalAdaptiveCardUpdater` with the polyfills registered, `main`'s host config, and the real `CardOverrideStyles` from `ContentFormControl.xaml`, 776 px wide. Progress bars, badges, icons, and rounded and bordered containers. `Bot` has no glyph, so that icon draws its `[Bot]` fallback. | Dark | Light | |---|---| |  |  | In light, `main`'s host config makes the emphasis background (`#80F6F6F6`) nearly invisible, so the rounded emphasis container and column are faint. The host config PR, #51205, changes container colors. Icon fallbacks, updated every second: | Dark | Light | |---|---| |  |  | <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed The 10 new files and `ContentFormControl.xaml` are identical to their versions at #51125's head (`8858a9e0a7`). `AdaptiveCardPolyfills.cs` and `README.md` differ from it only by what later layers add: the bar, gauge, donut, and pie charts, the author docs and samples, and the host config. ARM64 Debug builds, run from a `subst` drive (common build options: `-nologo -m -restore -t:Build -v:minimal -p:Configuration=Debug -p:Platform=ARM64 -p:RuntimeIdentifier=win-arm64 -p:CIBuild=false -p:GeneratePackageLocally=false -p:GenerateAppxPackageOnBuild=false -p:PublishAppxPackage=false -p:SkipLongPathsCheck=true`): - `MSBuild.exe src\modules\cmdpal\Tests\Microsoft.CmdPal.AdaptiveCards.Polyfills.UnitTests\Microsoft.CmdPal.AdaptiveCards.Polyfills.UnitTests.csproj <options>`: exit code 0, no warnings. It builds the library, which runs the trim and AOT analyzers. - `vstest.console.arm64.exe ARM64\Debug\WinUI3Apps\CmdPal\tests\Microsoft.CmdPal.AdaptiveCards.Polyfills.UnitTests.dll /Platform:ARM64`: 102 passed, 0 failed. That's 81 from layer 2 and 21 new ones in `IconModelTests` and `ProgressAndBadgeModelTests`. - `MSBuild.exe src\modules\cmdpal\Microsoft.CmdPal.UI\Microsoft.CmdPal.UI.csproj <options>`: exit code 0. The only warnings come from this machine's environment in native dependencies (CS1668 for a missing NETFXSDK ARM64 `LIB` path, and LNK4075). - `MSBuild.exe src\modules\cmdpal\Tests\Microsoft.CmdPal.UI.UnitTests\Microsoft.CmdPal.UI.UnitTests.csproj <options>`: exit code 0. - `vstest.console.arm64.exe ARM64\Debug\WinUI3Apps\CmdPal\tests\Microsoft.CmdPal.UI.UnitTests.dll /Platform:ARM64`: 715 passed, 0 failed. Card harness under JIT, dark and light, with live data for 8 seconds; it counts the updates that replaced the whole card: - A live card whose progress bar value, badge text, and text inside a rounded, bordered container change every second: 10–11 updates, 0 replaced the card. They patch in place, and the container keeps its style. - The icon fallback card above: 11 updates, 0 replaced the card. Text inside a `Container` fallback updates in place. - `main`'s Performance Monitor CPU card, which uses containers, column sets, and columns without the new properties: renders as before, with 13–14 updates and 0 replacing the card.
Summary of the Pull Request
Command Palette's Adaptive Cards get a Fluent host config that follows the app's theme, and cards re-render when the theme changes.
This is an independent PR split out of #51125; see that PR for the full picture. Nothing else in the split depends on it, so it can merge in any order.
Every extension's cards change, not just Performance Monitor's, so this PR needs a design review. The visible changes:
separator: truedraws a line, in the Fluent divider color.mainused a web font stack that XAML can't resolve, so monospace text wasn't monospace.#0063B1. All six container styles are defined;maindefined onlydefaultandemphasis, soaccent,good,warning, andattentioncontainers were pastel even in dark mode. Contrast themes use the system colors.ContentFormControl.PR Checklist
Detailed Description of the Pull Request / Additional comments
mainhard-codes two host config JSON strings, one for light and one for dark, and picks one when the control is created. This PR builds the JSON at runtime instead:AdaptiveCardThemeTokens(new) holds one theme's colors as#AARRGGBBstrings. The light and dark values are the Fluent theme resources (text, system fill, card, and divider colors). High contrast uses the user's system colors.AdaptiveHostConfigJson(new) builds the host config JSON from the tokens. Sizes and spacing follow the Fluent type ramp and 4 px grid.AdaptiveCardsConfig.Create(theme)picks the tokens for the theme, the system accent (AccentDark2on light andAccentLight3on dark, likeAccentTextFillColorPrimaryBrush), and the contrast state. It caches each parsed config by its JSON, so calls that resolve to the same colors return the same instance. It reads contrast colors withUISettings.UIElementColor, since a color boxed in a resource dictionary can't be unboxed under native AOT.ContentFormControlcreates its renderer withCreate(ActualTheme). OnLoadedandActualThemeChangedit asks for the config again; when the config differs, it swaps the renderer's config and re-renders the card.Loadedcovers a control created before it joins a window with its own requested theme.AdaptiveCardsConfig.LightandDarkremain. The publicLightHostConfigStringandDarkHostConfigStringfields are removed; nothing used them.Screenshots
From a local harness (not in the repo) that renders cards at 776 px with
main's host config and with this PR's. "Host config" is a test card that uses each setting the host config controls. "CPU" ismain's Performance Monitor CPU card. The harness simulates the Aquatic and Desert contrast themes by passing their system colors to the host config;mainignores them.main)Validation Steps Performed
ARM64 Debug, run from the repo root on a
substdrive, with Visual Studio 18'sMSBuild\Current\Bin\arm64\MSBuild.exeandCommon7\IDE\Extensions\TestPlatform\vstest.console.arm64.exe:MSBuild.exe src\modules\cmdpal\CommandPalette.slnf -nologo -t:restore -p:RestorePackagesConfig=true -v:minimal -p:Configuration=Debug -p:Platform=ARM64 -p:SkipLongPathsCheck=true: exit code 0.MSBuild.exe src\modules\cmdpal\Microsoft.CmdPal.UI\Microsoft.CmdPal.UI.csproj -nologo -m -restore -t:Build -v:minimal -p:Configuration=Debug -p:Platform=ARM64 -p:RuntimeIdentifier=win-arm64 -p:CIBuild=false -p:GeneratePackageLocally=false -p:GenerateAppxPackageOnBuild=false -p:PublishAppxPackage=false -p:SkipLongPathsCheck=true: exit code 0, with no warnings apart from CS1668 about a LIB directory missing on this machine.src\modules\cmdpal\Tests\Microsoft.CmdPal.UI.UnitTests\Microsoft.CmdPal.UI.UnitTests.csproj: exit code 0, same warnings.vstest.console.arm64.exe ARM64\Debug\WinUI3Apps\CmdPal\tests\Microsoft.CmdPal.UI.UnitTests.dll /Platform:ARM64: 715 passed, 0 failed.LoadedandActualThemeChangedre-render is checked by the build only.