git.lucas.co / cce-system-interface
system settings
git clone https://git.lucas.co/cce-system-interface.git

commitc5765b25b0da13d608f526130b25eca1495d0fcb
parent06b174aac9
authorLucas Galante <[email protected]>
date2026-07-09 16:20
feat: root Backplate + StatusBar DISSOLVED (Phase 6s)

The window is assembled by hand, replicating the legacy
render_widget(root Backplate) aggregate exactly: every child plain quad
(clipped to the window, corner-resolved against the root's rounded rect —
a quad flush with a window corner picks up the plate radius), then the
translucent window plate, then every child rounded quad, then the
root-clamped text, in the old child order [switcher, statusbar, dropdown,
search box]. Settings needed the corner resolution colors/files didn't:
its plate radius is a hardcoded 12.

The StatusBar widget dissolves outright (pure chrome here): its
Backplate-parent-coupled theming — statusbar theme color falling back to
STATUS_BG, bottom corners rounded at the root radius, statusbar text
color/font/align — is replicated as tuples; the widget is deleted and the
bar text lives in a status_text String. The page dropdown keeps its
backplate-concentric corner cut via the new Dropdown::set_corner_frame
(the ancestor walk finds nothing once the root is gone). The root's
with_border was never rendered (a rounded Backplate emits no plain bg
quad, and the border branch only fires on plain bg quads) — dropped.

Events were already root-free (direct dispatch to dropdown/switcher/
search box/page root). The window was with_movable(false); with no
registered Backplate the engine default is still no-drag.

