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

commit0189c2dfb60d5a93afb7afdab53d5e2e1eb264b3
parenta5ae03788c
authorLucas Galante <[email protected]>
date2026-06-10 22:00
Adapt system interface pages and integrate scale factor

 src/main.rs            | 694 +++++++++++++++++++++++++++++++++++++++++--------
 src/pages/interface.rs | 149 ++++++++++-
 2 files changed, 732 insertions(+), 111 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index c0b0c8c..0a17ab6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,4 @@
-use clear_ui::widget::{Finger, hover_animation, TextItem, Element};
+use clear_ui::widget::{Finger, hover_animation, TextItem, Element, PageSelector};
 use glyphon::{Attrs, Buffer, FontSystem, Metrics};
 
 use cce_system_interface::app::{AppAction, AppState, ContentButton, PageContent};
@@ -161,9 +161,10 @@ struct SystemInterface {
     audio_sink_dragging: Option<usize>,
     audio_source_dragging: Option<usize>,
     display_brightness_dragging: bool,
-    page_root_container: clear_ui::widget::Container,
     page_sec_containers: Vec<clear_ui::widget::Container>,
-    paginator: clear_ui::widget::Paginator,
+    menubar: clear_ui::widget::MenuBar,
+    switcher: clear_ui::widget::Switcher,
+    plates: Vec<clear_ui::widget::Plate>,
     sans_serif_family: String,
     serif_family: String,
     monospace_family: String,
@@ -404,9 +405,20 @@ impl clear_ui::engine::Application for SystemInterface {
         let (sans_family, serif_family, monospace_family, _, _, _, _, _) = pages::interface::read_preferred_fonts();
 
         let pages_names = Page::ALL.iter().map(|p| p.label().to_string()).collect::<Vec<_>>();
-        let paginator = clear_ui::widget::Paginator::new(56.0, pages_names)
-            .with_tabs_rotated(true)
-            .with_sidebar_label("SYSTEM");
+        let mut menubar = clear_ui::widget::MenuBar::new(0.0, 0.0, 180.0, 680.0)
+            .with_vertical(true)
+            .with_title("SYSTEM");
+        menubar.set_pages(pages_names);
+        let sidebar_width = menubar.sidebar_w();
+
+        let switcher = clear_ui::widget::Switcher::new(sidebar_width, 0.0, 820.0 - sidebar_width, 680.0);
+        let mut plates = Vec::new();
+        for page in Page::ALL.iter() {
+            let plate = clear_ui::widget::Plate::new(sidebar_width, 0.0, 820.0 - sidebar_width, 680.0)
+                .with_label(page.label())
+                .with_draggable(false);
+            plates.push(plate);
+        }
 
         let mut app_state = app;
         app_state.current_page = Page::ALL[initial_page_idx];
@@ -419,7 +431,7 @@ impl clear_ui::engine::Application for SystemInterface {
             widgets: Vec::new(),
             text_items: Vec::new(),
             page_buttons: Vec::new(),
-            sidebar_width: paginator.sidebar_w(),
+            sidebar_width,
             header_height: 0.0,
             status_height: 0.0,
             cursor_x: 0.0,
@@ -457,9 +469,10 @@ impl clear_ui::engine::Application for SystemInterface {
             audio_sink_dragging: None,
             audio_source_dragging: None,
             display_brightness_dragging: false,
-            page_root_container: clear_ui::widget::Container::new(),
             page_sec_containers: Vec::new(),
-            paginator,
+            menubar,
+            switcher,
+            plates,
             sans_serif_family: sans_family,
             serif_family,
             monospace_family,
@@ -467,6 +480,11 @@ impl clear_ui::engine::Application for SystemInterface {
             sender,
             ui_context: clear_ui::context::UiContext::new(),
         };
+
+        for plate in &mut this.plates {
+            this.switcher.add_child(plate.as_ptr(), &mut this.ui_context);
+        }
+
         this.rebuild_layout(820.0, 680.0);
         this
     }
@@ -575,60 +593,18 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
 }
 
     fn rebuild_layout(&mut self, sw: f32, sh: f32) {
-        self.sidebar_width = self.paginator.sidebar_w();
-        let s = 1.0f32;
-        let mut widgets = Vec::new();
-        let mut text_items = Vec::new();
-        let mut page_buttons = Vec::new();
-
-        clear_ui::widget::hover_animation::reset_frame_registration();
-        clear_ui::widget::popovers::clear();
-        clear_ui::widget::hover_animation::set_scroll_offset(self.scroll_y);
-        clear_ui::widget::hover_animation::set_cursor_pos(self.cursor_x / s, self.cursor_y / s);
-
-        let lcx = self.sidebar_width;
-        let lcy = self.header_height;
-        let lcw = sw / s - self.sidebar_width;
-        let lch = sh / s - self.header_height - self.status_height;
-
-        let page_idx = Page::ALL.iter().position(|&p| p == self.app.current_page).unwrap_or(0);
-        self.paginator.set_selected_page(page_idx);
-
-        let mut paginator_pc = PageContent::new();
-        clear_ui::layout::render_widget(&mut paginator_pc, &mut self.paginator, 0.0, 0.0, sw / s, sh / s, &mut self.ui_context);
-
-        for (c, x, y, w, h) in &paginator_pc.rects {
-            widgets.push(AppWidget {
-                x: *x * s, y: *y * s, w: *w * s, h: *h * s,
-                color: *c, hover_color: *c,
-                hovering: false,
-            });
-        }
-        for (t, size, x, y, tc, font_opt, bounds) in &paginator_pc.texts {
-            text_items.push(TextItem {
-                buffer: make_text_buffer_with_font(
-                    &mut self.font_system,
-                    t,
-                    *size * s,
-                    font_opt.as_deref(),
-                    &self.sans_serif_family,
-                    &self.serif_family,
-                    &self.monospace_family,
-                ),
-                x: *x * s, y: *y * s,
-                color: glyphon::Color::rgb(
-                    (tc[0] * 255.0) as u8, (tc[1] * 255.0) as u8, (tc[2] * 255.0) as u8,
-                ),
-                bounds: *bounds,
-            });
+        self.switcher.clear_children(&mut self.ui_context);
+        for plate in &mut self.plates {
+            self.switcher.add_child(plate.as_ptr(), &mut self.ui_context);
         }
 
-        // Page content in LOGICAL coordinates, then scale to physical
-        let mut pc = self.render_page_content(lcx, lcy, lcw, lch);
+        let page_idx = Page::ALL.iter().position(|&p| p == self.app.current_page).unwrap_or(0);
 
         // ── Rebuild Element Focus Hierarchy ──
-        self.page_root_container.clear_children(&mut self.ui_context);
-        self.page_root_container.set_parent(None, &mut self.ui_context);
+        for plate in &mut self.plates {
+            plate.clear_children(&mut self.ui_context);
+        }
+        let page_root = &mut self.plates[page_idx];
         self.page_sec_containers.clear();
 
         // Clear all widgets' hierarchy links
@@ -666,6 +642,8 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
         }
         self.app.interface.tab_margin_spinbox_x.clear_children(&mut self.ui_context);
         self.app.interface.tab_margin_spinbox_x.set_parent(None, &mut self.ui_context);
+        self.app.interface.menubar_opacity_spinbox.clear_children(&mut self.ui_context);
+        self.app.interface.menubar_opacity_spinbox.set_parent(None, &mut self.ui_context);
         self.app.interface.tab_margin_spinbox_y.clear_children(&mut self.ui_context);
         self.app.interface.tab_margin_spinbox_y.set_parent(None, &mut self.ui_context);
         self.app.interface.tab_padding_spinbox_x.clear_children(&mut self.ui_context);
@@ -690,6 +668,8 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
         self.app.interface.graph_uniform_background_toggle.set_parent(None, &mut self.ui_context);
         self.app.interface.graph_network_opacity_slider.clear_children(&mut self.ui_context);
         self.app.interface.graph_network_opacity_slider.set_parent(None, &mut self.ui_context);
+        self.app.interface.graph_gap_width_spinbox.clear_children(&mut self.ui_context);
+        self.app.interface.graph_gap_width_spinbox.set_parent(None, &mut self.ui_context);
         self.app.interface.page_margin_spinbox.clear_children(&mut self.ui_context);
         self.app.interface.page_margin_spinbox.set_parent(None, &mut self.ui_context);
         self.app.interface.grid_min_col_width_spinbox.clear_children(&mut self.ui_context);
@@ -733,7 +713,6 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
         self.app.interface.terminal_menu.clear_children(&mut self.ui_context); self.app.interface.terminal_menu.set_parent(None, &mut self.ui_context);
         self.app.interface.terminal_box.clear_children(&mut self.ui_context); self.app.interface.terminal_box.set_parent(None, &mut self.ui_context);
 
-
         self.app.services.notifications_enable_toggle.clear_children(&mut self.ui_context); self.app.services.notifications_enable_toggle.set_parent(None, &mut self.ui_context);
         self.app.services.notifications_bell_toggle.clear_children(&mut self.ui_context); self.app.services.notifications_bell_toggle.set_parent(None, &mut self.ui_context);
         self.app.services.notifications_duration_spinbox.clear_children(&mut self.ui_context); self.app.services.notifications_duration_spinbox.set_parent(None, &mut self.ui_context);
@@ -774,6 +753,18 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
 
         self.app.display.brightness_spinbox.clear_children(&mut self.ui_context); self.app.display.brightness_spinbox.set_parent(None, &mut self.ui_context);
         self.app.display.brightness_slider.clear_children(&mut self.ui_context); self.app.display.brightness_slider.set_parent(None, &mut self.ui_context);
+        self.app.display.night_light_label.clear_children(&mut self.ui_context); self.app.display.night_light_label.set_parent(None, &mut self.ui_context);
+        self.app.display.screensaver_enable_toggle.clear_children(&mut self.ui_context); self.app.display.screensaver_enable_toggle.set_parent(None, &mut self.ui_context);
+        self.app.display.screensaver_lock_screen_toggle.clear_children(&mut self.ui_context); self.app.display.screensaver_lock_screen_toggle.set_parent(None, &mut self.ui_context);
+        self.app.display.screensaver_timeout_spinbox.clear_children(&mut self.ui_context); self.app.display.screensaver_timeout_spinbox.set_parent(None, &mut self.ui_context);
+        self.app.display.screensaver_style_menu.clear_children(&mut self.ui_context); self.app.display.screensaver_style_menu.set_parent(None, &mut self.ui_context);
+        for out in &mut self.app.display.outputs {
+            out.name_label.clear_children(&mut self.ui_context); out.name_label.set_parent(None, &mut self.ui_context);
+            out.resolution_label.clear_children(&mut self.ui_context); out.resolution_label.set_parent(None, &mut self.ui_context);
+            if let Some(ref mut scale_lbl) = out.scale_label {
+                scale_lbl.clear_children(&mut self.ui_context); scale_lbl.set_parent(None, &mut self.ui_context);
+            }
+        }
         self.app.services.status_separators_toggle.clear_children(&mut self.ui_context); self.app.services.status_separators_toggle.set_parent(None, &mut self.ui_context);
         self.app.services.status_underline_toggle.clear_children(&mut self.ui_context); self.app.services.status_underline_toggle.set_parent(None, &mut self.ui_context);
         self.app.services.status_padding_spinbox.clear_children(&mut self.ui_context); self.app.services.status_padding_spinbox.set_parent(None, &mut self.ui_context);
@@ -791,20 +782,20 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
         match self.app.current_page {
             Page::Accounts => {
                 if self.app.accounts.editing_oauth_creds {
-                    link_parent_child(&mut self.page_root_container, &mut self.app.accounts.oauth_client_id_box, &mut self.ui_context);
-                    link_parent_child(&mut self.page_root_container, &mut self.app.accounts.oauth_client_secret_box, &mut self.ui_context);
+                    link_parent_child(page_root, &mut self.app.accounts.oauth_client_id_box, &mut self.ui_context);
+                    link_parent_child(page_root, &mut self.app.accounts.oauth_client_secret_box, &mut self.ui_context);
                 } else {
-                    link_parent_child(&mut self.page_root_container, &mut self.app.accounts.email_box, &mut self.ui_context);
-                    link_parent_child(&mut self.page_root_container, &mut self.app.accounts.password_box, &mut self.ui_context);
-                    link_parent_child(&mut self.page_root_container, &mut self.app.accounts.imap_box, &mut self.ui_context);
-                    link_parent_child(&mut self.page_root_container, &mut self.app.accounts.smtp_box, &mut self.ui_context);
+                    link_parent_child(page_root, &mut self.app.accounts.email_box, &mut self.ui_context);
+                    link_parent_child(page_root, &mut self.app.accounts.password_box, &mut self.ui_context);
+                    link_parent_child(page_root, &mut self.app.accounts.imap_box, &mut self.ui_context);
+                    link_parent_child(page_root, &mut self.app.accounts.smtp_box, &mut self.ui_context);
                 }
             }
 
             Page::Services => {
                 self.page_sec_containers.resize_with(3, clear_ui::widget::Container::new);
                 for i in 0..3 {
-                    link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[i], &mut self.ui_context);
+                    link_parent_child(page_root, &mut self.page_sec_containers[i], &mut self.ui_context);
                 }
                 link_parent_child(&mut self.page_sec_containers[0], &mut self.app.services.search_box, &mut self.ui_context);
                 link_parent_child(&mut self.page_sec_containers[0], &mut self.app.services.list_box.scroll_box, &mut self.ui_context);
@@ -819,18 +810,18 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                 link_parent_child(&mut self.page_sec_containers[2], &mut self.app.services.status_padding_spinbox, &mut self.ui_context);
             }
             Page::Hardware => {
-                link_parent_child(&mut self.page_root_container, &mut self.app.hardware.cpu_list_box.scroll_box, &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.app.hardware.cpu_gov_menu, &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.app.hardware.gpu_gov_menu, &mut self.ui_context);
+                link_parent_child(page_root, &mut self.app.hardware.cpu_list_box.scroll_box, &mut self.ui_context);
+                link_parent_child(page_root, &mut self.app.hardware.cpu_gov_menu, &mut self.ui_context);
+                link_parent_child(page_root, &mut self.app.hardware.gpu_gov_menu, &mut self.ui_context);
             }
             Page::Radios => {
-                link_parent_child(&mut self.page_root_container, &mut self.app.network.wifi_list_box.scroll_box, &mut self.ui_context);
+                link_parent_child(page_root, &mut self.app.network.wifi_list_box.scroll_box, &mut self.ui_context);
             }
             Page::Layout => {
                 self.page_sec_containers.resize_with(7, clear_ui::widget::Container::new);
                 
                 for i in 0..7 {
-                    link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[i], &mut self.ui_context);
+                    link_parent_child(page_root, &mut self.page_sec_containers[i], &mut self.ui_context);
                 }
                 
                 // Fullscreen (Section 0)
@@ -843,7 +834,6 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                 link_parent_child(&mut self.page_sec_containers[1], &mut self.app.layout.top_gap_spinbox, &mut self.ui_context);
                 
                 // Grid (Section 2)
-                link_parent_child(&mut self.page_sec_containers[2], &mut self.app.layout.spinboxes[2], &mut self.ui_context);
                 link_parent_child(&mut self.page_sec_containers[2], &mut self.app.layout.grid_gap_spinbox, &mut self.ui_context);
                 
                 // Floating (Section 3)
@@ -865,7 +855,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                 self.page_sec_containers.resize_with(5, clear_ui::widget::Container::new);
                 
                 for i in 0..5 {
-                    link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[i], &mut self.ui_context);
+                    link_parent_child(page_root, &mut self.page_sec_containers[i], &mut self.ui_context);
                 }
                 
                 // Section 0: Layout (parent of Plate, Sections, Grid Layout)
@@ -880,6 +870,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                 link_parent_child(&mut self.page_sec_containers[0], &mut self.app.interface.graph_snap_enabled_toggle, &mut self.ui_context);
                 link_parent_child(&mut self.page_sec_containers[0], &mut self.app.interface.graph_uniform_background_toggle, &mut self.ui_context);
                 link_parent_child(&mut self.page_sec_containers[0], &mut self.app.interface.graph_network_opacity_slider, &mut self.ui_context);
+                link_parent_child(&mut self.page_sec_containers[0], &mut self.app.interface.graph_gap_width_spinbox, &mut self.ui_context);
                 // (Sections child widgets)
                 link_parent_child(&mut self.page_sec_containers[0], &mut self.app.interface.section_padding_spinbox, &mut self.ui_context);
                 link_parent_child(&mut self.page_sec_containers[0], &mut self.app.interface.page_margin_spinbox, &mut self.ui_context);
@@ -908,6 +899,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                 link_parent_child(&mut self.page_sec_containers[2], &mut self.app.interface.tab_margin_spinbox_y, &mut self.ui_context);
                 link_parent_child(&mut self.page_sec_containers[2], &mut self.app.interface.tab_padding_spinbox_x, &mut self.ui_context);
                 link_parent_child(&mut self.page_sec_containers[2], &mut self.app.interface.tab_padding_spinbox_y, &mut self.ui_context);
+                link_parent_child(&mut self.page_sec_containers[2], &mut self.app.interface.menubar_opacity_spinbox, &mut self.ui_context);
                 
                 // (Toggles child widgets)
                 link_parent_child(&mut self.page_sec_containers[2], &mut self.app.interface.color_selectors[12], &mut self.ui_context);
@@ -958,11 +950,11 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
 
             Page::Input => {
                 self.page_sec_containers.resize_with(5, clear_ui::widget::Container::new);
-                link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[0], &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[1], &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[2], &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[3], &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[4], &mut self.ui_context);
+                link_parent_child(page_root, &mut self.page_sec_containers[0], &mut self.ui_context);
+                link_parent_child(page_root, &mut self.page_sec_containers[1], &mut self.ui_context);
+                link_parent_child(page_root, &mut self.page_sec_containers[2], &mut self.ui_context);
+                link_parent_child(page_root, &mut self.page_sec_containers[3], &mut self.ui_context);
+                link_parent_child(page_root, &mut self.page_sec_containers[4], &mut self.ui_context);
                 
                 link_parent_child(&mut self.page_sec_containers[0], &mut self.app.input.dwtp_toggle, &mut self.ui_context);
                 link_parent_child(&mut self.page_sec_containers[0], &mut self.app.input.trackpoint_accel_speed_spinbox, &mut self.ui_context);
@@ -986,8 +978,8 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
             }
             Page::Audio => {
                 self.page_sec_containers.resize_with(2, clear_ui::widget::Container::new);
-                link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[0], &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[1], &mut self.ui_context);
+                link_parent_child(page_root, &mut self.page_sec_containers[0], &mut self.ui_context);
+                link_parent_child(page_root, &mut self.page_sec_containers[1], &mut self.ui_context);
                 
                 for sb in &mut self.app.audio.sink_spinboxes {
                     link_parent_child(&mut self.page_sec_containers[0], sb, &mut self.ui_context);
@@ -1003,17 +995,38 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                 }
             }
             Page::Display => {
-                link_parent_child(&mut self.page_root_container, &mut self.app.display.brightness_spinbox, &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.app.display.brightness_slider, &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.app.display.screensaver_enable_toggle, &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.app.display.screensaver_lock_screen_toggle, &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.app.display.screensaver_timeout_spinbox, &mut self.ui_context);
-                link_parent_child(&mut self.page_root_container, &mut self.app.display.screensaver_style_menu, &mut self.ui_context);
+                self.page_sec_containers.resize_with(4, clear_ui::widget::Container::new);
+                link_parent_child(page_root, &mut self.page_sec_containers[0], &mut self.ui_context);
+                link_parent_child(page_root, &mut self.page_sec_containers[1], &mut self.ui_context);
+                link_parent_child(page_root, &mut self.page_sec_containers[2], &mut self.ui_context);
+                link_parent_child(page_root, &mut self.page_sec_containers[3], &mut self.ui_context);
+
+                // Section 0: Brightness
+                link_parent_child(&mut self.page_sec_containers[0], &mut self.app.display.brightness_spinbox, &mut self.ui_context);
+                link_parent_child(&mut self.page_sec_containers[0], &mut self.app.display.brightness_slider, &mut self.ui_context);
+
+                // Section 1: Night Light
+                link_parent_child(&mut self.page_sec_containers[1], &mut self.app.display.night_light_label, &mut self.ui_context);
+
+                // Section 2: Outputs
+                for out in &mut self.app.display.outputs {
+                    link_parent_child(&mut self.page_sec_containers[2], &mut out.name_label, &mut self.ui_context);
+                    link_parent_child(&mut self.page_sec_containers[2], &mut out.resolution_label, &mut self.ui_context);
+                    if let Some(ref mut scale_lbl) = out.scale_label {
+                        link_parent_child(&mut self.page_sec_containers[2], scale_lbl, &mut self.ui_context);
+                    }
+                }
+
+                // Section 3: Screensaver Settings
+                link_parent_child(&mut self.page_sec_containers[3], &mut self.app.display.screensaver_enable_toggle, &mut self.ui_context);
+                link_parent_child(&mut self.page_sec_containers[3], &mut self.app.display.screensaver_lock_screen_toggle, &mut self.ui_context);
+                link_parent_child(&mut self.page_sec_containers[3], &mut self.app.display.screensaver_timeout_spinbox, &mut self.ui_context);
+                link_parent_child(&mut self.page_sec_containers[3], &mut self.app.display.screensaver_style_menu, &mut self.ui_context);
             }
             Page::Packages => {
                 self.page_sec_containers.resize_with(2, clear_ui::widget::Container::new);
                 for i in 0..2 {
-                    link_parent_child(&mut self.page_root_container, &mut self.page_sec_containers[i], &mut self.ui_context);
+                    link_parent_child(page_root, &mut self.page_sec_containers[i], &mut self.ui_context);
                 }
                 link_parent_child(&mut self.page_sec_containers[0], &mut self.app.packages.search_box, &mut self.ui_context);
                 match self.app.packages.active_tab {
@@ -1028,8 +1041,67 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
             _ => {}
         }
 
+        self.sidebar_width = self.menubar.sidebar_w();
+        self.header_height = 0.0;
+        let s = 1.0f32;
+        let mut widgets = Vec::new();
+        let mut text_items = Vec::new();
+        let mut page_buttons = Vec::new();
+
+
+        clear_ui::widget::hover_animation::reset_frame_registration();
+        clear_ui::widget::popovers::clear();
+        clear_ui::widget::hover_animation::set_scroll_offset(self.scroll_y);
+        clear_ui::widget::hover_animation::set_cursor_pos(self.cursor_x / s, self.cursor_y / s);
+
+        let lcx = self.sidebar_width;
+        let lcy = self.header_height;
+        let lcw = sw / s - self.sidebar_width;
+        let lch = sh / s - self.header_height - self.status_height;
+
+        let page_idx = Page::ALL.iter().position(|&p| p == self.app.current_page).unwrap_or(0);
+        self.menubar.set_selected_page(page_idx);
+        self.switcher.set_active_index(Some(page_idx));
+
+        let mut menubar_pc = PageContent::new();
+        clear_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();
+        clear_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);
+
+        for pc_part in &[menubar_pc, switcher_pc] {
+            for (c, x, y, w, h) in &pc_part.rects {
+                widgets.push(AppWidget {
+                    x: *x * s, y: *y * s, w: *w * s, h: *h * s,
+                    color: *c, hover_color: *c,
+                    hovering: false,
+                });
+            }
+            for (t, size, x, y, tc, font_opt, bounds) in &pc_part.texts {
+                text_items.push(TextItem {
+                    buffer: make_text_buffer_with_font(
+                        &mut self.font_system,
+                        t,
+                        *size * s,
+                        font_opt.as_deref(),
+                        &self.sans_serif_family,
+                        &self.serif_family,
+                        &self.monospace_family,
+                    ),
+                    x: *x * s, y: *y * s,
+                    color: glyphon::Color::rgb(
+                        (tc[0] * 255.0) as u8, (tc[1] * 255.0) as u8, (tc[2] * 255.0) as u8,
+                    ),
+                    bounds: *bounds,
+                });
+            }
+        }
+
+        // Page content in LOGICAL coordinates, then scale to physical
+        let mut pc = self.render_page_content(lcx, lcy, lcw, lch);
+
         let mut popovers = Vec::new();
