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

commit641a9a4e79108fb58099178f2c5f33536cf648e3
parent2473bf3768
authorLucas Galante <[email protected]>
date2026-07-21 10:03
feat: backplate_padding + backplate_gap config keys

Two DE-wide spacing keys under style.surface.backplate: padding (plate
rim to objects, default 16) and gap (between sibling objects on the
plate, default 12). Apps were hardcoding divergent values — cce-files
16/12 vs cce-data-editor 10/10 — visibly inconsistent side by side.
DemoApp consumes both as the reference.

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

 src/layout.rs | 19 +++++++++++++++++++
 src/main.rs   |  9 ++++++---
 2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/src/layout.rs b/src/layout.rs
index 9360647..65577ce 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -142,6 +142,8 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "style.surface.desktop.solid_color" => "desktop_solid_color",
                 "style.surface.desktop.grid_cell_size" => "desktop_grid_scale",
                 "style.surface.plate.padding" => "plate_padding",
+                "style.surface.backplate.padding" => "backplate_padding",
+                "style.surface.backplate.gap" => "backplate_gap",
                 "style.surface.backplate.color" => "backplate_color",
                 "style.surface.backplate.blur" => "backplate_blur",
                 "style.surface.backplate.corner_radius" => "backplate_corner_radius",
@@ -158,6 +160,7 @@ fn flatten_json_to_flat_props(val: &serde_json::Value, prefix: &str, flat_props:
                 "style.surface.graph.cell_color" => "graph_cell_color",
                 "style.surface.graph.gap_color" => "graph_gap_color",
                 "style.surface.graph.opacity" => "graph_opacity",
+                "style.surface.graph.node.opacity" => "graph_node_opacity",
                 "style.surface.graph.spacing_x" => "graph_spacing_x",
                 "style.surface.graph.spacing_y" => "graph_spacing_y",
                 "style.surface.graph.gap_col_w" => "graph_gap_col_w",
@@ -1536,6 +1539,22 @@ pub fn bevel_width() -> f32 {
     get_style_registry().read().unwrap().get_float("bevel_width").unwrap_or(9.3)
 }
 
+/// Padding between the window plate's edge and the objects sitting on it, in
+/// logical px (`style.surface.backplate.padding` in config.kdl). DE-wide so
+/// every app's content sits the same distance off the plate rim.
+pub fn backplate_padding() -> f32 {
+    lazy_init_style_registry();
+    get_style_registry().read().unwrap().get_float("backplate_padding").unwrap_or(16.0)
+}
+
+/// Gap between sibling objects on the window plate, in logical px
+/// (`style.surface.backplate.gap` in config.kdl) — pane splits, control rows.
+/// The companion to [`backplate_padding`]: rim distance vs object spacing.
+pub fn backplate_gap() -> f32 {
+    lazy_init_style_registry();
+    get_style_registry().read().unwrap().get_float("backplate_gap").unwrap_or(12.0)
+}
+
 /// 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 a6e6260..d1e87da 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -260,9 +260,12 @@ 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::backplate_padding();
+            let plate_gap = cce_ui::layout::backplate_gap();
             let mut arena: Arena<LayoutBox> = Arena::new();
             let root = arena.insert(LayoutBox::container(
-                Style::column().padding(16.0).gap(14.0).cross_align(CrossAlign::Stretch),
+                Style::column().padding(plate_pad).gap(plate_gap).cross_align(CrossAlign::Stretch),
             ));
             let title = arena.insert(LayoutBox::leaf(
                 Style::row(),
@@ -279,7 +282,7 @@ impl Application for DemoApp {
             // toggle, and dropdown plates land on the same top and bottom edge.
             const CONTROL_H: f32 = 28.0;
             let controls = arena.insert(LayoutBox::container(
-                Style::row().gap(14.0).height(Length::Fixed(CONTROL_H)),
+                Style::row().gap(plate_gap).height(Length::Fixed(CONTROL_H)),
             ));
             // `shrink` lets the fixed leaves give up width when the window is at its
             // minimum instead of overflowing the row.
@@ -290,7 +293,7 @@ impl Application for DemoApp {
             let name_box = arena.insert(LayoutBox::leaf(Style::row(), LSize::new(0.0, 30.0)));
             // ImageView row: same texture through two fit modes side by side.
             let images = arena.insert(LayoutBox::container(
-                Style::row().gap(14.0).height(Length::Fixed(72.0)),
+                Style::row().gap(plate_gap).height(Length::Fixed(72.0)),
             ));
             let image_contain = arena.insert(LayoutBox::leaf(Style::row().grow(1.0), LSize::new(0.0, 72.0)));
             let image_stretch = arena.insert(LayoutBox::leaf(Style::row().grow(1.0), LSize::new(0.0, 72.0)));