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

commit693ae084369e072e9d176a7957aca7be86a17e8f
parenteeca5b7159
authorLucas Galante <[email protected]>
date2026-09-24 12:45
The standard root plate: PlateSpec::window, PaintCtx::root_plate, root_plate_inset

Every app that painted the DE's rolled root plate carried the same
eight-line block — page-low colour, an opacity override that is a no-op
(root_plate_opacity IS page_low_color's alpha), four window corners, the
DE roll — and the copies had begun to drift (their own tints, their own
depths, a quad fallback, prose restating the convention). The spec now
has a constructor: PlateSpec::window(w, h) is that block, built on
Material::root() (the rung's material, bound or legacy), with
with_material / with_depth for the deliberate deviations; root_at(rect)
is the same for an overlay drawing the silhouette inside a larger
surface. PaintCtx::root_plate(w, h) emits it in one call.

layout::root_plate_inset() is the companion for layout: bevel_width plus
root_plate_padding, where content starts on a rolled root plate measured
from the window edge — the padding is a run of flat face, and the face
begins where the roll ends. Between siblings the gap, inside a pane the
plate padding; that is the whole spacing rule for an app on the standard
plate.

The demo app is the reference: it paints through root_plate and insets
by root_plate_inset. tests/plate_golden.rs reproduces the pre-change dump
byte for byte.

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

 src/layout.rs      | 16 ++++++++++++++++
 src/main.rs        | 31 ++++++++++---------------------
 src/scene/paint.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 78 insertions(+), 21 deletions(-)

diff --git a/src/layout.rs b/src/layout.rs
index 4d325d7..e715d80 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -2232,6 +2232,22 @@ pub fn root_plate_gap() -> f32 {
     get_style_registry().read().unwrap().get_float("root_plate_gap").unwrap_or(12.0)
 }
 
+/// Where content starts on the standard root plate, measured from the
+/// WINDOW edge: the plate's rolled rim ([`bevel_width`]) plus one
+/// [`root_plate_padding`]. The padding is a run of flat plate face, the
+/// same run [`root_plate_gap`] leaves between two siblings; but the face
+/// only begins where the roll ends, so a bare padding at a window edge
+/// leaves most of it on the roll — measured at 4px of visible flat against
+/// 12 between panes (cce-mail, 2026-09-19). This is the one number an app
+/// on the standard plate insets by at its four edges; between siblings it
+/// uses the gap, and everything inside a pane plate uses
+/// [`plate_padding`]. An app whose base is NOT the rolled root plate (a
+/// transparent surface, a bare fill) has no roll to clear and insets by
+/// [`root_plate_padding`] alone.
+pub fn root_plate_inset() -> f32 {
+    bevel_width() + root_plate_padding()
+}
+
 /// Roll-off width for the wall where a bar (menubar / status bar / the demo's
 /// header band) steps down into the window plate. Wider than the plate's own
 /// perimeter roll on purpose: the carve depth saturates at `bevel_width` in the
diff --git a/src/main.rs b/src/main.rs
index c5138c0..3ee44fa 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -260,8 +260,10 @@ impl Application for DemoApp {
             // ── Layout: a plain LayoutBox tree, solved in one call. Leaves carry their
             // intrinsic sizes; `grow` distributes leftover space; the solved rects are
             // assigned straight onto the widgets.
-            // DE-wide plate spacing: rim padding and object gap from config.
-            let plate_pad = cce_ui::layout::root_plate_padding();
+            // DE-wide plate spacing: the inset from the window edge (the
+            // root plate's roll plus one padding) and the gap between
+            // siblings on the plate, both from config.
+            let plate_pad = cce_ui::layout::root_plate_inset();
             let plate_gap = cce_ui::layout::root_plate_gap();
             let mut arena: Arena<LayoutBox> = Arena::new();
             let root = arena.insert(LayoutBox::container(
@@ -356,25 +358,12 @@ impl Application for DemoApp {
         let w = self.width as f32;
         let h = self.height as f32;
 
-        // The window plate — the dissolved root plate container as a lit object: page-low color
-        // at the configured opacity, config corner radius, perimeter rolled over
-        // `bevel_width` so the surface reads as a physical plate rather than a flat fill.
-        let mut plate = cce_ui::color::page_low_color();
-        if plate[3] > 0.001 {
-            plate[3] = cce_ui::color::root_plate_opacity();
-        }
-        // The root plate as a PlateSpec (RFC Phase 7b): all four corners are
-        // window corners, so the radii come from the SHARED silhouette curve
-        // — under squircle corner_shape this widens the perimeter roll to
-        // match the compositor's clip, which the old hand-rolled
-        // root_plate_corner_radius did not.
-        let frame = Rect { x: 0.0, y: 0.0, width: w, height: h };
-        pc.plate_spec(&cce_ui::scene::paint::PlateSpec {
-            rect: frame,
-            material: cce_ui::scene::Material::opaque(plate),
-            window_corners: (true, true, true, true),
-            depth: cce_ui::layout::bevel_width(),
-        });
+        // The standard root plate (`PlateSpec::window`): the DE's root
+        // material at its configured opacity, the shared silhouette arc on
+        // all four corners, the perimeter rolled over `bevel_width`. This
+        // demo is the reference app, so its base is the one every cce app
+        // should paint first.
+        pc.root_plate(w, h);
 
         // Header band: the title strip carved one step down into the plate. Flush to the
         // window's top and sides, so its only real wall is the bottom one facing the
diff --git a/src/scene/paint.rs b/src/scene/paint.rs
index 732bfe1..e412c36 100644
--- a/src/scene/paint.rs
+++ b/src/scene/paint.rs
@@ -59,6 +59,49 @@ pub struct PlateSpec {
 }
 
 impl PlateSpec {
+    /// THE standard root plate of a `width` x `height` window — the base
+    /// surface every cce app stands its panes and controls on: the whole
+    /// window, the root rung's material ([`Material::root`], which is the
+    /// DE's `style.surface.plate.root.color` at its configured opacity
+    /// unless a `material=` is bound), all four corners on the silhouette,
+    /// and the perimeter rolled over [`crate::layout::bevel_width`].
+    ///
+    /// This is the spec every app used to hand-copy as an eight-line block
+    /// (page-low colour, opacity override, four window corners, the DE roll)
+    /// — the copies are gone, and a window whose base is anything else is
+    /// off the standard on purpose, which its code should say. Emit it with
+    /// [`PaintCtx::root_plate`]; deviate with [`Self::with_material`] /
+    /// [`Self::with_depth`] (cce-system-interface's own tint, an overlay's
+    /// shallower roll).
+    pub fn window(width: f32, height: f32) -> Self {
+        Self::root_at(Rect { x: 0.0, y: 0.0, width, height })
+    }
+
+    /// [`Self::window`] for a root plate that is not the whole surface — a
+    /// layer-shell overlay drawing the window silhouette itself inside a
+    /// larger transparent surface (cce-cloud). Same material, corners and
+    /// roll; `rect` is where the "window" is.
+    pub fn root_at(rect: Rect) -> Self {
+        Self {
+            rect,
+            material: Material::root(),
+            window_corners: (true, true, true, true),
+            depth: crate::layout::bevel_width(),
+        }
+    }
+
+    /// This plate made of `material` instead of its rung's default.
+    pub fn with_material(mut self, material: Material) -> Self {
+        self.material = material;
+        self
+    }
+
+    /// This plate with a `depth` roll instead of the DE's `bevel_width`.
+    pub fn with_depth(mut self, depth: f32) -> Self {
+        self.depth = depth;
+        self
+    }
+
     /// All four corners on the silhouette: this plate IS the window's base
     /// surface.
     pub fn is_root(&self) -> bool {
@@ -1539,6 +1582,15 @@ impl PaintCtx {
         self.plate(spec.rect, (tl / f, tr / f, br / f, bl / f), &spec.material.for_role(spec.role()), spec.depth);
     }
 
+    /// The standard root plate of a `width` x `height` window —
+    /// [`PlateSpec::window`] emitted. The first prim of a standard cce app's
+    /// frame: everything else is laid on this surface (pane plates atop it,
+    /// bands and wells carved into it), starting
+    /// [`crate::layout::root_plate_inset`] in from each window edge.
+    pub fn root_plate(&mut self, width: f32, height: f32) {
+        self.plate_spec(&PlateSpec::window(width, height));
+    }
+
     pub fn arc(&mut self, cx: f32, cy: f32, radius: f32, thickness: f32, start: f32, end: f32, color: [f32; 4]) {
         let (ox, oy) = self.offset;
         self.push(Prim::Arc { cx: cx + ox, cy: cy + oy, radius, thickness, start, end, color });