Repository navigation
Add opt-in WinUI 3 (Fluent) Windows target: --target windows-winui #4680
Copy link
Copy link
Closed
Labels
help wantedExtra attention is neededExtra attention is neededrfcProposal that needs design agreement before implementationProposal that needs design agreement before implementationtriagedMaintainer reviewed; type, scope, and next step are clearMaintainer reviewed; type, scope, and next step are clear
Description
Summary
Add an opt-in WinUI 3 / Fluent rendering backend for Windows, selectable via --target windows-winui, alongside the existing Win32/GDI backend (crates/perry-ui-windows).
Motivation from discussion #3486: the current Win32/GDI output looks dated (boxy, flat, no animation, Win7-era controls). WinUI 3 brings Fluent Design — rounded corners, Mica/Acrylic materials, smooth animations, modern typography, crisp auto-DPI — and is the closest Windows analog to our iOS/Android look-and-feel.
Scope / decisions
- Opt-in, not the default. WinUI 3 requires the WinAppSDK runtime + an MSIX/bootstrapper packaging story, which conflicts with Perry's single-native-
.exemodel. Keep Win32 as the default target; expose WinUI via--target windows-winui. Mirrors how we ship multiple Apple targets. - No new "UI framework" dependency confusion:
microsoft/windows-rs(already a dep,windows = "0.58") is the WinRT projection crate, not a UI lib. WinUI from Rust = driving WinAppSDK + XAML through the WinRT projections (imperative XAML object trees in Rust). - Drop Win7/8/8.1 as a floor for this target; no XAML Islands (kludge). WinUI 3 targets Win10 1809+/Win11.
Work breakdown (rough)
- New crate
perry-ui-windows-winui(or a feature on the existing crate) wiring WinAppSDK + Microsoft.UI.Xaml projections. - Target plumbing:
--target windows-winuithrough the compile/link pipeline; bootstrapper/runtime acquisition (Microsoft.WindowsAppRuntime.Bootstrap). - Widget mapping layer: map the perry-ui widget set (~40 widgets) onto XAML controls (Button, ListView/ItemsRepeater, NavigationView, TextBox, etc.).
- Window chrome: Mica/Acrylic backdrop, Fluent title bar, theme (light/dark/system).
- Packaging: MSIX or sideload-friendly unpackaged WinAppSDK bootstrap; document the runtime dependency.
- Geisterhand
apply_styledispatcher for the WinUI backend (parity with macOS/GTK4/Win32).
Acceptance
--target windows-winuiproduces a runnable app rendering the ToDo sample with Fluent chrome (Mica, rounded corners, animated controls).- Win32 remains the default and unaffected.
- Docs cover the WinAppSDK runtime requirement and how to choose backends.
Tracks discussion #3486. Companion near-term Win32 polish: see the linked Win32 Fluent-polish issue.
Reacted by Paul O
Reacted by Paul O
Metadata
Metadata
Assignees
Labels
help wantedExtra attention is neededExtra attention is neededrfcProposal that needs design agreement before implementationProposal that needs design agreement before implementationtriagedMaintainer reviewed; type, scope, and next step are clearMaintainer reviewed; type, scope, and next step are clear
Activity
paulo-assoc commentedon Jun 7, 2026
I am looking forward to this one.
feat(ui-windows): WinUI 3 backend scaffold + --target windows-winui (#…
feat(ui-windows): WinUI 3 backend scaffold + --target windows-winui (#…
paulo-assoc commentedon Jun 8, 2026
@proggeramlug What I had in mind was that Perry should target WinUI via Windows Reactor. Read this merged pull request from the windows-rs repo: microsoft/windows-rs#4479
Here is the operative section which states that a WinUI app built this way is a small, single executable. This is not in conflict with Perry's single executable model. And it shows that the apps performance runs circles around a C# counterpart, whether using C# with JIT or AOT. And no runtime framework to deploy.
Windows Reactor is a UI library for Rust developers targeting WinUI 3 to deliver native, efficient Windows experiences. It brings a React-like component model to native Windows desktop apps:
Declarative UI, no macros — build UIs as pure functions of state using a builder DSL and tuple-based children
Function components — typed props, context propagation, and error boundaries
Hooks — use_state, use_reducer, use_effect, use_context, use_memo, use_callback, use_resource, and more
55+ widgets — Button, TextBlock, NavigationView, Grid, and many more, plus virtualized lists (ListView, GridView, FlipView) with templated row rendering
Async — use_resource and use_mutation for background data loading
Theming — runtime light/dark switching with ThemeRef brush bindings
Accessibility — built-in automation property modifiers, keyboard accelerators, and tooltips
Small footprint — single ~3 MB binary, no runtime framework to deploy
feat(ui-windows-winui): real Windows App SDK bootstrap probe (#4680 s…
feat(ui-windows-winui): surface bootstrap HRESULT in PERRY_WINUI_DIAG (…
proggeramlug commentedon Jun 10, 2026
Progress + findings: bootstrap proven, bindgen wall hit, Windows Reactor evaluated
Status update on the incremental WinUI 3 build-out (
--target windows-winui).Landed / in flight
#4681series). The crate re-exports the Win32 backend and shares its Fluent DWM chrome (Mica, rounded corners, theme-aware title bar), so--target windows-winuibuilds/links/runs today through Win32.winui::bootstrap::initialize()resolves the bootstrapper at runtime viaLoadLibraryW+MddBootstrapInitialize2(no link dependency → preserves the single self-contained.exe; absent runtime → falls back to Win32).winui::backend::active()(Fluent iff bootstrap Ready, else Win32) + a.CRT$XCUstartup probe that runs only inwindows-winuibinaries + an HRESULT diagnostic underPERRY_WINUI_DIAG.Bootstrap is now proven end-to-end
On a host with the WinAppSDK 1.6 framework installed, the bootstrap initially failed with
HRESULT 0x80670016— the framework package was registered but the DDLM (Dynamic Dependency Lifetime Manager) was not. After registering the DDLM/Main/Singleton runtime packages,initialize()flipsRuntimeMissing → Readyand the seam reportsfluent. So both arms of theMddBootstrapInitialize2FFI are validated against a real runtime. (The diagnostic added in #4897 is what pinpointed the missing-DDLM cause.)The bindgen wall: stock
windows-bindgencan't generateMicrosoft.UI.XamlTried generating
Microsoft.UI.XamlRust projections from the WinAppSDK winmd:windows-bindgen0.58 (our currentwindowspin): panicsnot implemented: Microsoft.UI.Dispatching.DispatcherQueue— every XAML control pullsDependencyObject → DispatcherQueue.windows-bindgen0.66 (latest on crates.io): gets further, then panicstype not found: Microsoft.UI.Composition.IAnimationObject—UIElementimplements it, but Composition metadata isn't in the WinAppSDKlib/uap10.0/*.winmdset.Conclusion: raw projection generation at our toolchain is a toolchain dead-end, not just an effort problem.
Windows Reactor evaluation (windows-rs PR #4479)
windows-reactor(Kenny Kerr / the windows-rs team, merged to master, MIT/Apache-2.0) is a React-style declarative WinUI 3 framework for Rust — single ~3 MB exe, no framework to deploy, WinAppSDK 2.x.It solves exactly the two hardest blockers:
crates/libs/reactor/src/bindings.rsis a ~941 KB checked-inMicrosoft.UI.Xamlprojection, produced with windows-rs's newer method-level-filtering / vtable-demotion bindgen.Applicationsubclassing (app_shim.rs), bootstrap, and single-exe packaging (build.rscopiesBootstrap.dllandresources.prinext to the exe) are all done.But its public API is a React DSL (
use_state, function components, declarative element trees), which does not fit Perry's pipeline — Perry's compiler emits imperative C-ABI calls from LLVM and can't author Rust component/hook functions. Under the DSL, though, reactor has an imperative reconciler backend (core/backend.rs— enum-keyed widget kinds + property setters) plusbindings.rs+app_shim.rs, which are the pieces Perry needs.Recommended approach — reuse the plumbing, not the DSL
perry-ui-winui3-syscrate that vendors (MIT/Apache, attribution preserved) reactor'sMicrosoft.UI.Xamlbindings.rs+ the Application/bootstrap/packaging shim, on the latestwindows-core— decoupled fromperry-ui-windows'swindows0.58 (Cargo compiles multiplewindowsversions fine).perry_ui_*WinUI FFI on top, mapping the perry-ui widget set → XAML controls imperatively, behind thewinui::backend::active()seam from feat(ui-windows-winui): render-backend dispatch seam + startup probe (#4680 step 3) #4897.build.rspattern to fetch winmd + copyBootstrap.dll/resources.pri.Costs/risks: a newer
windows-coremajor in the tree (isolated to the new crate); WinAppSDK 2.x runtime; maintaining a vendored ~941 KBbindings.rs(reactor isversion = "0.0.0", i.e. not yet on crates.io, so vendor or git-pin); reactor is days-old/experimental → expect churn.This removes the bindgen + Application-hosting dead-ends and is the most credible path to real Fluent rendering. Win32 (
--target windows) stays the default and unaffected throughout.paulo-assoc commentedon Jun 10, 2026
Thanks, @proggeramlug. I agree with you, that the best approach is to reuse the plumbing, but not the DSL.
feat(ui-windows-winui): render-backend dispatch seam + startup probe (#…
feat(ui-windows-winui): surface bootstrap HRESULT in PERRY_WINUI_DIAG (…
feat(ui-windows-winui): render-backend dispatch seam + startup probe (#…
proggeramlug commentedon Aug 24, 2026
Closed by #8722, landed on
mainvia #8733 (squash0749bd338).That PR was landed through a batch wrapper PR rather than merged directly, so GitHub's close keyword never fired at merge time. Verified before closing:
crates/perry-ui-windows-winui/and the vendored Reactor snapshot atthird_party/windows-winui/are both onmain.Note the backend's runtime behaviour is unverified — it cannot be built or run from macOS, so someone on Windows should confirm it works before it is relied on. See the follow-up note on #8722 about stale callback pointers captured into Reactor element-tree closures.