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

commit1a35a9956b18c7fe2d19ca6bb4e1f33068e28370
parentd3d576a90a
authorLucas Galante <[email protected]>
date2026-07-21 12:22
fix: serve the params sliders' fill, readout, and thumb to legacy hosts

A host on the legacy flat views (the designer's params pane) lost three
of the slider's four parts: rounded_quads skipped slider rows (fill +
readout box), and the thumb knob is a Prim::Sphere, which no flat view
carries — only the recess carve survived, an empty well.

rounded_quads now includes slider rows, and a spheres() view serves the
thumbs off the new Slider::thumb_sphere (shared by paint so the two
paths can't drift), drawn after reliefs per the widget's own
fill -> carve -> thumb order.

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

 src/widget/container/parameters_bg.rs | 33 +++++++++++++++++++++++++++++++++
 src/widget/input/slider.rs            | 35 +++++++++++++++++++++++------------
 2 files changed, 56 insertions(+), 12 deletions(-)

diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index 33f952d..8d893d7 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -870,6 +870,13 @@ impl ParametersBg {
                 if let Some(tb) = &self.texts[i] {
                     out.extend(tb.all_rounded_quads(ctx));
                 }
+            } else if p.2.starts_with("slider") {
+                // Track (square style only — the recessed style has no track
+                // background), value fill, and readout box; the thumb knob is a
+                // `Prim::Sphere` and rides `spheres()` instead.
+                if let Some(s) = &self.sliders[i] {
+                    out.extend(s.all_rounded_quads(ctx));
+                }
             } else if p.2.starts_with("choice") {
                 if let Some(d) = &self.choices[i] {
                     out.extend(d.all_rounded_quads(ctx));
@@ -946,6 +953,32 @@ impl ParametersBg {
         out
     }
 
+    /// The sphere companion to [`Self::rounded_quads`]: the slider rows' thumb
+    /// knobs, which are `Prim::Sphere` — a prim NO legacy flat view carries, so
+    /// a host rendering this panel through the legacy views must read this
+    /// getter or the knobs vanish. Returned unclipped; the host clips to the
+    /// pane's scroll viewport and draws these AFTER [`Self::reliefs`], matching
+    /// the widget's own fill → carve → thumb order.
+    pub fn spheres(&self) -> Vec<(f32, f32, f32, [f32; 4])> {
+        if !self.visible {
+            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] || !p.2.starts_with("slider") {
+                continue;
+            }
+            if let Some(s) = &self.sliders[i] {
+                let (x, y, w, h) = s.rect();
+                if let Some(sphere) = s.inner().thumb_sphere(Rect { x, y, width: w, height: h }) {
+                    out.push(sphere);
+                }
+            }
+        }
+        out
+    }
+
 }
 
 impl Layout for ParametersBg {
diff --git a/src/widget/input/slider.rs b/src/widget/input/slider.rs
index 5b0c96c..4be0574 100644
--- a/src/widget/input/slider.rs
+++ b/src/widget/input/slider.rs
@@ -126,6 +126,24 @@ impl Slider {
         Some((g.track_x, g.y, g.track_w, g.h, radius, depth))
     }
 
+    /// The thumb knob's circle (cx, cy, radius, color) for hosts that draw this
+    /// control through the legacy flat views (see `ParametersBg::spheres`): the
+    /// `Prim::Sphere` the rounded-corner paint emits — no flat view can carry
+    /// it. None under the square style, whose quad thumb already reaches the
+    /// plain-quad view. The same geometry `paint` draws.
+    pub fn thumb_sphere(&self, rect: Rect) -> Option<(f32, f32, f32, [f32; 4])> {
+        if crate::layout::slider_corner_radius() <= 0.0 {
+            return None;
+        }
+        let g = self.geom(rect);
+        let recess_t = crate::layout::bevel_width().min(g.h * 0.2);
+        let thumb_x = g.track_x + self.value * (g.track_w - g.thumb_size);
+        let thumb_y = g.y + (g.h - g.thumb_size) / 2.0;
+        let diameter = if self.recessed { g.h - recess_t } else { g.thumb_size };
+        let color = if self.dragging { colors::slider_thumb_drag() } else { colors::slider_thumb() };
+        Some((thumb_x + g.thumb_size / 2.0, thumb_y + g.thumb_size / 2.0, diameter / 2.0, color))
+    }
+
     fn geom(&self, rect: Rect) -> SliderGeom {
         let side = side_offset(&self.label);
         let x = rect.x + side;
@@ -313,18 +331,11 @@ impl Paint for Slider {
         // reads wrong — the thumb should stay round under any corner style.
         let thumb_y = g.y + (g.h - g.thumb_size) / 2.0;
         let thumb_color = if self.dragging { colors::slider_thumb_drag() } else { colors::slider_thumb() };
-        if rounded {
-            // Recessed style: the knob sits IN the well, so its diameter is the
-            // flat floor between the walls (each intrudes half its width) —
-            // drawn size only; the drag/hit geometry keeps the full thumb_size.
-            let diameter =
-                if self.recessed { g.h - recess_t } else { g.thumb_size };
-            ctx.sphere(
-                thumb_x + g.thumb_size / 2.0,
-                thumb_y + g.thumb_size / 2.0,
-                diameter / 2.0,
-                thumb_color,
-            );
+        // Recessed style: the knob sits IN the well, so its diameter is the
+        // flat floor between the walls (each intrudes half its width) —
+        // drawn size only; the drag/hit geometry keeps the full thumb_size.
+        if let Some((cx, cy, r, c)) = self.thumb_sphere(rect) {
+            ctx.sphere(cx, cy, r, c);
         } else {
             rrect(
                 Rect { x: thumb_x, y: thumb_y, width: g.thumb_size, height: g.thumb_size },