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

commitf3c5c6464dcc09bd39ecbdb67758cf2955cd7ed2
parentb607187398
authorLucas Galante <[email protected]>
date2026-06-26 12:50
Add Background Opacity and Background Blur sliders to Status section

 src/input_handler.rs     |  65 ++++++++++++++++++++++++++++
 src/main.rs              |  16 +++----
 src/pages/system_info.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/renderer.rs          |   2 +
 4 files changed, 180 insertions(+), 13 deletions(-)

diff --git a/src/input_handler.rs b/src/input_handler.rs
index 9c5e802..2a3b2ad 100644
--- a/src/input_handler.rs
+++ b/src/input_handler.rs
@@ -87,6 +87,23 @@ impl SystemInterface {
                 }
             }
         }
+        if self.app.current_page == Page::System {
+            if self.status_box_opacity_dragging {
+                drag_handled = true;
+                if self.app.system_info.status_box_opacity_slider.drag_update(lx, ly) {
+                    changed = true;
+                    let val = self.app.system_info.status_box_opacity_slider.value();
+                    self.handle_action(&AppAction::SystemInfo(pages::system_info::SystemMessage::StatusSetBoxOpacity(val)));
+                }
+            } else if self.status_box_blur_dragging {
+                drag_handled = true;
+                if self.app.system_info.status_box_blur_slider.drag_update(lx, ly) {
+                    changed = true;
+                    let val = self.app.system_info.status_box_blur_slider.value();
+                    self.handle_action(&AppAction::SystemInfo(pages::system_info::SystemMessage::StatusSetBoxBlur(val)));
+                }
+            }
+        }
 
         if !drag_handled {
             let event = cce_ui::widget::Event::PointerMove { x: lx, y: ly, local_x: lx, local_y: ly };
@@ -218,6 +235,18 @@ impl SystemInterface {
                     self.needs_rebuild = true;
                 }
             }
+            if self.app.current_page == Page::System {
+                if self.status_box_opacity_dragging {
+                    self.app.system_info.status_box_opacity_slider.drag_end();
+                    self.status_box_opacity_dragging = false;
+                    self.needs_rebuild = true;
+                }
+                if self.status_box_blur_dragging {
+                    self.app.system_info.status_box_blur_slider.drag_end();
+                    self.status_box_blur_dragging = false;
+                    self.needs_rebuild = true;
+                }
+            }
         }
 
         let lx = self.cursor_x / s;
@@ -227,6 +256,36 @@ impl SystemInterface {
             self.ui_context.propagate_event(&event, root);
         }
 
