git.lucas.co / cce-system-interface
system settings
git clone https://git.lucas.co/cce-system-interface.git

commit8759098e2dbb1b5971788063ef6f0e5c582f4e0d
parent858f68cc82
authorLucas Galante <[email protected]>
date2026-08-24 13:02
settings: re-emit Button and Toggle carves with the rest

Follows the toolkit's bridge from one hook per widget to one carve type:
`control_reliefs` is now a `Vec<ControlCarve>` — `Plate` (the inset face a
Dropdown or a Button draws) or `Step` (a `cce_ui::layout::ReliefCarve`,
which carries its own rect, per-corner radii, depth and wall mask, so nothing
here re-derives them). The scroll shift the popover collector applies moved
onto `ControlCarve::shifted_y`, one place instead of a tuple rebuild at each
site.

Buttons need one extra step: they never reach `render_widget` in this app,
because it collects them into `pc.buttons` and draws them itself, so the
toolkit's bridge cannot see them. The page carve list now asks each collected
button for `Button::inset_face` — the same source the drawn one reads — and
carves it in page coords with everything else.

Verified live headless on the Browser page: Record History draws its rocker
where there was previously nothing but a label, and Apply gained its groove
ring (measured trough profile at its top edge). Dropdowns, text fields and the
open dropdown popover are unchanged.

 src/app.rs      | 49 ++++++++++++++++++++++++++++++++-----------------
 src/main.rs     | 34 +++++++++++++++++++---------------
 src/renderer.rs | 35 ++++++++++++++++++++++++++++++++---
 3 files changed, 83 insertions(+), 35 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index 9d89605..5624342 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -124,18 +124,33 @@ pub enum AppAction {
 }
 
 
