system settings
git clone https://git.lucas.co/cce-system-interface.git
Merge Services page into Processes page, implement automated event propagation, and scrolling list clipping
src/app.rs | 102 +-
src/input_handler.rs | 3662 +++++-------------------------------------------
src/main.rs | 39 +-
src/pages/hardware.rs | 637 ---------
src/pages/interface.rs | 54 +
src/pages/mod.rs | 14 +-
src/pages/network.rs | 4 +-
src/pages/packages.rs | 6 +-
src/pages/processes.rs | 1524 ++++++++++++++++++++
src/pages/services.rs | 894 ------------
src/renderer.rs | 78 +-
src/watchers.rs | 44 +-
12 files changed, 2129 insertions(+), 4929 deletions(-)
diff --git a/src/app.rs b/src/app.rs
index 0478dd9..d0eb14e 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -4,10 +4,9 @@ use crate::pages::audio;
use crate::pages::display;
use crate::pages::input;
use crate::pages::network;
-use crate::pages::hardware;
+use crate::pages::processes;
use crate::pages::system_info;
use crate::pages::storage;
-use crate::pages::services;
use crate::pages::interface;
use crate::pages::accounts;
use crate::pages::packages;
@@ -19,10 +18,9 @@ pub struct AppState {
pub display: display::DisplayState,
pub network: network::NetworkState,
pub input: input::InputState,
- pub hardware: hardware::HardwareState,
+ pub processes: processes::ProcessesState,
pub system_info: system_info::SystemState,
pub storage: storage::StorageState,
- pub services: services::ServicesState,
pub interface: interface::InterfaceState,
pub accounts: accounts::AccountsState,
pub packages: packages::PackagesState,
@@ -36,10 +34,9 @@ impl Default for AppState {
display: display::DisplayState::default(),
network: network::NetworkState::default(),
input: input::InputState::default(),
- hardware: hardware::HardwareState::default(),
+ processes: processes::ProcessesState::default(),
system_info: system_info::SystemState::default(),
storage: storage::StorageState::default(),
- services: services::ServicesState::default(),
interface: interface::InterfaceState::default(),
accounts: accounts::AccountsState::default_mock(),
packages: packages::PackagesState::default(),
@@ -54,10 +51,9 @@ pub enum AppAction {
Display(display::DisplayMessage),
Radios(network::NetworkMessage),
Input(input::InputMessage),
- Hardware(hardware::HardwareMessage),
+ Processes(processes::ProcessesMessage),
SystemInfo(system_info::SystemMessage),
Storage(storage::StorageMessage),
- Services(services::ServicesMessage),
Interface(interface::InterfaceMessage),
Accounts(accounts::AccountsMessage),
Packages(packages::PackagesMessage),
@@ -69,23 +65,64 @@ pub struct PageContent {
pub rects: Vec<([f32; 4], f32, f32, f32, f32, f32, (bool, bool, bool, bool))>,
pub texts: Vec<(String, f32, f32, f32, [f32; 4], Option<String>, Option<[f32; 4]>)>,
pub buttons: Vec<(cce_ui::widget::Button, AppAction)>,
+ pub clip_stack: Vec<[f32; 4]>,
}
impl PageContent {
pub fn new() -> Self {
- Self { rects: Vec::new(), texts: Vec::new(), buttons: Vec::new() }
+ Self { rects: Vec::new(), texts: Vec::new(), buttons: Vec::new(), clip_stack: Vec::new() }
+ }
+
+ fn get_clipped_rect(&self, x: f32, y: f32, w: f32, h: f32) -> Option<(f32, f32, f32, f32)> {
+ if let Some(&clip) = self.clip_stack.last() {
+ let cx = clip[0];
+ let cy = clip[1];
+ let cw = clip[2];
+ let ch = clip[3];
+ let rx1 = x.max(cx);
+ let ry1 = y.max(cy);
+ let rx2 = (x + w).min(cx + cw);
+ let ry2 = (y + h).min(cy + ch);
+ if rx1 < rx2 && ry1 < ry2 {
+ Some((rx1, ry1, rx2 - rx1, ry2 - ry1))
+ } else {
+ None
+ }
+ } else {
+ Some((x, y, w, h))
+ }
+ }
+
+ fn get_clipped_bounds(&self, bounds: Option<[f32; 4]>) -> Option<[f32; 4]> {
+ if let Some(&clip) = self.clip_stack.last() {
+ if let Some(b) = bounds {
+ let rx1 = b[0].max(clip[0]);
+ let ry1 = b[1].max(clip[1]);
+ let rx2 = b[2].min(clip[0] + clip[2]);
+ let ry2 = b[3].min(clip[1] + clip[3]);
+ Some([rx1, ry1, rx2.max(rx1), ry2.max(ry1)])
+ } else {
+ Some([clip[0], clip[1], clip[0] + clip[2], clip[1] + clip[3]])
+ }
+ } else {
+ bounds
+ }
}
pub fn rect(&mut self, color: [f32; 4], x: f32, y: f32, w: f32, h: f32) {
- self.rects.push((color, x, y, w, h, 0.0, (true, true, true, true)));
+ if let Some((cx, cy, cw, ch)) = self.get_clipped_rect(x, y, w, h) {
+ self.rects.push((color, cx, cy, cw, ch, 0.0, (true, true, true, true)));
+ }
}
pub fn text(&mut self, content: &str, x: f32, y: f32, size: f32, color: [f32; 4]) {
- self.texts.push((content.to_string(), size, x, y, color, None, None));
+ let cb = self.get_clipped_bounds(None);
+ self.texts.push((content.to_string(), size, x, y, color, None, cb));
}
pub fn text_with_font(&mut self, content: &str, x: f32, y: f32, size: f32, color: [f32; 4], font: &str) {
- self.texts.push((content.to_string(), size, x, y, color, Some(font.to_string()), None));
+ let cb = self.get_clipped_bounds(None);
+ self.texts.push((content.to_string(), size, x, y, color, Some(font.to_string()), cb));
}
pub fn button(&mut self, label: &str, x: f32, y: f32, w: f32, h: f32,
@@ -114,31 +151,58 @@ impl PageContent {
impl RenderTarget for PageContent {
fn rect(&mut self, color: [f32; 4], x: f32, y: f32, w: f32, h: f32) {
- self.rects.push((color, x, y, w, h, 0.0, (true, true, true, true)));
+ if let Some((cx, cy, cw, ch)) = self.get_clipped_rect(x, y, w, h) {
+ self.rects.push((color, cx, cy, cw, ch, 0.0, (true, true, true, true)));
+ }
}
fn rect_with_radius(&mut self, color: [f32; 4], x: f32, y: f32, w: f32, h: f32, radius: f32) {
- self.rects.push((color, x, y, w, h, radius, (true, true, true, true)));
+ if let Some((cx, cy, cw, ch)) = self.get_clipped_rect(x, y, w, h) {
+ self.rects.push((color, cx, cy, cw, ch, radius, (true, true, true, true)));
+ }
}
fn rect_with_radius_corners(&mut self, color: [f32; 4], x: f32, y: f32, w: f32, h: f32, radius: f32, corners: (bool, bool, bool, bool)) {
- self.rects.push((color, x, y, w, h, radius, corners));
+ if let Some((cx, cy, cw, ch)) = self.get_clipped_rect(x, y, w, h) {
+ self.rects.push((color, cx, cy, cw, ch, radius, corners));
+ }
}
fn text(&mut self, content: &str, x: f32, y: f32, size: f32, color: [f32; 4]) {
- self.texts.push((content.to_string(), size, x, y, color, None, None));
+ let cb = self.get_clipped_bounds(None);
+ self.texts.push((content.to_string(), size, x, y, color, None, cb));
}
fn text_with_font(&mut self, content: &str, x: f32, y: f32, size: f32, color: [f32; 4], font: &str) {
- self.texts.push((content.to_string(), size, x, y, color, Some(font.to_string()), None));
+ let cb = self.get_clipped_bounds(None);
+ self.texts.push((content.to_string(), size, x, y, color, Some(font.to_string()), cb));
}
fn text_with_bounds(&mut self, content: &str, x: f32, y: f32, size: f32, color: [f32; 4], bounds: Option<[f32; 4]>) {
- self.texts.push((content.to_string(), size, x, y, color, None, bounds));
+ let cb = self.get_clipped_bounds(bounds);
+ self.texts.push((content.to_string(), size, x, y, color, None, cb));
}
fn text_with_font_and_bounds(&mut self, content: &str, x: f32, y: f32, size: f32, color: [f32; 4], font: &str, bounds: Option<[f32; 4]>) {
- self.texts.push((content.to_string(), size, x, y, color, Some(font.to_string()), bounds));
+ let cb = self.get_clipped_bounds(bounds);
+ self.texts.push((content.to_string(), size, x, y, color, Some(font.to_string()), cb));
+ }
+
+ fn push_clip_rect(&mut self, x: f32, y: f32, w: f32, h: f32) {
+ let clip = if let Some(&parent_clip) = self.clip_stack.last() {
+ let cx = x.max(parent_clip[0]);
+ let cy = y.max(parent_clip[1]);
+ let cw = (x + w).min(parent_clip[0] + parent_clip[2]) - cx;
+ let ch = (y + h).min(parent_clip[1] + parent_clip[3]) - cy;
+ [cx, cy, cw.max(0.0), ch.max(0.0)]
+ } else {
+ [x, y, w, h]
+ };
+ self.clip_stack.push(clip);
+ }
+
+ fn pop_clip_rect(&mut self) {
+ self.clip_stack.pop();
}
}
diff --git a/src/input_handler.rs b/src/input_handler.rs
index cfe29a5..a038d4a 100644
--- a/src/input_handler.rs
+++ b/src/input_handler.rs
@@ -30,300 +30,12 @@ impl SystemInterface {
if self.switcher.cursor_moved(lx_no_scroll, ly_no_scroll, &mut self.ui_context) {
changed = true;
}
- for w in &mut self.widgets {
- let was = w.hovering;
- w.hovering = self.cursor_x >= w.x && self.cursor_x <= w.x + w.w
- && self.cursor_y >= w.y && self.cursor_y <= w.y + w.h;
- if w.hovering != was {
- w.color = if w.hovering { w.hover_color } else { w.color };
- changed = true;
- }
- }
-
- if self.app.current_page == Page::Interface {
- for cp in &mut self.app.interface.color_selectors {
- if cp.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.button_padding_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.section_padding_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.label_alignment_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.label_offset_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.label_margin_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.plate_padding_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.graph_show_grid_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.graph_snap_enabled_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.graph_uniform_background_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.graph_cell_opacity_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.graph_gap_opacity_spinbox.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;
- }
- if self.app.interface.grid_min_col_width_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.spinbox_height_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.spinbox_corner_radius_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.toggle_height_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.toggle_corner_radius_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.plate_opacity_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.plate_corner_radius_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.page_opacity_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.layer_opacity_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
-
- if self.app.interface.color_selector_height_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.color_selector_corner_radius_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.color_selector_preview_corner_radius_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.color_selector_preview_margin_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.color_selector_font_selector.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.menubar_font_selector.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.breadcrumb_font_selector.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.section_label_font_selector.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.nested_section_label_font_selector.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.textbox_height_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.textbox_corner_radius_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.slider_height_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.font_selector_height_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.font_selector_corner_radius_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.dropdown_height_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.dropdown_corner_radius_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.button_corner_radius_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.notification_opacity_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.window_opacity_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.window_corner_radius_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.sans_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.serif_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.mono_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.borders_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.borders_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.status_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.status_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.fuzzel_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.fuzzel_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.terminal_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.terminal_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.borders_size_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.status_size_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.fuzzel_size_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.terminal_size_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.custom_multicontrol.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- for sb in &mut self.app.interface.windows.spinboxes {
- if sb.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- if self.app.interface.windows.cascade_offset_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.edge_gap_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.top_gap_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.grid_gap_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.transition_duration_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.status_height_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- for menu in &mut self.app.interface.windows.tag_layout_menus {
- if menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- if self.app.interface.windows.side_panel_behavior_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.side_panel_position_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.side_panel_width_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.side_panel_border_gap_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.side_panel_border_opacity_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.transparency_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.interface.windows.blur_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- if self.app.current_page == Page::Input {
- if self.app.input.rate_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.delay_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.tap_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.scroll_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.scroll_friction_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.pointer_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.pointer_friction_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.trackpad_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.trackpad_friction_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.dwtp_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.trackpoint_accel_speed_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.trackpoint_accel_profile_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.zoom_in_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.zoom_out_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.input.keybinds_control.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
+ // Drag updates are high-priority overrides
+ let mut drag_handled = false;
if self.app.current_page == Page::Audio {
if let Some(idx) = self.audio_sink_dragging {
+ drag_handled = true;
if idx < self.app.audio.sink_sliders.len() {
if self.app.audio.sink_sliders[idx].drag_update(lx, ly) {
changed = true;
@@ -338,6 +50,7 @@ impl SystemInterface {
}
}
} else if let Some(idx) = self.audio_source_dragging {
+ drag_handled = true;
if idx < self.app.audio.source_sliders.len() {
if self.app.audio.source_sliders[idx].drag_update(lx, ly) {
changed = true;
@@ -351,31 +64,11 @@ impl SystemInterface {
}
}
}
- } else {
- for sb in &mut self.app.audio.sink_spinboxes {
- if sb.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- for sb in &mut self.app.audio.source_spinboxes {
- if sb.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- for slider in &mut self.app.audio.sink_sliders {
- if slider.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- for slider in &mut self.app.audio.source_sliders {
- if slider.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
}
}
if self.app.current_page == Page::Display {
if self.display_brightness_dragging {
+ drag_handled = true;
if self.app.display.brightness_slider.drag_update(lx, ly) {
changed = true;
let val = self.app.display.brightness_slider.value();
@@ -383,172 +76,21 @@ impl SystemInterface {
self.app.display.brightness_spinbox.value = pct as i32;
self.handle_action(&AppAction::Display(pages::display::DisplayMessage::BrightnessSet(pct)));
}
- } else {
- if self.app.display.brightness_slider.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.display.brightness_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- if self.app.display.night_light_label.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- for out in &mut self.app.display.outputs {
- if out.name_label.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if out.resolution_label.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if let Some(ref mut scale_lbl) = out.scale_label {
- if scale_lbl.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- }
- if self.app.display.screensaver_enable_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.display.screensaver_lock_screen_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.display.screensaver_timeout_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.display.screensaver_style_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
}
}
- if self.app.current_page == Page::Hardware {
- if self.app.hardware.cpu_label.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.hardware.cpu_usage_label.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.hardware.cpu_temp_label.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- for gpu_lbl in &mut self.app.hardware.gpu_labels {
- if gpu_lbl.cursor_moved(lx, ly, &mut self.ui_context) {
+ if !drag_handled {
+ let event = cce_ui::widget::Event::PointerMove { x: lx, y: ly };
+ if let Some(root) = self.get_page_root_widget() {
+ if self.ui_context.propagate_event(&event, root) {
changed = true;
}
}
- if self.app.hardware.cpu_list_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.hardware.cpu_gov_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.hardware.gpu_gov_menu.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
-
- if self.app.current_page == Page::Packages {
- let pkgs = &mut self.app.packages;
- if pkgs.search_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- match pkgs.active_tab {
- pages::packages::PackageTab::Installed => {
- if pkgs.installed_list_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- let query = if pkgs.search_box.editing {
- pkgs.search_box.edit_buffer.to_lowercase()
- } else {
- pkgs.search_box.text.to_lowercase()
- };
- let matching = pkgs.installed.iter()
- .filter(|p| p.name.to_lowercase().contains(&query) || p.version.to_lowercase().contains(&query))
- .count();
- for i in 0..matching.min(pkgs.installed_items.len()) {
- if pkgs.installed_items[i].cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- }
- pages::packages::PackageTab::Updates => {
- if pkgs.updates_list_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- let query = if pkgs.search_box.editing {
- pkgs.search_box.edit_buffer.to_lowercase()
- } else {
- pkgs.search_box.text.to_lowercase()
- };
- let matching = pkgs.updates.iter()
- .filter(|p| p.name.to_lowercase().contains(&query))
- .count();
- for i in 0..matching.min(pkgs.updates_items.len()) {
- if pkgs.updates_items[i].cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- }
- }
}
-
-
- if self.app.current_page == Page::Services {
- if self.app.services.search_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.services.list_box.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- let query = if self.app.services.search_box.editing {
- self.app.services.search_box.edit_buffer.to_lowercase()
- } else {
- self.app.services.search_box.text.to_lowercase()
- };
- let matching_count = self.app.services.services.iter()
- .filter(|s| s.is_system == (self.app.services.active_tab == pages::services::ServiceTab::System))
- .filter(|s| s.name.to_lowercase().contains(&query) || s.description.to_lowercase().contains(&query))
- .count();
- for i in 0..matching_count.min(self.app.services.service_items.len()) {
- if self.app.services.service_items[i].cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- if self.app.services.notifications_enable_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.services.notifications_bell_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.services.notifications_duration_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.services.status_label.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.services.status_separators_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.services.status_underline_toggle.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- if self.app.services.status_padding_spinbox.cursor_moved(lx, ly, &mut self.ui_context) {
- changed = true;
- }
- }
- if self.app.current_page == Page::Accounts {
- if self.app.accounts.editing_oauth_creds {
- if self.app.accounts.oauth_client_id_box.cursor_moved(lx, ly, &mut self.ui_context) { changed = true; }
- if self.app.accounts.oauth_client_secret_box.cursor_moved(lx, ly, &mut self.ui_context) { changed = true; }
- } else if self.app.accounts.adding_new {
- if self.app.accounts.email_box.cursor_moved(lx, ly, &mut self.ui_context) { changed = true; }
- if self.app.accounts.password_box.cursor_moved(lx, ly, &mut self.ui_context) { changed = true; }
- if self.app.accounts.imap_box.cursor_moved(lx, ly, &mut self.ui_context) { changed = true; }
- if self.app.accounts.smtp_box.cursor_moved(lx, ly, &mut self.ui_context) { changed = true; }
- }
+ if changed {
+ self.needs_rebuild = true;
}
- if changed { self.needs_rebuild = true; }
changed
}
@@ -621,6 +163,7 @@ impl SystemInterface {
}
if button != cce_ui::widget::MouseButton::Left && button != cce_ui::widget::MouseButton::Right { return false; }
+ let mut actions = Vec::new();
if button == cce_ui::widget::MouseButton::Left && state == cce_ui::widget::ElementState::Released {
if self.app.current_page == Page::Audio {
let mut ended = false;
@@ -649,2769 +192,364 @@ impl SystemInterface {
self.needs_rebuild = true;
}
}
-
- let (px, py) = (self.cursor_x, self.cursor_y);
- for (btn, action) in &self.page_buttons.clone() {
- let base = btn.base().unwrap();
- if px >= base.x && px <= base.x + base.w && py >= base.y && py <= base.y + base.h {
- self.handle_action(action);
- self.needs_rebuild = true;
- return true;
- }
- }
}
+
let lx = self.cursor_x / s;
let ly = self.cursor_y / s + self.scroll_y;
- let mut actions = Vec::new();
-
- if state == cce_ui::widget::ElementState::Pressed {
- let mut clicked_any_focusable = false;
- match self.app.current_page {
- Page::Accounts => {
- let accs = &mut self.app.accounts;
- if accs.editing_oauth_creds {
- if accs.oauth_client_id_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if accs.oauth_client_secret_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- } else if accs.adding_new {
- if accs.email_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if accs.password_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if accs.imap_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if accs.smtp_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- }
+ let event = cce_ui::widget::Event::MouseButton { button, state, x: lx, y: ly };
+ if let Some(root) = self.get_page_root_widget() {
+ self.ui_context.propagate_event(&event, root);
+ }
- Page::Interface => {
- if self.app.interface.custom_multicontrol.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- for cp in &mut self.app.interface.color_selectors {
- if cp.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.button_padding_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.section_padding_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.label_alignment_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.label_offset_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.label_margin_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.plate_padding_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.graph_show_grid_toggle.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- 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_cell_opacity_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.graph_gap_opacity_spinbox.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; }
- if self.app.interface.spinbox_corner_radius_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.toggle_height_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.toggle_corner_radius_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.plate_opacity_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.plate_corner_radius_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.page_opacity_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.layer_opacity_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
+ self.propagate_widget_changes(&mut actions);
+ for a in &actions {
+ self.handle_action(a);
+ }
+ if !actions.is_empty() {
+ self.needs_rebuild = true;
+ return true;
+ }
+ self.needs_rebuild = true;
+ true
+ }
- if self.app.interface.color_selector_height_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.color_selector_corner_radius_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.color_selector_preview_corner_radius_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.color_selector_preview_margin_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.color_selector_font_selector.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.menubar_font_selector.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.breadcrumb_font_selector.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.section_label_font_selector.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.nested_section_label_font_selector.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.textbox_height_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.textbox_corner_radius_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.slider_height_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.font_selector_height_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.font_selector_corner_radius_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.dropdown_height_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.dropdown_corner_radius_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.button_corner_radius_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.notification_opacity_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.window_opacity_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.interface.window_corner_radius_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- let tf = &mut self.app.interface;
- if tf.sans_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.serif_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.mono_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.borders_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.borders_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.status_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.status_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.fuzzel_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.fuzzel_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.terminal_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.terminal_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- for sb in &mut tf.windows.spinboxes {
- if sb.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- if tf.windows.cascade_offset_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.edge_gap_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.top_gap_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.grid_gap_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.transition_duration_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.status_height_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- for menu in &mut tf.windows.tag_layout_menus {
- if menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
+ pub(crate) fn propagate_widget_changes(&mut self, actions: &mut Vec<AppAction>) {
+ match self.app.current_page {
+ 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::SetDesktopBackground(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::SetMenubarTabLabelColor(cp.color),
+ 12 => pages::interface::InterfaceMessage::SetToggleEnabledColor(cp.color),
+ 13 => pages::interface::InterfaceMessage::SetToggleDisabledColor(cp.color),
+ 14 => pages::interface::InterfaceMessage::SetScrollingListBgColor(cp.color),
+ 15 => pages::interface::InterfaceMessage::SetBreadcrumbBgColor(cp.color),
+ 16 => pages::interface::InterfaceMessage::SetPopoverBgColor(cp.color),
+ 17 => pages::interface::InterfaceMessage::SetNotificationBgColor(cp.color),
+ 18 => pages::interface::InterfaceMessage::SetWindowColor(cp.color),
+ 19 => pages::interface::InterfaceMessage::SetPageColor(cp.color),
+ 20 => pages::interface::InterfaceMessage::SetLayerColor(cp.color),
+ 21 => pages::interface::InterfaceMessage::SetScrollingListEntryBgColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
+ 22 => pages::interface::InterfaceMessage::SetScrollingListEntryHighlightColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
+ _ => pages::interface::InterfaceMessage::SetDesktopBackground(cp.color),
+ }));
}
- if tf.windows.side_panel_behavior_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.side_panel_position_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.side_panel_width_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.side_panel_border_gap_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.side_panel_border_opacity_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.transparency_toggle.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if tf.windows.blur_toggle.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- Page::Input => {
- if self.app.input.rate_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.input.delay_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.input.scroll_friction_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.input.pointer_friction_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.input.trackpad_friction_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.input.trackpoint_accel_speed_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.input.trackpoint_accel_profile_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.input.zoom_in_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.input.zoom_out_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.input.keybinds_control.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
}
-
- Page::Audio => {
- for sb in &mut self.app.audio.sink_spinboxes {
- if sb.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- for sb in &mut self.app.audio.source_spinboxes {
- if sb.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- for slider in &mut self.app.audio.sink_sliders {
- if slider.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- for slider in &mut self.app.audio.source_sliders {
- if slider.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
+ if self.app.interface.notification_opacity_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetNotificationOpacity(self.app.interface.notification_opacity_spinbox.value as f32 / 100.0)));
}
- Page::Display => {
- if self.app.display.brightness_slider.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.display.brightness_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.display.night_light_label.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- for out in &mut self.app.display.outputs {
- if out.name_label.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if out.resolution_label.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if let Some(ref mut scale_lbl) = out.scale_label {
- if scale_lbl.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- }
- if self.app.display.screensaver_timeout_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if self.app.display.screensaver_style_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- Page::Services => {
- let srv = &mut self.app.services;
- if srv.search_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if srv.list_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if srv.notifications_duration_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if srv.status_label.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if srv.status_padding_spinbox.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- Page::Packages => {
- let pkgs = &mut self.app.packages;
- if pkgs.search_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- match pkgs.active_tab {
- pages::packages::PackageTab::Installed => {
- if pkgs.installed_list_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- pages::packages::PackageTab::Updates => {
- if pkgs.updates_list_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- }
- }
+ 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)));
}
- Page::Hardware => {
- let hw = &mut self.app.hardware;
- if hw.cpu_list_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if hw.cpu_gov_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
- if hw.gpu_gov_menu.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
+
+ if self.app.interface.button_padding_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetButtonPadding(self.app.interface.button_padding_spinbox.value as u16)));
}
- Page::Radios => {
- let net = &mut self.app.network;
- if net.wifi_list_box.hit_test(lx, ly, &self.ui_context) { clicked_any_focusable = true; }
+ 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 !clicked_any_focusable {
- cce_ui::widget::focus::clear_focus();
- }
- }
-
- if state == cce_ui::widget::ElementState::Pressed && self.app.current_page == Page::Interface {
- let tf = &mut self.app.interface.windows;
- for (i, sb) in tf.spinboxes.iter_mut().enumerate() {
- 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::Windows(
- pages::interface::WindowsMessage::SetWidth(
- pages::interface::WidthParam::ALL[i],
- sb.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)));
}
- }
- let sb = &mut tf.cascade_offset_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::Windows(
- pages::interface::WindowsMessage::SetCascadeOffset(sb.value as u16)
- )
- ));
- }
- let sb = &mut tf.edge_gap_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::Windows(
- pages::interface::WindowsMessage::SetEdgeGap(sb.value as u16)
- )
- ));
- }
- let sb = &mut tf.top_gap_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::Windows(
- pages::interface::WindowsMessage::SetTopGap(sb.value as u16)
- )
- ));
- }
- let sb = &mut tf.grid_gap_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::Windows(
- pages::interface::WindowsMessage::SetGridGap(sb.value as u16)
- )
- ));
- }
- let sb = &mut tf.transition_duration_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::Windows(
- pages::interface::WindowsMessage::SetTransitionDuration(sb.value as u16)
- )
- ));
- }
- let sb = &mut tf.status_height_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::Windows(
- pages::interface::WindowsMessage::SetStatusHeight(sb.value as u16)
- )
- ));
- }
- }
- if self.app.current_page == Page::Interface {
- let tf = &mut self.app.interface.windows;
- for (idx, menu) in tf.tag_layout_menus.iter_mut().enumerate() {
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetTagLayout(idx + 1, menu.selected)
- )
- ));
- }
- }
- let menu = &mut tf.side_panel_behavior_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetSidePanelBehavior(menu.selected)
- )
- ));
- }
- let menu = &mut tf.side_panel_position_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetSidePanelPosition(menu.selected)
- )
- ));
- }
- let sb = &mut tf.side_panel_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::Windows(
- pages::interface::WindowsMessage::SetSidePanelWidth(sb.value as u16)
- )
- ));
- }
- let sb = &mut tf.side_panel_border_gap_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::Windows(
- pages::interface::WindowsMessage::SetSidePanelBorderGap(sb.value as u16)
- )
- ));
- }
- let sb = &mut tf.side_panel_border_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::Windows(
- pages::interface::WindowsMessage::SetSidePanelBorderOpacity(sb.value as u16)
- )
- ));
- }
- let toggle = &mut tf.transparency_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::ToggleTransparency
- )
- ));
- }
- let toggle2 = &mut tf.blur_toggle;
- toggle2.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle2.take_click() {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::ToggleBlur
- )
- ));
- }
- }
- if state == cce_ui::widget::ElementState::Pressed && self.app.current_page == Page::Interface {
- for (i, cp) in self.app.interface.color_selectors.iter_mut().enumerate() {
- let old = cp.color;
- let old_alpha = cp.alpha;
- if !cp.hit_test(lx, ly, &self.ui_context) { cp.unfocus(); }
- cp.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if cp.take_click() {
- actions.push(AppAction::Interface(match i {
- 0 => pages::interface::InterfaceMessage::PickPageLowColor,
- 1 => pages::interface::InterfaceMessage::PickHighColor,
- 2 => pages::interface::InterfaceMessage::PickVisualGuides,
- 3 => pages::interface::InterfaceMessage::PickDisabledColor,
- 4 => pages::interface::InterfaceMessage::PickSeparatorColor,
- 5 => pages::interface::InterfaceMessage::PickSliderTrackColor,
- 6 => pages::interface::InterfaceMessage::PickColorBordersColor,
- 7 => pages::interface::InterfaceMessage::PickLowColor,
- 8 => pages::interface::InterfaceMessage::PickNormalColor,
- 9 => pages::interface::InterfaceMessage::PickPaginatorSidebarColor,
- 10 => pages::interface::InterfaceMessage::PickPrimaryHighlightColor,
- 11 => pages::interface::InterfaceMessage::PickMenubarTabLabelColor,
- 12 => pages::interface::InterfaceMessage::PickToggleEnabledColor,
- 13 => pages::interface::InterfaceMessage::PickToggleDisabledColor,
- 14 => pages::interface::InterfaceMessage::PickScrollingListBgColor,
- 15 => pages::interface::InterfaceMessage::PickBreadcrumbBgColor,
- 16 => pages::interface::InterfaceMessage::PickPopoverBgColor,
- 17 => pages::interface::InterfaceMessage::PickNotificationBgColor,
- 18 => pages::interface::InterfaceMessage::PickWindowColor,
- 19 => pages::interface::InterfaceMessage::PickPageColor,
- 20 => pages::interface::InterfaceMessage::PickLayerColor,
- 21 => pages::interface::InterfaceMessage::PickScrollingListEntryBgColor,
- 22 => pages::interface::InterfaceMessage::PickScrollingListEntryHighlightColor,
- _ => pages::interface::InterfaceMessage::PickLowColor,
- }));
- }
- if cp.color != old || cp.alpha != old_alpha {
- 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::SetDesktopBackground(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::SetMenubarTabLabelColor(cp.color),
- 12 => pages::interface::InterfaceMessage::SetToggleEnabledColor(cp.color),
- 13 => pages::interface::InterfaceMessage::SetToggleDisabledColor(cp.color),
- 14 => pages::interface::InterfaceMessage::SetScrollingListBgColor(cp.color),
- 15 => pages::interface::InterfaceMessage::SetBreadcrumbBgColor(cp.color),
- 16 => pages::interface::InterfaceMessage::SetPopoverBgColor(cp.color),
- 17 => pages::interface::InterfaceMessage::SetNotificationBgColor(cp.color),
- 18 => pages::interface::InterfaceMessage::SetWindowColor(cp.color),
- 19 => pages::interface::InterfaceMessage::SetPageColor(cp.color),
- 20 => pages::interface::InterfaceMessage::SetLayerColor(cp.color),
- 21 => pages::interface::InterfaceMessage::SetScrollingListEntryBgColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
- 22 => pages::interface::InterfaceMessage::SetScrollingListEntryHighlightColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
- _ => pages::interface::InterfaceMessage::SetDesktopBackground(cp.color),
- }));
- }
- }
- 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.notification_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::SetNotificationOpacity(sb.value as f32 / 100.0)));
- }
- let sb = &mut self.app.interface.window_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::SetWindowOpacity(sb.value as f32 / 100.0)));
- }
- let sb = &mut self.app.interface.window_corner_radius_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::SetWindowCornerRadius(sb.value as u16)));
- }
-
- let sb = &mut self.app.interface.button_padding_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::SetButtonPadding(sb.value as u16)));
- }
- let sb = &mut self.app.interface.section_padding_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::SetSectionPadding(sb.value as u16)));
- }
- let menu = &mut self.app.interface.label_alignment_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetNestedSectionLabelAlignment(menu.selected)));
- }
- let sb = &mut self.app.interface.label_offset_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::SetNestedSectionLabelOffset(sb.value as i16)));
- }
- let sb = &mut self.app.interface.label_margin_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::SetLabelMargin(sb.value as u16)));
- }
- let sb = &mut self.app.interface.plate_padding_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::SetPlatePadding(sb.value as u16)));
- }
- let toggle = &mut self.app.interface.graph_show_grid_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphShowGrid(toggle.toggled())));
- }
- let toggle = &mut self.app.interface.graph_snap_enabled_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphSnapEnabled(toggle.toggled())));
- }
- let toggle = &mut self.app.interface.graph_uniform_background_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- 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 sb = &mut self.app.interface.graph_cell_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::SetGraphCellOpacity(sb.value as f32 / 100.0)));
- }
- let sb = &mut self.app.interface.graph_gap_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::SetGraphGapOpacity(sb.value as f32 / 100.0)));
- }
- let sb = &mut self.app.interface.page_margin_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::SetPageMargin(sb.value as u16)));
- }
- let sb = &mut self.app.interface.grid_min_col_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::SetGridMinColWidth(sb.value as u16)));
- }
- let sb = &mut self.app.interface.spinbox_height_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::SetSpinboxHeight(sb.value as u16)));
- }
- let sb = &mut self.app.interface.spinbox_corner_radius_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::SetSpinboxCornerRadius(sb.value as u16)));
- }
- let sb = &mut self.app.interface.toggle_height_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::SetToggleHeight(sb.value as u16)));
- }
- let sb = &mut self.app.interface.toggle_corner_radius_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::SetToggleCornerRadius(sb.value as u16)));
- }
- let sb = &mut self.app.interface.plate_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::SetPlateOpacity(sb.value as f32 / 100.0)));
- }
- let sb = &mut self.app.interface.plate_corner_radius_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::SetPlateCornerRadius(sb.value as u16)));
- }
- let sb = &mut self.app.interface.page_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::SetPageOpacity(sb.value as f32 / 100.0)));
- }
- let sb = &mut self.app.interface.layer_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::SetLayerOpacity(sb.value as f32 / 100.0)));
- }
-
-
- let sb = &mut self.app.interface.color_selector_height_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::SetColorSelectorHeight(sb.value as u16)));
- }
- let sb = &mut self.app.interface.color_selector_corner_radius_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::SetColorSelectorCornerRadius(sb.value as u16)));
- }
- let sb = &mut self.app.interface.color_selector_preview_corner_radius_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::SetColorSelectorPreviewCornerRadius(sb.value as u16)));
- }
- let sb = &mut self.app.interface.color_selector_preview_margin_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::SetColorSelectorPreviewMargin(sb.value as u16)));
- }
- let sb = &mut self.app.interface.textbox_height_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::SetTextboxHeight(sb.value as u16)));
- }
- let sb = &mut self.app.interface.textbox_corner_radius_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::SetTextboxCornerRadius(sb.value as u16)));
- }
- let sb = &mut self.app.interface.slider_height_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::SetSliderHeight(sb.value as u16)));
- }
- let sb = &mut self.app.interface.font_selector_height_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::SetFontSelectorHeight(sb.value as u16)));
- }
- let sb = &mut self.app.interface.font_selector_corner_radius_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::SetFontSelectorCornerRadius(sb.value as u16)));
- }
- let sb = &mut self.app.interface.dropdown_height_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::SetDropdownHeight(sb.value as u16)));
- }
- let sb = &mut self.app.interface.dropdown_corner_radius_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::SetDropdownCornerRadius(sb.value as u16)));
- }
- let sb = &mut self.app.interface.button_corner_radius_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::SetButtonCornerRadius(sb.value as u16)));
- }
- }
-
- if self.app.current_page == Page::Interface {
- let mc = &mut self.app.interface.custom_multicontrol;
- if state == cce_ui::widget::ElementState::Pressed && !mc.hit_test(lx, ly, &self.ui_context) {
- mc.unfocus();
- }
- if mc.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- }
- if state == cce_ui::widget::ElementState::Pressed && self.app.current_page == Page::Input {
- let sb = &mut self.app.input.rate_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::Input(pages::input::InputMessage::ApplyRepeat));
- }
- let sb = &mut self.app.input.delay_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::Input(pages::input::InputMessage::ApplyRepeat));
- }
- let sb = &mut self.app.input.scroll_friction_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::Input(pages::input::InputMessage::ApplyScrollFriction));
- }
- let sb = &mut self.app.input.scroll_speed_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::Input(pages::input::InputMessage::ApplyScrollSpeed));
- }
- let sb = &mut self.app.input.pointer_friction_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::Input(pages::input::InputMessage::ApplyPointerFriction));
- }
- let sb = &mut self.app.input.trackpad_friction_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::Input(pages::input::InputMessage::ApplyTrackpadFriction));
- }
- let sb = &mut self.app.input.trackpoint_accel_speed_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::Input(pages::input::InputMessage::ApplyTrackpointAccelSpeed));
- }
- let sb = &mut self.app.input.cursor_size_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::Input(pages::input::InputMessage::ApplyCursorSize));
- }
- let kc = &mut self.app.input.keybinds_control;
- if state == cce_ui::widget::ElementState::Pressed && !kc.hit_test(lx, ly, &self.ui_context) {
- kc.unfocus();
- }
- }
- if state == cce_ui::widget::ElementState::Pressed && self.app.current_page == Page::Services {
- let sb = &mut self.app.services.notifications_duration_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::Services(pages::services::ServicesMessage::SetNotificationsDuration(sb.value)));
- }
- let sb2 = &mut self.app.services.status_padding_spinbox;
- if !sb2.hit_test(lx, ly, &self.ui_context) { sb2.unfocus(); }
- let old2 = sb2.value;
- if sb2.mouse_input(button, state, lx, ly, &mut self.ui_context) && sb2.value != old2 {
- actions.push(AppAction::Services(pages::services::ServicesMessage::StatusSetPadding(sb2.value as u16)));
- }
- }
- if self.app.current_page == Page::Input {
- let toggle = &mut self.app.input.tap_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Input(pages::input::InputMessage::ToggleTapToClick));
- }
- let toggle = &mut self.app.input.scroll_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Input(pages::input::InputMessage::ToggleInertialScroll));
- }
- let toggle = &mut self.app.input.natural_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Input(pages::input::InputMessage::ToggleNaturalScroll));
- }
- let toggle = &mut self.app.input.pointer_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Input(pages::input::InputMessage::ToggleInertialPointer));
- }
- let toggle = &mut self.app.input.trackpad_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Input(pages::input::InputMessage::ToggleInertialTrackpad));
- }
- let toggle = &mut self.app.input.dwtp_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Input(pages::input::InputMessage::ToggleDwtp));
- }
- let menu = &mut self.app.input.trackpoint_accel_profile_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Input(pages::input::InputMessage::ApplyTrackpointAccelProfile(menu.selected)));
- }
- let menu = &mut self.app.input.cursor_theme_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Input(pages::input::InputMessage::ApplyCursorTheme(menu.selected)));
- }
-
- let tb = &mut self.app.input.zoom_in_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && tb.take_change() {
- actions.push(AppAction::Input(pages::input::InputMessage::ApplyZoomIn));
- }
-
- let tb = &mut self.app.input.zoom_out_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && tb.take_change() {
- actions.push(AppAction::Input(pages::input::InputMessage::ApplyZoomOut));
- }
- let kc = &mut self.app.input.keybinds_control;
- if kc.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if kc.take_change() {
- actions.push(AppAction::Input(pages::input::InputMessage::ReloadKeybinds));
- self.needs_rebuild = true;
- }
- }
- if self.app.current_page == Page::Services {
- let toggle = &mut self.app.services.notifications_enable_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Services(pages::services::ServicesMessage::ToggleNotificationsEnable));
- }
- let toggle = &mut self.app.services.notifications_bell_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Services(pages::services::ServicesMessage::ToggleNotificationsBell));
- }
- if state == cce_ui::widget::ElementState::Pressed {
- let lbl1 = &mut self.app.services.status_label;
- if !lbl1.hit_test(lx, ly, &self.ui_context) { lbl1.unfocus(); }
- lbl1.mouse_input(button, state, lx, ly, &mut self.ui_context);
- }
- let toggle = &mut self.app.services.status_separators_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Services(pages::services::ServicesMessage::StatusToggleSeparators));
- }
- let toggle2 = &mut self.app.services.status_underline_toggle;
- toggle2.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle2.take_click() {
- actions.push(AppAction::Services(pages::services::ServicesMessage::StatusToggleUnderline));
- }
- }
- if self.app.current_page == Page::Display {
- let toggle = &mut self.app.display.screensaver_enable_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Display(pages::display::DisplayMessage::ToggleScreensaverEnable));
- }
-
- let toggle = &mut self.app.display.screensaver_lock_screen_toggle;
- toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if toggle.take_click() {
- actions.push(AppAction::Display(pages::display::DisplayMessage::ToggleScreensaverLockScreen));
- }
-
- let menu = &mut self.app.display.screensaver_style_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Display(pages::display::DisplayMessage::SetScreensaverStyle(menu.selected)));
- }
- }
- if self.app.current_page == Page::Hardware {
- let menu = &mut self.app.hardware.cpu_gov_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- if menu.selected == 0 {
- actions.push(AppAction::Hardware(pages::hardware::HardwareMessage::SetCpuPerformance));
- } else {
- actions.push(AppAction::Hardware(pages::hardware::HardwareMessage::SetCpuPowersave));
- }
- }
-
- let menu = &mut self.app.hardware.gpu_gov_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- if menu.selected == 0 {
- actions.push(AppAction::Hardware(pages::hardware::HardwareMessage::SetGpuDefault));
- } else {
- actions.push(AppAction::Hardware(pages::hardware::HardwareMessage::SetGpuPowersave));
- }
- }
- }
-
- if state == cce_ui::widget::ElementState::Pressed && self.app.current_page == Page::Audio {
- if button == cce_ui::widget::MouseButton::Left {
- for (i, slider) in self.app.audio.sink_sliders.iter_mut().enumerate() {
- if slider.hit_test(lx, ly, &self.ui_context) {
- slider.drag_begin(lx, ly);
- self.audio_sink_dragging = Some(i);
- self.needs_rebuild = true;
- }
- }
- for (i, slider) in self.app.audio.source_sliders.iter_mut().enumerate() {
- if slider.hit_test(lx, ly, &self.ui_context) {
- slider.drag_begin(lx, ly);
- self.audio_source_dragging = Some(i);
- self.needs_rebuild = true;
- }
- }
- }
- for (i, sb) in self.app.audio.sink_spinboxes.iter_mut().enumerate() {
- 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 {
- 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.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 {
- let id = self.app.audio.sources[i].id;
- actions.push(AppAction::Audio(pages::audio::AudioMessage::SourceVolume(id, sb.value as f32 / 100.0)));
- }
- }
- }
- if state == cce_ui::widget::ElementState::Pressed && self.app.current_page == Page::Display {
- if button == cce_ui::widget::MouseButton::Left {
- let slider = &mut self.app.display.brightness_slider;
- if slider.hit_test(lx, ly, &self.ui_context) {
- slider.drag_begin(lx, ly);
- self.display_brightness_dragging = true;
- self.needs_rebuild = true;
- }
- }
- let sb = &mut self.app.display.brightness_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::Display(pages::display::DisplayMessage::BrightnessSet(sb.value as u32)));
- }
- let lbl = &mut self.app.display.night_light_label;
- if !lbl.hit_test(lx, ly, &self.ui_context) { lbl.unfocus(); }
- lbl.mouse_input(button, state, lx, ly, &mut self.ui_context);
-
- for out in &mut self.app.display.outputs {
- let lbl = &mut out.name_label;
- if !lbl.hit_test(lx, ly, &self.ui_context) { lbl.unfocus(); }
- lbl.mouse_input(button, state, lx, ly, &mut self.ui_context);
-
- let lbl2 = &mut out.resolution_label;
- if !lbl2.hit_test(lx, ly, &self.ui_context) { lbl2.unfocus(); }
- lbl2.mouse_input(button, state, lx, ly, &mut self.ui_context);
-
- if let Some(ref mut scale_lbl) = out.scale_label {
- if !scale_lbl.hit_test(lx, ly, &self.ui_context) { scale_lbl.unfocus(); }
- scale_lbl.mouse_input(button, state, lx, ly, &mut self.ui_context);
- }
- }
-
- let sb = &mut self.app.display.screensaver_timeout_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::Display(pages::display::DisplayMessage::SetScreensaverTimeout(sb.value)));
- }
- }
-
- if self.app.current_page == Page::Accounts {
- if self.app.accounts.editing_oauth_creds {
- let tb = &mut self.app.accounts.oauth_client_id_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
-
- let tb = &mut self.app.accounts.oauth_client_secret_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- } else if self.app.accounts.adding_new {
- let tb = &mut self.app.accounts.email_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && tb.take_change() {
- let email_val = tb.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();
- }
- }
-
- let tb = &mut self.app.accounts.password_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
-
- let tb = &mut self.app.accounts.imap_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
-
- let tb = &mut self.app.accounts.smtp_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- }
- }
- if self.app.current_page == Page::Interface {
- let tb = &mut self.app.interface.sans_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSans(tb.text.clone())));
- }
-
- let tb = &mut self.app.interface.serif_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSerif(tb.text.clone())));
- }
-
- let tb = &mut self.app.interface.mono_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetMono(tb.text.clone())));
- }
-
- let menu = &mut self.app.interface.borders_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBordersMenu(menu.selected)));
- }
-
- let tb = &mut self.app.interface.borders_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBorders(tb.text.clone())));
- }
-
- let menu = &mut self.app.interface.status_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatusMenu(menu.selected)));
- }
-
- let tb = &mut self.app.interface.status_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatus(tb.text.clone())));
- }
-
- let menu = &mut self.app.interface.fuzzel_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFuzzelMenu(menu.selected)));
- }
-
- let tb = &mut self.app.interface.fuzzel_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFuzzel(tb.text.clone())));
- }
-
- let menu = &mut self.app.interface.terminal_menu;
- if state == cce_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly, &self.ui_context) { menu.unfocus(); }
- if menu.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && menu.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTerminalMenu(menu.selected)));
- }
-
- let tb = &mut self.app.interface.terminal_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if state == cce_ui::widget::ElementState::Pressed && tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTerminal(tb.text.clone())));
- }
-
-
-
-
-
- let fs = &mut self.app.interface.color_selector_font_selector;
- if state == cce_ui::widget::ElementState::Pressed && !fs.hit_test(lx, ly, &self.ui_context) { fs.unfocus(); }
- if fs.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if fs.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetColorSelectorFont(fs.font_family.clone())));
- }
-
- let fs = &mut self.app.interface.menubar_font_selector;
- if state == cce_ui::widget::ElementState::Pressed && !fs.hit_test(lx, ly, &self.ui_context) { fs.unfocus(); }
- if fs.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if fs.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetMenubarFont(fs.font_family.clone())));
- }
-
- let fs = &mut self.app.interface.breadcrumb_font_selector;
- if state == cce_ui::widget::ElementState::Pressed && !fs.hit_test(lx, ly, &self.ui_context) { fs.unfocus(); }
- if fs.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if fs.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBreadcrumbFont(fs.font_family.clone())));
- }
-
- let fs = &mut self.app.interface.section_label_font_selector;
- if state == cce_ui::widget::ElementState::Pressed && !fs.hit_test(lx, ly, &self.ui_context) { fs.unfocus(); }
- if fs.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if fs.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSectionLabelFont(fs.font_family.clone())));
- }
-
- let fs = &mut self.app.interface.nested_section_label_font_selector;
- if state == cce_ui::widget::ElementState::Pressed && !fs.hit_test(lx, ly, &self.ui_context) { fs.unfocus(); }
- if fs.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if fs.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetNestedSectionLabelFont(fs.font_family.clone())));
- }
-
- let sb = &mut self.app.interface.borders_size_box;
- if state == cce_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly, &self.ui_context) { sb.unfocus(); }
- let old_val = sb.value;
- if sb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if sb.value != old_val {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBordersSize(sb.value)));
- }
-
- let sb = &mut self.app.interface.status_size_box;
- if state == cce_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly, &self.ui_context) { sb.unfocus(); }
- let old_val = sb.value;
- if sb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if sb.value != old_val {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatusSize(sb.value)));
- }
-
- let sb = &mut self.app.interface.fuzzel_size_box;
- if state == cce_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly, &self.ui_context) { sb.unfocus(); }
- let old_val = sb.value;
- if sb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if sb.value != old_val {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFuzzelSize(sb.value)));
- }
-
- let sb = &mut self.app.interface.terminal_size_box;
- if state == cce_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly, &self.ui_context) { sb.unfocus(); }
- let old_val = sb.value;
- if sb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if sb.value != old_val {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTerminalSize(sb.value)));
- }
-
-
-
-
- }
- if self.app.current_page == Page::Services {
- let tb = &mut self.app.services.search_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) { tb.unfocus(); }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- let srv = &mut self.app.services;
- if srv.list_box.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- let query = if srv.search_box.editing {
- srv.search_box.edit_buffer.to_lowercase()
- } else {
- srv.search_box.text.to_lowercase()
- };
- let matching_count = srv.services.iter()
- .filter(|s| s.is_system == (srv.active_tab == pages::services::ServiceTab::System))
- .filter(|s| s.name.to_lowercase().contains(&query) || s.description.to_lowercase().contains(&query))
- .count();
- for i in 0..matching_count.min(srv.service_items.len()) {
- if srv.service_items[i].mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- }
- }
- if self.app.current_page == Page::Packages {
- let pkgs = &mut self.app.packages;
- let tb = &mut pkgs.search_box;
- if state == cce_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly, &self.ui_context) {
- tb.unfocus();
- }
- if tb.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- match pkgs.active_tab {
- pages::packages::PackageTab::Installed => {
- if pkgs.installed_list_box.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- let query = if pkgs.search_box.editing {
- pkgs.search_box.edit_buffer.to_lowercase()
- } else {
- pkgs.search_box.text.to_lowercase()
- };
- let filtered: Vec<&pages::packages::PackageInfo> = pkgs.installed.iter()
- .filter(|p| p.name.to_lowercase().contains(&query) || p.version.to_lowercase().contains(&query))
- .collect();
- let matching = filtered.len();
- for i in 0..matching.min(pkgs.installed_items.len()) {
- if pkgs.installed_items[i].mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if pkgs.installed_items[i].take_click() {
- let pkg_name = filtered[i].name.clone();
- actions.push(AppAction::Packages(pages::packages::PackagesMessage::SelectPackage(Some(pkg_name))));
- }
- }
- }
- pages::packages::PackageTab::Updates => {
- if pkgs.updates_list_box.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- let query = if pkgs.search_box.editing {
- pkgs.search_box.edit_buffer.to_lowercase()
- } else {
- pkgs.search_box.text.to_lowercase()
- };
- let filtered: Vec<&pages::packages::UpdateInfo> = pkgs.updates.iter()
- .filter(|p| p.name.to_lowercase().contains(&query))
- .collect();
- let matching = filtered.len();
- for i in 0..matching.min(pkgs.updates_items.len()) {
- if pkgs.updates_items[i].mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- if pkgs.updates_items[i].take_click() {
- let pkg_name = filtered[i].name.clone();
- actions.push(AppAction::Packages(pages::packages::PackagesMessage::SelectPackage(Some(pkg_name))));
- }
- }
- }
- }
- }
- if state == cce_ui::widget::ElementState::Pressed && self.app.current_page == Page::Hardware {
- let hw = &mut self.app.hardware;
- if hw.cpu_list_box.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- }
- if state == cce_ui::widget::ElementState::Pressed && self.app.current_page == Page::Radios {
- let net = &mut self.app.network;
- if net.wifi_list_box.mouse_input(button, state, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- }
- net.wifi_toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if net.wifi_toggle.take_click() {
- actions.push(AppAction::Radios(pages::network::NetworkMessage::ToggleWifi));
- }
- net.bt_toggle.mouse_input(button, state, lx, ly, &mut self.ui_context);
- if net.bt_toggle.take_click() {
- actions.push(AppAction::Radios(pages::network::NetworkMessage::ToggleBluetooth));
- }
- }
- for a in &actions {
- self.handle_action(a);
- }
- if !actions.is_empty() {
- self.needs_rebuild = true;
- return true;
- }
- self.needs_rebuild = true;
- true
- }
-
- pub(crate) fn propagate_widget_changes(&mut self, actions: &mut Vec<AppAction>) {
- match self.app.current_page {
- 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::SetDesktopBackground(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::SetMenubarTabLabelColor(cp.color),
- 12 => pages::interface::InterfaceMessage::SetToggleEnabledColor(cp.color),
- 13 => pages::interface::InterfaceMessage::SetToggleDisabledColor(cp.color),
- 14 => pages::interface::InterfaceMessage::SetScrollingListBgColor(cp.color),
- 15 => pages::interface::InterfaceMessage::SetBreadcrumbBgColor(cp.color),
- 16 => pages::interface::InterfaceMessage::SetPopoverBgColor(cp.color),
- 17 => pages::interface::InterfaceMessage::SetNotificationBgColor(cp.color),
- 18 => pages::interface::InterfaceMessage::SetWindowColor(cp.color),
- 19 => pages::interface::InterfaceMessage::SetPageColor(cp.color),
- 20 => pages::interface::InterfaceMessage::SetLayerColor(cp.color),
- 21 => pages::interface::InterfaceMessage::SetScrollingListEntryBgColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
- 22 => pages::interface::InterfaceMessage::SetScrollingListEntryHighlightColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
- _ => pages::interface::InterfaceMessage::SetDesktopBackground(cp.color),
- }));
- }
- }
- if self.app.interface.notification_opacity_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetNotificationOpacity(self.app.interface.notification_opacity_spinbox.value as f32 / 100.0)));
- }
- 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.button_padding_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetButtonPadding(self.app.interface.button_padding_spinbox.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_cell_opacity_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphCellOpacity(self.app.interface.graph_cell_opacity_spinbox.value as f32 / 100.0)));
- }
- if self.app.interface.graph_gap_opacity_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphGapOpacity(self.app.interface.graph_gap_opacity_spinbox.value as f32 / 100.0)));
- }
- 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.spinbox_corner_radius_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSpinboxCornerRadius(self.app.interface.spinbox_corner_radius_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.toggle_corner_radius_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetToggleCornerRadius(self.app.interface.toggle_corner_radius_spinbox.value as u16)));
- }
- if self.app.interface.plate_opacity_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPlateOpacity(self.app.interface.plate_opacity_spinbox.value as f32 / 100.0)));
- }
- if self.app.interface.plate_corner_radius_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPlateCornerRadius(self.app.interface.plate_corner_radius_spinbox.value as u16)));
- }
- if self.app.interface.page_opacity_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPageOpacity(self.app.interface.page_opacity_spinbox.value as f32 / 100.0)));
- }
- if self.app.interface.layer_opacity_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetLayerOpacity(self.app.interface.layer_opacity_spinbox.value as f32 / 100.0)));
- }
-
-
- 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_corner_radius_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetColorSelectorCornerRadius(self.app.interface.color_selector_corner_radius_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.textbox_corner_radius_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTextboxCornerRadius(self.app.interface.textbox_corner_radius_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.font_selector_corner_radius_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFontSelectorCornerRadius(self.app.interface.font_selector_corner_radius_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.dropdown_corner_radius_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetDropdownCornerRadius(self.app.interface.dropdown_corner_radius_spinbox.value as u16)));
- }
- if self.app.interface.button_corner_radius_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetButtonCornerRadius(self.app.interface.button_corner_radius_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)));
- }
-
- let tf = &mut self.app.interface.windows;
- for (i, sb) in tf.spinboxes.iter_mut().enumerate() {
- if sb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetWidth(pages::interface::WidthParam::ALL[i], sb.value as u16))));
- }
- }
- if tf.cascade_offset_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetCascadeOffset(tf.cascade_offset_spinbox.value as u16))));
- }
- if tf.edge_gap_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetEdgeGap(tf.edge_gap_spinbox.value as u16))));
- }
- if tf.top_gap_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetTopGap(tf.top_gap_spinbox.value as u16))));
- }
- if tf.grid_gap_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetGridGap(tf.grid_gap_spinbox.value as u16))));
- }
- if tf.transition_duration_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetTransitionDuration(tf.transition_duration_spinbox.value as u16))));
- }
- if tf.status_height_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetStatusHeight(tf.status_height_spinbox.value as u16))));
- }
- for (idx, menu) in tf.tag_layout_menus.iter_mut().enumerate() {
- if menu.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetTagLayout(idx + 1, menu.selected))));
- }
- }
- if tf.side_panel_behavior_menu.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetSidePanelBehavior(tf.side_panel_behavior_menu.selected))));
- }
- if tf.side_panel_position_menu.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetSidePanelPosition(tf.side_panel_position_menu.selected))));
- }
- if tf.side_panel_width_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetSidePanelWidth(tf.side_panel_width_spinbox.value as u16))));
- }
- if tf.side_panel_border_gap_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetSidePanelBorderGap(tf.side_panel_border_gap_spinbox.value as u16))));
- }
- if tf.side_panel_border_opacity_spinbox.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetSidePanelBorderOpacity(tf.side_panel_border_opacity_spinbox.value as u16))));
- }
- }
- 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)));
- }
- if self.app.input.zoom_in_box.take_change() {
- actions.push(AppAction::Input(pages::input::InputMessage::ApplyZoomIn));
- }
- if self.app.input.zoom_out_box.take_change() {
- actions.push(AppAction::Input(pages::input::InputMessage::ApplyZoomOut));
- }
- if self.app.input.keybinds_control.take_change() {
- actions.push(AppAction::Input(pages::input::InputMessage::ReloadKeybinds));
- }
- }
- 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.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.adding_new && 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)));
- }
+ 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)));
}
- 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)));
- }
+ 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)));
}
- 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)));
- }
+ 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)));
}
- }
- Page::Radios => {
- let net = &mut self.app.network;
- if net.wifi_toggle.take_change() {
- actions.push(AppAction::Radios(pages::network::NetworkMessage::ToggleWifi));
+ 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 net.bt_toggle.take_change() {
- actions.push(AppAction::Radios(pages::network::NetworkMessage::ToggleBluetooth));
+ 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())));
}
- }
- _ => {}
- }
- }
-
- pub(crate) fn handle_mouse_wheel_internal(&mut self, delta: &cce_ui::widget::MouseScrollDelta, px: f32, py: f32) -> bool {
- if let Ok(mut file) = std::fs::OpenOptions::new().create(true).append(true).open("/tmp/cce-scroll-debug.log") {
- use std::io::Write;
- let _ = writeln!(file, "handle_mouse_wheel_internal: px={}, py={}, delta={:?}, sidebar_w={}", px, py, delta, self.sidebar_width);
- }
- let s = 1.0f32;
- if px >= self.sidebar_width * s {
- let lx = px / s;
- let ly = py / s + self.scroll_y;
-
- if self.app.current_page == Page::Input {
- let input = &self.app.input;
- if input.is_over_trackpad(lx, ly, &self.ui_context) {
- return true;
+ 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.current_page == Page::Audio {
- let mut actions = Vec::new();
- for (i, sb) in self.app.audio.sink_spinboxes.iter_mut().enumerate() {
- let old = sb.value;
- if sb.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb.value != old {
- let id = self.app.audio.sinks[i].id;
- actions.push(AppAction::Audio(pages::audio::AudioMessage::SinkVolume(id, sb.value as f32 / 100.0)));
- }
- }
+ if self.app.interface.graph_cell_opacity_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphCellOpacity(self.app.interface.graph_cell_opacity_spinbox.value as f32 / 100.0)));
}
- for (i, sb) in self.app.audio.source_spinboxes.iter_mut().enumerate() {
- let old = sb.value;
- if sb.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb.value != old {
- let id = self.app.audio.sources[i].id;
- actions.push(AppAction::Audio(pages::audio::AudioMessage::SourceVolume(id, sb.value as f32 / 100.0)));
- }
- }
+ if self.app.interface.graph_gap_opacity_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphGapOpacity(self.app.interface.graph_gap_opacity_spinbox.value as f32 / 100.0)));
}
- for (i, slider) in self.app.audio.sink_sliders.iter_mut().enumerate() {
- let old = slider.value();
- if slider.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- let new_val = slider.value();
- if new_val != old {
- let id = self.app.audio.sinks[i].id;
- if i < self.app.audio.sink_spinboxes.len() {
- self.app.audio.sink_spinboxes[i].value = (new_val * 100.0).round() as i32;
- }
- actions.push(AppAction::Audio(pages::audio::AudioMessage::SinkVolume(id, new_val)));
- }
- }
+ 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)));
}
- for (i, slider) in self.app.audio.source_sliders.iter_mut().enumerate() {
- let old = slider.value();
- if slider.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- let new_val = slider.value();
- if new_val != old {
- let id = self.app.audio.sources[i].id;
- if i < self.app.audio.source_spinboxes.len() {
- self.app.audio.source_spinboxes[i].value = (new_val * 100.0).round() as i32;
- }
- actions.push(AppAction::Audio(pages::audio::AudioMessage::SourceVolume(id, new_val)));
- }
- }
+ 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)));
}
- for a in &actions {
- self.handle_action(a);
+ 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 !actions.is_empty() {
- self.needs_rebuild = true;
- return true;
+ if self.app.interface.spinbox_corner_radius_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSpinboxCornerRadius(self.app.interface.spinbox_corner_radius_spinbox.value as u16)));
}
- }
-
- if self.app.current_page == Page::Display {
- let mut actions = Vec::new();
- let slider = &mut self.app.display.brightness_slider;
- let old_val = slider.value();
- if slider.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- let new_val = slider.value();
- if new_val != old_val {
- let pct = (new_val * 100.0).round() as u32;
- self.app.display.brightness_spinbox.value = pct as i32;
- actions.push(AppAction::Display(pages::display::DisplayMessage::BrightnessSet(pct)));
- }
+ 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)));
}
- let sb = &mut self.app.display.brightness_spinbox;
- let old = sb.value;
- if sb.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Display(pages::display::DisplayMessage::BrightnessSet(sb.value as u32)));
- }
+ if self.app.interface.toggle_corner_radius_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetToggleCornerRadius(self.app.interface.toggle_corner_radius_spinbox.value as u16)));
}
- let sb2 = &mut self.app.display.screensaver_timeout_spinbox;
- let old2 = sb2.value;
- if sb2.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb2.value != old2 {
- actions.push(AppAction::Display(pages::display::DisplayMessage::SetScreensaverTimeout(sb2.value)));
- }
+ if self.app.interface.plate_opacity_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPlateOpacity(self.app.interface.plate_opacity_spinbox.value as f32 / 100.0)));
}
- for a in &actions {
- self.handle_action(a);
+ if self.app.interface.plate_corner_radius_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPlateCornerRadius(self.app.interface.plate_corner_radius_spinbox.value as u16)));
}
- if !actions.is_empty() {
- self.needs_rebuild = true;
- return true;
+ if self.app.interface.page_opacity_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPageOpacity(self.app.interface.page_opacity_spinbox.value as f32 / 100.0)));
+ }
+ if self.app.interface.layer_opacity_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetLayerOpacity(self.app.interface.layer_opacity_spinbox.value as f32 / 100.0)));
}
- }
- if self.app.current_page == Page::Interface {
- let mut actions = Vec::new();
- let sb = &mut self.app.interface.graph_cell_opacity_spinbox;
- let old = sb.value;
- if sb.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphCellOpacity(sb.value as f32 / 100.0)));
- }
- }
- let sb2 = &mut self.app.interface.graph_gap_opacity_spinbox;
- let old2 = sb2.value;
- if sb2.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb2.value != old2 {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetGraphGapOpacity(sb2.value as f32 / 100.0)));
- }
- }
- let sb3 = &mut self.app.interface.window_opacity_spinbox;
- let old3 = sb3.value;
- if sb3.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb3.value != old3 {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetWindowOpacity(sb3.value as f32 / 100.0)));
- }
- }
- let sb4 = &mut self.app.interface.window_corner_radius_spinbox;
- let old4 = sb4.value;
- if sb4.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb4.value != old4 {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetWindowCornerRadius(sb4.value as u16)));
- }
- }
- let sb5 = &mut self.app.interface.plate_opacity_spinbox;
- let old5 = sb5.value;
- if sb5.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb5.value != old5 {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPlateOpacity(sb5.value as f32 / 100.0)));
- }
- }
- let sb6 = &mut self.app.interface.plate_corner_radius_spinbox;
- let old6 = sb6.value;
- if sb6.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb6.value != old6 {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPlateCornerRadius(sb6.value as u16)));
- }
- }
- let sb7 = &mut self.app.interface.page_opacity_spinbox;
- let old7 = sb7.value;
- if sb7.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb7.value != old7 {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetPageOpacity(sb7.value as f32 / 100.0)));
- }
- }
- let sb8 = &mut self.app.interface.layer_opacity_spinbox;
- let old8 = sb8.value;
- if sb8.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- if sb8.value != old8 {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetLayerOpacity(sb8.value as f32 / 100.0)));
- }
- }
- for a in &actions {
- self.handle_action(a);
+ 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 !actions.is_empty() {
- self.needs_rebuild = true;
- return true;
+ if self.app.interface.color_selector_corner_radius_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetColorSelectorCornerRadius(self.app.interface.color_selector_corner_radius_spinbox.value as u16)));
}
- }
-
- if self.app.current_page == Page::Services {
- let srv = &mut self.app.services;
- if srv.list_box.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
+ 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.current_page == Page::Packages {
- let pkgs = &mut self.app.packages;
- match pkgs.active_tab {
- pages::packages::PackageTab::Installed => {
- if pkgs.installed_list_box.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
- }
- }
- pages::packages::PackageTab::Updates => {
- if pkgs.updates_list_box.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
- }
- }
+ 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.current_page == Page::Hardware {
- let hw = &mut self.app.hardware;
- if hw.cpu_list_box.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
+ 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.current_page == Page::Radios {
- let net = &mut self.app.network;
- if net.wifi_list_box.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
+ if self.app.interface.textbox_corner_radius_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTextboxCornerRadius(self.app.interface.textbox_corner_radius_spinbox.value as u16)));
}
- }
-
- let scroll_speed = 24.0;
- let dy = match delta {
- cce_ui::widget::MouseScrollDelta::LineDelta(_, y) => -y * scroll_speed,
- cce_ui::widget::MouseScrollDelta::PixelDelta(pos) => -pos.y as f32,
- };
- let old_scroll = self.scroll_y;
- self.scroll_y = (self.scroll_y + dy).max(0.0).min(self.max_scroll_y);
- if (self.scroll_y - old_scroll).abs() > 0.01 {
- self.needs_rebuild = true;
- return true;
- }
- } else {
- let lx = px / s;
- let ly = py / s;
- if self.menubar.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
- }
- }
- false
- }
-
- pub(crate) fn get_page_root_widget(&mut self) -> Option<*mut (dyn cce_ui::widget::Element + 'static)> {
- 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 cce_ui::widget::Element as *mut dyn cce_ui::widget::Element;
- let static_ptr = unsafe {
- std::mem::transmute::<*mut dyn cce_ui::widget::Element, *mut (dyn cce_ui::widget::Element + 'static)>(ptr)
- };
- Some(static_ptr)
- }
-
- pub(crate) fn handle_key_input_internal(&mut self, event: &cce_ui::widget::KeyEvent) -> bool {
- if cce_ui::widget::context_menu::is_visible() {
- if event.state == cce_ui::widget::ElementState::Pressed
- && event.logical_key == cce_ui::widget::Key::Named(cce_ui::widget::NamedKey::Escape)
- {
- cce_ui::widget::context_menu::hide();
- self.needs_rebuild = true;
- return true;
- }
- }
-
- if event.state == cce_ui::widget::ElementState::Pressed && !event.repeat {
- let is_nav_key = match (&event.logical_key, event.ctrl) {
- (cce_ui::widget::Key::Character(c), true) if c == "j" || c == "J" || c == "k" || c == "K" || c == "u" || c == "U" || c == "i" || c == "I" => true,
- _ => false,
- };
- if is_nav_key {
- if cce_ui::widget::focus::has_focus() {
- if cce_ui::widget::focus::navigate_focus(&event.logical_key, event.ctrl) {
- self.needs_rebuild = true;
- return true;
- }
- } else {
- if let Some(root_ptr) = self.get_page_root_widget() {
- unsafe {
- let root_ref = &mut *root_ptr;
- cce_ui::widget::focus::set_focused(root_ref);
- root_ref.focus();
- self.needs_rebuild = true;
- return true;
- }
- }
+ 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.current_page == Page::Interface {
- let mc = &mut self.app.interface.custom_multicontrol;
- if mc.keyboard_input(event, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
- }
- let mut changed = false;
- let mut actions = Vec::new();
- for (i, cp) in self.app.interface.color_selectors.iter_mut().enumerate() {
- let old = cp.color;
- let old_alpha = cp.alpha;
- if cp.keyboard_input(event, &mut self.ui_context) {
- if cp.color != old || cp.alpha != old_alpha {
- 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::SetDesktopBackground(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::SetMenubarTabLabelColor(cp.color),
- 12 => pages::interface::InterfaceMessage::SetToggleEnabledColor(cp.color),
- 13 => pages::interface::InterfaceMessage::SetToggleDisabledColor(cp.color),
- 14 => pages::interface::InterfaceMessage::SetScrollingListBgColor(cp.color),
- 15 => pages::interface::InterfaceMessage::SetBreadcrumbBgColor(cp.color),
- 16 => pages::interface::InterfaceMessage::SetPopoverBgColor(cp.color),
- 17 => pages::interface::InterfaceMessage::SetNotificationBgColor(cp.color),
- 18 => pages::interface::InterfaceMessage::SetWindowColor(cp.color),
- 19 => pages::interface::InterfaceMessage::SetPageColor(cp.color),
- 20 => pages::interface::InterfaceMessage::SetLayerColor(cp.color),
- 21 => pages::interface::InterfaceMessage::SetScrollingListEntryBgColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
- 22 => pages::interface::InterfaceMessage::SetScrollingListEntryHighlightColor([cp.color[0], cp.color[1], cp.color[2], cp.alpha]),
- _ => pages::interface::InterfaceMessage::SetDesktopBackground(cp.color),
- }));
- }
- changed = true;
+ 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)));
}
- }
- for a in &actions {
- self.handle_action(a);
- }
- if changed {
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.notification_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::SetNotificationOpacity(new_val as f32 / 100.0)));
+ if self.app.interface.font_selector_corner_radius_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFontSelectorCornerRadius(self.app.interface.font_selector_corner_radius_spinbox.value as u16)));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.window_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::SetWindowOpacity(new_val as f32 / 100.0)));
+ 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)));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.window_corner_radius_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::SetWindowCornerRadius(new_val as u16)));
+ if self.app.interface.dropdown_corner_radius_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetDropdownCornerRadius(self.app.interface.dropdown_corner_radius_spinbox.value as u16)));
}
- 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)));
+ if self.app.interface.button_corner_radius_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetButtonCornerRadius(self.app.interface.button_corner_radius_spinbox.value as u16)));
}
- 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)));
+ if self.app.interface.sans_box.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSans(self.app.interface.sans_box.text.clone())));
}
- self.needs_rebuild = true;
- return true;
- }
-
- let sb = &mut self.app.interface.button_padding_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::SetButtonPadding(new_val as u16)));
+ if self.app.interface.serif_box.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSerif(self.app.interface.serif_box.text.clone())));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.section_padding_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::SetSectionPadding(new_val as u16)));
+ if self.app.interface.mono_box.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetMono(self.app.interface.mono_box.text.clone())));
}
- self.needs_rebuild = true;
- return true;
- }
- let (menu_changed, old_selected, new_selected) = {
- let menu = &mut self.app.interface.label_alignment_menu;
- let old = menu.selected;
- let changed = menu.keyboard_input(event, &mut self.ui_context);
- (changed, old, menu.selected)
- };
- if menu_changed {
- if new_selected != old_selected {
- self.handle_action(&AppAction::Interface(pages::interface::InterfaceMessage::SetNestedSectionLabelAlignment(new_selected)));
+ if self.app.interface.borders_menu.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBordersMenu(self.app.interface.borders_menu.selected)));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.label_offset_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::SetNestedSectionLabelOffset(new_val as i16)));
+ if self.app.interface.borders_box.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBorders(self.app.interface.borders_box.text.clone())));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.label_margin_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::SetLabelMargin(new_val as u16)));
+ if self.app.interface.status_menu.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatusMenu(self.app.interface.status_menu.selected)));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.plate_padding_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::SetPlatePadding(new_val as u16)));
+ if self.app.interface.status_box.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatus(self.app.interface.status_box.text.clone())));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.page_margin_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::SetPageMargin(new_val as u16)));
+ if self.app.interface.fuzzel_menu.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFuzzelMenu(self.app.interface.fuzzel_menu.selected)));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.grid_min_col_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::SetGridMinColWidth(new_val as u16)));
+ if self.app.interface.fuzzel_box.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFuzzel(self.app.interface.fuzzel_box.text.clone())));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.spinbox_height_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::SetSpinboxHeight(new_val as u16)));
+ if self.app.interface.terminal_menu.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTerminalMenu(self.app.interface.terminal_menu.selected)));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.spinbox_corner_radius_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::SetSpinboxCornerRadius(new_val as u16)));
+ if self.app.interface.terminal_box.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTerminal(self.app.interface.terminal_box.text.clone())));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.toggle_height_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::SetToggleHeight(new_val as u16)));
+ 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())));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.toggle_corner_radius_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::SetToggleCornerRadius(new_val as u16)));
+ 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())));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.plate_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::SetPlateOpacity(new_val as f32 / 100.0)));
+ 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())));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.plate_corner_radius_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::SetPlateCornerRadius(new_val as u16)));
+ 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())));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.page_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::SetPageOpacity(new_val as f32 / 100.0)));
+ if self.app.interface.borders_size_box.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBordersSize(self.app.interface.borders_size_box.value)));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.layer_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::SetLayerOpacity(new_val as f32 / 100.0)));
+ if self.app.interface.status_size_box.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatusSize(self.app.interface.status_size_box.value)));
}
- self.needs_rebuild = true;
- return true;
- }
-
- let sb = &mut self.app.interface.color_selector_height_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::SetColorSelectorHeight(new_val as u16)));
+ let tf = &mut self.app.interface.windows;
+ for (i, sb) in tf.spinboxes.iter_mut().enumerate() {
+ if sb.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetWidth(pages::interface::WidthParam::ALL[i], sb.value as u16))));
+ }
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.color_selector_corner_radius_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::SetColorSelectorCornerRadius(new_val as u16)));
+ if tf.cascade_offset_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetCascadeOffset(tf.cascade_offset_spinbox.value as u16))));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.color_selector_preview_corner_radius_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::SetColorSelectorPreviewCornerRadius(new_val as u16)));
+ if tf.edge_gap_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetEdgeGap(tf.edge_gap_spinbox.value as u16))));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.color_selector_preview_margin_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::SetColorSelectorPreviewMargin(new_val as u16)));
+ if tf.top_gap_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetTopGap(tf.top_gap_spinbox.value as u16))));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.textbox_height_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::SetTextboxHeight(new_val as u16)));
+ if tf.grid_gap_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetGridGap(tf.grid_gap_spinbox.value as u16))));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.textbox_corner_radius_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::SetTextboxCornerRadius(new_val as u16)));
+ if tf.transition_duration_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetTransitionDuration(tf.transition_duration_spinbox.value as u16))));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.slider_height_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::SetSliderHeight(new_val as u16)));
+ if tf.status_height_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetStatusHeight(tf.status_height_spinbox.value as u16))));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.font_selector_height_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::SetFontSelectorHeight(new_val as u16)));
+ for (idx, menu) in tf.tag_layout_menus.iter_mut().enumerate() {
+ if menu.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetTagLayout(idx + 1, menu.selected))));
+ }
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.font_selector_corner_radius_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::SetFontSelectorCornerRadius(new_val as u16)));
+ if tf.side_panel_behavior_menu.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetSidePanelBehavior(tf.side_panel_behavior_menu.selected))));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.dropdown_height_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::SetDropdownHeight(new_val as u16)));
+ if tf.side_panel_position_menu.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetSidePanelPosition(tf.side_panel_position_menu.selected))));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.dropdown_corner_radius_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::SetDropdownCornerRadius(new_val as u16)));
+ if tf.side_panel_width_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetSidePanelWidth(tf.side_panel_width_spinbox.value as u16))));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.interface.button_corner_radius_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::SetButtonCornerRadius(new_val as u16)));
+ if tf.side_panel_border_gap_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetSidePanelBorderGap(tf.side_panel_border_gap_spinbox.value as u16))));
}
- self.needs_rebuild = true;
- return true;
- }
-
-
-
- let mut actions = Vec::new();
- let mut consumed = false;
-
- let tf = &mut self.app.interface;
- let tb = &mut tf.sans_box;
- if tb.keyboard_input(event, &mut self.ui_context) {
- if tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSans(tb.text.clone())));
+ if tf.side_panel_border_opacity_spinbox.take_change() {
+ actions.push(AppAction::Interface(pages::interface::InterfaceMessage::Windows(pages::interface::WindowsMessage::SetSidePanelBorderOpacity(tf.side_panel_border_opacity_spinbox.value as u16))));
}
- consumed = true;
}
-
- let tb = &mut self.app.interface.serif_box;
- if tb.keyboard_input(event, &mut self.ui_context) {
- if tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetSerif(tb.text.clone())));
+ Page::Input => {
+ if self.app.input.rate_spinbox.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ApplyRepeat));
}
- consumed = true;
- }
-
- let tb = &mut self.app.interface.mono_box;
- if tb.keyboard_input(event, &mut self.ui_context) {
- if tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetMono(tb.text.clone())));
+ if self.app.input.delay_spinbox.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ApplyRepeat));
}
- consumed = true;
- }
-
- let tb = &mut self.app.interface.borders_box;
- if tb.keyboard_input(event, &mut self.ui_context) {
- if tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBorders(tb.text.clone())));
+ if self.app.input.scroll_friction_spinbox.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ApplyScrollFriction));
}
- consumed = true;
- }
-
- let tb = &mut self.app.interface.status_box;
- if tb.keyboard_input(event, &mut self.ui_context) {
- if tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatus(tb.text.clone())));
+ if self.app.input.scroll_speed_spinbox.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ApplyScrollSpeed));
}
- consumed = true;
- }
-
- let tb = &mut self.app.interface.fuzzel_box;
- if tb.keyboard_input(event, &mut self.ui_context) {
- if tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFuzzel(tb.text.clone())));
+ if self.app.input.pointer_friction_spinbox.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ApplyPointerFriction));
}
- consumed = true;
- }
-
- let tb = &mut self.app.interface.terminal_box;
- if tb.keyboard_input(event, &mut self.ui_context) {
- if tb.take_change() {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTerminal(tb.text.clone())));
+ if self.app.input.trackpad_friction_spinbox.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ApplyTrackpadFriction));
}
- consumed = true;
- }
-
-
-
-
-
- let sb = &mut self.app.interface.borders_size_box;
- if sb.keyboard_input(event, &mut self.ui_context) {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetBordersSize(sb.value)));
- consumed = true;
- }
-
- let sb = &mut self.app.interface.status_size_box;
- if sb.keyboard_input(event, &mut self.ui_context) {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetStatusSize(sb.value)));
- consumed = true;
- }
-
- let sb = &mut self.app.interface.fuzzel_size_box;
- if sb.keyboard_input(event, &mut self.ui_context) {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetFuzzelSize(sb.value)));
- consumed = true;
- }
-
- let sb = &mut self.app.interface.terminal_size_box;
- if sb.keyboard_input(event, &mut self.ui_context) {
- actions.push(AppAction::Interface(pages::interface::InterfaceMessage::SetTerminalSize(sb.value)));
- consumed = true;
- }
-
-
-
- for a in &actions {
- self.handle_action(a);
- }
- if consumed {
- self.needs_rebuild = true;
- return true;
- }
-
- let mut changed = false;
- let mut actions = Vec::new();
- {
- let tf = &mut self.app.interface.windows;
- for (i, sb) in tf.spinboxes.iter_mut().enumerate() {
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetWidth(
- pages::interface::WidthParam::ALL[i],
- sb.value as u16,
- )
- )
- ));
- }
- changed = true;
- }
+ 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));
}
- let sb = &mut tf.cascade_offset_spinbox;
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetCascadeOffset(sb.value as u16)
- )
- ));
- }
- changed = true;
+ if self.app.input.trackpad_toggle.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ToggleInertialTrackpad));
}
- let sb = &mut tf.edge_gap_spinbox;
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetEdgeGap(sb.value as u16)
- )
- ));
- }
- changed = true;
+ if self.app.input.dwtp_toggle.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ToggleDwtp));
}
- let sb = &mut tf.top_gap_spinbox;
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetTopGap(sb.value as u16)
- )
- ));
- }
- changed = true;
+ 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)));
}
- let sb = &mut tf.grid_gap_spinbox;
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetGridGap(sb.value as u16)
- )
- ));
- }
- changed = true;
+ if self.app.input.cursor_theme_menu.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ApplyCursorTheme(self.app.input.cursor_theme_menu.selected)));
}
- let sb = &mut tf.transition_duration_spinbox;
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetTransitionDuration(sb.value as u16)
- )
- ));
- }
- changed = true;
+ if self.app.input.zoom_in_box.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ApplyZoomIn));
}
- let sb = &mut tf.status_height_spinbox;
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetStatusHeight(sb.value as u16)
- )
- ));
- }
- changed = true;
+ if self.app.input.zoom_out_box.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ApplyZoomOut));
}
- let (menu_changed, old_selected, new_selected) = {
- let menu = &mut tf.side_panel_behavior_menu;
- let old = menu.selected;
- let changed = menu.keyboard_input(event, &mut self.ui_context);
- (changed, old, menu.selected)
- };
- if menu_changed {
- if new_selected != old_selected {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetSidePanelBehavior(new_selected)
- )
- ));
- }
- changed = true;
+ if self.app.input.keybinds_control.take_change() {
+ actions.push(AppAction::Input(pages::input::InputMessage::ReloadKeybinds));
}
- let (menu_changed, old_selected, new_selected) = {
- let menu = &mut tf.side_panel_position_menu;
- let old = menu.selected;
- let changed = menu.keyboard_input(event, &mut self.ui_context);
- (changed, old, menu.selected)
- };
- if menu_changed {
- if new_selected != old_selected {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetSidePanelPosition(new_selected)
- )
- ));
- }
- changed = true;
+ }
+ Page::Display => {
+ if self.app.display.screensaver_enable_toggle.take_change() {
+ actions.push(AppAction::Display(pages::display::DisplayMessage::ToggleScreensaverEnable));
}
- let sb = &mut tf.side_panel_width_spinbox;
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetSidePanelWidth(sb.value as u16)
- )
- ));
- }
- changed = true;
+ if self.app.display.screensaver_lock_screen_toggle.take_change() {
+ actions.push(AppAction::Display(pages::display::DisplayMessage::ToggleScreensaverLockScreen));
}
- let sb = &mut tf.side_panel_border_gap_spinbox;
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetSidePanelBorderGap(sb.value as u16)
- )
- ));
- }
- changed = true;
+ if self.app.display.screensaver_style_menu.take_change() {
+ actions.push(AppAction::Display(pages::display::DisplayMessage::SetScreensaverStyle(self.app.display.screensaver_style_menu.selected)));
}
- let sb = &mut tf.side_panel_border_opacity_spinbox;
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
- actions.push(AppAction::Interface(
- pages::interface::InterfaceMessage::Windows(
- pages::interface::WindowsMessage::SetSidePanelBorderOpacity(sb.value as u16)
- )
- ));
- }
- changed = true;
+ if self.app.display.brightness_spinbox.take_change() {
+ actions.push(AppAction::Display(pages::display::DisplayMessage::BrightnessSet(self.app.display.brightness_spinbox.value as u32)));
}
- }
- for a in &actions {
- self.handle_action(a);
- }
- if changed {
- self.needs_rebuild = true;
- return true;
- }
- }
- if self.app.current_page == Page::Services {
- let sb = &mut self.app.services.notifications_duration_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::Services(pages::services::ServicesMessage::SetNotificationsDuration(new_val)));
+ 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)));
}
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.services.status_padding_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::Services(pages::services::ServicesMessage::StatusSetPadding(new_val as u16)));
+ if self.app.display.screensaver_timeout_spinbox.take_change() {
+ actions.push(AppAction::Display(pages::display::DisplayMessage::SetScreensaverTimeout(self.app.display.screensaver_timeout_spinbox.value)));
}
- self.needs_rebuild = true;
- return true;
- }
- }
- if self.app.current_page == Page::Input {
- if self.app.input.rate_spinbox.keyboard_input(event, &mut self.ui_context) {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ApplyRepeat));
- self.needs_rebuild = true;
- return true;
- }
- if self.app.input.delay_spinbox.keyboard_input(event, &mut self.ui_context) {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ApplyRepeat));
- self.needs_rebuild = true;
- return true;
- }
- if self.app.input.scroll_friction_spinbox.keyboard_input(event, &mut self.ui_context) {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ApplyScrollFriction));
- self.needs_rebuild = true;
- return true;
- }
- if self.app.input.scroll_speed_spinbox.keyboard_input(event, &mut self.ui_context) {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ApplyScrollSpeed));
- self.needs_rebuild = true;
- return true;
- }
- if self.app.input.pointer_friction_spinbox.keyboard_input(event, &mut self.ui_context) {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ApplyPointerFriction));
- self.needs_rebuild = true;
- return true;
- }
- if self.app.input.trackpad_friction_spinbox.keyboard_input(event, &mut self.ui_context) {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ApplyTrackpadFriction));
- self.needs_rebuild = true;
- return true;
- }
- if self.app.input.trackpoint_accel_speed_spinbox.keyboard_input(event, &mut self.ui_context) {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ApplyTrackpointAccelSpeed));
- self.needs_rebuild = true;
- return true;
- }
- if self.app.input.cursor_size_spinbox.keyboard_input(event, &mut self.ui_context) {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ApplyCursorSize));
- self.needs_rebuild = true;
- return true;
- }
- if self.app.input.zoom_in_box.keyboard_input(event, &mut self.ui_context) {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ApplyZoomIn));
- self.needs_rebuild = true;
- return true;
- }
- if self.app.input.zoom_out_box.keyboard_input(event, &mut self.ui_context) {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ApplyZoomOut));
- self.needs_rebuild = true;
- return true;
}
- if self.app.input.keybinds_control.keyboard_input(event, &mut self.ui_context) {
- if self.app.input.keybinds_control.take_change() {
- self.handle_action(&AppAction::Input(pages::input::InputMessage::ReloadKeybinds));
+ Page::Processes => {
+ if self.app.processes.cpu_gov_menu.take_change() {
+ if self.app.processes.cpu_gov_menu.selected == 0 {
+ actions.push(AppAction::Processes(pages::processes::ProcessesMessage::SetCpuPerformance));
+ } else {
+ actions.push(AppAction::Processes(pages::processes::ProcessesMessage::SetCpuPowersave));
+ }
+ }
+ if self.app.processes.gpu_gov_menu.take_change() {
+ if self.app.processes.gpu_gov_menu.selected == 0 {
+ actions.push(AppAction::Processes(pages::processes::ProcessesMessage::SetGpuDefault));
+ } else {
+ actions.push(AppAction::Processes(pages::processes::ProcessesMessage::SetGpuPowersave));
+ }
}
- self.needs_rebuild = true;
- return true;
}
- }
- if self.app.current_page == Page::Accounts {
- let mut consumed = false;
- if self.app.accounts.editing_oauth_creds {
- let tb = &mut self.app.accounts.oauth_client_id_box;
- if tb.keyboard_input(event, &mut self.ui_context) { consumed = true; }
- let tb = &mut self.app.accounts.oauth_client_secret_box;
- if tb.keyboard_input(event, &mut self.ui_context) { consumed = true; }
- } else if self.app.accounts.adding_new {
- let tb = &mut self.app.accounts.email_box;
- if tb.keyboard_input(event, &mut self.ui_context) {
- consumed = true;
- let email_val = tb.edit_buffer.trim().to_lowercase();
+ Page::Accounts => {
+ if self.app.accounts.adding_new && 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();
@@ -3429,65 +567,73 @@ impl SystemInterface {
self.app.accounts.smtp_box.edit_buffer = "smtp.office365.com:587".to_string();
}
}
- let tb = &mut self.app.accounts.password_box;
- if tb.keyboard_input(event, &mut self.ui_context) { consumed = true; }
- let tb = &mut self.app.accounts.imap_box;
- if tb.keyboard_input(event, &mut self.ui_context) { consumed = true; }
- let tb = &mut self.app.accounts.smtp_box;
- if tb.keyboard_input(event, &mut self.ui_context) { consumed = true; }
- }
-
- if consumed {
- self.needs_rebuild = true;
- return true;
}
- }
- if self.app.current_page == Page::Audio {
- let mut actions = Vec::new();
- for (i, sb) in self.app.audio.sink_spinboxes.iter_mut().enumerate() {
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
+ 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() {
- let old = sb.value;
- if sb.keyboard_input(event, &mut self.ui_context) {
- if sb.value != old {
+ 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() {
- let old = slider.value();
- if slider.keyboard_input(event, &mut self.ui_context) {
- let new_val = slider.value();
- if new_val != old {
+ for (i, slider) in self.app.audio.sink_sliders.iter_mut().enumerate() {
+ if slider.take_change() {
let id = self.app.audio.sinks[i].id;
- if i < self.app.audio.sink_spinboxes.len() {
- self.app.audio.sink_spinboxes[i].value = (new_val * 100.0).round() as i32;
- }
- actions.push(AppAction::Audio(pages::audio::AudioMessage::SinkVolume(id, new_val)));
+ 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() {
- let old = slider.value();
- if slider.keyboard_input(event, &mut self.ui_context) {
- let new_val = slider.value();
- if new_val != old {
+ for (i, slider) in self.app.audio.source_sliders.iter_mut().enumerate() {
+ if slider.take_change() {
let id = self.app.audio.sources[i].id;
- if i < self.app.audio.source_spinboxes.len() {
- self.app.audio.source_spinboxes[i].value = (new_val * 100.0).round() as i32;
- }
- actions.push(AppAction::Audio(pages::audio::AudioMessage::SourceVolume(id, new_val)));
+ actions.push(AppAction::Audio(pages::audio::AudioMessage::SourceVolume(id, slider.value() as f32 / 100.0)));
}
}
}
+ Page::Radios => {
+ let net = &mut self.app.network;
+ if net.wifi_toggle.take_change() {
+ actions.push(AppAction::Radios(pages::network::NetworkMessage::ToggleWifi));
+ }
+ if net.bt_toggle.take_change() {
+ actions.push(AppAction::Radios(pages::network::NetworkMessage::ToggleBluetooth));
+ }
+ }
+ _ => {}
+ }
+ }
+
+ pub(crate) fn handle_mouse_wheel_internal(&mut self, delta: &cce_ui::widget::MouseScrollDelta, px: f32, py: f32) -> bool {
+ if let Ok(mut file) = std::fs::OpenOptions::new().create(true).append(true).open("/tmp/cce-scroll-debug.log") {
+ use std::io::Write;
+ let _ = writeln!(file, "handle_mouse_wheel_internal: px={}, py={}, delta={:?}, sidebar_w={}", px, py, delta, self.sidebar_width);
+ }
+ let s = 1.0f32;
+ if px >= self.sidebar_width * s {
+ let lx = px / s;
+ let ly = py / s + self.scroll_y;
+
+ if self.app.current_page == Page::Input {
+ let input = &self.app.input;
+ if input.is_over_trackpad(lx, ly, &self.ui_context) {
+ return true;
+ }
+ }
+
+ let event = cce_ui::widget::Event::MouseWheel { delta: delta.clone(), x: lx, y: ly };
+ let mut handled = false;
+ if let Some(root) = self.get_page_root_widget() {
+ if self.ui_context.propagate_event(&event, root) {
+ handled = true;
+ }
+ }
+
+ let mut actions = Vec::new();
+ self.propagate_widget_changes(&mut actions);
for a in &actions {
self.handle_action(a);
}
@@ -3495,142 +641,92 @@ impl SystemInterface {
self.needs_rebuild = true;
return true;
}
- }
- if self.app.current_page == Page::Display {
- let slider = &mut self.app.display.brightness_slider;
- let old_slider = slider.value();
- if slider.keyboard_input(event, &mut self.ui_context) {
- let new_slider = slider.value();
- if new_slider != old_slider {
- let pct = (new_slider * 100.0).round() as u32;
- self.app.display.brightness_spinbox.value = pct as i32;
- self.handle_action(&AppAction::Display(pages::display::DisplayMessage::BrightnessSet(pct)));
- }
- self.needs_rebuild = true;
- return true;
- }
- let sb = &mut self.app.display.brightness_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::Display(pages::display::DisplayMessage::BrightnessSet(new_val as u32)));
- }
+ if handled {
self.needs_rebuild = true;
return true;
}
- let sb = &mut self.app.display.screensaver_timeout_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::Display(pages::display::DisplayMessage::SetScreensaverTimeout(new_val)));
- }
+
+ let scroll_speed = 24.0;
+ let dy = match delta {
+ cce_ui::widget::MouseScrollDelta::LineDelta(_, y) => -y * scroll_speed,
+ cce_ui::widget::MouseScrollDelta::PixelDelta(pos) => -pos.y as f32,
+ };
+ let old_scroll = self.scroll_y;
+ self.scroll_y = (self.scroll_y + dy).max(0.0).min(self.max_scroll_y);
+ if (self.scroll_y - old_scroll).abs() > 0.01 {
self.needs_rebuild = true;
return true;
}
- let (menu_changed, old_selected, new_selected) = {
- let menu = &mut self.app.display.screensaver_style_menu;
- let old = menu.selected;
- let changed = menu.keyboard_input(event, &mut self.ui_context);
- (changed, old, menu.selected)
- };
- if menu_changed {
- if new_selected != old_selected {
- self.handle_action(&AppAction::Display(pages::display::DisplayMessage::SetScreensaverStyle(new_selected)));
- }
+ } else {
+ let lx = px / s;
+ let ly = py / s;
+ if self.menubar.mouse_wheel(delta, lx, ly, &mut self.ui_context) {
self.needs_rebuild = true;
return true;
}
}
+ false
+ }
+ pub(crate) fn get_page_root_widget(&mut self) -> Option<*mut (dyn cce_ui::widget::Element + 'static)> {
+ 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 cce_ui::widget::Element as *mut dyn cce_ui::widget::Element;
+ let static_ptr = unsafe {
+ std::mem::transmute::<*mut dyn cce_ui::widget::Element, *mut (dyn cce_ui::widget::Element + 'static)>(ptr)
+ };
+ Some(static_ptr)
+ }
- if self.app.current_page == Page::Services {
- let srv = &mut self.app.services;
- if srv.list_box.keyboard_input(event, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
- }
- let tb = &mut srv.search_box;
- if tb.keyboard_input(event, &mut self.ui_context) {
- tb.take_change();
+ pub(crate) fn handle_key_input_internal(&mut self, event: &cce_ui::widget::KeyEvent) -> bool {
+ if cce_ui::widget::context_menu::is_visible() {
+ if event.state == cce_ui::widget::ElementState::Pressed
+ && event.logical_key == cce_ui::widget::Key::Named(cce_ui::widget::NamedKey::Escape)
+ {
+ cce_ui::widget::context_menu::hide();
self.needs_rebuild = true;
return true;
}
}
- if self.app.current_page == Page::Packages {
- let pkgs = &mut self.app.packages;
- match pkgs.active_tab {
- pages::packages::PackageTab::Installed => {
- if pkgs.installed_list_box.keyboard_input(event, &mut self.ui_context) {
+
+ if event.state == cce_ui::widget::ElementState::Pressed && !event.repeat {
+ let is_nav_key = match (&event.logical_key, event.ctrl) {
+ (cce_ui::widget::Key::Character(c), true) if c == "j" || c == "J" || c == "k" || c == "K" || c == "u" || c == "U" || c == "i" || c == "I" => true,
+ _ => false,
+ };
+ if is_nav_key {
+ if cce_ui::widget::focus::has_focus() {
+ if cce_ui::widget::focus::navigate_focus(&event.logical_key, event.ctrl) {
self.needs_rebuild = true;
return true;
}
- }
- pages::packages::PackageTab::Updates => {
- if pkgs.updates_list_box.keyboard_input(event, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
+ } else {
+ if let Some(root_ptr) = self.get_page_root_widget() {
+ unsafe {
+ let root_ref = &mut *root_ptr;
+ cce_ui::widget::focus::set_focused(root_ref);
+ root_ref.focus();
+ self.needs_rebuild = true;
+ return true;
+ }
}
}
}
- let tb = &mut pkgs.search_box;
- if tb.keyboard_input(event, &mut self.ui_context) {
- tb.take_change();
- self.needs_rebuild = true;
- return true;
- }
}
- if self.app.current_page == Page::Hardware {
- let hw = &mut self.app.hardware;
- if hw.cpu_list_box.keyboard_input(event, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
- }
- let (cpu_changed, old_cpu, new_cpu) = {
- let menu = &mut hw.cpu_gov_menu;
- let old = menu.selected;
- let changed = menu.keyboard_input(event, &mut self.ui_context);
- (changed, old, menu.selected)
- };
- if cpu_changed {
- if new_cpu != old_cpu {
- if new_cpu == 0 {
- self.handle_action(&AppAction::Hardware(pages::hardware::HardwareMessage::SetCpuPerformance));
- } else {
- self.handle_action(&AppAction::Hardware(pages::hardware::HardwareMessage::SetCpuPowersave));
- }
- }
- self.needs_rebuild = true;
- return true;
- }
- let (gpu_changed, old_gpu, new_gpu) = {
- let menu = &mut hw.gpu_gov_menu;
- let old = menu.selected;
- let changed = menu.keyboard_input(event, &mut self.ui_context);
- (changed, old, menu.selected)
- };
- if gpu_changed {
- if new_gpu != old_gpu {
- if new_gpu == 0 {
- self.handle_action(&AppAction::Hardware(pages::hardware::HardwareMessage::SetGpuDefault));
- } else {
- self.handle_action(&AppAction::Hardware(pages::hardware::HardwareMessage::SetGpuPowersave));
- }
+
+ let event_wrapper = cce_ui::widget::Event::KeyInput(event.clone());
+ if let Some(root) = self.get_page_root_widget() {
+ if self.ui_context.propagate_event(&event_wrapper, root) {
+ let mut actions = Vec::new();
+ self.propagate_widget_changes(&mut actions);
+ for a in actions {
+ self.handle_action(&a);
}
self.needs_rebuild = true;
return true;
}
}
- if self.app.current_page == Page::Radios {
- let net = &mut self.app.network;
- if net.wifi_list_box.keyboard_input(event, &mut self.ui_context) {
- self.needs_rebuild = true;
- return true;
- }
- }
+
false
}
-
}
diff --git a/src/main.rs b/src/main.rs
index 2aca62c..f3b9e2d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -138,13 +138,13 @@ struct SystemInterface {
rx_wm_events: std::sync::mpsc::Receiver<()>,
rx_input: std::sync::mpsc::Receiver<pages::input::InputState>,
rx_fingers: std::sync::mpsc::Receiver<Vec<Finger>>,
- rx_hardware: std::sync::mpsc::Receiver<pages::hardware::HardwareState>,
+ rx_processes: std::sync::mpsc::Receiver<pages::processes::ProcessesState>,
rx_system: std::sync::mpsc::Receiver<pages::system_info::SystemState>,
- rx_status: std::sync::mpsc::Receiver<pages::services::StatusData>,
+ rx_status: std::sync::mpsc::Receiver<pages::processes::StatusData>,
rx_storage: std::sync::mpsc::Receiver<pages::storage::StorageState>,
- rx_notifications: std::sync::mpsc::Receiver<pages::services::NotificationsConfig>,
+ rx_notifications: std::sync::mpsc::Receiver<pages::processes::NotificationsConfig>,
rx_typeface: std::sync::mpsc::Receiver<pages::interface::InterfaceState>,
- rx_services: std::sync::mpsc::Receiver<Vec<pages::services::ServiceInfo>>,
+ rx_services: std::sync::mpsc::Receiver<Vec<pages::processes::ServiceInfo>>,
rx_interface: std::sync::mpsc::Receiver<pages::interface::InterfaceState>,
rx_accounts: std::sync::mpsc::Receiver<Vec<pages::accounts::AccountInfo>>,
tx_backup: std::sync::mpsc::Sender<pages::storage::StorageMessage>,
@@ -237,7 +237,7 @@ impl cce_ui::engine::Application for SystemInterface {
rx_wm_events: watchers.rx_wm_events,
rx_input: watchers.rx_input,
rx_fingers: watchers.rx_fingers,
- rx_hardware: watchers.rx_hardware,
+ rx_processes: watchers.rx_processes,
rx_system: watchers.rx_system,
rx_status: watchers.rx_status,
rx_storage: watchers.rx_storage,
@@ -320,14 +320,15 @@ impl cce_ui::engine::Application for SystemInterface {
*needs_rebuild = true;
self.needs_rebuild = true;
}
- if self.needs_rebuild {
+ if self.needs_rebuild || self.ui_context.is_dirty() {
*needs_rebuild = true;
+ self.needs_rebuild = true;
}
}
fn view(&mut self, _quads: &mut Vec<(f32, f32, f32, f32, [f32; 4])>, size: cce_ui::engine::LogicalSize, scale: f64) {
let (width, height) = (size.width, size.height);
- if self.needs_rebuild || self.width != width as u32 || self.height != height as u32 || self.scale_factor != scale {
+ if self.needs_rebuild || self.ui_context.is_dirty() || self.width != width as u32 || self.height != height as u32 || self.scale_factor != scale {
self.width = width as u32;
self.height = height as u32;
self.scale_factor = scale;
@@ -338,7 +339,7 @@ impl cce_ui::engine::Application for SystemInterface {
fn view_rounded_quads(&mut self, quads: &mut Vec<(f32, f32, f32, f32, f32, [f32; 4], (bool, bool, bool, bool))>, size: cce_ui::engine::LogicalSize, scale: f64) {
let (width, height) = (size.width, size.height);
- if self.needs_rebuild || self.width != width as u32 || self.height != height as u32 || self.scale_factor != scale {
+ if self.needs_rebuild || self.ui_context.is_dirty() || self.width != width as u32 || self.height != height as u32 || self.scale_factor != scale {
self.width = width as u32;
self.height = height as u32;
self.scale_factor = scale;
@@ -611,15 +612,15 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
self.needs_rebuild = true;
}
}
- while let Ok(s) = self.rx_hardware.try_recv() {
- hardware::update(&mut self.app.hardware, hardware::HardwareMessage::Refreshed(s));
- if self.app.current_page == Page::Hardware {
+ while let Ok(s) = self.rx_processes.try_recv() {
+ processes::update(&mut self.app.processes, processes::ProcessesMessage::Refreshed(s));
+ if self.app.current_page == Page::Processes {
self.needs_rebuild = true;
}
}
while let Ok(s) = self.rx_status.try_recv() {
- services::update(&mut self.app.services, services::ServicesMessage::StatusRefreshed(s));
- if self.app.current_page == Page::Services {
+ processes::update(&mut self.app.processes, processes::ProcessesMessage::StatusRefreshed(s));
+ if self.app.current_page == Page::Processes {
self.needs_rebuild = true;
}
}
@@ -630,8 +631,8 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
}
}
while let Ok(s) = self.rx_notifications.try_recv() {
- services::update(&mut self.app.services, services::ServicesMessage::NotificationsRefreshed(s));
- if self.app.current_page == Page::Services {
+ processes::update(&mut self.app.processes, processes::ProcessesMessage::NotificationsRefreshed(s));
+ if self.app.current_page == Page::Processes {
self.needs_rebuild = true;
}
}
@@ -645,8 +646,8 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
}
}
while let Ok(s) = self.rx_services.try_recv() {
- pages::services::update(&mut self.app.services, pages::services::ServicesMessage::Refreshed(s));
- if self.app.current_page == Page::Services {
+ processes::update(&mut self.app.processes, processes::ProcessesMessage::ServicesRefreshed(s));
+ if self.app.current_page == Page::Processes {
self.needs_rebuild = true;
}
}
@@ -691,7 +692,7 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
AppAction::Radios(m) => network::update(&mut self.app.network, m.clone()),
AppAction::Input(m) => input::update(&mut self.app.input, m.clone()),
AppAction::SystemInfo(m) => system_info::update(&mut self.app.system_info, m.clone()),
- AppAction::Hardware(m) => hardware::update(&mut self.app.hardware, m.clone()),
+ AppAction::Processes(m) => processes::update(&mut self.app.processes, m.clone()),
AppAction::Storage(m) => match m {
pages::storage::StorageMessage::StartBackup => {
pages::storage::update(&mut self.app.storage, pages::storage::StorageMessage::StartBackup);
@@ -704,7 +705,7 @@ fn collect_popover_rects(w: &dyn cce_ui::widget::Element, popovers: &mut Vec<(f3
_ => pages::storage::update(&mut self.app.storage, m.clone()),
},
- AppAction::Services(m) => services::update(&mut self.app.services, m.clone()),
+
AppAction::Interface(m) => {
interface::update(&mut self.app.interface, m.clone());
self.sans_serif_family = self.app.interface.sans_serif.clone();
diff --git a/src/pages/hardware.rs b/src/pages/hardware.rs
deleted file mode 100644
index 1413639..0000000
--- a/src/pages/hardware.rs
+++ /dev/null
@@ -1,637 +0,0 @@
-use crate::app::{AppAction, PageContent};
-use cce_ui::layout::{render_widget, PageLayoutBuilder, LayoutStrategy};
-use cce_ui::widget::{Label, ScrollingList, Dropdown, InfoBox};
-
-#[derive(Debug, Clone, Default)]
-pub struct BatteryInfo {
- pub percentage: f32,
- pub state: String,
- pub energy: f64,
- pub energy_full: f64,
- pub energy_rate: f64,
- pub time_to_empty: i64,
- pub time_to_full: i64,
- pub vendor: String,
- pub model: String,
-}
-
-#[derive(Debug, Clone)]
-pub struct HardwareState {
- pub cpu_model: String,
- pub cpu_usage: f32,
- pub cpu_cores: u32,
- pub gpus: Vec<String>,
- pub loaded: bool,
- pub cpu_label: Label,
- pub cpu_usage_label: Label,
- pub cpu_temp_label: Label,
- pub gpu_labels: Vec<Label>,
- pub processes: Vec<(String, String, String)>, // (pid, cpu, comm)
- pub cpu_list_box: ScrollingList,
-
- // Power-related fields
- pub battery: BatteryInfo,
- pub on_ac: bool,
- pub cpu_powersave: bool,
- pub gpu_powersave: bool,
- pub cpu_gov_menu: Dropdown,
- pub gpu_gov_menu: Dropdown,
-}
-
-impl Default for HardwareState {
- fn default() -> Self {
- Self {
- cpu_model: String::new(),
- cpu_usage: 0.0,
- cpu_cores: 0,
- gpus: Vec::new(),
- loaded: false,
- cpu_label: Label::new("CPU Info"),
- cpu_usage_label: Label::new("CPU Usage"),
- cpu_temp_label: Label::new("CPU Temp"),
- gpu_labels: Vec::new(),
- processes: Vec::new(),
- cpu_list_box: ScrollingList::new(24.0, 2.0),
-
- battery: BatteryInfo::default(),
- on_ac: true,
- cpu_powersave: false,
- gpu_powersave: false,
- cpu_gov_menu: Dropdown::new(
- vec!["Performance".to_string(), "Powersave".to_string()],
- 0,
- ).with_label("CPU Governor"),
- gpu_gov_menu: Dropdown::new(
- vec!["Default (80W)".to_string(), "Eco Cap (5W)".to_string()],
- 0,
- ).with_label("GPU Power Limit"),
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub enum HardwareMessage {
- Refreshed(HardwareState),
- SetCpuPerformance,
- SetCpuPowersave,
- SetGpuDefault,
- SetGpuPowersave,
- None,
-}
-
-// ── zbus proxies ────────────────────────────────────────────────────
-
-#[zbus::proxy(
- interface = "org.freedesktop.UPower.Device",
- default_service = "org.freedesktop.UPower",
- default_path = "/org/freedesktop/UPower/devices/battery_BAT0"
-)]
-trait UpowerBattery {
- #[zbus(property)]
- fn percentage(&self) -> zbus::Result<f64>;
- #[zbus(property)]
- fn state(&self) -> zbus::Result<u32>;
- #[zbus(property)]
- fn energy(&self) -> zbus::Result<f64>;
- #[zbus(property)]
- fn energy_full(&self) -> zbus::Result<f64>;
- #[zbus(property)]
- fn energy_rate(&self) -> zbus::Result<f64>;
- #[zbus(property)]
- fn time_to_empty(&self) -> zbus::Result<i64>;
- #[zbus(property)]
- fn time_to_full(&self) -> zbus::Result<i64>;
- #[zbus(property)]
- fn vendor(&self) -> zbus::Result<String>;
- #[zbus(property)]
- fn model(&self) -> zbus::Result<String>;
-}
-
-#[zbus::proxy(
- interface = "org.freedesktop.UPower",
- default_service = "org.freedesktop.UPower",
- default_path = "/org/freedesktop/UPower"
-)]
-trait UpowerDaemon {
- #[zbus(property, name = "OnBattery")]
- fn on_battery(&self) -> zbus::Result<bool>;
-}
-
-// ── Helpers ─────────────────────────────────────────────────────────
-
-fn format_duration(secs: i64) -> String {
- let h = secs / 3600;
- let m = (secs % 3600) / 60;
- if h > 0 { format!("{}h {}m", h, m) } else { format!("{}m", m) }
-}
-
-fn spawn_cpu_power(powersave: bool) {
- let script = if powersave { "cpu-powersave-on" } else { "cpu-powersave-off" };
- let _ = tokio::process::Command::new("pkexec")
- .arg(format!("/home/lsgalante/.local/share/cce-system-interface/helpers/{}", script))
- .spawn();
-}
-
-fn spawn_gpu_power(powersave: bool) {
- let script = if powersave { "gpu-powersave-on" } else { "gpu-powersave-off" };
- let _ = tokio::process::Command::new("pkexec")
- .arg(format!("/home/lsgalante/.local/share/cce-system-interface/helpers/{}", script))
- .spawn();
-}
-
-fn current_cpu_governor() -> String {
- std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor")
- .unwrap_or_default().trim().to_string()
-}
-
-async fn current_gpu_power_cap() -> bool {
- tokio::process::Command::new("nvidia-smi")
- .args(["--query-gpu=power.limit", "--format=csv,noheader,nounits"])
- .output().await.ok()
- .and_then(|o| String::from_utf8_lossy(&o.stdout).trim().parse::<f32>().ok())
- .map(|w| w <= 10.0).unwrap_or(false)
-}
-
-async fn fetch_upower() -> (BatteryInfo, bool) {
- let conn = match zbus::Connection::system().await {
- Ok(c) => c,
- Err(_) => return (BatteryInfo::default(), true),
- };
-
- let battery = match UpowerBatteryProxy::new(&conn).await {
- Ok(proxy) => BatteryInfo {
- percentage: proxy.percentage().await.unwrap_or(0.0) as f32,
- state: {
- let s = proxy.state().await.unwrap_or(0);
- match s { 1 => "charging", 2 => "discharging", 4 => "fully-charged", _ => "unknown" }.into()
- },
- energy: proxy.energy().await.unwrap_or(0.0),
- energy_full: proxy.energy_full().await.unwrap_or(0.0),
- energy_rate: proxy.energy_rate().await.unwrap_or(0.0),
- time_to_empty: proxy.time_to_empty().await.unwrap_or(0),
- time_to_full: proxy.time_to_full().await.unwrap_or(0),
- vendor: proxy.vendor().await.unwrap_or_default(),
- model: proxy.model().await.unwrap_or_default(),
- },
- Err(_) => BatteryInfo::default(),
- };
-
- let on_ac = match UpowerDaemonProxy::new(&conn).await {
- Ok(proxy) => !proxy.on_battery().await.unwrap_or(false),
- Err(_) => true,
- };
-
- (battery, on_ac)
-}
-
-fn read_cpu_temp() -> Option<f32> {
- if let Ok(entries) = std::fs::read_dir("/sys/class/hwmon") {
- for entry in entries.filter_map(|e| e.ok()) {
- let path = entry.path();
- if let Ok(name) = std::fs::read_to_string(path.join("name")) {
- let name = name.trim();
- if name == "thinkpad" {
- if let Ok(val) = std::fs::read_to_string(path.join("temp1_input")) {
- if let Ok(temp_milli) = val.trim().parse::<f32>() {
- return Some(temp_milli / 1000.0);
- }
- }
- } else if name == "coretemp" {
- if let Ok(val) = std::fs::read_to_string(path.join("temp1_input")) {
- if let Ok(temp_milli) = val.trim().parse::<f32>() {
- return Some(temp_milli / 1000.0);
- }
- }
- }
- }
- }
- }
- None
-}
-
-fn read_thinkpad_gpu_temp() -> Option<f32> {
- if let Ok(entries) = std::fs::read_dir("/sys/class/hwmon") {
- for entry in entries.filter_map(|e| e.ok()) {
- let path = entry.path();
- if let Ok(name) = std::fs::read_to_string(path.join("name")) {
- if name.trim() == "thinkpad" {
- for i in 1..=8 {
- let label_path = path.join(format!("temp{}_label", i));
- if let Ok(lbl) = std::fs::read_to_string(&label_path) {
- if lbl.trim() == "GPU" {
- if let Ok(val) = std::fs::read_to_string(path.join(format!("temp{}_input", i))) {
- if let Ok(temp_milli) = val.trim().parse::<f32>() {
- return Some(temp_milli / 1000.0);
- }
- }
- }
- }
- }
- }
- }
- }
- }
- None
-}
-
-async fn read_nvidia_gpu_temp() -> Option<f32> {
- let out = tokio::process::Command::new("nvidia-smi")
- .args(["--query-gpu=temperature.gpu", "--format=csv,noheader,nounits"])
- .output().await.ok()?;
- let val_str = String::from_utf8_lossy(&out.stdout);
- val_str.trim().parse::<f32>().ok()
-}
-
-pub async fn fetch_hardware_state() -> HardwareState {
- static CPU_INFO: std::sync::OnceLock<(String, u32)> = std::sync::OnceLock::new();
- let (cpu_model, cpu_cores) = CPU_INFO.get_or_init(|| {
- let output = std::process::Command::new("lscpu")
- .output().ok()
- .map(|o| String::from_utf8_lossy(&o.stdout).to_string())
- .unwrap_or_default();
- let model = output.lines()
- .find(|l| l.contains("Model name"))
- .and_then(|l| l.split(':').nth(1))
- .map(|s| s.trim().to_string())
- .unwrap_or_default();
- let cores = output.lines()
- .find(|l| l.contains("CPU(s)"))
- .and_then(|l| {
- let rest = l.split(':').nth(1).unwrap_or("").trim();
- rest.split_whitespace().next().and_then(|n| n.parse::<u32>().ok())
- })
- .unwrap_or(0);
- (model, cores)
- }).clone();
-
- let cpu_usage = {
- let read_stat = || -> Option<(u64, u64)> {
- let stat = std::fs::read_to_string("/proc/stat").ok()?;
- let first = stat.lines().next()?;
- let vals: Vec<u64> = first.split_whitespace().skip(1).filter_map(|v| v.parse().ok()).collect();
- if vals.len() < 3 { return None; }
- let total: u64 = vals.iter().sum();
- let idle = vals.get(3).copied().unwrap_or(0);
- Some((idle, total))
- };
- let (idle1, total1) = read_stat().unwrap_or((0, 1));
- tokio::time::sleep(std::time::Duration::from_millis(100)).await;
- let (idle2, total2) = read_stat().unwrap_or((0, 1));
- let d_idle = idle2.saturating_sub(idle1);
- let d_total = total2.saturating_sub(total1);
- if d_total > 0 {
- (1.0 - d_idle as f64 / d_total as f64) * 100.0
- } else { 0.0 }
- } as f32;
-
- static GPUS_INFO: std::sync::OnceLock<Vec<String>> = std::sync::OnceLock::new();
- let gpus = GPUS_INFO.get_or_init(|| {
- let mut list = Vec::new();
- if let Some(o) = std::process::Command::new("lspci").output().ok() {
- for line in String::from_utf8_lossy(&o.stdout).lines() {
- if line.contains("VGA") || line.contains("3D") {
- if let Some(name) = line.split(':').nth(2) {
- let trimmed = name.trim().to_string();
- if !trimmed.is_empty() {
- list.push(trimmed);
- }
- }
- }
- }
- }
- list
- }).clone();
-
- let processes = {
- let mut list = Vec::new();
- if let Some(o) = tokio::process::Command::new("ps")
- .args(["-eo", "pid,%cpu,comm", "--sort=-%cpu"])
- .output().await.ok()
- {
- let text = String::from_utf8_lossy(&o.stdout);
- for line in text.lines().skip(1) {
- let parts: Vec<&str> = line.split_whitespace().collect();
- if parts.len() >= 3 {
- let pid = parts[0].to_string();
- let cpu = parts[1].to_string();
- let comm = parts[2..].join(" ");
- list.push((pid, cpu, comm));
- }
- }
- }
- list
- };
-
- let cpu_temp = read_cpu_temp();
- let tp_gpu_temp = read_thinkpad_gpu_temp();
- let nv_gpu_temp = read_nvidia_gpu_temp().await;
-
- let cpu_label_text = format!("CPU {} ({} cores)", cpu_model, cpu_cores);
- let cpu_usage_text = format!("Usage {:.0}%", cpu_usage);
- let cpu_temp_text = cpu_temp.map(|t| format!("Temp {:.0}°C", t)).unwrap_or_else(|| "Temp N/A".to_string());
-
- let gpu_labels = gpus.iter().map(|gpu_name| {
- let temp = if gpu_name.to_lowercase().contains("nvidia") {
- nv_gpu_temp.or(tp_gpu_temp)
- } else {
- tp_gpu_temp
- };
- let temp_str = temp.map(|t| format!(" — {:.0}°C", t)).unwrap_or_default();
- let text = format!("GPU {}{}", gpu_name, temp_str);
- Label::new(&text).with_font_size(12.0).with_color([212, 212, 212])
- }).collect();
-
- let (battery, on_ac) = fetch_upower().await;
- let cpu_powersave = current_cpu_governor() == "powersave";
- let gpu_powersave = current_gpu_power_cap().await;
-
- HardwareState {
- cpu_model,
- cpu_usage,
- cpu_cores,
- gpus,
- loaded: true,
- cpu_label: Label::new(&cpu_label_text).with_font_size(12.0).with_color([212, 212, 212]),
- cpu_usage_label: Label::new(&cpu_usage_text).with_font_size(12.0).with_color([212, 212, 212]),
- cpu_temp_label: Label::new(&cpu_temp_text).with_font_size(12.0).with_color([212, 212, 212]),
- gpu_labels,
- processes,
- cpu_list_box: ScrollingList::new(24.0, 2.0),
- battery,
- on_ac,
- cpu_powersave,
- gpu_powersave,
- cpu_gov_menu: Dropdown::new(
- vec!["Performance".to_string(), "Powersave".to_string()],
- if cpu_powersave { 1 } else { 0 },
- ).with_label("CPU Governor"),
- gpu_gov_menu: Dropdown::new(
- vec!["Default (80W)".to_string(), "Eco Cap (5W)".to_string()],
- if gpu_powersave { 1 } else { 0 },
- ).with_label("GPU Power Limit"),
- }
-}
-
-const TEXT_FG: [f32; 4] = [0.83, 0.83, 0.83, 1.0];
-const TEXT_DIM: [f32; 4] = [0.53, 0.53, 0.60, 1.0];
-const ACCENT: [f32; 4] = [0.36, 0.56, 0.38, 1.0];
-const RED: [f32; 4] = [1.0, 0.33, 0.33, 1.0];
-const ORANGE: [f32; 4] = [1.0, 0.73, 0.20, 1.0];
-
-pub fn view(state: &mut HardwareState, cx: f32, cy: f32, cw: f32, ch: f32, root_focused: bool, layout: &mut dyn LayoutStrategy, ctx: &mut cce_ui::context::UiContext) -> PageContent {
- let mut final_pc = PageContent::new();
- let sec_w = 320.0f32;
- let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(5);
-
- // ── CPU Section ──
- builder.add_section(&mut final_pc, "CPU", root_focused, |sec| {
- let rx = sec.left;
- if !state.loaded {
- sec.text("Loading CPU model and utilization...", 12.0, 0.0, 12.0, TEXT_FG);
- sec.spacing(10.0);
- } else {
- // CPU Info Label
- sec.widget(&mut state.cpu_label, 12.0, sec.cw - 24.0, 26.0, ctx);
- sec.spacing(12.0);
-
- // CPU Usage Label
- sec.widget(&mut state.cpu_usage_label, 12.0, sec.cw - 24.0, 26.0, ctx);
- sec.spacing(12.0);
-
- // CPU Temp Label
- sec.widget(&mut state.cpu_temp_label, 12.0, sec.cw - 24.0, 26.0, ctx);
- sec.spacing(12.0);
-
- // Scrolling box configuration for process list
- let list_box_x = rx + 12.0;
- let list_box_y = sec.ay();
- let list_box_w = sec.cw - 24.0;
- let list_box_h = 220.0;
-
- // Render the standardized ScrollBox widget
- render_widget(sec.pc, &mut state.cpu_list_box, list_box_x, list_box_y, list_box_w, list_box_h, ctx);
-
- // Header for process list columns (drawn static on top of the ScrollBox background)
- let header_h = 22.0;
- sec.pc.rect([0.12, 0.12, 0.16, 0.5], list_box_x + 1.0, list_box_y + 1.0, list_box_w - 2.0, header_h);
- sec.pc.rect([0.18, 0.18, 0.24, 1.0], list_box_x + 1.0, list_box_y + header_h, list_box_w - 2.0, 1.0); // Divider
-
- sec.pc.text("PID", list_box_x + 12.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
- sec.pc.text("COMMAND", list_box_x + 80.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
- sec.pc.text("CPU %", list_box_x + list_box_w - 60.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
-
- let row_h = 24.0;
- // Update ScrollingList bounds for the scrollable viewport (which starts below the header)
- state.cpu_list_box.update_bounds(state.processes.len(), list_box_y + header_h, list_box_h - header_h - 6.0);
-
- // Visible process rows rendering (virtualized/clipped)
- for (idx, (pid, cpu, comm)) in state.processes.iter().enumerate() {
- if let Some(draw_y) = state.cpu_list_box.get_item_draw_y(idx, 4.0) {
- // Standard row action button (transparent background, highlights on hover)
- sec.pc.button(
- "",
- list_box_x + 2.0,
- draw_y,
- list_box_w - 16.0,
- row_h,
- [0.0, 0.0, 0.0, 0.0],
- [1.0, 1.0, 1.0, 0.06],
- [0.0, 0.0, 0.0, 0.0],
- AppAction::Hardware(HardwareMessage::None),
- );
-
- sec.pc.text(pid, list_box_x + 12.0, draw_y + 6.0, 12.0, [0.80, 0.80, 0.85, 1.0]);
- sec.pc.text(comm, list_box_x + 80.0, draw_y + 6.0, 12.0, [0.80, 0.80, 0.85, 1.0]);
- sec.pc.text(&format!("{}%", cpu), list_box_x + list_box_w - 60.0, draw_y + 6.0, 12.0, [0.56, 0.83, 0.56, 1.0]);
- }
- }
-
- if state.processes.is_empty() {
- sec.pc.text("No active processes", list_box_x + 12.0, list_box_y + header_h + 16.0, 12.0, TEXT_DIM);
- }
-
- sec.content_y += list_box_h;
- }
- });
-
- // ── GPU Section ──
- builder.add_section(&mut final_pc, "GPU", false, |sec_gpu| {
- if !state.loaded {
- sec_gpu.text("Loading GPU models...", 12.0, 0.0, 12.0, TEXT_FG);
- sec_gpu.spacing(10.0);
- } else {
- for (i, gpu_lbl) in state.gpu_labels.iter_mut().enumerate() {
- if i > 0 { sec_gpu.spacing(12.0); }
- sec_gpu.widget(gpu_lbl, 12.0, sec_gpu.cw - 24.0, 26.0, ctx);
- }
- }
- });
-
- // ── Battery Section ──
- builder.add_section(&mut final_pc, "Battery", false, |sec_bat| {
- if !state.loaded {
- sec_bat.text("Loading battery status...", 12.0, 0.0, 12.0, TEXT_DIM);
- sec_bat.spacing(18.0);
- } else {
- let bat = &state.battery;
- let bat_icon = match bat.state.as_str() {
- "charging" => "+",
- "fully-charged" => "=",
- _ => "",
- };
-
- let pct_color = if bat.percentage < 20.0 { RED }
- else if bat.percentage < 50.0 { ORANGE }
- else { ACCENT };
-
- let pct_str = format!("{} {:.0}%", bat_icon, bat.percentage);
- sec_bat.text(&pct_str, 12.0, 0.0, 24.0, pct_color);
- sec_bat.spacing(30.0);
-
- let state_str = format!("{} • {:.1}W • {:.1}/{:.1} Wh",
- bat.state, bat.energy_rate, bat.energy, bat.energy_full);
- sec_bat.text(&state_str, 12.0, 0.0, 12.0, TEXT_DIM);
- sec_bat.spacing(18.0);
-
- let time_str = if bat.time_to_empty > 0 {
- format!("Time remaining: {}", format_duration(bat.time_to_empty))
- } else if bat.time_to_full > 0 {
- format!("Time to full: {}", format_duration(bat.time_to_full))
- } else { String::new() };
- if !time_str.is_empty() {
- sec_bat.text(&time_str, 12.0, 0.0, 12.0, TEXT_DIM);
- sec_bat.spacing(18.0);
- }
-
- let detail_str = format!("{} {}", bat.vendor, bat.model);
- sec_bat.text(&detail_str, 12.0, 0.0, 11.0, TEXT_DIM);
- sec_bat.spacing(20.0);
-
- let ac_str = if state.on_ac { "On AC Power" } else { "On Battery" };
- sec_bat.text(ac_str, 12.0, 0.0, 14.0, TEXT_FG);
- sec_bat.spacing(20.0);
- }
- });
-
- // ── CPU Governor section ──
- builder.add_section(&mut final_pc, "CPU Governor", false, |sec_gov| {
- let rx = sec_gov.left;
- if !state.loaded {
- sec_gov.text("Loading CPU governor...", 12.0, 0.0, 12.0, TEXT_DIM);
- sec_gov.spacing(18.0);
- } else {
- sec_gov.widget(&mut state.cpu_gov_menu, 12.0, sec_gov.cw - 24.0, 26.0, ctx);
- sec_gov.spacing(12.0);
-
- let (info_title, info_lines) = if state.cpu_powersave {
- (
- "CPU Governor: Powersave",
- vec![
- "• Active: powersave".to_string(),
- "• Governor set to powersave — lower power, slower burst".to_string(),
- ],
- )
- } else {
- (
- "CPU Governor: Performance",
- vec![
- "• Active: performance".to_string(),
- "• Governor set to performance".to_string(),
- ],
- )
- };
-
- let mut info_box = InfoBox::new(info_title, info_lines);
- let info_h = 80.0;
- let info_y = sec_gov.ay();
- render_widget(sec_gov.pc, &mut info_box, rx + 12.0, info_y, sec_gov.cw - 24.0, info_h, ctx);
- sec_gov.spacing(info_h + 12.0);
- }
- });
-
- // ── GPU Power section ──
- builder.add_section(&mut final_pc, "GPU Power", false, |sec_gpow| {
- let rx = sec_gpow.left;
- if !state.loaded {
- sec_gpow.text("Loading GPU power status...", 12.0, 0.0, 12.0, TEXT_DIM);
- sec_gpow.spacing(18.0);
- } else {
- sec_gpow.widget(&mut state.gpu_gov_menu, 12.0, sec_gpow.cw - 24.0, 26.0, ctx);
- sec_gpow.spacing(12.0);
-
- let (info_title, info_lines) = if state.gpu_powersave {
- (
- "GPU Power Limit: Eco Cap",
- vec![
- "• Mode: 5W Cap".to_string(),
- "• NVIDIA power limit capped at 5W — minimal draw".to_string(),
- ],
- )
- } else {
- (
- "GPU Power Limit: Default",
- vec![
- "• Mode: 80W Default".to_string(),
- "• NVIDIA running at default power limit".to_string(),
- ],
- )
- };
-
- let mut info_box = InfoBox::new(info_title, info_lines);
- let info_h = 80.0;
- let info_y = sec_gpow.ay();
- render_widget(sec_gpow.pc, &mut info_box, rx + 12.0, info_y, sec_gpow.cw - 24.0, info_h, ctx);
- sec_gpow.spacing(info_h + 12.0);
- }
- });
-
- final_pc
-}
-
-pub fn update(state: &mut HardwareState, msg: HardwareMessage) {
- match msg {
- HardwareMessage::Refreshed(new) => {
- state.loaded = new.loaded;
- state.cpu_model = new.cpu_model;
- state.cpu_usage = new.cpu_usage;
- state.cpu_cores = new.cpu_cores;
- state.gpus = new.gpus;
- state.cpu_label = new.cpu_label;
- state.cpu_usage_label = new.cpu_usage_label;
- state.cpu_temp_label = new.cpu_temp_label;
- state.gpu_labels = new.gpu_labels;
- state.processes = new.processes;
- let old_scroll = state.cpu_list_box.scroll_y();
- state.cpu_list_box = new.cpu_list_box;
- state.cpu_list_box.set_scroll_y(old_scroll);
-
- state.battery = new.battery;
- state.on_ac = new.on_ac;
- state.cpu_powersave = new.cpu_powersave;
- state.gpu_powersave = new.gpu_powersave;
- state.cpu_gov_menu.selected = new.cpu_gov_menu.selected;
- state.gpu_gov_menu.selected = new.gpu_gov_menu.selected;
- }
- HardwareMessage::SetCpuPerformance => {
- state.cpu_powersave = false;
- state.cpu_gov_menu.selected = 0;
- spawn_cpu_power(false);
- }
- HardwareMessage::SetCpuPowersave => {
- state.cpu_powersave = true;
- state.cpu_gov_menu.selected = 1;
- spawn_cpu_power(true);
- }
- HardwareMessage::SetGpuDefault => {
- state.gpu_powersave = false;
- state.gpu_gov_menu.selected = 0;
- spawn_gpu_power(false);
- }
- HardwareMessage::SetGpuPowersave => {
- state.gpu_powersave = true;
- state.gpu_gov_menu.selected = 1;
- spawn_gpu_power(true);
- }
- HardwareMessage::None => {}
- }
-}
diff --git a/src/pages/interface.rs b/src/pages/interface.rs
index c41e2f4..08f2be7 100644
--- a/src/pages/interface.rs
+++ b/src/pages/interface.rs
@@ -199,6 +199,8 @@ pub struct InterfaceState {
pub paginator_tab_padding_y: u16,
pub button_padding: u16,
pub button_padding_spinbox: Spinbox,
+ pub button_strip_spacing: u16,
+ pub button_strip_spacing_spinbox: Spinbox,
pub section_padding: u16,
pub section_padding_spinbox: Spinbox,
pub plate_padding: u16,
@@ -361,6 +363,8 @@ impl Default for InterfaceState {
paginator_tab_padding_y: 14,
button_padding: 14,
button_padding_spinbox: Spinbox::new(14, 0, 100, 1).with_label("Button Padding").with_unit("px"),
+ button_strip_spacing: 8,
+ button_strip_spacing_spinbox: Spinbox::new(8, 0, 100, 1).with_label("Spacing").with_unit("px"),
section_padding: 8,
section_padding_spinbox: Spinbox::new(8, 0, 100, 1).with_label("Padding").with_unit("px"),
plate_padding: 20,
@@ -512,6 +516,7 @@ pub enum InterfaceMessage {
SetWindowOpacity(f32),
SetWindowCornerRadius(u16),
SetButtonPadding(u16),
+ SetButtonStripSpacing(u16),
SetSectionPadding(u16),
SetPlatePadding(u16),
SetPlateOpacity(f32),
@@ -645,6 +650,7 @@ pub fn read_interface_config() -> InterfaceState {
let paginator_tab_padding_x = parse_u16_from(&content, "paginator_tab_padding_x", 10);
let paginator_tab_padding_y = parse_u16_from(&content, "paginator_tab_padding_y", 14);
let button_padding = parse_u16_from(&content, "button_padding", paginator_tab_padding_y);
+ let button_strip_spacing = parse_u16_from(&content, "button_strip_spacing", 8);
let section_padding = parse_u16_from(&content, "section_padding", 8);
let plate_padding = parse_u16_from(&content, "plate_padding", 20);
let plate_opacity = parse_f32_from(&content, "plate_opacity", 1.0);
@@ -745,6 +751,8 @@ pub fn read_interface_config() -> InterfaceState {
paginator_tab_padding_y,
button_padding,
button_padding_spinbox: Spinbox::new(button_padding as i32, 0, 100, 1).with_label("Button Padding").with_unit("px"),
+ button_strip_spacing,
+ button_strip_spacing_spinbox: Spinbox::new(button_strip_spacing as i32, 0, 100, 1).with_label("Spacing").with_unit("px"),
section_padding,
section_padding_spinbox: Spinbox::new(section_padding as i32, 0, 100, 1).with_label("Padding").with_unit("px"),
plate_padding,
@@ -1396,6 +1404,13 @@ pub fn propagate_links(state: &mut InterfaceState, key: &str, val_str: &str) {
apply_button_padding(val);
}
}
+ "button_strip_spacing" => {
+ if let Ok(val) = val_str.parse::<u16>() {
+ state.button_strip_spacing = val;
+ state.button_strip_spacing_spinbox.value = val as i32;
+ apply_button_strip_spacing(val);
+ }
+ }
"section_padding" => {
if let Ok(val) = val_str.parse::<u16>() {
state.section_padding = val;
@@ -1847,6 +1862,12 @@ fn apply_button_padding(padding: u16) {
cce_ui::layout::set_button_padding(padding as f32);
}
+fn apply_button_strip_spacing(spacing: u16) {
+ write_config_value("button_strip_spacing", &spacing.to_string());
+ send_ipc_command(&format!("layout button_strip_spacing {}", spacing));
+ cce_ui::layout::set_button_strip_spacing(spacing as f32);
+}
+
fn apply_plate_padding(padding: u16) {
write_config_value("plate_padding", &padding.to_string());
cce_ui::layout::set_plate_padding(padding as f32);
@@ -2607,6 +2628,9 @@ pub fn view(state: &mut InterfaceState, cx: f32, cy: f32, cw: f32, ch: f32, sec_
state.button_padding_spinbox.value = state.button_padding as i32;
subsec.widget_full(&mut state.button_padding_spinbox, 44.0, ctx);
subsec.spacing(8.0);
+ state.button_strip_spacing_spinbox.value = state.button_strip_spacing as i32;
+ subsec.widget_full(&mut state.button_strip_spacing_spinbox, 44.0, ctx);
+ subsec.spacing(8.0);
});
sec.spacing(12.0);
@@ -3140,6 +3164,11 @@ pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
apply_button_padding(padding);
propagate_links(state, "button_padding", &padding.to_string());
}
+ InterfaceMessage::SetButtonStripSpacing(spacing) => {
+ state.button_strip_spacing = spacing;
+ apply_button_strip_spacing(spacing);
+ propagate_links(state, "button_strip_spacing", &spacing.to_string());
+ }
InterfaceMessage::SetSectionPadding(padding) => {
state.section_padding = padding;
apply_section_padding(padding);
@@ -3371,6 +3400,7 @@ pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
InterfaceMessage::PickLowColor | InterfaceMessage::PickHighColor | InterfaceMessage::PickDisabledColor | InterfaceMessage::PickSeparatorColor | InterfaceMessage::PickVisualGuides | InterfaceMessage::PickSliderTrackColor | InterfaceMessage::PickPageLowColor | InterfaceMessage::PickColorBordersColor | InterfaceMessage::PickNormalColor | InterfaceMessage::PickPaginatorSidebarColor | InterfaceMessage::PickPrimaryHighlightColor | InterfaceMessage::PickMenubarTabLabelColor | InterfaceMessage::PickToggleEnabledColor | InterfaceMessage::PickToggleDisabledColor | InterfaceMessage::PickScrollingListBgColor | InterfaceMessage::PickScrollingListEntryBgColor | InterfaceMessage::PickScrollingListEntryHighlightColor | InterfaceMessage::PickBreadcrumbBgColor | InterfaceMessage::PickPopoverBgColor | InterfaceMessage::PickNotificationBgColor | InterfaceMessage::PickWindowColor | InterfaceMessage::PickPageColor | InterfaceMessage::PickLayerColor => {}
InterfaceMessage::Refreshed(new) => {
let was_bp_hovered = state.button_padding_spinbox.hovered();
+ let was_bss_hovered = state.button_strip_spacing_spinbox.hovered();
let was_sp_hovered = state.section_padding_spinbox.hovered();
let was_pp_hovered = state.plate_padding_spinbox.hovered();
let was_pl_op_hovered = state.plate_opacity_spinbox.hovered();
@@ -3435,6 +3465,7 @@ pub fn update(state: &mut InterfaceState, msg: InterfaceMessage) {
*state = new;
state.button_padding_spinbox.set_hovered(was_bp_hovered);
+ state.button_strip_spacing_spinbox.set_hovered(was_bss_hovered);
state.section_padding_spinbox.set_hovered(was_sp_hovered);
state.plate_padding_spinbox.set_hovered(was_pp_hovered);
state.plate_opacity_spinbox.set_hovered(was_pl_op_hovered);
@@ -4804,6 +4835,29 @@ mod tests {
let _ = fs::remove_file(path_str);
}
+ #[test]
+ fn test_read_write_button_strip_spacing() {
+ let dir = std::env::temp_dir();
+ let path = dir.join("test_button_strip_spacing_config.toml");
+ let path_str = path.to_str().unwrap();
+
+ let initial_content = "{\"layout\": {\"gap\": 18, \"border_color\": \"#374673\"}}";
+ fs::write(path_str, initial_content).unwrap();
+
+ let content = fs::read_to_string(path_str).unwrap();
+ let val = parse_u16_from(&content, "button_strip_spacing", 8);
+ assert_eq!(val, 8);
+
+ assert!(write_config_value_path(path_str, "button_strip_spacing", "12"));
+ let updated = fs::read_to_string(path_str).unwrap();
+ assert!(updated.contains("\"button_strip_spacing\": 12"));
+
+ let val2 = parse_u16_from(&updated, "button_strip_spacing", 8);
+ assert_eq!(val2, 12);
+
+ let _ = fs::remove_file(path_str);
+ }
+
#[test]
fn test_read_write_slider_height() {
let dir = std::env::temp_dir();
diff --git a/src/pages/mod.rs b/src/pages/mod.rs
index ffecbab..c0ae57b 100644
--- a/src/pages/mod.rs
+++ b/src/pages/mod.rs
@@ -5,8 +5,7 @@ pub mod storage;
pub mod system_info;
pub mod keybindings;
pub mod input;
-pub mod hardware;
-pub mod services;
+pub mod processes;
pub mod interface;
pub mod accounts;
pub mod packages;
@@ -16,27 +15,25 @@ pub enum Page {
Accounts,
Audio,
Radios,
- Services,
Storage,
Display,
System,
- Hardware,
+ Processes,
Input,
Interface,
Packages,
}
impl Page {
- pub const ALL: [Page; 11] = [
+ pub const ALL: [Page; 10] = [
Page::Accounts,
Page::Audio,
Page::Display,
- Page::Hardware,
Page::Input,
Page::Interface,
Page::Packages,
+ Page::Processes,
Page::Radios,
- Page::Services,
Page::Storage,
Page::System,
];
@@ -46,11 +43,10 @@ impl Page {
Page::Accounts => "Accounts",
Page::Audio => "Audio",
Page::Radios => "Radios",
- Page::Services => "Services",
Page::Storage => "Storage",
Page::Display => "Display",
Page::System => "System",
- Page::Hardware => "Hardware",
+ Page::Processes => "Processes",
Page::Input => "Input",
Page::Interface => "Interface",
Page::Packages => "Packages",
diff --git a/src/pages/network.rs b/src/pages/network.rs
index 060c519..e4ac8d0 100644
--- a/src/pages/network.rs
+++ b/src/pages/network.rs
@@ -1,5 +1,5 @@
use crate::app::{AppAction, PageContent, SectionContextExt};
-use cce_ui::layout::{render_widget, PageLayoutBuilder, LayoutStrategy};
+use cce_ui::layout::{render_widget, PageLayoutBuilder, LayoutStrategy, RenderTarget};
use cce_ui::widget::{ScrollingList, Toggle, Element};
#[derive(Debug, Clone)]
@@ -345,6 +345,7 @@ pub fn view(state: &mut NetworkState, cx: f32, cy: f32, cw: f32, ch: f32, root_f
let btn_w = list_box_w - 2.0 * margin;
let max_chars = ((btn_w / 6.5) as usize).saturating_sub(10).max(5);
+ sec.pc.push_clip_rect(list_box_x, list_box_y, list_box_w, list_box_h);
for (idx, net) in state.available.iter().enumerate() {
if let Some(draw_y) = state.wifi_list_box.get_item_draw_y(idx, 4.0) {
let prefix = if net.in_use { ">" } else { " " };
@@ -361,6 +362,7 @@ pub fn view(state: &mut NetworkState, cx: f32, cy: f32, cw: f32, ch: f32, root_f
AppAction::Radios(NetworkMessage::ConnectWifi(net.ssid.clone())));
}
}
+ sec.pc.pop_clip_rect();
sec.content_y += list_box_h + row_gap;
}
}
diff --git a/src/pages/packages.rs b/src/pages/packages.rs
index e6437ad..9971f77 100644
--- a/src/pages/packages.rs
+++ b/src/pages/packages.rs
@@ -1,5 +1,5 @@
use crate::app::{AppAction, PageContent, SectionContextExt};
-use cce_ui::layout::{render_widget, PageLayoutBuilder, LayoutStrategy, SectionContext};
+use cce_ui::layout::{render_widget, PageLayoutBuilder, LayoutStrategy, SectionContext, RenderTarget};
use cce_ui::widget::{Element, ScrollingList, TextBox, InteractiveListItem};
#[derive(Debug, Clone)]
@@ -415,6 +415,7 @@ pub fn view(
}
}
+ sec.pc.push_clip_rect(list_box_x, list_box_y, list_box_w, list_box_h);
for (idx, pkg) in filtered.iter().enumerate() {
if let Some(draw_y) = state.installed_list_box.get_item_draw_y(idx, 4.0) {
let item = &mut state.installed_items[idx];
@@ -424,6 +425,7 @@ pub fn view(
render_widget(sec.pc, item, list_box_x + 24.0, draw_y, list_box_w - 44.0, item_h, ctx);
}
}
+ sec.pc.pop_clip_rect();
if filtered.is_empty() {
sec.pc.text("No packages match the query", list_box_x + 16.0, list_box_y + 16.0, 12.0, TEXT_DIM);
@@ -446,6 +448,7 @@ pub fn view(
}
}
+ sec.pc.push_clip_rect(list_box_x, list_box_y, list_box_w, list_box_h);
for (idx, pkg) in filtered.iter().enumerate() {
if let Some(draw_y) = state.updates_list_box.get_item_draw_y(idx, 4.0) {
let item = &mut state.updates_items[idx];
@@ -455,6 +458,7 @@ pub fn view(
render_widget(sec.pc, item, list_box_x + 24.0, draw_y, list_box_w - 44.0, item_h, ctx);
}
}
+ sec.pc.pop_clip_rect();
if filtered.is_empty() {
sec.pc.text("No updates match the query", list_box_x + 16.0, list_box_y + 16.0, 12.0, TEXT_DIM);
diff --git a/src/pages/processes.rs b/src/pages/processes.rs
new file mode 100644
index 0000000..16d94c9
--- /dev/null
+++ b/src/pages/processes.rs
@@ -0,0 +1,1524 @@
+use crate::app::{AppAction, PageContent, SectionContextExt};
+use cce_ui::layout::{render_widget, PageLayoutBuilder, LayoutStrategy, RenderTarget};
+use cce_ui::widget::{Label, ScrollingList, Dropdown, InfoBox, TextBox, StatusDot, DotStatus, InteractiveListItem, Toggle, Spinbox, Element};
+use crate::pages::interface::parse_u16_from;
+use std::io::Write;
+use std::fs;
+
+#[derive(Debug, Clone, Default)]
+pub struct BatteryInfo {
+ pub percentage: f32,
+ pub state: String,
+ pub energy: f64,
+ pub energy_full: f64,
+ pub energy_rate: f64,
+ pub time_to_empty: i64,
+ pub time_to_full: i64,
+ pub vendor: String,
+ pub model: String,
+}
+
+#[derive(Debug, Clone)]
+pub struct NotificationsConfig {
+ pub enable: bool,
+ pub bell: bool,
+ pub duration: i32,
+}
+
+#[derive(Debug, Clone)]
+pub struct StatusData {
+ pub font_size: u16,
+ pub padding: u16,
+ pub separators: bool,
+ pub underline: bool,
+ pub running: bool,
+}
+
+#[derive(Debug, Clone)]
+pub struct ServiceInfo {
+ pub name: String,
+ pub description: String,
+ pub active_state: String,
+ pub sub_state: String,
+ pub is_system: bool,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ServiceTab {
+ System,
+ User,
+}
+
+impl Default for ServiceTab {
+ fn default() -> Self {
+ ServiceTab::System
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct ProcessesState {
+ pub cpu_model: String,
+ pub cpu_usage: f32,
+ pub cpu_cores: u32,
+ pub gpus: Vec<String>,
+ pub loaded: bool,
+ pub cpu_label: Label,
+ pub cpu_usage_label: Label,
+ pub cpu_temp_label: Label,
+ pub gpu_labels: Vec<Label>,
+ pub processes: Vec<(String, String, String)>, // (pid, cpu, comm)
+ pub cpu_list_box: ScrollingList,
+
+ // Power-related fields
+ pub battery: BatteryInfo,
+ pub on_ac: bool,
+ pub cpu_powersave: bool,
+ pub gpu_powersave: bool,
+ pub cpu_gov_menu: Dropdown,
+ pub gpu_gov_menu: Dropdown,
+
+ // Services-related fields
+ pub services_loaded: bool,
+ pub services: Vec<ServiceInfo>,
+ pub services_active_tab: ServiceTab,
+ pub services_search_box: TextBox,
+ pub services_list_box: ScrollingList,
+ pub service_items: Vec<InteractiveListItem>,
+ pub notifications_loaded: bool,
+ pub notifications_enable: bool,
+ pub notifications_enable_toggle: Toggle,
+ pub notifications_bell: bool,
+ pub notifications_bell_toggle: Toggle,
+ pub notifications_duration: i32,
+ pub notifications_duration_spinbox: Spinbox,
+
+ // Status Interface fields
+ pub status_loaded: bool,
+ pub status_font_size: u16,
+ pub status_padding: u16,
+ pub status_separators: bool,
+ pub status_underline: bool,
+ pub status_running: bool,
+ pub status_label: Label,
+ pub status_separators_toggle: Toggle,
+ pub status_underline_toggle: Toggle,
+ pub status_padding_spinbox: Spinbox,
+}
+
+impl Default for ProcessesState {
+ fn default() -> Self {
+ Self {
+ cpu_model: String::new(),
+ cpu_usage: 0.0,
+ cpu_cores: 0,
+ gpus: Vec::new(),
+ loaded: false,
+ cpu_label: Label::new("CPU Info"),
+ cpu_usage_label: Label::new("CPU Usage"),
+ cpu_temp_label: Label::new("CPU Temp"),
+ gpu_labels: Vec::new(),
+ processes: Vec::new(),
+ cpu_list_box: ScrollingList::new(24.0, 2.0),
+
+ battery: BatteryInfo::default(),
+ on_ac: true,
+ cpu_powersave: false,
+ gpu_powersave: false,
+ cpu_gov_menu: Dropdown::new(
+ vec!["Performance".to_string(), "Powersave".to_string()],
+ 0,
+ ).with_label("CPU Governor"),
+ gpu_gov_menu: Dropdown::new(
+ vec!["Default (80W)".to_string(), "Eco Cap (5W)".to_string()],
+ 0,
+ ).with_label("GPU Power Limit"),
+
+ services_loaded: false,
+ services: Vec::new(),
+ services_active_tab: ServiceTab::System,
+ services_search_box: TextBox::new(String::new()).with_label("Filter Services"),
+ services_list_box: ScrollingList::new(36.0, 6.0),
+ service_items: Vec::new(),
+ notifications_loaded: false,
+ notifications_enable: true,
+ notifications_enable_toggle: Toggle::new().with_label("Enable Notifications"),
+ notifications_bell: false,
+ notifications_bell_toggle: Toggle::new().with_label("Play Bell Sound"),
+ notifications_duration: 5,
+ notifications_duration_spinbox: Spinbox::new(5, 1, 60, 1)
+ .with_label("Notification Duration")
+ .with_unit("s"),
+
+ status_loaded: false,
+ status_font_size: 11,
+ status_padding: 8,
+ status_separators: true,
+ status_underline: true,
+ status_running: false,
+ status_label: Label::new("Status Interface: Stopped").with_font_size(14.0).with_color([170, 51, 51]),
+ status_separators_toggle: Toggle::new().with_label("Show Separators"),
+ status_underline_toggle: Toggle::new().with_label("Show Underline"),
+ status_padding_spinbox: Spinbox::new(8, 0, 32, 1).with_label("Side Padding").with_unit("px"),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum ProcessesMessage {
+ Refreshed(ProcessesState),
+ SetCpuPerformance,
+ SetCpuPowersave,
+ SetGpuDefault,
+ SetGpuPowersave,
+ None,
+
+ // Services-related variants
+ ServicesRefreshed(Vec<ServiceInfo>),
+ ServicesSetTab(ServiceTab),
+ ServicesStart(String, bool),
+ ServicesStop(String, bool),
+ ServicesRestart(String, bool),
+ ToggleNotificationsEnable,
+ ToggleNotificationsBell,
+ SetNotificationsDuration(i32),
+ SendTestNotification,
+ NotificationsRefreshed(NotificationsConfig),
+
+ // Status Interface variants
+ StatusRefreshed(StatusData),
+ StatusToggleSeparators,
+ StatusToggleUnderline,
+ StatusReload,
+ StatusSetPadding(u16),
+}
+
+// ── zbus proxies ────────────────────────────────────────────────────
+
+#[zbus::proxy(
+ interface = "org.freedesktop.UPower.Device",
+ default_service = "org.freedesktop.UPower",
+ default_path = "/org/freedesktop/UPower/devices/battery_BAT0"
+)]
+trait UpowerBattery {
+ #[zbus(property)]
+ fn percentage(&self) -> zbus::Result<f64>;
+ #[zbus(property)]
+ fn state(&self) -> zbus::Result<u32>;
+ #[zbus(property)]
+ fn energy(&self) -> zbus::Result<f64>;
+ #[zbus(property)]
+ fn energy_full(&self) -> zbus::Result<f64>;
+ #[zbus(property)]
+ fn energy_rate(&self) -> zbus::Result<f64>;
+ #[zbus(property)]
+ fn time_to_empty(&self) -> zbus::Result<i64>;
+ #[zbus(property)]
+ fn time_to_full(&self) -> zbus::Result<i64>;
+ #[zbus(property)]
+ fn vendor(&self) -> zbus::Result<String>;
+ #[zbus(property)]
+ fn model(&self) -> zbus::Result<String>;
+}
+
+#[zbus::proxy(
+ interface = "org.freedesktop.UPower",
+ default_service = "org.freedesktop.UPower",
+ default_path = "/org/freedesktop/UPower"
+)]
+trait UpowerDaemon {
+ #[zbus(property, name = "OnBattery")]
+ fn on_battery(&self) -> zbus::Result<bool>;
+}
+
+// ── Helpers ─────────────────────────────────────────────────────────
+
+fn format_duration(secs: i64) -> String {
+ let h = secs / 3600;
+ let m = (secs % 3600) / 60;
+ if h > 0 { format!("{}h {}m", h, m) } else { format!("{}m", m) }
+}
+
+fn spawn_cpu_power(powersave: bool) {
+ let script = if powersave { "cpu-powersave-on" } else { "cpu-powersave-off" };
+ let _ = tokio::process::Command::new("pkexec")
+ .arg(format!("/home/lsgalante/.local/share/cce-system-interface/helpers/{}", script))
+ .spawn();
+}
+
+fn spawn_gpu_power(powersave: bool) {
+ let script = if powersave { "gpu-powersave-on" } else { "gpu-powersave-off" };
+ let _ = tokio::process::Command::new("pkexec")
+ .arg(format!("/home/lsgalante/.local/share/cce-system-interface/helpers/{}", script))
+ .spawn();
+}
+
+fn current_cpu_governor() -> String {
+ std::fs::read_to_string("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor")
+ .unwrap_or_default().trim().to_string()
+}
+
+async fn current_gpu_power_cap() -> bool {
+ tokio::process::Command::new("nvidia-smi")
+ .args(["--query-gpu=power.limit", "--format=csv,noheader,nounits"])
+ .output().await.ok()
+ .and_then(|o| String::from_utf8_lossy(&o.stdout).trim().parse::<f32>().ok())
+ .map(|w| w <= 10.0).unwrap_or(false)
+}
+
+async fn fetch_upower() -> (BatteryInfo, bool) {
+ let conn = match zbus::Connection::system().await {
+ Ok(c) => c,
+ Err(_) => return (BatteryInfo::default(), true),
+ };
+
+ let battery = match UpowerBatteryProxy::new(&conn).await {
+ Ok(proxy) => BatteryInfo {
+ percentage: proxy.percentage().await.unwrap_or(0.0) as f32,
+ state: {
+ let s = proxy.state().await.unwrap_or(0);
+ match s { 1 => "charging", 2 => "discharging", 4 => "fully-charged", _ => "unknown" }.into()
+ },
+ energy: proxy.energy().await.unwrap_or(0.0),
+ energy_full: proxy.energy_full().await.unwrap_or(0.0),
+ energy_rate: proxy.energy_rate().await.unwrap_or(0.0),
+ time_to_empty: proxy.time_to_empty().await.unwrap_or(0),
+ time_to_full: proxy.time_to_full().await.unwrap_or(0),
+ vendor: proxy.vendor().await.unwrap_or_default(),
+ model: proxy.model().await.unwrap_or_default(),
+ },
+ Err(_) => BatteryInfo::default(),
+ };
+
+ let on_ac = match UpowerDaemonProxy::new(&conn).await {
+ Ok(proxy) => !proxy.on_battery().await.unwrap_or(false),
+ Err(_) => true,
+ };
+
+ (battery, on_ac)
+}
+
+fn read_cpu_temp() -> Option<f32> {
+ if let Ok(entries) = std::fs::read_dir("/sys/class/hwmon") {
+ for entry in entries.filter_map(|e| e.ok()) {
+ let path = entry.path();
+ if let Ok(name) = std::fs::read_to_string(path.join("name")) {
+ let name = name.trim();
+ if name == "thinkpad" {
+ if let Ok(val) = std::fs::read_to_string(path.join("temp1_input")) {
+ if let Ok(temp_milli) = val.trim().parse::<f32>() {
+ return Some(temp_milli / 1000.0);
+ }
+ }
+ } else if name == "coretemp" {
+ if let Ok(val) = std::fs::read_to_string(path.join("temp1_input")) {
+ if let Ok(temp_milli) = val.trim().parse::<f32>() {
+ return Some(temp_milli / 1000.0);
+ }
+ }
+ }
+ }
+ }
+ }
+ None
+}
+
+fn read_thinkpad_gpu_temp() -> Option<f32> {
+ if let Ok(entries) = std::fs::read_dir("/sys/class/hwmon") {
+ for entry in entries.filter_map(|e| e.ok()) {
+ let path = entry.path();
+ if let Ok(name) = std::fs::read_to_string(path.join("name")) {
+ if name.trim() == "thinkpad" {
+ for i in 1..=8 {
+ let label_path = path.join(format!("temp{}_label", i));
+ if let Ok(lbl) = std::fs::read_to_string(&label_path) {
+ if lbl.trim() == "GPU" {
+ if let Ok(val) = std::fs::read_to_string(path.join(format!("temp{}_input", i))) {
+ if let Ok(temp_milli) = val.trim().parse::<f32>() {
+ return Some(temp_milli / 1000.0);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ None
+}
+
+async fn read_nvidia_gpu_temp() -> Option<f32> {
+ let out = tokio::process::Command::new("nvidia-smi")
+ .args(["--query-gpu=temperature.gpu", "--format=csv,noheader,nounits"])
+ .output().await.ok()?;
+ let val_str = String::from_utf8_lossy(&out.stdout);
+ val_str.trim().parse::<f32>().ok()
+}
+
+pub async fn fetch_processes_state() -> ProcessesState {
+ static CPU_INFO: std::sync::OnceLock<(String, u32)> = std::sync::OnceLock::new();
+ let (cpu_model, cpu_cores) = CPU_INFO.get_or_init(|| {
+ let output = std::process::Command::new("lscpu")
+ .output().ok()
+ .map(|o| String::from_utf8_lossy(&o.stdout).to_string())
+ .unwrap_or_default();
+ let model = output.lines()
+ .find(|l| l.contains("Model name"))
+ .and_then(|l| l.split(':').nth(1))
+ .map(|s| s.trim().to_string())
+ .unwrap_or_default();
+ let cores = output.lines()
+ .find(|l| l.contains("CPU(s)"))
+ .and_then(|l| {
+ let rest = l.split(':').nth(1).unwrap_or("").trim();
+ rest.split_whitespace().next().and_then(|n| n.parse::<u32>().ok())
+ })
+ .unwrap_or(0);
+ (model, cores)
+ }).clone();
+
+ let cpu_usage = {
+ let read_stat = || -> Option<(u64, u64)> {
+ let stat = std::fs::read_to_string("/proc/stat").ok()?;
+ let first = stat.lines().next()?;
+ let vals: Vec<u64> = first.split_whitespace().skip(1).filter_map(|v| v.parse().ok()).collect();
+ if vals.len() < 3 { return None; }
+ let total: u64 = vals.iter().sum();
+ let idle = vals.get(3).copied().unwrap_or(0);
+ Some((idle, total))
+ };
+ let (idle1, total1) = read_stat().unwrap_or((0, 1));
+ tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+ let (idle2, total2) = read_stat().unwrap_or((0, 1));
+ let d_idle = idle2.saturating_sub(idle1);
+ let d_total = total2.saturating_sub(total1);
+ if d_total > 0 {
+ (1.0 - d_idle as f64 / d_total as f64) * 100.0
+ } else { 0.0 }
+ } as f32;
+
+ static GPUS_INFO: std::sync::OnceLock<Vec<String>> = std::sync::OnceLock::new();
+ let gpus = GPUS_INFO.get_or_init(|| {
+ let mut list = Vec::new();
+ if let Some(o) = std::process::Command::new("lspci").output().ok() {
+ for line in String::from_utf8_lossy(&o.stdout).lines() {
+ if line.contains("VGA") || line.contains("3D") {
+ if let Some(name) = line.split(':').nth(2) {
+ let trimmed = name.trim().to_string();
+ if !trimmed.is_empty() {
+ list.push(trimmed);
+ }
+ }
+ }
+ }
+ }
+ list
+ }).clone();
+
+ let processes = {
+ let mut list = Vec::new();
+ if let Some(o) = tokio::process::Command::new("ps")
+ .args(["-eo", "pid,%cpu,comm", "--sort=-%cpu"])
+ .output().await.ok()
+ {
+ let text = String::from_utf8_lossy(&o.stdout);
+ for line in text.lines().skip(1) {
+ let parts: Vec<&str> = line.split_whitespace().collect();
+ if parts.len() >= 3 {
+ let pid = parts[0].to_string();
+ let cpu = parts[1].to_string();
+ let comm = parts[2..].join(" ");
+ list.push((pid, cpu, comm));
+ }
+ }
+ }
+ list
+ };
+
+ let cpu_temp = read_cpu_temp();
+ let tp_gpu_temp = read_thinkpad_gpu_temp();
+ let nv_gpu_temp = read_nvidia_gpu_temp().await;
+
+ let cpu_label_text = format!("CPU {} ({} cores)", cpu_model, cpu_cores);
+ let cpu_usage_text = format!("Usage {:.0}%", cpu_usage);
+ let cpu_temp_text = cpu_temp.map(|t| format!("Temp {:.0}°C", t)).unwrap_or_else(|| "Temp N/A".to_string());
+
+ let gpu_labels = gpus.iter().map(|gpu_name| {
+ let temp = if gpu_name.to_lowercase().contains("nvidia") {
+ nv_gpu_temp.or(tp_gpu_temp)
+ } else {
+ tp_gpu_temp
+ };
+ let temp_str = temp.map(|t| format!(" — {:.0}°C", t)).unwrap_or_default();
+ let text = format!("GPU {}{}", gpu_name, temp_str);
+ Label::new(&text).with_font_size(12.0).with_color([212, 212, 212])
+ }).collect();
+
+ let (battery, on_ac) = fetch_upower().await;
+ let cpu_powersave = current_cpu_governor() == "powersave";
+ let gpu_powersave = current_gpu_power_cap().await;
+
+ ProcessesState {
+ cpu_model,
+ cpu_usage,
+ cpu_cores,
+ gpus,
+ loaded: true,
+ cpu_label: Label::new(&cpu_label_text).with_font_size(12.0).with_color([212, 212, 212]),
+ cpu_usage_label: Label::new(&cpu_usage_text).with_font_size(12.0).with_color([212, 212, 212]),
+ cpu_temp_label: Label::new(&cpu_temp_text).with_font_size(12.0).with_color([212, 212, 212]),
+ gpu_labels,
+ processes,
+ cpu_list_box: ScrollingList::new(24.0, 2.0),
+ battery,
+ on_ac,
+ cpu_powersave,
+ gpu_powersave,
+ cpu_gov_menu: Dropdown::new(
+ vec!["Performance".to_string(), "Powersave".to_string()],
+ if cpu_powersave { 1 } else { 0 },
+ ).with_label("CPU Governor"),
+ gpu_gov_menu: Dropdown::new(
+ vec!["Default (80W)".to_string(), "Eco Cap (5W)".to_string()],
+ if gpu_powersave { 1 } else { 0 },
+ ).with_label("GPU Power Limit"),
+
+ services_loaded: false,
+ services: Vec::new(),
+ services_active_tab: ServiceTab::System,
+ services_search_box: TextBox::new(String::new()).with_label("Filter Services"),
+ services_list_box: ScrollingList::new(36.0, 6.0),
+ service_items: Vec::new(),
+ notifications_loaded: false,
+ notifications_enable: true,
+ notifications_enable_toggle: Toggle::new().with_label("Enable Notifications"),
+ notifications_bell: false,
+ notifications_bell_toggle: Toggle::new().with_label("Play Bell Sound"),
+ notifications_duration: 5,
+ notifications_duration_spinbox: Spinbox::new(5, 1, 60, 1)
+ .with_label("Notification Duration")
+ .with_unit("s"),
+
+ status_loaded: false,
+ status_font_size: 11,
+ status_padding: 8,
+ status_separators: true,
+ status_underline: true,
+ status_running: false,
+ status_label: Label::new("Status Interface: Stopped").with_font_size(14.0).with_color([170, 51, 51]),
+ status_separators_toggle: Toggle::new().with_label("Show Separators"),
+ status_underline_toggle: Toggle::new().with_label("Show Underline"),
+ status_padding_spinbox: Spinbox::new(8, 0, 32, 1).with_label("Side Padding").with_unit("px"),
+ }
+}
+
+const TEXT_FG: [f32; 4] = [0.83, 0.83, 0.83, 1.0];
+const TEXT_DIM: [f32; 4] = [0.53, 0.53, 0.60, 1.0];
+const ACCENT: [f32; 4] = [0.36, 0.56, 0.38, 1.0];
+const RED: [f32; 4] = [1.0, 0.33, 0.33, 1.0];
+const ORANGE: [f32; 4] = [1.0, 0.73, 0.20, 1.0];
+
+pub fn view(state: &mut ProcessesState, cx: f32, cy: f32, cw: f32, ch: f32, root_focused: bool, sec_focused: &[bool], layout: &mut dyn LayoutStrategy, ctx: &mut cce_ui::context::UiContext) -> PageContent {
+ let mut final_pc = PageContent::new();
+ let sec_w = 320.0f32;
+ let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(8);
+
+ // ── CPU Section ──
+ builder.add_section(&mut final_pc, "CPU", root_focused, |sec| {
+ let rx = sec.left;
+ if !state.loaded {
+ sec.text("Loading CPU model and utilization...", 12.0, 0.0, 12.0, TEXT_FG);
+ sec.spacing(10.0);
+ } else {
+ // CPU Info Label
+ sec.widget(&mut state.cpu_label, 12.0, sec.cw - 24.0, 26.0, ctx);
+ sec.spacing(12.0);
+
+ // CPU Usage Label
+ sec.widget(&mut state.cpu_usage_label, 12.0, sec.cw - 24.0, 26.0, ctx);
+ sec.spacing(12.0);
+
+ // CPU Temp Label
+ sec.widget(&mut state.cpu_temp_label, 12.0, sec.cw - 24.0, 26.0, ctx);
+ sec.spacing(12.0);
+
+ // Scrolling box configuration for process list
+ let list_box_x = rx + 12.0;
+ let list_box_y = sec.ay();
+ let list_box_w = sec.cw - 24.0;
+ let list_box_h = 220.0;
+
+ // Render the standardized ScrollBox widget
+ render_widget(sec.pc, &mut state.cpu_list_box, list_box_x, list_box_y, list_box_w, list_box_h, ctx);
+
+ // Header for process list columns (drawn static on top of the ScrollBox background)
+ let header_h = 22.0;
+ sec.pc.rect([0.12, 0.12, 0.16, 0.5], list_box_x + 1.0, list_box_y + 1.0, list_box_w - 2.0, header_h);
+ sec.pc.rect([0.18, 0.18, 0.24, 1.0], list_box_x + 1.0, list_box_y + header_h, list_box_w - 2.0, 1.0); // Divider
+
+ sec.pc.text("PID", list_box_x + 12.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
+ sec.pc.text("COMMAND", list_box_x + 80.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
+ sec.pc.text("CPU %", list_box_x + list_box_w - 60.0, list_box_y + 5.0, 11.0, [0.53, 0.53, 0.60, 1.0]);
+
+ let row_h = 24.0;
+ // Update ScrollingList bounds for the scrollable viewport (which starts below the header)
+ state.cpu_list_box.update_bounds(state.processes.len(), list_box_y + header_h, list_box_h - header_h - 6.0);
+
+ // Visible process rows rendering (virtualized/clipped)
+ sec.pc.push_clip_rect(list_box_x, list_box_y + header_h, list_box_w, list_box_h - header_h);
+ for (idx, (pid, cpu, comm)) in state.processes.iter().enumerate() {
+ if let Some(draw_y) = state.cpu_list_box.get_item_draw_y(idx, 4.0) {
+ // Standard row action button (transparent background, highlights on hover)
+ sec.pc.button(
+ "",
+ list_box_x + 2.0,
+ draw_y,
+ list_box_w - 16.0,
+ row_h,
+ [0.0, 0.0, 0.0, 0.0],
+ [1.0, 1.0, 1.0, 0.06],
+ [0.0, 0.0, 0.0, 0.0],
+ AppAction::Processes(ProcessesMessage::None),
+ );
+
+ sec.pc.text(pid, list_box_x + 12.0, draw_y + 6.0, 12.0, [0.80, 0.80, 0.85, 1.0]);
+ sec.pc.text(comm, list_box_x + 80.0, draw_y + 6.0, 12.0, [0.80, 0.80, 0.85, 1.0]);
+ sec.pc.text(&format!("{}%", cpu), list_box_x + list_box_w - 60.0, draw_y + 6.0, 12.0, [0.56, 0.83, 0.56, 1.0]);
+ }
+ }
+ sec.pc.pop_clip_rect();
+
+ if state.processes.is_empty() {
+ sec.pc.text("No active processes", list_box_x + 12.0, list_box_y + header_h + 16.0, 12.0, TEXT_DIM);
+ }
+
+ sec.content_y += list_box_h;
+ }
+ });
+
+ // ── GPU Section ──
+ builder.add_section(&mut final_pc, "GPU", false, |sec_gpu| {
+ if !state.loaded {
+ sec_gpu.text("Loading GPU models...", 12.0, 0.0, 12.0, TEXT_FG);
+ sec_gpu.spacing(10.0);
+ } else {
+ for (i, gpu_lbl) in state.gpu_labels.iter_mut().enumerate() {
+ if i > 0 { sec_gpu.spacing(12.0); }
+ sec_gpu.widget(gpu_lbl, 12.0, sec_gpu.cw - 24.0, 26.0, ctx);
+ }
+ }
+ });
+
+ // ── Battery Section ──
+ builder.add_section(&mut final_pc, "Battery", false, |sec_bat| {
+ if !state.loaded {
+ sec_bat.text("Loading battery status...", 12.0, 0.0, 12.0, TEXT_DIM);
+ sec_bat.spacing(18.0);
+ } else {
+ let bat = &state.battery;
+ let bat_icon = match bat.state.as_str() {
+ "charging" => "+",
+ "fully-charged" => "=",
+ _ => "",
+ };
+
+ let pct_color = if bat.percentage < 20.0 { RED }
+ else if bat.percentage < 50.0 { ORANGE }
+ else { ACCENT };
+
+ let pct_str = format!("{} {:.0}%", bat_icon, bat.percentage);
+ sec_bat.text(&pct_str, 12.0, 0.0, 24.0, pct_color);
+ sec_bat.spacing(30.0);
+
+ let state_str = format!("{} • {:.1}W • {:.1}/{:.1} Wh",
+ bat.state, bat.energy_rate, bat.energy, bat.energy_full);
+ sec_bat.text(&state_str, 12.0, 0.0, 12.0, TEXT_DIM);
+ sec_bat.spacing(18.0);
+
+ let time_str = if bat.time_to_empty > 0 {
+ format!("Time remaining: {}", format_duration(bat.time_to_empty))
+ } else if bat.time_to_full > 0 {
+ format!("Time to full: {}", format_duration(bat.time_to_full))
+ } else { String::new() };
+ if !time_str.is_empty() {
+ sec_bat.text(&time_str, 12.0, 0.0, 12.0, TEXT_DIM);
+ sec_bat.spacing(18.0);
+ }
+
+ let detail_str = format!("{} {}", bat.vendor, bat.model);
+ sec_bat.text(&detail_str, 12.0, 0.0, 11.0, TEXT_DIM);
+ sec_bat.spacing(20.0);
+
+ let ac_str = if state.on_ac { "On AC Power" } else { "On Battery" };
+ sec_bat.text(ac_str, 12.0, 0.0, 14.0, TEXT_FG);
+ sec_bat.spacing(20.0);
+ }
+ });
+
+ // ── CPU Governor section ──
+ builder.add_section(&mut final_pc, "CPU Governor", false, |sec_gov| {
+ let rx = sec_gov.left;
+ if !state.loaded {
+ sec_gov.text("Loading CPU governor...", 12.0, 0.0, 12.0, TEXT_DIM);
+ sec_gov.spacing(18.0);
+ } else {
+ sec_gov.widget(&mut state.cpu_gov_menu, 12.0, sec_gov.cw - 24.0, 26.0, ctx);
+ sec_gov.spacing(12.0);
+
+ let (info_title, info_lines) = if state.cpu_powersave {
+ (
+ "CPU Governor: Powersave",
+ vec![
+ "• Active: powersave".to_string(),
+ "• Governor set to powersave — lower power, slower burst".to_string(),
+ ],
+ )
+ } else {
+ (
+ "CPU Governor: Performance",
+ vec![
+ "• Active: performance".to_string(),
+ "• Governor set to performance".to_string(),
+ ],
+ )
+ };
+
+ let mut info_box = InfoBox::new(info_title, info_lines);
+ let info_h = 80.0;
+ let info_y = sec_gov.ay();
+ render_widget(sec_gov.pc, &mut info_box, rx + 12.0, info_y, sec_gov.cw - 24.0, info_h, ctx);
+ sec_gov.spacing(info_h + 12.0);
+ }
+ });
+
+ // ── GPU Power section ──
+ builder.add_section(&mut final_pc, "GPU Power", false, |sec_gpow| {
+ let rx = sec_gpow.left;
+ if !state.loaded {
+ sec_gpow.text("Loading GPU power status...", 12.0, 0.0, 12.0, TEXT_DIM);
+ sec_gpow.spacing(18.0);
+ } else {
+ sec_gpow.widget(&mut state.gpu_gov_menu, 12.0, sec_gpow.cw - 24.0, 26.0, ctx);
+ sec_gpow.spacing(12.0);
+
+ let (info_title, info_lines) = if state.gpu_powersave {
+ (
+ "GPU Power Limit: Eco Cap",
+ vec![
+ "• Mode: 5W Cap".to_string(),
+ "• NVIDIA power limit capped at 5W — minimal draw".to_string(),
+ ],
+ )
+ } else {
+ (
+ "GPU Power Limit: Default",
+ vec![
+ "• Mode: 80W Default".to_string(),
+ "• NVIDIA running at default power limit".to_string(),
+ ],
+ )
+ };
+
+ let mut info_box = InfoBox::new(info_title, info_lines);
+ let info_h = 80.0;
+ let info_y = sec_gpow.ay();
+ render_widget(sec_gpow.pc, &mut info_box, rx + 12.0, info_y, sec_gpow.cw - 24.0, info_h, ctx);
+ sec_gpow.spacing(info_h + 12.0);
+ }
+ });
+
+ // ── Services Section ──
+ builder.add_section_spanned(&mut final_pc, "Services", 2, sec_focused.get(5).copied().unwrap_or(false), |sec| {
+ let sec_w = sec.cw;
+ if !state.services_loaded {
+ sec.text("Loading systemd services...", 12.0, 0.0, 12.0, TEXT_DIM);
+ sec.spacing(18.0);
+ } else {
+ // Tab header buttons: System Services, User Services
+ let tab_w = (sec_w - 24.0 - 8.0) / 2.0;
+ let tab_h = 28.0;
+ let tab_y = sec.ay();
+ let active_bg = [0.20, 0.40, 0.65, 0.4];
+ let inactive_bg = [0.10, 0.10, 0.16, 0.3];
+ let hover_bg = [0.20, 0.20, 0.25, 0.15];
+
+ let label1 = if tab_w < 110.0 { "System" } else { "System Services" };
+ let label2 = if tab_w < 110.0 { "User" } else { "User Services" };
+
+ let tab_x1 = sec.left + 12.0;
+ let tab_x2 = sec.left + 12.0 + tab_w + 8.0;
+
+ sec.pc.button(
+ label1,
+ tab_x1,
+ tab_y,
+ tab_w,
+ tab_h,
+ if state.services_active_tab == ServiceTab::System { active_bg } else { inactive_bg },
+ hover_bg,
+ [0.90, 0.90, 0.95, 1.0],
+ crate::app::AppAction::Processes(ProcessesMessage::ServicesSetTab(ServiceTab::System)),
+ );
+
+ sec.pc.button(
+ label2,
+ tab_x2,
+ tab_y,
+ tab_w,
+ tab_h,
+ if state.services_active_tab == ServiceTab::User { active_bg } else { inactive_bg },
+ hover_bg,
+ [0.90, 0.90, 0.95, 1.0],
+ crate::app::AppAction::Processes(ProcessesMessage::ServicesSetTab(ServiceTab::User)),
+ );
+ sec.content_y += tab_h + 12.0;
+
+ // Search textbox
+ let search_y = sec.ay();
+ let search_w = sec_w - 24.0;
+ let search_h = 46.0;
+
+ state.services_search_box.set_row_rect(sec.left + 12.0, search_w);
+ cce_ui::layout::render_widget(
+ sec.pc,
+ &mut state.services_search_box,
+ sec.left + 12.0,
+ search_y,
+ search_w,
+ search_h,
+ ctx,
+ );
+ sec.content_y += search_h + 16.0;
+
+ // Scroll box list
+ let list_box_x = sec.left + 12.0;
+ let list_box_y = sec.ay();
+ let list_box_w = sec_w - 24.0;
+ let list_box_h = 360.0;
+
+ cce_ui::layout::render_widget(sec.pc, &mut state.services_list_box, list_box_x, list_box_y, list_box_w, list_box_h, ctx);
+
+ // Filter services
+ let query = if state.services_search_box.editing {
+ state.services_search_box.edit_buffer.to_lowercase()
+ } else {
+ state.services_search_box.text.to_lowercase()
+ };
+ let filtered_services: Vec<&ServiceInfo> = state.services.iter()
+ .filter(|s| s.is_system == (state.services_active_tab == ServiceTab::System))
+ .filter(|s| s.name.to_lowercase().contains(&query) || s.description.to_lowercase().contains(&query))
+ .collect();
+
+ // Update ScrollingList bounds
+ state.services_list_box.update_bounds(filtered_services.len(), list_box_y, list_box_h);
+
+ let item_h = state.services_list_box.item_height;
+
+ if state.service_items.len() != filtered_services.len() {
+ state.service_items.clear();
+ for _ in 0..filtered_services.len() {
+ state.service_items.push(InteractiveListItem::new(""));
+ }
+ }
+
+ sec.pc.push_clip_rect(list_box_x, list_box_y, list_box_w, list_box_h);
+ for (idx, service) in filtered_services.iter().enumerate() {
+ if let Some(draw_y) = state.services_list_box.get_item_draw_y(idx, 4.0) {
+ let is_active = service.active_state == "active" || service.sub_state == "running";
+
+ // Control buttons: Start, Stop, Restart on the right
+ let is_small = sec_w < 350.0;
+ let btn_w = if is_small { 24.0 } else { 46.0 };
+ let r_btn_w = if is_small { 24.0 } else { 54.0 };
+ let btn_gap = if is_small { 4.0 } else { 6.0 };
+ let right_edge = list_box_x + list_box_w - 24.0 - 8.0;
+
+ let restart_x = right_edge - r_btn_w;
+ let stop_x = restart_x - btn_gap - btn_w;
+ let start_x = stop_x - btn_gap - btn_w;
+
+ let btn_y = draw_y + (item_h - 22.0) / 2.0;
+ let btn_h = 22.0;
+
+ // Service Description (Truncate dynamically based on remaining space before Start button)
+ let text_max_w = (start_x - 8.0) - (list_box_x + 32.0);
+ let max_chars = ((text_max_w / 6.0) as usize).max(10);
+ let desc = if service.description.is_empty() { "No description" } else { &service.description };
+ let desc_truncated = if desc.len() > max_chars {
+ format!("{}...", &desc[..max_chars.saturating_sub(3)])
+ } else {
+ desc.to_string()
+ };
+
+ // Render InteractiveListItem background and text labels
+ let item_btn = &mut state.service_items[idx];
+ item_btn.title = service.name.clone();
+ item_btn.subtitle = Some(desc_truncated);
+ cce_ui::layout::render_widget(sec.pc, item_btn, list_box_x + 24.0, draw_y, list_box_w - 44.0, item_h, ctx);
+
+ // Render StatusDot
+ let status_dot_state = if service.active_state == "failed" {
+ DotStatus::Error
+ } else if is_active {
+ DotStatus::Active
+ } else {
+ DotStatus::Inactive
+ };
+ let mut dot = StatusDot::new(status_dot_state);
+ cce_ui::layout::render_widget(sec.pc, &mut dot, list_box_x + 10.0, draw_y + (item_h - 10.0) / 2.0, 10.0, 10.0, ctx);
+
+ let active_txt = [0.90, 0.90, 0.95, 1.0];
+ let disabled_txt = [0.40, 0.40, 0.45, 1.0];
+
+ let start_lbl = if is_small { "▶" } else { "Start" };
+ let stop_lbl = if is_small { "■" } else { "Stop" };
+ let restart_lbl = if is_small { "⟳" } else { "Restart" };
+
+ // Start button
+ sec.pc.button(
+ start_lbl,
+ start_x,
+ btn_y,
+ btn_w,
+ btn_h,
+ if !is_active { [0.16, 0.35, 0.18, 0.4] } else { [0.12, 0.12, 0.16, 0.1] },
+ [0.22, 0.45, 0.25, 0.6],
+ if !is_active { active_txt } else { disabled_txt },
+ crate::app::AppAction::Processes(ProcessesMessage::ServicesStart(service.name.clone(), service.is_system)),
+ );
+
+ // Stop button
+ sec.pc.button(
+ stop_lbl,
+ stop_x,
+ btn_y,
+ btn_w,
+ btn_h,
+ if is_active { [0.55, 0.16, 0.16, 0.3] } else { [0.12, 0.12, 0.16, 0.1] },
+ [0.70, 0.22, 0.22, 0.5],
+ if is_active { active_txt } else { disabled_txt },
+ crate::app::AppAction::Processes(ProcessesMessage::ServicesStop(service.name.clone(), service.is_system)),
+ );
+
+ // Restart button
+ sec.pc.button(
+ restart_lbl,
+ restart_x,
+ btn_y,
+ r_btn_w,
+ btn_h,
+ [0.15, 0.28, 0.45, 0.3],
+ [0.20, 0.38, 0.58, 0.5],
+ active_txt,
+ crate::app::AppAction::Processes(ProcessesMessage::ServicesRestart(service.name.clone(), service.is_system)),
+ );
+ }
+ }
+ sec.pc.pop_clip_rect();
+
+ if filtered_services.is_empty() {
+ sec.pc.text("No services match the query", list_box_x + 16.0, list_box_y + 16.0, 12.0, TEXT_DIM);
+ }
+
+ sec.content_y += list_box_h;
+ }
+ });
+
+ // ── System Notifications ──
+ builder.add_section(&mut final_pc, "System Notifications", sec_focused.get(6).copied().unwrap_or(false), |sec2| {
+ let sec_w = sec2.cw;
+ state.notifications_enable_toggle.set_toggled(state.notifications_enable);
+ sec2.widget_full(&mut state.notifications_enable_toggle, cce_ui::layout::toggle_height(), ctx);
+ sec2.spacing(8.0);
+
+ state.notifications_bell_toggle.set_toggled(state.notifications_bell);
+ sec2.widget_full(&mut state.notifications_bell_toggle, cce_ui::layout::toggle_height(), ctx);
+ sec2.spacing(16.0);
+
+ state.notifications_duration_spinbox.value = state.notifications_duration;
+ state.notifications_duration_spinbox.set_label("Notification Duration");
+ sec2.widget(&mut state.notifications_duration_spinbox, 14.0, sec_w - 28.0, 44.0, ctx);
+ sec2.spacing(16.0);
+
+ let btn_h = 32.0;
+ let btn_y = sec2.ay();
+ let white_color = [1.0, 1.0, 1.0, 1.0];
+ let btn_bg = [0.20, 0.40, 0.65, 1.0];
+ let btn_hover = [0.28, 0.50, 0.78, 1.0];
+
+ let cols = sec2.row_layout(1, 0.0);
+ if let Some(&(x, w)) = cols.first() {
+ sec2.button(
+ "Send Test Notification",
+ x,
+ btn_y,
+ w,
+ btn_h,
+ btn_bg,
+ btn_hover,
+ white_color,
+ AppAction::Processes(ProcessesMessage::SendTestNotification),
+ );
+ }
+ sec2.spacing(12.0);
+ });
+
+ // ── Status Interface ──
+ builder.add_section(&mut final_pc, "Status Interface", sec_focused.get(7).copied().unwrap_or(false), |sec3| {
+ let sec_w = sec3.cw;
+ if !state.status_loaded {
+ sec3.text("Loading Status Interface status...", 12.0, 0.0, 12.0, TEXT_DIM);
+ sec3.spacing(18.0);
+ } else {
+ // Status
+ let status_text = if state.status_running { "Status Interface: Running" } else { "Status Interface: Stopped" };
+ let status_color = if state.status_running { [92, 143, 97] } else { [170, 51, 51] };
+ state.status_label.set_text(status_text);
+ state.status_label.set_color(status_color);
+ sec3.widget(&mut state.status_label, 12.0, sec_w - 24.0, 20.0, ctx);
+ sec3.spacing(12.0);
+
+ sec3.spacing(4.0);
+
+ // Separators toggle
+ state.status_separators_toggle.set_toggled(state.status_separators);
+ sec3.widget_full(&mut state.status_separators_toggle, cce_ui::layout::toggle_height(), ctx);
+ sec3.spacing(16.0);
+
+ // Underline toggle
+ state.status_underline_toggle.set_toggled(state.status_underline);
+ sec3.widget_full(&mut state.status_underline_toggle, cce_ui::layout::toggle_height(), ctx);
+ sec3.spacing(16.0);
+
+ // Padding spinbox
+ state.status_padding_spinbox.value = state.status_padding as i32;
+ sec3.widget(&mut state.status_padding_spinbox, 12.0, sec_w - 24.0, 44.0, ctx);
+ sec3.spacing(16.0);
+
+ // Reload button
+ let yt_reload = sec3.ay();
+ let btn_w = sec_w - 24.0;
+ let button_x = sec3.left + 12.0;
+ sec3.button(
+ "Reload Status Interface",
+ button_x,
+ yt_reload,
+ btn_w,
+ 32.0,
+ [0.13, 0.18, 0.14, 1.0],
+ [0.25, 0.30, 0.26, 1.0],
+ [1.0, 1.0, 1.0, 1.0],
+ AppAction::Processes(ProcessesMessage::StatusReload),
+ );
+ sec3.spacing(12.0);
+ }
+ });
+
+ final_pc
+}
+
+pub fn update(state: &mut ProcessesState, msg: ProcessesMessage) {
+ match msg {
+ ProcessesMessage::Refreshed(new) => {
+ state.loaded = new.loaded;
+ state.cpu_model = new.cpu_model;
+ state.cpu_usage = new.cpu_usage;
+ state.cpu_cores = new.cpu_cores;
+ state.gpus = new.gpus;
+ state.cpu_label = new.cpu_label;
+ state.cpu_usage_label = new.cpu_usage_label;
+ state.cpu_temp_label = new.cpu_temp_label;
+ state.gpu_labels = new.gpu_labels;
+ state.processes = new.processes;
+ let old_scroll = state.cpu_list_box.scroll_y();
+ state.cpu_list_box = new.cpu_list_box;
+ state.cpu_list_box.set_scroll_y(old_scroll);
+
+ state.battery = new.battery;
+ state.on_ac = new.on_ac;
+ state.cpu_powersave = new.cpu_powersave;
+ state.gpu_powersave = new.gpu_powersave;
+ state.cpu_gov_menu.selected = new.cpu_gov_menu.selected;
+ state.gpu_gov_menu.selected = new.gpu_gov_menu.selected;
+ }
+ ProcessesMessage::SetCpuPerformance => {
+ state.cpu_powersave = false;
+ state.cpu_gov_menu.selected = 0;
+ spawn_cpu_power(false);
+ }
+ ProcessesMessage::SetCpuPowersave => {
+ state.cpu_powersave = true;
+ state.cpu_gov_menu.selected = 1;
+ spawn_cpu_power(true);
+ }
+ ProcessesMessage::SetGpuDefault => {
+ state.gpu_powersave = false;
+ state.gpu_gov_menu.selected = 0;
+ spawn_gpu_power(false);
+ }
+ ProcessesMessage::SetGpuPowersave => {
+ state.gpu_powersave = true;
+ state.gpu_gov_menu.selected = 1;
+ spawn_gpu_power(true);
+ }
+ ProcessesMessage::ServicesRefreshed(new_services) => {
+ state.services_loaded = true;
+ state.services = new_services;
+ state.service_items.clear();
+ }
+ ProcessesMessage::ServicesSetTab(tab) => {
+ state.services_active_tab = tab;
+ state.services_list_box.set_scroll_y(0.0);
+ state.service_items.clear();
+ }
+ ProcessesMessage::ServicesStart(name, is_system) => {
+ if let Some(srv) = state.services.iter_mut().find(|s| s.name == name && s.is_system == is_system) {
+ srv.active_state = "activating".to_string();
+ srv.sub_state = "starting".to_string();
+ }
+ service_action(&name, "start", is_system);
+ }
+ ProcessesMessage::ServicesStop(name, is_system) => {
+ if let Some(srv) = state.services.iter_mut().find(|s| s.name == name && s.is_system == is_system) {
+ srv.active_state = "deactivating".to_string();
+ srv.sub_state = "stopping".to_string();
+ }
+ service_action(&name, "stop", is_system);
+ }
+ ProcessesMessage::ServicesRestart(name, is_system) => {
+ if let Some(srv) = state.services.iter_mut().find(|s| s.name == name && s.is_system == is_system) {
+ srv.active_state = "activating".to_string();
+ srv.sub_state = "restarting".to_string();
+ }
+ service_action(&name, "restart", is_system);
+ }
+ ProcessesMessage::ToggleNotificationsEnable => {
+ state.notifications_enable = !state.notifications_enable;
+ write_enable_notifications(state.notifications_enable);
+ }
+ ProcessesMessage::ToggleNotificationsBell => {
+ state.notifications_bell = !state.notifications_bell;
+ write_config_value("bell", &state.notifications_bell.to_string());
+ }
+ ProcessesMessage::SetNotificationsDuration(d) => {
+ state.notifications_duration = d;
+ write_config_value("duration", &state.notifications_duration.to_string());
+ }
+ ProcessesMessage::SendTestNotification => {
+ tokio::spawn(async move {
+ if let Ok(connection) = zbus::Connection::session().await {
+ let _ = connection.call_method(
+ Some("org.freedesktop.Notifications"),
+ "/org/freedesktop/Notifications",
+ Some("org.freedesktop.Notifications"),
+ "Notify",
+ &(
+ "cce-client",
+ 0u32,
+ "",
+ "System notifications are working correctly!",
+ "",
+ Vec::<&str>::new(),
+ std::collections::HashMap::<&str, zbus::zvariant::Value>::new(),
+ -1i32,
+ )
+ ).await;
+ }
+ });
+ }
+ ProcessesMessage::NotificationsRefreshed(new) => {
+ state.notifications_loaded = true;
+ state.notifications_enable = new.enable;
+ state.notifications_bell = new.bell;
+ state.notifications_duration = new.duration;
+ }
+ ProcessesMessage::StatusRefreshed(new) => {
+ 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();
+
+ state.status_loaded = true;
+ state.status_font_size = new.font_size;
+ state.status_padding = new.padding;
+ state.status_separators = new.separators;
+ state.status_underline = new.underline;
+ state.status_running = new.running;
+
+ 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);
+ }
+ ProcessesMessage::StatusToggleSeparators => {
+ state.status_separators = !state.status_separators;
+ write_status_separators(state.status_separators);
+ status_interface_reload();
+ }
+ ProcessesMessage::StatusToggleUnderline => {
+ state.status_underline = !state.status_underline;
+ write_status_underline(state.status_underline);
+ status_interface_reload();
+ }
+ ProcessesMessage::StatusSetPadding(val) => {
+ state.status_padding = val;
+ write_status_padding(val);
+ status_interface_reload();
+ }
+ ProcessesMessage::StatusReload => {
+ status_interface_reload();
+ }
+ ProcessesMessage::None => {}
+ }
+}
+
+// ── Background Fetching ──
+
+pub async fn fetch_services() -> Vec<ServiceInfo> {
+ let mut services = Vec::new();
+
+ // 1. Fetch system-level services
+ if let Ok(output) = tokio::process::Command::new("systemctl")
+ .args(["list-units", "--type=service", "--all", "--no-legend"])
+ .output()
+ .await
+ {
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ for line in stdout.lines() {
+ if let Some(info) = parse_service_line(line, true) {
+ services.push(info);
+ }
+ }
+ }
+
+ // 2. Fetch user-level services
+ if let Ok(output) = tokio::process::Command::new("systemctl")
+ .args(["--user", "list-units", "--type=service", "--all", "--no-legend"])
+ .output()
+ .await
+ {
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ for line in stdout.lines() {
+ if let Some(info) = parse_service_line(line, false) {
+ services.push(info);
+ }
+ }
+ }
+
+ // Sort alphabetically by name
+ services.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
+ services
+}
+
+fn parse_service_line(line: &str, is_system: bool) -> Option<ServiceInfo> {
+ let cleaned = line.trim_start_matches('●').trim();
+ if cleaned.is_empty() {
+ return None;
+ }
+ let parts: Vec<&str> = cleaned.split_whitespace().collect();
+ if parts.len() >= 4 && parts[0].ends_with(".service") {
+ let name = parts[0].to_string();
+ let _load = parts[1];
+ let active_state = parts[2].to_string();
+ let sub_state = parts[3].to_string();
+ let description = parts[4..].join(" ");
+ Some(ServiceInfo {
+ name,
+ description,
+ active_state,
+ sub_state,
+ is_system,
+ })
+ } else {
+ None
+ }
+}
+
+fn service_action(name: &str, action: &str, is_system: bool) {
+ if is_system {
+ // System service needs root privilege, spawn via pkexec
+ let _ = tokio::process::Command::new("pkexec")
+ .args(["systemctl", action, name])
+ .spawn();
+ } else {
+ let _ = tokio::process::Command::new("systemctl")
+ .args(["--user", action, name])
+ .spawn();
+ }
+}
+
+// ── Notifications Configuration Reader & Writer ──
+
+const CONFIG_PATH: &str = "/home/lsgalante/.config/cce/config.json";
+
+fn get_socket_path() -> String {
+ match std::env::var("WAYLAND_DISPLAY") {
+ Ok(display) => format!("/tmp/cce-{}.sock", display),
+ Err(_) => "/tmp/cce.sock".to_string(),
+ }
+}
+
+pub fn read_notifications_config() -> NotificationsConfig {
+ let content = fs::read_to_string(CONFIG_PATH).unwrap_or_default();
+ let enable = parse_notifications_enable(&content);
+ let bell = parse_notifications_bell(&content);
+ let duration = parse_notifications_duration(&content);
+ NotificationsConfig {
+ enable,
+ bell,
+ duration,
+ }
+}
+
+fn parse_json(content: &str) -> serde_json::Value {
+ serde_json::from_str(content).unwrap_or_default()
+}
+
+fn parse_notifications_enable(content: &str) -> bool {
+ let val = parse_json(content);
+ val["notifications"]["enable"].as_bool().unwrap_or(true)
+}
+
+fn parse_notifications_bell(content: &str) -> bool {
+ let val = parse_json(content);
+ val["notifications"]["bell"].as_bool().unwrap_or(false)
+}
+
+fn parse_notifications_duration(content: &str) -> i32 {
+ let val = parse_json(content);
+ val["notifications"]["duration"].as_i64().map(|v| v as i32).unwrap_or(5)
+}
+
+fn send_ipc_command(cmd: &str) {
+ if let Ok(mut stream) = std::os::unix::net::UnixStream::connect(get_socket_path()) {
+ let _ = stream.write_all(format!("{}\n", cmd).as_bytes());
+ }
+}
+
+fn write_config_value(key: &str, value: &str) {
+ let content = fs::read_to_string(CONFIG_PATH).unwrap_or_default();
+ let mut val = parse_json(&content);
+ let j_val = if let Ok(b) = value.parse::<bool>() {
+ serde_json::json!(b)
+ } else if let Ok(n) = value.parse::<i64>() {
+ serde_json::json!(n)
+ } else if let Ok(f) = value.parse::<f64>() {
+ serde_json::json!(f)
+ } else {
+ serde_json::json!(value)
+ };
+ if let Some(notifications) = val.get_mut("notifications").and_then(|n| n.as_object_mut()) {
+ notifications.insert(key.to_string(), j_val);
+ } else {
+ let mut map = serde_json::Map::new();
+ map.insert(key.to_string(), j_val);
+ if let Some(obj) = val.as_object_mut() {
+ obj.insert("notifications".to_string(), serde_json::Value::Object(map));
+ }
+ }
+ if let Ok(updated_str) = serde_json::to_string_pretty(&val) {
+ let _ = fs::write(CONFIG_PATH, updated_str);
+ }
+}
+
+fn write_enable_notifications(enabled: bool) {
+ write_config_value("enable", &enabled.to_string());
+ send_ipc_command("reload");
+}
+
+
+thread_local! {
+ static TEST_CONFIG_PATH: std::cell::RefCell<Option<String>> = std::cell::RefCell::new(None);
+}
+
+fn get_config_path() -> String {
+ #[cfg(test)]
+ {
+ TEST_CONFIG_PATH.with(|p| {
+ if let Some(path) = p.borrow().as_ref() {
+ return path.clone();
+ }
+ "/home/lsgalante/.config/cce/config.json".to_string()
+ })
+ }
+ #[cfg(not(test))]
+ {
+ "/home/lsgalante/.config/cce/config.json".to_string()
+ }
+}
+
+fn write_status_value(key: &str, value: &str) {
+ crate::pages::interface::write_config_value_path(&get_config_path(), key, value);
+}
+
+fn read_status_font_size() -> Option<u16> {
+ let content = std::fs::read_to_string(&get_config_path()).ok()?;
+ Some(parse_u16_from(&content, "status_font_size", 11))
+}
+
+
+fn read_status_padding() -> Option<u16> {
+ let content = std::fs::read_to_string(&get_config_path()).ok()?;
+ Some(parse_u16_from(&content, "status_padding", 8))
+}
+
+fn write_status_padding(padding: u16) {
+ write_status_value("status_padding", &padding.to_string());
+}
+
+fn read_status_separators() -> Option<bool> {
+ let content = std::fs::read_to_string(&get_config_path()).ok()?;
+ Some(crate::pages::interface::parse_bool_from(&content, "status_separators", true))
+}
+
+fn write_status_separators(val: bool) {
+ write_status_value("status_separators", &val.to_string());
+}
+
+fn read_status_underline() -> Option<bool> {
+ let content = std::fs::read_to_string(&get_config_path()).ok()?;
+ Some(crate::pages::interface::parse_bool_from(&content, "status_underline", true))
+}
+
+fn write_status_underline(val: bool) {
+ write_status_value("status_underline", &val.to_string());
+}
+
+fn status_interface_reload() {
+ let _ = std::process::Command::new("pkill")
+ .args(["-f", "cce-status-interface"])
+ .status();
+ std::thread::sleep(std::time::Duration::from_millis(150));
+ send_ipc_command("spawn cce-status-interface");
+}
+
+pub async fn fetch_status_state() -> StatusData {
+ let running = tokio::process::Command::new("pgrep")
+ .args(["-f", "cce-status-interface"]).output().await.ok()
+ .map(|o| !o.stdout.is_empty())
+ .unwrap_or(false);
+
+ let font_size = read_status_font_size().unwrap_or(11);
+ let padding = read_status_padding().unwrap_or(8);
+ let separators = read_status_separators().unwrap_or(true);
+ let underline = read_status_underline().unwrap_or(true);
+
+ StatusData {
+ font_size,
+ padding,
+ separators,
+ underline,
+ running,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_view_layout_grid() {
+ let mut state = ProcessesState::default();
+ let mut layout = cce_ui::layout::ColumnLayout::new(20.0);
+ let sec_focused = vec![false, false, false, false, false, false, false, false];
+ let mut ctx = cce_ui::context::UiContext::new();
+ let pc = view(&mut state, 10.0, 20.0, 800.0, 600.0, false, &sec_focused, &mut layout, &mut ctx);
+ assert!(!pc.rects.is_empty() || !pc.texts.is_empty());
+ }
+
+ #[test]
+ fn test_parse_notifications_enable_default() {
+ assert!(parse_notifications_enable(""));
+ assert!(parse_notifications_enable("[layout]\ngap = 18\n"));
+ }
+
+ #[test]
+ fn test_parse_notifications_enable_explicit() {
+ let content = "{\"notifications\": {\"enable\": false}}";
+ assert!(!parse_notifications_enable(content));
+
+ let content = "{\"notifications\": {\"enable\": true}}";
+ assert!(parse_notifications_enable(content));
+ }
+
+ #[test]
+ fn test_parse_notifications_enable_other_sections() {
+ let content = r#"{
+ "layout": {"enable": false},
+ "notifications": {"enable": true},
+ "input": {"enable": false}
+ }"#;
+ assert!(parse_notifications_enable(content));
+
+ let content = r#"{
+ "layout": {"enable": true},
+ "notifications": {"enable": false},
+ "input": {"enable": true}
+ }"#;
+ assert!(!parse_notifications_enable(content));
+ }
+
+ #[test]
+ fn test_parse_notifications_duration_default() {
+ assert_eq!(parse_notifications_duration(""), 5);
+ assert_eq!(parse_notifications_duration("{\"notifications\": {}}"), 5);
+ }
+
+ #[test]
+ fn test_parse_notifications_duration_explicit() {
+ let content = "{\"notifications\": {\"duration\": 10}}";
+ assert_eq!(parse_notifications_duration(content), 10);
+ }
+
+ #[test]
+ fn test_read_write_separators() {
+ let dir = std::env::temp_dir();
+ let path = dir.join("test_status_separators.json");
+ let path_str = path.to_str().unwrap().to_string();
+
+ let _ = fs::write(&path_str, "{\"layout\": {\"status_separators\": true, \"status_padding\": 8}}");
+ TEST_CONFIG_PATH.with(|p| *p.borrow_mut() = Some(path_str));
+
+ let original = read_status_separators().unwrap_or(true);
+ write_status_separators(!original);
+ assert_eq!(read_status_separators(), Some(!original));
+ write_status_separators(original);
+ assert_eq!(read_status_separators(), Some(original));
+
+ let _ = fs::remove_file(path);
+ }
+
+ #[test]
+ fn test_read_write_padding() {
+ let dir = std::env::temp_dir();
+ let path = dir.join("test_status_padding.json");
+ let path_str = path.to_str().unwrap().to_string();
+
+ let _ = fs::write(&path_str, "{\"layout\": {\"status_separators\": true, \"status_padding\": 8}}");
+ TEST_CONFIG_PATH.with(|p| *p.borrow_mut() = Some(path_str));
+
+ let original = read_status_padding().unwrap_or(8);
+ write_status_padding(12);
+ assert_eq!(read_status_padding(), Some(12));
+ write_status_padding(original);
+ assert_eq!(read_status_padding(), Some(original));
+
+ let _ = fs::remove_file(path);
+ }
+
+ #[test]
+ fn test_read_write_underline() {
+ let dir = std::env::temp_dir();
+ let path = dir.join("test_status_underline.json");
+ let path_str = path.to_str().unwrap().to_string();
+
+ let _ = fs::write(&path_str, "{\"layout\": {\"status_underline\": true, \"status_padding\": 8}}");
+ TEST_CONFIG_PATH.with(|p| *p.borrow_mut() = Some(path_str));
+
+ let original = read_status_underline().unwrap_or(true);
+ write_status_underline(!original);
+ assert_eq!(read_status_underline(), Some(!original));
+ write_status_underline(original);
+ assert_eq!(read_status_underline(), Some(original));
+
+ let _ = fs::remove_file(path);
+ }
+}
diff --git a/src/pages/services.rs b/src/pages/services.rs
deleted file mode 100644
index 32d1679..0000000
--- a/src/pages/services.rs
+++ /dev/null
@@ -1,894 +0,0 @@
-use std::fs;
-use std::io::Write;
-use crate::app::{AppAction, PageContent, SectionContextExt};
-use cce_ui::layout::{PageLayoutBuilder, LayoutStrategy};
-use cce_ui::widget::{Element, ScrollingList, TextBox, StatusDot, DotStatus, InteractiveListItem, Toggle, Spinbox, Label};
-use crate::pages::interface::parse_u16_from;
-
-// ── Notifications Data and Settings Configuration ──
-
-#[derive(Debug, Clone)]
-pub struct NotificationsConfig {
- pub enable: bool,
- pub bell: bool,
- pub duration: i32,
-}
-
-// ── Status Interface Data ──
-
-#[derive(Debug, Clone)]
-pub struct StatusData {
- pub font_size: u16,
- pub padding: u16,
- pub separators: bool,
- pub underline: bool,
- pub running: bool,
-}
-
-// ── Service Types and Page State ──
-
-#[derive(Debug, Clone)]
-pub struct ServiceInfo {
- pub name: String,
- pub description: String,
- pub active_state: String,
- pub sub_state: String,
- pub is_system: bool,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum ServiceTab {
- System,
- User,
-}
-
-impl Default for ServiceTab {
- fn default() -> Self {
- ServiceTab::System
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct ServicesState {
- pub loaded: bool,
- pub services: Vec<ServiceInfo>,
- pub active_tab: ServiceTab,
- pub search_box: TextBox,
- pub list_box: ScrollingList,
- pub service_items: Vec<InteractiveListItem>,
- pub notifications_loaded: bool,
- pub notifications_enable: bool,
- pub notifications_enable_toggle: Toggle,
- pub notifications_bell: bool,
- pub notifications_bell_toggle: Toggle,
- pub notifications_duration: i32,
- pub notifications_duration_spinbox: Spinbox,
-
- // Status Interface fields
- pub status_loaded: bool,
- pub status_font_size: u16,
- pub status_padding: u16,
- pub status_separators: bool,
- pub status_underline: bool,
- pub status_running: bool,
- pub status_label: Label,
- pub status_separators_toggle: Toggle,
- pub status_underline_toggle: Toggle,
- pub status_padding_spinbox: Spinbox,
-}
-
-impl Default for ServicesState {
- fn default() -> Self {
- Self {
- loaded: false,
- services: Vec::new(),
- active_tab: ServiceTab::System,
- search_box: TextBox::new(String::new()).with_label("Filter Services"),
- list_box: ScrollingList::new(36.0, 6.0),
- service_items: Vec::new(),
- notifications_loaded: false,
- notifications_enable: true,
- notifications_enable_toggle: Toggle::new().with_label("Enable Notifications"),
- notifications_bell: false,
- notifications_bell_toggle: Toggle::new().with_label("Play Bell Sound"),
- notifications_duration: 5,
- notifications_duration_spinbox: Spinbox::new(5, 1, 60, 1)
- .with_label("Notification Duration")
- .with_unit("s"),
-
- // Status Interface default initialization
- status_loaded: false,
- status_font_size: 11,
- status_padding: 8,
- status_separators: true,
- status_underline: true,
- status_running: false,
- status_label: Label::new("Status Interface: Stopped").with_font_size(14.0).with_color([170, 51, 51]),
- status_separators_toggle: Toggle::new().with_label("Show Separators"),
- status_underline_toggle: Toggle::new().with_label("Show Underline"),
- status_padding_spinbox: Spinbox::new(8, 0, 32, 1).with_label("Side Padding").with_unit("px"),
- }
- }
-}
-
-#[derive(Debug, Clone)]
-pub enum ServicesMessage {
- Refreshed(Vec<ServiceInfo>),
- SetTab(ServiceTab),
- Start(String, bool),
- Stop(String, bool),
- Restart(String, bool),
- ToggleNotificationsEnable,
- ToggleNotificationsBell,
- SetNotificationsDuration(i32),
- SendTestNotification,
- NotificationsRefreshed(NotificationsConfig),
-
- // Status Interface variants
- StatusRefreshed(StatusData),
- StatusToggleSeparators,
- StatusToggleUnderline,
- StatusReload,
- StatusSetPadding(u16),
-}
-
-// ── Background Fetching ──
-
-pub async fn fetch_services() -> Vec<ServiceInfo> {
- let mut services = Vec::new();
-
- // 1. Fetch system-level services
- if let Ok(output) = tokio::process::Command::new("systemctl")
- .args(["list-units", "--type=service", "--all", "--no-legend"])
- .output()
- .await
- {
- let stdout = String::from_utf8_lossy(&output.stdout);
- for line in stdout.lines() {
- if let Some(info) = parse_service_line(line, true) {
- services.push(info);
- }
- }
- }
-
- // 2. Fetch user-level services
- if let Ok(output) = tokio::process::Command::new("systemctl")
- .args(["--user", "list-units", "--type=service", "--all", "--no-legend"])
- .output()
- .await
- {
- let stdout = String::from_utf8_lossy(&output.stdout);
- for line in stdout.lines() {
- if let Some(info) = parse_service_line(line, false) {
- services.push(info);
- }
- }
- }
-
- // Sort alphabetically by name
- services.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
- services
-}
-
-fn parse_service_line(line: &str, is_system: bool) -> Option<ServiceInfo> {
- let cleaned = line.trim_start_matches('●').trim();
- if cleaned.is_empty() {
- return None;
- }
- let parts: Vec<&str> = cleaned.split_whitespace().collect();
- if parts.len() >= 4 && parts[0].ends_with(".service") {
- let name = parts[0].to_string();
- let _load = parts[1];
- let active_state = parts[2].to_string();
- let sub_state = parts[3].to_string();
- let description = parts[4..].join(" ");
- Some(ServiceInfo {
- name,
- description,
- active_state,
- sub_state,
- is_system,
- })
- } else {
- None
- }
-}
-
-fn service_action(name: &str, action: &str, is_system: bool) {
- if is_system {
- // System service needs root privilege, spawn via pkexec
- let _ = tokio::process::Command::new("pkexec")
- .args(["systemctl", action, name])
- .spawn();
- } else {
- // User service does not need root
- let _ = tokio::process::Command::new("systemctl")
- .args(["--user", action, name])
- .spawn();
- }
-}
-
-// ── View & Update ──
-
-const TEXT_DIM: [f32; 4] = [0.53, 0.53, 0.60, 1.0];
-
-pub fn view(state: &mut ServicesState, cx: f32, cy: f32, cw: f32, ch: f32, sec_focused: &[bool], layout: &mut dyn LayoutStrategy, ctx: &mut cce_ui::context::UiContext) -> PageContent {
- let mut final_pc = PageContent::new();
- let sec_w = 360.0f32;
- let mut builder = PageLayoutBuilder::new(layout, cx, cy, cw, ch, sec_w).with_section_count(3);
-
- builder.add_section_spanned(&mut final_pc, "Services", 2, sec_focused.first().copied().unwrap_or(false), |sec| {
- let sec_w = sec.cw;
- if !state.loaded {
- sec.text("Loading systemd services...", 12.0, 0.0, 12.0, TEXT_DIM);
- sec.spacing(18.0);
- } else {
- // Tab header buttons: System Services, User Services
- let tab_w = (sec_w - 24.0 - 8.0) / 2.0;
- let tab_h = 28.0;
- let tab_y = sec.ay();
- let active_bg = [0.20, 0.40, 0.65, 0.4];
- let inactive_bg = [0.10, 0.10, 0.16, 0.3];
- let hover_bg = [0.20, 0.20, 0.25, 0.15];
-
- let label1 = if tab_w < 110.0 { "System" } else { "System Services" };
- let label2 = if tab_w < 110.0 { "User" } else { "User Services" };
-
- let tab_x1 = sec.left + 12.0;
- let tab_x2 = sec.left + 12.0 + tab_w + 8.0;
-
- sec.pc.button(
- label1,
- tab_x1,
- tab_y,
- tab_w,
- tab_h,
- if state.active_tab == ServiceTab::System { active_bg } else { inactive_bg },
- hover_bg,
- [0.90, 0.90, 0.95, 1.0],
- crate::app::AppAction::Services(ServicesMessage::SetTab(ServiceTab::System)),
- );
-
- sec.pc.button(
- label2,
- tab_x2,
- tab_y,
- tab_w,
- tab_h,
- if state.active_tab == ServiceTab::User { active_bg } else { inactive_bg },
- hover_bg,
- [0.90, 0.90, 0.95, 1.0],
- crate::app::AppAction::Services(ServicesMessage::SetTab(ServiceTab::User)),
- );
- sec.content_y += tab_h + 12.0;
-
- // Search textbox
- let search_y = sec.ay();
- let search_w = sec_w - 24.0;
- let search_h = 46.0;
-
- state.search_box.set_row_rect(sec.left + 12.0, search_w);
- cce_ui::layout::render_widget(
- sec.pc,
- &mut state.search_box,
- sec.left + 12.0,
- search_y,
- search_w,
- search_h,
- ctx,
- );
- sec.content_y += search_h + 16.0;
-
- // Scroll box list
- let list_box_x = sec.left + 12.0;
- let list_box_y = sec.ay();
- let list_box_w = sec_w - 24.0;
- let list_box_h = 360.0;
-
- cce_ui::layout::render_widget(sec.pc, &mut state.list_box, list_box_x, list_box_y, list_box_w, list_box_h, ctx);
-
- // Filter services
- let query = if state.search_box.editing {
- state.search_box.edit_buffer.to_lowercase()
- } else {
- state.search_box.text.to_lowercase()
- };
- let filtered_services: Vec<&ServiceInfo> = state.services.iter()
- .filter(|s| s.is_system == (state.active_tab == ServiceTab::System))
- .filter(|s| s.name.to_lowercase().contains(&query) || s.description.to_lowercase().contains(&query))
- .collect();
-
- // Update ScrollingList bounds
- state.list_box.update_bounds(filtered_services.len(), list_box_y, list_box_h);
-
- let item_h = state.list_box.item_height;
-
- if state.service_items.len() != filtered_services.len() {
- state.service_items.clear();
- for _ in 0..filtered_services.len() {
- state.service_items.push(InteractiveListItem::new(""));
- }
- }
-
- for (idx, service) in filtered_services.iter().enumerate() {
- if let Some(draw_y) = state.list_box.get_item_draw_y(idx, 4.0) {
- let is_active = service.active_state == "active" || service.sub_state == "running";
-
- // Control buttons: Start, Stop, Restart on the right
- let is_small = sec_w < 350.0;
- let btn_w = if is_small { 24.0 } else { 46.0 };
- let r_btn_w = if is_small { 24.0 } else { 54.0 };
- let btn_gap = if is_small { 4.0 } else { 6.0 };
- let right_edge = list_box_x + list_box_w - 24.0 - 8.0;
-
- let restart_x = right_edge - r_btn_w;
- let stop_x = restart_x - btn_gap - btn_w;
- let start_x = stop_x - btn_gap - btn_w;
-
- let btn_y = draw_y + (item_h - 22.0) / 2.0;
- let btn_h = 22.0;
-
- // Service Description (Truncate dynamically based on remaining space before Start button)
- let text_max_w = (start_x - 8.0) - (list_box_x + 32.0);
- let max_chars = ((text_max_w / 6.0) as usize).max(10);
- let desc = if service.description.is_empty() { "No description" } else { &service.description };
- let desc_truncated = if desc.len() > max_chars {
- format!("{}...", &desc[..max_chars.saturating_sub(3)])
- } else {
- desc.to_string()
- };
-
- // Render InteractiveListItem background and text labels
- let item_btn = &mut state.service_items[idx];
- item_btn.title = service.name.clone();
- item_btn.subtitle = Some(desc_truncated);
- cce_ui::layout::render_widget(sec.pc, item_btn, list_box_x + 24.0, draw_y, list_box_w - 44.0, item_h, ctx);
-
- // Render StatusDot
- let status_dot_state = if service.active_state == "failed" {
- DotStatus::Error
- } else if is_active {
- DotStatus::Active
- } else {
- DotStatus::Inactive
- };
- let mut dot = StatusDot::new(status_dot_state);
- cce_ui::layout::render_widget(sec.pc, &mut dot, list_box_x + 10.0, draw_y + (item_h - 10.0) / 2.0, 10.0, 10.0, ctx);
-
- let active_txt = [0.90, 0.90, 0.95, 1.0];
- let disabled_txt = [0.40, 0.40, 0.45, 1.0];
-
- let start_lbl = if is_small { "▶" } else { "Start" };
- let stop_lbl = if is_small { "■" } else { "Stop" };
- let restart_lbl = if is_small { "⟳" } else { "Restart" };
-
- // Start button
- sec.pc.button(
- start_lbl,
- start_x,
- btn_y,
- btn_w,
- btn_h,
- if !is_active { [0.16, 0.35, 0.18, 0.4] } else { [0.12, 0.12, 0.16, 0.1] },
- [0.22, 0.45, 0.25, 0.6],
- if !is_active { active_txt } else { disabled_txt },
- crate::app::AppAction::Services(ServicesMessage::Start(service.name.clone(), service.is_system)),
- );
-
- // Stop button
- sec.pc.button(
- stop_lbl,
- stop_x,
- btn_y,
- btn_w,
- btn_h,
- if is_active { [0.55, 0.16, 0.16, 0.3] } else { [0.12, 0.12, 0.16, 0.1] },
- [0.70, 0.22, 0.22, 0.5],
- if is_active { active_txt } else { disabled_txt },
- crate::app::AppAction::Services(ServicesMessage::Stop(service.name.clone(), service.is_system)),
- );
-
- // Restart button
- sec.pc.button(
- restart_lbl,
- restart_x,
- btn_y,
- r_btn_w,
- btn_h,
- [0.15, 0.28, 0.45, 0.3],
- [0.20, 0.38, 0.58, 0.5],
- active_txt,
- crate::app::AppAction::Services(ServicesMessage::Restart(service.name.clone(), service.is_system)),
- );
- }
- }
-
- if filtered_services.is_empty() {
- sec.pc.text("No services match the query", list_box_x + 16.0, list_box_y + 16.0, 12.0, TEXT_DIM);
- }
-
- sec.content_y += list_box_h;
- }
- });
-
- // ── System Notifications ──
- builder.add_section(&mut final_pc, "System Notifications", sec_focused.get(1).copied().unwrap_or(false), |sec2| {
- let sec_w = sec2.cw;
- state.notifications_enable_toggle.set_toggled(state.notifications_enable);
- sec2.widget_full(&mut state.notifications_enable_toggle, cce_ui::layout::toggle_height(), ctx);
- sec2.spacing(8.0);
-
- state.notifications_bell_toggle.set_toggled(state.notifications_bell);
- sec2.widget_full(&mut state.notifications_bell_toggle, cce_ui::layout::toggle_height(), ctx);
- sec2.spacing(16.0);
-
- state.notifications_duration_spinbox.value = state.notifications_duration;
- state.notifications_duration_spinbox.set_label("Notification Duration");
- sec2.widget(&mut state.notifications_duration_spinbox, 14.0, sec_w - 28.0, 44.0, ctx);
- sec2.spacing(16.0);
-
- let btn_h = 32.0;
- let btn_y = sec2.ay();
- let white_color = [1.0, 1.0, 1.0, 1.0];
- let btn_bg = [0.20, 0.40, 0.65, 1.0];
- let btn_hover = [0.28, 0.50, 0.78, 1.0];
-
- let cols = sec2.row_layout(1, 0.0);
- if let Some(&(x, w)) = cols.first() {
- sec2.button(
- "Send Test Notification",
- x,
- btn_y,
- w,
- btn_h,
- btn_bg,
- btn_hover,
- white_color,
- AppAction::Services(ServicesMessage::SendTestNotification),
- );
- }
- sec2.spacing(12.0);
- });
-
- // ── Status Interface ──
- builder.add_section(&mut final_pc, "Status Interface", sec_focused.get(2).copied().unwrap_or(false), |sec3| {
- let sec_w = sec3.cw;
- if !state.status_loaded {
- sec3.text("Loading Status Interface status...", 12.0, 0.0, 12.0, TEXT_DIM);
- sec3.spacing(18.0);
- } else {
- // Status
- let status_text = if state.status_running { "Status Interface: Running" } else { "Status Interface: Stopped" };
- let status_color = if state.status_running { [92, 143, 97] } else { [170, 51, 51] };
- state.status_label.set_text(status_text);
- state.status_label.set_color(status_color);
- sec3.widget(&mut state.status_label, 12.0, sec_w - 24.0, 20.0, ctx);
- sec3.spacing(12.0);
-
- sec3.spacing(4.0);
-
- // Separators toggle
- state.status_separators_toggle.set_toggled(state.status_separators);
- sec3.widget_full(&mut state.status_separators_toggle, cce_ui::layout::toggle_height(), ctx);
- sec3.spacing(16.0);
-
- // Underline toggle
- state.status_underline_toggle.set_toggled(state.status_underline);
- sec3.widget_full(&mut state.status_underline_toggle, cce_ui::layout::toggle_height(), ctx);
- sec3.spacing(16.0);
-
- // Padding spinbox
- state.status_padding_spinbox.value = state.status_padding as i32;
- sec3.widget(&mut state.status_padding_spinbox, 12.0, sec_w - 24.0, 44.0, ctx);
- sec3.spacing(16.0);
-
- // Reload button
- let yt_reload = sec3.ay();
- let btn_w = sec_w - 24.0;
- let button_x = sec3.left + 12.0;
- sec3.button(
- "Reload Status Interface",
- button_x,
- yt_reload,
- btn_w,
- 32.0,
- [0.13, 0.18, 0.14, 1.0],
- [0.25, 0.30, 0.26, 1.0],
- [1.0, 1.0, 1.0, 1.0],
- AppAction::Services(ServicesMessage::StatusReload),
- );
- sec3.spacing(12.0);
- }
- });
-
- final_pc
-}
-
-pub fn update(state: &mut ServicesState, msg: ServicesMessage) {
- match msg {
- ServicesMessage::Refreshed(new_services) => {
- state.loaded = true;
- state.services = new_services;
- state.service_items.clear();
- }
- ServicesMessage::SetTab(tab) => {
- state.active_tab = tab;
- state.list_box.set_scroll_y(0.0);
- state.service_items.clear();
- }
- ServicesMessage::Start(name, is_system) => {
- if let Some(srv) = state.services.iter_mut().find(|s| s.name == name && s.is_system == is_system) {
- srv.active_state = "activating".to_string();
- srv.sub_state = "starting".to_string();
- }
- service_action(&name, "start", is_system);
- }
- ServicesMessage::Stop(name, is_system) => {
- if let Some(srv) = state.services.iter_mut().find(|s| s.name == name && s.is_system == is_system) {
- srv.active_state = "deactivating".to_string();
- srv.sub_state = "stopping".to_string();
- }
- service_action(&name, "stop", is_system);
- }
- ServicesMessage::Restart(name, is_system) => {
- if let Some(srv) = state.services.iter_mut().find(|s| s.name == name && s.is_system == is_system) {
- srv.active_state = "activating".to_string();
- srv.sub_state = "restarting".to_string();
- }
- service_action(&name, "restart", is_system);
- }
- ServicesMessage::ToggleNotificationsEnable => {
- state.notifications_enable = !state.notifications_enable;
- write_enable_notifications(state.notifications_enable);
- }
- ServicesMessage::ToggleNotificationsBell => {
- state.notifications_bell = !state.notifications_bell;
- write_config_value("bell", &state.notifications_bell.to_string());
- }
- ServicesMessage::SetNotificationsDuration(d) => {
- state.notifications_duration = d;
- write_config_value("duration", &state.notifications_duration.to_string());
- }
- ServicesMessage::SendTestNotification => {
- tokio::spawn(async move {
- if let Ok(connection) = zbus::Connection::session().await {
- let _ = connection.call_method(
- Some("org.freedesktop.Notifications"),
- "/org/freedesktop/Notifications",
- Some("org.freedesktop.Notifications"),
- "Notify",
- &(
- "cce-client",
- 0u32,
- "",
- "System notifications are working correctly!",
- "",
- Vec::<&str>::new(),
- std::collections::HashMap::<&str, zbus::zvariant::Value>::new(),
- -1i32,
- )
- ).await;
- }
- });
- }
- ServicesMessage::NotificationsRefreshed(new) => {
- state.notifications_loaded = true;
- state.notifications_enable = new.enable;
- state.notifications_bell = new.bell;
- state.notifications_duration = new.duration;
- }
- ServicesMessage::StatusRefreshed(new) => {
- 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();
-
- state.status_loaded = true;
- state.status_font_size = new.font_size;
- state.status_padding = new.padding;
- state.status_separators = new.separators;
- state.status_underline = new.underline;
- state.status_running = new.running;
-
- 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);
- }
-
- ServicesMessage::StatusToggleSeparators => {
- state.status_separators = !state.status_separators;
- write_status_separators(state.status_separators);
- status_interface_reload();
- }
- ServicesMessage::StatusToggleUnderline => {
- state.status_underline = !state.status_underline;
- write_status_underline(state.status_underline);
- status_interface_reload();
- }
- ServicesMessage::StatusSetPadding(val) => {
- state.status_padding = val;
- write_status_padding(val);
- status_interface_reload();
- }
- ServicesMessage::StatusReload => {
- status_interface_reload();
- }
- }
-}
-
-// ── Notifications Configuration Reader & Writer ──
-
-const CONFIG_PATH: &str = "/home/lsgalante/.config/cce/config.json";
-
-fn get_socket_path() -> String {
- match std::env::var("WAYLAND_DISPLAY") {
- Ok(display) => format!("/tmp/cce-{}.sock", display),
- Err(_) => "/tmp/cce.sock".to_string(),
- }
-}
-
-pub fn read_notifications_config() -> NotificationsConfig {
- let content = fs::read_to_string(CONFIG_PATH).unwrap_or_default();
- let enable = parse_notifications_enable(&content);
- let bell = parse_notifications_bell(&content);
- let duration = parse_notifications_duration(&content);
- NotificationsConfig {
- enable,
- bell,
- duration,
- }
-}
-
-fn parse_json(content: &str) -> serde_json::Value {
- serde_json::from_str(content).unwrap_or_default()
-}
-
-fn parse_notifications_enable(content: &str) -> bool {
- let val = parse_json(content);
- val["notifications"]["enable"].as_bool().unwrap_or(true)
-}
-
-fn parse_notifications_bell(content: &str) -> bool {
- let val = parse_json(content);
- val["notifications"]["bell"].as_bool().unwrap_or(false)
-}
-
-fn parse_notifications_duration(content: &str) -> i32 {
- let val = parse_json(content);
- val["notifications"]["duration"].as_i64().map(|v| v as i32).unwrap_or(5)
-}
-
-fn send_ipc_command(cmd: &str) {
- if let Ok(mut stream) = std::os::unix::net::UnixStream::connect(get_socket_path()) {
- let _ = stream.write_all(format!("{}\n", cmd).as_bytes());
- }
-}
-
-fn write_config_value(key: &str, value: &str) {
- let content = fs::read_to_string(CONFIG_PATH).unwrap_or_default();
- let mut val = parse_json(&content);
- let j_val = if let Ok(b) = value.parse::<bool>() {
- serde_json::json!(b)
- } else if let Ok(n) = value.parse::<i64>() {
- serde_json::json!(n)
- } else if let Ok(f) = value.parse::<f64>() {
- serde_json::json!(f)
- } else {
- serde_json::json!(value)
- };
- if let Some(notifications) = val.get_mut("notifications").and_then(|n| n.as_object_mut()) {
- notifications.insert(key.to_string(), j_val);
- } else {
- let mut map = serde_json::Map::new();
- map.insert(key.to_string(), j_val);
- if let Some(obj) = val.as_object_mut() {
- obj.insert("notifications".to_string(), serde_json::Value::Object(map));
- }
- }
- if let Ok(updated_str) = serde_json::to_string_pretty(&val) {
- let _ = fs::write(CONFIG_PATH, updated_str);
- }
-}
-
-fn write_enable_notifications(enabled: bool) {
- write_config_value("enable", &enabled.to_string());
- send_ipc_command("reload");
-}
-
-
-thread_local! {
- static TEST_CONFIG_PATH: std::cell::RefCell<Option<String>> = std::cell::RefCell::new(None);
-}
-
-fn get_config_path() -> String {
- #[cfg(test)]
- {
- TEST_CONFIG_PATH.with(|p| {
- if let Some(path) = p.borrow().as_ref() {
- return path.clone();
- }
- "/home/lsgalante/.config/cce/config.json".to_string()
- })
- }
- #[cfg(not(test))]
- {
- "/home/lsgalante/.config/cce/config.json".to_string()
- }
-}
-
-fn write_status_value(key: &str, value: &str) {
- crate::pages::interface::write_config_value_path(&get_config_path(), key, value);
-}
-
-fn read_status_font_size() -> Option<u16> {
- let content = std::fs::read_to_string(&get_config_path()).ok()?;
- Some(parse_u16_from(&content, "status_font_size", 11))
-}
-
-
-fn read_status_padding() -> Option<u16> {
- let content = std::fs::read_to_string(&get_config_path()).ok()?;
- Some(parse_u16_from(&content, "status_padding", 8))
-}
-
-fn write_status_padding(padding: u16) {
- write_status_value("status_padding", &padding.to_string());
-}
-
-fn read_status_separators() -> Option<bool> {
- let content = std::fs::read_to_string(&get_config_path()).ok()?;
- Some(crate::pages::interface::parse_bool_from(&content, "status_separators", true))
-}
-
-fn write_status_separators(val: bool) {
- write_status_value("status_separators", &val.to_string());
-}
-
-fn read_status_underline() -> Option<bool> {
- let content = std::fs::read_to_string(&get_config_path()).ok()?;
- Some(crate::pages::interface::parse_bool_from(&content, "status_underline", true))
-}
-
-fn write_status_underline(val: bool) {
- write_status_value("status_underline", &val.to_string());
-}
-
-fn status_interface_reload() {
- let _ = std::process::Command::new("pkill")
- .args(["-f", "cce-status-interface"])
- .status();
- std::thread::sleep(std::time::Duration::from_millis(150));
- send_ipc_command("spawn cce-status-interface");
-}
-
-pub async fn fetch_status_state() -> StatusData {
- let running = tokio::process::Command::new("pgrep")
- .args(["-f", "cce-status-interface"]).output().await.ok()
- .map(|o| !o.stdout.is_empty())
- .unwrap_or(false);
-
- let font_size = read_status_font_size().unwrap_or(11);
- let padding = read_status_padding().unwrap_or(8);
- let separators = read_status_separators().unwrap_or(true);
- let underline = read_status_underline().unwrap_or(true);
-
- StatusData {
- font_size,
- padding,
- separators,
- underline,
- running,
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_view_layout_grid() {
- let mut state = ServicesState::default();
- let mut layout = cce_ui::layout::ColumnLayout::new(20.0);
- let sec_focused = vec![false, false];
- let mut ctx = cce_ui::context::UiContext::new();
- let pc = view(&mut state, 10.0, 20.0, 800.0, 600.0, &sec_focused, &mut layout, &mut ctx);
- assert!(!pc.rects.is_empty() || !pc.texts.is_empty());
- }
-
- #[test]
- fn test_parse_notifications_enable_default() {
- assert!(parse_notifications_enable(""));
- assert!(parse_notifications_enable("[layout]\ngap = 18\n"));
- }
-
- #[test]
- fn test_parse_notifications_enable_explicit() {
- let content = "{\"notifications\": {\"enable\": false}}";
- assert!(!parse_notifications_enable(content));
-
- let content = "{\"notifications\": {\"enable\": true}}";
- assert!(parse_notifications_enable(content));
- }
-
- #[test]
- fn test_parse_notifications_enable_other_sections() {
- let content = r#"{
- "layout": {"enable": false},
- "notifications": {"enable": true},
- "input": {"enable": false}
- }"#;
- assert!(parse_notifications_enable(content));
-
- let content = r#"{
- "layout": {"enable": true},
- "notifications": {"enable": false},
- "input": {"enable": true}
- }"#;
- assert!(!parse_notifications_enable(content));
- }
-
- #[test]
- fn test_parse_notifications_duration_default() {
- assert_eq!(parse_notifications_duration(""), 5);
- assert_eq!(parse_notifications_duration("{\"notifications\": {}}"), 5);
- }
-
- #[test]
- fn test_parse_notifications_duration_explicit() {
- let content = "{\"notifications\": {\"duration\": 10}}";
- assert_eq!(parse_notifications_duration(content), 10);
- }
-
- #[test]
- fn test_read_write_separators() {
- let dir = std::env::temp_dir();
- let path = dir.join("test_status_separators.json");
- let path_str = path.to_str().unwrap().to_string();
-
- let _ = fs::write(&path_str, "{\"layout\": {\"status_separators\": true, \"status_padding\": 8}}");
- TEST_CONFIG_PATH.with(|p| *p.borrow_mut() = Some(path_str));
-
- let original = read_status_separators().unwrap_or(true);
- write_status_separators(!original);
- assert_eq!(read_status_separators(), Some(!original));
- write_status_separators(original);
- assert_eq!(read_status_separators(), Some(original));
-
- let _ = fs::remove_file(path);
- }
-
- #[test]
- fn test_read_write_padding() {
- let dir = std::env::temp_dir();
- let path = dir.join("test_status_padding.json");
- let path_str = path.to_str().unwrap().to_string();
-
- let _ = fs::write(&path_str, "{\"layout\": {\"status_separators\": true, \"status_padding\": 8}}");
- TEST_CONFIG_PATH.with(|p| *p.borrow_mut() = Some(path_str));
-
- let original = read_status_padding().unwrap_or(8);
- write_status_padding(12);
- assert_eq!(read_status_padding(), Some(12));
- write_status_padding(original);
- assert_eq!(read_status_padding(), Some(original));
-
- let _ = fs::remove_file(path);
- }
-
- #[test]
- fn test_read_write_underline() {
- let dir = std::env::temp_dir();
- let path = dir.join("test_status_underline.json");
- let path_str = path.to_str().unwrap().to_string();
-
- let _ = fs::write(&path_str, "{\"layout\": {\"status_underline\": true, \"status_padding\": 8}}");
- TEST_CONFIG_PATH.with(|p| *p.borrow_mut() = Some(path_str));
-
- let original = read_status_underline().unwrap_or(true);
- write_status_underline(!original);
- assert_eq!(read_status_underline(), Some(!original));
- write_status_underline(original);
- assert_eq!(read_status_underline(), Some(original));
-
- let _ = fs::remove_file(path);
- }
-}
diff --git a/src/renderer.rs b/src/renderer.rs
index 4aa6aaf..61a87b1 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -21,7 +21,7 @@ impl SystemInterface {
self.page_sec_containers.clear();
// Clear all widgets' hierarchy links
- self.app.services.search_box.clear_children(&mut self.ui_context); self.app.services.search_box.set_parent(None, &mut self.ui_context);
+ self.app.processes.services_search_box.clear_children(&mut self.ui_context); self.app.processes.services_search_box.set_parent(None, &mut self.ui_context);
self.app.packages.search_box.clear_children(&mut self.ui_context); self.app.packages.search_box.set_parent(None, &mut self.ui_context);
self.app.packages.installed_list_box.scroll_box.clear_children(&mut self.ui_context); self.app.packages.installed_list_box.scroll_box.set_parent(None, &mut self.ui_context);
self.app.packages.updates_list_box.scroll_box.clear_children(&mut self.ui_context); self.app.packages.updates_list_box.scroll_box.set_parent(None, &mut self.ui_context);
@@ -32,9 +32,8 @@ impl SystemInterface {
self.app.accounts.oauth_client_id_box.clear_children(&mut self.ui_context); self.app.accounts.oauth_client_id_box.set_parent(None, &mut self.ui_context);
self.app.accounts.oauth_client_secret_box.clear_children(&mut self.ui_context); self.app.accounts.oauth_client_secret_box.set_parent(None, &mut self.ui_context);
- self.app.services.list_box.scroll_box.clear_children(&mut self.ui_context); self.app.services.list_box.scroll_box.set_parent(None, &mut self.ui_context);
-
- self.app.hardware.cpu_list_box.scroll_box.clear_children(&mut self.ui_context); self.app.hardware.cpu_list_box.scroll_box.set_parent(None, &mut self.ui_context);
+ self.app.processes.services_list_box.scroll_box.clear_children(&mut self.ui_context); self.app.processes.services_list_box.scroll_box.set_parent(None, &mut self.ui_context);
+ self.app.processes.cpu_list_box.scroll_box.clear_children(&mut self.ui_context); self.app.processes.cpu_list_box.scroll_box.set_parent(None, &mut self.ui_context);
self.app.network.wifi_list_box.scroll_box.clear_children(&mut self.ui_context); self.app.network.wifi_list_box.scroll_box.set_parent(None, &mut self.ui_context);
@@ -156,9 +155,9 @@ impl SystemInterface {
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);
+ self.app.processes.notifications_enable_toggle.clear_children(&mut self.ui_context); self.app.processes.notifications_enable_toggle.set_parent(None, &mut self.ui_context);
+ self.app.processes.notifications_bell_toggle.clear_children(&mut self.ui_context); self.app.processes.notifications_bell_toggle.set_parent(None, &mut self.ui_context);
+ self.app.processes.notifications_duration_spinbox.clear_children(&mut self.ui_context); self.app.processes.notifications_duration_spinbox.set_parent(None, &mut self.ui_context);
self.app.input.rate_spinbox.clear_children(&mut self.ui_context); self.app.input.rate_spinbox.set_parent(None, &mut self.ui_context);
self.app.input.delay_spinbox.clear_children(&mut self.ui_context); self.app.input.delay_spinbox.set_parent(None, &mut self.ui_context);
@@ -210,9 +209,9 @@ impl SystemInterface {
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);
+ self.app.processes.status_separators_toggle.clear_children(&mut self.ui_context); self.app.processes.status_separators_toggle.set_parent(None, &mut self.ui_context);
+ self.app.processes.status_underline_toggle.clear_children(&mut self.ui_context); self.app.processes.status_underline_toggle.set_parent(None, &mut self.ui_context);
+ self.app.processes.status_padding_spinbox.clear_children(&mut self.ui_context); self.app.processes.status_padding_spinbox.set_parent(None, &mut self.ui_context);
for menu in &mut self.app.interface.windows.tag_layout_menus {
menu.clear_children(&mut self.ui_context);
@@ -247,30 +246,22 @@ impl SystemInterface {
}
}
- Page::Services => {
- self.page_sec_containers.resize_with(3, cce_ui::widget::Container::new);
- for i in 0..3 {
- 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);
-
- link_parent_child(&mut self.page_sec_containers[1], &mut self.app.services.notifications_enable_toggle, &mut self.ui_context);
- link_parent_child(&mut self.page_sec_containers[1], &mut self.app.services.notifications_bell_toggle, &mut self.ui_context);
- link_parent_child(&mut self.page_sec_containers[1], &mut self.app.services.notifications_duration_spinbox, &mut self.ui_context);
-
- link_parent_child(&mut self.page_sec_containers[2], &mut self.app.services.status_separators_toggle, &mut self.ui_context);
- link_parent_child(&mut self.page_sec_containers[2], &mut self.app.services.status_underline_toggle, &mut self.ui_context);
- link_parent_child(&mut self.page_sec_containers[2], &mut self.app.services.status_padding_spinbox, &mut self.ui_context);
- }
- Page::Hardware => {
- self.page_sec_containers.resize_with(5, cce_ui::widget::Container::new);
- for i in 0..5 {
+ Page::Processes => {
+ self.page_sec_containers.resize_with(8, cce_ui::widget::Container::new);
+ for i in 0..8 {
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.hardware.cpu_list_box.scroll_box, &mut self.ui_context);
- link_parent_child(&mut self.page_sec_containers[3], &mut self.app.hardware.cpu_gov_menu, &mut self.ui_context);
- link_parent_child(&mut self.page_sec_containers[4], &mut self.app.hardware.gpu_gov_menu, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[0], &mut self.app.processes.cpu_list_box.scroll_box, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[3], &mut self.app.processes.cpu_gov_menu, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[4], &mut self.app.processes.gpu_gov_menu, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[5], &mut self.app.processes.services_search_box, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[5], &mut self.app.processes.services_list_box.scroll_box, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[6], &mut self.app.processes.notifications_enable_toggle, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[6], &mut self.app.processes.notifications_bell_toggle, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[6], &mut self.app.processes.notifications_duration_spinbox, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[7], &mut self.app.processes.status_separators_toggle, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[7], &mut self.app.processes.status_underline_toggle, &mut self.ui_context);
+ link_parent_child(&mut self.page_sec_containers[7], &mut self.app.processes.status_padding_spinbox, &mut self.ui_context);
}
Page::Radios => {
self.page_sec_containers.resize_with(2, cce_ui::widget::Container::new);
@@ -705,18 +696,17 @@ impl SystemInterface {
// Auto-detect if inside a ScrollBox to apply left alignment by default
if !left_align && base.w >= 60.0 {
- if self.app.current_page == Page::Hardware {
- let sb = &self.app.hardware.cpu_list_box;
- let (sb_x, sb_y, sb_w, sb_h) = sb.rect();
- if base.x >= sb_x - 1.0 && base.x + base.w <= sb_x + sb_w + 1.0
- && base.y >= sb_y - 1.0 && base.y + base.h <= sb_y + sb_h + 1.0 {
+ if self.app.current_page == Page::Processes {
+ let sb1 = &self.app.processes.cpu_list_box;
+ let (sb1_x, sb1_y, sb1_w, sb1_h) = sb1.rect();
+ if base.x >= sb1_x - 1.0 && base.x + base.w <= sb1_x + sb1_w + 1.0
+ && base.y >= sb1_y - 1.0 && base.y + base.h <= sb1_y + sb1_h + 1.0 {
left_align = true;
}
- } else if self.app.current_page == Page::Services {
- let sb = &self.app.services.list_box;
- let (sb_x, sb_y, sb_w, sb_h) = sb.rect();
- if base.x >= sb_x - 1.0 && base.x + base.w <= sb_x + sb_w + 1.0
- && base.y >= sb_y - 1.0 && base.y + base.h <= sb_y + sb_h + 1.0 {
+ let sb2 = &self.app.processes.services_list_box;
+ let (sb2_x, sb2_y, sb2_w, sb2_h) = sb2.rect();
+ if base.x >= sb2_x - 1.0 && base.x + base.w <= sb2_x + sb2_w + 1.0
+ && base.y >= sb2_y - 1.0 && base.y + base.h <= sb2_y + sb2_h + 1.0 {
left_align = true;
}
}
@@ -808,6 +798,7 @@ impl SystemInterface {
self.text_items = text_items;
self.page_buttons = page_buttons;
self.needs_rebuild = false;
+ self.ui_context.clear_dirty();
}
pub(crate) fn render_page_content(&mut self, cx: f32, cy: f32, cw: f32, ch: f32) -> PageContent {
@@ -829,11 +820,10 @@ impl SystemInterface {
Page::Audio => audio::view(&mut self.app.audio, cx, cy, cw, ch, &sec_focused, &mut layout, &mut self.ui_context),
Page::Display => display::view(&mut self.app.display, cx, cy, cw, ch, &mut layout, &mut self.ui_context),
Page::Radios => network::view(&mut self.app.network, cx, cy, cw, ch, root_focused, &mut layout, &mut self.ui_context),
- Page::Hardware => hardware::view(&mut self.app.hardware, cx, cy, cw, ch, root_focused, &mut layout, &mut self.ui_context),
+ Page::Processes => processes::view(&mut self.app.processes, cx, cy, cw, ch, root_focused, &sec_focused, &mut layout, &mut self.ui_context),
Page::Input => input::view(&mut self.app.input, cx, cy, cw, ch, &sec_focused, &mut layout, &mut self.ui_context),
Page::System => system_info::view(&self.app.system_info, cx, cy, cw, ch, &mut layout, &mut self.ui_context),
Page::Storage => storage::view(&self.app.storage, cx, cy, cw, ch, &mut layout, &mut self.ui_context),
- Page::Services => services::view(&mut self.app.services, cx, cy, cw, ch, &sec_focused, &mut layout, &mut self.ui_context),
Page::Interface => interface::view(&mut self.app.interface, cx, cy, cw, ch, &sec_focused, &mut layout, &mut self.ui_context),
Page::Packages => packages::view(&mut self.app.packages, cx, cy, cw, ch, &sec_focused, &mut layout, &mut self.ui_context),
}
diff --git a/src/watchers.rs b/src/watchers.rs
index f4a5067..f038872 100644
--- a/src/watchers.rs
+++ b/src/watchers.rs
@@ -1,7 +1,7 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::mpsc::{channel, Receiver, Sender};
-use crate::pages::{audio, display, network, interface, input, hardware, system_info, services, storage, packages, accounts};
+use crate::pages::{audio, display, network, interface, input, processes, system_info, storage, packages, accounts};
use cce_ui::widget::Finger;
pub struct Watchers {
@@ -12,13 +12,13 @@ pub struct Watchers {
pub rx_wm_events: Receiver<()>,
pub rx_input: Receiver<input::InputState>,
pub rx_fingers: Receiver<Vec<Finger>>,
- pub rx_hardware: Receiver<hardware::HardwareState>,
+ pub rx_processes: Receiver<processes::ProcessesState>,
pub rx_system: Receiver<system_info::SystemState>,
- pub rx_status: Receiver<services::StatusData>,
+ pub rx_status: Receiver<processes::StatusData>,
pub rx_storage: Receiver<storage::StorageState>,
- pub rx_notifications: Receiver<services::NotificationsConfig>,
+ pub rx_notifications: Receiver<processes::NotificationsConfig>,
pub rx_typeface: Receiver<interface::InterfaceState>,
- pub rx_services: Receiver<Vec<services::ServiceInfo>>,
+ pub rx_services: Receiver<Vec<processes::ServiceInfo>>,
pub rx_interface: Receiver<interface::InterfaceState>,
pub rx_accounts: Receiver<Vec<accounts::AccountInfo>>,
pub rx_packages: Receiver<packages::PackagesState>,
@@ -76,7 +76,7 @@ pub fn spawn_all(
let mut last_fetch: Option<std::time::Instant> = None;
loop {
let current_page = current_page_shared.load(Ordering::SeqCst);
- if current_page == 5 { // Interface is index 5
+ if current_page == 4 { // Interface is index 4
let should_fetch = match last_fetch {
None => true,
Some(t) => t.elapsed() >= std::time::Duration::from_secs(30),
@@ -112,7 +112,7 @@ pub fn spawn_all(
loop {
let current_page = current_page_shared.load(Ordering::SeqCst);
- if current_page == 5 { // Interface is index 5
+ if current_page == 4 { // Interface is index 4
let mut changed = false;
for p in &[&windows_path, &tags_path, &title_path] {
if let Some(mtime) = check_mtime(p) {
@@ -139,7 +139,7 @@ pub fn spawn_all(
let mut last_fetch: Option<std::time::Instant> = None;
loop {
let current_page = current_page_shared.load(Ordering::SeqCst);
- if current_page == 4 { // Input is index 4
+ if current_page == 3 { // Input is index 3
let should_fetch = match last_fetch {
None => true,
Some(t) => t.elapsed() >= std::time::Duration::from_secs(30),
@@ -168,13 +168,13 @@ pub fn spawn_all(
};
loop {
let current_page = current_page_shared.load(Ordering::SeqCst);
- if current_page == 4 { // Input is index 4
+ if current_page == 3 { // Input is index 3
if let Ok(stream) = tokio::net::UnixStream::connect(&socket_path).await {
use tokio::io::AsyncBufReadExt;
let reader = tokio::io::BufReader::new(stream);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
- if current_page_shared.load(Ordering::SeqCst) != 4 {
+ if current_page_shared.load(Ordering::SeqCst) != 3 {
break;
}
if let Ok(fingers) = serde_json::from_str::<Vec<Finger>>(&line) {
@@ -191,25 +191,25 @@ pub fn spawn_all(
rx
};
- let rx_system = spawn_bg_active(current_page_shared.clone(), 10, 5, || system_info::fetch_system_state());
- let rx_hardware = spawn_bg_active(current_page_shared.clone(), 3, 3, || hardware::fetch_hardware_state());
- let rx_status = spawn_bg_active(current_page_shared.clone(), 8, 10, || services::fetch_status_state());
- let rx_storage = spawn_bg_active(current_page_shared.clone(), 9, 10, || storage::fetch_storage_state());
+ let rx_system = spawn_bg_active(current_page_shared.clone(), 9, 5, || system_info::fetch_system_state());
+ let rx_processes = spawn_bg_active(current_page_shared.clone(), 6, 3, || processes::fetch_processes_state());
+ let rx_status = spawn_bg_active(current_page_shared.clone(), 6, 10, || processes::fetch_status_state());
+ let rx_storage = spawn_bg_active(current_page_shared.clone(), 8, 10, || storage::fetch_storage_state());
let rx_notifications = {
- let (tx, rx) = channel::<services::NotificationsConfig>();
+ let (tx, rx) = channel::<processes::NotificationsConfig>();
let current_page_shared = current_page_shared.clone();
tokio::spawn(async move {
let mut last_fetch: Option<std::time::Instant> = None;
loop {
let current_page = current_page_shared.load(Ordering::SeqCst);
- if current_page == 8 { // Services is index 8
+ if current_page == 6 { // Processes is index 6
let should_fetch = match last_fetch {
None => true,
Some(t) => t.elapsed() >= std::time::Duration::from_secs(30),
};
if should_fetch {
- let val = tokio::task::spawn_blocking(|| services::read_notifications_config()).await;
+ let val = tokio::task::spawn_blocking(|| processes::read_notifications_config()).await;
if let Ok(val) = val {
if tx.send(val).is_err() { break; }
}
@@ -222,8 +222,8 @@ pub fn spawn_all(
rx
};
- let rx_typeface = spawn_bg_active(current_page_shared.clone(), 5, 30, || interface::fetch_typeface_state());
- let rx_services = spawn_bg_active(current_page_shared.clone(), 8, 3, || services::fetch_services());
+ let rx_typeface = spawn_bg_active(current_page_shared.clone(), 4, 30, || interface::fetch_typeface_state());
+ let rx_services = spawn_bg_active(current_page_shared.clone(), 6, 3, || processes::fetch_services());
let rx_accounts = spawn_bg_active(current_page_shared.clone(), 0, 3, || accounts::fetch_accounts());
let rx_interface = {
@@ -233,7 +233,7 @@ pub fn spawn_all(
let mut last_fetch: Option<std::time::Instant> = None;
loop {
let current_page = current_page_shared.load(Ordering::SeqCst);
- if current_page == 5 { // Interface is index 5
+ if current_page == 4 { // Interface is index 4
let should_fetch = match last_fetch {
None => true,
Some(t) => t.elapsed() >= std::time::Duration::from_secs(30),
@@ -253,7 +253,7 @@ pub fn spawn_all(
};
let (tx_backup, rx_backup) = channel();
- let rx_packages = spawn_bg_active(current_page_shared.clone(), 6, 30, || packages::fetch_packages_state());
+ let rx_packages = spawn_bg_active(current_page_shared.clone(), 5, 30, || packages::fetch_packages_state());
let (tx_update, rx_update) = channel();
(
@@ -265,7 +265,7 @@ pub fn spawn_all(
rx_wm_events,
rx_input,
rx_fingers,
- rx_hardware,
+ rx_processes,
rx_system,
rx_status,
rx_storage,