-        Self::collect_popover_rects(&self.page_root_container, &mut popovers, &self.ui_context);
+        Self::collect_popover_rects(&self.plates[page_idx], &mut popovers, &self.ui_context);
 
         if !popovers.is_empty() {
             pc.texts.retain(|(text, size, tx, ty, _, _, _)| {
@@ -1162,6 +1234,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
             let cy = clear_ui::widget::context_menu::y();
             let cw = clear_ui::widget::context_menu::w();
             let ch = clear_ui::widget::context_menu::h();
+
             
             // Border
             widgets.push(AppWidget {
@@ -1189,7 +1262,9 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
             // Texts
             for (idx, opt) in clear_ui::widget::context_menu::options().iter().enumerate() {
                 let iy = cy + idx as f32 * 24.0 + (24.0 - 12.0) / 2.0;
-                let text_color = if clear_ui::widget::context_menu::hovered_item() == Some(idx) {
+                let text_color = if idx == 0 {
+                    glyphon::Color::rgb(0x70, 0x70, 0x78)
+                } else if clear_ui::widget::context_menu::hovered_item() == Some(idx) {
                     glyphon::Color::rgb(0xff, 0xff, 0xff)
                 } else {
                     glyphon::Color::rgb(0xcc, 0xcc, 0xd4)
@@ -1220,7 +1295,8 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
         let cw = (cw - 2.0 * margin).max(1.0);
         let ch = (ch - 2.0 * margin).max(1.0);
         let mut layout = GridLayout::new(260.0, 20.0);
-        let root_focused = clear_ui::widget::focus::is_focused(&self.page_root_container);
+        let page_idx = Page::ALL.iter().position(|&p| p == self.app.current_page).unwrap_or(0);
+        let root_focused = clear_ui::widget::focus::is_focused(&self.plates[page_idx]);
         let sec_focused: Vec<bool> = self.page_sec_containers.iter()
             .map(|c| clear_ui::widget::focus::is_focused(c))
             .collect();
@@ -1247,7 +1323,9 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
             needs_redraw = true;
             self.needs_rebuild = true;
         }
-        if self.paginator.tick(dt, &mut self.ui_context) {
+        let menubar_changed = self.menubar.tick(dt, &mut self.ui_context);
+        let switcher_changed = self.switcher.tick(dt, &mut self.ui_context);
+        if menubar_changed || switcher_changed {
             needs_redraw = true;
             self.needs_rebuild = true;
         }
@@ -1564,7 +1642,11 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
         clear_ui::widget::hover_animation::set_cursor_pos(lx, ly_no_scroll);
         let mut changed = false;
         if lx_no_scroll < self.sidebar_width {
-            if self.paginator.cursor_moved(lx_no_scroll, ly_no_scroll, &mut self.ui_context) {
+            if self.menubar.cursor_moved(lx_no_scroll, ly_no_scroll, &mut self.ui_context) {
+                changed = true;
+            }
+        } else {
+            if self.switcher.cursor_moved(lx_no_scroll, ly_no_scroll, &mut self.ui_context) {
                 changed = true;
             }
         }
@@ -1622,6 +1704,9 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
             if self.app.interface.tab_margin_spinbox_x.cursor_moved(lx, ly, &mut self.ui_context) {
                 changed = true;
             }
+            if self.app.interface.menubar_opacity_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
+                changed = true;
+            }
             if self.app.interface.tab_margin_spinbox_y.cursor_moved(lx, ly, &mut self.ui_context) {
                 changed = true;
             }
@@ -1658,6 +1743,9 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
             if self.app.interface.graph_network_opacity_slider.cursor_moved(lx, ly, &mut self.ui_context) {
                 changed = true;
             }
+            if self.app.interface.graph_gap_width_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
+                changed = true;
+            }
             if self.app.interface.page_margin_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
                 changed = true;
             }
@@ -2037,17 +2125,24 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
         let lx_no_scroll = self.cursor_x / s;
         let ly_no_scroll = self.cursor_y / s;
 
+
+
         if clear_ui::widget::context_menu::is_visible() {
             if clear_ui::widget::context_menu::mouse_input(button, state, lx_no_scroll, ly_no_scroll) {
+                let mut actions = Vec::new();
+                self.propagate_widget_changes(&mut actions);
+                for action in actions {
+                    self.handle_action(&action);
+                }
                 self.needs_rebuild = true;
                 return true;
             }
         }
 
         if lx_no_scroll < self.sidebar_width {
-            if self.paginator.mouse_input(button, state, lx_no_scroll, ly_no_scroll, &mut self.ui_context) {
-                if self.paginator.take_click() {
-                    let idx = self.paginator.selected_page();
+            if self.menubar.mouse_input(button, state, lx_no_scroll, ly_no_scroll, &mut self.ui_context) {
+                use clear_ui::widget::MenuController;
+                if let Some((idx, _)) = self.menubar.menu_click() {
                     if idx < Page::ALL.len() {
                         clear_ui::widget::focus::clear_focus();
                         let new_page = Page::ALL[idx];
@@ -2060,6 +2155,11 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                 self.needs_rebuild = true;
                 return true;
             }
+        } else {
+            if self.switcher.mouse_input(button, state, lx_no_scroll, ly_no_scroll, &mut self.ui_context) {
+                self.needs_rebuild = true;
+                return true;
+            }
         }
 
         if button != clear_ui::widget::MouseButton::Left && button != clear_ui::widget::MouseButton::Right { return false; }
@@ -2147,6 +2247,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                         if cp.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
                     }
                     if self.app.interface.tab_margin_spinbox_x.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
+                    if self.app.interface.menubar_opacity_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
                     if self.app.interface.tab_margin_spinbox_y.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
                     if self.app.interface.tab_padding_spinbox_x.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
                     if self.app.interface.tab_padding_spinbox_y.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
@@ -2159,6 +2260,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                     if self.app.interface.graph_snap_enabled_toggle.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
                     if self.app.interface.graph_uniform_background_toggle.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
                     if self.app.interface.graph_network_opacity_slider.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
+                    if self.app.interface.graph_gap_width_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
                     if self.app.interface.page_margin_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
                     if self.app.interface.grid_min_col_width_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
                     if self.app.interface.spinbox_height_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
@@ -2397,6 +2499,12 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                     }));
                 }
             }
+            let sb = &mut self.app.interface.menubar_opacity_spinbox;
+            if !sb.hit_test(lx, ly, &self.ui_context) { sb.unfocus(); }
+            let old = sb.value;
+            if sb.mouse_input(button, state, lx, ly, &mut self.ui_context) && sb.value != old {
+                actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetMenubarOpacity(sb.value as f32 / 100.0)));
+            }
             let sb = &mut self.app.interface.tab_margin_spinbox_x;
             if !sb.hit_test(lx, ly, &self.ui_context) { sb.unfocus(); }
             let old = sb.value;
@@ -2468,6 +2576,12 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
             if toggle.take_click() {
                 actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphUniformBackground(toggle.toggled())));
             }
