git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commit064eeb8e22a6f6f8978b0ecec39532fc0cedec01
parent25aa9ff456
authorLucas Galante <[email protected]>
date2026-08-26 12:36
refactor: the params pane paints through its widget (cce-ui paint_ui)

PARAM_IDX joins the playbar and breadcrumb on the modern path: instead
of hand-pulling ParametersBg's five chrome hatches (rounded wells, arcs,
reliefs, throat fillets, thumb spheres) plus scene rows and the flat
extra_quads, the branch calls paint_self — which routes to the new
ParametersBg::paint_ui (cce-ui@854f78c) emitting the identical chrome in
the identical order, and appends the fonted labels via the own-labels
bridge, so append_frame_text now skips this slot.

Designer-owned pieces stay: the plate (span-widened radii + focus tint),
the viewport clip around paint_self (a clip inside the widget would not
survive the replay), and the scrollbar straddle (idle sinks behind the
translucent plate, active rides above the content).

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

 src/render.rs | 93 +++++++++++++++++++----------------------------------------
 src/window.rs |  2 +-
 2 files changed, 30 insertions(+), 65 deletions(-)

diff --git a/src/render.rs b/src/render.rs
index cc9823c..fa56578 100644
--- a/src/render.rs
+++ b/src/render.rs
@@ -401,12 +401,17 @@ impl State {
                     });
                 }
             }
