#473
feat(canvas): widget painters and custom-control seams
Summary
- Widget painters.
Widget.paintis an optional painter that emits a widget's chrome in place of the kind's built-in visuals, with an eight-floatpaint_datapayload for its state. The SDK keeps layout, children (emitted after the painter in both render walks), input, focus, semantics, and invalidation. The painter pointer and payload take part in retained invalidation. Editable kinds keep their text alignment and read a positivepaint_data[7]as the painted well's text inset. - Nested emission restores tree state. A painter that re-enters
emitWidgetTreefor a nested widget (a field's editor, an icon in a chip) used to leave the thread-local scrim viewport at the nested frame, so later modal scrims covered that frame instead of the window, and it cleared the culling bounds for the rest of the outer tree. Both are now saved and restored. - Painted controls keep built-in semantics. A painted button with authored horizontal padding measures to that padding instead of the 44 pt house floor. A painted button group stamps its children's segments in the static walk too. A painted input group resolves focus-within before its painter runs.
controls.container_feedback. Lets a theme whose surfaces own their feedback keep an actionable row's authored background through hover and press.Builder.allocGradientStops. Emit-computed gradient stops get builder-owned storage, likeallocPathElements. A stack-local stop array otherwise dies before the display list that references it. The retained per-view gradient stop pool now matches the builder's budget, so views with more than 32 two-stop gradients no longer freeze when scrolled into view.WidgetStyle.cursor. Overrides the cursor a widget's kind implies, for example a divider built from containers or a drag handle. Hit records carry it, andresize_verticaljoinsresize_horizontal.widget-action <view> <id> hover. Automation can hover widgets, making hover states, cursors, and delayed tooltips testable headlessly.
Notes for review
- The painter API is the part most worth discussing: a function pointer plus a fixed
[8]f32payload, compared by value for invalidation. It keepsWidgeta plain value type, but a typed or tagged payload is a reasonable alternative. - The retained
Widgetfootprint goes from 776 to 816 bytes. - The cursor override and the hover action don't depend on the painter work and can be split out if the painter discussion takes a while.
Verification
zig build teston macOS with Zig 0.16.0: passed.scripts/gate.sh fast: every step passed exceptexamples-mobile, which needs the Android NDK. The NDK isn't installed here, and upstreammainfails the same way.surface-claimsneedednpm ci --include=devinpackages/coreand passed when rerun.docs-checkpassed.- New tests:
a widget painter replaces the built-in chrome and its children still render,a painter's nested widget tree leaves later modal scrims at the root(fails without the fix),retained gradient controls survive repeated native scroll reversals, andretained gradients support the builder budget and preserve the frame on overflow.
@rdev is attempting to deploy a commit to the Vercel Labs Team on Vercel.
A member of the Team first needs to authorize it.