-/// A control carve bridged out of a widget's `paint` for the flat path: the
-/// two relief idioms the toolkit's controls draw for themselves, which the
-/// legacy `all_quads` stream cannot carry. The page collects them and
-/// `display_list` re-emits them as real prims into the window plate.
+/// A control carve bridged out of a widget's `paint` for the flat path — the
+/// relief prims the toolkit's controls draw for themselves and the legacy
+/// `all_quads` stream cannot carry. The page collects them and `display_list`
+/// re-emits them as real prims into the window plate.
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub enum ControlCarve {
     /// `PaintCtx::inset_plate` — a flush inset face over a boundary seam
-    /// (Dropdown). `color` fills the face; transparent leaves the plate below.
-    Plate { color: [f32; 4] },
-    /// `PaintCtx::recess` / `recess_tinted` — a sunken well (TextBox).
-    /// `tint` is the focus accent on the rim while the box is being edited.
-    Well { tint: Option<[f32; 3]> },
+    /// (Dropdown, Button). `color` fills the face; transparent leaves the
+    /// plate below.
+    Plate { x: f32, y: f32, w: f32, h: f32, radius: f32, depth: f32, color: [f32; 4] },
+    /// A step carve straight from the toolkit (TextBox well, Toggle rocker
+    /// halves and glider) — it already carries its own rect, per-corner radii,
+    /// depth and wall mask, so nothing here re-derives them.
+    Step(cce_ui::layout::ReliefCarve),
+}
+
+impl ControlCarve {
+    /// This carve shifted vertically — the page-scroll adjustment the popover
+    /// collector applies when it lifts page carves to the popover layer.
+    pub fn shifted_y(self, dy: f32) -> Self {
+        match self {
+            Self::Plate { x, y, w, h, radius, depth, color } => {
+                Self::Plate { x, y: y + dy, w, h, radius, depth, color }
+            }
+            Self::Step(c) => Self::Step(c.shifted_y(dy)),
+        }
+    }
 }
 
 pub struct PageContent {
@@ -154,7 +169,7 @@ pub struct PageContent {
     /// `RenderTarget::recess` for a TextBox's well) — (x, y, w, h, radius,
     /// depth, carve), page coordinates, pre-scroll; display_list re-emits them
     /// as real relief prims.
-    pub control_reliefs: Vec<(f32, f32, f32, f32, f32, f32, ControlCarve)>,
+    pub control_reliefs: Vec<ControlCarve>,
     pub clip_stack: Vec<[f32; 4]>,
     pub measure_only: bool,
 }
@@ -325,18 +340,18 @@ impl RenderTarget for PageContent {
         if self.measure_only {
             return;
         }
-        self.control_reliefs.push((x, y, w, h, radius, depth, ControlCarve::Plate { color }));
+        self.control_reliefs.push(ControlCarve::Plate { x, y, w, h, radius, depth, color });
     }
 
-    /// The same bridge for a TextBox's well — a DIFFERENT carve, not the same
-    /// one at another rect: a trough is a seam about the boundary with the face
-    /// level, a well drops the whole interior. Collapsing them would give the
-    /// settings app text fields no other relief host has.
-    fn recess(&mut self, x: f32, y: f32, w: f32, h: f32, radius: f32, depth: f32, tint: Option<[f32; 3]>) {
+    /// The same bridge for the step carves — a DIFFERENT shape, not an inset
+    /// plate at another rect: a trough is a seam about the boundary with the
+    /// face left level, a well drops the whole interior, a boss raises it.
+    /// Collapsing them would give this app controls no other relief host has.
+    fn relief_carve(&mut self, carve: &cce_ui::layout::ReliefCarve) {
         if self.measure_only {
             return;
         }
-        self.control_reliefs.push((x, y, w, h, radius, depth, ControlCarve::Well { tint }));
+        self.control_reliefs.push(ControlCarve::Step(*carve));
     }
 
     fn section_relief(&mut self, f: &cce_ui::layout::SectionFrame) -> bool {
diff --git a/src/main.rs b/src/main.rs
index bec1283..af72d5a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -46,7 +46,7 @@ struct SystemInterface {
     popover_texts: Vec<(String, f32, f32, f32, [f32; 4], Option<String>, Option<[f32; 4]>)>,
     /// Popover-layer inset plates (window coords, scroll already applied) —
     /// the dropdown's grown-trigger surface via the inset_plate hook.
-    popover_control_reliefs: Vec<(f32, f32, f32, f32, f32, f32, ControlCarve)>,
+    popover_control_reliefs: Vec<ControlCarve>,
     page_buttons: Vec<(cce_ui::widget::Adapted<cce_ui::widget::Button>, AppAction)>,
 
     sidebar_width: f32,
@@ -108,7 +108,7 @@ struct SystemInterface {
     page_reliefs: Vec<((f32, f32, f32, f32), Option<(f32, f32, f32, f32)>)>,
     /// Control troughs from `PageContent::control_reliefs` (page coords,
     /// pre-scroll) — each carved as a flush inset plate after the section wells.
-    page_control_reliefs: Vec<(f32, f32, f32, f32, f32, f32, ControlCarve)>,
+    page_control_reliefs: Vec<ControlCarve>,
     // Root Backplate + StatusBar DISSOLVED (Phase 6s): the window plate and the status
     // bar are emitted as tuples in rebuild_layout.
     sans_serif_family: String,
@@ -417,13 +417,15 @@ impl cce_ui::engine::Application for SystemInterface {
             };
             let scroll_y = self.scroll_y;
             pc.clip(view, |pc| {
-                for &(x, y, w, h, radius, depth, carve) in &self.page_control_reliefs {
-                    let rect = Rect { x, y: y - scroll_y, width: w, height: h };
-                    let radii = (radius, radius, radius, radius);
-                    match carve {
-                        ControlCarve::Plate { color } => pc.inset_plate(rect, radii, color, depth),
-                        ControlCarve::Well { tint: Some(t) } => pc.recess_tinted(rect, radii, depth, t),
-                        ControlCarve::Well { tint: None } => pc.recess(rect, radii, depth),
+                for &carve in &self.page_control_reliefs {
+                    match carve.shifted_y(-scroll_y) {
+                        ControlCarve::Plate { x, y, w, h, radius, depth, color } => pc.inset_plate(
+                            Rect { x, y, width: w, height: h },
+                            (radius, radius, radius, radius),
+                            color,
+                            depth,
+                        ),
+                        ControlCarve::Step(c) => pc.carve(&c),
                     }
                 }
             });
@@ -444,13 +446,15 @@ impl cce_ui::engine::Application for SystemInterface {
         // Popover surfaces claimed through the inset_plate hook (the dropdown's
         // grown-trigger plate): real relief prims at the popover layer, over
         // the page and its control troughs.
-        for &(x, y, w, h, radius, depth, carve) in &self.popover_control_reliefs {
-            let rect = Rect { x, y, width: w, height: h };
-            let radii = (radius, radius, radius, radius);
+        for &carve in &self.popover_control_reliefs {
             match carve {
-                ControlCarve::Plate { color } => pc.inset_plate(rect, radii, color, depth),
-                ControlCarve::Well { tint: Some(t) } => pc.recess_tinted(rect, radii, depth, t),
-                ControlCarve::Well { tint: None } => pc.recess(rect, radii, depth),
+                ControlCarve::Plate { x, y, w, h, radius, depth, color } => pc.inset_plate(
+                    Rect { x, y, width: w, height: h },
+                    (radius, radius, radius, radius),
+                    color,
+                    depth,
+                ),
+                ControlCarve::Step(c) => pc.carve(&c),
             }
         }
         for (text, font_size, x, y, col, font, bounds) in self.texts.iter().chain(self.popover_texts.iter()) {
diff --git a/src/renderer.rs b/src/renderer.rs
index aa280aa..6456898 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -1,5 +1,5 @@
 use crate::{SystemInterface, AppWidget, make_text_buffer_with_font};
-use cce_settings::app::PageContent;
+use cce_settings::app::{ControlCarve, PageContent};
 use cce_settings::pages::Page;
 use cce_ui::widget::WidgetHost;
 
@@ -264,6 +264,35 @@ impl SystemInterface {
         let pc = self.render_page_content(lcx, lcy, lcw, lch);
         self.page_reliefs = pc.reliefs.clone();
         self.page_control_reliefs = pc.control_reliefs.clone();
+        // Buttons never reach the toolkit's flat-path bridge here: this app
+        // collects them into `pc.buttons` and draws them itself, so
+        // `render_widget` — where a Dropdown's and a TextBox's carves are
+        // offered — never sees one. Ask each button for the inset face its own
+        // `paint` draws (`Button::inset_face`, the same source the drawn one
+        // reads) and carve it with the rest. Page coords, pre-scroll, like
+        // everything else in this list.
+        if cce_ui::layout::control_relief() {
+            for (btn, _, _) in &pc.buttons {
+                let base = btn.base();
+                let rect = cce_ui::scene::layout::Rect {
+                    x: base.x,
+                    y: base.y,
+                    width: base.w,
+                    height: base.h,
+                };
+                if let Some((face, radius, depth, color)) = btn.inset_face(rect) {
+                    self.page_control_reliefs.push(ControlCarve::Plate {
+                        x: face.x,
+                        y: face.y,
+                        w: face.width,
+                        h: face.height,
+                        radius,
+                        depth,
+                        color,
+                    });
+                }
+            }
+        }
 
 
 
@@ -602,8 +631,8 @@ impl SystemInterface {
                 let shifted = bounds.map(|[l, tb, rr, b]| [l, tb - self.scroll_y, rr, b - self.scroll_y]);
                 popover_pc.texts.push((t, size, x, y - self.scroll_y, tc, font, shifted));
             }
-            for (x, y, w, h, r, d, c) in page_pop_pc.control_reliefs {
-                popover_pc.control_reliefs.push((x, y - self.scroll_y, w, h, r, d, c));
+            for carve in page_pop_pc.control_reliefs {
+                popover_pc.control_reliefs.push(carve.shifted_y(-self.scroll_y));
             }
         }
         if cce_ui::widget::context_menu::is_visible() {