Skip to content

Commit

Permalink
Add Brush tool warning; move font list loading to document creation time
Browse files Browse the repository at this point in the history
  • Loading branch information
Keavon committed Nov 9, 2024
1 parent 4576197 commit de366f9
Show file tree
Hide file tree
Showing 13 changed files with 99 additions and 82 deletions.
6 changes: 0 additions & 6 deletions editor/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Vec<VecDeque<Message>>>,
Expand Down Expand Up @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
]))
}

Expand Down
2 changes: 0 additions & 2 deletions editor/src/messages/frontend/frontend_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ pub enum FrontendMessage {
},
TriggerFontLoad {
font: Font,
#[serde(rename = "isDefault")]
is_default: bool,
},
TriggerImport,
TriggerIndexedDbRemoveDocument {
Expand Down
1 change: 0 additions & 1 deletion editor/src/messages/layout/layout_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
}
}

Expand Down
2 changes: 0 additions & 2 deletions editor/src/messages/portfolio/portfolio_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ pub enum PortfolioMessage {
font_style: String,
preview_url: String,
data: Vec<u8>,
is_default: bool,
},
ImaginateCheckServerStatus,
ImaginatePollServerStatus,
Expand All @@ -63,7 +62,6 @@ pub enum PortfolioMessage {
},
LoadFont {
font: Font,
is_default: bool,
},
NewDocumentWithName {
name: String,
Expand Down
11 changes: 7 additions & 4 deletions editor/src/messages/portfolio/portfolio_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,11 +278,10 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> 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(
Expand Down Expand Up @@ -334,9 +333,9 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> 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 } => {
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
if let Ok(deserialized_preferences) = serde_json::from_str::<PreferencesMessageHandler>(&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 {
Expand Down
18 changes: 18 additions & 0 deletions editor/src/messages/tool/tool_messages/brush_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub struct BrushTool {
}

pub struct BrushOptions {
legacy_warning_was_shown: bool,
diameter: f64,
hardness: f64,
flow: f64,
Expand All @@ -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.,
Expand Down Expand Up @@ -78,6 +80,7 @@ pub enum BrushToolMessageOptionsUpdate {
Hardness(f64),
Spacing(f64),
WorkingColors(Option<Color>, Option<Color>),
NoDisplayLegacyWarning,
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
Expand Down Expand Up @@ -217,6 +220,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> 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);
Expand Down Expand Up @@ -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;
};
Expand Down
60 changes: 38 additions & 22 deletions frontend/src/state-providers/fonts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string | undefined> {
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://");
}
Expand All @@ -47,33 +55,41 @@ export function createFontsState(editor: Editor) {
return `${weightName}${isItalic ? " Italic" : ""} (${weight})`;
}

let fontList: Promise<{ family: string; variants: string[]; files: Map<string, string> }[]> | undefined;

async function loadFontList(): Promise<{ family: string; variants: string[]; files: Map<string, string> }[]> {
if (fontList) return fontList;

fontList = new Promise<{ family: string; variants: string[]; files: Map<string, string> }[]>((resolve) => {
fetch(fontListAPI)
.then((response) => response.json())
.then((fontListResponse) => {
const fontListData = fontListResponse.items as { family: string; variants: string[]; files: Record<string, string> }[];
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<string, string> }[]>((resolve) => {
fetch(fontListAPI)
.then((response) => response.json())
.then((fontListResponse) => {
const fontListData = fontListResponse.items as { family: string; variants: string[]; files: Record<string, string> }[];
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,
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/wasm-communication/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -892,8 +892,6 @@ export class Font {
export class TriggerFontLoad extends JsMessage {
@Type(() => Font)
font!: Font;

isDefault!: boolean;
}

export class TriggerVisitLink extends JsMessage {
Expand Down
3 changes: 1 addition & 2 deletions frontend/wasm/src/editor_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u8>, is_default: bool) -> Result<(), JsValue> {
pub fn on_font_load(&self, font_family: String, font_style: String, preview_url: String, data: Vec<u8>) -> Result<(), JsValue> {
let message = PortfolioMessage::FontLoaded {
font_family,
font_style,
preview_url,
data,
is_default,
};
self.dispatch(message);

Expand Down
20 changes: 6 additions & 14 deletions node-graph/gcore/src/text/font_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ pub struct FontCache {
font_file_data: HashMap<Font, Vec<u8>>,
/// Web font preview URLs used for showing fonts when live editing
preview_urls: HashMap<Font, String>,
/// The default font (used as a fallback)
default_font: Option<Font>,
}
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)
}
}

Expand All @@ -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<u8>, is_default: bool) {
if is_default {
self.default_font = Some(font.clone());
}
pub fn insert(&mut self, font: Font, perview_url: String, data: Vec<u8>) {
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)
Expand Down

0 comments on commit de366f9

Please sign in to comment.