Skip to content

Add opt-in WinUI 3 (Fluent) Windows target: --target windows-winui #4680

Description

@proggeramlug

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-.exe model. 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)

  1. New crate perry-ui-windows-winui (or a feature on the existing crate) wiring WinAppSDK + Microsoft.UI.Xaml projections.
  2. Target plumbing: --target windows-winui through the compile/link pipeline; bootstrapper/runtime acquisition (Microsoft.WindowsAppRuntime.Bootstrap).
  3. Widget mapping layer: map the perry-ui widget set (~40 widgets) onto XAML controls (Button, ListView/ItemsRepeater, NavigationView, TextBox, etc.).
  4. Window chrome: Mica/Acrylic backdrop, Fluent title bar, theme (light/dark/system).
  5. Packaging: MSIX or sideload-friendly unpackaged WinAppSDK bootstrap; document the runtime dependency.
  6. Geisterhand apply_style dispatcher for the WinUI backend (parity with macOS/GTK4/Win32).

Acceptance

  • --target windows-winui produces 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.

Activity

  1. paulo-assoc commented on Jun 7, 2026

    @paulo-assoc

    I am looking forward to this one.

  2. paulo-assoc commented on Jun 8, 2026

    @paulo-assoc

    @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

  3. proggeramlug commented on Jun 10, 2026

    @proggeramlug
    ContributorAuthor

    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

    • Step 1 (target plumbing + scaffold): done (the #4681 series). The crate re-exports the Win32 backend and shares its Fluent DWM chrome (Mica, rounded corners, theme-aware title bar), so --target windows-winui builds/links/runs today through Win32.
    • Step 2 (Windows App SDK bootstrap): merged in feat(ui-windows-winui): real Windows App SDK bootstrap probe (#4680 step 2) #4896. winui::bootstrap::initialize() resolves the bootstrapper at runtime via LoadLibraryW + MddBootstrapInitialize2 (no link dependency → preserves the single self-contained .exe; absent runtime → falls back to Win32).
    • Step 3 (render-backend dispatch seam): PR feat(ui-windows-winui): render-backend dispatch seam + startup probe (#4680 step 3) #4897. winui::backend::active() (Fluent iff bootstrap Ready, else Win32) + a .CRT$XCU startup probe that runs only in windows-winui binaries + an HRESULT diagnostic under PERRY_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() flips RuntimeMissing → Ready and the seam reports fluent. So both arms of the MddBootstrapInitialize2 FFI are validated against a real runtime. (The diagnostic added in #4897 is what pinpointed the missing-DDLM cause.)

    The bindgen wall: stock windows-bindgen can't generate Microsoft.UI.Xaml

    Tried generating Microsoft.UI.Xaml Rust projections from the WinAppSDK winmd:

    • windows-bindgen 0.58 (our current windows pin): panics not implemented: Microsoft.UI.Dispatching.DispatcherQueue — every XAML control pulls DependencyObject → DispatcherQueue.
    • windows-bindgen 0.66 (latest on crates.io): gets further, then panics type not found: Microsoft.UI.Composition.IAnimationObject — UIElement implements it, but Composition metadata isn't in the WinAppSDK lib/uap10.0/*.winmd set.

    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:

    • The bindgen closure problem is solved and the output is committed: crates/libs/reactor/src/bindings.rs is a ~941 KB checked-in Microsoft.UI.Xaml projection, produced with windows-rs's newer method-level-filtering / vtable-demotion bindgen.
    • Application subclassing (app_shim.rs), bootstrap, and single-exe packaging (build.rs copies Bootstrap.dll and resources.pri next 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) plus bindings.rs + app_shim.rs, which are the pieces Perry needs.

    Recommended approach — reuse the plumbing, not the DSL

    1. New perry-ui-winui3-sys crate that vendors (MIT/Apache, attribution preserved) reactor's Microsoft.UI.Xaml bindings.rs + the Application/bootstrap/packaging shim, on the latest windows-core — decoupled from perry-ui-windows's windows 0.58 (Cargo compiles multiple windows versions fine).
    2. Build Perry's imperative perry_ui_* WinUI FFI on top, mapping the perry-ui widget set → XAML controls imperatively, behind the winui::backend::active() seam from feat(ui-windows-winui): render-backend dispatch seam + startup probe (#4680 step 3) #4897.
    3. Move the bootstrap default to WinAppSDK 2.x and reuse reactor's build.rs pattern to fetch winmd + copy Bootstrap.dll/resources.pri.

    Costs/risks: a newer windows-core major in the tree (isolated to the new crate); WinAppSDK 2.x runtime; maintaining a vendored ~941 KB bindings.rs (reactor is version = "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.

  4. paulo-assoc commented on Jun 10, 2026

    @paulo-assoc

    Thanks, @proggeramlug. I agree with you, that the best approach is to reuse the plumbing, but not the DSL.

  5. added
    rfcProposal that needs design agreement before implementation
    triagedMaintainer reviewed; type, scope, and next step are clear
    and removed
    enhancementNew capability or improvement
    on Jul 26, 2026
  6. proggeramlug commented on Aug 24, 2026

    @proggeramlug
    ContributorAuthor

    Closed by #8722, landed on main via #8733 (squash 0749bd338).

    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 at third_party/windows-winui/ are both on main.

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is neededrfcProposal that needs design agreement before implementationtriagedMaintainer reviewed; type, scope, and next step are clear

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions