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

commit28e12b052f707b602bd519c3f8189f4db87df0ea
parentc416cdab96
authorLucas Galante <[email protected]>
date2026-07-22 12:21
style: rocker toggle halves are flat steps, not tilted planes

The state read no longer comes from lit/shaded face tints (a tilted
plane's uniform normal): both halves stay flat and untinted, the state
half a plateau raised out of the pill and the other recessed, meeting
in a single step at the hinge. rocker_faces folds into rocker_reliefs
as the one geometry source.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/widget/container/parameters_bg.rs | 10 ++---
 src/widget/input/checkbox.rs          | 79 +++++++++++------------------------
 2 files changed, 29 insertions(+), 60 deletions(-)

diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index 7c639ca..b806bcf 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -1024,11 +1024,11 @@ impl ParametersBg {
             } else if p.2 == "button" {
                 self.buttons[i].as_ref().map(|w| (w as &dyn WidgetHost, crate::layout::button_corner_radius(), true))
             } else if p.2 == "toggle" || p.2 == "checkbox" {
-                // The rocker's flat faces are RoundedRect prims — they arrive
-                // through the rounded-quad view. The entries here are the
-                // faces' beveled edges (`Toggle::rocker_reliefs`, exactly what
-                // the widget's own raised paint emits): the state half's lip
-                // rises, the other falls away, hinge wall open on both.
+                // The rocker's bg pill arrives through the rounded-quad view;
+                // the entries here are its two flat halves' beveled edges
+                // (`Toggle::rocker_reliefs`, exactly what the widget's own
+                // raised paint emits): the state half a raised plateau, the
+                // other recessed, hinge wall open on both.
                 if let Some(t) = &self.toggles[i] {
                     let (x, y, w, h) = t.rect();
                     if w > 0.0 && h > 0.0 {
diff --git a/src/widget/input/checkbox.rs b/src/widget/input/checkbox.rs
index 4995e4b..cda7839 100644
--- a/src/widget/input/checkbox.rs
+++ b/src/widget/input/checkbox.rs
@@ -226,10 +226,10 @@ pub struct Toggle {
     /// Where the label sits across the pill. Mirrors `Button::justify` — same enum, same
     /// 8px edge inset — so the two read as one control set wherever they share a column.
     justify: Justification,
-    /// Raised style: the pill renders as a rocker — two angled faces with
-    /// beveled edges, the state half raised, the other recessed (see
-    /// `rocker_faces` and the paint impl) — and the flat style's state
-    /// gradient is dropped.
+    /// Raised style: the pill renders as a rocker — two flat half faces with
+    /// beveled edges, the state half a raised plateau, the other recessed
+    /// (see `rocker_reliefs` and the paint impl) — and the flat style's
+    /// state gradient is dropped.
     raised: bool,
 }
 
@@ -266,52 +266,26 @@ impl Toggle {
         }
     }
 
-    /// The rocker's two flat faces over `rect`: (half rect, corner flags,
-    /// tint). The pill reads as a plate pivoting about its horizontal midline —
-    /// two flat ANGLED surfaces meeting at the hinge, not stepped plateaus: the
-    /// STATE half (top when on, bottom when off) tilts out and catches the
-    /// light (white tint), the other tilts away into shadow (black tint). Flat
-    /// uniform tints — a tilted plane has one normal — so the faces flow
-    /// through the rounded-quad views with no relief machinery.
-    pub fn rocker_faces(
+    /// The rocker's two halves over `rect` as FLAT relief steps: (half rect,
+    /// per-corner radii, (top, right, bottom, left) walls, raised). The state
+    /// half (top when on, bottom when off) is a plateau raised out of the
+    /// surface (raised → `boss_edges`), the other falls away (`recess_edges`)
+    /// — both faces stay flat and untinted, so the state reads entirely from
+    /// the beveled edges. The hinge wall is open on both so the halves meet
+    /// in a single step, not a double-shaded trough.
+    pub fn rocker_reliefs(
         &self,
         rect: Rect,
-    ) -> [(Rect, (bool, bool, bool, bool), [f32; 4]); 2] {
-        const LIT: [f32; 4] = [1.0, 1.0, 1.0, 0.10];
-        const SHADED: [f32; 4] = [0.0, 0.0, 0.0, 0.16];
+    ) -> [(Rect, (f32, f32, f32, f32), (bool, bool, bool, bool), bool); 2] {
+        let r = crate::layout::toggle_corner_radius();
         let half_h = rect.height / 2.0;
         let top = Rect { x: rect.x, y: rect.y, width: rect.width, height: half_h };
         let bottom =
             Rect { x: rect.x, y: rect.y + half_h, width: rect.width, height: rect.height - half_h };
-        let top_corners = (true, true, false, false);
-        let bottom_corners = (false, false, true, true);
-        if self.toggled {
-            [(top, top_corners, LIT), (bottom, bottom_corners, SHADED)]
-        } else {
-            [(bottom, bottom_corners, LIT), (top, top_corners, SHADED)]
-        }
-    }
-
-    /// The rocker faces' beveled edges over `rect`: (half rect, per-corner
-    /// radii, (top, right, bottom, left) walls, raised). Companion to
-    /// `rocker_faces`, in the same order — the state half's lip rises out of
-    /// the surface (raised → `boss_edges`), the other falls away
-    /// (`recess_edges`). The hinge wall is open on both so the faces meet in
-    /// a crease, the recessed side reading as a step down from the raised
-    /// one rather than a double-shaded trough.
-    pub fn rocker_reliefs(
-        &self,
-        rect: Rect,
-    ) -> [(Rect, (f32, f32, f32, f32), (bool, bool, bool, bool), bool); 2] {
-        let r = crate::layout::toggle_corner_radius();
-        let mut state_half = true;
-        self.rocker_faces(rect).map(|(half, corners, _)| {
-            let is_top = corners.0;
-            let radii = if is_top { (r, r, 0.0, 0.0) } else { (0.0, 0.0, r, r) };
-            let walls = if is_top { (true, true, false, true) } else { (false, true, true, true) };
-            let raised = std::mem::take(&mut state_half);
-            (half, radii, walls, raised)
-        })
+        let top_half = (top, (r, r, 0.0, 0.0), (true, true, false, true));
+        let bottom_half = (bottom, (0.0, 0.0, r, r), (false, true, true, true));
+        let (state, other) = if self.toggled { (top_half, bottom_half) } else { (bottom_half, top_half) };
+        [(state.0, state.1, state.2, true), (other.0, other.1, other.2, false)]
     }
 }
 
@@ -372,14 +346,12 @@ impl Paint for Toggle {
         let bg = colors::toggle_bg_color();
 
         if self.raised {
-            // The rocker: two flat angled faces pivoting about the midline
-            // (see `rocker_faces`) — the physical read of the flat style's
-            // state gradient, which this style drops entirely. Each face
-            // carries a beveled edge along its outer perimeter: the state
-            // face's lip rises (boss), the other's falls away (recess). The
-            // hinge wall is omitted on both so the faces still meet in a
-            // crease, the recessed side reading as a step down from the
-            // raised one.
+            // The rocker: two FLAT half faces (see `rocker_reliefs`) — the
+            // state half a raised plateau, the other recessed, hinge wall
+            // open so they meet in a single step. The faces carry no tint;
+            // the state reads entirely from the beveled edges. This is the
+            // physical read of the flat style's state gradient, which this
+            // style drops entirely.
             if bg[3] > 0.001 {
                 if radius > 0.0 {
                     ctx.rounded_rect(rect, radius, (true, true, true, true), bg);
@@ -387,9 +359,6 @@ impl Paint for Toggle {
                     ctx.quad(rect, bg);
                 }
             }
-            for (half, corners, tint) in self.rocker_faces(rect) {
-                ctx.rounded_rect(half, radius, corners, tint);
-            }
             let depth = crate::layout::bevel_width().min(h * 0.2);
             for (half, radii, walls, raised) in self.rocker_reliefs(rect) {
                 if raised {