diff --git a/src/config.rs b/src/config.rs index 0f52512..e5a749c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use serde::Deserialize; +use slint::{Color, SharedString}; use std::fs; use std::path::PathBuf; -use slint::{Color, SharedString}; pub fn parse_hex_color(s: &str) -> Result { let clean = s.trim().trim_start_matches('#'); @@ -10,9 +10,7 @@ pub fn parse_hex_color(s: &str) -> Result { return Err("color must be RRGGBBAA".into()); } - let raw = - u32::from_str_radix(clean, 16) - .map_err(|_| "invalid hex color")?; + let raw = u32::from_str_radix(clean, 16).map_err(|_| "invalid hex color")?; Ok(Color::from_argb_encoded(raw)) } @@ -120,17 +118,12 @@ pub struct ButtonStyleResolved { pub option_text_color: Color, } -pub fn load_config( - screen_w: i32, - screen_h: i32, -) -> Result { +pub fn load_config(screen_w: i32, screen_h: i32) -> Result { let path = config_path(); let raw = if path.exists() { - let text = fs::read_to_string(&path) - .map_err(|e| e.to_string())?; - toml::from_str::(&text) - .map_err(|e| e.to_string())? + let text = fs::read_to_string(&path).map_err(|e| e.to_string())?; + toml::from_str::(&text).map_err(|e| e.to_string())? } else { Config { screen: None, @@ -145,9 +138,7 @@ pub fn load_config( } fn config_path() -> PathBuf { - let mut p = - dirs::config_dir() - .unwrap_or_else(|| PathBuf::from(".")); + let mut p = dirs::config_dir().unwrap_or_else(|| PathBuf::from(".")); p.push("candywidgets"); p.push("power-menu"); @@ -155,26 +146,19 @@ fn config_path() -> PathBuf { p } -fn resolve( - raw: Config, - fallback_w: i32, - fallback_h: i32, -) -> Result { +fn resolve(raw: Config, fallback_w: i32, fallback_h: i32) -> Result { let screen = raw.screen.as_ref(); let menu = raw.menu_popup.as_ref(); let buttons = raw.buttons.as_ref(); - let screen_width = - screen.and_then(|s| s.width).unwrap_or(fallback_w); - let screen_height = - screen.and_then(|s| s.height).unwrap_or(fallback_h); + let screen_width = screen.and_then(|s| s.width).unwrap_or(fallback_w); + let screen_height = screen.and_then(|s| s.height).unwrap_or(fallback_h); - let screen_bg = - screen - .and_then(|s| s.bg_color.as_deref()) - .map(parse_hex_color) - .transpose()? - .unwrap_or(Color::from_argb_encoded(0x20A3A3A3)); + let screen_bg = screen + .and_then(|s| s.bg_color.as_deref()) + .map(parse_hex_color) + .transpose()? + .unwrap_or(Color::from_argb_encoded(0x20A3A3A3)); let btn_width = buttons.and_then(|b| b.width).unwrap_or(120); let btn_height = buttons.and_then(|b| b.height).unwrap_or(120); @@ -188,13 +172,19 @@ fn resolve( btn_border_radius: buttons.and_then(|b| b.border_radius).unwrap_or(20), btn_bg_normal: parse_hex_color( - buttons.and_then(|b| b.bg_color_normal.as_deref()).unwrap_or("91919175") + buttons + .and_then(|b| b.bg_color_normal.as_deref()) + .unwrap_or("91919175"), )?, btn_bg_hover: parse_hex_color( - buttons.and_then(|b| b.bg_color_hover.as_deref()).unwrap_or("92929284") + buttons + .and_then(|b| b.bg_color_hover.as_deref()) + .unwrap_or("92929284"), )?, btn_bg_clicked: parse_hex_color( - buttons.and_then(|b| b.bg_color_clicked.as_deref()).unwrap_or("9B9B9BAB") + buttons + .and_then(|b| b.bg_color_clicked.as_deref()) + .unwrap_or("9B9B9BAB"), )?, option_icon_height: icon_height, @@ -211,13 +201,13 @@ fn resolve( let padding = menu.and_then(|m| m.padding_x).unwrap_or(20); let spacing = menu.and_then(|m| m.button_spacing).unwrap_or(padding); - let menu_width = - menu.and_then(|m| m.width) - .unwrap_or(btn_width * 4 + spacing * 3 + padding * 2); + let menu_width = menu + .and_then(|m| m.width) + .unwrap_or(btn_width * 4 + spacing * 3 + padding * 2); - let menu_height = - menu.and_then(|m| m.height) - .unwrap_or(btn_height + padding * 2); + let menu_height = menu + .and_then(|m| m.height) + .unwrap_or(btn_height + padding * 2); Ok(ResolvedConfig { screen_width, @@ -233,8 +223,12 @@ fn resolve( menu_width, menu_height, - menu_pos_x: menu.and_then(|m| m.pos_x).unwrap_or((screen_width - menu_width) / 2), - menu_pos_y: menu.and_then(|m| m.pos_y).unwrap_or((screen_height - menu_height) / 2), + menu_pos_x: menu + .and_then(|m| m.pos_x) + .unwrap_or((screen_width - menu_width) / 2), + menu_pos_y: menu + .and_then(|m| m.pos_y) + .unwrap_or((screen_height - menu_height) / 2), menu_bg: menu .and_then(|m| m.bg_color.as_deref()) .map(parse_hex_color) @@ -243,4 +237,4 @@ fn resolve( options: raw.options.unwrap_or_default(), }) -} \ No newline at end of file +} diff --git a/src/powermenu.rs b/src/powermenu.rs index a062bd3..d0516a8 100644 --- a/src/powermenu.rs +++ b/src/powermenu.rs @@ -1,11 +1,9 @@ -use slint::{Color, LogicalPosition, LogicalSize, SharedString}; use crate::config::ResolvedConfig; +use slint::{Color, LogicalPosition, LogicalSize, SharedString}; slint::include_modules!(); -fn to_slint_button_style( - s: crate::config::ButtonStyleResolved, -) -> ButtonStyle { +fn to_slint_button_style(s: crate::config::ButtonStyleResolved) -> ButtonStyle { ButtonStyle { btn_width: s.btn_width, btn_height: s.btn_height, @@ -27,9 +25,7 @@ fn to_slint_button_style( } } -pub fn run_power_menu( - cfg: ResolvedConfig -) -> Result<(), Box> { +pub fn run_power_menu(cfg: ResolvedConfig) -> Result<(), Box> { let ui = PowerMenu::new()?; let button_style = to_slint_button_style(cfg.button_style); @@ -43,7 +39,7 @@ pub fn run_power_menu( ui.set_menu_height(cfg.menu_height); ui.set_menu_pos_x(cfg.menu_pos_x); ui.set_menu_pos_y(cfg.menu_pos_y); - + ui.set_menu_padding_x(cfg.menu_padding_x); ui.set_menu_padding_y(cfg.menu_padding_y); ui.set_menu_spacing(cfg.menu_spacing); @@ -63,11 +59,17 @@ pub fn run_power_menu( ui.set_button_style(button_style); - ui.window() - .set_size(LogicalSize::new(cfg.screen_width as f32, cfg.screen_height as f32)); + ui.window().set_size(LogicalSize::new( + cfg.screen_width as f32, + cfg.screen_height as f32, + )); ui.window().set_position(LogicalPosition::new(0.0, 0.0)); + ui.on_request_close(|| { + slint::quit_event_loop(); + }); + ui.run(); Ok(()) -} \ No newline at end of file +} diff --git a/ui/power-menu.slint b/ui/power-menu.slint index c5b03ab..b46f278 100644 --- a/ui/power-menu.slint +++ b/ui/power-menu.slint @@ -71,6 +71,8 @@ component PowerMenuButton { } export component PowerMenu inherits Window { + callback request_close(); + in property screen_width; in property screen_height; in property screen_bg; @@ -113,6 +115,18 @@ export component PowerMenu inherits Window { y:0; background: screen_bg; + // Close upon clicking outside the menu + TouchArea { + x: 0; + y: 0; + width: 100%; + height: 100%; + + clicked => { + request_close(); + } + } + // Menu pop-up Rectangle { background: menu_bg; @@ -122,6 +136,10 @@ export component PowerMenu inherits Window { x: menu_pos_x *1px; y: menu_pos_y *1px; + TouchArea { + // Empty. Prevent close-on-click inside the menu. + } + // Buttons HorizontalLayout { padding: menu_padding_x *1px;