Load config

- All values either from config file or default values
This commit is contained in:
Candifloss 2026-01-10 22:10:58 +05:30
parent ed5a08ef94
commit 5f0ca85589
4 changed files with 230 additions and 75 deletions

View File

@ -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"] }

View File

@ -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<Color, String> {
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<MenuOption>,
}
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<ResolvedConfig, String> {
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::<Config>(&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<ResolvedConfig, String> {
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(),
})
}

View File

@ -26,8 +26,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
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)
}

View File

@ -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<dyn std::error::Error>> {
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(())
}
}