#472
feat(canvas): letter spacing, authored text size and line height, and rounded content clips
Summary
- Letter spacing. Paragraphs take a
trackingoption (Widget.text_tracking), applied as one advance quantum per cluster at the span measurement seam, so intrinsic size, line breaking, and the painted run agree.DrawTextcarries it: AppKit applies it asNSKern, Direct2D asIDWriteTextLayout1character spacing, and the reference renderer in its per-cluster walk. Ink and metric bounds include the positive tracking extent, so a tracked run's tail is not clipped. - Authored text size.
WidgetStyle.text_sizeis an optional exact point size, used ahead of the theme's size rung, so layout, caret and selection metrics, and paint agree on one size. Invalid values fall back to the rung, and buttons measure their labels at it. - Authored line advance. Paragraphs take a
line_heightoption (Widget.text_line_height) that sets the span layout's line advance. 0 keeps the natural height. - Rounded content clips on every backend.
RenderPlanner.pushClipkept only the clip rect, so a roundedclip_contentsurface cut its children square on every backend. The planner now carries aRenderClip(rect plus radii) through its state stack and composes nested clips exactly. A command receives radii only when its own geometry reaches an arc, so everything else keeps the plain-rect path.- The reference rasterizer applies per-pixel coverage.
- AppKit clips to a rounded path. The Metal solid-quad fast path declines rounded clips.
- Direct2D pushes a geometric-mask layer.
- Content clipping follows the authored radius over the per-kind default. Cover-fit images and revealing accordions use the same rounded clip.
Notes for review
- Wire format v6 covers both tracking (an f32 on text records) and clip radii (a presence byte, then four radii when set). JSON emits
trackingandclipRadiusonly when non-zero, so existing goldens stay byte-identical. Thebuild.zigwire-format check now also pins the AppKit and Windows decoder version gates, not just their prose. - The retained
Widgetfootprint goes from 776 to 792 bytes.
Verification
zig build teston macOS with Zig 0.16.0: passed, including at every intermediate commit throughzig build test-canvas.- The Windows Direct2D renderer compiles through
zig build test-windows-web-layer-audit, which cross-compilesui-inboxandwebview. It has not been run on Windows. 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.- New tests:
a rounded content clip rounds what it clips,a rounded clip only costs the commands that reach a corner,nested clips keep each corner's own radius,letter spacing widens a span paragraph by one quantum per cluster,display list omits tracking at the default and serializes it otherwise, andcover images and revealing accordions clip at their authored radius. The last one fails without its fix.
@rdev is attempting to deploy a commit to the Vercel Labs Team on Vercel.
A member of the Team first needs to authorize it.