git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit0a7f85f91364e9b7522d2a1613233def0ad2547b
parentf31c86b407
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-02 14:24
feat: a toggle is a field whose run glides

A dropdown, a button, a text row with its picker, a spinbox and a toggle
are one object: a well in a plate with a flush run standing in it, one
outline round both. They differ only in where the run is. Prim::Field's
run is now a span, split to end (PaintCtx::field_run), and where it stops
short of the right end MODE_FIELD mirrors the left: a well on that side,
the face inset half a wall from the seam with the run's left corners, the
seam the well's left wall. A run reaching the right end takes the same
path as before, term for term.

The Toggle is drawn so: a run half the field wide at the left end off,
the right end on, gliding between with a well either side. It was a
recess with a raised boss on its floor, the one control whose nested
plate stood above the surface instead of flush with it. Focus lights the
field's rim; ParametersBg draws it among its fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

 CLAUDE.md                             |  36 ++++-
 src/backend/window_runner.rs          |  46 +++---
 src/layout.rs                         |  30 ++--
 src/scene/paint.rs                    |  42 +++--
 src/vk/shader2d.wgsl                  |  42 ++++-
 src/widget/container/parameters_bg.rs |  79 +++++-----
 src/widget/input/checkbox.rs          | 280 ++++++++++++++--------------------
 7 files changed, 309 insertions(+), 246 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index af2e0b5..56a00ab 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -400,6 +400,40 @@ are the tests. Until the same day the picker and the run were nested INSIDE a fu
 well, their faces stopping at the base of its wall, so they never reached the edge a
 dropdown's ▼ does.
 
+### A field is one object, and a toggle is a field whose run glides (since 2026-10-02)
+
+A dropdown, a button, a text row with its picker, a spinbox and a toggle are ONE
+object: a well cut into a plate with a flush plate, the RUN, standing in it, one
+outline round both (`Prim::Field`). They differ only in where the run is:
+
+| form | run | well |
+| --- | --- | --- |
+| flush control plate (dropdown trigger, button, breadcrumb run, …) | the whole field | none |
+| text row with its picker, spinbox with its -/+ run | the right end | left of it |
+| toggle | half the field: the left end off, the right end on | the other half; both sides mid-glide |
+| plain text box | none (a `Prim::Recess`, for grouping — see below) | the whole field |
+
+So the run is a span, `split` to `end` (`PaintCtx::field_run`; `PaintCtx::field`
+is the run reaching the right end, `end` `FIELD_RUN_ONLY` past it). Where the run
+stops short of the right end, a well lies to its right too, and `MODE_FIELD`
+mirrors everything it does on the left: the face is inset half a wall from that
+seam with the corners of the run's LEFT end, and the seam is that well's left
+wall's inner half. A run reaching the right end takes the same path through the
+shader as before, term for term, so every field that was drawn is drawn as it was.
+The host-box slot carries both ends (`p_host.x`, `.y`); the legacy banded path and
+the flat-host bridge draw a well box either side of the run.
+
+**The Toggle is drawn so** (`Toggle::field` the outline, `Toggle::run` the span,
+`Toggle::face` what the relief-off path lights; `ParametersBg::fields` asks for
+the same, and its `reliefs` no longer carries toggles). Until this it was a recess
+with a raised `Boss` on its floor: the one control whose nested plate stood ABOVE
+the surface where every other stood flush with it, and whose outline turned its
+own corner round the plate instead of running round the whole control. The run
+is laid out half the field and a wall wider than its face, as a picker is, so the
+face reaches the well; it glides by `slide_t` as the boss did. Focus lights the
+field's rim (`ControlPlate::focus_tint`), where it lit the boss's.
+`a_toggle_is_a_field_whose_run_glides` is the test.
+
 ### A grouped carve shades as its overlay does (since 2026-10-02)
 
 A full-ring, untinted `Prim::Recess` / `Boss` emitted while a plate's grouping window is
@@ -585,7 +619,7 @@ What this buys, and where the code is heading:
   instead of black (`FOCUS_SHADOW`, both at `FOCUS_GAIN`, in
   `shader2d.wgsl`), so a focused plate still reads which edges face the lamp.
   A Checkbox lights the ring its mark already draws; a Toggle lights the
