···77[dependencies]
88# UI
99slint = { version = "1", features = ["unstable-winit-030"] }
1010+fontdue = "0.7"
10111112# System tray + menu
1213tray-icon = "0.19"
+26-1
src/settings/mod.rs
···22use std::sync::{Arc, Mutex};
3344use slint::{ComponentHandle, Model, ModelRc, SharedString, VecModel};
55+use fontdue::Font;
5667use crate::autostart;
78use crate::config::{AppConfig, BreakLevelConfig, BreakModeConfig, LongBreakConfig, OverlayTheme};
···143144 .map(|p| SharedString::from(p.name.as_str()))
144145 .collect();
145146 names.sort();
146146- window.set_profile_names(ModelRc::new(VecModel::from(names)));
147147+ window.set_profile_names(ModelRc::new(VecModel::from(names.clone())));
148148+149149+ // Measure profile name widths (pixels) using bundled Nunito Regular at 12px
150150+ fn measure_name_widths(names: &[SharedString]) -> Vec<i32> {
151151+ // include_bytes with concat! so path is resolved from manifest dir at compile time
152152+ let font_bytes = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/fonts/Nunito-Regular.ttf"));
153153+ let font = Font::from_bytes(font_bytes.as_ref(), fontdue::FontSettings::default()).unwrap();
154154+ let font_size = 12.0;
155155+ names
156156+ .iter()
157157+ .map(|s| {
158158+ let mut w = 0.0_f32;
159159+ for ch in s.as_str().chars() {
160160+ let m = font.metrics(ch, font_size);
161161+ w += m.advance_width;
162162+ }
163163+ // round and ensure at least 16px (defensive)
164164+ w.round() as i32
165165+ })
166166+ .collect()
167167+ }
168168+169169+ let name_widths = measure_name_widths(&names);
170170+ // set as a VecModel of ints for the Slint property
171171+ window.set_profile_name_widths(ModelRc::new(VecModel::from(name_widths)));
147172148173 window.set_sound_enabled(cfg.appearance.sound_enabled);
149174 window.set_sound_volume(cfg.appearance.sound_volume);