From 5f0ca8558958de0622ecbca08535adfec4b2f865 Mon Sep 17 00:00:00 2001 From: Candifloss Date: Sat, 10 Jan 2026 22:10:58 +0530 Subject: [PATCH] Load config - All values either from config file or default values --- Cargo.toml | 1 + src/config.rs | 183 ++++++++++++++++++++++++++++++++++++++++++++++- src/main.rs | 8 ++- src/powermenu.rs | 113 +++++++++++------------------ 4 files changed, 230 insertions(+), 75 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0abb73e..2e85aec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" build = "build.rs" [dependencies] +dirs = "6.0.0" i-slint-backend-winit = { version = "1.14.1", default-features = false, features = ["x11"] } serde = { version = "1.0.228", default-features = false, features = ["derive"] } slint = { version = "1.14.1", default-features = false, features = ["backend-winit", "compat-1-2", "renderer-software"] } diff --git a/src/config.rs b/src/config.rs index b7b7cd0..0f52512 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,21 @@ use serde::Deserialize; +use std::fs; +use std::path::PathBuf; use slint::{Color, SharedString}; -use std::{fs, path::PathBuf}; + +pub fn parse_hex_color(s: &str) -> Result { + let clean = s.trim().trim_start_matches('#'); + + if clean.len() != 8 { + return Err("color must be RRGGBBAA".into()); + } + + let raw = + u32::from_str_radix(clean, 16) + .map_err(|_| "invalid hex color")?; + + Ok(Color::from_argb_encoded(raw)) +} #[derive(Debug, Deserialize)] pub struct Config { @@ -63,3 +78,169 @@ pub struct MenuOption { pub text: String, pub command: String, } + +pub struct ResolvedConfig { + pub screen_width: i32, + pub screen_height: i32, + pub screen_bg: Color, + + pub button_style: ButtonStyleResolved, + + pub menu_padding_x: i32, + pub menu_padding_y: i32, + pub menu_spacing: i32, + pub menu_border_radius: i32, + + pub menu_width: i32, + pub menu_height: i32, + pub menu_pos_x: i32, + pub menu_pos_y: i32, + pub menu_bg: Color, + + pub options: Vec, +} + +pub struct ButtonStyleResolved { + pub btn_width: i32, + pub btn_height: i32, + pub btn_border_radius: i32, + + pub btn_bg_normal: Color, + pub btn_bg_hover: Color, + pub btn_bg_clicked: Color, + + pub option_icon_height: i32, + pub icon_font_size: i32, + pub icon_font: SharedString, + pub icon_font_color: Color, + + pub option_text_height: i32, + pub option_text_font_size: i32, + pub option_text_font: SharedString, + pub option_text_color: Color, +} + +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())? + } else { + Config { + screen: None, + menu_popup: None, + buttons: None, + button: None, + options: None, + } + }; + + resolve(raw, screen_w, screen_h) +} + +fn config_path() -> PathBuf { + let mut p = + dirs::config_dir() + .unwrap_or_else(|| PathBuf::from(".")); + + p.push("candywidgets"); + p.push("power-menu"); + p.push("config.toml"); + p +} + +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_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); + + let icon_height = btn_height * 75 / 100; + let text_height = btn_height * 25 / 100; + + let button_style = ButtonStyleResolved { + btn_width, + btn_height, + 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") + )?, + btn_bg_hover: parse_hex_color( + 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") + )?, + + option_icon_height: icon_height, + icon_font_size: icon_height * 80 / 100, + icon_font: SharedString::from("IosevkaTermSlab Nerd Font Mono"), + icon_font_color: Color::from_argb_encoded(0xFFFFFFFF), + + option_text_height: text_height, + option_text_font_size: text_height * 45 / 100, + option_text_font: SharedString::from("IosevkaTermSlab Nerd Font Mono"), + option_text_color: Color::from_argb_encoded(0xFFFFFFFF), + }; + + 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_height = + menu.and_then(|m| m.height) + .unwrap_or(btn_height + padding * 2); + + Ok(ResolvedConfig { + screen_width, + screen_height, + screen_bg, + + button_style, + + menu_padding_x: padding, + menu_padding_y: menu.and_then(|m| m.padding_y).unwrap_or(padding), + menu_spacing: spacing, + menu_border_radius: menu.and_then(|m| m.border_radius).unwrap_or(padding), + + 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_bg: menu + .and_then(|m| m.bg_color.as_deref()) + .map(parse_hex_color) + .transpose()? + .unwrap_or(Color::from_argb_encoded(0x287A7A7A)), + + options: raw.options.unwrap_or_default(), + }) +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index df70f35..164d532 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,8 +26,10 @@ fn main() -> Result<(), Box> { slint::platform::set_platform(Box::new(backend))?; - let screen_width = 1920; - let screen_height = 1080; + let screen_width = 1366; + let screen_height = 768; - powermenu::run_power_menu(screen_width, screen_height) + let conf = config::load_config(screen_width, screen_height)?; + + powermenu::run_power_menu(conf) } diff --git a/src/powermenu.rs b/src/powermenu.rs index 398472f..a062bd3 100644 --- a/src/powermenu.rs +++ b/src/powermenu.rs @@ -1,84 +1,55 @@ use slint::{Color, LogicalPosition, LogicalSize, SharedString}; +use crate::config::ResolvedConfig; slint::include_modules!(); +fn to_slint_button_style( + s: crate::config::ButtonStyleResolved, +) -> ButtonStyle { + ButtonStyle { + btn_width: s.btn_width, + btn_height: s.btn_height, + btn_border_radius: s.btn_border_radius, + + btn_bg_normal: s.btn_bg_normal, + btn_bg_hover: s.btn_bg_hover, + btn_bg_clicked: s.btn_bg_clicked, + + option_icon_height: s.option_icon_height, + icon_font_size: s.icon_font_size, + icon_font: s.icon_font, + icon_font_color: s.icon_font_color, + + option_text_height: s.option_text_height, + option_text_font_size: s.option_text_font_size, + option_text_font: s.option_text_font, + option_text_color: s.option_text_color, + } +} + pub fn run_power_menu( - screen_width: i32, - screen_height: i32, + cfg: ResolvedConfig ) -> Result<(), Box> { let ui = PowerMenu::new()?; - let screen_bg = Color::from_argb_encoded(0x20A3A3A3); + let button_style = to_slint_button_style(cfg.button_style); - let btn_width = 120; - let btn_height = 120; - let btn_border_radius = 20; + ui.set_screen_width(cfg.screen_width); + ui.set_screen_height(cfg.screen_height); - let btn_bg_normal = Color::from_argb_encoded(0x75919191); - let btn_bg_hover = Color::from_argb_encoded(0x84929292); - let btn_bg_clicked = Color::from_argb_encoded(0xAB9B9B9B); + ui.set_screen_bg(cfg.screen_bg); - let option_icon_height = btn_height * 75 / 100; - let icon_font_size = option_icon_height * 80 / 100; - let icon_font = SharedString::from("IosevkaTermSlab Nerd Font Mono"); - let icon_font_color = Color::from_argb_encoded(0xFFFFFFFF); + ui.set_menu_width(cfg.menu_width); + 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); + ui.set_menu_border_radius(cfg.menu_border_radius); - let option_text_height = btn_height * 25 / 100; - let option_text_font_size = option_text_height * 45 / 100; - let option_text_font = SharedString::from("IosevkaTermSlab Nerd Font Mono"); - let option_text_color = Color::from_argb_encoded(0xFFFFFFFF); - - let button_style = ButtonStyle { - btn_width, - btn_height, - btn_border_radius, - - btn_bg_normal, - btn_bg_hover, - btn_bg_clicked, - - option_icon_height, - icon_font_size, - icon_font, - icon_font_color, - - option_text_height, - option_text_font_size, - option_text_font, - option_text_color, - }; - - let menu_padding_x = 20; - let menu_padding_y = menu_padding_x; - let menu_spacing = menu_padding_x; - let menu_border_radius = menu_padding_x; - - let menu_width = btn_width * 4 + menu_spacing * 3 + menu_padding_x * 2; - - let menu_height = btn_height + menu_padding_y * 2; - - let menu_pos_x = (screen_width - menu_width) / 2; - let menu_pos_y = (screen_height - menu_height) / 2; - - let menu_bg = Color::from_argb_encoded(0x287A7A7A); - - ui.set_screen_width(screen_width); - ui.set_screen_height(screen_height); - - ui.set_screen_bg(screen_bg); - - ui.set_menu_padding_x(menu_padding_x); - ui.set_menu_padding_y(menu_padding_y); - ui.set_menu_spacing(menu_spacing); - ui.set_menu_border_radius(menu_border_radius); - - ui.set_menu_width(menu_width); - ui.set_menu_height(menu_height); - - ui.set_menu_pos_x(menu_pos_x); - ui.set_menu_pos_y(menu_pos_y); - - ui.set_menu_bg(menu_bg); + ui.set_menu_bg(cfg.menu_bg); ui.set_ico_lockscreen(SharedString::from("")); ui.set_ico_logout(SharedString::from("")); @@ -93,10 +64,10 @@ pub fn run_power_menu( ui.set_button_style(button_style); ui.window() - .set_size(LogicalSize::new(screen_width as f32, screen_height as f32)); + .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.run(); Ok(()) -} +} \ No newline at end of file