Load config
- All values either from config file or default values
This commit is contained in:
parent
ed5a08ef94
commit
5f0ca85589
@ -5,6 +5,7 @@ edition = "2024"
|
|||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
dirs = "6.0.0"
|
||||||
i-slint-backend-winit = { version = "1.14.1", default-features = false, features = ["x11"] }
|
i-slint-backend-winit = { version = "1.14.1", default-features = false, features = ["x11"] }
|
||||||
serde = { version = "1.0.228", default-features = false, features = ["derive"] }
|
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"] }
|
slint = { version = "1.14.1", default-features = false, features = ["backend-winit", "compat-1-2", "renderer-software"] }
|
||||||
|
|||||||
183
src/config.rs
183
src/config.rs
@ -1,6 +1,21 @@
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
use slint::{Color, SharedString};
|
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)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
@ -63,3 +78,169 @@ pub struct MenuOption {
|
|||||||
pub text: String,
|
pub text: String,
|
||||||
pub command: 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(),
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -26,8 +26,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
slint::platform::set_platform(Box::new(backend))?;
|
slint::platform::set_platform(Box::new(backend))?;
|
||||||
|
|
||||||
let screen_width = 1920;
|
let screen_width = 1366;
|
||||||
let screen_height = 1080;
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
113
src/powermenu.rs
113
src/powermenu.rs
@ -1,84 +1,55 @@
|
|||||||
use slint::{Color, LogicalPosition, LogicalSize, SharedString};
|
use slint::{Color, LogicalPosition, LogicalSize, SharedString};
|
||||||
|
use crate::config::ResolvedConfig;
|
||||||
|
|
||||||
slint::include_modules!();
|
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(
|
pub fn run_power_menu(
|
||||||
screen_width: i32,
|
cfg: ResolvedConfig
|
||||||
screen_height: i32,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let ui = PowerMenu::new()?;
|
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;
|
ui.set_screen_width(cfg.screen_width);
|
||||||
let btn_height = 120;
|
ui.set_screen_height(cfg.screen_height);
|
||||||
let btn_border_radius = 20;
|
|
||||||
|
|
||||||
let btn_bg_normal = Color::from_argb_encoded(0x75919191);
|
ui.set_screen_bg(cfg.screen_bg);
|
||||||
let btn_bg_hover = Color::from_argb_encoded(0x84929292);
|
|
||||||
let btn_bg_clicked = Color::from_argb_encoded(0xAB9B9B9B);
|
|
||||||
|
|
||||||
let option_icon_height = btn_height * 75 / 100;
|
ui.set_menu_width(cfg.menu_width);
|
||||||
let icon_font_size = option_icon_height * 80 / 100;
|
ui.set_menu_height(cfg.menu_height);
|
||||||
let icon_font = SharedString::from("IosevkaTermSlab Nerd Font Mono");
|
ui.set_menu_pos_x(cfg.menu_pos_x);
|
||||||
let icon_font_color = Color::from_argb_encoded(0xFFFFFFFF);
|
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;
|
ui.set_menu_bg(cfg.menu_bg);
|
||||||
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_ico_lockscreen(SharedString::from(""));
|
ui.set_ico_lockscreen(SharedString::from(""));
|
||||||
ui.set_ico_logout(SharedString::from(""));
|
ui.set_ico_logout(SharedString::from(""));
|
||||||
@ -93,10 +64,10 @@ pub fn run_power_menu(
|
|||||||
ui.set_button_style(button_style);
|
ui.set_button_style(button_style);
|
||||||
|
|
||||||
ui.window()
|
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.window().set_position(LogicalPosition::new(0.0, 0.0));
|
||||||
|
|
||||||
ui.run();
|
ui.run();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user