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

commitd30d3eb3696c7eaf9ce79d36527d8cb182a27177
parenta1204b85f7
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-02 10:37
feat: a field's run has the same padding and rounding at both ends

The run's face (a text row's picker, a spinbox's -/+) met the seam square,
its lip being the outline's own inner half. It is a rounded rect of its
own now, inset half a wall from the seam as from the outline, its left
corners its right corners.

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

 CLAUDE.md            | 10 ++++--
 src/vk/shader2d.wgsl | 86 +++++++++++++++++++++++++++++++++++++---------------
 2 files changed, 70 insertions(+), 26 deletions(-)

diff --git a/CLAUDE.md b/CLAUDE.md
index 3423e65..a7fe12d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -337,8 +337,14 @@ the inner half differs: on to the floor in the well, mirrored back up to the fac
 run, blended across one wall width about the seam. Note the run's valley is NOT
 `Prim::Trough`'s, which fits its whole fall and rise into the wall's width, so its outer
 half is a compressed copy of a step that read differently from the well's beside it. The
-seam is the well's floor rising to the run's face — a step along `split` — fading to
-nothing at the outline, where both sides stand at half the step. Never grouped into a host
+run's FACE is a rounded rect of its own, inset half a wall on every side — from the
+outline on the top, right and bottom, from the seam on the left — whose lip is the
+well's fall mirrored back up, and whose left corners are its right corners, so the
+button has the same padding and rounding at both ends (since 2026-10-02; until then
+the lip was the outline's own inner half and the face met the seam square). Where the
+face's rounded corner leaves room by the straight outline is the valley's flat floor.
+The seam is the well's right wall's inner half: from half the step at `split` down to
+the floor, meeting the face's lip there at the same height, faded out at the outline. Never grouped into a host
 plate (its profile is not a monotonic step); the overlay's host-box slot carries `split`.
 The legacy banded path and flat hosts (`layout.rs`'s bridge) draw the two-box form.
 
diff --git a/src/vk/shader2d.wgsl b/src/vk/shader2d.wgsl
index 1f5efd7..f095b89 100644
--- a/src/vk/shader2d.wgsl
+++ b/src/vk/shader2d.wgsl
@@ -897,31 +897,69 @@ fn plate_shade(frag: vec2f, vcol: vec4f) -> vec4f {
     if (eff == MODE_FIELD) {
         // A well (the interior one step DOWN) left of the seam at
         // p_host.x, a flush run (a valley on the outline, the interior back
-        // at the surface) right of it — one outline. The run's valley is
-        // NOT MODE_TROUGH's: that one fits its whole fall and rise into the
-        // wall's width, so its outer half is a compressed copy of a step and
-        // the edge read differently either side of the seam. Here the
-        // valley's outer half IS the well's — the same fall, the same
-        // shoulder — and its inner half that fall mirrored back up to the
-        // face. So the outline's outer half is one profile all the way round
-        // the field, and only the inner half, which the run's face really
-        // does rise from, blends across one wall width about the seam.
+        // at the surface) right of it — one outline. Three walls:
+        //
+        // * The OUTLINE, whose outer half — the fall from the surface to
+        //   half the step — is the well's profile all the way round the
+        //   field, so the edge never breaks. Over the well its inner half
+        //   falls on to the floor; over the run it is not the outline's at
+        //   all (below). (MODE_TROUGH fits its whole fall and rise into the
+        //   wall's width, a compressed copy of a step that read differently
+        //   from the well beside it; this valley's outer half IS the well's.)
+        // * The run's FACE, a rounded rect inset half a wall on every side
+        //   — from the outline on the top, right and bottom, from the seam
+        //   on the left — whose lip is the fall mirrored back up. Its left
+        //   corners are its right corners, so the button reads the same at
+        //   both ends: the same padding, the same rounding. Where the face's
+        //   rounded corner leaves room by the straight outline, that is the
+        //   valley's floor, flat.
+        // * The SEAM, the well's own right wall's inner half: from half the
+        //   step at the seam down to the floor, meeting the face's lip at
+        //   the seam where both stand at half the step.
+        //
+        // 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.
         let split = rrect_clip.p_host.x;
-        let wf = smoothstep(split - 0.5 * t, split + 0.5 * t, frag.x);
-        let s_rec = -cd * carve_slope(u);
-        let c_rec = rrect_clip.p_mat.w * sin(u * TAU);
-        let s_tr = select(s_rec, cd * carve_slope(1.0 - u), u > 0.5);
-        let c_tr = rrect_clip.p_mat.w * sin(min(u, 1.0 - u) * TAU);
-        slope = mix(s_rec, s_tr, wf);
-        curv = mix(c_rec, c_tr, wf);
-        // The seam: the well's floor rising to the run's face — the well's
-        // own right wall, a step along x = split. Full inside the field,
-        // gone at the outline, where the outer wall stands both sides at
-        // half the step and there is nothing between them to climb.
-        let us = clamp((split - frag.x) / t + 0.5, 0.0, 1.0);
-        let fade = clamp(d / (0.5 * t), 0.0, 1.0);
-        sv_seam = vec2f(1.0, 0.0) * (-cd * carve_slope(us)) * fade;
-        curv_seam = rrect_clip.p_mat.w * sin(us * TAU) * fade;
+        let hw = 0.5 * t;
+        let pr = rrect_clip.p_rect;
+        let fl = pr.x - pr.z;
+        let has_well = split > fl + 0.5;
+        let run_l = select(fl, split, has_well);
+        let fx0 = run_l + hw;
+        let fx1 = pr.x + pr.z - 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 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);
+        // 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);
+        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;
+        // The face's lip: half a wall wide, rising to the face.
+        let gf = rr_sdf_grad(frag, face, face_r);
+        let df = -gf.z;
+        if (df >= -hw && df <= 0.0) {
+            let uf = df / t + 1.0;
+            sv_seam = gf.xy * (cd * carve_slope(1.0 - uf));
+            curv_seam = rrect_clip.p_mat.w * sin((1.0 - uf) * TAU);
+        }
+        // The seam: the well's right wall, its inner half, faded out at the
+        // outline (outside the field there is no well to fall into).
+        if (has_well) {
+            let us = (split - frag.x) / 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