GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
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);
}