diff --git a/editor/src/messages/broadcast/broadcast_message_handler.rs b/editor/src/messages/broadcast/broadcast_message_handler.rs index 3bbc8b3e..51d64b58 100644 --- a/editor/src/messages/broadcast/broadcast_message_handler.rs +++ b/editor/src/messages/broadcast/broadcast_message_handler.rs @@ -22,6 +22,6 @@ impl MessageHandler for BroadcastMessageHandler { } fn actions(&self) -> ActionList { - vec![] + actions!(BroadcastEventDiscriminant;) } } diff --git a/editor/src/messages/input_mapper/default_mapping.rs b/editor/src/messages/input_mapper/input_mappings.rs similarity index 98% rename from editor/src/messages/input_mapper/default_mapping.rs rename to editor/src/messages/input_mapper/input_mappings.rs index 4a45b17d..1a07e307 100644 --- a/editor/src/messages/input_mapper/default_mapping.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -15,13 +15,13 @@ use glam::DVec2; impl From for Mapping { fn from(value: MappingVariant) -> Self { match value { - MappingVariant::Default => default_mapping(), + MappingVariant::Default => input_mappings(), MappingVariant::ZoomWithScroll => zoom_with_scroll(), } } } -pub fn default_mapping() -> Mapping { +pub fn input_mappings() -> Mapping { use InputMapperMessage::*; use Key::*; @@ -61,7 +61,7 @@ pub fn default_mapping() -> Mapping { entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=NodeGraphMessage::DuplicateSelectedNodes), entry!(KeyDown(KeyH); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleSelectedVisibility), entry!(KeyDown(KeyL); modifiers=[Accel], action_dispatch=NodeGraphMessage::ToggleSelectedLocked), - entry!(KeyDown(KeyL); modifiers=[Alt], action_dispatch=NodeGraphMessage::ToggleSelectedLayers), + entry!(KeyDown(KeyL); modifiers=[Alt], action_dispatch=NodeGraphMessage::ToggleSelectedAsLayersOrNodes), // // TransformLayerMessage entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation), @@ -292,6 +292,9 @@ pub fn default_mapping() -> Mapping { entry!(KeyDown(Delete); action_dispatch=DocumentMessage::DeleteSelectedLayers), entry!(KeyDown(Backspace); action_dispatch=DocumentMessage::DeleteSelectedLayers), entry!(KeyDown(KeyP); modifiers=[Alt], action_dispatch=DocumentMessage::DebugPrintDocument), + entry!(KeyDown(KeyO); modifiers=[Alt], action_dispatch=DocumentMessage::ToggleOverlaysVisibility), + entry!(KeyDown(KeyS); modifiers=[Alt], action_dispatch=DocumentMessage::ToggleSnapping), + entry!(KeyDown(KeyG); modifiers=[Alt], action_dispatch=DocumentMessage::ToggleGridVisibility), entry!(KeyDown(KeyZ); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::Redo), entry!(KeyDown(KeyY); modifiers=[Accel], action_dispatch=DocumentMessage::Redo), entry!(KeyDown(KeyZ); modifiers=[Accel], action_dispatch=DocumentMessage::Undo), @@ -422,7 +425,7 @@ pub fn default_mapping() -> Mapping { pub fn zoom_with_scroll() -> Mapping { use InputMapperMessage::*; - let mut mapping = default_mapping(); + let mut mapping = input_mappings(); let remove = [ entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::CanvasZoomMouseWheel), diff --git a/editor/src/messages/input_mapper/mod.rs b/editor/src/messages/input_mapper/mod.rs index 2bde0286..14aff3e3 100644 --- a/editor/src/messages/input_mapper/mod.rs +++ b/editor/src/messages/input_mapper/mod.rs @@ -1,7 +1,7 @@ mod input_mapper_message; mod input_mapper_message_handler; -pub mod default_mapping; +pub mod input_mappings; pub mod key_mapping; pub mod utility_types; diff --git a/editor/src/messages/layout/layout_message_handler.rs b/editor/src/messages/layout/layout_message_handler.rs index fa72d319..8664e75d 100644 --- a/editor/src/messages/layout/layout_message_handler.rs +++ b/editor/src/messages/layout/layout_message_handler.rs @@ -296,7 +296,7 @@ impl Vec> MessageHandler ActionList { - actions!() + actions!(LayoutMessageDiscriminant;) } } diff --git a/editor/src/messages/portfolio/document/document_message.rs b/editor/src/messages/portfolio/document/document_message.rs index 26e56c20..65ccc850 100644 --- a/editor/src/messages/portfolio/document/document_message.rs +++ b/editor/src/messages/portfolio/document/document_message.rs @@ -62,7 +62,7 @@ pub enum DocumentMessage { GraphViewOverlayToggle, GridOptions(GridSnapping), GridOverlays(OverlayContext), - GridVisible(bool), + GridVisibility(bool), GroupSelectedLayers, ImaginateGenerate, ImaginateRandom { @@ -138,6 +138,9 @@ pub enum DocumentMessage { ToggleLayerExpansion { id: NodeId, }, + ToggleGridVisibility, + ToggleOverlaysVisibility, + ToggleSnapping, Undo, UndoFinished, UngroupSelectedLayers, diff --git a/editor/src/messages/portfolio/document/document_message_handler.rs b/editor/src/messages/portfolio/document/document_message_handler.rs index 1d4a6b63..7fb02531 100644 --- a/editor/src/messages/portfolio/document/document_message_handler.rs +++ b/editor/src/messages/portfolio/document/document_message_handler.rs @@ -46,6 +46,7 @@ pub struct DocumentMessageHandler { // ====================== // Child message handlers // ====================== + // #[serde(skip)] navigation_handler: NavigationMessageHandler, #[serde(skip)] @@ -54,49 +55,78 @@ pub struct DocumentMessageHandler { overlays_message_handler: OverlaysMessageHandler, #[serde(skip)] properties_panel_message_handler: PropertiesPanelMessageHandler, + // ============================================ // Fields that are saved in the document format // ============================================ + // + /// The node graph that generates this document's artwork. + /// It recursively stores its sub-graphs, so this root graph is the whole snapshot of the document content. #[serde(default = "default_network")] pub network: NodeNetwork, + /// List of the [`NodeId`]s that are currently selected by the user. #[serde(default = "default_selected_nodes")] pub selected_nodes: SelectedNodes, + /// List of the [`LayerNodeIdentifier`]s that are currently collapsed by the user in the Layers panel. + /// Collapsed means that the expansion arrow isn't set to show the children of these layers. #[serde(default = "default_collapsed")] pub collapsed: CollapsedLayers, + /// The name of the document, which is displayed in the tab and title bar of the editor. #[serde(default = "default_name")] pub name: String, + /// The full Git commit hash of the Graphite repository that was used to build the editor. + /// We save this to provide a hint about which version of the editor was used to create the document. #[serde(default = "default_commit_hash")] commit_hash: String, + /// The current pan, tilt, and zoom state of the viewport's view of the document canvas. #[serde(default = "default_pan_tilt_zoom")] pub navigation: PTZ, + /// The current mode that the document is in, which starts out as Design Mode. This choice affects the editing behavior of the tools. #[serde(default = "default_document_mode")] document_mode: DocumentMode, + /// The current view mode that the user has set for rendering the document within the viewport. + /// This is usually "Normal" but can be set to "Outline" or "Pixels" to see the canvas differently. #[serde(default = "default_view_mode")] pub view_mode: ViewMode, + /// Sets whether or not all the viewport overlays should be drawn on top of the artwork. + /// This includes tool interaction visualizations (like the transform cage and path anchors/handles), the grid, and more. #[serde(default = "default_overlays_visible")] overlays_visible: bool, + /// Sets whether or not the rulers should be drawn along the top and left edges of the viewport area. #[serde(default = "default_rulers_visible")] pub rulers_visible: bool, + /// Sets whether or not the node graph is drawn (as an overlay) on top of the viewport area, or otherwise if it's hidden. + #[serde(default = "default_graph_view_overlay_open")] + graph_view_overlay_open: bool, + /// The current user choices for snapping behavior, including whether snapping is enabled at all. + #[serde(default = "default_snapping_state")] + pub snapping_state: SnappingState, + // ============================================= // Fields omitted from the saved document format // ============================================= + // + /// Stack of document network snapshots for previous history states. #[serde(skip)] document_undo_history: VecDeque, + /// Stack of document network snapshots for future history states. #[serde(skip)] document_redo_history: VecDeque, + /// Hash of the document snapshot that was most recently saved to disk by the user. #[serde(skip)] saved_hash: Option, + /// Hash of the document snapshot that was most recently auto-saved to the IndexedDB storage that will reopen when the editor is reloaded. #[serde(skip)] auto_saved_hash: Option, - /// Don't allow aborting transactions whilst undoing to avoid #559 + /// Disallow aborting transactions whilst undoing to avoid #559. #[serde(skip)] undo_in_progress: bool, - #[serde(skip)] - graph_view_overlay_open: bool, - #[serde(skip)] - pub snapping_state: SnappingState, + /// The ID of the layer at the start of a range selection in the Layers panel. + /// If the user clicks or Ctrl-clicks one layer, it becomes the start of the range selection and then Shift-clicking another layer selects all layers between the start and end. #[serde(skip)] layer_range_selection_reference: Option, + /// Stores stateful information about the document's network such as the graph's structural topology and which layers are hidden, locked, etc. + /// This is updated frequently, whenever the information it's derived from changes. #[serde(skip)] pub metadata: DocumentMetadata, } @@ -330,7 +360,7 @@ impl MessageHandler> for DocumentMessag grid_overlay(self, &mut overlay_context) } } - DocumentMessage::GridVisible(enabled) => { + DocumentMessage::GridVisibility(enabled) => { self.snapping_state.grid_snapping = enabled; responses.add(OverlaysMessage::Draw); } @@ -844,6 +874,20 @@ impl MessageHandler> for DocumentMessag } responses.add(NodeGraphMessage::RunDocumentGraph); } + DocumentMessage::ToggleGridVisibility => { + self.snapping_state.grid_snapping = !self.snapping_state.grid_snapping; + responses.add(OverlaysMessage::Draw); + responses.add(PortfolioMessage::UpdateDocumentWidgets); + } + DocumentMessage::ToggleOverlaysVisibility => { + self.overlays_visible = !self.overlays_visible; + responses.add(OverlaysMessage::Draw); + responses.add(PortfolioMessage::UpdateDocumentWidgets); + } + DocumentMessage::ToggleSnapping => { + self.snapping_state.snapping_enabled = !self.snapping_state.snapping_enabled; + responses.add(PortfolioMessage::UpdateDocumentWidgets); + } DocumentMessage::Undo => { self.undo_in_progress = true; responses.add(ToolMessage::PreUndo); @@ -967,7 +1011,52 @@ impl MessageHandler> for DocumentMessag } fn actions(&self) -> ActionList { - unimplemented!("Must use `actions_with_graph_open` instead (unless we change every implementation of the MessageHandler trait).") + let mut common = actions!(DocumentMessageDiscriminant; + CreateEmptyFolder, + DebugPrintDocument, + DeselectAllLayers, + GraphViewOverlayToggle, + Noop, + Redo, + SaveDocument, + SelectAllLayers, + SetSnapping, + ToggleGridVisibility, + ToggleOverlaysVisibility, + ToggleSnapping, + Undo, + ZoomCanvasTo100Percent, + ZoomCanvasTo200Percent, + ZoomCanvasToFitAll, + ); + + // Additional actions if there are any selected layers + if self.selected_nodes.selected_layers(self.metadata()).next().is_some() { + let select = actions!(DocumentMessageDiscriminant; + DeleteSelectedLayers, + DuplicateSelectedLayers, + GroupSelectedLayers, + NudgeSelectedLayers, + SelectedLayersLower, + SelectedLayersLowerToBack, + SelectedLayersRaise, + SelectedLayersRaiseToFront, + UngroupSelectedLayers, + ); + common.extend(select); + } + // Additional actions if the node graph is open + if self.graph_view_overlay_open { + common.extend(actions!(DocumentMessageDiscriminant; + GraphViewOverlay, + )); + common.extend(self.node_graph_handler.actions_additional_if_node_graph_is_open()); + } + // More additional actions + common.extend(self.node_graph_handler.actions()); + common.extend(self.navigation_handler.actions()); + + common } } @@ -1377,6 +1466,7 @@ impl DocumentMessageHandler { CheckboxInput::new(self.overlays_visible) .icon("Overlays") .tooltip("Overlays") + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleOverlaysVisibility)) .on_update(|optional_input: &CheckboxInput| DocumentMessage::SetOverlaysVisibility { visible: optional_input.checked }.into()) .widget_holder(), PopoverButton::new() @@ -1393,6 +1483,7 @@ impl DocumentMessageHandler { CheckboxInput::new(snapping_state.snapping_enabled) .icon("Snapping") .tooltip("Snapping") + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSnapping)) .on_update(move |optional_input: &CheckboxInput| { let snapping_enabled = optional_input.checked; DocumentMessage::SetSnapping { @@ -1508,7 +1599,8 @@ impl DocumentMessageHandler { CheckboxInput::new(self.snapping_state.grid_snapping) .icon("Grid") .tooltip("Grid") - .on_update(|optional_input: &CheckboxInput| DocumentMessage::GridVisible(optional_input.checked).into()) + .tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleGridVisibility)) + .on_update(|optional_input: &CheckboxInput| DocumentMessage::GridVisibility(optional_input.checked).into()) .widget_holder(), PopoverButton::new() .popover_layout(overlay_options(&self.snapping_state.grid)) @@ -1819,47 +1911,6 @@ impl DocumentMessageHandler { let insert_index = if relative_index_offset < 0 { neighbor_index } else { neighbor_index + 1 } as isize; responses.add(DocumentMessage::MoveSelectedLayersTo { parent, insert_index }); } - - pub fn actions_with_graph_open(&self) -> ActionList { - let mut common = actions!(DocumentMessageDiscriminant; - Noop, - Undo, - Redo, - SelectAllLayers, - DeselectAllLayers, - SaveDocument, - SetSnapping, - DebugPrintDocument, - ZoomCanvasToFitAll, - ZoomCanvasTo100Percent, - ZoomCanvasTo200Percent, - GraphViewOverlayToggle, - CreateEmptyFolder, - ); - - if self.graph_view_overlay_open { - let escape = actions!(DocumentMessageDiscriminant; GraphViewOverlay); - common.extend(escape); - } - - if self.selected_nodes.selected_layers(self.metadata()).next().is_some() { - let select = actions!(DocumentMessageDiscriminant; - DeleteSelectedLayers, - DuplicateSelectedLayers, - NudgeSelectedLayers, - SelectedLayersLower, - SelectedLayersLowerToBack, - SelectedLayersRaise, - SelectedLayersRaiseToFront, - GroupSelectedLayers, - UngroupSelectedLayers, - ); - common.extend(select); - } - common.extend(self.navigation_handler.actions()); - common.extend(self.node_graph_handler.actions_with_node_graph_open(self.graph_view_overlay_open)); - common - } } impl Default for DocumentMessageHandler { @@ -1941,6 +1992,14 @@ fn default_overlays_visible() -> bool { fn default_rulers_visible() -> bool { DocumentMessageHandler::default().rulers_visible } +#[inline(always)] +fn default_graph_view_overlay_open() -> bool { + DocumentMessageHandler::default().graph_view_overlay_open +} +#[inline(always)] +fn default_snapping_state() -> SnappingState { + DocumentMessageHandler::default().snapping_state +} fn root_network() -> NodeNetwork { { diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs index d19c7b75..3a5646d7 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message.rs @@ -144,7 +144,7 @@ pub enum NodeGraphMessage { TogglePreviewImpl { node_id: NodeId, }, - ToggleSelectedLayers, + ToggleSelectedAsLayersOrNodes, ToggleSelectedLocked, ToggleSelectedVisibility, ToggleVisibility { diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs index 3ada209d..9b5485b7 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler.rs @@ -702,7 +702,7 @@ impl<'a> MessageHandler> for NodeGrap document_metadata.load_structure(document_network, selected_nodes); self.update_selection_action_buttons(document_network, document_metadata, selected_nodes, responses); } - NodeGraphMessage::ToggleSelectedLayers => { + NodeGraphMessage::ToggleSelectedAsLayersOrNodes => { let Some(network) = document_network.nested_network_mut(&self.network) else { return }; for node_id in selected_nodes.selected_nodes() { @@ -788,16 +788,28 @@ impl<'a> MessageHandler> for NodeGrap } fn actions(&self) -> ActionList { - unimplemented!("Must use `actions_with_node_graph_open` instead (unless we change every implementation of the MessageHandler trait).") + if self.has_selection { + actions!(NodeGraphMessageDiscriminant; + ToggleSelectedLocked, + ToggleSelectedVisibility, + ) + } else { + actions!(NodeGraphMessageDiscriminant;) + } } } impl NodeGraphMessageHandler { - pub fn actions_with_node_graph_open(&self, graph_open: bool) -> ActionList { - if self.has_selection && graph_open { - actions!(NodeGraphMessageDiscriminant; ToggleSelectedVisibility, ToggleSelectedLocked, ToggleSelectedLayers, DuplicateSelectedNodes, DeleteSelectedNodes, Cut, Copy) - } else if self.has_selection { - actions!(NodeGraphMessageDiscriminant; ToggleSelectedVisibility, ToggleSelectedLocked) + /// Similar to [`NodeGraphMessageHandler::actions`], but this provides additional actions if the node graph is open and should only be called in that circumstance. + pub fn actions_additional_if_node_graph_is_open(&self) -> ActionList { + if self.has_selection { + actions!(NodeGraphMessageDiscriminant; + Copy, + Cut, + DeleteSelectedNodes, + DuplicateSelectedNodes, + ToggleSelectedAsLayersOrNodes, + ) } else { actions!(NodeGraphMessageDiscriminant;) } diff --git a/editor/src/messages/portfolio/document/utility_types/misc.rs b/editor/src/messages/portfolio/document/utility_types/misc.rs index ae0049e6..1faf4d3d 100644 --- a/editor/src/messages/portfolio/document/utility_types/misc.rs +++ b/editor/src/messages/portfolio/document/utility_types/misc.rs @@ -53,7 +53,7 @@ impl DocumentMode { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] /// SnappingState determines the current individual snapping states pub struct SnappingState { pub snapping_enabled: bool, diff --git a/editor/src/messages/portfolio/portfolio_message_handler.rs b/editor/src/messages/portfolio/portfolio_message_handler.rs index 4fa4837a..68ebaa57 100644 --- a/editor/src/messages/portfolio/portfolio_message_handler.rs +++ b/editor/src/messages/portfolio/portfolio_message_handler.rs @@ -536,16 +536,17 @@ impl MessageHandler> for PortfolioMes ToggleRulers, ); + // Extend with actions that require an active document if let Some(document) = self.active_document() { + common.extend(document.actions()); + + // Extend with actions that must have a selected layer if document.selected_nodes.selected_layers(document.metadata()).next().is_some() { - let select = actions!(PortfolioMessageDiscriminant; + common.extend(actions!(PortfolioMessageDiscriminant; Copy, Cut, - ); - common.extend(select); + )); } - - common.extend(document.actions_with_graph_open()); } common