-        } else {
-            // The params scrollbar sinks behind the pane plate when idle and rises above the
-            // pane content when active (dragged / recently scrolled / hovered). It straddles
-            // the plate here rather than riding the pane's `extra_quads`, so its depth can
-            // change without touching the rest of the pane chrome.
-            let param_scrollbar = if idx == PARAM_IDX {
+        } else if idx == PARAM_IDX {
+            // Modern-paint pane: ParametersBg::paint_ui (via paint_self)
+            // authors the complete row chrome — wells, arcs, reliefs, throat
+            // fillets, thumb spheres, scene rows, flat quads — plus the
+            // fonted labels from the own-labels bridge, so append_frame_text
+            // skips this slot. The pane keeps three designer-owned pieces:
+            // the plate (span-widened radii + focus tint), the viewport clip
+            // (a clip inside the widget would not survive paint_self's
+            // replay), and the scrollbar straddle — idle it sinks behind the
+            // translucent plate, active it rides above the content.
+            let param_scrollbar = {
                 let pb = self
                     .slots
                     .param
@@ -418,11 +423,7 @@ impl State {
                 } else {
                     None
                 }
-            } else {
-                None
             };
-
-            // Idle: draw the scrollbar first so the translucent pane plate settles over it.
             // Track and thumb are pills — half-width radius on the DE corner
             // family (squircle when corner_shape > 2), like the nodes.
             if let Some((quads, false)) = &param_scrollbar {
@@ -434,51 +435,20 @@ impl State {
             let (wx, wy, ww2, wh2) = w.rect();
             append_widget_plate_radii(w, pc, self.plate_focus_tint(idx), self.pane_plate_radii(wx, wy, ww2, wh2));
 
-            // The params pane serves its chrome through the legacy plain-quad view,
-            // which carries flat quads only — the controls' rounded-rect backgrounds
-            // (textbox/dropdown/button/toggle/color) come from the rounded view and the
-            // section outlines' corner fillets from the arc view, drawn under the flat
-            // chrome and clipped to the pane's scroll viewport.
-            if idx == PARAM_IDX {
-                let (px, py, pw, ph) = self.positions[PARAM_IDX];
-                let view = rect(px, py + 4.0, pw, (ph - 8.0).max(0.0));
-                let param_bg = self
-                    .slots
-                    .param
-                    .as_any()
-                    .downcast_ref::<cce_ui::widget::ParametersBg>()
-                    .expect("PARAM_IDX must be a ParametersBg");
-                pc.clip(view, |pc| {
-                    for (qx, qy, qw, qh, qr, qc, corners) in param_bg.rounded_quads(&self.ui_context) {
-                        pc.rounded_rect(rect(qx, qy, qw, qh), qr, corners, qc);
-                    }
-                    for (acx, acy, ar, at, a0, a1, ac) in param_bg.arcs() {
-                        pc.arc(acx, acy, ar, at, a0, a1, ac);
-                    }
-                    // The controls' relief steps (control_relief styling), after the
-                    // flat quads so the walls shade the fills they cross.
-                    for (rx, ry, rw, rh, radii, rd, raised, edges) in param_bg.reliefs() {
-                        if raised {
-                            pc.boss_edges(rect(rx, ry, rw, rh), radii, rd, edges);
-                        } else {
-                            pc.recess_edges(rect(rx, ry, rw, rh), radii, rd, edges);
-                        }
-                    }
-                    // The section carves' concave throat fillets — the inside
-                    // corners the box reliefs can't round.
-                    for (fcx, fcy, fr, fd, fs) in param_bg.section_fillets() {
-                        pc.concave_fillet(fcx, fcy, fr, fd, fs, false);
-                    }
-                    // The slider thumbs (Prim::Sphere — no flat view carries
-                    // them), after the reliefs so the knob rides the carve.
-                    for (scx, scy, sr, sc) in param_bg.spheres() {
-                        pc.sphere(scx, scy, sr, sc);
-                    }
-                    // Scene-path rows (ramp curves) — geometry no flat view
-                    // carries; drawn last so they sit over the section wells.
-                    param_bg.paint_scene_rows(pc);
-                });
+            let (px, py, pw, ph) = self.positions[PARAM_IDX];
+            let view = rect(px, py + 4.0, pw, (ph - 8.0).max(0.0));
+            pc.clip(view, |pc| {
+                w.paint_self(&self.ui_context, pc);
+            });
+
+            if let Some((quads, true)) = &param_scrollbar {
+                for &(qx, qy, qw, qh, qc) in quads {
+                    pc.rounded_rect(rect(qx, qy, qw, qh), qw.min(qh) * 0.5, (true, true, true, true), qc);
+                }
             }
+        } else {
+            let (wx, wy, ww2, wh2) = w.rect();
+            append_widget_plate_radii(w, pc, self.plate_focus_tint(idx), self.pane_plate_radii(wx, wy, ww2, wh2));
 
             for (qx, qy, qw, qh, qc) in w.extra_quads() {
                 pc.quad(rect(qx, qy, qw, qh), qc);
@@ -486,13 +456,6 @@ impl State {
             for (cx, cy, cr, cc) in w.extra_circles() {
                 pc.circle(cx, cy, cr, cc);
             }
-
-            // Active: draw the scrollbar last so it rides above the pane content and plate.
-            if let Some((quads, true)) = &param_scrollbar {
-                for &(qx, qy, qw, qh, qc) in quads {
-                    pc.rounded_rect(rect(qx, qy, qw, qh), qw.min(qh) * 0.5, (true, true, true, true), qc);
-                }
-            }
         }
 
         // Child elements, then the widget's popover on top of them.
@@ -659,9 +622,11 @@ impl State {
                 continue;
             }
             let is_menubar = i == HEADER_IDX || i == LEFT_MENUBAR_IDX || i == RIGHT_MENUBAR_IDX || i == PARAM_MENUBAR_IDX || i == SPREADSHEET_MENUBAR_IDX;
-            // The playbar's text is already in the geometry pass (subtree
-            // painter via paint_self — see paint_widget's PLAYBAR_IDX branch).
-            if is_menubar || i == PLAYBAR_IDX {
+            // The playbar's and params pane's text is already in the geometry
+            // pass (paint_self — see paint_widget's PLAYBAR_IDX / PARAM_IDX
+            // branches: subtree text for the playbar, the own-labels bridge
+            // with per-row fonts and code-box bounds for the params).
+            if is_menubar || i == PLAYBAR_IDX || i == PARAM_IDX {
                 continue;
             }
             let is_node = i == CONTENT_IDX;
diff --git a/src/window.rs b/src/window.rs
index 72424f0..ac28626 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -10,7 +10,7 @@ use std::path::Path;
 
 use cce_ui::widget::WidgetHost;
 use crate::shortcut::Action;
-use crate::app::{State, CustomEvent, McpAction, TouchPhase, get_next_visible_pane, Project, ProjectViewState, ParamDef};
+use crate::app::{State, CustomEvent, McpAction, TouchPhase, get_next_visible_pane, Project, ParamDef};
 use crate::slots::{LEFT_MENUBAR_IDX, RIGHT_MENUBAR_IDX, PARAM_MENUBAR_IDX, SPREADSHEET_MENUBAR_IDX, HEADER_IDX, PARAM_IDX, WIDGET_COUNT};
 
 #[derive(Debug, Clone, Copy)]