pub mod color; pub mod font; use color::ColorPair; use font::FontExt; use pango::{prelude::FontExt as _, Font, Language, SCALE}; use wmenu_rs::Menu as WMenu; struct Config { pub bottom: bool, pub padding: usize, pub lines: i32, pub font: String, pub font_size: u8, pub normal_color: ColorPair, pub prompt_color: ColorPair, pub selection_color: ColorPair, } impl Config { fn font_string(&self) -> String { format!("{} {}", self.font, self.font_size) } fn font_height(&self, font: &str) -> i32 { let font = Font::load_from_string(font).unwrap(); let metrics = font.metrics(Some(&Language::default())); metrics.height() / SCALE } fn apply(self, wmenu: &mut WMenu) { let font_string = self.font_string(); let font_height = self.font_height(&font_string); let line_height = font_height + 2; let mut height = line_height; if self.lines > 0 { height = height + height * self.lines; } wmenu .height(height) .bottom(self.bottom) .padding(self.padding.try_into().unwrap()) .lines(self.lines.try_into().unwrap()) .line_height(line_height) .font(&font_string) .normal_bg(self.normal_color.bg.into()) .normal_fg(self.normal_color.fg.into()) .prompt_bg(self.prompt_color.bg.into()) .prompt_fg(self.prompt_color.fg.into()) .selection_bg(self.selection_color.bg.into()) .selection_fg(self.selection_color.fg.into()); } } impl Default for Config { fn default() -> Self { Self { bottom: false, lines: 0, padding: 16, font: "monospace".to_string(), font_size: 12, normal_color: (0x222222ff, 0xbbbbbbffu32).into(), prompt_color: (0x005577ff, 0xeeeeeeffu32).into(), selection_color: (0x005577ff, 0x227799ffu32).into(), } } } struct Menu { wmenu: WMenu, } impl Menu { pub fn new(config: Config) -> Self { let mut wmenu = WMenu::new(); config.apply(&mut wmenu); Self { wmenu } } pub fn inner(&self) -> &WMenu { &self.wmenu } pub fn inner_mut(&mut self) -> &mut WMenu { &mut self.wmenu } pub fn add_item(&mut self, item: &str) -> &mut Menu { self.inner_mut().add_item(item); self } pub fn add_items(&mut self, items: &[&str]) -> &mut Menu { self.inner_mut().add_items(items); self } } impl Default for Menu { fn default() -> Self { Self::new(Config::default()) } } impl From for WMenu { fn from(value: Menu) -> Self { value.wmenu } } #[cfg(test)] mod tests { use crate::{Config, Menu}; #[test] fn it_works() { let mut menu = Menu::new(Config::default()); menu.add_items(&["rowern", "sybil", "vex"]); let result = menu.inner_mut().run(); println!("{result:?}"); } }