From fc2d983bd770884abfa36a421be77105d7008b6c Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Sat, 21 May 2022 19:46:15 -0700 Subject: [PATCH] Vue initialization and FloatingMenu codebase refactoring and cleanup (#649) * Clean up Vue initialization-related code * Rename folder: dispatcher -> interop * Rename folder: state -> providers * Comments and clarification * Rename JS dispatcher to subscription router * Assorted cleanup and renaming * Rename: js-messages.ts -> messages.ts * Comments * Remove unused Vue component injects * Clean up coming soon and add warning about freezing the app * Further cleanup * Dangerous changes * Simplify App.vue code * Move more disparate init code from components into managers * Rename folder: providers -> state-providers * Other * Move Document panel options bar separator to backend * Add destructors to managers to fix HMR * Comments and code style * Rename variable: font -> font_file_url * Fix async font loading; refactor janky floating menu openness and min-width measurement; fix Vetur errors * Fix misaligned canvas in viewport until panning on page (re)load * Add Vue bidirectional props documentation * More folder renaming for better terminology; add some documentation --- editor/src/document/document_message.rs | 4 +- .../src/document/document_message_handler.rs | 65 +-- .../src/document/portfolio_message_handler.rs | 1 + .../properties_panel_message_handler.rs | 8 +- editor/src/frontend/frontend_message.rs | 2 +- editor/src/layout/layout_message_handler.rs | 8 +- editor/src/layout/widgets.rs | 5 +- editor/src/viewport_tools/tools/text_tool.rs | 4 +- frontend/src/App.vue | 112 +++-- frontend/src/README.md | 53 ++ frontend/src/components/README.md | 33 ++ frontend/src/components/panels/Document.vue | 93 +--- frontend/src/components/panels/LayerTree.vue | 8 +- frontend/src/components/panels/NodeGraph.vue | 18 +- frontend/src/components/panels/Properties.vue | 7 +- .../src/components/widgets/WidgetLayout.vue | 2 +- frontend/src/components/widgets/WidgetRow.vue | 12 +- .../src/components/widgets/WidgetSection.vue | 2 +- .../components/widgets/buttons/IconButton.vue | 2 +- .../widgets/buttons/PopoverButton.vue | 17 +- .../components/widgets/buttons/TextButton.ts | 16 + .../widgets/floating-menus/ColorPicker.vue | 6 +- .../widgets/floating-menus/DialogModal.vue | 2 +- .../widgets/floating-menus/FloatingMenu.vue | 295 ++++++----- .../widgets/floating-menus/MenuList.vue | 146 +++--- .../widgets/inputs/CheckboxInput.vue | 2 +- .../components/widgets/inputs/ColorInput.vue | 26 +- .../widgets/inputs/DropdownInput.vue | 49 +- .../components/widgets/inputs/FontInput.vue | 55 +- .../widgets/inputs/MenuBarInput.vue | 36 +- .../components/widgets/inputs/NumberInput.vue | 5 +- .../widgets/inputs/OptionalInput.vue | 2 +- .../components/widgets/inputs/RadioInput.vue | 2 +- .../widgets/inputs/SwatchPairInput.vue | 41 +- .../components/widgets/labels/IconLabel.vue | 2 +- .../widgets/labels/UserInputLabel.vue | 5 +- .../components/widgets/rulers/CanvasRuler.vue | 2 +- .../scrollbars/PersistentScrollbar.vue | 4 + .../widgets/separators/Separator.vue | 3 +- .../window/status-bar/StatusBar.vue | 4 +- .../components/window/title-bar/TitleBar.vue | 9 +- frontend/src/components/workspace/Panel.vue | 1 - frontend/src/io-managers/build-metadata.ts | 26 + frontend/src/io-managers/clipboard.ts | 10 + frontend/src/io-managers/hyperlinks.ts | 9 + .../src/{lifetime => io-managers}/input.ts | 476 ++---------------- .../errors.ts => io-managers/panic.ts} | 22 +- frontend/src/io-managers/persistence.ts | 91 ++++ frontend/src/lifetime/auto-save.ts | 90 ---- frontend/src/main.ts | 5 +- frontend/src/state-providers/dialog.ts | 53 ++ frontend/src/state-providers/fonts.ts | 95 ++++ .../{state => state-providers}/fullscreen.ts | 31 +- .../{state => state-providers}/portfolio.ts | 40 +- .../{state => state-providers}/workspace.ts | 10 +- frontend/src/state/dialog.ts | 58 --- frontend/src/state/wasm-loader.ts | 89 ---- frontend/src/utilities/fonts.ts | 80 --- frontend/src/utilities/widgets.ts | 92 ---- .../{utilities => utility-functions}/color.ts | 2 +- .../{utilities => utility-functions}/files.ts | 2 + .../{utilities => utility-functions}/icons.ts | 2 +- .../src/utility-functions/keyboard-entry.ts | 367 ++++++++++++++ .../{utilities => utility-functions}/math.ts | 0 frontend/src/utility-functions/panic-proxy.ts | 40 ++ .../strip-indents.ts | 0 frontend/src/volar.d.ts | 23 + frontend/src/wasm-communication/editor.ts | 50 ++ .../messages.ts} | 24 +- .../subscription-router.ts} | 12 +- frontend/wasm/src/api.rs | 58 ++- frontend/wasm/src/lib.rs | 2 + graphene/src/document.rs | 6 +- 73 files changed, 1572 insertions(+), 1462 deletions(-) create mode 100644 frontend/src/README.md create mode 100644 frontend/src/components/README.md create mode 100644 frontend/src/components/widgets/buttons/TextButton.ts create mode 100644 frontend/src/io-managers/build-metadata.ts create mode 100644 frontend/src/io-managers/clipboard.ts create mode 100644 frontend/src/io-managers/hyperlinks.ts rename frontend/src/{lifetime => io-managers}/input.ts (51%) rename frontend/src/{lifetime/errors.ts => io-managers/panic.ts} (82%) create mode 100644 frontend/src/io-managers/persistence.ts delete mode 100644 frontend/src/lifetime/auto-save.ts create mode 100644 frontend/src/state-providers/dialog.ts create mode 100644 frontend/src/state-providers/fonts.ts rename frontend/src/{state => state-providers}/fullscreen.ts (80%) rename frontend/src/{state => state-providers}/portfolio.ts (63%) rename frontend/src/{state => state-providers}/workspace.ts (56%) delete mode 100644 frontend/src/state/dialog.ts delete mode 100644 frontend/src/state/wasm-loader.ts delete mode 100644 frontend/src/utilities/fonts.ts delete mode 100644 frontend/src/utilities/widgets.ts rename frontend/src/{utilities => utility-functions}/color.ts (95%) rename frontend/src/{utilities => utility-functions}/files.ts (88%) rename frontend/src/{utilities => utility-functions}/icons.ts (99%) create mode 100644 frontend/src/utility-functions/keyboard-entry.ts rename frontend/src/{utilities => utility-functions}/math.ts (100%) create mode 100644 frontend/src/utility-functions/panic-proxy.ts rename frontend/src/{utilities => utility-functions}/strip-indents.ts (100%) create mode 100644 frontend/src/volar.d.ts create mode 100644 frontend/src/wasm-communication/editor.ts rename frontend/src/{dispatcher/js-messages.ts => wasm-communication/messages.ts} (95%) rename frontend/src/{dispatcher/js-dispatcher.ts => wasm-communication/subscription-router.ts} (89%) diff --git a/editor/src/document/document_message.rs b/editor/src/document/document_message.rs index 6254237a7..ec072599d 100644 --- a/editor/src/document/document_message.rs +++ b/editor/src/document/document_message.rs @@ -73,7 +73,7 @@ pub enum DocumentMessage { affected_folder_path: Vec, }, FontLoaded { - font: String, + font_file_url: String, data: Vec, is_default: bool, }, @@ -82,7 +82,7 @@ pub enum DocumentMessage { affected_layer_path: Vec, }, LoadFont { - font: String, + font_file_url: String, }, MoveSelectedLayersTo { folder_path: Vec, diff --git a/editor/src/document/document_message_handler.rs b/editor/src/document/document_message_handler.rs index 3501a9ffd..18e26ebb1 100644 --- a/editor/src/document/document_message_handler.rs +++ b/editor/src/document/document_message_handler.rs @@ -523,6 +523,7 @@ impl DocumentMessageHandler { } } + // TODO: Loading the default font should happen on a per-application basis, not a per-document basis pub fn load_default_font(&self, responses: &mut VecDeque) { if !self.graphene_document.font_cache.has_default() { responses.push_back(FrontendMessage::TriggerFontLoadDefault.into()) @@ -669,30 +670,36 @@ impl DocumentMessageHandler { }]); let document_mode_layout = WidgetLayout::new(vec![LayoutRow::Row { - widgets: vec![WidgetHolder::new(Widget::DropdownInput(DropdownInput { - entries: vec![vec![ - DropdownEntryData { - label: DocumentMode::DesignMode.to_string(), - icon: DocumentMode::DesignMode.icon_name(), - ..DropdownEntryData::default() - }, - DropdownEntryData { - label: DocumentMode::SelectMode.to_string(), - icon: DocumentMode::SelectMode.icon_name(), - on_update: WidgetCallback::new(|_| DialogMessage::RequestComingSoonDialog { issue: Some(330) }.into()), - ..DropdownEntryData::default() - }, - DropdownEntryData { - label: DocumentMode::GuideMode.to_string(), - icon: DocumentMode::GuideMode.icon_name(), - on_update: WidgetCallback::new(|_| DialogMessage::RequestComingSoonDialog { issue: Some(331) }.into()), - ..DropdownEntryData::default() - }, - ]], - selected_index: Some(self.document_mode as u32), - draw_icon: true, - ..Default::default() - }))], + widgets: vec![ + WidgetHolder::new(Widget::DropdownInput(DropdownInput { + entries: vec![vec![ + DropdownEntryData { + label: DocumentMode::DesignMode.to_string(), + icon: DocumentMode::DesignMode.icon_name(), + ..DropdownEntryData::default() + }, + DropdownEntryData { + label: DocumentMode::SelectMode.to_string(), + icon: DocumentMode::SelectMode.icon_name(), + on_update: WidgetCallback::new(|_| DialogMessage::RequestComingSoonDialog { issue: Some(330) }.into()), + ..DropdownEntryData::default() + }, + DropdownEntryData { + label: DocumentMode::GuideMode.to_string(), + icon: DocumentMode::GuideMode.icon_name(), + on_update: WidgetCallback::new(|_| DialogMessage::RequestComingSoonDialog { issue: Some(331) }.into()), + ..DropdownEntryData::default() + }, + ]], + selected_index: Some(self.document_mode as u32), + draw_icon: true, + ..Default::default() + })), + WidgetHolder::new(Widget::Separator(Separator { + separator_type: SeparatorType::Section, + direction: SeparatorDirection::Horizontal, + })), + ], }]); responses.push_back( @@ -1107,8 +1114,8 @@ impl MessageHandler for Docum let affected_layer_path = affected_folder_path; responses.extend([LayerChanged { affected_layer_path }.into(), DocumentStructureChanged.into()]); } - FontLoaded { font, data, is_default } => { - self.graphene_document.font_cache.insert(font, data, is_default); + FontLoaded { font_file_url, data, is_default } => { + self.graphene_document.font_cache.insert(font_file_url, data, is_default); responses.push_back(DocumentMessage::DirtyRenderDocument.into()); } GroupSelectedLayers => { @@ -1147,9 +1154,9 @@ impl MessageHandler for Docum responses.push_back(PropertiesPanelMessage::CheckSelectedWasUpdated { path: affected_layer_path }.into()); self.update_layer_tree_options_bar_widgets(responses); } - LoadFont { font } => { - if !self.graphene_document.font_cache.loaded_font(&font) { - responses.push_front(FrontendMessage::TriggerFontLoad { font }.into()); + LoadFont { font_file_url } => { + if !self.graphene_document.font_cache.loaded_font(&font_file_url) { + responses.push_front(FrontendMessage::TriggerFontLoad { font_file_url }.into()); } } MoveSelectedLayersTo { diff --git a/editor/src/document/portfolio_message_handler.rs b/editor/src/document/portfolio_message_handler.rs index 3b42db133..f9dcefb3b 100644 --- a/editor/src/document/portfolio_message_handler.rs +++ b/editor/src/document/portfolio_message_handler.rs @@ -79,6 +79,7 @@ impl PortfolioMessageHandler { new_document.update_layer_tree_options_bar_widgets(responses); new_document.load_image_data(responses, &new_document.graphene_document.root.data, Vec::new()); + // TODO: Loading the default font should happen on a per-application basis, not a per-document basis new_document.load_default_font(responses); self.documents.insert(document_id, new_document); diff --git a/editor/src/document/properties_panel_message_handler.rs b/editor/src/document/properties_panel_message_handler.rs index aaa84b7fc..46858c95e 100644 --- a/editor/src/document/properties_panel_message_handler.rs +++ b/editor/src/document/properties_panel_message_handler.rs @@ -714,12 +714,12 @@ fn node_section_font(layer: &TextLayer) -> LayoutRow { is_style_picker: false, font_family: layer.font_family.clone(), font_style: layer.font_style.clone(), - font_file: String::new(), + font_file_url: String::new(), on_update: WidgetCallback::new(move |font_input: &FontInput| { PropertiesPanelMessage::ModifyFont { font_family: font_input.font_family.clone(), font_style: font_input.font_style.clone(), - font_file: Some(font_input.font_file.clone()), + font_file: Some(font_input.font_file_url.clone()), size, } .into() @@ -741,12 +741,12 @@ fn node_section_font(layer: &TextLayer) -> LayoutRow { is_style_picker: true, font_family: layer.font_family.clone(), font_style: layer.font_style.clone(), - font_file: String::new(), + font_file_url: String::new(), on_update: WidgetCallback::new(move |font_input: &FontInput| { PropertiesPanelMessage::ModifyFont { font_family: font_input.font_family.clone(), font_style: font_input.font_style.clone(), - font_file: Some(font_input.font_file.clone()), + font_file: Some(font_input.font_file_url.clone()), size, } .into() diff --git a/editor/src/frontend/frontend_message.rs b/editor/src/frontend/frontend_message.rs index 8fea2ff73..396304679 100644 --- a/editor/src/frontend/frontend_message.rs +++ b/editor/src/frontend/frontend_message.rs @@ -22,7 +22,7 @@ pub enum FrontendMessage { // Trigger prefix: cause a browser API to do something TriggerFileDownload { document: String, name: String }, TriggerFileUpload, - TriggerFontLoad { font: String }, + TriggerFontLoad { font_file_url: String }, TriggerFontLoadDefault, TriggerIndexedDbRemoveDocument { document_id: u64 }, TriggerIndexedDbWriteDocument { document: String, details: FrontendDocumentDetails, version: String }, diff --git a/editor/src/layout/layout_message_handler.rs b/editor/src/layout/layout_message_handler.rs index 9d46bb58f..505858d50 100644 --- a/editor/src/layout/layout_message_handler.rs +++ b/editor/src/layout/layout_message_handler.rs @@ -95,17 +95,17 @@ impl MessageHandler for LayoutMessageHandler { let update_value = value.as_object().expect("FontInput update was not of type: object"); let font_family_value = update_value.get("fontFamily").expect("FontInput update does not have a fontFamily"); let font_style_value = update_value.get("fontStyle").expect("FontInput update does not have a fontStyle"); - let font_file_value = update_value.get("fontFile").expect("FontInput update does not have a fontFile"); + let font_file_url_value = update_value.get("fontFileUrl").expect("FontInput update does not have a fontFileUrl"); let font_family = font_family_value.as_str().expect("FontInput update fontFamily was not of type: string"); let font_style = font_style_value.as_str().expect("FontInput update fontStyle was not of type: string"); - let font_file = font_file_value.as_str().expect("FontInput update fontFile was not of type: string"); + let font_file_url = font_file_url_value.as_str().expect("FontInput update fontFileUrl was not of type: string"); font_input.font_family = font_family.into(); font_input.font_style = font_style.into(); - font_input.font_file = font_file.into(); + font_input.font_file_url = font_file_url.into(); - responses.push_back(DocumentMessage::LoadFont { font: font_file.into() }.into()); + responses.push_back(DocumentMessage::LoadFont { font_file_url: font_file_url.into() }.into()); let callback_message = (font_input.on_update.callback)(font_input); responses.push_back(callback_message); } diff --git a/editor/src/layout/widgets.rs b/editor/src/layout/widgets.rs index e83fe2a23..9a66bf7fa 100644 --- a/editor/src/layout/widgets.rs +++ b/editor/src/layout/widgets.rs @@ -49,6 +49,7 @@ impl WidgetLayout { pub type SubLayout = Vec; +// TODO: Rename LayoutRow to something more generic #[remain::sorted] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum LayoutRow { @@ -254,8 +255,8 @@ pub struct FontInput { pub font_family: String, #[serde(rename = "fontStyle")] pub font_style: String, - #[serde(rename = "fontFile")] - pub font_file: String, + #[serde(rename = "fontFileUrl")] + pub font_file_url: String, #[serde(skip)] #[derivative(Debug = "ignore", PartialEq = "ignore")] pub on_update: WidgetCallback, diff --git a/editor/src/viewport_tools/tools/text_tool.rs b/editor/src/viewport_tools/tools/text_tool.rs index db2507622..df7f88f73 100644 --- a/editor/src/viewport_tools/tools/text_tool.rs +++ b/editor/src/viewport_tools/tools/text_tool.rs @@ -84,7 +84,7 @@ impl PropertyHolder for TextTool { TextMessage::UpdateOptions(TextOptionsUpdate::Font { family: font_input.font_family.clone(), style: font_input.font_style.clone(), - file: font_input.font_file.clone(), + file: font_input.font_file_url.clone(), }) .into() }), @@ -102,7 +102,7 @@ impl PropertyHolder for TextTool { TextMessage::UpdateOptions(TextOptionsUpdate::Font { family: font_input.font_family.clone(), style: font_input.font_style.clone(), - file: font_input.font_file.clone(), + file: font_input.font_file_url.clone(), }) .into() }), diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 676e32de6..f92cd141e 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,7 +1,7 @@