+        if state == cce_ui::widget::ElementState::Pressed {
+            if self.app.current_page == Page::Display {
+                if self.app.display.brightness_slider.is_dragging() {
+                    self.display_brightness_dragging = true;
+                }
+            }
+            if self.app.current_page == Page::Audio {
+                for (i, s) in self.app.audio.sink_sliders.iter().enumerate() {
+                    if s.is_dragging() {
+                        self.audio_sink_dragging = Some(i);
+                        break;
+                    }
+                }
+                for (i, s) in self.app.audio.source_sliders.iter().enumerate() {
+                    if s.is_dragging() {
+                        self.audio_source_dragging = Some(i);
+                        break;
+                    }
+                }
+            }
+            if self.app.current_page == Page::System {
+                if self.app.system_info.status_box_opacity_slider.is_dragging() {
+                    self.status_box_opacity_dragging = true;
+                }
+                if self.app.system_info.status_box_blur_slider.is_dragging() {
+                    self.status_box_blur_dragging = true;
+                }
+            }
+        }
+
         self.propagate_widget_changes(&mut actions);
 
         for a in &actions {
@@ -629,6 +688,12 @@ impl SystemInterface {
                 if self.app.system_info.status_padding_spinbox.take_change() {
                     actions.push(AppAction::SystemInfo(pages::system_info::SystemMessage::StatusSetPadding(self.app.system_info.status_padding_spinbox.value as u16)));
                 }
+                if self.app.system_info.status_box_opacity_slider.take_change() {
+                    actions.push(AppAction::SystemInfo(pages::system_info::SystemMessage::StatusSetBoxOpacity(self.app.system_info.status_box_opacity_slider.value())));
+                }
+                if self.app.system_info.status_box_blur_slider.take_change() {
+                    actions.push(AppAction::SystemInfo(pages::system_info::SystemMessage::StatusSetBoxBlur(self.app.system_info.status_box_blur_slider.value())));
+                }
             }
             Page::Accounts => {
                 if self.app.accounts.adding_new && self.app.accounts.email_box.take_change() {
diff --git a/src/main.rs b/src/main.rs
index 36c5c28..a9da7df 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -204,6 +204,8 @@ struct SystemInterface {
     audio_sink_dragging: Option<usize>,
     audio_source_dragging: Option<usize>,
     display_brightness_dragging: bool,
+    status_box_opacity_dragging: bool,
+    status_box_blur_dragging: bool,
     page_sec_containers: Vec<cce_ui::widget::SectionContainer>,
     root_window: cce_ui::widget::Backplate,
     menubar: cce_ui::widget::Paginator,
@@ -309,6 +311,8 @@ impl cce_ui::engine::Application for SystemInterface {
             audio_sink_dragging: None,
             audio_source_dragging: None,
             display_brightness_dragging: false,
+            status_box_opacity_dragging: false,
+            status_box_blur_dragging: false,
             page_sec_containers: Vec::new(),
             root_window: cce_ui::widget::Backplate::new(0.0, 0.0, 820.0, 680.0)
                 .with_background([
@@ -516,20 +520,10 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
         if hover_animation::tick(dt) {
             needs_redraw = true;
         }
-        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 {
+        if self.ui_context.tick(dt) {
             needs_redraw = true;
             self.needs_rebuild = true;
         }
-        if let Some(root_ptr) = self.get_page_root_widget() {
-            unsafe {
-                if (*root_ptr).tick(dt, &mut self.ui_context) {
-                    needs_redraw = true;
-                    self.needs_rebuild = true;
-                }
-            }
-        }
 
         // Asynchronously check color selector changes (e.g. Zenity process exit)
         let mut color_changed = false;
diff --git a/src/pages/system_info.rs b/src/pages/system_info.rs
index 8e1488a..f30a205 100644
--- a/src/pages/system_info.rs
+++ b/src/pages/system_info.rs
@@ -1,7 +1,7 @@
 use crate::app::{AppAction, PageContent, SectionContextExt};
 use cce_ui::layout::{render_widget, PageLayoutBuilder, LayoutStrategy};
-use cce_ui::widget::{Label, Dropdown, InfoBox, Toggle, Spinbox, Element};
-use crate::pages::interface::parse_u16_from;
+use cce_ui::widget::{Label, Dropdown, InfoBox, Toggle, Spinbox, Slider, Element};
+use crate::pages::interface::{parse_u16_from, parse_f32_from};
 use crate::config_manager::CONFIG_PATH;
 use std::io::Write;
 use std::fs;
@@ -33,6 +33,8 @@ pub struct StatusData {
     pub separators: bool,
     pub underline: bool,
     pub running: bool,
+    pub bg_opacity: f32,
+    pub bg_blur: f32,
 }
 
 #[derive(Debug, Clone)]
@@ -80,6 +82,10 @@ pub struct SystemState {
     pub status_separators_toggle: Toggle,
     pub status_underline_toggle: Toggle,
     pub status_padding_spinbox: Spinbox,
+    pub status_box_opacity: f32,
+    pub status_box_blur: f32,
+    pub status_box_opacity_slider: Slider,
+    pub status_box_blur_slider: Slider,
 }
 
 impl Default for SystemState {
@@ -133,6 +139,10 @@ impl Default for SystemState {
             status_separators_toggle: Toggle::new().with_label("Show Separators").with_config(&get_config_path(), "status_separators"),
             status_underline_toggle: Toggle::new().with_label("Show Underline").with_config(&get_config_path(), "status_underline"),
             status_padding_spinbox: Spinbox::new(8, 0, 32, 1).with_label("Side Padding").with_unit("px").with_config(&get_config_path(), "status_padding"),
+            status_box_opacity: 1.0,
+            status_box_blur: 0.0,
+            status_box_opacity_slider: Slider::new().with_range(0.0, 100.0).with_scroll(true).with_value(1.0).with_label("Background Opacity").with_config(&get_config_path(), "status_box_opacity"),
+            status_box_blur_slider: Slider::new().with_range(0.0, 100.0).with_scroll(true).with_value(0.0).with_label("Background Blur").with_config(&get_config_path(), "status_box_blur"),
         }
     }
 }
@@ -160,6 +170,8 @@ pub enum SystemMessage {
     StatusToggleUnderline,
     StatusReload,
     StatusSetPadding(u16),
+    StatusSetBoxOpacity(f32),
+    StatusSetBoxBlur(f32),
 }
 
 // ── zbus proxies ────────────────────────────────────────────────────
@@ -478,6 +490,10 @@ pub async fn fetch_system_state() -> SystemState {
         status_separators_toggle: Toggle::new().with_label("Show Separators").with_config(&get_config_path(), "status_separators"),
         status_underline_toggle: Toggle::new().with_label("Show Underline").with_config(&get_config_path(), "status_underline"),
         status_padding_spinbox: Spinbox::new(8, 0, 32, 1).with_label("Side Padding").with_unit("px").with_config(&get_config_path(), "status_padding"),
+        status_box_opacity: 1.0,
+        status_box_blur: 0.0,
+        status_box_opacity_slider: Slider::new().with_range(0.0, 100.0).with_scroll(true).with_value(1.0).with_label("Background Opacity").with_config(&get_config_path(), "status_box_opacity"),
+        status_box_blur_slider: Slider::new().with_range(0.0, 100.0).with_scroll(true).with_value(0.0).with_label("Background Blur").with_config(&get_config_path(), "status_box_blur"),
     }
 }
 
@@ -760,6 +776,22 @@ pub fn view(state: &mut SystemState, cx: f32, cy: f32, cw: f32, ch: f32, _root_f
             sec3.widget(&mut state.status_padding_spinbox, 12.0, sec_w - 24.0, 44.0, ctx);
             sec3.spacing(16.0);
 
+            // Opacity slider
+            state.status_box_opacity_slider.set_label(&format!("Background Opacity: {}%", (state.status_box_opacity * 100.0).round() as i32));
+            state.status_box_opacity_slider.set_value(state.status_box_opacity);
+            let label_h_op = cce_ui::widget::label_offset(&state.status_box_opacity_slider);
+            let slider_h_op = cce_ui::layout::slider_height() + label_h_op;
+            sec3.widget_full(&mut state.status_box_opacity_slider, slider_h_op, ctx);
+            sec3.spacing(16.0);
+
+            // Blur slider
+            state.status_box_blur_slider.set_label(&format!("Background Blur: {}%", (state.status_box_blur * 100.0).round() as i32));
+            state.status_box_blur_slider.set_value(state.status_box_blur);
+            let label_h_bl = cce_ui::widget::label_offset(&state.status_box_blur_slider);
+            let slider_h_bl = cce_ui::layout::slider_height() + label_h_bl;
+            sec3.widget_full(&mut state.status_box_blur_slider, slider_h_bl, ctx);
+            sec3.spacing(16.0);
+
             // Reload button
             let yt_reload = sec3.ay();
             let btn_w = sec_w - 24.0;
@@ -875,6 +907,8 @@ pub fn update(state: &mut SystemState, msg: SystemMessage) {
             let was_status_hovered = state.status_label.hovered();
             let was_separators_hovered = state.status_separators_toggle.hovered();
             let was_underline_hovered = state.status_underline_toggle.hovered();
+            let was_opacity_hovered = state.status_box_opacity_slider.hovered();
+            let was_blur_hovered = state.status_box_blur_slider.hovered();
 
             state.status_loaded = true;
             state.status_font_size = new.font_size;
@@ -882,10 +916,14 @@ pub fn update(state: &mut SystemState, msg: SystemMessage) {
             state.status_separators = new.separators;
             state.status_underline = new.underline;
             state.status_running = new.running;
+            state.status_box_opacity = new.bg_opacity;
+            state.status_box_blur = new.bg_blur;
 
             state.status_label.set_hovered(was_status_hovered);
             state.status_separators_toggle.set_hovered(was_separators_hovered);
             state.status_underline_toggle.set_hovered(was_underline_hovered);
+            state.status_box_opacity_slider.set_hovered(was_opacity_hovered);
+            state.status_box_blur_slider.set_hovered(was_blur_hovered);
         }
         SystemMessage::StatusToggleSeparators => {
             state.status_separators = !state.status_separators;
@@ -902,6 +940,16 @@ pub fn update(state: &mut SystemState, msg: SystemMessage) {
             write_status_padding(val);
             status_interface_reload();
         }
+        SystemMessage::StatusSetBoxOpacity(val) => {
+            state.status_box_opacity = val;
+            write_status_box_opacity(val);
+            status_interface_reload();
+        }
+        SystemMessage::StatusSetBoxBlur(val) => {
+            state.status_box_blur = val;
+            write_status_box_blur(val);
+            status_interface_reload();
+        }
         SystemMessage::StatusReload => {
             status_interface_reload();
         }
@@ -1019,6 +1067,24 @@ fn write_status_underline(val: bool) {
     write_status_value("status_underline", &val.to_string());
 }
 
+fn read_status_box_opacity() -> Option<f32> {
+    let content = std::fs::read_to_string(&get_config_path()).ok()?;
+    Some(parse_f32_from(&content, "status_box_opacity", 1.0))
+}
+
+fn write_status_box_opacity(val: f32) {
+    write_status_value("status_box_opacity", &val.to_string());
+}
+
+fn read_status_box_blur() -> Option<f32> {
+    let content = std::fs::read_to_string(&get_config_path()).ok()?;
+    Some(parse_f32_from(&content, "status_box_blur", 0.0))
+}
+
+fn write_status_box_blur(val: f32) {
+    write_status_value("status_box_blur", &val.to_string());
+}
+
 fn status_interface_reload() {
     std::thread::spawn(|| {
         let _ = std::process::Command::new("pkill")
@@ -1039,6 +1105,8 @@ pub async fn fetch_status_state() -> StatusData {
     let padding = read_status_padding().unwrap_or(8);
     let separators = read_status_separators().unwrap_or(true);
     let underline = read_status_underline().unwrap_or(true);
+    let bg_opacity = read_status_box_opacity().unwrap_or(1.0);
+    let bg_blur = read_status_box_blur().unwrap_or(0.0);
 
     StatusData {
         font_size,
@@ -1046,6 +1114,8 @@ pub async fn fetch_status_state() -> StatusData {
         separators,
         underline,
         running,
+        bg_opacity,
+        bg_blur,
     }
 }
 
@@ -1160,4 +1230,40 @@ mod tests {
 
         let _ = fs::remove_file(path);
     }
+
+    #[test]
+    fn test_read_write_status_box_opacity() {
+        let dir = std::env::temp_dir();
+        let path = dir.join("test_status_box_opacity.json");
+        let path_str = path.to_str().unwrap().to_string();
+
+        let _ = fs::write(&path_str, "{\"layout\": {\"status_box_opacity\": 1.0}}");
+        TEST_CONFIG_PATH.with(|p| *p.borrow_mut() = Some(path_str));
+
+        let original = read_status_box_opacity().unwrap_or(1.0);
+        write_status_box_opacity(0.75);
+        assert_eq!(read_status_box_opacity(), Some(0.75));
+        write_status_box_opacity(original);
+        assert_eq!(read_status_box_opacity(), Some(original));
+
+        let _ = fs::remove_file(path);
+    }
+
+    #[test]
+    fn test_read_write_status_box_blur() {
+        let dir = std::env::temp_dir();
+        let path = dir.join("test_status_box_blur.json");
+        let path_str = path.to_str().unwrap().to_string();
+
+        let _ = fs::write(&path_str, "{\"layout\": {\"status_box_blur\": 0.0}}");
+        TEST_CONFIG_PATH.with(|p| *p.borrow_mut() = Some(path_str));
+
+        let original = read_status_box_blur().unwrap_or(0.0);
+        write_status_box_blur(0.5);
+        assert_eq!(read_status_box_blur(), Some(0.5));
+        write_status_box_blur(original);
+        assert_eq!(read_status_box_blur(), Some(original));
+
+        let _ = fs::remove_file(path);
+    }
 }
diff --git a/src/renderer.rs b/src/renderer.rs
index a4272ad..d2e21c3 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -297,6 +297,8 @@ impl SystemInterface {
                 link_parent_child(&mut self.page_sec_containers[8], &mut self.app.system_info.status_separators_toggle, &mut self.ui_context);
                 link_parent_child(&mut self.page_sec_containers[8], &mut self.app.system_info.status_underline_toggle, &mut self.ui_context);
                 link_parent_child(&mut self.page_sec_containers[8], &mut self.app.system_info.status_padding_spinbox, &mut self.ui_context);
+                link_parent_child(&mut self.page_sec_containers[8], &mut self.app.system_info.status_box_opacity_slider, &mut self.ui_context);
+                link_parent_child(&mut self.page_sec_containers[8], &mut self.app.system_info.status_box_blur_slider, &mut self.ui_context);
             }
             Page::Processes => {
                 self.page_sec_containers.clear();