git.lucas.co / cce-status-interface
status bar
git clone https://git.lucas.co/cce-status-interface.git

commit57ffba5c4a592f83353fc63751a18b1bb4e343e2
parent473ffc95a4
authorLucas Galante <[email protected]>
date2026-07-05 00:49
Merge ViewportModule and WindowModule for viewport tab rendering and click interaction

 src/main.rs    |  62 +++++++--------
 src/modules.rs | 248 +++++++++++++++++++++++----------------------------------
 2 files changed, 129 insertions(+), 181 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 8040d95..cbbc4ed 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,5 @@
 mod modules;
-use modules::{StatusModule, ViewportModule, WindowModule, ClockModule, BatteryModule, VolumeModule, BrightnessModule, MemoryModule, CpuModule, TrayModule};
+use modules::{StatusModule, WindowModule, ClockModule, BatteryModule, VolumeModule, BrightnessModule, MemoryModule, CpuModule, TrayModule};
 
 use std::collections::HashMap;
 use std::sync::Arc;
@@ -417,7 +417,7 @@ impl StatusApp {
                 }
                 is_first_left = false;
 
-                if !module.has_custom_background() {
+                if !module.has_custom_background(&self.title) {
                     if let Some(color) = box_bg_color {
                         self.rounded_boxes.push(RoundedBox {
                             x: left_x,
@@ -507,7 +507,7 @@ impl StatusApp {
                 });
                 self.input_regions.push((right_x.round() as i32, 0, w.round() as i32, bar_h.round() as i32));
 
-                if !module.has_custom_background() {
+                if !module.has_custom_background(&self.title) {
                     if let Some(color) = box_bg_color {
                         self.rounded_boxes.push(RoundedBox {
                             x: right_x,
@@ -1056,7 +1056,7 @@ fn get_module_side(name: &str) -> Side {
         }
     }
     match name {
-        "viewport" | "window" => Side::Left,
+        "window" => Side::Left,
         _ => Side::Right,
     }
 }
@@ -1082,15 +1082,10 @@ impl cce_ui::engine::Application for StatusApp {
         let mut left_modules: Vec<Box<dyn StatusModule>> = Vec::new();
         let mut right_modules: Vec<Box<dyn StatusModule>> = Vec::new();
 
-        let mut has_viewport = false;
         let mut has_window = false;
 
         if let Some((ref name, _)) = selected_module {
             let module: Box<dyn StatusModule> = match name.as_str() {
-                "viewport" => {
-                    has_viewport = true;
-                    Box::new(ViewportModule)
-                }
                 "window" => {
                     has_window = true;
                     Box::new(WindowModule)
@@ -1106,7 +1101,6 @@ impl cce_ui::engine::Application for StatusApp {
             };
             left_modules.push(module);
         } else {
-            left_modules.push(Box::new(ViewportModule));
             left_modules.push(Box::new(WindowModule));
             right_modules.push(Box::new(TrayModule));
             right_modules.push(Box::new(CpuModule));
@@ -1115,15 +1109,12 @@ impl cce_ui::engine::Application for StatusApp {
             right_modules.push(Box::new(VolumeModule));
             right_modules.push(Box::new(BatteryModule));
             right_modules.push(Box::new(ClockModule));
-            has_viewport = true;
             has_window = true;
         }
 
-        if has_viewport {
+        if has_window {
             tokio::spawn(spawn_status_listener("viewport", sender.clone()));
             tokio::spawn(spawn_status_listener("layout", sender.clone()));
-        }
-        if has_window {
             tokio::spawn(spawn_status_listener("title", sender.clone()));
         }
         if selected_module.is_none() {
@@ -1669,7 +1660,7 @@ impl cce_ui::engine::Application for StatusApp {
                             "widgets": [
                                 { "type": "label", "text": mb.name },
                                 { "type": "button", "text": menu_text, "id": "toggle_hide" },
-                                { "type": "button", "text": "Adjust Module Positions", "id": "toggle_adjust" }
+                                { "type": "button", "text": "Adjust Positions", "id": "toggle_adjust" }
                             ]
                         }).to_string()
                     };
@@ -1883,23 +1874,30 @@ impl cce_ui::engine::Application for StatusApp {
                     }
 
                     if clicked_window {
-                        eprintln!("[window-click] Window module clicked!");
-                        self.trigger_switcher(false);
-                    } else {
-                        for bound in &self.viewport_bounds {
-                            eprintln!("[viewport-click] Checking Viewport '{}' bounds: x=[{}..{}], y=[{}..{}]", 
-                                bound.name, bound.x, bound.x + bound.w, bound.y, bound.y + bound.h);
-                            if cx >= bound.x as f64 && cx <= (bound.x + bound.w) as f64
-                                && cy >= bound.y as f64 && cy <= (bound.y + bound.h) as f64 {
-                                eprintln!("[viewport-click] Viewport matched: {}", bound.name);
-                                let name = bound.name.clone();
-                                std::thread::spawn(move || {
-                                    let _ = std::process::Command::new("clearctl")
-                                        .args(["view", &name])
-                                        .spawn();
-                                });
-                                break;
+                        let has_focus = !self.title.is_empty() && self.title != "(none)";
+                        if !has_focus {
+                            let mut clicked_viewport = false;
+                            for bound in &self.viewport_bounds {
+                                if cx >= bound.x as f64 && cx <= (bound.x + bound.w) as f64
+                                    && cy >= bound.y as f64 && cy <= (bound.y + bound.h) as f64 {
+                                    eprintln!("[viewport-click-via-window] Viewport matched: {}", bound.name);
+                                    let name = bound.name.clone();
+                                    std::thread::spawn(move || {
+                                        let _ = std::process::Command::new("clearctl")
+                                            .args(["view", &name])
+                                            .spawn();
+                                    });
+                                    clicked_viewport = true;
+                                    break;
+                                }
                             }
+                            if !clicked_viewport {
+                                eprintln!("[window-click] Window module clicked (no window focused, fallback to switcher)!");
+                                self.trigger_switcher(false);
+                            }
+                        } else {
+                            eprintln!("[window-click] Window module clicked (window focused)!");
+                            self.trigger_switcher(false);
                         }
                     }
                 }
@@ -3049,7 +3047,7 @@ fn main() {
             let mut sigterm = signal(SignalKind::terminate()).expect("SIGTERM");
             
             let modules = vec![
-                "viewport", "window", "tray", "cpu", "memory", "brightness",
+                "window", "tray", "cpu", "memory", "brightness",
                 "volume", "battery", "clock"
             ];
             let current_exe = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from("/home/lsgalante/.local/bin/cce-status-interface"));
diff --git a/src/modules.rs b/src/modules.rs
index 129d3b9..b0181f6 100644
--- a/src/modules.rs
+++ b/src/modules.rs
@@ -11,7 +11,7 @@ use crate::{
 pub trait StatusModule {
     fn name(&self) -> &'static str;
     
-    fn has_custom_background(&self) -> bool { false }
+    fn has_custom_background(&self, _title: &str) -> bool { false }
 
     fn width(
         &self,
@@ -54,102 +54,19 @@ pub trait StatusModule {
     );
 }
 
-pub struct ViewportModule;
-
-impl StatusModule for ViewportModule {
-    fn name(&self) -> &'static str { "viewport" }
-    
-    fn has_custom_background(&self) -> bool { true }
-
-    fn width(
-        &self,
-        _stats: &Option<SystemStats>,
-        viewport: &str,
-        _layout: &str,
-        _title: &str,
-        font_system: &mut FontSystem,
-        font_family: &str,
-        font_size: f32,
-        _tray_items: &HashMap<String, TrayItem>,
-        padding: f32,
-    ) -> f32 {
-        let viewport_parsed = parse_viewport_text(viewport);
-        if viewport_parsed.is_empty() {
-            0.0
-        } else {
-            let mut total_w = 0.0;
-            for (col, text) in &viewport_parsed {
-                let label = Label::new_with_family(font_system, text, font_size, *col, font_family);
-                total_w += label.w + 2.0 * padding + 4.0;
-            }
-            if total_w > 0.0 { total_w - 4.0 } else { 0.0 }
-        }
-    }
-
-    fn render(
-        &self,
-        x: f32,
-        _w: f32,
-        _stats: &Option<SystemStats>,
-        viewport: &str,
-        _layout: &str,
-        _title: &str,
-        font_system: &mut FontSystem,
-        font_family: &str,
-        font_size: f32,
-        _normal_color: [f32; 4],
-        bar_h: f32,
-        _scale_factor: f64,
-        text_items: &mut Vec<TextItem>,
-        _rects: &mut Vec<RectWidget>,
-        _overlay_rects: &mut Vec<RectWidget>,
-        viewport_bounds: &mut Vec<ViewportBounds>,
-        _layout_bounds: &mut Option<LayoutBounds>,
-        _tray_items: &HashMap<String, TrayItem>,
-        _tray_item_bounds: &mut Vec<TrayIconBounds>,
-        box_bg_color: Option<[f32; 4]>,
-        status_box_radius: f32,
-        rounded_boxes: &mut Vec<RoundedBox>,
-        padding: f32,
-    ) {
-        let viewport_parsed = parse_viewport_text(viewport);
-        let mut cur_x = x;
-        for (col, text) in viewport_parsed {
-            let label = Label::new_with_family(font_system, &text, font_size, col, font_family);
-            let box_w = label.w + 2.0 * padding;
-            if let Some(color) = box_bg_color {
-                rounded_boxes.push(RoundedBox {
-                    x: cur_x,
-                    y: 0.0,
-                    w: box_w,
-                    h: bar_h,
-                    radius: status_box_radius,
-                    color,
-                    corners: (false, false, true, true),
-                });
-            }
-            label.draw(text_items, cur_x + padding, (bar_h - font_size * 1.4) / 2.0);
-            viewport_bounds.push(ViewportBounds {
-                name: text.clone(),
-                x: cur_x,
-                y: 0.0,
-                w: box_w,
-                h: bar_h,
-            });
-            cur_x += box_w + 4.0;
-        }
-    }
-}
-
 pub struct WindowModule;
 
 impl StatusModule for WindowModule {
     fn name(&self) -> &'static str { "window" }
 
+    fn has_custom_background(&self, title: &str) -> bool {
+        title.is_empty() || title == "(none)"
+    }
+
     fn width(
         &self,
         _stats: &Option<SystemStats>,
-        _viewport: &str,
+        viewport: &str,
         layout: &str,
         title: &str,
         font_system: &mut FontSystem,
@@ -159,33 +76,42 @@ impl StatusModule for WindowModule {
         padding: f32,
     ) -> f32 {
         let has_title = !title.is_empty() && title != "(none)";
-        let has_layout = !layout.is_empty();
-
-        if !has_title && !has_layout {
-            return 0.0;
-        }
-
-        let mut total_w = 0.0;
         if has_title {
-            let mut display_title = title.to_string();
-            if display_title.chars().count() > 40 {
-                display_title = display_title.chars().take(37).collect::<String>() + "...";
+            let has_layout = !layout.is_empty();
+            let mut total_w = 0.0;
+            if has_title {
+                let mut display_title = title.to_string();
+                if display_title.chars().count() > 40 {
+                    display_title = display_title.chars().take(37).collect::<String>() + "...";
+                }
+                let label = Label::new_with_family(font_system, &display_title, font_size, [0.0, 0.0, 0.0, 1.0], font_family);
+                total_w += label.w;
             }
-            let label = Label::new_with_family(font_system, &display_title, font_size, [0.0, 0.0, 0.0, 1.0], font_family);
-            total_w += label.w;
-        }
 
-        if has_title && has_layout {
-            let sep_label = Label::new_with_family(font_system, " - ", font_size, [0.0, 0.0, 0.0, 1.0], font_family);
-            total_w += sep_label.w;
-        }
+            if has_title && has_layout {
+                let sep_label = Label::new_with_family(font_system, " - ", font_size, [0.0, 0.0, 0.0, 1.0], font_family);
+                total_w += sep_label.w;
+            }
 
-        if has_layout {
-            let layout_label = Label::new_with_family(font_system, layout, font_size, [0.0, 0.0, 0.0, 1.0], font_family);
-            total_w += layout_label.w;
-        }
+            if has_layout {
+                let layout_label = Label::new_with_family(font_system, layout, font_size, [0.0, 0.0, 0.0, 1.0], font_family);
+                total_w += layout_label.w;
+            }
 
-        total_w + 2.0 * padding
+            total_w + 2.0 * padding
+        } else {
+            let viewport_parsed = parse_viewport_text(viewport);
+            if viewport_parsed.is_empty() {
+                0.0
+            } else {
+                let mut total_w = 0.0;
+                for (col, text) in &viewport_parsed {
+                    let label = Label::new_with_family(font_system, text, font_size, *col, font_family);
+                    total_w += label.w + 2.0 * padding + 4.0;
+                }
+                if total_w > 0.0 { total_w - 4.0 } else { 0.0 }
+            }
+        }
     }
 
     fn render(
@@ -193,7 +119,7 @@ impl StatusModule for WindowModule {
         x: f32,
         _w: f32,
         _stats: &Option<SystemStats>,
-        _viewport: &str,
+        viewport: &str,
         layout: &str,
         title: &str,
         font_system: &mut FontSystem,
@@ -205,53 +131,77 @@ impl StatusModule for WindowModule {
         text_items: &mut Vec<TextItem>,
         _rects: &mut Vec<RectWidget>,
         _overlay_rects: &mut Vec<RectWidget>,
-        _viewport_bounds: &mut Vec<ViewportBounds>,
+        viewport_bounds: &mut Vec<ViewportBounds>,
         layout_bounds: &mut Option<LayoutBounds>,
         _tray_items: &HashMap<String, TrayItem>,
         _tray_item_bounds: &mut Vec<TrayIconBounds>,
-        _box_bg_color: Option<[f32; 4]>,
-        _status_box_radius: f32,
-        _rounded_boxes: &mut Vec<RoundedBox>,
+        box_bg_color: Option<[f32; 4]>,
+        status_box_radius: f32,
+        rounded_boxes: &mut Vec<RoundedBox>,
         padding: f32,
     ) {
         let has_title = !title.is_empty() && title != "(none)";
-        let has_layout = !layout.is_empty();
-
-        if !has_title && !has_layout {
-            return;
-        }
-
-        let mut cur_x = x + padding;
-        let y_pos = (bar_h - font_size * 1.4) / 2.0;
-
         if has_title {
-            let mut display_title = title.to_string();
-            if display_title.chars().count() > 40 {
-                display_title = display_title.chars().take(37).collect::<String>() + "...";
+            let has_layout = !layout.is_empty();
+            let mut cur_x = x + padding;
+            let y_pos = (bar_h - font_size * 1.4) / 2.0;
+
+            if has_title {
+                let mut display_title = title.to_string();
+                if display_title.chars().count() > 40 {
+                    display_title = display_title.chars().take(37).collect::<String>() + "...";
+                }
+                let label = Label::new_with_family(font_system, &display_title, font_size, normal_color, font_family);
+                let w = label.w;
+                label.draw(text_items, cur_x, y_pos);
+                cur_x += w;
             }
-            let label = Label::new_with_family(font_system, &display_title, font_size, normal_color, font_family);
-            let w = label.w;
-            label.draw(text_items, cur_x, y_pos);
-            cur_x += w;
-        }
 
-        if has_title && has_layout {
-            let sep_label = Label::new_with_family(font_system, " - ", font_size, normal_color, font_family);
-            let w = sep_label.w;
-            sep_label.draw(text_items, cur_x, y_pos);
-            cur_x += w;
-        }
+            if has_title && has_layout {
+                let sep_label = Label::new_with_family(font_system, " - ", font_size, normal_color, font_family);
+                let w = sep_label.w;
+                sep_label.draw(text_items, cur_x, y_pos);
+                cur_x += w;
+            }
 
-        if has_layout {
-            let layout_label = Label::new_with_family(font_system, layout, font_size, normal_color, font_family);
-            let w = layout_label.w;
-            layout_label.draw(text_items, cur_x, y_pos);
-            *layout_bounds = Some(LayoutBounds {
-                x: cur_x - 2.0,
-                y: 0.0,
-                w: w + 4.0,
-                h: bar_h,
-            });
+            if has_layout {
+                let layout_label = Label::new_with_family(font_system, layout, font_size, normal_color, font_family);
+                let w = layout_label.w;
+                layout_label.draw(text_items, cur_x, y_pos);
+                *layout_bounds = Some(LayoutBounds {
+                    x: cur_x - 2.0,
+                    y: 0.0,
+                    w: w + 4.0,
+                    h: bar_h,
+                });
+            }
+        } else {
+            let viewport_parsed = parse_viewport_text(viewport);
+            let mut cur_x = x;
+            for (col, text) in viewport_parsed {
+                let label = Label::new_with_family(font_system, &text, font_size, col, font_family);
+                let box_w = label.w + 2.0 * padding;
+                if let Some(color) = box_bg_color {
+                    rounded_boxes.push(RoundedBox {
+                        x: cur_x,
+                        y: 0.0,
+                        w: box_w,
+                        h: bar_h,
+                        radius: status_box_radius,
+                        color,
+                        corners: (false, false, true, true),
+                    });
+                }
+                label.draw(text_items, cur_x + padding, (bar_h - font_size * 1.4) / 2.0);
+                viewport_bounds.push(ViewportBounds {
+                    name: text.clone(),
+                    x: cur_x,
+                    y: 0.0,
+                    w: box_w,
+                    h: bar_h,
+                });
+                cur_x += box_w + 4.0;
+            }
         }
     }
 }