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

commitc69e7e5ef1ba1841399c71b1366b5ca3e668a8c6
parentbcb014e76f
authorLucas Galante <[email protected]>
date2026-07-20 15:50
feat: ParametersBg reliefs view — control_relief for legacy-view hosts

The control_relief styling (boss rims, recess wells) lives only in the
widgets' modern paint(), so a host rendering the panel through the
legacy flat views (plain/rounded quads) lost it entirely — with the
DE's transparent control backgrounds the controls all but vanished.
reliefs() returns the row controls' relief steps as edges-only variants
(the flat views own the faces, the widgets' own transparent-fill
bevel→boss degradation), plus Slider::track_relief for the recessed
track's sub-rect carve.

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

 src/widget/container/parameters_bg.rs | 55 +++++++++++++++++++++++++++++++++++
 src/widget/input/slider.rs            | 14 +++++++++
 2 files changed, 69 insertions(+)

diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index c0bd54b..33f952d 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -891,6 +891,61 @@ impl ParametersBg {
         out
     }
 
+    /// The relief companion to [`Self::rounded_quads`]: the row controls'
+    /// raised/recessed step prims (boss rims, recess wells), which the flat
+    /// views cannot carry — a host rendering this panel through the legacy
+    /// views must read this getter too or the `control_relief` styling is lost
+    /// entirely (with the DE's transparent control backgrounds the controls all
+    /// but vanish). Edges-only variants throughout: the flat views own the
+    /// faces, exactly the widgets' own transparent-fill bevel→boss degradation.
+    /// Returned unclipped; the host clips to the pane's scroll viewport and
+    /// draws these AFTER the flat quads, so the walls' shading modulates the
+    /// fills they cross (the order the widgets' own paints use). Tuple:
+    /// (x, y, w, h, radius, depth, raised) — raised maps to `PaintCtx::boss`,
+    /// flat to `PaintCtx::recess`.
+    pub fn reliefs(&self) -> Vec<(f32, f32, f32, f32, f32, f32, bool)> {
+        if !self.visible || !crate::layout::control_relief() {
+            return Vec::new();
+        }
+        let mut out = Vec::new();
+        let hidden = self.hidden_rows();
+        for (i, p) in self.display_params.iter().enumerate() {
+            if hidden[i] {
+                continue;
+            }
+            // (control, its configured corner radius, raised vs recessed)
+            let ctl: Option<(&dyn WidgetHost, f32, bool)> = if p.2 == "text" {
+                self.texts[i].as_ref().map(|w| (w as &dyn WidgetHost, crate::layout::textbox_corner_radius(), false))
+            } else if p.2.starts_with("choice") {
+                self.choices[i].as_ref().map(|w| (w as &dyn WidgetHost, crate::layout::dropdown_corner_radius(), true))
+            } 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" {
+                self.toggles[i].as_ref().map(|w| (w as &dyn WidgetHost, crate::layout::toggle_corner_radius(), true))
+            } else {
+                if let Some(s) = &self.sliders[i] {
+                    let (x, y, w, h) = s.rect();
+                    if let Some((rx, ry, rw, rh, rr, rd)) = s.inner().track_relief(Rect { x, y, width: w, height: h }) {
+                        out.push((rx, ry, rw, rh, rr, rd, false));
+                    }
+                }
+                None
+            };
+            if let Some((w, radius, raised)) = ctl {
+                let (x, y, ww, h) = w.rect();
+                if ww <= 0.0 || h <= 0.0 {
+                    continue;
+                }
+                // The side-label inset the widget's own paint applies (0 for the
+                // exempt kinds — button, toggle).
+                let lx = w.label_x_offset();
+                let depth = crate::layout::bevel_width().min(h * 0.2);
+                out.push((x + lx, y, ww - lx, h, radius, depth, raised));
+            }
+        }
+        out
+    }
+
 }
 
 impl Layout for ParametersBg {
diff --git a/src/widget/input/slider.rs b/src/widget/input/slider.rs
index 24e721a..5b0c96c 100644
--- a/src/widget/input/slider.rs
+++ b/src/widget/input/slider.rs
@@ -112,6 +112,20 @@ impl Slider {
         }
     }
 
+    /// The recessed track's carve for hosts that draw this control through the
+    /// legacy flat views (see `ParametersBg::reliefs`): (x, y, w, h, radius,
+    /// depth) over the widget's assigned `rect`, or None when the style is off.
+    /// The same geometry `paint` carves.
+    pub fn track_relief(&self, rect: Rect) -> Option<(f32, f32, f32, f32, f32, f32)> {
+        if !self.recessed {
+            return None;
+        }
+        let g = self.geom(rect);
+        let radius = crate::layout::slider_corner_radius();
+        let depth = crate::layout::bevel_width().min(g.h * 0.2);
+        Some((g.track_x, g.y, g.track_w, g.h, radius, depth))
+    }
+
     fn geom(&self, rect: Rect) -> SliderGeom {
         let side = side_offset(&self.label);
         let x = rect.x + side;