Context
#210 wired 4 of the 5 Windows styling stubs (decoration / opacity / border_color / border_width) at v0.5.347. The fifth — widget.shadow — remained Stub. Storage was already in place at crates/perry-ui-windows/src/widgets/mod.rs::SHADOW_PARAMS; what's missing is the rendering pass.
Why a separate issue
Real CSS box-shadow rendering on Win32 child controls (vs top-level windows) needs DirectComposition:
IDCompositionDevice / IDCompositionVisual / IDCompositionEffectGroup
IDCompositionDropShadowEffect (the named CSS-equivalent shape: color, blur, offset)
- DXGI swap chain integration so DirectComposition can composite atop the GDI render
Or alternatively a custom alpha-blended WM_PAINT pass on the parent that draws N concentric translucent rectangles to approximate Gaussian blur. This is the classic GDI "fake shadow" — visible but not pixel-accurate.
Either path is multi-day work that didn't fit inside #210's scope. Decoration / opacity / border could land via small per-prop apply functions; shadow needs new infrastructure.
Acceptance criteria
widget.shadow row in the styling matrix flips from Stub → Wired for Windows.
- A widget calling
widgetSetShadow(handle, r, g, b, a, blur, offsetX, offsetY) produces a visible drop shadow under the widget's bounds, matching the CSS box-shadow reference within reasonable approximation (color, offset, and at least 8-16px of blur radius).
- Existing widgets with corner radius (which uses
SetWindowRgn) still render correctly — the shadow path must compose with the rounded clip, not fight it.
docs/examples/ui/styling/visual_test.ts section 4 (Shadow) renders visible shadows on Windows.
Hints for the implementer
crates/perry-ui-macos/src/widgets/mod.rs::set_shadow is the reference (CALayer-based — sets setMasksToBounds: false + shadowOpacity / shadowRadius / shadowOffset). Behavior to match: opaque shadow color with alpha riding on shadowOpacity; positive offsetY = downward (HTML convention).
- The
SHADOW_PARAMS store already exists (mod.rs:798) with the full 7-tuple. The apply_corner_radius deferred-paint pattern at mod.rs:962 is the canonical "call from layout pipeline" hook — apply_shadow(handle) should slot in symmetrically.
- DirectComposition requires Windows 8+ (matches Perry's minimum). The
windows-rs crate already enables Win32_Graphics_DirectComposition would need to be added to crates/perry-ui-windows/Cargo.toml's windows features.
- Two-stage strategy that keeps initial scope manageable: (1) GDI alpha-blend approximation first — visible shadow but not perfectly Gaussian; (2) DirectComposition upgrade later for true CSS parity. This issue could be split if the GDI step is enough for a v1.
Closes
When this lands: Windows reaches 43/43 Wired alongside every other Perry-supported platform. Combined with #210 (now closed except this row), the styling matrix is fully green for issue #185.
Context
#210 wired 4 of the 5 Windows styling stubs (decoration / opacity / border_color / border_width) at v0.5.347. The fifth —
widget.shadow— remainedStub. Storage was already in place atcrates/perry-ui-windows/src/widgets/mod.rs::SHADOW_PARAMS; what's missing is the rendering pass.Why a separate issue
Real CSS
box-shadowrendering on Win32 child controls (vs top-level windows) needs DirectComposition:IDCompositionDevice/IDCompositionVisual/IDCompositionEffectGroupIDCompositionDropShadowEffect(the named CSS-equivalent shape: color, blur, offset)Or alternatively a custom alpha-blended
WM_PAINTpass on the parent that draws N concentric translucent rectangles to approximate Gaussian blur. This is the classic GDI "fake shadow" — visible but not pixel-accurate.Either path is multi-day work that didn't fit inside #210's scope. Decoration / opacity / border could land via small per-prop apply functions; shadow needs new infrastructure.
Acceptance criteria
widget.shadowrow in the styling matrix flips fromStub→Wiredfor Windows.widgetSetShadow(handle, r, g, b, a, blur, offsetX, offsetY)produces a visible drop shadow under the widget's bounds, matching the CSSbox-shadowreference within reasonable approximation (color, offset, and at least 8-16px of blur radius).SetWindowRgn) still render correctly — the shadow path must compose with the rounded clip, not fight it.docs/examples/ui/styling/visual_test.tssection 4 (Shadow) renders visible shadows on Windows.Hints for the implementer
crates/perry-ui-macos/src/widgets/mod.rs::set_shadowis the reference (CALayer-based — setssetMasksToBounds: false+shadowOpacity/shadowRadius/shadowOffset). Behavior to match: opaque shadow color with alpha riding on shadowOpacity; positiveoffsetY= downward (HTML convention).SHADOW_PARAMSstore already exists (mod.rs:798) with the full 7-tuple. Theapply_corner_radiusdeferred-paint pattern atmod.rs:962is the canonical "call from layout pipeline" hook —apply_shadow(handle)should slot in symmetrically.windows-rscrate already enablesWin32_Graphics_DirectCompositionwould need to be added tocrates/perry-ui-windows/Cargo.toml'swindowsfeatures.Closes
When this lands: Windows reaches 43/43 Wired alongside every other Perry-supported platform. Combined with #210 (now closed except this row), the styling matrix is fully green for issue #185.