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

commit574a573102b96de934d8e3494d15d5ab4a7201ce
parent5cb024ec12
authorLucas Galante <[email protected]>
date2026-09-20 20:57
Prim::Fill (a material's flat fill) and parameter-row floors with their own compression

A frosted RoundedRect promotes to a zero-depth plate at the DE's default
recipe; nothing could fill a rect with a MATERIAL — its own compression,
refraction, radius. Prim::Fill is that: the same promotion, the
material's recipe, no roll, no carve host; an opaque material is a plain
rounded fill.

ParametersBg uses it for row floors: style.surface.param.
backdrop_compression (layout::param_compression, unset = nothing drawn)
floors every visible parameter row with the pane material frosted at
that compression, at the control corner radius, before the row's
controls paint — each parameter on its own tablet, the row-scale twin of
the designer's node compression.

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

 src/backend/window_runner.rs          | 24 +++++++++++++++++++++++-
 src/layout.rs                         | 14 ++++++++++++++
 src/scene/paint.rs                    | 15 +++++++++++++++
 src/widget/container/parameters_bg.rs | 27 +++++++++++++++++++++++++++
 4 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 9ba27d2..87c106b 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -1712,7 +1712,7 @@ fn prim_kind(p: &crate::scene::paint::Prim) -> &'static str {
         P::Sphere { .. } => "Sphere", P::Droplet { .. } => "Droplet",
         P::DropletScrim { .. } => "DropletScrim",
         P::ConcaveFillet { .. } => "ConcaveFillet",
-        P::Groove { .. } => "Groove", P::Lattice { .. } => "Lattice", P::Grout { .. } => "Grout",
+        P::Groove { .. } => "Groove", P::Lattice { .. } => "Lattice", P::Grout { .. } => "Grout", P::Fill { .. } => "Fill",
         P::CarveUnion { .. } => "CarveUnion", P::Glow { .. } => "Glow",
         P::Text { .. } => "Text", P::Image { .. } => "Image",
     }
@@ -1872,6 +1872,9 @@ pub fn tessellate_display_list(
         ) || matches!(
             &item.prim,
             crate::scene::paint::Prim::Border { fill, .. } if fill[3] < 0.0
+        ) || matches!(
+            &item.prim,
+            crate::scene::paint::Prim::Fill { material, .. } if material.fill(PlateRole::Nested)[3] < 0.0
         );
         // Logical [cx, cy, r] → the physical-pixel triple the vertex attribute carries.
         let no = item
@@ -1917,6 +1920,25 @@ pub fn tessellate_display_list(
                 plate = Some(flat_frost_push(rect, radii, *color, scale, plate_light, plate_mat));
                 promoted = true;
             }
