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

commite56463fa59e7139593a6dd2799355c96cf04ac21
parentb1df53d31d
authorLucas Galante <[email protected]>
date2026-07-05 21:47
Refactor layouts using VStack on system_info, notifications, storage, packages, and processes pages

 src/pages/notifications.rs | 21 ++++++------
 src/pages/packages.rs      | 84 ++++++++++++++++++++++------------------------
 src/pages/processes.rs     | 82 ++++++++++++++++++++------------------------
 src/pages/storage.rs       |  9 +++--
 src/pages/system_info.rs   | 23 ++++++-------
 5 files changed, 103 insertions(+), 116 deletions(-)

diff --git a/src/pages/notifications.rs b/src/pages/notifications.rs
index 9c75266..f7c32ee 100644
--- a/src/pages/notifications.rs
+++ b/src/pages/notifications.rs
@@ -232,9 +232,10 @@ impl AppPage for NotificationsState {
         let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(1);
 
         builder.add_section(&mut final_pc, "Notifications Settings", sec_focused.first().copied().unwrap_or(false), |sec| {
-            let sec_w = sec.cw;
+            let mut stack = sec.vstack(8.0);
+            let sec_w = stack.context.cw;
             self.enable_toggle.set_toggled(self.enable);
-            sec.widget_full(&mut self.enable_toggle, cce_ui::layout::toggle_height(), ctx);
+            stack.add_widget(&mut self.enable_toggle, sec_w - 28.0, cce_ui::layout::toggle_height(), ctx);
 
             let selected_idx = match self.bell.as_str() {
                 "none" => 0,
@@ -244,24 +245,24 @@ impl AppPage for NotificationsState {
                 _ => 0,
             };
             self.bell_menu.selected = selected_idx;
-            sec.widget(&mut self.bell_menu, 14.0, sec_w - 28.0, 44.0, ctx);
+            self.bell_menu.set_row_rect(stack.context.left + 14.0, sec_w - 28.0);
+            stack.add_widget(&mut self.bell_menu, sec_w - 28.0, 44.0, ctx);
 
             self.duration_spinbox.value = self.duration;
             self.duration_spinbox.set_label("Notification Duration");
-            sec.widget(&mut self.duration_spinbox, 14.0, sec_w - 28.0, 44.0, ctx);
+            self.duration_spinbox.set_row_rect(stack.context.left + 14.0, sec_w - 28.0);
+            stack.add_widget(&mut self.duration_spinbox, sec_w - 28.0, 44.0, ctx);
 
             let btn_h = 32.0;
-            let btn_y = sec.ay();
             let white_color = [1.0, 1.0, 1.0, 1.0];
             let btn_bg = [0.20, 0.40, 0.65, 1.0];
             let btn_hover = [0.28, 0.50, 0.78, 1.0];
 
-            let cols = sec.row_layout(1, 0.0);
-            if let Some(&(x, w)) = cols.first() {
-                sec.button(
+            stack.add_row(1, 0.0, btn_h, |ctx, _, x, w| {
+                ctx.button(
                     "Send Test Notification",
                     x,
-                    btn_y,
+                    ctx.ay(),
                     w,
                     btn_h,
                     btn_bg,
@@ -269,7 +270,7 @@ impl AppPage for NotificationsState {
                     white_color,
                     AppAction::Notifications(NotificationsMessage::SendTestNotification),
                 );
-            }
+            });
         });
 
         final_pc
diff --git a/src/pages/packages.rs b/src/pages/packages.rs
index a497042..3ef981b 100644
--- a/src/pages/packages.rs
+++ b/src/pages/packages.rs
@@ -337,9 +337,8 @@ pub fn view(
             sec.text("Loading package lists...", 12.0, 0.0, 12.0, TEXT_DIM);
         } else {
             // Tab header: Installed, Updates
-            let tab_w = (sec_w - 24.0 - 8.0) / 2.0;
+            let mut stack = sec.vstack(8.0);
             let tab_h = 28.0;
-            let tab_y = sec.ay();
             let active_bg = [0.20, 0.40, 0.65, 0.4];
             let inactive_bg = [0.10, 0.10, 0.16, 0.3];
             let hover_bg = [0.20, 0.20, 0.25, 0.15];
@@ -347,42 +346,43 @@ pub fn view(
             let label1 = "Installed";
             let label2 = "Updates";
 
-            let tab_x1 = sec.left + 12.0;
-            let tab_x2 = sec.left + 12.0 + tab_w + 8.0;
-
-            sec.pc.button(
-                label1,
-                tab_x1,
-                tab_y,
-                tab_w,
-                tab_h,
-                if state.active_tab == PackageTab::Installed { active_bg } else { inactive_bg },
-                hover_bg,
-                [0.90, 0.90, 0.95, 1.0],
-                AppAction::Packages(PackagesMessage::SetTab(PackageTab::Installed)),
-            );
-
-            sec.pc.button(
-                label2,
-                tab_x2,
-                tab_y,
-                tab_w,
-                tab_h,
-                if state.active_tab == PackageTab::Updates { active_bg } else { inactive_bg },
-                hover_bg,
-                [0.90, 0.90, 0.95, 1.0],
-                AppAction::Packages(PackagesMessage::SetTab(PackageTab::Updates)),
-            );
-            sec.content_y += tab_h + 12.0;
+            stack.add_row(2, 8.0, tab_h, |ctx, i, x, w| {
+                if i == 0 {
+                    ctx.button(
+                        label1,
+                        x,
+                        ctx.ay(),
+                        w,
+                        tab_h,
+                        if state.active_tab == PackageTab::Installed { active_bg } else { inactive_bg },
+                        hover_bg,
+                        [0.90, 0.90, 0.95, 1.0],
+                        AppAction::Packages(PackagesMessage::SetTab(PackageTab::Installed)),
+                    );
+                } else {
+                    ctx.button(
+                        label2,
+                        x,
+                        ctx.ay(),
+                        w,
+                        tab_h,
+                        if state.active_tab == PackageTab::Updates { active_bg } else { inactive_bg },
+                        hover_bg,
+                        [0.90, 0.90, 0.95, 1.0],
+                        AppAction::Packages(PackagesMessage::SetTab(PackageTab::Updates)),
+                    );
+                }
+            });
+
+            stack.context.spacing(4.0);
 
             // Search box
-            let search_y = sec.ay();
             let search_w = sec_w - 24.0;
             let search_h = 46.0;
 
-            state.search_box.set_row_rect(sec.left + 12.0, search_w);
-            render_widget(sec.pc, &mut state.search_box, sec.left + 12.0, search_y, search_w, search_h, ctx);
-            sec.content_y += search_h + 16.0;
+            state.search_box.set_row_rect(stack.context.left + 12.0, search_w);
+            stack.add_widget(&mut state.search_box, search_w, search_h, ctx);
+            stack.context.spacing(8.0);
 
             // List area
             let list_box_x = sec.left + 12.0;
@@ -540,17 +540,16 @@ pub fn view(
                         }
 
                         if state.active_tab == PackageTab::Installed {
+                            let mut stack = subsec.vstack(8.0);
                             let btn_h = 32.0;
-                            let btn_y = subsec.ay();
                             let (btn_lbl, bg, hover, action) = if state.uninstalling {
                                 ("Uninstalling...", TOGGLE_OFF, TOGGLE_OFF, AppAction::Packages(PackagesMessage::StartUninstall(pkg_name.clone())))
                             } else {
                                 ("Uninstall Package", RED, BTN_HOVER, AppAction::Packages(PackagesMessage::StartUninstall(pkg_name.clone())))
                             };
-                            let cols = subsec.row_layout(1, 0.0);
-                            if let Some(&(x, w)) = cols.first() {
-                                subsec.button(btn_lbl, x, btn_y, w, btn_h, bg, hover, WHITE, action);
-                            }
+                            stack.add_row(1, 0.0, btn_h, |ctx, _, x, w| {
+                                ctx.button(btn_lbl, x, ctx.ay(), w, btn_h, bg, hover, WHITE, action.clone());
+                            });
                         }
                     } else {
                         subsec.text("No details available.", 12.0, 0.0, 12.0, TEXT_DIM);
@@ -597,8 +596,8 @@ pub fn view(
                 }
             }
 
+            let mut stack = sec2.vstack(8.0);
             let btn_h = 32.0;
-            let btn_y = sec2.ay();
 
             let (btn_lbl, bg, hover, action) = if state.updating {
                 ("Updating...", TOGGLE_OFF, TOGGLE_OFF, AppAction::Packages(PackagesMessage::StartUpdate))
@@ -606,10 +605,9 @@ pub fn view(
                 ("Update System", TOGGLE_ON, BTN_HOVER, AppAction::Packages(PackagesMessage::StartUpdate))
             };
 
-            let cols = sec2.row_layout(1, 0.0);
-            if let Some(&(x, w)) = cols.first() {
-                sec2.button(btn_lbl, x, btn_y, w, btn_h, bg, hover, WHITE, action);
-            }
+            stack.add_row(1, 0.0, btn_h, |ctx, _, x, w| {
+                ctx.button(btn_lbl, x, ctx.ay(), w, btn_h, bg, hover, WHITE, action.clone());
+            });
         }
     });
 
diff --git a/src/pages/processes.rs b/src/pages/processes.rs
index 8533316..588bed5 100644
--- a/src/pages/processes.rs
+++ b/src/pages/processes.rs
@@ -1,4 +1,4 @@
-use crate::app::{AppAction, PageContent};
+use crate::app::{AppAction, PageContent, SectionContextExt};
 use cce_ui::layout::{render_widget, PageLayoutBuilder, LayoutStrategy, RenderTarget};
 use cce_ui::widget::{List, TextBox, StatusDot, DotStatus, InteractiveListItem, Element};
 
@@ -177,60 +177,52 @@ pub fn view(state: &mut ProcessesState, cx: f32, cy: f32, cw: f32, ch: f32, root
             sec.text("Loading systemd services...", 12.0, 0.0, 12.0, TEXT_DIM);
         } else {
             // Tab header buttons: System Services, User Services
-            let tab_w = (sec_w - 24.0 - 8.0) / 2.0;
+            let mut stack = sec.vstack(8.0);
             let tab_h = 28.0;
-            let tab_y = sec.ay();
             let active_bg = [0.20, 0.40, 0.65, 0.4];
             let inactive_bg = [0.10, 0.10, 0.16, 0.3];
             let hover_bg = [0.20, 0.20, 0.25, 0.15];
 
-            let label1 = if tab_w < 110.0 { "System" } else { "System Services" };
-            let label2 = if tab_w < 110.0 { "User" } else { "User Services" };
-
-            let tab_x1 = sec.left + 12.0;
-            let tab_x2 = sec.left + 12.0 + tab_w + 8.0;
-
-            sec.pc.button(
-                label1,
-                tab_x1,
-                tab_y,
-                tab_w,
-                tab_h,
-                if state.services_active_tab == ServiceTab::System { active_bg } else { inactive_bg },
-                hover_bg,
-                [0.90, 0.90, 0.95, 1.0],
-                crate::app::AppAction::Processes(ProcessesMessage::ServicesSetTab(ServiceTab::System)),
-            );
-
-            sec.pc.button(
-                label2,
-                tab_x2,
-                tab_y,
-                tab_w,
-                tab_h,
-                if state.services_active_tab == ServiceTab::User { active_bg } else { inactive_bg },
-                hover_bg,
-                [0.90, 0.90, 0.95, 1.0],
-                crate::app::AppAction::Processes(ProcessesMessage::ServicesSetTab(ServiceTab::User)),
-            );
-            sec.content_y += tab_h + 12.0;
+            let label1 = if stack.context.cw < 250.0 { "System" } else { "System Services" };
+            let label2 = if stack.context.cw < 250.0 { "User" } else { "User Services" };
+
+            stack.add_row(2, 8.0, tab_h, |ctx, i, x, w| {
+                if i == 0 {
+                    ctx.button(
+                        label1,
+                        x,
+                        ctx.ay(),
+                        w,
+                        tab_h,
+                        if state.services_active_tab == ServiceTab::System { active_bg } else { inactive_bg },
+                        hover_bg,
+                        [0.90, 0.90, 0.95, 1.0],
+                        crate::app::AppAction::Processes(ProcessesMessage::ServicesSetTab(ServiceTab::System)),
+                    );
+                } else {
+                    ctx.button(
+                        label2,
+                        x,
+                        ctx.ay(),
+                        w,
+                        tab_h,
+                        if state.services_active_tab == ServiceTab::User { active_bg } else { inactive_bg },
+                        hover_bg,
+                        [0.90, 0.90, 0.95, 1.0],
+                        crate::app::AppAction::Processes(ProcessesMessage::ServicesSetTab(ServiceTab::User)),
+                    );
+                }
+            });
+
+            stack.context.spacing(4.0);
 
             // Search textbox
-            let search_y = sec.ay();
             let search_w = sec_w - 24.0;
             let search_h = 46.0;
             
-            state.services_search_box.set_row_rect(sec.left + 12.0, search_w);
-            render_widget(
-                sec.pc,
-                &mut state.services_search_box,
-                sec.left + 12.0,
-                search_y,
-                search_w,
-                search_h,
-                ctx,
-            );
-            sec.content_y += search_h + 16.0;
+            state.services_search_box.set_row_rect(stack.context.left + 12.0, search_w);
+            stack.add_widget(&mut state.services_search_box, search_w, search_h, ctx);
+            stack.context.spacing(8.0);
 
             // Scroll box list
             let list_box_x = sec.left + 12.0;
diff --git a/src/pages/storage.rs b/src/pages/storage.rs
index 2089f3a..87a9bb2 100644
--- a/src/pages/storage.rs
+++ b/src/pages/storage.rs
@@ -259,8 +259,8 @@ pub fn view(state: &StorageState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &m
             }
 
             // Action Button
+            let mut stack = sec.vstack(8.0);
             let btn_h = 32.0;
-            let yt = sec.ay();
             
             let (btn_label, bg, hover, action) = if state.backup_in_progress {
                 ("Backing up...", BTN_DISABLED, BTN_DISABLED, AppAction::Storage(StorageMessage::StartBackup))
@@ -268,10 +268,9 @@ pub fn view(state: &StorageState, cx: f32, cy: f32, cw: f32, ch: f32, layout: &m
                 ("Run Backup", BTN_BG, BTN_HOVER, AppAction::Storage(StorageMessage::StartBackup))
             };
             
-            let cols = sec.row_layout(1, 0.0);
-            if let Some(&(x, w)) = cols.first() {
-                sec.button(btn_label, x, yt, w, btn_h, bg, hover, WHITE, action.clone());
-            }
+            stack.add_row(1, 0.0, btn_h, |ctx, _, x, w| {
+                ctx.button(btn_label, x, ctx.ay(), w, btn_h, bg, hover, WHITE, action.clone());
+            });
         }
     });
 
diff --git a/src/pages/system_info.rs b/src/pages/system_info.rs
index cba5d90..fff00fb 100644
--- a/src/pages/system_info.rs
+++ b/src/pages/system_info.rs
@@ -564,38 +564,35 @@ pub fn view(state: &mut SystemState, cx: f32, cy: f32, cw: f32, ch: f32, _root_f
 
     // ── 2. System Actions Section ──
     builder.add_section(&mut final_pc, "System Actions", false, |sec| {
-        let yt = sec.ay();
+        let mut stack = sec.vstack(8.0);
         let act_btn_h = 32.0;
 
-        let cols = sec.row_layout(4, 8.0);
-        for (i, &(x, w)) in cols.iter().enumerate() {
+        stack.add_row(4, 8.0, act_btn_h, |ctx, i, x, w| {
             match i {
                 0 => {
-                    sec.button("Suspend", x, yt, w, act_btn_h,
+                    ctx.button("Suspend", x, ctx.ay(), w, act_btn_h,
                         SAFE_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::Suspend));
                 }
                 1 => {
-                    sec.button("Hibernate", x, yt, w, act_btn_h,
+                    ctx.button("Hibernate", x, ctx.ay(), w, act_btn_h,
                         SAFE_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::Hibernate));
                 }
                 2 => {
-                    sec.button("Reboot", x, yt, w, act_btn_h,
+                    ctx.button("Reboot", x, ctx.ay(), w, act_btn_h,
                         DANGER_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::Reboot));
                 }
                 3 => {
-                    sec.button("Power Off", x, yt, w, act_btn_h,
+                    ctx.button("Power Off", x, ctx.ay(), w, act_btn_h,
                         DANGER_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::PowerOff));
                 }
                 _ => {}
             }
-        }
+        });
 
-        let yt2 = yt + act_btn_h + 8.0;
-        let cols2 = sec.row_layout(1, 0.0);
-        if let Some(&(x, w)) = cols2.first() {
-            sec.button("Force Shutdown", x, yt2, w, act_btn_h,
+        stack.add_row(1, 0.0, act_btn_h, |ctx, _, x, w| {
+            ctx.button("Force Shutdown", x, ctx.ay(), w, act_btn_h,
                 DANGER_BG, BTN_HOVER, WHITE, AppAction::SystemInfo(SystemMessage::ForceShutdown));
-        }
+        });
     });
 
     // ── 3. CPU Section ──