+            let sb = &mut self.app.interface.graph_gap_width_spinbox;
+            if !sb.hit_test(lx, ly, &self.ui_context) { sb.unfocus(); }
+            let old = sb.value;
+            if sb.mouse_input(button, state, lx, ly, &mut self.ui_context) && sb.value != old {
+                actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphGapWidth(sb.value as u16)));
+            }
             let slider = &mut self.app.interface.graph_network_opacity_slider;
             if button == clear_ui::widget::MouseButton::Left {
                 if state == clear_ui::widget::ElementState::Pressed {
@@ -3184,6 +3298,354 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
         true
     }
 
+    fn propagate_widget_changes(&mut self, actions: &mut Vec<AppAction>) {
+        match self.app.current_page {
+            Page::Layout => {
+                for (i, sb) in self.app.layout.spinboxes.iter_mut().enumerate() {
+                    if sb.take_change() {
+                        actions.push(AppAction::Layout(pages::layout::LayoutMessage::SetWidth(pages::layout::WidthParam::ALL[i], sb.value as u16)));
+                    }
+                }
+                if self.app.layout.cascade_offset_spinbox.take_change() {
+                    actions.push(AppAction::Layout(pages::layout::LayoutMessage::SetCascadeOffset(self.app.layout.cascade_offset_spinbox.value as u16)));
+                }
+                if self.app.layout.edge_gap_spinbox.take_change() {
+                    actions.push(AppAction::Layout(pages::layout::LayoutMessage::SetEdgeGap(self.app.layout.edge_gap_spinbox.value as u16)));
+                }
+                if self.app.layout.top_gap_spinbox.take_change() {
+                    actions.push(AppAction::Layout(pages::layout::LayoutMessage::SetTopGap(self.app.layout.top_gap_spinbox.value as u16)));
+                }
+                if self.app.layout.grid_gap_spinbox.take_change() {
+                    actions.push(AppAction::Layout(pages::layout::LayoutMessage::SetGridGap(self.app.layout.grid_gap_spinbox.value as u16)));
+                }
+                if self.app.layout.transition_duration_spinbox.take_change() {
+                    actions.push(AppAction::Layout(pages::layout::LayoutMessage::SetTransitionDuration(self.app.layout.transition_duration_spinbox.value as u16)));
+                }
+                if self.app.layout.status_height_spinbox.take_change() {
+                    actions.push(AppAction::Layout(pages::layout::LayoutMessage::SetStatusHeight(self.app.layout.status_height_spinbox.value as u16)));
+                }
+                for (idx, menu) in self.app.layout.tag_layout_menus.iter_mut().enumerate() {
+                    if menu.take_change() {
+                        actions.push(AppAction::Layout(pages::layout::LayoutMessage::SetTagLayout(idx + 1, menu.selected)));
+                    }
+                }
+                if self.app.layout.side_panel_behavior_menu.take_change() {
+                    actions.push(AppAction::Layout(pages::layout::LayoutMessage::SetSidePanelBehavior(self.app.layout.side_panel_behavior_menu.selected)));
+                }
+                if self.app.layout.side_panel_width_spinbox.take_change() {
+                    actions.push(AppAction::Layout(pages::layout::LayoutMessage::SetSidePanelWidth(self.app.layout.side_panel_width_spinbox.value as u16)));
+                }
+            }
+            Page::Interface => {
+                for (i, cp) in self.app.interface.color_selectors.iter_mut().enumerate() {
+                    if cp.take_change() {
+                        actions.push(AppAction::Interface(match i {
+                            0 => pages::interface::InterfaceMessage::SetPageLowColor(cp.color),
+                            1 => pages::interface::InterfaceMessage::SetHighColor(cp.color),
+                            2 => pages::interface::InterfaceMessage::SetVisualGuidesColor(cp.color),
+                            3 => pages::interface::InterfaceMessage::SetDisabledColor(cp.color),
+                            4 => pages::interface::InterfaceMessage::SetSeparatorColor(cp.color),
+                            5 => pages::interface::InterfaceMessage::SetSliderTrackColor(cp.color),
+                            6 => pages::interface::InterfaceMessage::SetColorBordersColor(cp.color),
+                            7 => pages::interface::InterfaceMessage::SetLowColor(cp.color),
+                            8 => pages::interface::InterfaceMessage::SetNormalColor(cp.color),
+                            9 => pages::interface::InterfaceMessage::SetPaginatorSidebarColor(cp.color),
+                            10 => pages::interface::InterfaceMessage::SetPrimaryHighlightColor(cp.color),
+                            11 => pages::interface::InterfaceMessage::SetPaginatorTabLabelColor(cp.color),
+                            12 => pages::interface::InterfaceMessage::SetToggleEnabledColor(cp.color),
+                            13 => pages::interface::InterfaceMessage::SetToggleDisabledColor(cp.color),
+                            _ => pages::interface::InterfaceMessage::SetLowColor(cp.color),
+                        }));
+                    }
+                }
+                if self.app.interface.menubar_opacity_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetMenubarOpacity(self.app.interface.menubar_opacity_spinbox.value as f32 / 100.0)));
+                }
+                if self.app.interface.tab_margin_spinbox_x.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTabMarginX(self.app.interface.tab_margin_spinbox_x.value as u16)));
+                }
+                if self.app.interface.tab_margin_spinbox_y.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTabMarginY(self.app.interface.tab_margin_spinbox_y.value as u16)));
+                }
+                if self.app.interface.tab_padding_spinbox_x.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTabPaddingX(self.app.interface.tab_padding_spinbox_x.value as u16)));
+                }
+                if self.app.interface.tab_padding_spinbox_y.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTabPaddingY(self.app.interface.tab_padding_spinbox_y.value as u16)));
+                }
+                if self.app.interface.section_padding_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSectionPadding(self.app.interface.section_padding_spinbox.value as u16)));
+                }
+                if self.app.interface.label_alignment_menu.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetNestedSectionLabelAlignment(self.app.interface.label_alignment_menu.selected)));
+                }
+                if self.app.interface.label_offset_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetNestedSectionLabelOffset(self.app.interface.label_offset_spinbox.value as i16)));
+                }
+                if self.app.interface.label_margin_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetLabelMargin(self.app.interface.label_margin_spinbox.value as u16)));
+                }
+                if self.app.interface.plate_padding_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPlatePadding(self.app.interface.plate_padding_spinbox.value as u16)));
+                }
+                if self.app.interface.graph_show_grid_toggle.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphShowGrid(self.app.interface.graph_show_grid_toggle.toggled())));
+                }
+                if self.app.interface.graph_snap_enabled_toggle.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphSnapEnabled(self.app.interface.graph_snap_enabled_toggle.toggled())));
+                }
+                if self.app.interface.graph_uniform_background_toggle.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphUniformBackground(self.app.interface.graph_uniform_background_toggle.toggled())));
+                }
+                if self.app.interface.graph_network_opacity_slider.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphOpacity(self.app.interface.graph_network_opacity_slider.value())));
+                }
+                if self.app.interface.page_margin_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPageMargin(self.app.interface.page_margin_spinbox.value as u16)));
+                }
+                if self.app.interface.grid_min_col_width_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGridMinColWidth(self.app.interface.grid_min_col_width_spinbox.value as u16)));
+                }
+                if self.app.interface.spinbox_height_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSpinboxHeight(self.app.interface.spinbox_height_spinbox.value as u16)));
+                }
+                if self.app.interface.toggle_height_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetToggleHeight(self.app.interface.toggle_height_spinbox.value as u16)));
+                }
+                if self.app.interface.color_selector_height_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetColorSelectorHeight(self.app.interface.color_selector_height_spinbox.value as u16)));
+                }
+                if self.app.interface.color_selector_preview_corner_radius_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetColorSelectorPreviewCornerRadius(self.app.interface.color_selector_preview_corner_radius_spinbox.value as u16)));
+                }
+                if self.app.interface.color_selector_preview_margin_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetColorSelectorPreviewMargin(self.app.interface.color_selector_preview_margin_spinbox.value as u16)));
+                }
+                if self.app.interface.textbox_height_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTextboxHeight(self.app.interface.textbox_height_spinbox.value as u16)));
+                }
+                if self.app.interface.slider_height_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSliderHeight(self.app.interface.slider_height_spinbox.value as u16)));
+                }
+                if self.app.interface.font_selector_height_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFontSelectorHeight(self.app.interface.font_selector_height_spinbox.value as u16)));
+                }
+                if self.app.interface.dropdown_height_spinbox.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetDropdownHeight(self.app.interface.dropdown_height_spinbox.value as u16)));
+                }
+                if self.app.interface.sans_box.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSans(self.app.interface.sans_box.text.clone())));
+                }
+                if self.app.interface.serif_box.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSerif(self.app.interface.serif_box.text.clone())));
+                }
+                if self.app.interface.mono_box.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetMono(self.app.interface.mono_box.text.clone())));
+                }
+                if self.app.interface.borders_menu.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBordersMenu(self.app.interface.borders_menu.selected)));
+                }
+                if self.app.interface.borders_box.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBorders(self.app.interface.borders_box.text.clone())));
+                }
+                if self.app.interface.status_menu.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatusMenu(self.app.interface.status_menu.selected)));
+                }
+                if self.app.interface.status_box.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatus(self.app.interface.status_box.text.clone())));
+                }
+                if self.app.interface.fuzzel_menu.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFuzzelMenu(self.app.interface.fuzzel_menu.selected)));
+                }
+                if self.app.interface.fuzzel_box.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFuzzel(self.app.interface.fuzzel_box.text.clone())));
+                }
+                if self.app.interface.terminal_menu.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTerminalMenu(self.app.interface.terminal_menu.selected)));
+                }
+                if self.app.interface.terminal_box.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTerminal(self.app.interface.terminal_box.text.clone())));
+                }
+                if self.app.interface.color_selector_font_selector.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetColorSelectorFont(self.app.interface.color_selector_font_selector.font_family.clone())));
+                }
+                if self.app.interface.menubar_font_selector.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetMenubarFont(self.app.interface.menubar_font_selector.font_family.clone())));
+                }
+                if self.app.interface.section_label_font_selector.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSectionLabelFont(self.app.interface.section_label_font_selector.font_family.clone())));
+                }
+                if self.app.interface.nested_section_label_font_selector.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetNestedSectionLabelFont(self.app.interface.nested_section_label_font_selector.font_family.clone())));
+                }
+                if self.app.interface.borders_size_box.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBordersSize(self.app.interface.borders_size_box.value)));
+                }
+                if self.app.interface.status_size_box.take_change() {
+                    actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatusSize(self.app.interface.status_size_box.value)));
+                }
+            }
+            Page::Input => {
+                if self.app.input.rate_spinbox.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ApplyRepeat));
+                }
+                if self.app.input.delay_spinbox.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ApplyRepeat));
+                }
+                if self.app.input.scroll_friction_spinbox.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ApplyScrollFriction));
+                }
+                if self.app.input.scroll_speed_spinbox.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ApplyScrollSpeed));
+                }
+                if self.app.input.pointer_friction_spinbox.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ApplyPointerFriction));
+                }
+                if self.app.input.trackpad_friction_spinbox.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ApplyTrackpadFriction));
+                }
+                if self.app.input.trackpoint_accel_speed_spinbox.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ApplyTrackpointAccelSpeed));
+                }
+                if self.app.input.cursor_size_spinbox.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ApplyCursorSize));
+                }
+                if self.app.input.tap_toggle.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ToggleTapToClick));
+                }
+                if self.app.input.scroll_toggle.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ToggleInertialScroll));
+                }
+                if self.app.input.natural_toggle.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ToggleNaturalScroll));
+                }
+                if self.app.input.pointer_toggle.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ToggleInertialPointer));
+                }
+                if self.app.input.trackpad_toggle.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ToggleInertialTrackpad));
+                }
+                if self.app.input.dwtp_toggle.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ToggleDwtp));
+                }
+                if self.app.input.trackpoint_accel_profile_menu.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ApplyTrackpointAccelProfile(self.app.input.trackpoint_accel_profile_menu.selected)));
+                }
+                if self.app.input.cursor_theme_menu.take_change() {
+                    actions.push(AppAction::Input(pages::input::InputMessage::ApplyCursorTheme(self.app.input.cursor_theme_menu.selected)));
+                }
+            }
+            Page::Services => {
+                if self.app.services.notifications_duration_spinbox.take_change() {
+                    actions.push(AppAction::Services(pages::services::ServicesMessage::SetNotificationsDuration(self.app.services.notifications_duration_spinbox.value)));
+                }
+                if self.app.services.status_padding_spinbox.take_change() {
+                    actions.push(AppAction::Services(pages::services::ServicesMessage::StatusSetPadding(self.app.services.status_padding_spinbox.value as u16)));
+                }
+                if self.app.services.notifications_enable_toggle.take_change() {
+                    actions.push(AppAction::Services(pages::services::ServicesMessage::ToggleNotificationsEnable));
+                }
+                if self.app.services.notifications_bell_toggle.take_change() {
+                    actions.push(AppAction::Services(pages::services::ServicesMessage::ToggleNotificationsBell));
+                }
+                if self.app.services.notifications_opacity_slider.take_change() {
+                    let val = self.app.services.notifications_opacity_slider.value() as f32 / 100.0;
+                    actions.push(AppAction::Services(pages::services::ServicesMessage::SetNotificationsOpacity(val)));
+                }
+                if self.app.services.status_separators_toggle.take_change() {
+                    actions.push(AppAction::Services(pages::services::ServicesMessage::StatusToggleSeparators));
+                }
+                if self.app.services.status_underline_toggle.take_change() {
+                    actions.push(AppAction::Services(pages::services::ServicesMessage::StatusToggleUnderline));
+                }
+            }
+            Page::Display => {
+                if self.app.display.screensaver_enable_toggle.take_change() {
+                    actions.push(AppAction::Display(pages::display::DisplayMessage::ToggleScreensaverEnable));
+                }
+                if self.app.display.screensaver_lock_screen_toggle.take_change() {
+                    actions.push(AppAction::Display(pages::display::DisplayMessage::ToggleScreensaverLockScreen));
+                }
+                if self.app.display.screensaver_style_menu.take_change() {
+                    actions.push(AppAction::Display(pages::display::DisplayMessage::SetScreensaverStyle(self.app.display.screensaver_style_menu.selected)));
+                }
+                if self.app.display.brightness_spinbox.take_change() {
+                    actions.push(AppAction::Display(pages::display::DisplayMessage::BrightnessSet(self.app.display.brightness_spinbox.value as u32)));
+                }
+                if self.app.display.brightness_slider.take_change() {
+                    actions.push(AppAction::Display(pages::display::DisplayMessage::BrightnessSet((self.app.display.brightness_slider.value() * 100.0).round() as u32)));
+                }
+                if self.app.display.screensaver_timeout_spinbox.take_change() {
+                    actions.push(AppAction::Display(pages::display::DisplayMessage::SetScreensaverTimeout(self.app.display.screensaver_timeout_spinbox.value)));
+                }
+            }
+            Page::Hardware => {
+                if self.app.hardware.cpu_gov_menu.take_change() {
+                    if self.app.hardware.cpu_gov_menu.selected == 0 {
+                        actions.push(AppAction::Hardware(pages::hardware::HardwareMessage::SetCpuPerformance));
+                    } else {
+                        actions.push(AppAction::Hardware(pages::hardware::HardwareMessage::SetCpuPowersave));
+                    }
+                }
+                if self.app.hardware.gpu_gov_menu.take_change() {
+                    if self.app.hardware.gpu_gov_menu.selected == 0 {
+                        actions.push(AppAction::Hardware(pages::hardware::HardwareMessage::SetGpuDefault));
+                    } else {
+                        actions.push(AppAction::Hardware(pages::hardware::HardwareMessage::SetGpuPowersave));
+                    }
+                }
+            }
+            Page::Accounts => {
+                if self.app.accounts.email_box.take_change() {
+                    let email_val = self.app.accounts.email_box.text.trim().to_lowercase();
+                    if email_val.ends_with("@gmail.com") {
+                        self.app.accounts.imap_box.text = "imap.gmail.com:993".to_string();
+                        self.app.accounts.imap_box.edit_buffer = "imap.gmail.com:993".to_string();
+                        self.app.accounts.smtp_box.text = "smtp.gmail.com:465".to_string();
+                        self.app.accounts.smtp_box.edit_buffer = "smtp.gmail.com:465".to_string();
+                    } else if email_val.ends_with("@icloud.com") {
+                        self.app.accounts.imap_box.text = "imap.mail.me.com:993".to_string();
+                        self.app.accounts.imap_box.edit_buffer = "imap.mail.me.com:993".to_string();
+                        self.app.accounts.smtp_box.text = "smtp.mail.me.com:587".to_string();
+                        self.app.accounts.smtp_box.edit_buffer = "smtp.mail.me.com:587".to_string();
+                    } else if email_val.ends_with("@outlook.com") || email_val.ends_with("@hotmail.com") {
+                        self.app.accounts.imap_box.text = "outlook.office365.com:993".to_string();
+                        self.app.accounts.imap_box.edit_buffer = "outlook.office365.com:993".to_string();
+                        self.app.accounts.smtp_box.text = "smtp.office365.com:587".to_string();
+                        self.app.accounts.smtp_box.edit_buffer = "smtp.office365.com:587".to_string();
+                    }
+                }
+            }
+            Page::Audio => {
+                for (i, sb) in self.app.audio.sink_spinboxes.iter_mut().enumerate() {
+                    if sb.take_change() {
+                        let id = self.app.audio.sinks[i].id;
+                        actions.push(AppAction::Audio(pages::audio::AudioMessage::SinkVolume(id, sb.value as f32 / 100.0)));
+                    }
+                }
+                for (i, sb) in self.app.audio.source_spinboxes.iter_mut().enumerate() {
+                    if sb.take_change() {
+                        let id = self.app.audio.sources[i].id;
+                        actions.push(AppAction::Audio(pages::audio::AudioMessage::SourceVolume(id, sb.value as f32 / 100.0)));
+                    }
+                }
+                for (i, slider) in self.app.audio.sink_sliders.iter_mut().enumerate() {
+                    if slider.take_change() {
+                        let id = self.app.audio.sinks[i].id;
+                        actions.push(AppAction::Audio(pages::audio::AudioMessage::SinkVolume(id, slider.value() as f32 / 100.0)));
+                    }
+                }
+                for (i, slider) in self.app.audio.source_sliders.iter_mut().enumerate() {
+                    if slider.take_change() {
+                        let id = self.app.audio.sources[i].id;
+                        actions.push(AppAction::Audio(pages::audio::AudioMessage::SourceVolume(id, slider.value() as f32 / 100.0)));
+                    }
+                }
+            }
+            _ => {}
+        }
+    }
+
     fn handle_mouse_wheel_internal(&mut self, delta: &clear_ui::widget::MouseScrollDelta, px: f32, py: f32) -> bool {
         if let Ok(mut file) = std::fs::OpenOptions::new().create(true).append(true).open("/tmp/clear-scroll-debug.txt") {
             use std::io::Write;
@@ -3357,7 +3819,7 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
         } else {
             let lx = px / s;
             let ly = py / s;
-            if self.paginator.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
+            if self.menubar.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
                 self.needs_rebuild = true;
                 return true;
             }
@@ -3366,18 +3828,12 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
     }
 
     fn get_page_root_widget(&mut self) -> Option<*mut (dyn clear_ui::widget::Element + 'static)> {
-        match self.app.current_page {
-            Page::Services | Page::Hardware | Page::Radios |
-            Page::Layout | Page::Interface | Page::Input |
-            Page::Audio | Page::Display => {
-                let ptr = &mut self.page_root_container as &mut dyn clear_ui::widget::Element as *mut dyn clear_ui::widget::Element;
-                let static_ptr = unsafe {
-                    std::mem::transmute::<*mut dyn clear_ui::widget::Element, *mut (dyn clear_ui::widget::Element + 'static)>(ptr)
-                };
-                Some(static_ptr)
-            }
-            _ => None,
-        }
+        let page_idx = Page::ALL.iter().position(|&p| p == self.app.current_page).unwrap_or(0);
+        let ptr = &mut self.plates[page_idx] as &mut dyn clear_ui::widget::Element as *mut dyn clear_ui::widget::Element;
+        let static_ptr = unsafe {
+            std::mem::transmute::<*mut dyn clear_ui::widget::Element, *mut (dyn clear_ui::widget::Element + 'static)>(ptr)
+        };
+        Some(static_ptr)
     }
 
     fn handle_key_input_internal(&mut self, event: &clear_ui::widget::KeyEvent) -> bool {
@@ -3548,6 +4004,26 @@ fn collect_popover_rects(w: &dyn clear_ui::widget::Element, popovers: &mut Vec<(
                 self.needs_rebuild = true;
                 return true;
             }
+            let sb = &mut self.app.interface.menubar_opacity_spinbox;
+            let old = sb.value;
+            if sb.keyboard_input(event, &mut self.ui_context) {
+                let new_val = sb.value;
+                if new_val != old {
+                    self.handle_action(&AppAction::Interface(pages::interface::InterfaceMessage::SetMenubarOpacity(new_val as f32 / 100.0)));
+                }
+                self.needs_rebuild = true;
+                return true;
+            }
+            let sb = &mut self.app.interface.graph_gap_width_spinbox;
+            let old = sb.value;
+            if sb.keyboard_input(event, &mut self.ui_context) {
+                let new_val = sb.value;
+                if new_val != old {
+                    self.handle_action(&AppAction::Interface(pages::interface::InterfaceMessage::SetGraphGapWidth(new_val as u16)));
+                }
+                self.needs_rebuild = true;
+                return true;
+            }
             let sb = &mut self.app.interface.tab_margin_spinbox_x;
             let old = sb.value;
             if sb.keyboard_input(event, &mut self.ui_context) {
diff --git a/src/pages/interface.rs b/src/pages/interface.rs
index 0aadfd0..af82da4 100644
--- a/src/pages/interface.rs
+++ b/src/pages/interface.rs
@@ -32,6 +32,8 @@ pub struct InterfaceState {
     pub toggle_enabled_color: [u8; 3],
     pub toggle_disabled_color: [u8; 3],
     pub color_selectors: Vec<ColorSelector>,
+    pub menubar_opacity: f32,
+    pub menubar_opacity_spinbox: Spinbox,
     pub paginator_tab_margin_x: u16,
     pub paginator_tab_margin_y: u16,
     pub tab_margin_spinbox_x: Spinbox,
@@ -116,6 +118,8 @@ pub struct InterfaceState {
     pub graph_uniform_background_toggle: Toggle,
     pub graph_network_opacity: f32,
     pub graph_network_opacity_slider: Slider,
+    pub graph_gap_width: u16,
+    pub graph_gap_width_spinbox: Spinbox,
 }
 
 impl Default for InterfaceState {
@@ -145,7 +149,7 @@ impl Default for InterfaceState {
                 ColorSelector::new([124, 124, 137]).with_label("Borders"), // 6: Controls - Borders
                 ColorSelector::new([0x0a, 0x1a, 0x0e]).with_label("Low Color"), // 7: Layout - Low Color
                 ColorSelector::new([0xcc, 0xcc, 0xd8]).with_label("Normal"), // 8: Status - Normal
-                ColorSelector::new([90, 90, 101]).with_label("Paginator Sidebar"), // 9: Controls - Paginator Sidebar
+                ColorSelector::new([90, 90, 101]).with_label("Background"), // 9: Controls - Paginator Sidebar (now Background)
                 ColorSelector::new([255, 255, 255]).with_label("Primary Highlight"), // 10: Controls - Primary Highlight
                 ColorSelector::new([230, 230, 242]).with_label("Paginator Tab Label"), // 11: Controls - Paginator Tab Label
                 ColorSelector::new([104, 217, 165]).with_label("Enabled"), // 12: Toggles - Enabled
@@ -236,6 +240,10 @@ impl Default for InterfaceState {
             graph_uniform_background_toggle: Toggle::new().with_label("Uniform Background"),
             graph_network_opacity: 0.95,
             graph_network_opacity_slider: Slider::new().with_label("Network Opacity").with_value(0.95),
+            graph_gap_width: 35,
+            graph_gap_width_spinbox: Spinbox::new(35, 0, 100, 1).with_label("Gap Width").with_unit("px"),
+            menubar_opacity: 0.9,
+            menubar_opacity_spinbox: Spinbox::new(90, 0, 100, 5).with_label("Opacity").with_unit("%"),
         }
     }
 }
@@ -284,6 +292,8 @@ pub enum InterfaceMessage {
     SetGraphSnapEnabled(bool),
     SetGraphUniformBackground(bool),
     SetGraphOpacity(f32),
+    SetGraphGapWidth(u16),
+    SetMenubarOpacity(f32),
     PickLowColor,
     PickHighColor,
     PickDisabledColor,
@@ -386,6 +396,8 @@ pub fn read_interface_config() -> InterfaceState {
     let graph_snap_enabled = parse_bool_from(&content, "graph_snap_enabled", true);
     let graph_uniform_background = parse_bool_from(&content, "graph_uniform_background", false);
     let graph_network_opacity = parse_f32_from(&content, "graph_network_opacity", 0.95);
+    let graph_gap_width = parse_u16_from(&content, "graph_gap_width", 35);
+    let opacity = parse_transparency_opacity(&content);
     
     InterfaceState {
         low_color: bg,
@@ -412,7 +424,7 @@ pub fn read_interface_config() -> InterfaceState {
             ColorSelector::new(color_borders).with_label("Borders").with_font_family(&color_selector_font), // 6: Controls - Borders
             ColorSelector::new(bg).with_label("Low Color").with_font_family(&color_selector_font), // 7: Layout - Low Color
             ColorSelector::new(normal).with_label("Normal").with_font_family(&color_selector_font), // 8: Status - Normal
-            ColorSelector::new(paginator_sidebar).with_label("Paginator Sidebar").with_font_family(&color_selector_font), // 9: Controls - Paginator Sidebar
+            ColorSelector::new(paginator_sidebar).with_label("Background").with_font_family(&color_selector_font), // 9: Controls - Paginator Sidebar (now Background)
             ColorSelector::new(primary_highlight).with_label("Primary Highlight").with_font_family(&color_selector_font), // 10: Controls - Primary Highlight
             ColorSelector::new(paginator_tab_label).with_label("Paginator Tab Label").with_font_family(&color_selector_font), // 11: Controls - Paginator Tab Label
             ColorSelector::new(toggle_enabled).with_label("Enabled").with_font_family(&color_selector_font), // 12: Toggles - Enabled
@@ -503,6 +515,10 @@ pub fn read_interface_config() -> InterfaceState {
         graph_uniform_background_toggle: Toggle::new().with_label("Uniform Background"),
         graph_network_opacity,
         graph_network_opacity_slider: Slider::new().with_label("Network Opacity").with_value(graph_network_opacity),
+        graph_gap_width,
+        graph_gap_width_spinbox: Spinbox::new(graph_gap_width as i32, 0, 100, 1).with_label("Gap Width").with_unit("px"),
+        menubar_opacity: opacity,
+        menubar_opacity_spinbox: Spinbox::new((opacity * 100.0).round() as i32, 0, 100, 5).with_label("Opacity").with_unit("%"),
     }
 }
 
@@ -1337,6 +1353,9 @@ pub fn view(state: &mut InterfaceState, cx: f32, cy: f32, cw: f32, ch: f32, sec_
             state.graph_network_opacity_slider.set_value(state.graph_network_opacity);
             subsec.widget_full(&mut state.graph_network_opacity_slider, state.slider_height as f32, ctx);
             subsec.spacing(8.0);
+            state.graph_gap_width_spinbox.value = state.graph_gap_width as i32;
+            subsec.widget_full(&mut state.graph_gap_width_spinbox, 44.0, ctx);
+            subsec.spacing(8.0);
         });
         sec.spacing(8.0);
     });
@@ -1398,6 +1417,9 @@ pub fn view(state: &mut InterfaceState, cx: f32, cy: f32, cw: f32, ch: f32, sec_
             state.menubar_font_selector.font_family = state.menubar_font.clone();
             subsec.widget_full(&mut state.menubar_font_selector, 44.0, ctx);
             subsec.spacing(8.0);
+            state.menubar_opacity_spinbox.value = (state.menubar_opacity * 100.0).round() as i32;
+            subsec.widget_full(&mut state.menubar_opacity_spinbox, 44.0, ctx);
+            subsec.spacing(8.0);
         });
         sec.spacing(12.0);
 
@@ -1782,6 +1804,18 @@ pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
             write_config_value("graph_network_opacity", &format!("{:.2}", opacity));
             cce_graph_reload();
         }
+        InterfaceMessage::SetGraphGapWidth(gap) => {
+            state.graph_gap_width = gap;
+            state.graph_gap_width_spinbox.value = gap as i32;
+            write_config_value("graph_gap_width", &gap.to_string());
+            cce_graph_reload();
+        }
+        InterfaceMessage::SetMenubarOpacity(opacity) => {
+            state.menubar_opacity = opacity;
+            state.menubar_opacity_spinbox.value = (opacity * 100.0).round() as i32;
+            write_transparency_config_value("opacity", &format!("{:.2}", opacity));
+            send_ipc_command("reload");
+        }
         InterfaceMessage::PickLowColor | InterfaceMessage::PickHighColor | InterfaceMessage::PickDisabledColor | InterfaceMessage::PickSeparatorColor | InterfaceMessage::PickVisualGuides | InterfaceMessage::PickSliderTrackColor | InterfaceMessage::PickPageLowColor | InterfaceMessage::PickColorBordersColor | InterfaceMessage::PickNormalColor | InterfaceMessage::PickPaginatorSidebarColor | InterfaceMessage::PickPrimaryHighlightColor | InterfaceMessage::PickPaginatorTabLabelColor | InterfaceMessage::PickToggleEnabledColor | InterfaceMessage::PickToggleDisabledColor => {}
         InterfaceMessage::Refreshed(new) => {
             let was_mx_hovered = state.tab_margin_spinbox_x.hovered();
@@ -1798,10 +1832,12 @@ pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
             let was_gse_hovered = state.graph_snap_enabled_toggle.hovered();
             let was_gub_hovered = state.graph_uniform_background_toggle.hovered();
             let was_gno_hovered = state.graph_network_opacity_slider.hovered();
+            let was_ggw_hovered = state.graph_gap_width_spinbox.hovered();
             let was_csh_hovered = state.color_selector_height_spinbox.hovered();
             let was_tbh_hovered = state.textbox_height_spinbox.hovered();
             let was_fsh_hovered = state.font_selector_height_spinbox.hovered();
             let was_lm_hovered = state.label_margin_spinbox.hovered();
+            let was_mo_hovered = state.menubar_opacity_spinbox.hovered();
             // Preserve typeface fields
             let typeface_loaded = state.typeface_loaded;
             let sans_serif = state.sans_serif.clone();
@@ -1846,10 +1882,12 @@ pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
             state.graph_snap_enabled_toggle.set_hovered(was_gse_hovered);
             state.graph_uniform_background_toggle.set_hovered(was_gub_hovered);
             state.graph_network_opacity_slider.set_hovered(was_gno_hovered);
+            state.graph_gap_width_spinbox.set_hovered(was_ggw_hovered);
             state.color_selector_height_spinbox.set_hovered(was_csh_hovered);
             state.textbox_height_spinbox.set_hovered(was_tbh_hovered);
             state.font_selector_height_spinbox.set_hovered(was_fsh_hovered);
             state.label_margin_spinbox.set_hovered(was_lm_hovered);
+            state.menubar_opacity_spinbox.set_hovered(was_mo_hovered);
 
             if typeface_loaded {
                 state.typeface_loaded = typeface_loaded;
@@ -2184,6 +2222,94 @@ pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
     }
 }
 
+fn parse_transparency_opacity(content: &str) -> f32 {
+    let mut in_section = false;
+    for line in content.lines() {
+        let trimmed = line.trim();
+        if trimmed == "[transparency]" {
+            in_section = true;
+            continue;
+        }
+        if trimmed.starts_with('[') && in_section {
+            break;
+        }
+        if in_section && trimmed.starts_with("opacity") {
+            if let Some(val) = trimmed.split('=').nth(1) {
+                if let Ok(o) = val.trim().parse::<f32>() {
+                    return o.clamp(0.0, 1.0);
+                }
+            }
+        }
+    }
+    0.9 // default to 0.9
+}
+
+fn write_transparency_config_value(key: &str, value: &str) {
+    let content = fs::read_to_string(CONFIG_PATH).unwrap_or_default();
+    let new_line = format!("{} = {}", key, value);
+
+    let mut found = false;
+    let mut updated_lines = Vec::new();
+    let mut in_section = false;
+
+    for line in content.lines() {
+        let trimmed = line.trim();
+        if trimmed == "[transparency]" {
+            in_section = true;
+            updated_lines.push(line.to_string());
+            continue;
+        }
+        if trimmed.starts_with('[') && in_section {
+            in_section = false;
+        }
+        if in_section && trimmed.starts_with(key) {
+            found = true;
+            updated_lines.push(new_line.clone());
+        } else {
+            updated_lines.push(line.to_string());
+        }
+    }
+
+    let mut updated = updated_lines.join("\n");
+
+    if !found {
+        let mut result = String::new();
+        let has_section = content.lines().any(|l| l.trim() == "[transparency]");
+        if has_section {
+            let mut in_section = false;
+            let mut inserted = false;
+            for line in updated.lines() {
+                if line.trim() == "[transparency]" {
+                    in_section = true;
+                    result.push_str(line);
+                    result.push('\n');
+                    continue;
+                }
+                if line.trim().starts_with('[') && in_section {
+                    if !inserted {
+                        result.push_str(&new_line);
+                        result.push('\n');
+                        inserted = true;
+                    }
+                    in_section = false;
+                }
+                result.push_str(line);
+                result.push('\n');
+            }
+            if !inserted {
+                result.push_str(&new_line);
+                result.push('\n');
+            }
+            updated = result;
+        } else {
+            updated.push_str("\n[transparency]\n");
+            updated.push_str(&new_line);
+            updated.push_str("\n");
+        }
+    }
+    let _ = fs::write(CONFIG_PATH, updated);
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -2874,6 +3000,25 @@ mod tests {
         // Clean up
         let _ = fs::remove_file(path_str);
     }
+
+    #[test]
+    fn test_read_write_menubar_opacity() {
+        let dir = std::env::temp_dir();
+        let path = dir.join("test_menubar_opacity_config.toml");
+        let path_str = path.to_str().unwrap();
+
+        // 1. Initial configuration
+        let initial_content = "[transparency]\nopacity = 0.85\n";
+        fs::write(path_str, initial_content).unwrap();
+
+        // 2. Parse when present (should return 0.85)
+        let content = fs::read_to_string(path_str).unwrap();
+        let val = parse_transparency_opacity(&content);
+        assert_eq!(val, 0.85);
+
+        // Clean up
+        let _ = fs::remove_file(path_str);
+    }
 }