diff --git a/Cargo.toml b/Cargo.toml index 1041fa4..a971b7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,6 +100,7 @@ tokio = { version = "1.24.2", optional = true } tracing = "0.1" unicode-segmentation = "1.6" url = "2.4.0" +ustr = { version = "1.0.0", features = ["serde"] } zbus = { version = "4.2.1", default-features = false, optional = true } [target.'cfg(unix)'.dependencies] diff --git a/examples/cosmic/src/main.rs b/examples/cosmic/src/main.rs index eae2c95..ed470eb 100644 --- a/examples/cosmic/src/main.rs +++ b/examples/cosmic/src/main.rs @@ -16,7 +16,7 @@ pub fn main() -> cosmic::iced::Result { cosmic::icon_theme::set_default("Pop"); #[allow(clippy::field_reassign_with_default)] let settings = Settings { - default_font: cosmic::font::FONT, + default_font: cosmic::font::default(), window: cosmic::iced::window::Settings { min_size: Some(cosmic::iced::Size::new(600., 300.)), ..cosmic::iced::window::Settings::default() diff --git a/examples/cosmic/src/window/demo.rs b/examples/cosmic/src/window/demo.rs index 713ec5c..f009793 100644 --- a/examples/cosmic/src/window/demo.rs +++ b/examples/cosmic/src/window/demo.rs @@ -320,7 +320,7 @@ impl State { .padding(0) .into(), Some(DemoView::TabB) => settings::view_column(vec![ - text("Selection").font(cosmic::font::FONT_SEMIBOLD).into(), + text("Selection").font(cosmic::font::semibold()).into(), text("Horizontal").into(), segmented_control::horizontal(&self.selection) .on_activate(Message::Selection) @@ -378,9 +378,7 @@ impl State { .spacing(12) .width(Length::Fill) .into(), - text("View Switcher") - .font(cosmic::font::FONT_SEMIBOLD) - .into(), + text("View Switcher").font(cosmic::font::semibold()).into(), text("Horizontal").into(), tab_bar::horizontal(&self.selection) .on_activate(Message::Selection) diff --git a/iced b/iced index 90f904f..3dc0bbf 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit 90f904fe28fba26805aaac2c2c5dd1514607242e +Subproject commit 3dc0bbf653d2788b2352ede423804f831f14436c diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index 1a62713..8b43769 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -627,7 +627,7 @@ impl Cosmic { crate::icon_theme::set_default(config.icon_theme.clone()); } - *crate::config::COSMIC_TK.lock().unwrap() = config; + *crate::config::COSMIC_TK.write().unwrap() = config; } Message::Focus(f) => { diff --git a/src/app/settings.rs b/src/app/settings.rs index 5b27f9a..af976d9 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -81,7 +81,7 @@ impl Default for Settings { no_main_window: false, client_decorations: true, debug: false, - default_font: font::FONT, + default_font: font::default(), default_icon_theme: None, default_text_size: 14.0, resizable: Some(8.0), diff --git a/src/applet/mod.rs b/src/applet/mod.rs index fc3a9cf..2ee8230 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -164,7 +164,7 @@ impl Context { ) .resizable(None) .default_text_size(14.0) - .default_font(crate::font::FONT) + .default_font(crate::font::default()) .transparent(true); if let Some(theme) = self.theme() { settings = settings.theme(theme); @@ -339,7 +339,7 @@ impl Context { Size::PanelSize(PanelSize::XS) => crate::widget::text::body, Size::Hardcoded(_) => crate::widget::text, }; - t(msg).font(crate::font::FONT) + t(msg).font(crate::font::default()) } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 9054e95..14cf419 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -6,14 +6,16 @@ use crate::cosmic_theme::Density; use cosmic_config::cosmic_config_derive::CosmicConfigEntry; use cosmic_config::{Config, CosmicConfigEntry}; +use iced::font::Family; use serde::{Deserialize, Serialize}; -use std::sync::{LazyLock, Mutex}; +use std::sync::{LazyLock, RwLock}; +use ustr::Ustr; /// ID for the `CosmicTk` config. pub const ID: &str = "com.system76.CosmicTk"; -pub static COSMIC_TK: LazyLock> = LazyLock::new(|| { - Mutex::new( +pub static COSMIC_TK: LazyLock> = LazyLock::new(|| { + RwLock::new( CosmicTk::config() .map(|c| { CosmicTk::get_entry(&c).unwrap_or_else(|(errors, mode)| { @@ -30,37 +32,47 @@ pub static COSMIC_TK: LazyLock> = LazyLock::new(|| { /// Apply the theme to other toolkits. #[allow(clippy::missing_panics_doc)] pub fn apply_theme_global() -> bool { - COSMIC_TK.lock().unwrap().apply_theme_global + COSMIC_TK.read().unwrap().apply_theme_global } /// Show minimize button in window header. #[allow(clippy::missing_panics_doc)] pub fn show_minimize() -> bool { - COSMIC_TK.lock().unwrap().show_minimize + COSMIC_TK.read().unwrap().show_minimize } /// Show maximize button in window header. #[allow(clippy::missing_panics_doc)] pub fn show_maximize() -> bool { - COSMIC_TK.lock().unwrap().show_maximize + COSMIC_TK.read().unwrap().show_maximize } /// Preferred icon theme. #[allow(clippy::missing_panics_doc)] pub fn icon_theme() -> String { - COSMIC_TK.lock().unwrap().icon_theme.clone() + COSMIC_TK.read().unwrap().icon_theme.clone() } /// Density of CSD/SSD header bars. #[allow(clippy::missing_panics_doc)] pub fn header_size() -> Density { - COSMIC_TK.lock().unwrap().header_size + COSMIC_TK.read().unwrap().header_size } /// Interface density. #[allow(clippy::missing_panics_doc)] pub fn interface_density() -> Density { - COSMIC_TK.lock().unwrap().interface_density + COSMIC_TK.read().unwrap().interface_density +} + +#[allow(clippy::missing_panics_doc)] +pub fn interface_font() -> FontConfig { + COSMIC_TK.read().unwrap().interface_font +} + +#[allow(clippy::missing_panics_doc)] +pub fn monospace_font() -> FontConfig { + COSMIC_TK.read().unwrap().monospace_font } #[derive(Clone, CosmicConfigEntry, Debug, Eq, PartialEq)] @@ -83,6 +95,12 @@ pub struct CosmicTk { /// Interface density. pub interface_density: Density, + + /// Interface font family + pub interface_font: FontConfig, + + /// Mono font family + pub monospace_font: FontConfig, } impl Default for CosmicTk { @@ -94,6 +112,18 @@ impl Default for CosmicTk { icon_theme: String::from("Cosmic"), header_size: Density::Standard, interface_density: Density::Standard, + interface_font: FontConfig { + family: Ustr::from("Fira Sans"), + weight: iced::font::Weight::Normal, + stretch: iced::font::Stretch::Normal, + style: iced::font::Style::Normal, + }, + monospace_font: FontConfig { + family: Ustr::from("Fira Mono"), + weight: iced::font::Weight::Normal, + stretch: iced::font::Stretch::Normal, + style: iced::font::Style::Normal, + }, } } } @@ -103,3 +133,22 @@ impl CosmicTk { Config::new(ID, Self::VERSION) } } + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +pub struct FontConfig { + pub family: Ustr, + pub weight: iced::font::Weight, + pub stretch: iced::font::Stretch, + pub style: iced::font::Style, +} + +impl From for iced::Font { + fn from(font: FontConfig) -> Self { + Self { + family: iced::font::Family::Name(font.family.as_str()), + weight: font.weight, + stretch: font.stretch, + style: font.style, + } + } +} diff --git a/src/font.rs b/src/font.rs index 926ef14..2182531 100644 --- a/src/font.rs +++ b/src/font.rs @@ -10,59 +10,31 @@ use iced::{ }; use iced_core::font::Family; -pub const DEFAULT: Font = FONT; - -pub const FONT: Font = Font { - family: Family::Name("Fira Sans"), - weight: iced_core::font::Weight::Normal, - stretch: iced_core::font::Stretch::Normal, - style: iced_core::font::Style::Normal, -}; - -pub const FONT_DATA: &[u8] = include_bytes!("../res/Fira/FiraSans-Regular.otf"); - -pub const FONT_LIGHT: Font = Font { - family: Family::Name("Fira Sans"), - weight: iced_core::font::Weight::Light, - stretch: iced_core::font::Stretch::Normal, - style: iced_core::font::Style::Normal, -}; - -pub const FONT_LIGHT_DATA: &[u8] = include_bytes!("../res/Fira/FiraSans-Light.otf"); - -pub const FONT_SEMIBOLD: Font = Font { - family: Family::Name("Fira Sans"), - weight: iced_core::font::Weight::Semibold, - stretch: iced_core::font::Stretch::Normal, - style: iced_core::font::Style::Normal, -}; - -pub const FONT_SEMIBOLD_DATA: &[u8] = include_bytes!("../res/Fira/FiraSans-SemiBold.otf"); - -pub const FONT_BOLD: Font = Font { - family: Family::Name("Fira Sans"), - weight: iced_core::font::Weight::Bold, - stretch: iced_core::font::Stretch::Normal, - style: iced_core::font::Style::Normal, -}; - -pub const FONT_BOLD_DATA: &[u8] = include_bytes!("../res/Fira/FiraSans-Bold.otf"); - -pub const FONT_MONO_REGULAR: Font = Font { - family: Family::Name("Fira Mono"), - weight: iced_core::font::Weight::Normal, - stretch: iced_core::font::Stretch::Normal, - style: iced_core::font::Style::Normal, -}; - -pub const FONT_MONO_REGULAR_DATA: &[u8] = include_bytes!("../res/Fira/FiraMono-Regular.otf"); - -pub fn load_fonts() -> Command> { - Command::batch(vec![ - load(FONT_DATA), - load(FONT_LIGHT_DATA), - load(FONT_SEMIBOLD_DATA), - load(FONT_BOLD_DATA), - load(FONT_MONO_REGULAR_DATA), - ]) +pub fn default() -> Font { + Font::from(crate::config::interface_font()) +} + +pub fn light() -> Font { + Font { + weight: iced_core::font::Weight::Light, + ..default() + } +} + +pub fn semibold() -> Font { + Font { + weight: iced_core::font::Weight::Semibold, + ..default() + } +} + +pub fn bold() -> Font { + Font { + weight: iced_core::font::Weight::Bold, + ..default() + } +} + +pub fn mono() -> Font { + Font::from(crate::config::monospace_font()) } diff --git a/src/widget/button/icon.rs b/src/widget/button/icon.rs index aa3c2be..311f8eb 100644 --- a/src/widget/button/icon.rs +++ b/src/widget/button/icon.rs @@ -145,10 +145,9 @@ impl<'a, Message: Clone + 'static> From> for Element<'a, Mes crate::widget::text(builder.label) .size(builder.font_size) .line_height(LineHeight::Absolute(builder.line_height.into())) - .font({ - let mut font = crate::font::DEFAULT; - font.weight = builder.font_weight; - font + .font(crate::font::Font { + weight: builder.font_weight, + ..crate::font::default() }) .into(), ); @@ -182,10 +181,9 @@ impl<'a, Message: Clone + 'static> From> for Element<'a, Mes } else { tooltip(button, builder.tooltip, tooltip::Position::Top) .size(builder.font_size) - .font({ - let mut font = crate::font::DEFAULT; - font.weight = builder.font_weight; - font + .font(crate::font::Font { + weight: builder.font_weight, + ..crate::font::default() }) .into() } diff --git a/src/widget/button/link.rs b/src/widget/button/link.rs index 3128ca7..e62b05b 100644 --- a/src/widget/button/link.rs +++ b/src/widget/button/link.rs @@ -63,14 +63,14 @@ impl<'a, Message: Clone + 'static> From> for Element<'a, Mes fn from(mut builder: Button<'a, Message>) -> Element<'a, Message> { let button: super::Button<'a, Message> = row::with_capacity(2) .push({ - let mut font = crate::font::DEFAULT; - font.weight = builder.font_weight; - // TODO: Avoid allocation crate::widget::text(builder.label.to_string()) .size(builder.font_size) .line_height(LineHeight::Absolute(builder.line_height.into())) - .font(font) + .font(crate::font::Font { + weight: builder.font_weight, + ..crate::font::default() + }) }) .push_maybe(if builder.variant.trailing_icon { Some(icon().icon().size(builder.icon_size)) @@ -93,10 +93,9 @@ impl<'a, Message: Clone + 'static> From> for Element<'a, Mes } else { tooltip(button, builder.tooltip, tooltip::Position::Top) .size(builder.font_size) - .font({ - let mut font = crate::font::DEFAULT; - font.weight = builder.font_weight; - font + .font(crate::font::Font { + weight: builder.font_weight, + ..crate::font::default() }) .into() } diff --git a/src/widget/button/text.rs b/src/widget/button/text.rs index b5dccc8..0a59976 100644 --- a/src/widget/button/text.rs +++ b/src/widget/button/text.rs @@ -101,8 +101,10 @@ impl<'a, Message: Clone + 'static> From> for Element<'a, Mes }); let label: Option> = (!builder.label.is_empty()).then(|| { - let mut font = crate::font::DEFAULT; - font.weight = builder.font_weight; + let font = crate::font::Font { + weight: builder.font_weight, + ..crate::font::default() + }; // TODO: Avoid allocation crate::widget::text(builder.label.to_string()) @@ -135,10 +137,9 @@ impl<'a, Message: Clone + 'static> From> for Element<'a, Mes } else { tooltip(button, builder.tooltip, tooltip::Position::Top) .size(builder.font_size) - .font({ - let mut font = crate::font::DEFAULT; - font.weight = builder.font_weight; - font + .font(crate::font::Font { + weight: builder.font_weight, + ..crate::font::default() }) .into() } diff --git a/src/widget/dropdown/menu/mod.rs b/src/widget/dropdown/menu/mod.rs index 3e26edf..e3c056f 100644 --- a/src/widget/dropdown/menu/mod.rs +++ b/src/widget/dropdown/menu/mod.rs @@ -459,7 +459,7 @@ impl<'a, S: AsRef, Message> Widget }, ); - (appearance.selected_text_color, crate::font::FONT_SEMIBOLD) + (appearance.selected_text_color, crate::font::semibold()) } else if *self.hovered_option == Some(i) { let item_x = bounds.x + appearance.border_width; let item_width = appearance.border_width.mul_add(-2.0, bounds.width); @@ -480,9 +480,9 @@ impl<'a, S: AsRef, Message> Widget appearance.hovered_background, ); - (appearance.hovered_text_color, crate::font::FONT) + (appearance.hovered_text_color, crate::font::default()) } else { - (appearance.text_color, crate::font::FONT) + (appearance.text_color, crate::font::default()) }; let bounds = Rectangle { diff --git a/src/widget/dropdown/multi/menu.rs b/src/widget/dropdown/multi/menu.rs index f586e9e..3710698 100644 --- a/src/widget/dropdown/multi/menu.rs +++ b/src/widget/dropdown/multi/menu.rs @@ -549,7 +549,7 @@ where }, ); - (appearance.selected_text_color, crate::font::FONT_SEMIBOLD) + (appearance.selected_text_color, crate::font::semibold()) } else if self.hovered_option.as_ref() == Some(item) { let item_x = bounds.x + appearance.border_width; let item_width = bounds.width - appearance.border_width * 2.0; @@ -572,9 +572,9 @@ where appearance.hovered_background, ); - (appearance.hovered_text_color, crate::font::FONT) + (appearance.hovered_text_color, crate::font::default()) } else { - (appearance.text_color, crate::font::FONT) + (appearance.text_color, crate::font::default()) }; let bounds = Rectangle { @@ -640,7 +640,7 @@ where bounds: bounds.size(), size: iced::Pixels(text_size), line_height: text::LineHeight::Absolute(Pixels(text_line_height + 4.0)), - font: crate::font::FONT, + font: crate::font::default(), horizontal_alignment: alignment::Horizontal::Center, vertical_alignment: alignment::Vertical::Center, shaping: text::Shaping::Advanced, diff --git a/src/widget/dropdown/multi/widget.rs b/src/widget/dropdown/multi/widget.rs index 4fcb73e..a97f03f 100644 --- a/src/widget/dropdown/multi/widget.rs +++ b/src/widget/dropdown/multi/widget.rs @@ -156,9 +156,7 @@ impl<'a, S: AsRef, Message: 'a, Item: Clone + PartialEq + 'static> cursor: mouse::Cursor, viewport: &Rectangle, ) { - let font = self - .font - .unwrap_or_else(|| text::Renderer::default_font(renderer)); + let font = self.font.unwrap_or_else(|| crate::font::default()); draw( renderer, @@ -275,7 +273,7 @@ pub fn layout( bounds: Size::new(f32::MAX, f32::MAX), size: iced::Pixels(text_size), line_height: text_line_height, - font: font.unwrap_or_else(|| text::Renderer::default_font(renderer)), + font: font.unwrap_or_else(|| crate::font::default()), horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, shaping: text::Shaping::Advanced, @@ -418,7 +416,7 @@ pub fn overlay<'a, S: AsRef, Message: 'a, Item: Clone + PartialEq + 'static bounds: Size::new(f32::MAX, f32::MAX), size: iced::Pixels(text_size), line_height, - font: font.unwrap_or_else(|| text::Renderer::default_font(renderer)), + font: font.unwrap_or_else(|| crate::font::default()), horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, shaping: text::Shaping::Advanced, diff --git a/src/widget/dropdown/widget.rs b/src/widget/dropdown/widget.rs index c4af8cb..691c7ad 100644 --- a/src/widget/dropdown/widget.rs +++ b/src/widget/dropdown/widget.rs @@ -76,7 +76,7 @@ impl<'a, S: AsRef, Message> Dropdown<'a, S, Message> { size: iced::Pixels(self.text_size.unwrap_or(14.0)), line_height: self.text_line_height, - font: self.font.unwrap_or(crate::font::FONT), + font: self.font.unwrap_or_else(crate::font::default), horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, shaping: text::Shaping::Advanced, @@ -113,7 +113,7 @@ impl<'a, S: AsRef, Message: 'a> Widget, Message: 'a> Widget(text: impl Into> + 'a) -> Text<'a, crate::Theme, Renderer> { - Text::new(text) + Text::new(text).font(crate::font::default()) } /// Available presets for text typography @@ -29,7 +29,7 @@ pub fn title1<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Text::new(text) .size(32.0) .line_height(LineHeight::Absolute(44.0.into())) - .font(crate::font::FONT_LIGHT) + .font(crate::font::semibold()) } /// [`Text`] widget with the Title 2 typography preset. @@ -37,6 +37,7 @@ pub fn title2<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Text::new(text) .size(28.0) .line_height(LineHeight::Absolute(36.0.into())) + .font(crate::font::default()) } /// [`Text`] widget with the Title 3 typography preset. @@ -44,6 +45,7 @@ pub fn title3<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Text::new(text) .size(24.0) .line_height(LineHeight::Absolute(32.0.into())) + .font(crate::font::default()) } /// [`Text`] widget with the Title 4 typography preset. @@ -51,6 +53,7 @@ pub fn title4<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Text::new(text) .size(20.0) .line_height(LineHeight::Absolute(28.0.into())) + .font(crate::font::default()) } /// [`Text`] widget with the Heading typography preset. @@ -58,7 +61,7 @@ pub fn heading<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Text::new(text) .size(14.0) .line_height(LineHeight::Absolute(iced::Pixels(20.0))) - .font(crate::font::FONT_SEMIBOLD) + .font(crate::font::semibold()) } /// [`Text`] widget with the Caption Heading typography preset. @@ -66,7 +69,7 @@ pub fn caption_heading<'a>(text: impl Into> + 'a) -> Text<'a, crate Text::new(text) .size(10.0) .line_height(LineHeight::Absolute(iced::Pixels(14.0))) - .font(crate::font::FONT_SEMIBOLD) + .font(crate::font::semibold()) } /// [`Text`] widget with the Body typography preset. @@ -74,6 +77,7 @@ pub fn body<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Re Text::new(text) .size(14.0) .line_height(LineHeight::Absolute(20.0.into())) + .font(crate::font::default()) } /// [`Text`] widget with the Caption typography preset. @@ -81,6 +85,7 @@ pub fn caption<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme, Text::new(text) .size(10.0) .line_height(LineHeight::Absolute(14.0.into())) + .font(crate::font::default()) } /// [`Text`] widget with the Monotext typography preset. @@ -88,5 +93,5 @@ pub fn monotext<'a>(text: impl Into> + 'a) -> Text<'a, crate::Theme Text::new(text) .size(14.0) .line_height(LineHeight::Absolute(20.0.into())) - .font(crate::font::FONT_MONO_REGULAR) + .font(crate::font::mono()) }