Context
The styling matrix added in #185 Phase A (crates/perry-ui/src/styling_matrix.rs) audits which styling FFIs are wired on each backend. After Phase B closures (v0.5.297–v0.5.303), every Apple platform + Android + Web is at 43/43 Wired, GTK4 at 39/43 (#202), and Windows at 38/43 Wired + 5 Stub — all 5 stubs are in a single "deferred-paint family" that shares the same blocker: no custom WM_PAINT rendering pass for child controls yet.
The 5 Stub rows
| Prop |
FFI symbol |
Storage |
Rendering blocker |
widget.shadow |
perry_ui_widget_set_shadow |
SHADOW_PARAMS: Mutex<Vec<(i64, ShadowData)>> (mod.rs) |
Custom WM_PAINT alpha-blit OR DirectComposition IDCompositionVisual + DropShadowEffect |
widget.opacity |
perry_ui_widget_set_opacity |
OPACITY_VALUES: Mutex<Vec<(i64, f64)>> |
WS_EX_LAYERED + SetLayeredWindowAttributes per-class wiring |
widget.border_color |
perry_ui_widget_set_border_color |
BORDER_STATE: Mutex<Vec<(i64, (Option<color>, Option<width>))>> |
Rectangle() in WM_PAINT, OR wrapping owner-drawn parent |
widget.border_width |
perry_ui_widget_set_border_width |
(same BORDER_STATE) |
(same as above — joint state) |
text.decoration |
perry_ui_text_set_decoration |
DECORATION_VALUES: Mutex<Vec<(i64, i64)>> (text.rs) |
GetObjectW → modify LOGFONT (lfUnderline/lfStrikeOut) → CreateFontIndirectW → WM_SETFONT |
All 5 follow the same shape: cross-platform code already calls the FFI, params are stored, but no paint path consumes them.
What's needed
The shared blocker is the WM_PAINT path. Once that lands for any one of these, the rest follow the same pattern:
- Shared infrastructure: extend whatever WM_PAINT handler currently exists (or add owner-drawn support to the relevant control classes) to consume the per-handle stored params.
- Per-stub apply pass (mirrors the existing
apply_corner_radius deferred-paint at mod.rs:843): on resize/paint, read state from the Mutex map, emit the appropriate GDI / DirectComposition calls.
- Matrix flip in
crates/perry-ui/src/styling_matrix.rs: change Stub → Wired for the platform[7] (Windows) cell on each row as it lands. Drift integration test catches mismatches.
Why a separate issue
The macOS dev host can't easily verify Windows paint output. The existing Windows backend (crates/perry-ui-windows/) builds via cross-compile but actually rendering needs a real Windows host or VM. The CI windows-2022 runner has the toolchain but no display for screenshot verification.
A Windows contributor can pick this up incrementally — each row can flip Stub → Wired independently as paint paths land.
Notes for the implementer
apply_corner_radius at crates/perry-ui-windows/src/widgets/mod.rs:843 is the canonical "deferred-paint" pattern. It uses CreateRoundRectRgn() + SetWindowRgn() post-layout. Adding apply_shadow / apply_opacity / apply_border / apply_decoration symmetric to this is the natural shape.
WS_EX_LAYERED for opacity needs to be set BEFORE the HWND is shown — the existing apply_opacity would need to coordinate with widget creation, not just be called at paint time.
text.decoration's LOGFONT recreate has the same lifetime concerns the existing apply_font at text.rs:336 already handles (DeleteObject of old HFONT before assigning new one).
- After landing, run
cargo test -p perry-ui to verify the matrix integration test stays clean.
Closes
Once landed: Windows reaches 43/43 Wired alongside Apple+Android+Web. Combined with #202's GTK4 4-row closure, the styling matrix is fully green for issue #185 across every Perry-supported platform.
Context
The styling matrix added in #185 Phase A (
crates/perry-ui/src/styling_matrix.rs) audits which styling FFIs are wired on each backend. After Phase B closures (v0.5.297–v0.5.303), every Apple platform + Android + Web is at 43/43 Wired, GTK4 at 39/43 (#202), and Windows at 38/43 Wired + 5 Stub — all 5 stubs are in a single "deferred-paint family" that shares the same blocker: no customWM_PAINTrendering pass for child controls yet.The 5 Stub rows
widget.shadowperry_ui_widget_set_shadowSHADOW_PARAMS: Mutex<Vec<(i64, ShadowData)>>(mod.rs)IDCompositionVisual+DropShadowEffectwidget.opacityperry_ui_widget_set_opacityOPACITY_VALUES: Mutex<Vec<(i64, f64)>>WS_EX_LAYERED+SetLayeredWindowAttributesper-class wiringwidget.border_colorperry_ui_widget_set_border_colorBORDER_STATE: Mutex<Vec<(i64, (Option<color>, Option<width>))>>Rectangle()in WM_PAINT, OR wrapping owner-drawn parentwidget.border_widthperry_ui_widget_set_border_widthBORDER_STATE)text.decorationperry_ui_text_set_decorationDECORATION_VALUES: Mutex<Vec<(i64, i64)>>(text.rs)GetObjectW→ modify LOGFONT (lfUnderline/lfStrikeOut) →CreateFontIndirectW→WM_SETFONTAll 5 follow the same shape: cross-platform code already calls the FFI, params are stored, but no paint path consumes them.
What's needed
The shared blocker is the WM_PAINT path. Once that lands for any one of these, the rest follow the same pattern:
apply_corner_radiusdeferred-paint at mod.rs:843): on resize/paint, read state from the Mutex map, emit the appropriate GDI / DirectComposition calls.crates/perry-ui/src/styling_matrix.rs: changeStub→Wiredfor the platform[7] (Windows) cell on each row as it lands. Drift integration test catches mismatches.Why a separate issue
The macOS dev host can't easily verify Windows paint output. The existing Windows backend (
crates/perry-ui-windows/) builds via cross-compile but actually rendering needs a real Windows host or VM. The CI windows-2022 runner has the toolchain but no display for screenshot verification.A Windows contributor can pick this up incrementally — each row can flip
Stub→Wiredindependently as paint paths land.Notes for the implementer
apply_corner_radiusatcrates/perry-ui-windows/src/widgets/mod.rs:843is the canonical "deferred-paint" pattern. It usesCreateRoundRectRgn()+SetWindowRgn()post-layout. Addingapply_shadow/apply_opacity/apply_border/apply_decorationsymmetric to this is the natural shape.WS_EX_LAYEREDfor opacity needs to be set BEFORE the HWND is shown — the existingapply_opacitywould need to coordinate with widget creation, not just be called at paint time.text.decoration's LOGFONT recreate has the same lifetime concerns the existingapply_fontattext.rs:336already handles (DeleteObject of old HFONT before assigning new one).cargo test -p perry-uito verify the matrix integration test stays clean.Closes
Once landed: Windows reaches 43/43 Wired alongside Apple+Android+Web. Combined with #202's GTK4 4-row closure, the styling matrix is fully green for issue #185 across every Perry-supported platform.