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

commit854f78c6cc73141b75405401ff88e35748e0fbd1
parent43088040cd
authorLucas Galante <[email protected]>
date2026-08-26 12:34
paint: Paint::paint_ui — the ui-aware hook paint_self runs; ParametersBg completes it

paint_self now routes through Paint::paint_ui(ui, rect, ctx), whose
default forwards to paint() — a behavioral no-op for every ordinary
widget. Composites whose own geometry aggregates context-dependent child
chrome (ParametersBg: rounded_quads takes the UiContext for the child
controls' all_rounded_quads) had no way to reach the context from the
paint path, leaving their modern emission incomplete forever.

ParametersBg::paint_ui emits the COMPLETE row chrome in the hosts'
canonical order: rounded wells, outline arcs, relief steps, section
throat fillets, slider-thumb spheres, scene rows, then the flat quads.
Plate, scrollbar, and text stay out by design — color() carries the
plate, hosts place the scrollbar's depth, and paint_self's own-labels
bridge carries per-row fonts and code-box bounds. Hosts clip to their
pane viewport (a rect clip pushed inside would not survive paint_self's
replay, which restores only clip_circle).

The uiless paint() keeps its old body for direct callers.

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

 src/widget/container/parameters_bg.rs | 44 ++++++++++++++++++++++++++++++++---
 src/widget/model.rs                   | 11 ++++++++-
 2 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index a1126af..200d3ff 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -1362,9 +1362,47 @@ impl Paint for ParametersBg {
         Some(crate::layout::control_label_font())
     }
 
-    /// Scene-path emission (the designer renders through the legacy hatches instead): the row
-    /// chrome plus the viewport-filtered labels. The background plate stays out — see
-    /// [`color`](Paint::color).
+    /// The COMPLETE row chrome — everything the legacy hatches carry, in the hosts' canonical
+    /// draw order: the controls' rounded wells, the flat-style section outline fillets, the
+    /// relief steps (after the fills so the walls shade what they cross), the section carves'
+    /// concave throat fillets, the slider-thumb spheres, the scene-path rows, then the flat
+    /// plain-quad chrome. What stays OUT, deliberately: the background plate (see
+    /// [`color`](Paint::color)), the scrollbar (hosts place its depth — the designer straddles
+    /// it around the pane plate), and text (`paint_self`'s own-labels bridge carries the
+    /// per-row fonts and code-box bounds). Hosts clip this to their pane viewport — a rect
+    /// clip pushed here would not survive `paint_self`'s replay.
+    fn paint_ui(&self, ui: &UiContext, _rect: Rect, ctx: &mut PaintCtx) {
+        if !self.visible {
+            return;
+        }
+        for (qx, qy, qw, qh, qr, qc, corners) in self.rounded_quads(ui) {
+            ctx.rounded_rect(Rect { x: qx, y: qy, width: qw, height: qh }, qr, corners, qc);
+        }
+        for (acx, acy, ar, at, a0, a1, ac) in self.arcs() {
+            ctx.arc(acx, acy, ar, at, a0, a1, ac);
+        }
+        for (rx, ry, rw, rh, radii, rd, raised, edges) in self.reliefs() {
+            if raised {
+                ctx.boss_edges(Rect { x: rx, y: ry, width: rw, height: rh }, radii, rd, edges);
+            } else {
+                ctx.recess_edges(Rect { x: rx, y: ry, width: rw, height: rh }, radii, rd, edges);
+            }
+        }
+        for (fcx, fcy, fr, fd, fs) in self.section_fillets() {
+            ctx.concave_fillet(fcx, fcy, fr, fd, fs, false);
+        }
+        for (scx, scy, sr, sc) in self.spheres() {
+            ctx.sphere(scx, scy, sr, sc);
+        }
+        self.paint_scene_rows(ctx);
+        for (qx, qy, qw, qh, qc) in self.plain_quads() {
+            ctx.quad(Rect { x: qx, y: qy, width: qw, height: qh }, qc);
+        }
+    }
+
+    /// Ui-less emission (the [`paint_ui`](Paint::paint_ui) override above is what `paint_self`
+    /// runs): the flat subset plus the scrollbar, kept for direct callers only. The background
+    /// plate stays out — see [`color`](Paint::color).
     fn paint(&self, _rect: Rect, ctx: &mut PaintCtx) {
         for (qx, qy, qw, qh, qc) in self.plain_quads() {
             ctx.quad(Rect { x: qx, y: qy, width: qw, height: qh }, qc);
diff --git a/src/widget/model.rs b/src/widget/model.rs
index 6959633..808cf5a 100644
--- a/src/widget/model.rs
+++ b/src/widget/model.rs
@@ -165,6 +165,15 @@ pub trait Paint {
         }
     }
 
+    /// [`paint`](Paint::paint) with the live [`UiContext`] — what `paint_self` actually calls.
+    /// The default forwards to `paint`, so ordinary widgets implement only that. Override this
+    /// for composites whose own geometry aggregates hover/coverage-dependent child chrome that
+    /// needs the context (ParametersBg): child-holding widgets that never entered the arena tree
+    /// have no other way to reach it from the paint path.
+    fn paint_ui(&self, _ui: &UiContext, rect: Rect, ctx: &mut PaintCtx) {
+        self.paint(rect, ctx);
+    }
+
     /// Whether the paint walk clips this widget's children to its `rect` (scroll/backplate
     /// containers). Default: no.
     fn clips_children(&self) -> bool {
@@ -1244,7 +1253,7 @@ impl<W: Layout + Paint + Input + 'static> WidgetHost for Adapted<W> {
 
     fn paint_self(&self, ui: &UiContext, ctx: &mut PaintCtx) {
         let mut tmp = PaintCtx::new();
-        Paint::paint(&self.inner, self.content_rect(), &mut tmp);
+        Paint::paint_ui(&self.inner, ui, self.content_rect(), &mut tmp);
         // Subtree painters (paints_own_subtree) author their COMPLETE text in paint() —
         // per-child fonts and clip bounds included — so their Text prims pass through
         // verbatim and the single-font own-labels re-derivation below is skipped