From 9ece0593a19283aa94e3b0952b1643a8421acc87 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Mon, 26 Jun 2023 15:15:15 -0700 Subject: [PATCH] Allow custom font --- Cargo.lock | 1 + Cargo.toml | 1 + config.yaml | 6 +++++ data/src/config.rs | 8 ++++++ src/font.rs | 63 ++++++++++++++++++++++++++++++++++--------- src/main.rs | 34 +++++++++++++++-------- src/screen/help.rs | 2 +- src/screen/welcome.rs | 2 +- 8 files changed, 92 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9a953c811..3f45892d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1063,6 +1063,7 @@ dependencies = [ "fern", "iced", "log", + "once_cell", "open", "palette", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 28b0f6bb3..2b59fbf7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ chrono = { version = "0.4", features = ['serde'] } fern = "0.6.1" iced = { version = "0.9", features = ["tokio", "lazy", "advanced", "image"] } log = "0.4.16" +once_cell = "1.18.0" palette = "=0.7.2" thiserror = "1.0.30" tokio = { version = "1.0", features = ["rt", "fs", "process"] } diff --git a/config.yaml b/config.yaml index 7b0abee2b..7a17d4a75 100644 --- a/config.yaml +++ b/config.yaml @@ -34,6 +34,12 @@ 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 # Settings applied by default to new buffers new_buffer: diff --git a/data/src/config.rs b/data/src/config.rs index 4fd8e9061..2c7b6369f 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, + // TODO: Do we make size, etc configurable and pass to Theme? +} + 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..9b1788ed1 100644 --- a/src/font.rs +++ b/src/font.rs @@ -1,21 +1,60 @@ +use data::Config; +use once_cell::sync::OnceCell; + 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: OnceCell, +} -pub const ICON: Font = Font { - monospaced: true, - ..Font::with_name("bootstrap-icons") -}; +impl Font { + const fn new(bold: bool) -> Self { + Self { + bold, + inner: OnceCell::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..378876507 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,19 @@ fn window_settings() -> iced::window::Settings { } } -fn settings() -> iced::Settings<()> { +fn settings( + config_load: Result, +) -> iced::Settings> { iced::Settings { - default_font: font::MONO, + default_font: font::MONO.clone().into(), default_text_size: theme::TEXT_SIZE, window: iced::window::Settings { ..window_settings() }, exit_on_close_request: false, - ..Default::default() + flags: config_load, + id: None, + antialiasing: false, } } @@ -92,7 +102,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 +116,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 +172,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 +211,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 +229,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",