diff --git a/Cargo.lock b/Cargo.lock index 33df1cdfc7..205f985167 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2181,6 +2181,7 @@ dependencies = [ "log", "num_enum", "once_cell", + "parley", "preprocessor", "serde", "serde_bytes", diff --git a/editor/Cargo.toml b/editor/Cargo.toml index c7ee2c3ebb..2943279591 100644 --- a/editor/Cargo.toml +++ b/editor/Cargo.toml @@ -45,6 +45,7 @@ vello = { workspace = true } base64 = { workspace = true } spin = { workspace = true } image = { workspace = true } +parley = { workspace = true } # Optional local dependencies wgpu-executor = { workspace = true, optional = true } diff --git a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs index 99fa880289..35d0c1d779 100644 --- a/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs +++ b/editor/src/messages/dialog/preferences_dialog/preferences_dialog_message_handler.rs @@ -300,7 +300,32 @@ impl PreferencesDialogMessageHandler { .widget_instance(), ]; - rows.extend_from_slice(&[header, node_graph_wires_label, graph_wire_style, brush_tool]); + let fonts_checkbox_id = CheckboxId::new(); + let system_fonts_description = " + Switch the font catalog to system fonts. On Desktop, this queries the system fonts from the backend. In future versions, this will query system fonts in the browser.\n\ + \n\ + This experimental functionality is slated for replacement in future versions of Graphite that will have more detailed font management.\n\ + \n\ + *Default: Off.* + " + .trim(); + let system_fonts = vec![ + Separator::new(SeparatorStyle::Unrelated).widget_instance(), + Separator::new(SeparatorStyle::Unrelated).widget_instance(), + CheckboxInput::new(preferences.system_fonts) + .tooltip_label("System Fonts") + .tooltip_description(system_fonts_description) + .on_update(|checkbox_input: &CheckboxInput| PreferencesMessage::SystemFonts { enabled: checkbox_input.checked }.into()) + .for_label(fonts_checkbox_id) + .widget_instance(), + TextLabel::new("System Fonts") + .tooltip_label("System Fonts") + .tooltip_description(system_fonts_description) + .for_checkbox(fonts_checkbox_id) + .widget_instance(), + ]; + + rows.extend_from_slice(&[header, node_graph_wires_label, graph_wire_style, brush_tool, system_fonts]); } // ============= diff --git a/editor/src/messages/portfolio/portfolio_message.rs b/editor/src/messages/portfolio/portfolio_message.rs index b5bdc45c1c..a81741737d 100644 --- a/editor/src/messages/portfolio/portfolio_message.rs +++ b/editor/src/messages/portfolio/portfolio_message.rs @@ -50,6 +50,7 @@ pub enum PortfolioMessage { }, DestroyAllDocuments, EditorPreferences, + LoadFontCatalog, FontCatalogLoaded { catalog: FontCatalog, }, diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 0231640404..664d0479e2 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -1,7 +1,7 @@ use super::document::utility_types::document_metadata::LayerNodeIdentifier; use super::document::utility_types::network_interface; use super::persistent_state::{PersistentStateMessage, PersistentStateMessageContext, PersistentStateMessageHandler}; -use super::utility_types::{CachedData, PanelLayoutSubdivision, PanelType, WorkspacePanelLayout}; +use super::utility_types::{CachedData, FontCatalog, FontCatalogFamily, FontCatalogStyle, PanelLayoutSubdivision, PanelType, WorkspacePanelLayout}; use crate::application::{Editor, generate_uuid}; use crate::consts::{DEFAULT_DOCUMENT_NAME, DEFAULT_STROKE_WIDTH, FILE_EXTENSION}; use crate::messages::animation::TimingInformation; @@ -34,6 +34,8 @@ use graphene_std::subpath::BezierHandles; use graphene_std::text::Font; use graphene_std::vector::misc::HandleId; use graphene_std::vector::{PointId, SegmentId, Vector, VectorModificationType}; +use parley::FontStyle; +use parley::fontique::{Collection, CollectionOptions}; use std::path::PathBuf; use std::vec; @@ -368,6 +370,33 @@ impl MessageHandler> for Portfolio responses.add(MenuBarMessage::SendLayout); responses.add(PersistentStateMessage::WriteState); } + PortfolioMessage::LoadFontCatalog => { + if Editor::environment().is_desktop() && preferences.system_fonts { + self.cached_data.system_font_collection.0 = Collection::new(CollectionOptions { shared: false, system_fonts: true }); + // shove font metadata into cached data catalog + let system_font_family_names: Vec = self.cached_data.system_font_collection.0.family_names().map(|n| n.to_owned()).collect(); + let mut families_for_catalog = Vec::new(); + for name in system_font_family_names { + let family = self.cached_data.system_font_collection.0.family_by_name(&name).unwrap(); + let mut styles = Vec::new(); + for font in family.fonts() { + let style = FontCatalogStyle { + weight: (font.weight().value()) as u32, + italic: font.style() == FontStyle::Italic, + url: "_SYSTEM".to_owned(), + }; + styles.push(style); + } + families_for_catalog.push(FontCatalogFamily { name: name.to_owned(), styles }); + } + responses.add(PortfolioMessage::FontCatalogLoaded { + catalog: FontCatalog(families_for_catalog), + }); + } else { + // if not desktop or not system fonts, call to frontend as usual + responses.add_front(FrontendMessage::TriggerFontCatalogLoad); + } + } PortfolioMessage::FontCatalogLoaded { catalog } => { self.cached_data.font_catalog = catalog; @@ -384,7 +413,28 @@ impl MessageHandler> for Portfolio let font = Font::new(font.font_family, style.to_named_style()); if !self.cached_data.font_cache.loaded_font(&font) { - responses.add(FrontendMessage::TriggerFontDataLoad { font, url: style.url }); + if style.url == "_SYSTEM" { + let system_font_family_names: Vec = self.cached_data.system_font_collection.0.family_names().map(|n| n.to_owned()).collect(); + for name in system_font_family_names { + if name == font.font_family { + let family = self.cached_data.system_font_collection.0.family_by_name(&name).unwrap(); + for system_font_style in family.fonts() { + if (system_font_style.weight().value() as u32 == style.weight) && ((system_font_style.style() == FontStyle::Italic) == style.italic) { + let mut font_data_vec = Vec::new(); + font_data_vec.extend(system_font_style.load(None).unwrap().data()); + responses.add(PortfolioMessage::FontLoaded { + font_family: name.clone(), + font_style: style.to_named_style(), + data: font_data_vec, + }); + return; + } + } + } + } + } else { + responses.add(FrontendMessage::TriggerFontDataLoad { font, url: style.url }); + } } } } @@ -439,9 +489,10 @@ impl MessageHandler> for Portfolio PortfolioMessage::EditorPreferences => self.executor.update_editor_preferences(preferences.editor_preferences()), PortfolioMessage::LoadDocumentResources { document_id } => { let catalog = &self.cached_data.font_catalog; - + // add handling for if font catalog preference has changed or maybe if user + // has requested font catalog refresh? if catalog.0.is_empty() { - responses.add_front(FrontendMessage::TriggerFontCatalogLoad); + responses.add(PortfolioMessage::LoadFontCatalog); return; } diff --git a/editor/src/messages/portfolio/utility_types.rs b/editor/src/messages/portfolio/utility_types.rs index f7e0ebb386..194f6ef946 100644 --- a/editor/src/messages/portfolio/utility_types.rs +++ b/editor/src/messages/portfolio/utility_types.rs @@ -1,11 +1,23 @@ use graphene_std::Color; use graphene_std::raster::Image; use graphene_std::text::{Font, FontCache}; +use parley::fontique; +use std::fmt; #[derive(Debug, Default)] pub struct CachedData { pub font_cache: FontCache, pub font_catalog: FontCatalog, + pub system_font_collection: FontCollection, +} + +#[derive(Default)] +pub struct FontCollection(pub fontique::Collection); + +impl fmt::Debug for FontCollection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Font Collection") + } } // TODO: Should this be a BTreeMap instead? diff --git a/editor/src/messages/preferences/preferences_message.rs b/editor/src/messages/preferences/preferences_message.rs index 8b6b64caa3..18aa7358a3 100644 --- a/editor/src/messages/preferences/preferences_message.rs +++ b/editor/src/messages/preferences/preferences_message.rs @@ -18,6 +18,9 @@ pub enum PreferencesMessage { BrushTool { enabled: bool, }, + SystemFonts { + enabled: bool, + }, ModifyLayout { zoom_with_scroll: bool, }, diff --git a/editor/src/messages/preferences/preferences_message_handler.rs b/editor/src/messages/preferences/preferences_message_handler.rs index 591f9cf3a3..e2b8582ea0 100644 --- a/editor/src/messages/preferences/preferences_message_handler.rs +++ b/editor/src/messages/preferences/preferences_message_handler.rs @@ -18,6 +18,7 @@ pub struct PreferencesMessageHandler { pub selection_mode: SelectionMode, pub zoom_with_scroll: bool, pub brush_tool: bool, + pub system_fonts: bool, pub graph_wire_style: GraphWireStyle, pub viewport_zoom_wheel_rate: f64, pub ui_scale: f64, @@ -61,6 +62,7 @@ impl Default for PreferencesMessageHandler { selection_mode: SelectionMode::Touched, zoom_with_scroll: matches!(MappingVariant::default(), MappingVariant::ZoomWithScroll), brush_tool: false, + system_fonts: false, graph_wire_style: GraphWireStyle::default(), viewport_zoom_wheel_rate: VIEWPORT_ZOOM_WHEEL_RATE, ui_scale: UI_SCALE_DEFAULT, @@ -103,6 +105,12 @@ impl MessageHandler> for Prefe responses.add(ToolMessage::RefreshToolShelf); } + // Per-preference messages + PreferencesMessage::SystemFonts { enabled } => { + self.system_fonts = enabled; + + responses.add(PortfolioMessage::LoadFontCatalog); + } PreferencesMessage::ModifyLayout { zoom_with_scroll } => { self.zoom_with_scroll = zoom_with_scroll;