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

commit0a0f2f790a0ed8dbca050491eaff5d62f8348572
parentc1f1d91f2e
authorLucas Galante <[email protected]>
date2026-06-18 10:22
Refactor SystemInterface to use root Window widget and CSD titlebar

 src/app.rs  |  1 +
 src/main.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 58 insertions(+), 10 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index 77dbf2e..3203e93 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -52,6 +52,7 @@ impl Default for AppState {
 
 #[derive(Debug, Clone)]
 pub enum AppAction {
+    Exit,
     Audio(audio::AudioMessage),
     Display(display::DisplayMessage),
     Radios(network::NetworkMessage),
diff --git a/src/main.rs b/src/main.rs
index b8cd946..e2f9588 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,5 @@
 use cce_ui::widget::{Finger, hover_animation, TextItem, Element, PageSelector};
+use cce_ui::layout::RenderTarget;
 use glyphon::{Attrs, Buffer, FontSystem, Metrics};
 
 use cce_system_interface::app::{AppAction, AppState, PageContent};
@@ -162,6 +163,7 @@ struct SystemInterface {
     audio_source_dragging: Option<usize>,
     display_brightness_dragging: bool,
     page_sec_containers: Vec<cce_ui::widget::Container>,
+    root_window: cce_ui::widget::Window,
     menubar: cce_ui::widget::MenuBar,
     switcher: cce_ui::widget::Switcher,
     plates: Vec<cce_ui::widget::Plate>,
@@ -469,6 +471,10 @@ impl cce_ui::engine::Application for SystemInterface {
             audio_source_dragging: None,
             display_brightness_dragging: false,
             page_sec_containers: Vec::new(),
+            root_window: cce_ui::widget::Window::new(0.0, 0.0, 820.0, 680.0)
+                .with_background([0.06, 0.06, 0.09, 1.0])
+                .with_border([0.22, 0.22, 0.28, 1.0], 1.5)
+                .with_radius(12.0),
             menubar,
             switcher,
             plates,
@@ -484,6 +490,9 @@ impl cce_ui::engine::Application for SystemInterface {
             this.switcher.add_child(plate.as_ptr(), &mut this.ui_context);
         }
 
+        this.root_window.add_child(this.menubar.as_ptr(), &mut this.ui_context);
+        this.root_window.add_child(this.switcher.as_ptr(), &mut this.ui_context);
+
         this.rebuild_layout(820.0, 680.0);
         this
     }
@@ -499,7 +508,11 @@ impl cce_ui::engine::Application for SystemInterface {
         }
     }
 
-    fn update(&mut self, msg: Self::Message, needs_rebuild: &mut bool, _exit: &mut bool) {
+    fn update(&mut self, msg: Self::Message, needs_rebuild: &mut bool, exit: &mut bool) {
+        if matches!(msg, AppAction::Exit) {
+            *exit = true;
+            return;
+        }
         self.handle_action(&msg);
         *needs_rebuild = true;
         self.needs_rebuild = true;
@@ -1157,13 +1170,12 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
         }
 
         self.sidebar_width = self.menubar.sidebar_w();
-        self.header_height = 0.0;
+        self.header_height = 32.0; // CSD Titlebar height
         let s = 1.0f32;
         let mut widgets = Vec::new();
         let mut text_items = Vec::new();
         let mut page_buttons = Vec::new();
 
-
         cce_ui::widget::hover_animation::reset_frame_registration();
         cce_ui::widget::popovers::clear();
         cce_ui::widget::hover_animation::set_scroll_offset(self.scroll_y);
@@ -1178,13 +1190,39 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
         self.menubar.set_selected_page(page_idx);
         self.switcher.set_active_index(Some(page_idx));
 
-        let mut menubar_pc = PageContent::new();
-        cce_ui::layout::render_widget(&mut menubar_pc, &mut self.menubar, 0.0, 0.0, self.sidebar_width, sh / s, &mut self.ui_context);
-
-        let mut switcher_pc = PageContent::new();
-        cce_ui::layout::render_widget(&mut switcher_pc, &mut self.switcher, self.sidebar_width, 0.0, sw / s - self.sidebar_width, sh / s, &mut self.ui_context);
+        // Update root window size and children
+        self.root_window.set_rect(0.0, 0.0, sw / s, sh / s);
+        self.root_window.clear_children(&mut self.ui_context);
+        self.root_window.add_child(self.menubar.as_ptr(), &mut self.ui_context);
+        self.root_window.add_child(self.switcher.as_ptr(), &mut self.ui_context);
+
+        // Position sidebar and switcher below the titlebar
+        let mut dummy_pc = PageContent::new();
+        cce_ui::layout::render_widget(&mut dummy_pc, &mut self.menubar, 0.0, self.header_height, self.sidebar_width, sh / s - self.header_height, &mut self.ui_context);
+        cce_ui::layout::render_widget(&mut dummy_pc, &mut self.switcher, self.sidebar_width, self.header_height, sw / s - self.sidebar_width, sh / s - self.header_height, &mut self.ui_context);
+
+        // Render root window recursively
+        let mut window_pc = PageContent::new();
+        cce_ui::layout::render_widget(&mut window_pc, &mut self.root_window, 0.0, 0.0, sw / s, sh / s, &mut self.ui_context);
+
+        // Append CSD Titlebar background & border separator to window_pc
+        window_pc.rect([0.12, 0.12, 0.15, 1.0], 0.0, 0.0, sw / s, self.header_height);
+        window_pc.rect([0.22, 0.22, 0.28, 1.0], 0.0, self.header_height - 1.0, sw / s, 1.0);
+
+        // Title text in Titlebar
+        window_pc.text("SYSTEM INTERFACE", 12.0, (self.header_height - 12.0) / 2.0, 12.0, [0.8, 0.8, 0.83, 1.0]);
+
+        // CSD Window Control Buttons (Close, Minimize, Maximize)
+        let btn_y = (self.header_height - 12.0) / 2.0;
+        let close_x = sw / s - 24.0;
+        let min_x = sw / s - 44.0;
+        let max_x = sw / s - 64.0;
+        
+        window_pc.rect_with_radius([0.9, 0.3, 0.3, 1.0], close_x, btn_y, 12.0, 12.0, 6.0);
+        window_pc.rect_with_radius([0.9, 0.8, 0.2, 1.0], min_x, btn_y, 12.0, 12.0, 6.0);
+        window_pc.rect_with_radius([0.2, 0.8, 0.2, 1.0], max_x, btn_y, 12.0, 12.0, 6.0);
 
-        for pc_part in &[menubar_pc, switcher_pc] {
+        for pc_part in &[window_pc] {
             for (c, x, y, w, h, r, corners) in &pc_part.rects {
                 widgets.push(AppWidget {
                     x: *x * s, y: *y * s, w: *w * s, h: *h * s,
@@ -1645,6 +1683,7 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
     fn handle_action(&mut self, action: &AppAction) {
         use pages::*;
         match action {
+            AppAction::Exit => {}
             AppAction::Audio(m) => audio::update(&mut self.app.audio, m.clone()),
             AppAction::Display(m) => display::update(&mut self.app.display, m.clone()),
             AppAction::Radios(m) => network::update(&mut self.app.network, m.clone()),
@@ -2284,7 +2323,15 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
         let lx_no_scroll = self.cursor_x / s;
         let ly_no_scroll = self.cursor_y / s;
 
-
+        // CSD Close Button Interaction
+        if state == cce_ui::widget::ElementState::Pressed && button == cce_ui::widget::MouseButton::Left {
+            let btn_y = (self.header_height - 12.0) / 2.0;
+            let close_x = self.width as f32 - 24.0;
+            if lx_no_scroll >= close_x - 4.0 && lx_no_scroll <= close_x + 16.0 && ly_no_scroll >= btn_y - 4.0 && ly_no_scroll <= btn_y + 16.0 {
+                self.handle_action(&AppAction::Exit);
+                return true;
+            }
+        }
 
         if cce_ui::widget::context_menu::is_visible() {
             if cce_ui::widget::context_menu::mouse_input(button, state, lx_no_scroll, ly_no_scroll) {