+            Prim::Fill { rect, radii, material } if shader_plates && material.frost.is_frosted() => {
+                // A material's flat fill: the frosted promotion above with
+                // the MATERIAL's recipe (compression, refraction, radius)
+                // instead of the DE default's. Zero depth, circular
+                // corners, no host — exactly a promoted RoundedRect.
+                let color = material.fill(PlateRole::Nested);
+                verts.extend(quad_vertices(rect.x, rect.y, rect.width, rect.height, sw, sh, color));
+                let mut p = plate_push_raised(rect, *radii, 0.0, scale, plate_light, plate_mat, false, Some(2.0));
+                let [fz, fw] = material.frost.pack(scale);
+                p.host[2] = fz;
+                p.host[3] = fw;
+                plate = Some(p);
+                promoted = true;
+            }
+            Prim::Fill { rect, radii, material } => {
+                // Opaque (or the legacy path): a plain rounded fill.
+                let cr = crate::widget::CornerRadii::new(radii.0, radii.1, radii.2, radii.3);
+                push_rounded_rect_vertices_corners(rect.x, rect.y, rect.width, rect.height, cr, sw, sh, material.fill(PlateRole::Nested), no, None, &mut verts);
+            }
             Prim::Border { rect, radii, fill, border, thickness } if shader_plates && fill[3] < 0.0 => {
                 // The fill as its own plate batch, closed here; the stroke
                 // follows as ordinary geometry in the batch the tail makes.
diff --git a/src/layout.rs b/src/layout.rs
index 637bc98..cbd74f6 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -260,6 +260,7 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "style.surface.relief.profile_knobs" => "bevel_profile_knobs",
                 "style.surface.relief.edge_knobs" => "roll_profile_knobs",
                 "style.container.section.depth" => "section_depth",
+                "style.surface.param.backdrop_compression" => "param_compression",
                 "window_manager.bevel_shader" => "bevel_shader",
                 "window_manager.control_relief" => "control_relief",
                 "window_manager.corner_shape" => "corner_shape",
@@ -1684,6 +1685,19 @@ pub fn section_depth() -> f32 {
     get_style_registry().read().unwrap().get_float("section_depth").unwrap_or(1.0)
 }
 
+/// The parameter rows' backdrop compression
+/// (`style.surface.param.backdrop_compression`, 0..1): when set, every
+/// parameter row in a params pane is floored with the pane material frosted
+/// at THIS compression before its controls paint, so each parameter sits on
+/// a tablet that pulls the view toward the tint while the pane around it
+/// stays at its own — the row-scale twin of the designer's node
+/// compression. `None` (unset) draws no floor — the rows are bare, as they
+/// always were.
+pub fn param_compression() -> Option<f32> {
+    lazy_init_style_registry();
+    get_style_registry().read().unwrap().get_float("param_compression").map(|v| v.clamp(0.0, 1.0))
+}
+
 pub fn section_padding() -> f32 {
     #[cfg(test)]
     if let Some(v) = test_style::SECTION_PADDING.with(|c| *c.borrow()) {
diff --git a/src/scene/paint.rs b/src/scene/paint.rs
index de7f7ab..732bfe1 100644
--- a/src/scene/paint.rs
+++ b/src/scene/paint.rs
@@ -647,6 +647,14 @@ pub enum Prim {
     /// lines are when the cells are the surface beneath showing through.
     /// SDF path only — the legacy banded tessellation draws nothing.
     Grout { rect: Rect, period: (f32, f32), origin: (f32, f32), cell: (f32, f32), radius: f32, color: [f32; 4] },
+    /// A flat fill of a MATERIAL: `rect` at `radii`, no roll, no rim — the
+    /// material's tint, frosted at the material's own recipe when it is
+    /// frosted. What a frosted `RoundedRect` promotes to, except that the
+    /// recipe is the material's rather than the DE default's, so a fill can
+    /// compress harder (or softer) than the pane it sits on. Opens no carve
+    /// host: carves emitted after it overlay it, as they overlay any flat
+    /// geometry. An opaque material draws as a plain rounded fill.
+    Fill { rect: Rect, radii: Radii, material: Material },
     /// Several rounded boxes carved (`raised` false) or raised (`raised`
     /// true) as ONE shape: the union of the boxes is the well, and its wall
     /// follows the union's outline — straddling it by ±`depth`/2 like every
@@ -1061,6 +1069,7 @@ impl PaintCtx {
             Prim::Grout { rect, period, origin, cell, radius, color } => {
                 self.grout(rect, period, origin, cell, radius, color)
             }
+            Prim::Fill { rect, radii, material } => self.fill_material(rect, radii, &material),
             Prim::CarveUnion { boxes, depth, raised } => self.carve_union(boxes, depth, raised),
             Prim::Image { image, rect, alpha } => self.image(image, rect, alpha),
         }
@@ -1097,6 +1106,12 @@ impl PaintCtx {
         self.push(Prim::Lattice { rect, period, origin: (origin.0 + ox, origin.1 + oy), cell, radius, depth });
     }
 
+    /// A flat fill of `material` — see [`Prim::Fill`].
+    pub fn fill_material(&mut self, rect: Rect, radii: Radii, material: &Material) {
+        let rect = self.apply_offset(rect);
+        self.push(Prim::Fill { rect, radii, material: *material });
+    }
+
     /// Grout between a periodic field of rounded cells — see [`Prim::Grout`].
     /// `origin` is any one cell's centre; `rect` bounds the paint.
     pub fn grout(&mut self, rect: Rect, period: (f32, f32), origin: (f32, f32), cell: (f32, f32), radius: f32, color: [f32; 4]) {
diff --git a/src/widget/container/parameters_bg.rs b/src/widget/container/parameters_bg.rs
index ac9909a..6e7fa3c 100644
--- a/src/widget/container/parameters_bg.rs
+++ b/src/widget/container/parameters_bg.rs
@@ -324,6 +324,32 @@ impl ParametersBg {
     /// Each section's boxes: the title box, plus the box wrapping its rows (`None` when the
     /// section is collapsed or has no rows). The shared source for the outline's straight
     /// runs and its corner fillets, so the two halves can't disagree.
+    /// The row floors (`layout::param_compression`): the pane material
+    /// frosted at the configured compression, filled under every visible
+    /// parameter row (headers excepted) before anything else in the pane
+    /// paints, at the control corner radius — each parameter on its own
+    /// tablet, the way the designer's node bodies get their own compression.
+    /// Nothing when the key is unset. Relief only: the flat style has no
+    /// floor language.
+    fn paint_row_floors(&self, ctx: &mut PaintCtx) {
+        let Some(k) = crate::layout::param_compression() else { return };
+        if !crate::layout::control_relief() {
+            return;
+        }
+        let mut mat = crate::scene::Material::pane();
+        if let crate::scene::Frost::Frosted { compression, .. } = &mut mat.frost {
+            *compression = k;
+        }
+        let r = crate::layout::control_corner_radius();
+        let hidden = self.hidden_rows();
+        for (i, (x, y, w, h)) in self.get_param_rects().into_iter().enumerate() {
+            if hidden[i] || h <= 0.0 || self.display_params[i].2 == "section" {
+                continue;
+            }
+            ctx.fill_material(Rect { x, y, width: w, height: h }, (r, r, r, r), &mat);
+        }
+    }
+
     fn section_boxes(&self) -> Vec<((f32, f32, f32, f32), Option<(f32, f32, f32, f32)>)> {
         let rects = self.get_param_rects();
         let full_w = self.rect.width - 2.0 * SECTION_MARGIN;
@@ -1483,6 +1509,7 @@ impl Paint for ParametersBg {
         if !self.visible {
             return;
         }
+        self.paint_row_floors(ctx);
         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);
         }