diff --git a/config.yaml b/config.yaml index 7b0abee2b..21743d1da 100644 --- a/config.yaml +++ b/config.yaml @@ -34,6 +34,15 @@ servers: # Channels to join upon connecting to the server channels: - "##rust" + +# Font settings +font: + # Specify the monospaced font family to use + # - Default is Iosevka Term and provided by this application + family: Iosevka Term + # Specify the font size + # - Default is 13 + size: 13 # Settings applied by default to new buffers new_buffer: diff --git a/data/src/config.rs b/data/src/config.rs index 4fd8e9061..e944936d1 100644 --- a/data/src/config.rs +++ b/data/src/config.rs @@ -15,11 +15,19 @@ pub struct Config { #[serde(default)] pub palette: Palette, pub servers: server::Map, + #[serde(default)] + pub font: Font, /// Default settings when creating a new buffer #[serde(default)] pub new_buffer: buffer::Settings, } +#[derive(Debug, Clone, Default, Deserialize)] +pub struct Font { + pub family: Option, + pub size: Option, +} + impl Config { pub fn config_dir() -> PathBuf { let dir = environment::config_dir().join("halloy"); diff --git a/src/font.rs b/src/font.rs index 22f627478..809554675 100644 --- a/src/font.rs +++ b/src/font.rs @@ -1,21 +1,61 @@ +use std::sync::OnceLock; + +use data::Config; + use iced::font::{self, Error}; -use iced::{Command, Font}; +use iced::Command; -pub const MONO: Font = Font { +pub static MONO: Font = Font::new(false); +pub static MONO_BOLD: Font = Font::new(true); +pub const ICON: iced::Font = iced::Font { monospaced: true, - ..Font::with_name("Iosevka Term") + ..iced::Font::with_name("bootstrap-icons") }; -pub const MONO_BOLD: Font = Font { - monospaced: true, - weight: font::Weight::Bold, - ..Font::with_name("Iosevka Term") -}; +#[derive(Debug, Clone)] +pub struct Font { + bold: bool, + inner: OnceLock, +} -pub const ICON: Font = Font { - monospaced: true, - ..Font::with_name("bootstrap-icons") -}; +impl Font { + const fn new(bold: bool) -> Self { + Self { + bold, + inner: OnceLock::new(), + } + } + + fn set(&self, name: String) { + let name = Box::leak(name.into_boxed_str()); + let weight = if self.bold { + font::Weight::Bold + } else { + font::Weight::Normal + }; + + let _ = self.inner.set(iced::Font { + monospaced: true, + weight, + ..iced::Font::with_name(name) + }); + } +} + +impl From for iced::Font { + fn from(value: Font) -> Self { + value.inner.get().copied().expect("font is set on startup") + } +} + +pub fn set(config: Option<&Config>) { + let family = config + .and_then(|config| config.font.family.clone()) + .unwrap_or_else(|| String::from("Iosevka Term")); + + MONO.set(family.clone()); + MONO_BOLD.set(family); +} pub fn load() -> Command> { Command::batch(vec![ diff --git a/src/main.rs b/src/main.rs index 8b624c6d4..2a6a53823 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,7 +47,13 @@ pub fn main() -> iced::Result { data::environment::formatted_version() ); - if let Err(error) = Halloy::run(settings()) { + let config_load = Config::load(); + + // DANGER ZONE - font must be set using config + // before we do any iced related stuff w/ it + font::set(config_load.as_ref().ok()); + + if let Err(error) = Halloy::run(settings(config_load)) { log::error!("{}", error.to_string()); Err(error) } else { @@ -72,15 +78,26 @@ fn window_settings() -> iced::window::Settings { } } -fn settings() -> iced::Settings<()> { +fn settings( + config_load: Result, +) -> iced::Settings> { + let default_text_size = config_load + .as_ref() + .ok() + .and_then(|config| config.font.size) + .map(f32::from) + .unwrap_or(theme::TEXT_SIZE); + iced::Settings { - default_font: font::MONO, - default_text_size: theme::TEXT_SIZE, + default_font: font::MONO.clone().into(), + default_text_size, window: iced::window::Settings { ..window_settings() }, exit_on_close_request: false, - ..Default::default() + flags: config_load, + id: None, + antialiasing: false, } } @@ -92,7 +109,9 @@ struct Halloy { } impl Halloy { - pub fn load_from_state() -> (Halloy, Command) { + pub fn load_from_state( + config_load: Result, + ) -> (Halloy, Command) { let load_dashboard = |config| match data::Dashboard::load() { Ok(dashboard) => screen::Dashboard::restore(dashboard), Err(error) => { @@ -104,7 +123,7 @@ impl Halloy { } }; - let (screen, config, command) = match Config::load() { + let (screen, config, command) = match config_load { Ok(config) => { let (screen, command) = load_dashboard(&config); @@ -160,10 +179,10 @@ impl Application for Halloy { type Executor = executor::Default; type Message = Message; type Theme = theme::Theme; - type Flags = (); + type Flags = Result; - fn new(_flags: ()) -> (Halloy, Command) { - let (halloy, command) = Halloy::load_from_state(); + fn new(config_load: Self::Flags) -> (Halloy, Command) { + let (halloy, command) = Halloy::load_from_state(config_load); ( halloy, @@ -199,7 +218,7 @@ impl Application for Halloy { if let Some(event) = help.update(message) { match event { help::Event::RefreshConfiguration => { - let (halloy, command) = Halloy::load_from_state(); + let (halloy, command) = Halloy::load_from_state(Config::load()); *self = halloy; return command; @@ -217,7 +236,7 @@ impl Application for Halloy { if let Some(event) = welcome.update(message) { match event { welcome::Event::RefreshConfiguration => { - let (halloy, command) = Halloy::load_from_state(); + let (halloy, command) = Halloy::load_from_state(Config::load()); *self = halloy; return command; diff --git a/src/screen/help.rs b/src/screen/help.rs index a57196840..a9f14adb4 100644 --- a/src/screen/help.rs +++ b/src/screen/help.rs @@ -59,7 +59,7 @@ impl Help { .spacing(1) .push(icon::error().size(45)) .push(vertical_space(10)) - .push(text("Error reading configuration file").font(font::MONO_BOLD)) + .push(text("Error reading configuration file").font(font::MONO_BOLD.clone())) .push(vertical_space(3)) .push(text(self.error.to_string()).style(theme::Text::Error)) .push(vertical_space(10)) diff --git a/src/screen/welcome.rs b/src/screen/welcome.rs index ea521d15a..4cb7a32e0 100644 --- a/src/screen/welcome.rs +++ b/src/screen/welcome.rs @@ -64,7 +64,7 @@ impl Welcome { .spacing(1) .push(image(format!("{}/assets/logo.png", env!("CARGO_MANIFEST_DIR"))).width(150)) .push(vertical_space(10)) - .push(text("Welcome to Halloy!").font(font::MONO_BOLD)) + .push(text("Welcome to Halloy!").font(font::MONO_BOLD.clone())) .push(vertical_space(4)) .push(text( "No configuration file found. Please follow the steps below to proceed", diff --git a/src/theme.rs b/src/theme.rs index 033f284ad..ac79f671e 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -4,6 +4,8 @@ use iced::{application, Background, Color}; use crate::widget::selectable_text; +// TODO: If we use non-standard font sizes, we should consider +// Config.font.size since it's user configurable pub const TEXT_SIZE: f32 = 13.0; pub const ICON_SIZE: f32 = 12.0;