From de366f951424fcdf4463a419db3fa659910fabfd Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sat, 9 Nov 2024 12:27:09 -0800 Subject: [PATCH] Add Brush tool warning; move font list loading to document creation time --- editor/src/dispatcher.rs | 6 -- .../preferences_dialog_message_handler.rs | 48 +++++++-------- .../src/messages/frontend/frontend_message.rs | 2 - .../messages/layout/layout_message_handler.rs | 1 - .../document/document_message_handler.rs | 2 +- .../messages/portfolio/portfolio_message.rs | 2 - .../portfolio/portfolio_message_handler.rs | 11 ++-- .../preferences_message_handler.rs | 6 +- .../messages/tool/tool_messages/brush_tool.rs | 18 ++++++ frontend/src/state-providers/fonts.ts | 60 ++++++++++++------- frontend/src/wasm-communication/messages.ts | 2 - frontend/wasm/src/editor_api.rs | 3 +- node-graph/gcore/src/text/font_cache.rs | 20 ++----- 13 files changed, 99 insertions(+), 82 deletions(-) diff --git a/editor/src/dispatcher.rs b/editor/src/dispatcher.rs index 4225b0c6..69194b03 100644 --- a/editor/src/dispatcher.rs +++ b/editor/src/dispatcher.rs @@ -3,8 +3,6 @@ use crate::messages::dialog::DialogMessageData; use crate::messages::portfolio::document::node_graph::document_node_definitions; use crate::messages::prelude::*; -use graphene_core::text::Font; - #[derive(Debug, Default)] pub struct Dispatcher { buffered_queue: Option>>, @@ -135,10 +133,6 @@ impl Dispatcher { // Display the menu bar at the top of the window queue.add(MenuBarMessage::SendLayout); - // Load the default font - let font = Font::new(graphene_core::consts::DEFAULT_FONT_FAMILY.into(), graphene_core::consts::DEFAULT_FONT_STYLE.into()); - queue.add(FrontendMessage::TriggerFontLoad { font, is_default: true }); - // Send the information for tooltips and categories for each node/input. queue.add(FrontendMessage::SendUIMetadata { input_type_descriptions: Vec::new(), 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 26d5d797..492e175e 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 @@ -61,34 +61,34 @@ impl PreferencesDialogMessageHandler { .widget_holder(), ]; - let imaginate_server_hostname = vec![ - TextLabel::new("Imaginate").min_width(60).italic(true).widget_holder(), - TextLabel::new("Server Hostname").table_align(true).widget_holder(), - Separator::new(SeparatorType::Unrelated).widget_holder(), - TextInput::new(&preferences.imaginate_server_hostname) - .min_width(200) - .on_update(|text_input: &TextInput| PreferencesMessage::ImaginateServerHostname { hostname: text_input.value.clone() }.into()) - .widget_holder(), - ]; - - let imaginate_refresh_frequency = vec![ - TextLabel::new("").min_width(60).widget_holder(), - TextLabel::new("Refresh Frequency").table_align(true).widget_holder(), - Separator::new(SeparatorType::Unrelated).widget_holder(), - NumberInput::new(Some(preferences.imaginate_refresh_frequency)) - .unit(" seconds") - .min(0.) - .max((1_u64 << f64::MANTISSA_DIGITS) as f64) - .min_width(200) - .on_update(|number_input: &NumberInput| PreferencesMessage::ImaginateRefreshFrequency { seconds: number_input.value.unwrap() }.into()) - .widget_holder(), - ]; + // TODO: Reenable when Imaginate is restored + // let imaginate_server_hostname = vec![ + // TextLabel::new("Imaginate").min_width(60).italic(true).widget_holder(), + // TextLabel::new("Server Hostname").table_align(true).widget_holder(), + // Separator::new(SeparatorType::Unrelated).widget_holder(), + // TextInput::new(&preferences.imaginate_server_hostname) + // .min_width(200) + // .on_update(|text_input: &TextInput| PreferencesMessage::ImaginateServerHostname { hostname: text_input.value.clone() }.into()) + // .widget_holder(), + // ]; + // let imaginate_refresh_frequency = vec![ + // TextLabel::new("").min_width(60).widget_holder(), + // TextLabel::new("Refresh Frequency").table_align(true).widget_holder(), + // Separator::new(SeparatorType::Unrelated).widget_holder(), + // NumberInput::new(Some(preferences.imaginate_refresh_frequency)) + // .unit(" seconds") + // .min(0.) + // .max((1_u64 << f64::MANTISSA_DIGITS) as f64) + // .min_width(200) + // .on_update(|number_input: &NumberInput| PreferencesMessage::ImaginateRefreshFrequency { seconds: number_input.value.unwrap() }.into()) + // .widget_holder(), + // ]; Layout::WidgetLayout(WidgetLayout::new(vec![ LayoutGroup::Row { widgets: zoom_with_scroll }, LayoutGroup::Row { widgets: use_vello }, - LayoutGroup::Row { widgets: imaginate_server_hostname }, - LayoutGroup::Row { widgets: imaginate_refresh_frequency }, + // LayoutGroup::Row { widgets: imaginate_server_hostname }, + // LayoutGroup::Row { widgets: imaginate_refresh_frequency }, ])) } diff --git a/editor/src/messages/frontend/frontend_message.rs b/editor/src/messages/frontend/frontend_message.rs index 7b0dc9ab..8f95e55b 100644 --- a/editor/src/messages/frontend/frontend_message.rs +++ b/editor/src/messages/frontend/frontend_message.rs @@ -81,8 +81,6 @@ pub enum FrontendMessage { }, TriggerFontLoad { font: Font, - #[serde(rename = "isDefault")] - is_default: bool, }, TriggerImport, TriggerIndexedDbRemoveDocument { diff --git a/editor/src/messages/layout/layout_message_handler.rs b/editor/src/messages/layout/layout_message_handler.rs index 36c1158c..9a0ea3f8 100644 --- a/editor/src/messages/layout/layout_message_handler.rs +++ b/editor/src/messages/layout/layout_message_handler.rs @@ -189,7 +189,6 @@ impl LayoutMessageHandler { responses.add(PortfolioMessage::LoadFont { font: Font::new(font_family.into(), font_style.into()), - is_default: false, }); (font_input.on_update.callback)(font_input) } diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 0475f7c8..b25742f5 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -1676,7 +1676,7 @@ impl DocumentMessageHandler { } } for font in fonts { - responses.add_front(FrontendMessage::TriggerFontLoad { font, is_default: false }); + responses.add_front(FrontendMessage::TriggerFontLoad { font }); } } diff --git a/editor/src/messages/portfolio/portfolio_message.rs b/editor/src/messages/portfolio/portfolio_message.rs index 02bb698a..2674ec2f 100644 --- a/editor/src/messages/portfolio/portfolio_message.rs +++ b/editor/src/messages/portfolio/portfolio_message.rs @@ -51,7 +51,6 @@ pub enum PortfolioMessage { font_style: String, preview_url: String, data: Vec, - is_default: bool, }, ImaginateCheckServerStatus, ImaginatePollServerStatus, @@ -63,7 +62,6 @@ pub enum PortfolioMessage { }, LoadFont { font: Font, - is_default: bool, }, NewDocumentWithName { name: String, diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index b49c9634..c9443e23 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -278,11 +278,10 @@ impl MessageHandler> for PortfolioMes font_style, preview_url, data, - is_default, } => { let font = Font::new(font_family, font_style); - self.persistent_data.font_cache.insert(font, preview_url, data, is_default); + self.persistent_data.font_cache.insert(font, preview_url, data); self.executor.update_font_cache(self.persistent_data.font_cache.clone()); for document_id in self.document_ids.iter() { let _ = self.executor.submit_node_graph_evaluation( @@ -334,9 +333,9 @@ impl MessageHandler> for PortfolioMes document.load_layer_resources(responses); } } - PortfolioMessage::LoadFont { font, is_default } => { + PortfolioMessage::LoadFont { font } => { if !self.persistent_data.font_cache.loaded_font(&font) { - responses.add_front(FrontendMessage::TriggerFontLoad { font, is_default }); + responses.add_front(FrontendMessage::TriggerFontLoad { font }); } } PortfolioMessage::NewDocumentWithName { name } => { @@ -939,6 +938,10 @@ impl PortfolioMessageHandler { if self.active_document().is_some() { responses.add(BroadcastEvent::ToolAbort); responses.add(ToolMessage::DeactivateTools); + } else { + // Load the default font upon creating the first document + let font = Font::new(graphene_core::consts::DEFAULT_FONT_FAMILY.into(), graphene_core::consts::DEFAULT_FONT_STYLE.into()); + responses.add(FrontendMessage::TriggerFontLoad { font }); } // TODO: Remove this and find a way to fix the issue where creating a new document when the node graph is open causes the transform in the new document to be incorrect diff --git a/editor/src/messages/preferences/preferences_message_handler.rs b/editor/src/messages/preferences/preferences_message_handler.rs index 2ad57606..940c8aed 100644 --- a/editor/src/messages/preferences/preferences_message_handler.rs +++ b/editor/src/messages/preferences/preferences_message_handler.rs @@ -45,8 +45,10 @@ impl MessageHandler for PreferencesMessageHandler { if let Ok(deserialized_preferences) = serde_json::from_str::(&preferences) { *self = deserialized_preferences; - responses.add(PortfolioMessage::ImaginateServerHostname); - responses.add(PortfolioMessage::ImaginateCheckServerStatus); + // TODO: Reenable when Imaginate is restored + // responses.add(PortfolioMessage::ImaginateServerHostname); + // responses.add(PortfolioMessage::ImaginateCheckServerStatus); + responses.add(PortfolioMessage::EditorPreferences); responses.add(PortfolioMessage::UpdateVelloPreference); responses.add(PreferencesMessage::ModifyLayout { diff --git a/editor/src/messages/tool/tool_messages/brush_tool.rs b/editor/src/messages/tool/tool_messages/brush_tool.rs index 909ac7b5..6b86a360 100644 --- a/editor/src/messages/tool/tool_messages/brush_tool.rs +++ b/editor/src/messages/tool/tool_messages/brush_tool.rs @@ -29,6 +29,7 @@ pub struct BrushTool { } pub struct BrushOptions { + legacy_warning_was_shown: bool, diameter: f64, hardness: f64, flow: f64, @@ -41,6 +42,7 @@ pub struct BrushOptions { impl Default for BrushOptions { fn default() -> Self { Self { + legacy_warning_was_shown: false, diameter: DEFAULT_BRUSH_SIZE, hardness: 0., flow: 100., @@ -78,6 +80,7 @@ pub enum BrushToolMessageOptionsUpdate { Hardness(f64), Spacing(f64), WorkingColors(Option, Option), + NoDisplayLegacyWarning, } #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] @@ -217,6 +220,7 @@ impl<'a> MessageHandler> for BrushTo self.options.color.primary_working_color = primary; self.options.color.secondary_working_color = secondary; } + BrushToolMessageOptionsUpdate::NoDisplayLegacyWarning => self.options.legacy_warning_was_shown = true, } self.send_layout(responses, LayoutTarget::ToolOptions); @@ -308,6 +312,20 @@ impl Fsm for BrushToolFsmState { document, global_tool_data, input, .. } = tool_action_data; + if !tool_options.legacy_warning_was_shown { + responses.add(DialogMessage::DisplayDialogError { + title: "Unsupported tool".into(), + description: " + The current Brush tool is a legacy feature with\n\ + significant quality and performance limitations.\n\ + It will be replaced soon by a new implementation.\n\ + " + .trim() + .into(), + }); + responses.add(BrushToolMessage::UpdateOptions(BrushToolMessageOptionsUpdate::NoDisplayLegacyWarning)); + } + let ToolMessage::Brush(event) = event else { return self; }; diff --git a/frontend/src/state-providers/fonts.ts b/frontend/src/state-providers/fonts.ts index d2a0f9c8..ae7f8a6f 100644 --- a/frontend/src/state-providers/fonts.ts +++ b/frontend/src/state-providers/fonts.ts @@ -8,25 +8,33 @@ export function createFontsState(editor: Editor) { // TODO: Do some code cleanup to remove the need for this empty store const { subscribe } = writable({}); - function createURL(font: string): URL { + function createURL(font: string, weight: string): URL { const url = new URL("https://fonts.googleapis.com/css2"); url.searchParams.set("display", "swap"); - url.searchParams.set("family", font); + url.searchParams.set("family", `${font}:wght@${weight}`); url.searchParams.set("text", font); + return url; } async function fontNames(): Promise<{ name: string; url: URL | undefined }[]> { - return (await fontList).map((font) => ({ name: font.family, url: createURL(font.family) })); + const pickPreviewWeight = (variants: string[]) => { + const weights = variants.map((variant) => Number(variant.match(/.* \((\d+)\)/)?.[1] || "NaN")); + const weightGoal = 400; + const sorted = weights.map((weight) => [weight, Math.abs(weightGoal - weight - 1)]); + sorted.sort(([_, a], [__, b]) => a - b); + return sorted[0][0].toString(); + }; + return (await loadFontList()).map((font) => ({ name: font.family, url: createURL(font.family, pickPreviewWeight(font.variants)) })); } async function getFontStyles(fontFamily: string): Promise<{ name: string; url: URL | undefined }[]> { - const font = (await fontList).find((value) => value.family === fontFamily); + const font = (await loadFontList()).find((value) => value.family === fontFamily); return font?.variants.map((variant) => ({ name: variant, url: undefined })) || []; } async function getFontFileUrl(fontFamily: string, fontStyle: string): Promise { - const font = (await fontList).find((value) => value.family === fontFamily); + const font = (await loadFontList()).find((value) => value.family === fontFamily); const fontFileUrl = font?.files.get(fontStyle); return fontFileUrl?.replace("http://", "https://"); } @@ -47,33 +55,41 @@ export function createFontsState(editor: Editor) { return `${weightName}${isItalic ? " Italic" : ""} (${weight})`; } + let fontList: Promise<{ family: string; variants: string[]; files: Map }[]> | undefined; + + async function loadFontList(): Promise<{ family: string; variants: string[]; files: Map }[]> { + if (fontList) return fontList; + + fontList = new Promise<{ family: string; variants: string[]; files: Map }[]>((resolve) => { + fetch(fontListAPI) + .then((response) => response.json()) + .then((fontListResponse) => { + const fontListData = fontListResponse.items as { family: string; variants: string[]; files: Record }[]; + const result = fontListData.map((font) => { + const { family } = font; + const variants = font.variants.map(formatFontStyleName); + const files = new Map(font.variants.map((x) => [formatFontStyleName(x), font.files[x]])); + return { family, variants, files }; + }); + + resolve(result); + }); + }); + + return fontList; + } + // Subscribe to process backend events editor.subscriptions.subscribeJsMessage(TriggerFontLoad, async (triggerFontLoad) => { const url = await getFontFileUrl(triggerFontLoad.font.fontFamily, triggerFontLoad.font.fontStyle); if (url) { const response = await (await fetch(url)).arrayBuffer(); - editor.handle.onFontLoad(triggerFontLoad.font.fontFamily, triggerFontLoad.font.fontStyle, url, new Uint8Array(response), triggerFontLoad.isDefault); + editor.handle.onFontLoad(triggerFontLoad.font.fontFamily, triggerFontLoad.font.fontStyle, url, new Uint8Array(response)); } else { editor.handle.errorDialog("Failed to load font", `The font ${triggerFontLoad.font.fontFamily} with style ${triggerFontLoad.font.fontStyle} does not exist`); } }); - const fontList = new Promise<{ family: string; variants: string[]; files: Map }[]>((resolve) => { - fetch(fontListAPI) - .then((response) => response.json()) - .then((fontListResponse) => { - const fontListData = fontListResponse.items as { family: string; variants: string[]; files: Record }[]; - const result = fontListData.map((font) => { - const { family } = font; - const variants = font.variants.map(formatFontStyleName); - const files = new Map(font.variants.map((x) => [formatFontStyleName(x), font.files[x]])); - return { family, variants, files }; - }); - - resolve(result); - }); - }); - return { subscribe, fontNames, diff --git a/frontend/src/wasm-communication/messages.ts b/frontend/src/wasm-communication/messages.ts index 60ecb2e0..3b94dabb 100644 --- a/frontend/src/wasm-communication/messages.ts +++ b/frontend/src/wasm-communication/messages.ts @@ -892,8 +892,6 @@ export class Font { export class TriggerFontLoad extends JsMessage { @Type(() => Font) font!: Font; - - isDefault!: boolean; } export class TriggerVisitLink extends JsMessage { diff --git a/frontend/wasm/src/editor_api.rs b/frontend/wasm/src/editor_api.rs index f85ad59f..2742d22f 100644 --- a/frontend/wasm/src/editor_api.rs +++ b/frontend/wasm/src/editor_api.rs @@ -442,13 +442,12 @@ impl EditorHandle { /// A font has been downloaded #[wasm_bindgen(js_name = onFontLoad)] - pub fn on_font_load(&self, font_family: String, font_style: String, preview_url: String, data: Vec, is_default: bool) -> Result<(), JsValue> { + pub fn on_font_load(&self, font_family: String, font_style: String, preview_url: String, data: Vec) -> Result<(), JsValue> { let message = PortfolioMessage::FontLoaded { font_family, font_style, preview_url, data, - is_default, }; self.dispatch(message); diff --git a/node-graph/gcore/src/text/font_cache.rs b/node-graph/gcore/src/text/font_cache.rs index e097984e..40d8f260 100644 --- a/node-graph/gcore/src/text/font_cache.rs +++ b/node-graph/gcore/src/text/font_cache.rs @@ -27,16 +27,16 @@ pub struct FontCache { font_file_data: HashMap>, /// Web font preview URLs used for showing fonts when live editing preview_urls: HashMap, - /// The default font (used as a fallback) - default_font: Option, } impl FontCache { - /// Returns the font family name if the font is cached, otherwise returns the default font family name if that is cached + /// Returns the font family name if the font is cached, otherwise returns the fallback font family name if that is cached pub fn resolve_font<'a>(&'a self, font: &'a Font) -> Option<&'a Font> { - if self.loaded_font(font) { + if self.font_file_data.contains_key(font) { Some(font) } else { - self.default_font.as_ref().filter(|font| self.loaded_font(font)) + self.font_file_data + .keys() + .find(|font| font.font_family == crate::consts::DEFAULT_FONT_FAMILY && font.font_style == crate::consts::DEFAULT_FONT_STYLE) } } @@ -51,19 +51,11 @@ impl FontCache { } /// Insert a new font into the cache - pub fn insert(&mut self, font: Font, perview_url: String, data: Vec, is_default: bool) { - if is_default { - self.default_font = Some(font.clone()); - } + pub fn insert(&mut self, font: Font, perview_url: String, data: Vec) { self.font_file_data.insert(font.clone(), data); self.preview_urls.insert(font, perview_url); } - /// Checks if the font cache has a default font - pub fn has_default(&self) -> bool { - self.default_font.is_some() - } - /// Gets the preview URL for showing in text field when live editing pub fn get_preview_url(&self, font: &Font) -> Option<&String> { self.preview_urls.get(font)