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

commitfa75eea6a83db24a31d1c9d993c0bcd7e0a249a8
parente915e02cb8
authorLucas Galante <[email protected]>
date2026-09-24 12:56
Demo on the presets; scripts/style-audit, the conformance check

The demo is the reference app, so it lays out by rung — Style::root_column
for the plate, Style::controls_row for the control row — and names no
padding or gap of its own.

scripts/style-audit greps every sibling app crate and prints one row per
app: whether its base is the standard root plate (PLATE / FILL / NONE),
how many spacing-ladder getters it reads, and how many spacing consts of
its own it declares; a crate that is not a plate by design says so with a
'style-audit: opt-out <reason>' comment and is listed as such. --strict
exits non-zero on any off-standard row, for a build to gate on.

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

 scripts/style-audit | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/main.rs         | 17 +++++++----------
 2 files changed, 62 insertions(+), 10 deletions(-)

diff --git a/scripts/style-audit b/scripts/style-audit
new file mode 100755
index 0000000..f658fbd
--- /dev/null
+++ b/scripts/style-audit
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+# style-audit — does each cce app stand on the standard root plate and take
+# its spacing from the ladder? A conformance check, not a build step: it
+# greps the sibling app crates and prints one row per app.
+#
+#   cce-ui/scripts/style-audit            # every cce-* app beside cce-ui
+#   cce-ui/scripts/style-audit cce-mail   # one or more apps
+#   cce-ui/scripts/style-audit --strict   # exit 1 if any row is off-standard
+#
+# Columns:
+#   root      PLATE  paints PlateSpec::window / root_plate / root_at
+#             FILL   paints a full-window quad or rounded_rect instead (off)
+#             NONE   paints no window base at all (off unless opted out)
+#   ladder    how many spacing-ladder getters the app reads
+#             (root_plate_inset/padding/gap, plate_padding/gap, control_gap,
+#             the Style::root_/pane_/controls_ presets)
+#   literals  spacing consts of the app's own (const *PAD*/*GAP*/*MARGIN*),
+#             each one a number the ladder should be supplying
+#
+# Opt-outs: a crate that is not a plate by design (a transparent surface,
+# a full-bleed canvas) declares it with a line anywhere in its src:
+#   // style-audit: opt-out <reason>
+# and is listed as OPT-OUT rather than off-standard.
+set -u
+here=$(cd "$(dirname "$0")/.." && pwd)
+ws=$(dirname "$here")
+strict=0; apps=()
+for a in "$@"; do case "$a" in --strict) strict=1;; *) apps+=("$a");; esac; done
+if [ ${#apps[@]} -eq 0 ]; then
+  for d in "$ws"/cce-*/; do
+    d=${d%/}; n=$(basename "$d")
+    case "$n" in cce-ui|cce-compositor|cce-window-manager|cce-icons) continue;; esac
+    [ -f "$d/Cargo.toml" ] && grep -q '^cce-ui' "$d/Cargo.toml" 2>/dev/null && apps+=("$n")
+  done
+fi
+printf '%-24s %-8s %-7s %-9s %s\n' app root ladder literals note
+fail=0
+for n in "${apps[@]}"; do
+  src="$ws/$n/src"; [ -d "$src" ] || { printf '%-24s (no src)\n' "$n"; continue; }
+  if grep -rqE 'style-audit: *opt-out' "$src"; then
+    reason=$(grep -rhoE 'style-audit: *opt-out.*' "$src" | head -1 | sed 's/style-audit: *opt-out *//')
+    printf '%-24s %-8s %-7s %-9s %s\n' "$n" OPT-OUT - - "$reason"; continue
+  fi
+  if grep -rqE 'PlateSpec::(window|root_at)\(|\.root_plate\(' "$src"; then root=PLATE
+  elif grep -rqE '(quad|rounded_rect)\([^;]*(0\.0, *0\.0|x: *0\.0, *y: *0\.0)' "$src"; then root=FILL
+  else root=NONE; fi
+  ladder=$(grep -rhoE 'root_plate_(inset|padding|gap)\(\)|plate_(padding|gap)\(\)|control_gap\(\)|Style::(root|pane|controls)_(column|row)\(\)|ColumnLayout::(pane|controls)\(' "$src" | wc -l)
+  literals=$(grep -rhE '^\s*(pub )?const [A-Z_]*(PAD|GAP|MARGIN|INSET)[A-Z_]*: *f32' "$src" | wc -l)
+  note=""
+  [ "$root" != PLATE ] && { note="base is not the standard root plate"; fail=1; }
+  [ "$ladder" -eq 0 ] && { note="${note:+$note; }no ladder getter read"; fail=1; }
+  printf '%-24s %-8s %-7s %-9s %s\n' "$n" "$root" "$ladder" "$literals" "$note"
+done
+[ $strict -eq 1 ] && exit $fail
+exit 0
diff --git a/src/main.rs b/src/main.rs
index 3ee44fa..281b149 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -260,15 +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: 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();
+            // DE-wide spacing by rung, never by number: the root preset insets
+            // by the plate's roll plus one padding and spaces siblings by the
+            // root gap; the controls preset puts the control gap between a
+            // form's controls (`Style::root_column` / `Style::controls_row`).
             let mut arena: Arena<LayoutBox> = Arena::new();
-            let root = arena.insert(LayoutBox::container(
-                Style::column().padding(plate_pad).gap(plate_gap).cross_align(CrossAlign::Stretch),
-            ));
+            let root = arena.insert(LayoutBox::container(Style::root_column()));
             let title = arena.insert(LayoutBox::leaf(
                 Style::row(),
                 LSize::new(0.0, text_leaf_height(TITLE_FONT_SIZE)),
@@ -284,7 +281,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(plate_gap).height(Length::Fixed(CONTROL_H)),
+                Style::controls_row().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.
@@ -295,7 +292,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(plate_gap).height(Length::Fixed(72.0)),
+                Style::row().gap(cce_ui::layout::root_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)));