A/B: WINDOW_PC tuple stream byte-identical; pixels AE=0. Live-verified:
page-dropdown popover (renders on the engine xdg popup; item click
switches to Processes; statusbar text follows), wheel scroll, service
list render. Pre-existing, unchanged by this: the engine positioner
anchors the popup at the widget's bottom edge, so the open-upward
popover displays BELOW the window while clicks land on the app-side
(invisible, in-window) popover rect — reproduced on the pre-6s baseline.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016MjP3pGQEDLkJbV5WEmYBe

 src/main.rs     |  29 +++--------
 src/renderer.rs | 155 +++++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 143 insertions(+), 41 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index b81d825..0543bdb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -74,11 +74,12 @@ struct SystemInterface {
     scrollable_buttons_start_idx: usize,
     last_scroll_y: f32,
     page_sec_containers: Vec<cce_ui::widget::SectionContainer>,
-    root_window: cce_ui::widget::Backplate,
     page_dropdown: cce_ui::widget::Adapted<cce_ui::widget::input::Dropdown>,
     switcher: cce_ui::widget::Adapted<cce_ui::widget::Switcher>,
     pages: Vec<cce_ui::widget::Page>,
-    statusbar: cce_ui::widget::Adapted<cce_ui::widget::StatusBar>,
+    // Root Backplate + StatusBar DISSOLVED (Phase 6s): the window plate and the status
+    // bar are emitted as tuples in rebuild_layout; this is the bar's text.
+    status_text: String,
     sans_serif_family: String,
     serif_family: String,
     monospace_family: String,
@@ -137,10 +138,6 @@ impl cce_ui::engine::Application for SystemInterface {
         let mut app_state = app;
         app_state.current_page = Page::ALL[initial_page_idx];
 
-        let win_color = [0x0a, 0x1a, 0x0e];
-        let win_opacity = 1.0f32;
-        let win_radius = 12;
-
         let font_system = cce_ui::create_font_system();
 
         let mut this = Self {
@@ -181,20 +178,10 @@ impl cce_ui::engine::Application for SystemInterface {
             scrollable_buttons_start_idx: 0,
             last_scroll_y: 0.0,
             page_sec_containers: Vec::new(),
-            root_window: cce_ui::widget::Backplate::new(0.0, 0.0, 820.0, 680.0)
-                .with_movable(false)
-                .with_background([
-                    win_color[0] as f32 / 255.0,
-                    win_color[1] as f32 / 255.0,
-                    win_color[2] as f32 / 255.0,
-                    win_opacity,
-                ])
-                .with_border([0.22, 0.22, 0.28, 1.0], 1.5)
-                .with_radius(win_radius as f32),
             page_dropdown,
             switcher,
             pages,
-            statusbar: cce_ui::widget::StatusBar::new(),
+            status_text: String::new(),
             sans_serif_family: sans_family,
             serif_family,
             monospace_family,
@@ -214,10 +201,8 @@ impl cce_ui::engine::Application for SystemInterface {
             this.switcher.add_child(page.as_ptr(), &mut this.ui_context);
         }
 
-        this.root_window.add_child(this.switcher.as_ptr(), &mut this.ui_context);
-        this.root_window.add_child(this.statusbar.as_ptr(), &mut this.ui_context);
-        this.root_window.add_child(this.page_dropdown.as_ptr(), &mut this.ui_context);
-
+        // Root Backplate dissolved: switcher/dropdown register parentless via
+        // render_widget in rebuild_layout.
         this.update_status_text();
 
         this.rebuild_layout(820.0, 680.0);
@@ -609,7 +594,7 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
             Page::Storage => "Storage Settings: Manage local disks, partition structures, and backup runs.",
             Page::System => "System Settings: System properties, power management, and update checks.",
         };
-        self.statusbar.set_text(msg);
+        self.status_text = msg.to_string();
     }
 
 }
diff --git a/src/renderer.rs b/src/renderer.rs
index e9767c2..5875eda 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -3,6 +3,84 @@ use cce_settings::app::PageContent;
 use cce_settings::pages::Page;
 use cce_ui::widget::Element;
 
+type RectTuple = ([f32; 4], f32, f32, f32, f32, f32, (bool, bool, bool, bool));
+type TextTuple = (String, f32, f32, f32, [f32; 4], Option<String>, Option<[f32; 4]>);
+
+/// One child's contribution to the dissolved root's window assembly, replicating the
+/// legacy `render_widget(root Backplate)` aggregate exactly: plain quads are skipped
+/// when they are a rounded child's own bg (the rounded pass carries it), clipped to
+/// the window, and corner-resolved against the root's rounded rect (a quad flush with
+/// a window corner picks up the plate radius there); rounded quads are clipped;
+/// text bounds are clamped to the window (unbounded labels become window-bounded).
+fn collect_window_child(
+    w: &dyn Element,
+    ctx: &cce_ui::context::UiContext,
+    win_w: f32,
+    win_h: f32,
+    plate_radius: f32,
+    plain: &mut Vec<RectTuple>,
+    rounded: &mut Vec<RectTuple>,
+    texts: &mut Vec<TextTuple>,
+) {
+    let (cx, cy, cw, ch) = w.rect();
+    let child_rounded = w.rounded_corners() != (false, false, false, false);
+    for (qx, qy, qw, qh, qc) in w.all_quads(ctx) {
+        if child_rounded && (qx - cx).abs() < 0.1 && (qy - cy).abs() < 0.1 && (qw - cw).abs() < 0.1 && (qh - ch).abs() < 0.1 {
+            continue;
+        }
+        let x0 = qx.max(0.0);
+        let y0 = qy.max(0.0);
+        let x1 = (qx + qw).min(win_w);
+        let y1 = (qy + qh).min(win_h);
+        if x1 <= x0 || y1 <= y0 {
+            continue;
+        }
+        let corners = (
+            x0 <= 1.5 && y0 <= 1.5,
+            x1 >= win_w - 1.5 && y0 <= 1.5,
+            x1 >= win_w - 1.5 && y1 >= win_h - 1.5,
+            x0 <= 1.5 && y1 >= win_h - 1.5,
+        );
+        if corners == (false, false, false, false) {
+            plain.push((qc, x0, y0, x1 - x0, y1 - y0, 0.0, (false, false, false, false)));
+        } else {
+            plain.push((qc, x0, y0, x1 - x0, y1 - y0, plate_radius, corners));
+        }
+    }
+    for (qx, qy, qw, qh, qr, qc, qcorners) in w.all_rounded_quads(ctx) {
+        let x0 = qx.max(0.0);
+        let y0 = qy.max(0.0);
+        let x1 = (qx + qw).min(win_w);
+        let y1 = (qy + qh).min(win_h);
+        if x1 <= x0 || y1 <= y0 {
+            continue;
+        }
+        rounded.push((qc, x0, y0, x1 - x0, y1 - y0, qr, qcorners));
+    }
+    for (label, font, bounds) in w.text_labels_with_font_and_bounds(ctx) {
+        let cb = match bounds {
+            Some(b) => {
+                let bx0 = b[0].max(0.0);
+                let by0 = b[1].max(0.0);
+                let bx1 = b[2].min(win_w);
+                let by1 = b[3].min(win_h);
+                if bx1 <= bx0 || by1 <= by0 {
+                    continue;
+                }
+                Some([bx0, by0, bx1, by1])
+            }
+            None => Some([0.0, 0.0, win_w, win_h]),
+        };
+        let color = [
+            label.color[0] as f32 / 255.0,
+            label.color[1] as f32 / 255.0,
+            label.color[2] as f32 / 255.0,
+            1.0,
+        ];
+        texts.push((label.text.clone(), label.font_size, label.x, label.y, color, font, cb));
+    }
+}
+
 impl SystemInterface {
 
     pub(crate) fn rebuild_layout(&mut self, sw: f32, sh: f32) {
@@ -70,22 +148,10 @@ impl SystemInterface {
         self.page_dropdown.selected = page_idx;
         self.switcher.set_active_index(Some(page_idx));
 
-        // Update root window size, background color, opacity, corner radius, and children
-        self.root_window.set_rect(0.0, 0.0, logical_sw, logical_sh);
-        let win_r = 0x0a as f32 / 255.0;
-        let win_g = 0x1a as f32 / 255.0;
-        let win_b = 0x0e as f32 / 255.0;
-        let win_a = 1.0f32;
-        self.root_window.background_color = Some(cce_ui::color::to_linear([win_r, win_g, win_b, win_a]));
-        self.root_window.radius = 12.0;
-        self.root_window.clear_children(&mut self.ui_context);
-        self.root_window.add_child(self.switcher.as_ptr(), &mut self.ui_context);
-        self.root_window.add_child(self.statusbar.as_ptr(), &mut self.ui_context);
-        self.root_window.add_child(self.page_dropdown.as_ptr(), &mut self.ui_context);
-        if self.search_open {
-            use cce_ui::widget::focus::link_parent_child;
-            link_parent_child(&mut self.root_window, &mut self.search_box, &mut self.ui_context);
-        }
+        // Root Backplate DISSOLVED (Phase 6s): top-level widgets stay parentless
+        // (render_widget registers them); the window plate, the root aggregate's
+        // emission order, and the StatusBar's Backplate-coupled theming are all
+        // replicated by hand below.
 
         // Position sidebar and switcher below the titlebar
         let mut dummy_pc = PageContent::new();
@@ -98,6 +164,9 @@ impl SystemInterface {
         let dropdown_gap = (self.status_height - dropdown_h) / 2.0;
         let dropdown_x = logical_sw - dropdown_w - dropdown_gap;
         let dropdown_y = logical_sh - self.status_height + dropdown_gap;
+        // The dropdown sits flush against the window's rounded bottom-right corner; with the
+        // root Backplate dissolved, hand it the plate frame for its concentric-corner cut.
+        self.page_dropdown.set_corner_frame(Some(((0.0, 0.0, logical_sw, logical_sh), 12.0, (true, true, true, true))));
         cce_ui::layout::render_widget(&mut dummy_pc, &mut self.page_dropdown, dropdown_x, dropdown_y, dropdown_w, dropdown_h, &mut self.ui_context);
         let switcher_h = if self.search_open {
             logical_sh - self.header_height - 42.0 - self.status_height
@@ -105,10 +174,58 @@ impl SystemInterface {
             logical_sh - self.header_height - self.status_height
         };
         cce_ui::layout::render_widget(&mut dummy_pc, &mut self.switcher, self.sidebar_width, self.header_height, logical_sw - self.sidebar_width, switcher_h, &mut self.ui_context);
-        cce_ui::layout::render_widget(&mut dummy_pc, &mut self.statusbar, 0.0, logical_sh - self.status_height, logical_sw, self.status_height, &mut self.ui_context);
-        // Render root window recursively
+
+        // Assemble the window exactly as the legacy `render_widget(root Backplate)`
+        // aggregate did: every child plain quad (clipped to the window, with the root's
+        // corner resolution against its rounded rect), then the translucent window
+        // plate, then every child rounded quad, then the root-clamped text — in the old
+        // child order [switcher, statusbar, dropdown, search box]. The StatusBar widget
+        // is dissolved outright: its theming was Backplate-parent-coupled (statusbar
+        // theme color falling back to STATUS_BG, bottom corners rounded at the root's
+        // radius, statusbar text color/font), replicated here as tuples.
+        let plate_radius = 12.0f32;
         let mut window_pc = PageContent::new();
-        cce_ui::layout::render_widget(&mut window_pc, &mut self.root_window, 0.0, 0.0, logical_sw, logical_sh, &mut self.ui_context);
+        {
+            let mut plain: Vec<RectTuple> = Vec::new();
+            let mut rounded: Vec<RectTuple> = Vec::new();
+            let mut wtexts: Vec<TextTuple> = Vec::new();
+
+            collect_window_child(&self.switcher, &self.ui_context, logical_sw, logical_sh, plate_radius, &mut plain, &mut rounded, &mut wtexts);
+
+            // The dissolved status bar's slot in the child order.
+            let sb_y = logical_sh - self.status_height;
+            let sb_theme = cce_ui::colors::backplate_statusbar_color();
+            let sb_bg = if sb_theme[3] > 0.001 { sb_theme } else { cce_ui::color::STATUS_BG };
+            rounded.push((sb_bg, 0.0, sb_y, logical_sw, self.status_height, plate_radius, (false, false, true, true)));
+            if !self.status_text.is_empty() {
+                let (_, sb_font_size) = cce_ui::layout::statusbar_font_parsed();
+                let sb_size = if sb_font_size > 0.0 { sb_font_size } else { 12.0 };
+                let c = cce_ui::colors::backplate_statusbar_text_color();
+                // Same u8 round-trip the legacy TextLabel path applied.
+                let sb_color = [
+                    ((c[0] * 255.0) as u8) as f32 / 255.0,
+                    ((c[1] * 255.0) as u8) as f32 / 255.0,
+                    ((c[2] * 255.0) as u8) as f32 / 255.0,
+                    1.0,
+                ];
+                let sb_text_y = cce_ui::layout::align_text_y(sb_y, self.status_height, sb_size, 0.0);
+                wtexts.push((self.status_text.clone(), sb_size, 12.0, sb_text_y, sb_color, None, Some([0.0, 0.0, logical_sw, logical_sh])));
+            }
+
+            collect_window_child(&self.page_dropdown, &self.ui_context, logical_sw, logical_sh, plate_radius, &mut plain, &mut rounded, &mut wtexts);
+            if self.search_open {
+                collect_window_child(&self.search_box, &self.ui_context, logical_sw, logical_sh, plate_radius, &mut plain, &mut rounded, &mut wtexts);
+            }
+
+            window_pc.rects.extend(plain);
+            let mut plate = cce_ui::color::to_linear([0x0a as f32 / 255.0, 0x1a as f32 / 255.0, 0x0e as f32 / 255.0, 1.0]);
+            if plate[3] > 0.001 {
+                plate[3] = cce_ui::color::active_backplate_opacity();
+            }
+            window_pc.rects.push((plate, 0.0, 0.0, logical_sw, logical_sh, plate_radius, (true, true, true, true)));
+            window_pc.rects.extend(rounded);
+            window_pc.texts.extend(wtexts);
+        }
 
         let page_idx = Page::ALL.iter().position(|&p| p == self.app.current_page).unwrap_or(0);
         let active_page_widget = &self.pages[page_idx];