-  rim of the plate that slides in its well.
+  rim of its field, as every field is lit.
   Roles today: Button, Checkbox, Toggle, Dropdown, FontSelector, ButtonStrip
   (arrows move the selection between its segment plates) and Breadcrumb
   (arrows walk its visible segments, Enter navigates) are plates; TextBox,
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 9fd99c5..f914646 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -2173,9 +2173,9 @@ pub fn tessellate_display_list(
             // A sunken well ending in a flush run (see `Prim::Field`): one
             // outline, one overlay — never grouped, its profile is not a
             // monotonic step. The cover quad inflates by half the wall, as a
-            // free carve's does; the host-box slot carries the seam's x
-            // (physical px), since nothing fades against a host here.
-            Prim::Field { rect, radii, depth, split, tint } if shader_plates => {
+            // free carve's does; the host-box slot carries the run's two
+            // ends (physical px), since nothing fades against a host here.
+            Prim::Field { rect, radii, depth, split, end, tint } if shader_plates => {
                 let infl = *depth * 0.5 + 2.0;
                 verts.extend(quad_vertices(
                     rect.x - infl, rect.y - infl,
@@ -2187,7 +2187,7 @@ pub fn tessellate_display_list(
                 if let Some(t) = tint {
                     p.specular_tint = [t[0], t[1], t[2], 1.0];
                 }
-                p.host = [*split * scale, 0.0, 0.0, 0.0];
+                p.host = [*split * scale, *end * scale, 0.0, 0.0];
                 plate = Some(p);
             }
             Prim::Recess { rect, radii, depth, edges, .. }
@@ -2542,32 +2542,42 @@ pub fn tessellate_display_list(
                     EdgeKind::Step, &mut verts,
                 );
             }
-            Prim::Field { rect, radii, depth, split, .. } => {
+            Prim::Field { rect, radii, depth, split, end, .. } => {
                 // Legacy approximation: the two-box form `Prim::Field`
-                // replaced — the well to the seam a step down, the run past it
-                // the Trough arm's down-then-up stack. The banded machinery
-                // has no blended outline, and the legacy path exists only for
-                // A/B comparison.
+                // replaced — a well either side of the run a step down, the
+                // run the Trough arm's down-then-up stack. The banded
+                // machinery has no blended outline, and the legacy path
+                // exists only for A/B comparison.
                 let all = (true, true, true, true);
-                if *split > rect.x {
+                let (fl, fr) = (rect.x, rect.x + rect.width);
+                let (well_l, well_r) = (*split > fl, *end < fr);
+                let rx = split.max(fl);
+                let rw = (end.min(fr) - rx).max(0.0);
+                if well_l {
                     push_bevel_edge_vertices_banded(
-                        rect.x, rect.y, *split - rect.x, rect.height, (radii.0, 0.0, 0.0, radii.3), *depth,
+                        fl, rect.y, rx - fl, rect.height, (radii.0, 0.0, 0.0, radii.3), *depth,
                         sw, sh, [0.0; 4], no, -1.0, default_bevel_bands(*depth), all,
                         EdgeKind::Step, &mut verts,
                     );
                 }
-                let rx = split.max(rect.x);
-                let rw = (rect.x + rect.width - rx).max(0.0);
-                // All run (`PaintCtx::inset_plate`'s edge): its own left corners.
-                let (l0, l3) = if *split > rect.x { (0.0, 0.0) } else { (radii.0, radii.3) };
+                if well_r {
+                    push_bevel_edge_vertices_banded(
+                        rx + rw, rect.y, fr - rx - rw, rect.height, (0.0, radii.1, radii.2, 0.0), *depth,
+                        sw, sh, [0.0; 4], no, -1.0, default_bevel_bands(*depth), all,
+                        EdgeKind::Step, &mut verts,
+                    );
+                }
+                // A run's own corners where it reaches the outline; square at a seam.
+                let (l0, l3) = if well_l { (0.0, 0.0) } else { (radii.0, radii.3) };
+                let (r1, r2) = if well_r { (0.0, 0.0) } else { (radii.1, radii.2) };
                 let half = *depth * 0.5;
                 push_bevel_edge_vertices_banded(
-                    rx, rect.y, rw, rect.height, (l0, radii.1, radii.2, l3), half,
+                    rx, rect.y, rw, rect.height, (l0, r1, r2, l3), half,
                     sw, sh, [0.0; 4], no, -1.0, default_bevel_bands(half), all,
                     EdgeKind::Step, &mut verts,
                 );
-                let ir = (radii.1 - half).max(0.0);
-                let il = if *split > rect.x { 0.0 } else { (radii.0 - half).max(0.0) };
+                let ir = if well_r { 0.0 } else { (radii.1 - half).max(0.0) };
+                let il = if well_l { 0.0 } else { (radii.0 - half).max(0.0) };
                 push_bevel_edge_vertices_banded(
                     rx + half, rect.y + half, rw - *depth, rect.height - *depth, (il, ir, ir, il), half,
                     sw, sh, [0.0; 4], no, 1.0, default_bevel_bands(half), all,
diff --git a/src/layout.rs b/src/layout.rs
index 72c3bae..2da665e 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -4272,7 +4272,9 @@ pub fn render_widget<T: WidgetHost + 'static>(pc: &mut dyn RenderTarget, w: &mut
         let trough_for_face = match (&item.prim, &pending_face) {
             (Prim::Trough { rect, .. }, Some((face_rect, _, _))) => same_rect(*rect, *face_rect),
             // `inset_plate`'s edge: a field that is all run.
-            (Prim::Field { rect, split, .. }, Some((face_rect, _, _))) => *split <= rect.x && same_rect(*rect, *face_rect),
+            (Prim::Field { rect, split, end, .. }, Some((face_rect, _, _))) => {
+                *split <= rect.x && *end >= rect.x + rect.width && same_rect(*rect, *face_rect)
+            }
             _ => false,
         };
         if !trough_for_face {
@@ -4347,25 +4349,33 @@ pub fn render_widget<T: WidgetHost + 'static>(pc: &mut dyn RenderTarget, w: &mut
             // A flat host has no blended outline: the two-box form
             // `Prim::Field` replaced — the well to the seam, the run's flush
             // plate past it.
-            Prim::Field { rect, radii, depth, split, tint } => {
+            Prim::Field { rect, radii, depth, split, end, tint } => {
+                let (fl, fr) = (rect.x, rect.x + rect.width);
+                let (well_l, well_r) = (split > fl, end < fr);
                 // All run and no well (`PaintCtx::inset_plate`'s edge): the
                 // plate alone, with the face that came before it.
-                let face = if split <= rect.x { pending_face.take().map(|(_, _, fill)| fill) } else { None }.unwrap_or([0.0; 4]);
-                if split > rect.x {
+                let face = if !well_l && !well_r { pending_face.take().map(|(_, _, fill)| fill) } else { None }.unwrap_or([0.0; 4]);
+                let rx = split.max(fl);
+                let rw = (end.min(fr) - rx).max(0.0);
+                let mut well = |x: f32, w: f32, radii: (f32, f32, f32, f32)| {
                     pc.relief_carve(&ReliefCarve {
                         kind: CarveKind::Recess { tint },
-                        x: rect.x,
+                        x,
                         y: rect.y,
-                        w: split - rect.x,
+                        w,
                         h: rect.height,
-                        radii: (radii.0, 0.0, 0.0, radii.3),
+                        radii,
                         depth,
                         edges: (true, true, true, true),
                     });
+                };
+                if well_l {
+                    well(fl, rx - fl, (radii.0, 0.0, 0.0, radii.3));
+                }
+                if well_r {
+                    well(rx + rw, fr - rx - rw, (0.0, radii.1, radii.2, 0.0));
                 }
-                let r = radii.1.max(radii.2);
-                let rx = split.max(rect.x);
-                let rw = (rect.x + rect.width - rx).max(0.0);
+                let r = if well_r { radii.0.max(radii.3) } else { radii.1.max(radii.2) };
                 match tint {
                     Some(t) => pc.inset_plate_tinted(face, rx, rect.y, rw, rect.height, r, depth, t),
                     None => pc.inset_plate(face, rx, rect.y, rw, rect.height, r, depth),
diff --git a/src/scene/paint.rs b/src/scene/paint.rs
index c709792..cd21398 100644
--- a/src/scene/paint.rs
+++ b/src/scene/paint.rs
@@ -219,7 +219,9 @@ pub enum PlateStance {
 
 /// How far left of a [`Prim::Field`] its seam is put to make it all run and
 /// no well ([`PaintCtx::inset_plate`]): far enough that the blend across the
-/// seam and the seam's own wall land nowhere near the field.
+/// seam and the seam's own wall land nowhere near the field. A run's `end`
+/// is put as far past the field's right edge to reach that edge
+/// ([`PaintCtx::field`]).
 pub const FIELD_RUN_ONLY: f32 = 1.0e4;
 
 /// A control plate: the thing you press, at the control rung of the plate
@@ -586,12 +588,17 @@ pub enum Prim {
     /// `tint` lights the rim like [`Prim::Recess`]'s — the focus treatment of a
     /// flush control plate (`PaintCtx::control_plate`).
     Trough { rect: Rect, radii: Radii, depth: f32, edges: (bool, bool, bool, bool), tint: Option<[f32; 3]> },
-    /// A sunken well that ends in a FLUSH run: one field, one outer contour.
-    /// Left of `split` (an x in the same space as `rect`) it is a
-    /// [`Prim::Recess`] — the interior one step down; right of it, a
-    /// [`Prim::Trough`] — a valley on the outline and the interior back at
-    /// the surface's level, as a flush control plate's. A parameter pane's
-    /// text row with its completion picker, a spinbox's value and its -/+ run.
+    /// A sunken well holding a FLUSH run: one field, one outer contour.
+    /// Between `split` and `end` (xs in the same space as `rect`) the
+    /// interior is back at the surface's level, as a flush control plate's
+    /// face, inside a valley on the outline; on either side of that it is a
+    /// [`Prim::Recess`] — the interior one step down. A run that reaches an
+    /// end of the field (`split` left of it, or `end` right of it — by
+    /// [`FIELD_RUN_ONLY`]) has no well on that side. The forms in use: a
+    /// parameter pane's text row with its completion picker and a spinbox's
+    /// value with its -/+ run (the run at the right end), a flush control
+    /// plate (all run), a toggle (a run half the field wide, at the left end
+    /// off and the right end on, gliding between).
     ///
     /// Why one prim and not the two it replaces, side by side: each of those
     /// shades its OWN box, so at the seam the outline breaks — each box
@@ -607,7 +614,7 @@ pub enum Prim {
     ///
     /// SDF path only; the legacy banded tessellation and flat hosts draw the
     /// two-box form it replaced. `tint` lights it like a recess's.
-    Field { rect: Rect, radii: Radii, depth: f32, split: f32, tint: Option<[f32; 3]> },
+    Field { rect: Rect, radii: Radii, depth: f32, split: f32, end: f32, tint: Option<[f32; 3]> },
     /// The window's glass slab: a rounded fill plus a rolled, lit edge around its whole
     /// perimeter, drawn at full size. Distinct from `Bevel`, which insets its fill by
     /// `depth` — a plate must fill the window exactly, or the compositor's rounded window
@@ -1114,7 +1121,7 @@ impl PaintCtx {
                 Some(t) => self.trough_tinted(rect, radii, depth, t),
                 None => self.trough_edges(rect, radii, depth, edges),
             },
-            Prim::Field { rect, radii, depth, split, tint } => self.field(rect, radii, depth, split, tint),
+            Prim::Field { rect, radii, depth, split, end, tint } => self.field_run(rect, radii, depth, (split, end), tint),
             Prim::Plate { rect, radii, material, depth, shape } => {
                 self.plate_shaped(rect, radii, &material, depth, shape)
             }
@@ -1566,12 +1573,21 @@ impl PaintCtx {
         self.push(Prim::Trough { rect, radii, depth, edges, tint: Some(tint) });
     }
 
-    /// A sunken well ending in a flush run at `split` — see [`Prim::Field`].
-    /// `tint` lights its rim (the focus and hover treatment).
+    /// A sunken well ending in a flush run at `split`, the run reaching the
+    /// field's right end — see [`Prim::Field`]. `tint` lights its rim (the
+    /// focus and hover treatment).
     pub fn field(&mut self, rect: Rect, radii: Radii, depth: f32, split: f32, tint: Option<[f32; 3]>) {
-        let split = split + self.offset.0;
+        let end = rect.x + rect.width + FIELD_RUN_ONLY;
+        self.field_run(rect, radii, depth, (split, end), tint);
+    }
+
+    /// A sunken well with a flush run from `run.0` to `run.1` — see
+    /// [`Prim::Field`]: the general form, a well on whichever sides of the
+    /// run the field extends past it.
+    pub fn field_run(&mut self, rect: Rect, radii: Radii, depth: f32, run: (f32, f32), tint: Option<[f32; 3]>) {
+        let (split, end) = (run.0 + self.offset.0, run.1 + self.offset.0);
         let rect = self.apply_offset(rect);
-        self.push(Prim::Field { rect, radii, depth, split, tint });
+        self.push(Prim::Field { rect, radii, depth, split, end, tint });
     }
 
     /// Raise a rim along `rect`'s boundary — see `Prim::Ridge`. `depth` is the
diff --git a/src/vk/shader2d.wgsl b/src/vk/shader2d.wgsl
index b4230ba..77a9423 100644
--- a/src/vk/shader2d.wgsl
+++ b/src/vk/shader2d.wgsl
@@ -955,24 +955,47 @@ fn plate_shade(frag: vec2f, vcol: vec4f) -> vec4f {
         // A field with no well (split left of it — a dropdown trigger, a
         // button) is all run: the face inset from the outline all round,
         // its own corners.
+        //
+        // The run ends at p_host.y. Past the field's right end (every form
+        // above) it reaches the outline there; short of it, a well lies to
+        // its right too, mirroring everything said of the left: the face
+        // inset half a wall from that seam with the corners of its LEFT end,
+        // and the seam the well's left wall's inner half. A toggle's run is
+        // half the field and glides between its ends, so mid-glide it has a
+        // well on both sides.
         let split = rrect_clip.p_host.x;
+        let run_end = rrect_clip.p_host.y;
         let hw = 0.5 * t;
         let pr = rrect_clip.p_rect;
         let fl = pr.x - pr.z;
+        let fr = pr.x + pr.z;
         let has_well = split > fl + 0.5;
+        let has_well_r = run_end < fr - 0.5;
         let run_l = select(fl, split, has_well);
+        let run_r = select(fr, run_end, has_well_r);
         let fx0 = run_l + hw;
-        let fx1 = pr.x + pr.z - hw;
+        let fx1 = run_r - hw;
         let fy0 = pr.y - pr.w + hw;
         let fy1 = pr.y + pr.w - hw;
         let face = vec4f(0.5 * (fx0 + fx1), 0.5 * (fy0 + fy1), max(0.5 * (fx1 - fx0), 0.0), max(0.5 * (fy1 - fy0), 0.0));
         let rr = rrect_clip.p_radii;
+        let rtl = max(rr.x - hw, 0.0);
         let rtr = max(rr.y - hw, 0.0);
         let rbr = max(rr.z - hw, 0.0);
-        let face_r = select(vec4f(max(rr.x - hw, 0.0), rtr, rbr, max(rr.w - hw, 0.0)), vec4f(rtr, rtr, rbr, rbr), has_well);
+        let rbl = max(rr.w - hw, 0.0);
+        let face_r = vec4f(
+            select(rtl, rtr, has_well),
+            select(rtr, rtl, has_well_r),
+            select(rbr, rbl, has_well_r),
+            select(rbl, rbr, has_well),
+        );
         // The outline: its outer half everywhere, its inner half over the
-        // well alone (`over_well`, antialiased across the seam's pixel).
-        let over_well = 1.0 - smoothstep(split - 0.5, split + 0.5, frag.x);
+        // wells alone (`over_well`, antialiased across each seam's pixel).
+        let over_well = clamp(
+            1.0 - smoothstep(split - 0.5, split + 0.5, frag.x) + smoothstep(run_end - 0.5, run_end + 0.5, frag.x),
+            0.0,
+            1.0,
+        );
         let inner = select(1.0, over_well, u > 0.5);
         slope = -cd * carve_slope(u) * inner;
         curv = rrect_clip.p_mat.w * sin(u * TAU) * inner;
@@ -995,6 +1018,17 @@ fn plate_shade(frag: vec2f, vcol: vec4f) -> vec4f {
                 curv_seam = curv_seam + rrect_clip.p_mat.w * sin(usc * TAU) * fade;
             }
         }
+        // The right-hand seam: the right well's LEFT wall, its inner half,
+        // falling the other way.
+        if (has_well_r) {
+            let us = (frag.x - run_end) / t + 0.5;
+            if (us >= 0.5) {
+                let usc = min(us, 1.0);
+                let fade = clamp(d / hw, 0.0, 1.0);
+                sv_seam = sv_seam + vec2f(-1.0, 0.0) * (-cd * carve_slope(usc)) * fade;
+                curv_seam = curv_seam + rrect_clip.p_mat.w * sin(usc * TAU) * fade;
+            }
+        }
     } else if (eff == MODE_RIDGE || eff == MODE_TROUGH) {
         // Ridge bump: the carve profile mirrored about the boundary (rising
         // outer half, falling inner half), amplitude halved so the wall tilt
diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index 36cb9ef..86eb249 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -1746,26 +1746,8 @@ impl ParametersBg {
                 // island the button itself never drew.
                 None
             } else if p.2 == "toggle" || p.2 == "checkbox" {
-                // A toggle paints no fill at all, so these carves — the
-                // widget's own (`Toggle::flat_carves`, exactly what its paint
-                // emits, in the order it emits them) — ARE the control: the
-                // track's well (a recess) and the plate gliding on its floor
-                // (a boss). Neither is flush.
-                if let Some(t) = &self.toggles[i] {
-                    let (x, y, w, h) = t.rect();
-                    if w > 0.0 && h > 0.0 {
-                        let ty = t.label_strip();
-                        let rect = Rect { x, y: y + ty, width: w, height: h - ty };
-                        for c in t.inner().flat_carves(rect) {
-                            let raised = match c.kind {
-                                crate::layout::CarveKind::Boss { .. } => true,
-                                crate::layout::CarveKind::Recess { .. } => false,
-                                crate::layout::CarveKind::Trough => continue,
-                            };
-                            out.push((c.x, c.y, c.w, c.h, c.radii, c.depth, raised, c.edges));
-                        }
-                    }
-                }
+                // A toggle is a field ([`Self::fields`]): its well and the
+                // flush run gliding in it, one outline round both.
                 None
             } else if p.2.starts_with("spinbox") {
                 // A plain well only when there is no -/+ run; with one the
@@ -1829,16 +1811,20 @@ impl ParametersBg {
     }
 
     /// The rows that are ONE field ([`crate::scene::paint::Prim::Field`]):
-    /// a sunken well ending in a flush run, one outline round both —
-    /// `(x, y, w, h, radii, depth, split)`, drawn AFTER [`Self::reliefs`].
-    /// A textpick row is its text box's well and its picker; a spinbox, its
-    /// value's well and its -/+ run; a dropdown row, a field that is all run
-    /// (its seam [`crate::scene::paint::FIELD_RUN_ONLY`] to the left), so
-    /// every flush control in the pane has the same edge. They replaced
+    /// a sunken well holding a flush run, one outline round both —
+    /// `(x, y, w, h, radii, depth, split, end)`, the run from `split` to
+    /// `end`, drawn AFTER [`Self::reliefs`]. A textpick row is its text box's
+    /// well and its picker; a spinbox, its value's well and its -/+ run (both
+    /// runs reaching the right end, `end` [`crate::scene::paint::FIELD_RUN_ONLY`]
+    /// past it); a dropdown or button row, a field that is all run (its seam
+    /// as far to the left); a toggle, a well with its run at the end its
+    /// state says (`Toggle::field` and `Toggle::run`). So every flush control
+    /// in the pane has the same edge. They replaced
     /// `troughs()` on 2026-10-01. On the pane's own rule for wells and
     /// troughs: the content band as the outline, the wall straddling it.
     #[allow(clippy::type_complexity)]
-    pub fn fields(&self) -> Vec<(f32, f32, f32, f32, (f32, f32, f32, f32), f32, f32)> {
+    pub fn fields(&self) -> Vec<(f32, f32, f32, f32, (f32, f32, f32, f32), f32, f32, f32)> {
+        use crate::scene::paint::FIELD_RUN_ONLY;
         if !self.visible || !crate::layout::control_relief() {
             return Vec::new();
         }
@@ -1859,7 +1845,7 @@ impl ParametersBg {
                     if w > 0.0 && h - ty > 0.0 && dw > 0.0 {
                         let depth = crate::layout::bevel_width().min((h - ty) * 0.2);
                         let r = crate::layout::textbox_corner_radius();
-                        out.push((x, y + ty, dx + dw - x, h - ty, (r, r, r, r), depth, dx));
+                        out.push((x, y + ty, dx + dw - x, h - ty, (r, r, r, r), depth, dx, dx + dw + FIELD_RUN_ONLY));
                     }
                 }
             } else if p.2 == "button" {
@@ -1872,7 +1858,7 @@ impl ParametersBg {
                     if w > 0.0 && h - ty > 0.0 {
                         let depth = crate::layout::bevel_width().min((h - ty) * 0.2);
                         let r = crate::layout::button_corner_radius();
-                        out.push((x, y + ty, w, h - ty, (r, r, r, r), depth, x - crate::scene::paint::FIELD_RUN_ONLY));
+                        out.push((x, y + ty, w, h - ty, (r, r, r, r), depth, x - FIELD_RUN_ONLY, x + w + FIELD_RUN_ONLY));
                     }
                 }
             } else if p.2.starts_with("choice") {
@@ -1887,7 +1873,7 @@ impl ParametersBg {
                     if w > 0.0 && h - ty > 0.0 {
                         let depth = crate::layout::bevel_width().min((h - ty) * 0.2);
                         let r = crate::layout::dropdown_corner_radius();
-                        out.push((x, y + ty, w, h - ty, (r, r, r, r), depth, x - crate::scene::paint::FIELD_RUN_ONLY));
+                        out.push((x, y + ty, w, h - ty, (r, r, r, r), depth, x - FIELD_RUN_ONLY, x + w + FIELD_RUN_ONLY));
                     }
                 }
             } else if p.2.starts_with("spinbox") {
@@ -1898,10 +1884,33 @@ impl ParametersBg {
                     if let Some(rel) = sb.inner().relief_parts(band) {
                         if let Some((split, _)) = rel.run {
                             let r = rel.radius;
-                            out.push((band.x, band.y, band.width, band.height, (r, r, r, r), rel.depth, split));
+                            out.push((
+                                band.x,
+                                band.y,
+                                band.width,
+                                band.height,
+                                (r, r, r, r),
+                                rel.depth,
+                                split,
+                                band.x + band.width + FIELD_RUN_ONLY,
+                            ));
                         }
                     }
                 }
+            } else if p.2 == "toggle" || p.2 == "checkbox" {
+                // The toggle's own field (`Toggle::field`, `Toggle::run`) —
+                // exactly what its paint draws, on its band. It paints no
+                // fill, so this IS the control.
+                if let Some(t) = &self.toggles[i] {
+                    let (x, y, w, h) = t.rect();
+                    let ty = t.label_strip();
+                    if w > 0.0 && h - ty > 0.0 {
+                        let band = Rect { x, y: y + ty, width: w, height: h - ty };
+                        let (f, radii, depth) = t.inner().field(band);
+                        let (a, b) = t.inner().run(band);
+                        out.push((f.x, f.y, f.width, f.height, radii, depth, a, b));
+                    }
+                }
             }
         }
         out
@@ -2114,10 +2123,10 @@ impl Paint for ParametersBg {
                 (false, false) => ctx.recess_edges(rect, radii, rd, edges),
             }
         }
-        for (fx, fy, fw, fh, radii, fd, split) in self.fields() {
+        for (fx, fy, fw, fh, radii, fd, split, end) in self.fields() {
             let rect = Rect { x: fx, y: fy, width: fw, height: fh };
             let tint = hovered(fx, fy, fw, fh).then_some(Self::HOVER_TINT);
-            ctx.field(rect, radii, fd, split, tint);
+            ctx.field_run(rect, radii, fd, (split, end), tint);
         }
         for (ga, gb, gw, gd, ghost) in self.grooves() {
             ctx.groove(ga, gb, gw, gd, ghost);
@@ -3911,7 +3920,7 @@ mod tests {
         let rr = crate::layout::textbox_corner_radius();
         let fields = p.inner().fields();
         assert_eq!(fields.len(), 1, "{fields:?}");
-        let (fx, fy, fw, fh, fradii, _, split) = fields[0];
+        let (fx, fy, fw, fh, fradii, _, split, _) = fields[0];
         assert_eq!((fx, fy, fx + fw, fy + fh), (tx, dy, dx + dw, dy + dh), "the field spans box and picker");
         assert_eq!(fradii, (rr, rr, rr, rr), "its own radius at all four corners");
         assert_eq!(split, dx, "the seam is where the picker begins");
@@ -3950,7 +3959,7 @@ mod tests {
         let ty = d.label_strip();
         let fields = p.inner().fields();
         assert_eq!(fields.len(), 1, "{fields:?}");
-        let (fx, fy, fw, fh, _, _, split) = fields[0];
+        let (fx, fy, fw, fh, _, _, split, _) = fields[0];
         assert_eq!((fx, fy, fw, fh), (x, y + ty, w, h - ty), "on the trigger's band");
         assert_eq!(split, x - FIELD_RUN_ONLY, "all run, no well");
         let mut pc = PaintCtx::new();
diff --git a/src/widget/input/checkbox.rs b/src/widget/input/checkbox.rs
index 49de12e..20cca87 100644
--- a/src/widget/input/checkbox.rs
+++ b/src/widget/input/checkbox.rs
@@ -237,9 +237,19 @@ impl Input for Checkbox {
     }
 }
 
-/// A plate that slides in a well: the toggle's footprint is a track carved
-/// into the plate it sits on, and a half-width control plate stands on that
-/// well's floor at the left (off) or right (on) end, gliding between them.
+/// A field whose run glides: the toggle's footprint is ONE field
+/// ([`crate::scene::paint::Prim::Field`]) — a well carved into the plate it
+/// sits on, holding a flush run half its width, the run at the left end
+/// (off) or the right end (on) and gliding between them.
+///
+/// It is the same object as a text row with its picker, a spinbox and its
+/// -/+ run, and a dropdown trigger: a well in a plate with a flush plate in
+/// it, one outline round both. Those hold their run still at the right end
+/// (or are all run); a toggle's moves, and where it stands is the state.
+/// Until 2026-10-02 it was a recess with a raised boss standing on its
+/// floor, the one control whose nested plate stood ABOVE the surface where
+/// every other stood flush with it, and whose outline turned its own corner
+/// round the plate rather than running round the whole control.
 ///
 /// That is the ONE toggle style. The rocker — two flat half faces with a
 /// hinge between them, the state half tipped out toward the light — is gone,
@@ -255,11 +265,11 @@ pub struct Toggle {
     /// Where the label sits across the track. Mirrors `Button::justify` — same enum, same
     /// 8px edge inset — so the two read as one control set wherever they share a column.
     justify: Justification,
-    /// Relief style: the track is a real carved well and the glider a raised
-    /// plate standing in it. Without it the track falls back to the hairline
-    /// frame every well shares and the plate to a lit face — see `paint`.
+    /// Relief style: the toggle is a field, a carved well with a flush run in
+    /// it. Without it the well falls back to the hairline frame every well
+    /// shares and the run to a lit face — see `paint`.
     raised: Option<bool>,
-    /// The glider's animated position along its well, 0 (left/off) → 1 (right/on).
+    /// The run's animated position along its field, 0 (left/off) → 1 (right/on).
     /// Chases `toggled` in `tick` after a click; programmatic state syncs
     /// (`set_toggled`, `set_value_string`) snap it, so only user interaction
     /// animates.
@@ -307,89 +317,44 @@ impl Toggle {
         self.toggled
     }
 
-    /// The well the glider lives in: the toggle's whole footprint carved one
-    /// step down. Taken through [`crate::layout::carve_inside`], so the walls
-    /// stay inside the rect and the gap beside a toggle is the gap, exactly as
-    /// a TextBox's well is taken. `(rect, per-corner radii, wall width)`.
+    /// The field's outline: the toggle's whole footprint, carved one step
+    /// down. Taken through [`crate::layout::carve_inside`], so the walls stay
+    /// inside the rect and the gap beside a toggle is the gap, exactly as a
+    /// TextBox's well and a spinbox's field are taken. `(rect, per-corner
+    /// radii, wall width)`.
     ///
-    /// The SINGLE source for the track geometry: `paint` carves it here and
-    /// [`Toggle::slide_plate`] measures the floor from it.
-    pub fn well(&self, rect: Rect) -> (Rect, crate::scene::paint::Radii, f32) {
+    /// The SINGLE source for the toggle's geometry: `paint` draws it here,
+    /// [`Toggle::run`] places the run in it, and a host that draws the relief
+    /// itself (`ParametersBg::fields`) asks for both.
+    pub fn field(&self, rect: Rect) -> (Rect, crate::scene::paint::Radii, f32) {
         let r = crate::layout::toggle_corner_radius();
         let depth = crate::layout::bevel_width().min(rect.height * 0.2);
-        let (well, radii) = crate::layout::carve_inside(rect, (r, r, r, r), depth);
-        (well, radii, depth)
-    }
-
-    /// The sliding plate, as `(footprint, corner radius, wall width)`: half the
-    /// well's floor wide, gliding between the floor's ends by the animated
-    /// `slide_t` — left is off, right is on.
-    ///
-    /// It stands ON the floor — the flat region inside the well's walls, which
-    /// start half a wall in from the well's own boundary — and its roll abuts
-    /// that floor's edge instead of shading over the well's wall. Its corners
-    /// run concentric with the well's.
-    ///
-    /// Its wall is HALF the well's. A plate in a well is the shallower part of
-    /// the pair, and at a control's height it has to be: a toggle is 24px, a
-    /// well wall 4.8, and two full-depth walls stacked leave the boss no flat
-    /// top at all — its own walls meet in the middle and the plate reads as a
-    /// ridge drawn across the track rather than a thing standing in it.
-    pub fn slide_plate(&self, rect: Rect) -> (Rect, f32, f32) {
-        let (well, radii, wd) = self.well(rect);
-        let (floor, floor_r) = inset(well, radii.0, wd * 0.5);
-        let pd = (wd * 0.5).max(1.0);
-        let (travel, pr) = inset(floor, floor_r, pd * 0.5);
-        let pw = travel.width * 0.5;
-        let plate = Rect {
-            x: travel.x + self.slide_t * (travel.width - pw),
-            y: travel.y,
-            width: pw,
-            height: travel.height,
-        };
-        (plate, pr, pd)
-    }
-
-    /// The carves this Toggle paints: the track's well (a `Recess`) and the
-    /// glider standing in it (a `Boss`, the faceless raised plate
-    /// [`crate::scene::paint::PaintCtx::control_plate`] emits) — in that order,
-    /// the order `paint` emits them.
-    ///
-    /// Exists because a Toggle paints NO fill in any state: on a legacy-view
-    /// host (`ParametersBg::reliefs`) these carves are the ENTIRE control, and
-    /// without them the row is a bare label. Empty without `raised` styling,
-    /// where the frame and the lit face stand in for them.
-    pub fn flat_carves(&self, rect: Rect) -> Vec<crate::layout::ReliefCarve> {
-        use crate::layout::{CarveKind, ReliefCarve};
-        if !self.raised() {
-            return Vec::new();
-        }
-        let all = (true, true, true, true);
-        let (well, well_radii, depth) = self.well(rect);
-        let (plate, pr, pd) = self.slide_plate(rect);
-        let (boss, boss_radii) = crate::layout::carve_inside(plate, (pr, pr, pr, pr), pd);
-        vec![
-            ReliefCarve {
-                kind: CarveKind::Recess { tint: None },
-                x: well.x,
-                y: well.y,
-                w: well.width,
-                h: well.height,
-                radii: well_radii,
-                depth,
-                edges: all,
-            },
-            ReliefCarve {
-                kind: CarveKind::Boss { tint: None },
-                x: boss.x,
-                y: boss.y,
-                w: boss.width,
-                h: boss.height,
-                radii: boss_radii,
-                depth: pd,
-                edges: all,
-            },
-        ]
+        let (field, radii) = crate::layout::carve_inside(rect, (r, r, r, r), depth);
+        (field, radii, depth)
+    }
+
+    /// The flush run, as the xs it spans in [`Toggle::field`]: half the field
+    /// wide, placed by the animated `slide_t` — flush with the field's left
+    /// end off, its right end on, a well on both sides between. As a text
+    /// row's picker is, the run is laid out a wall wider than the face it
+    /// carries (the field insets the face half a wall from the outline and
+    /// from the seam), so the face reaches the well.
+    pub fn run(&self, rect: Rect) -> (f32, f32) {
+        let (field, _, _) = self.field(rect);
+        let w = field.width * 0.5;
+        let x = field.x + self.slide_t * (field.width - w);
+        (x, x + w)
+    }
+
+    /// The run's FACE — what stands at the surface's level inside the
+    /// field's valley: the run inset half a wall on every side, its corners
+    /// the field's less that. `(rect, corner radius)`. The relief-off paint
+    /// lights this rect, where the field cannot be carved.
+    pub fn face(&self, rect: Rect) -> (Rect, f32) {
+        let (field, radii, depth) = self.field(rect);
+        let (a, b) = self.run(rect);
+        let run = Rect { x: a, y: field.y, width: b - a, height: field.height };
+        inset(run, radii.0, depth * 0.5)
     }
 }
 
@@ -448,47 +413,41 @@ impl Paint for Toggle {
     }
 
     fn paint(&self, rect: Rect, ctx: &mut PaintCtx) {
-        use crate::scene::paint::{ControlPlate, PlateStance};
+        use crate::scene::paint::ControlPlate;
         let (x, y, w, h) = (rect.x, rect.y, rect.width, rect.height);
 
         // A toggle paints NO fill of its own: it is worked out of the plate it
         // sits on, so the plate's own material (tint, blur, whatever it is)
-        // shows through both the well's floor and the glider's face, and the
+        // shows through both the well's floor and the run's face, and the
         // state reads from light and relief alone — the DE's transparent-face
         // convention (closed dropdowns, inset troughs). The state colors this
         // used to tint with (enabled/disabled/background) are retired with the
         // rest of the toggle's palette.
-        let (well, well_radii, depth) = self.well(rect);
-        let (plate, plate_r, plate_depth) = self.slide_plate(rect);
+        let (field, field_radii, depth) = self.field(rect);
         if self.raised() {
-            // The track is a WELL — the same recess a TextBox carves — and the
-            // glider is a control plate standing on its floor, raised faceless
-            // (a `Boss`, the surface below as its face). Its position IS the
-            // read: left off, right on, animated in `tick`. Both also reach
-            // legacy-view hosts through `flat_carves` (see
-            // `ParametersBg::reliefs`), which must emit them in this order.
-            ctx.recess(well, well_radii, depth);
-            let focus = if self.focused { Some(ControlPlate::focus_tint()) } else { None };
-            ctx.control_plate(
-                &ControlPlate::control(plate, plate_r, PlateStance::Raised, None)
-                    .with_depth(plate_depth)
-                    .with_tint(focus),
-            );
+            // ONE field — the well and the flush run in it under one outline,
+            // as a text row's picker and a spinbox's -/+ run are drawn. The
+            // run's position IS the read: left off, right on, animated in
+            // `tick`. A host that draws the relief itself
+            // (`ParametersBg::fields`) asks for the same `field` and `run`.
+            // Focus lights the field's rim, the ring every field wears.
+            let focus = self.focused.then(ControlPlate::focus_tint);
+            ctx.field_run(field, field_radii, depth, self.run(rect), focus);
         } else {
             // Relief off: a well is its frame — the one hairline every well
             // falls back to, lit while focused, exactly `well_rim`'s flat arm —
-            // and the plate standing in it is a lit face, the neutral overlay
-            // the DE gives a surface it cannot carve (what the rocker's halves
-            // wore). Scaled by the relief strength, as that lighting was.
+            // and the run's face is a lit face, the neutral overlay the DE
+            // gives a surface it cannot carve (what the rocker's halves wore).
+            // Scaled by the relief strength, as that lighting was.
             //
-            // The plate's overlay is a ROUNDED RECT on purpose: the legacy
+            // The face's overlay is a ROUNDED RECT on purpose: the legacy
             // reverse bridge reads only those, never a `Border`, so a
-            // legacy-view host (`ParametersBg`, whose carves are gated on
-            // relief) still shows which end the plate is at.
+            // legacy-view host still shows which end the run is at.
             let bw = crate::layout::toggle_border_width().max(1.0);
-            ctx.border(well, well_radii, [0.0; 4], colors::well_frame_color(self.hovered, self.focused), bw);
+            ctx.border(field, field_radii, [0.0; 4], colors::well_frame_color(self.hovered, self.focused), bw);
+            let (face, face_r) = self.face(rect);
             let lit = (0.16 * (crate::layout::bevel_depth() / 0.15)).clamp(0.0, 0.5);
-            ctx.rounded_rect(plate, plate_r, (true, true, true, true), [1.0, 1.0, 1.0, lit]);
+            ctx.rounded_rect(face, face_r, (true, true, true, true), [1.0, 1.0, 1.0, lit]);
         }
 
         if let Some(ref label) = self.label {
@@ -499,13 +458,13 @@ impl Paint for Toggle {
                 Justification::Right => x + w - est_w - crate::layout::CONTROL_TEXT_INSET,
                 Justification::Center => x + (w - est_w) / 2.0,
             };
-            // The label never moves with the state. When the glider covers it
-            // the text shows through: the plate carries no face of its own, and
+            // The label never moves with the state. When the run covers it
+            // the text shows through: the run carries no face of its own, and
             // the glyphs land in the engine's later text pass either way.
             //
-            // Focus is the glider plate's own lit rim (`ControlPlate::with_tint`)
-            // — the ring every other plate wears, which the rocker's partial
-            // carves could not — so the label stays the label.
+            // Focus is the field's own lit rim — the ring every other field
+            // wears, which the rocker's partial carves could not — so the
+            // label stays the label.
             ctx.text_with(
                 label.clone(),
                 tx,
@@ -685,7 +644,7 @@ mod tests {
     }
 
     #[test]
-    fn toggle_click_glides_the_plate_across_its_well() {
+    fn toggle_click_glides_the_run_across_its_field() {
         let mut ctx = UiContext::new();
         let mut t = Toggle::new();
         let (id, ptr) = (t.id(), t.as_ptr_mut());
@@ -699,7 +658,7 @@ mod tests {
             pc.finish().items.into_iter().map(|i| format!("{:?}", i.prim)).collect::<Vec<_>>()
         };
         let before = painted(&t);
-        let plate_x = |t: &Adapted<Toggle>| t.inner().slide_plate(rect).0.x;
+        let plate_x = |t: &Adapted<Toggle>| t.inner().run(rect).0;
         let left = plate_x(&t);
 
         assert!(ctx.propagate_event(&click_at(30.0, 15.0), id), "toggle consumed the click");
@@ -720,57 +679,48 @@ mod tests {
         assert_eq!(WidgetHost::preferred_height(&t), Some(crate::layout::toggle_height()));
     }
 
-    /// The plate stands ON the well's floor, clear of its walls by one wall
-    /// width on every side, and travels between the floor's ends: off is flush
-    /// left, on is flush right, and it never reaches outside the toggle's rect.
+    /// The toggle is ONE field, the form a text row's picker and a spinbox's
+    /// -/+ run take: off, its run is the left half and the well the right;
+    /// on, the other way round; between, a well either side. The run's face
+    /// stands half a wall inside the run, so the run reaches the well.
     #[test]
-    fn toggle_plate_lives_inside_the_well() {
+    fn a_toggle_is_a_field_whose_run_glides() {
+        use crate::scene::paint::{Prim, FIELD_RUN_ONLY};
         let rect = Rect { x: 10.0, y: 4.0, width: 120.0, height: 24.0 };
-        let mut t = Toggle::new();
+        let mut t = Toggle::new().with_raised(true);
+        let (field, radii, depth) = t.inner().field(rect);
+        assert!(field.x >= rect.x && field.y >= rect.y, "the field carves inside the rect");
+        let (fl, fr) = (field.x, field.x + field.width);
 
-        let (well, _, depth) = t.inner().well(rect);
-        assert!(well.x >= rect.x && well.y >= rect.y, "the well carves inside the rect");
-
-        // The floor is the flat region inside the well's walls; the plate's own
-        // (half-depth) roll abuts its edge, so the plate is inset one more
-        // half-wall of its own from there.
-        let (off, _, pd) = t.inner().slide_plate(rect);
-        assert!((pd - depth * 0.5).abs() < 1e-4, "the plate's wall is half the well's");
-        let edge = depth * 0.5 + pd * 0.5;
-        assert!((off.x - (well.x + edge)).abs() < 1e-4, "off sits at the travel's left end");
-        assert!((off.y - (well.y + edge)).abs() < 1e-4, "and clear of the floor's top");
-        assert!(
-            off.height > pd * 2.0,
-            "the plate keeps a flat top: a boss no taller than its own wall is a ridge",
-        );
+        let fields = |t: &Adapted<Toggle>| -> Vec<Prim> {
+            let mut pc = crate::scene::paint::PaintCtx::new();
+            crate::widget::Paint::paint(t.inner(), rect, &mut pc);
+            pc.finish().items.into_iter().map(|i| i.prim).filter(|p| !matches!(p, Prim::Text { .. })).collect()
+        };
+        let off = fields(&t);
+        assert_eq!(off.len(), 1, "one prim, the field: {off:?}");
+        let Prim::Field { rect: r, radii: rr, depth: d, split, end, tint } = off[0] else { panic!("{off:?}") };
+        assert_eq!((r, rr, d, tint), (field, radii, depth, None));
+        assert_eq!(split, fl, "off: the run reaches the field's left end — no well there");
+        assert!((end - (fl + field.width * 0.5)).abs() < 1e-4, "half the field, the well beyond it");
 
         t.set_toggled(true); // programmatic syncs snap, so this is the on-end geometry
-        let (on, _, _) = t.inner().slide_plate(rect);
-        assert!(on.x > off.x, "on is to the right of off");
-        assert!(
-            (on.x + on.width - (well.x + well.width - edge)).abs() < 1e-4,
-            "on sits flush against the travel's right end",
-        );
-        assert!(on.x + on.width <= rect.x + rect.width, "and never leaves the toggle's rect");
-        assert!((off.width * 2.0 - (well.width - 2.0 * edge)).abs() < 1e-4, "half the travel wide");
-    }
-
-    /// The carves a legacy-view host re-emits (`ParametersBg::reliefs`) are the
-    /// well then the plate, in the order `paint` emits them — and nothing at
-    /// all with relief off, where the flat frames stand in.
-    #[test]
-    fn toggle_flat_carves_are_the_well_then_the_plate() {
-        use crate::layout::CarveKind;
-        let rect = Rect { x: 0.0, y: 0.0, width: 120.0, height: 24.0 };
-        let t = Toggle::new().with_raised(true);
-        let carves = t.inner().flat_carves(rect);
-        assert_eq!(carves.len(), 2);
-        assert!(matches!(carves[0].kind, CarveKind::Recess { .. }), "the track's well first");
-        assert!(matches!(carves[1].kind, CarveKind::Boss { .. }), "then the plate standing in it");
-        assert!(carves.iter().all(|c| c.edges == (true, true, true, true)), "both are full rings");
-
-        let flat = Toggle::new().with_raised(false);
-        assert!(flat.inner().flat_carves(rect).is_empty(), "relief off carves nothing");
+        let Prim::Field { split, end, .. } = fields(&t)[0] else { panic!() };
+        assert!((split - (fl + field.width * 0.5)).abs() < 1e-4 && (end - fr).abs() < 1e-4, "on: the right half");
+
+        t.slide_t = 0.5;
+        let Prim::Field { split, end, .. } = fields(&t)[0] else { panic!() };
+        assert!(split > fl + 1.0 && end < fr - 1.0, "mid-glide, a well either side of the run");
+        assert!(end < fr + FIELD_RUN_ONLY, "and the run's end is its own, not the field's");
+
+        let (face, face_r) = t.inner().face(rect);
+        let (a, b) = t.inner().run(rect);
+        assert!((face.x - (a + depth * 0.5)).abs() < 1e-4 && (face.x + face.width - (b - depth * 0.5)).abs() < 1e-4);
+        assert!((face_r - (radii.0 - depth * 0.5).max(0.0)).abs() < 1e-4, "concentric with the field");
+
+        // Focus lights the field's rim.
+        t.focused = true;
+        assert!(matches!(fields(&t)[0], Prim::Field { tint: Some(_), .. }), "focus tints the field");
     }
 
     #[test]