Improve the node graph with revamped top bar and disabling tools when graph is open (#2093)
* Add "Fade Artwork" slider and disable tools when graph is open * Add navigation and layer/node management buttons to graph top bar * Reduce code duplication
This commit is contained in:
parent
12ca06035c
commit
f1b0d8fa87
|
|
@ -199,10 +199,11 @@ impl Dispatcher {
|
||||||
Message::Portfolio(message) => {
|
Message::Portfolio(message) => {
|
||||||
let ipp = &self.message_handlers.input_preprocessor_message_handler;
|
let ipp = &self.message_handlers.input_preprocessor_message_handler;
|
||||||
let preferences = &self.message_handlers.preferences_message_handler;
|
let preferences = &self.message_handlers.preferences_message_handler;
|
||||||
|
let current_tool = &self.message_handlers.tool_message_handler.tool_state.tool_data.active_tool_type;
|
||||||
|
|
||||||
self.message_handlers
|
self.message_handlers
|
||||||
.portfolio_message_handler
|
.portfolio_message_handler
|
||||||
.process_message(message, &mut queue, PortfolioMessageData { ipp, preferences });
|
.process_message(message, &mut queue, PortfolioMessageData { ipp, preferences, current_tool });
|
||||||
}
|
}
|
||||||
Message::Preferences(message) => {
|
Message::Preferences(message) => {
|
||||||
self.message_handlers.preferences_message_handler.process_message(message, &mut queue, ());
|
self.message_handlers.preferences_message_handler.process_message(message, &mut queue, ());
|
||||||
|
|
@ -244,8 +245,10 @@ impl Dispatcher {
|
||||||
list.extend(self.message_handlers.input_preprocessor_message_handler.actions());
|
list.extend(self.message_handlers.input_preprocessor_message_handler.actions());
|
||||||
list.extend(self.message_handlers.key_mapping_message_handler.actions());
|
list.extend(self.message_handlers.key_mapping_message_handler.actions());
|
||||||
list.extend(self.message_handlers.debug_message_handler.actions());
|
list.extend(self.message_handlers.debug_message_handler.actions());
|
||||||
if self.message_handlers.portfolio_message_handler.active_document().is_some() {
|
if let Some(document) = self.message_handlers.portfolio_message_handler.active_document() {
|
||||||
list.extend(self.message_handlers.tool_message_handler.actions());
|
if !document.graph_view_overlay_open {
|
||||||
|
list.extend(self.message_handlers.tool_message_handler.actions());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
list.extend(self.message_handlers.portfolio_message_handler.actions());
|
list.extend(self.message_handlers.portfolio_message_handler.actions());
|
||||||
list
|
list
|
||||||
|
|
|
||||||
|
|
@ -84,9 +84,6 @@ pub enum FrontendMessage {
|
||||||
#[serde(rename = "isDefault")]
|
#[serde(rename = "isDefault")]
|
||||||
is_default: bool,
|
is_default: bool,
|
||||||
},
|
},
|
||||||
TriggerGraphViewOverlay {
|
|
||||||
open: bool,
|
|
||||||
},
|
|
||||||
TriggerImport,
|
TriggerImport,
|
||||||
TriggerIndexedDbRemoveDocument {
|
TriggerIndexedDbRemoveDocument {
|
||||||
#[serde(rename = "documentId")]
|
#[serde(rename = "documentId")]
|
||||||
|
|
@ -153,6 +150,9 @@ pub enum FrontendMessage {
|
||||||
#[serde(rename = "clickTargets")]
|
#[serde(rename = "clickTargets")]
|
||||||
click_targets: Option<FrontendClickTargets>,
|
click_targets: Option<FrontendClickTargets>,
|
||||||
},
|
},
|
||||||
|
UpdateGraphViewOverlay {
|
||||||
|
open: bool,
|
||||||
|
},
|
||||||
UpdateLayerWidths {
|
UpdateLayerWidths {
|
||||||
#[serde(rename = "layerWidths")]
|
#[serde(rename = "layerWidths")]
|
||||||
layer_widths: HashMap<NodeId, u32>,
|
layer_widths: HashMap<NodeId, u32>,
|
||||||
|
|
@ -221,6 +221,9 @@ pub enum FrontendMessage {
|
||||||
#[serde(rename = "setColorChoice")]
|
#[serde(rename = "setColorChoice")]
|
||||||
set_color_choice: Option<String>,
|
set_color_choice: Option<String>,
|
||||||
},
|
},
|
||||||
|
UpdateGraphFadeArtwork {
|
||||||
|
percentage: f64,
|
||||||
|
},
|
||||||
UpdateInputHints {
|
UpdateInputHints {
|
||||||
#[serde(rename = "hintData")]
|
#[serde(rename = "hintData")]
|
||||||
hint_data: HintData,
|
hint_data: HintData,
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ pub fn input_mappings() -> Mapping {
|
||||||
//
|
//
|
||||||
// Hack to prevent Left Click + Accel + Z combo (this effectively blocks you from making a double undo with AbortTransaction)
|
// Hack to prevent Left Click + Accel + Z combo (this effectively blocks you from making a double undo with AbortTransaction)
|
||||||
entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop),
|
entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop),
|
||||||
|
//
|
||||||
// NodeGraphMessage
|
// NodeGraphMessage
|
||||||
entry!(KeyDown(MouseLeft); action_dispatch=NodeGraphMessage::PointerDown {shift_click: false, control_click: false, alt_click: false, right_click: false}),
|
entry!(KeyDown(MouseLeft); action_dispatch=NodeGraphMessage::PointerDown {shift_click: false, control_click: false, alt_click: false, right_click: false}),
|
||||||
entry!(KeyDown(MouseLeft); modifiers=[Shift], action_dispatch=NodeGraphMessage::PointerDown {shift_click: true, control_click: false, alt_click: false, right_click: false}),
|
entry!(KeyDown(MouseLeft); modifiers=[Shift], action_dispatch=NodeGraphMessage::PointerDown {shift_click: true, control_click: false, alt_click: false, right_click: false}),
|
||||||
|
|
@ -69,6 +70,8 @@ pub fn input_mappings() -> Mapping {
|
||||||
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=NodeGraphMessage::Cut),
|
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=NodeGraphMessage::Cut),
|
||||||
entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy),
|
entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy),
|
||||||
entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=NodeGraphMessage::DuplicateSelectedNodes),
|
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::ToggleSelectedAsLayersOrNodes),
|
entry!(KeyDown(KeyL); modifiers=[Alt], action_dispatch=NodeGraphMessage::ToggleSelectedAsLayersOrNodes),
|
||||||
entry!(KeyDown(KeyC); modifiers=[Shift], action_dispatch=NodeGraphMessage::PrintSelectedNodeCoordinates),
|
entry!(KeyDown(KeyC); modifiers=[Shift], action_dispatch=NodeGraphMessage::PrintSelectedNodeCoordinates),
|
||||||
entry!(KeyDown(KeyC); modifiers=[Alt], action_dispatch=NodeGraphMessage::SendClickTargets),
|
entry!(KeyDown(KeyC); modifiers=[Alt], action_dispatch=NodeGraphMessage::SendClickTargets),
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,9 @@ pub enum DocumentMessage {
|
||||||
SetBlendModeForSelectedLayers {
|
SetBlendModeForSelectedLayers {
|
||||||
blend_mode: BlendMode,
|
blend_mode: BlendMode,
|
||||||
},
|
},
|
||||||
|
SetGraphFadeArtwork {
|
||||||
|
percentage: f64,
|
||||||
|
},
|
||||||
SetNodePinned {
|
SetNodePinned {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
pinned: bool,
|
pinned: bool,
|
||||||
|
|
|
||||||
|
|
@ -39,44 +39,7 @@ pub struct DocumentMessageData<'a> {
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||||
pub persistent_data: &'a PersistentData,
|
pub persistent_data: &'a PersistentData,
|
||||||
pub executor: &'a mut NodeGraphExecutor,
|
pub executor: &'a mut NodeGraphExecutor,
|
||||||
}
|
pub current_tool: &'a ToolType,
|
||||||
|
|
||||||
// TODO: Eventually remove this (probably starting late 2024)
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
||||||
pub struct OldDocumentMessageHandler {
|
|
||||||
// ============================================
|
|
||||||
// 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.
|
|
||||||
pub network: OldNodeNetwork,
|
|
||||||
/// List of the [`NodeId`]s that are currently selected by the user.
|
|
||||||
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.
|
|
||||||
pub collapsed: CollapsedLayers,
|
|
||||||
/// The name of the document, which is displayed in the tab and title bar of the editor.
|
|
||||||
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.
|
|
||||||
pub commit_hash: String,
|
|
||||||
/// The current pan, tilt, and zoom state of the viewport's view of the document canvas.
|
|
||||||
pub document_ptz: PTZ,
|
|
||||||
/// The current mode that the document is in, which starts out as Design Mode. This choice affects the editing behavior of the tools.
|
|
||||||
pub 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.
|
|
||||||
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.
|
|
||||||
pub overlays_visible: bool,
|
|
||||||
/// Sets whether or not the rulers should be drawn along the top and left edges of the viewport area.
|
|
||||||
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.
|
|
||||||
pub graph_view_overlay_open: bool,
|
|
||||||
/// The current user choices for snapping behavior, including whether snapping is enabled at all.
|
|
||||||
pub snapping_state: SnappingState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
|
@ -121,10 +84,12 @@ pub struct DocumentMessageHandler {
|
||||||
pub overlays_visible: bool,
|
pub overlays_visible: bool,
|
||||||
/// Sets whether or not the rulers should be drawn along the top and left edges of the viewport area.
|
/// Sets whether or not the rulers should be drawn along the top and left edges of the viewport area.
|
||||||
pub rulers_visible: bool,
|
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.
|
|
||||||
pub graph_view_overlay_open: bool,
|
|
||||||
/// The current user choices for snapping behavior, including whether snapping is enabled at all.
|
/// The current user choices for snapping behavior, including whether snapping is enabled at all.
|
||||||
pub snapping_state: SnappingState,
|
pub snapping_state: SnappingState,
|
||||||
|
/// Sets whether or not the node graph is drawn (as an overlay) on top of the viewport area, or otherwise if it's hidden.
|
||||||
|
pub graph_view_overlay_open: bool,
|
||||||
|
/// The current opacity of the faded node graph background that covers up the artwork.
|
||||||
|
pub graph_fade_artwork_percentage: f64,
|
||||||
|
|
||||||
// =============================================
|
// =============================================
|
||||||
// Fields omitted from the saved document format
|
// Fields omitted from the saved document format
|
||||||
|
|
@ -178,6 +143,7 @@ impl Default for DocumentMessageHandler {
|
||||||
rulers_visible: true,
|
rulers_visible: true,
|
||||||
graph_view_overlay_open: false,
|
graph_view_overlay_open: false,
|
||||||
snapping_state: SnappingState::default(),
|
snapping_state: SnappingState::default(),
|
||||||
|
graph_fade_artwork_percentage: 80.,
|
||||||
// =============================================
|
// =============================================
|
||||||
// Fields omitted from the saved document format
|
// Fields omitted from the saved document format
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
@ -199,6 +165,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||||
ipp,
|
ipp,
|
||||||
persistent_data,
|
persistent_data,
|
||||||
executor,
|
executor,
|
||||||
|
current_tool,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
let selected_nodes_bounding_box_viewport = self.network_interface.selected_nodes_bounding_box_viewport(&self.breadcrumb_network_path);
|
let selected_nodes_bounding_box_viewport = self.network_interface.selected_nodes_bounding_box_viewport(&self.breadcrumb_network_path);
|
||||||
|
|
@ -247,6 +214,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||||
collapsed: &mut self.collapsed,
|
collapsed: &mut self.collapsed,
|
||||||
ipp,
|
ipp,
|
||||||
graph_view_overlay_open: self.graph_view_overlay_open,
|
graph_view_overlay_open: self.graph_view_overlay_open,
|
||||||
|
graph_fade_artwork_percentage: self.graph_fade_artwork_percentage,
|
||||||
|
navigation_handler: &self.navigation_handler,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -477,15 +446,25 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||||
DocumentMessage::GraphViewOverlay { open } => {
|
DocumentMessage::GraphViewOverlay { open } => {
|
||||||
self.graph_view_overlay_open = open;
|
self.graph_view_overlay_open = open;
|
||||||
|
|
||||||
responses.add(FrontendMessage::TriggerGraphViewOverlay { open });
|
responses.add(FrontendMessage::UpdateGraphViewOverlay { open });
|
||||||
|
responses.add(FrontendMessage::UpdateGraphFadeArtwork {
|
||||||
|
percentage: self.graph_fade_artwork_percentage,
|
||||||
|
});
|
||||||
|
|
||||||
// Update the tilt menu bar buttons to be disabled when the graph is open
|
// Update the tilt menu bar buttons to be disabled when the graph is open
|
||||||
responses.add(MenuBarMessage::SendLayout);
|
responses.add(MenuBarMessage::SendLayout);
|
||||||
|
|
||||||
responses.add(DocumentMessage::RenderRulers);
|
responses.add(DocumentMessage::RenderRulers);
|
||||||
responses.add(DocumentMessage::RenderScrollbars);
|
responses.add(DocumentMessage::RenderScrollbars);
|
||||||
if open {
|
if open {
|
||||||
|
responses.add(ToolMessage::DeactivateTools);
|
||||||
|
responses.add(OverlaysMessage::Draw); // Clear the overlays
|
||||||
responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. });
|
responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. });
|
||||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||||
|
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||||
responses.add(NodeGraphMessage::SendGraph);
|
responses.add(NodeGraphMessage::SendGraph);
|
||||||
|
} else {
|
||||||
|
responses.add(ToolMessage::ActivateTool { tool_type: *current_tool });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DocumentMessage::GraphViewOverlayToggle => {
|
DocumentMessage::GraphViewOverlayToggle => {
|
||||||
|
|
@ -1019,6 +998,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||||
responses.add(GraphOperationMessage::BlendModeSet { layer, blend_mode });
|
responses.add(GraphOperationMessage::BlendModeSet { layer, blend_mode });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DocumentMessage::SetGraphFadeArtwork { percentage } => {
|
||||||
|
self.graph_fade_artwork_percentage = percentage;
|
||||||
|
responses.add(FrontendMessage::UpdateGraphFadeArtwork { percentage });
|
||||||
|
}
|
||||||
DocumentMessage::SetNodePinned { node_id, pinned } => {
|
DocumentMessage::SetNodePinned { node_id, pinned } => {
|
||||||
responses.add(DocumentMessage::StartTransaction);
|
responses.add(DocumentMessage::StartTransaction);
|
||||||
responses.add(NodeGraphMessage::SetPinned { node_id, pinned });
|
responses.add(NodeGraphMessage::SetPinned { node_id, pinned });
|
||||||
|
|
@ -1338,11 +1321,13 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
||||||
SelectedLayersRaise,
|
SelectedLayersRaise,
|
||||||
SelectedLayersRaiseToFront,
|
SelectedLayersRaiseToFront,
|
||||||
UngroupSelectedLayers,
|
UngroupSelectedLayers,
|
||||||
ToggleSelectedVisibility,
|
|
||||||
ToggleSelectedLocked
|
ToggleSelectedLocked
|
||||||
);
|
);
|
||||||
if !self.graph_view_overlay_open {
|
if !self.graph_view_overlay_open {
|
||||||
select.extend(actions!(DocumentMessageDiscriminant; NudgeSelectedLayers));
|
select.extend(actions!(DocumentMessageDiscriminant;
|
||||||
|
NudgeSelectedLayers,
|
||||||
|
ToggleSelectedVisibility,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
common.extend(select);
|
common.extend(select);
|
||||||
}
|
}
|
||||||
|
|
@ -1825,68 +1810,10 @@ impl DocumentMessageHandler {
|
||||||
])
|
])
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
IconButton::new("ZoomIn", 24)
|
|
||||||
.tooltip("Zoom In")
|
|
||||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasZoomIncrease))
|
|
||||||
.on_update(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
|
|
||||||
.widget_holder(),
|
|
||||||
IconButton::new("ZoomOut", 24)
|
|
||||||
.tooltip("Zoom Out")
|
|
||||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasZoomDecrease))
|
|
||||||
.on_update(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
|
|
||||||
.widget_holder(),
|
|
||||||
IconButton::new("ZoomReset", 24)
|
|
||||||
.tooltip("Reset Tilt and Zoom to 100%")
|
|
||||||
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
|
|
||||||
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
|
|
||||||
.disabled(self.document_ptz.tilt.abs() < 1e-4 && (self.document_ptz.zoom() - 1.).abs() < 1e-4)
|
|
||||||
.widget_holder(),
|
|
||||||
PopoverButton::new()
|
|
||||||
.popover_layout(vec![
|
|
||||||
LayoutGroup::Row {
|
|
||||||
widgets: vec![TextLabel::new("Canvas Navigation").bold(true).widget_holder()],
|
|
||||||
},
|
|
||||||
LayoutGroup::Row {
|
|
||||||
widgets: vec![TextLabel::new(
|
|
||||||
"
|
|
||||||
Interactive controls in this\n\
|
|
||||||
menu are coming soon.\n\
|
|
||||||
\n\
|
|
||||||
Pan:\n\
|
|
||||||
• Middle Click Drag\n\
|
|
||||||
\n\
|
|
||||||
Tilt:\n\
|
|
||||||
• Alt + Middle Click Drag\n\
|
|
||||||
\n\
|
|
||||||
Zoom:\n\
|
|
||||||
• Shift + Middle Click Drag\n\
|
|
||||||
• Ctrl + Scroll Wheel Roll
|
|
||||||
"
|
|
||||||
.trim(),
|
|
||||||
)
|
|
||||||
.multiline(true)
|
|
||||||
.widget_holder()],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
.widget_holder(),
|
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
|
||||||
NumberInput::new(Some(self.navigation_handler.snapped_zoom(self.document_ptz.zoom()) * 100.))
|
|
||||||
.unit("%")
|
|
||||||
.min(0.000001)
|
|
||||||
.max(1000000.)
|
|
||||||
.tooltip("Document zoom within the viewport")
|
|
||||||
.on_update(|number_input: &NumberInput| {
|
|
||||||
NavigationMessage::CanvasZoomSet {
|
|
||||||
zoom_factor: number_input.value.unwrap() / 100.,
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.increment_behavior(NumberInputIncrementBehavior::Callback)
|
|
||||||
.increment_callback_decrease(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
|
|
||||||
.increment_callback_increase(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
|
|
||||||
.widget_holder(),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
widgets.extend(navigation_controls(&self.document_ptz, &self.navigation_handler, "Canvas"));
|
||||||
|
|
||||||
let tilt_value = self.navigation_handler.snapped_tilt(self.document_ptz.tilt) / (std::f64::consts::PI / 180.);
|
let tilt_value = self.navigation_handler.snapped_tilt(self.document_ptz.tilt) / (std::f64::consts::PI / 180.);
|
||||||
if tilt_value.abs() > 0.00001 {
|
if tilt_value.abs() > 0.00001 {
|
||||||
widgets.extend([
|
widgets.extend([
|
||||||
|
|
@ -2059,15 +1986,15 @@ impl DocumentMessageHandler {
|
||||||
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
||||||
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
||||||
.tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
.tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
||||||
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedLocked))
|
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked))
|
||||||
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||||
.disabled(!has_selection)
|
.disabled(!has_selection)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
||||||
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
||||||
.tooltip(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
.tooltip(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
||||||
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
|
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
|
||||||
.on_update(|_| NodeGraphMessage::ToggleSelectedVisibility.into())
|
.on_update(|_| DocumentMessage::ToggleSelectedVisibility.into())
|
||||||
.disabled(!has_selection)
|
.disabled(!has_selection)
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
],
|
],
|
||||||
|
|
@ -2235,6 +2162,71 @@ impl<'a> ClickXRayIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn navigation_controls(ptz: &PTZ, navigation_handler: &NavigationMessageHandler, tooltip_name: &str) -> [WidgetHolder; 6] {
|
||||||
|
[
|
||||||
|
IconButton::new("ZoomIn", 24)
|
||||||
|
.tooltip("Zoom In")
|
||||||
|
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasZoomIncrease))
|
||||||
|
.on_update(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
|
||||||
|
.widget_holder(),
|
||||||
|
IconButton::new("ZoomOut", 24)
|
||||||
|
.tooltip("Zoom Out")
|
||||||
|
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasZoomDecrease))
|
||||||
|
.on_update(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
|
||||||
|
.widget_holder(),
|
||||||
|
IconButton::new("ZoomReset", 24)
|
||||||
|
.tooltip("Reset Tilt and Zoom to 100%")
|
||||||
|
.tooltip_shortcut(action_keys!(NavigationMessageDiscriminant::CanvasTiltResetAndZoomTo100Percent))
|
||||||
|
.on_update(|_| NavigationMessage::CanvasTiltResetAndZoomTo100Percent.into())
|
||||||
|
.disabled(ptz.tilt.abs() < 1e-4 && (ptz.zoom() - 1.).abs() < 1e-4)
|
||||||
|
.widget_holder(),
|
||||||
|
PopoverButton::new()
|
||||||
|
.popover_layout(vec![
|
||||||
|
LayoutGroup::Row {
|
||||||
|
widgets: vec![TextLabel::new(format!("{tooltip_name} Navigation")).bold(true).widget_holder()],
|
||||||
|
},
|
||||||
|
LayoutGroup::Row {
|
||||||
|
widgets: vec![TextLabel::new({
|
||||||
|
let tilt = if tooltip_name == "Canvas" { "Tilt:\n• Alt + Middle Click Drag\n\n" } else { "" };
|
||||||
|
format!(
|
||||||
|
"
|
||||||
|
Interactive controls in this\n\
|
||||||
|
menu are coming soon.\n\
|
||||||
|
\n\
|
||||||
|
Pan:\n\
|
||||||
|
• Middle Click Drag\n\
|
||||||
|
\n\
|
||||||
|
{tilt}Zoom:\n\
|
||||||
|
• Shift + Middle Click Drag\n\
|
||||||
|
• Ctrl + Scroll Wheel Roll
|
||||||
|
"
|
||||||
|
)
|
||||||
|
.trim()
|
||||||
|
})
|
||||||
|
.multiline(true)
|
||||||
|
.widget_holder()],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.widget_holder(),
|
||||||
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
|
NumberInput::new(Some(navigation_handler.snapped_zoom(ptz.zoom()) * 100.))
|
||||||
|
.unit("%")
|
||||||
|
.min(0.000001)
|
||||||
|
.max(1000000.)
|
||||||
|
.tooltip(format!("{tooltip_name} Zoom"))
|
||||||
|
.on_update(|number_input: &NumberInput| {
|
||||||
|
NavigationMessage::CanvasZoomSet {
|
||||||
|
zoom_factor: number_input.value.unwrap() / 100.,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.increment_behavior(NumberInputIncrementBehavior::Callback)
|
||||||
|
.increment_callback_decrease(|_| NavigationMessage::CanvasZoomDecrease { center_on_mouse: false }.into())
|
||||||
|
.increment_callback_increase(|_| NavigationMessage::CanvasZoomIncrease { center_on_mouse: false }.into())
|
||||||
|
.widget_holder(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for ClickXRayIter<'a> {
|
impl<'a> Iterator for ClickXRayIter<'a> {
|
||||||
type Item = LayerNodeIdentifier;
|
type Item = LayerNodeIdentifier;
|
||||||
|
|
||||||
|
|
@ -2266,3 +2258,41 @@ impl<'a> Iterator for ClickXRayIter<'a> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Eventually remove this (probably starting late 2024)
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct OldDocumentMessageHandler {
|
||||||
|
// ============================================
|
||||||
|
// 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.
|
||||||
|
pub network: OldNodeNetwork,
|
||||||
|
/// List of the [`NodeId`]s that are currently selected by the user.
|
||||||
|
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.
|
||||||
|
pub collapsed: CollapsedLayers,
|
||||||
|
/// The name of the document, which is displayed in the tab and title bar of the editor.
|
||||||
|
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.
|
||||||
|
pub commit_hash: String,
|
||||||
|
/// The current pan, tilt, and zoom state of the viewport's view of the document canvas.
|
||||||
|
pub document_ptz: PTZ,
|
||||||
|
/// The current mode that the document is in, which starts out as Design Mode. This choice affects the editing behavior of the tools.
|
||||||
|
pub 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.
|
||||||
|
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.
|
||||||
|
pub overlays_visible: bool,
|
||||||
|
/// Sets whether or not the rulers should be drawn along the top and left edges of the viewport area.
|
||||||
|
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.
|
||||||
|
pub graph_view_overlay_open: bool,
|
||||||
|
/// The current user choices for snapping behavior, including whether snapping is enabled at all.
|
||||||
|
pub snapping_state: SnappingState,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,11 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
};
|
};
|
||||||
ptz.tilt = 0.;
|
ptz.tilt = 0.;
|
||||||
ptz.set_zoom(1.);
|
ptz.set_zoom(1.);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
if graph_view_overlay_open {
|
||||||
|
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||||
|
} else {
|
||||||
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
|
}
|
||||||
responses.add(DocumentMessage::PTZUpdate);
|
responses.add(DocumentMessage::PTZUpdate);
|
||||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||||
}
|
}
|
||||||
|
|
@ -192,6 +196,9 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
};
|
};
|
||||||
ptz.tilt = angle_radians;
|
ptz.tilt = angle_radians;
|
||||||
responses.add(DocumentMessage::PTZUpdate);
|
responses.add(DocumentMessage::PTZUpdate);
|
||||||
|
if !graph_view_overlay_open {
|
||||||
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
NavigationMessage::CanvasZoomDecrease { center_on_mouse } => {
|
NavigationMessage::CanvasZoomDecrease { center_on_mouse } => {
|
||||||
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
let Some(ptz) = get_ptz(document_ptz, network_interface, graph_view_overlay_open, breadcrumb_network_path) else {
|
||||||
|
|
@ -252,7 +259,11 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
let zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
let zoom = zoom_factor.clamp(VIEWPORT_ZOOM_SCALE_MIN, VIEWPORT_ZOOM_SCALE_MAX);
|
||||||
let zoom = zoom * Self::clamp_zoom(zoom, document_bounds, old_zoom, ipp);
|
let zoom = zoom * Self::clamp_zoom(zoom, document_bounds, old_zoom, ipp);
|
||||||
ptz.set_zoom(zoom);
|
ptz.set_zoom(zoom);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
if graph_view_overlay_open {
|
||||||
|
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||||
|
} else {
|
||||||
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
|
}
|
||||||
responses.add(DocumentMessage::PTZUpdate);
|
responses.add(DocumentMessage::PTZUpdate);
|
||||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||||
}
|
}
|
||||||
|
|
@ -281,6 +292,11 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
ptz.tilt = self.snapped_tilt(ptz.tilt);
|
ptz.tilt = self.snapped_tilt(ptz.tilt);
|
||||||
ptz.set_zoom(self.snapped_zoom(ptz.zoom()));
|
ptz.set_zoom(self.snapped_zoom(ptz.zoom()));
|
||||||
responses.add(DocumentMessage::PTZUpdate);
|
responses.add(DocumentMessage::PTZUpdate);
|
||||||
|
if graph_view_overlay_open {
|
||||||
|
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||||
|
} else {
|
||||||
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
|
}
|
||||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||||
// Reset the navigation operation now that it's done
|
// Reset the navigation operation now that it's done
|
||||||
self.navigation_operation = NavigationOperation::None;
|
self.navigation_operation = NavigationOperation::None;
|
||||||
|
|
@ -336,7 +352,11 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
||||||
ptz.set_zoom(1.);
|
ptz.set_zoom(1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
if graph_view_overlay_open {
|
||||||
|
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||||
|
} else {
|
||||||
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
|
}
|
||||||
responses.add(DocumentMessage::PTZUpdate);
|
responses.add(DocumentMessage::PTZUpdate);
|
||||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||||
}
|
}
|
||||||
|
|
@ -478,11 +498,7 @@ impl NavigationMessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn snapped_zoom(&self, zoom: f64) -> f64 {
|
pub fn snapped_zoom(&self, zoom: f64) -> f64 {
|
||||||
if matches!(self.navigation_operation, NavigationOperation::Zoom { snap: true, .. }) {
|
snapped_zoom(&self.navigation_operation, zoom)
|
||||||
*VIEWPORT_ZOOM_LEVELS.iter().min_by(|a, b| (**a - zoom).abs().partial_cmp(&(**b - zoom).abs()).unwrap()).unwrap_or(&zoom)
|
|
||||||
} else {
|
|
||||||
zoom
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_offset_transform(&self, viewport_center: DVec2, ptz: &PTZ) -> DAffine2 {
|
pub fn calculate_offset_transform(&self, viewport_center: DVec2, ptz: &PTZ) -> DAffine2 {
|
||||||
|
|
@ -523,3 +539,11 @@ impl NavigationMessageHandler {
|
||||||
VIEWPORT_ZOOM_MIN_FRACTION_COVER / scale_factor
|
VIEWPORT_ZOOM_MIN_FRACTION_COVER / scale_factor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn snapped_zoom(navigation_operation: &NavigationOperation, zoom: f64) -> f64 {
|
||||||
|
if matches!(navigation_operation, NavigationOperation::Zoom { snap: true, .. }) {
|
||||||
|
*VIEWPORT_ZOOM_LEVELS.iter().min_by(|a, b| (**a - zoom).abs().partial_cmp(&(**b - zoom).abs()).unwrap()).unwrap_or(&zoom)
|
||||||
|
} else {
|
||||||
|
zoom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,7 @@ pub enum NodeGraphMessage {
|
||||||
CreateNodeFromContextMenu {
|
CreateNodeFromContextMenu {
|
||||||
node_id: Option<NodeId>,
|
node_id: Option<NodeId>,
|
||||||
node_type: String,
|
node_type: String,
|
||||||
x: i32,
|
xy: Option<(i32, i32)>,
|
||||||
y: i32,
|
|
||||||
},
|
},
|
||||||
CreateWire {
|
CreateWire {
|
||||||
output_connector: OutputConnector,
|
output_connector: OutputConnector,
|
||||||
|
|
@ -182,6 +181,7 @@ pub enum NodeGraphMessage {
|
||||||
node_graph_errors: GraphErrors,
|
node_graph_errors: GraphErrors,
|
||||||
},
|
},
|
||||||
UpdateActionButtons,
|
UpdateActionButtons,
|
||||||
|
UpdateGraphBarRight,
|
||||||
UpdateInSelectedNetwork,
|
UpdateInSelectedNetwork,
|
||||||
SendSelectedNodes,
|
SendSelectedNodes,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeWire, WirePath};
|
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeWire, WirePath};
|
||||||
use super::{document_node_definitions, node_properties};
|
use super::{document_node_definitions, node_properties};
|
||||||
|
use crate::consts::GRID_SIZE;
|
||||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||||
use crate::messages::layout::utility_types::widget_prelude::*;
|
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||||
|
use crate::messages::portfolio::document::document_message_handler::navigation_controls;
|
||||||
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
|
||||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
|
use crate::messages::portfolio::document::node_graph::document_node_definitions::NodePropertiesContext;
|
||||||
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType};
|
use crate::messages::portfolio::document::node_graph::utility_types::{ContextMenuData, Direction, FrontendGraphDataType};
|
||||||
|
|
@ -28,6 +30,8 @@ pub struct NodeGraphHandlerData<'a> {
|
||||||
pub collapsed: &'a mut CollapsedLayers,
|
pub collapsed: &'a mut CollapsedLayers,
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||||
pub graph_view_overlay_open: bool,
|
pub graph_view_overlay_open: bool,
|
||||||
|
pub graph_fade_artwork_percentage: f64,
|
||||||
|
pub navigation_handler: &'a NavigationMessageHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -76,8 +80,10 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||||
breadcrumb_network_path,
|
breadcrumb_network_path,
|
||||||
document_id,
|
document_id,
|
||||||
collapsed,
|
collapsed,
|
||||||
graph_view_overlay_open,
|
|
||||||
ipp,
|
ipp,
|
||||||
|
graph_view_overlay_open,
|
||||||
|
graph_fade_artwork_percentage,
|
||||||
|
navigation_handler,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
|
|
@ -149,7 +155,15 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||||
responses.add(PropertiesPanelMessage::Refresh);
|
responses.add(PropertiesPanelMessage::Refresh);
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::CreateNodeFromContextMenu { node_id, node_type, x, y } => {
|
NodeGraphMessage::CreateNodeFromContextMenu { node_id, node_type, xy } => {
|
||||||
|
let (x, y) = if let Some((x, y)) = xy {
|
||||||
|
(x, y)
|
||||||
|
} else if let Some(node_graph_ptz) = network_interface.node_graph_ptz(breadcrumb_network_path) {
|
||||||
|
((-node_graph_ptz.pan.x / GRID_SIZE as f64) as i32, (-node_graph_ptz.pan.y / GRID_SIZE as f64) as i32)
|
||||||
|
} else {
|
||||||
|
(0, 0)
|
||||||
|
};
|
||||||
|
|
||||||
let node_id = node_id.unwrap_or_else(NodeId::new);
|
let node_id = node_id.unwrap_or_else(NodeId::new);
|
||||||
|
|
||||||
let Some(document_node_type) = document_node_definitions::resolve_document_node_type(&node_type) else {
|
let Some(document_node_type) = document_node_definitions::resolve_document_node_type(&node_type) else {
|
||||||
|
|
@ -1374,9 +1388,14 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
||||||
}
|
}
|
||||||
NodeGraphMessage::UpdateActionButtons => {
|
NodeGraphMessage::UpdateActionButtons => {
|
||||||
if selection_network_path == breadcrumb_network_path {
|
if selection_network_path == breadcrumb_network_path {
|
||||||
self.update_selection_action_buttons(network_interface, breadcrumb_network_path, responses);
|
self.update_graph_bar_left(network_interface, breadcrumb_network_path, responses);
|
||||||
|
self.send_node_bar_layout(responses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NodeGraphMessage::UpdateGraphBarRight => {
|
||||||
|
self.update_graph_bar_right(graph_fade_artwork_percentage, network_interface, breadcrumb_network_path, navigation_handler);
|
||||||
|
self.send_node_bar_layout(responses);
|
||||||
|
}
|
||||||
NodeGraphMessage::UpdateInSelectedNetwork => responses.add(FrontendMessage::UpdateInSelectedNetwork {
|
NodeGraphMessage::UpdateInSelectedNetwork => responses.add(FrontendMessage::UpdateInSelectedNetwork {
|
||||||
in_selected_network: selection_network_path == breadcrumb_network_path,
|
in_selected_network: selection_network_path == breadcrumb_network_path,
|
||||||
}),
|
}),
|
||||||
|
|
@ -1435,7 +1454,7 @@ impl NodeGraphMessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the buttons for visibility, locked, and preview
|
/// Updates the buttons for visibility, locked, and preview
|
||||||
fn update_selection_action_buttons(&mut self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId], responses: &mut VecDeque<Message>) {
|
fn update_graph_bar_left(&mut self, network_interface: &mut NodeNetworkInterface, breadcrumb_network_path: &[NodeId], responses: &mut VecDeque<Message>) {
|
||||||
let Some(subgraph_path_names) = Self::collect_subgraph_names(network_interface, breadcrumb_network_path) else {
|
let Some(subgraph_path_names) = Self::collect_subgraph_names(network_interface, breadcrumb_network_path) else {
|
||||||
// If a node in a nested network could not be found, exit the nested network
|
// If a node in a nested network could not be found, exit the nested network
|
||||||
let breadcrumb_network_path_len = breadcrumb_network_path.len();
|
let breadcrumb_network_path_len = breadcrumb_network_path.len();
|
||||||
|
|
@ -1457,100 +1476,172 @@ impl NodeGraphMessageHandler {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let subgraph_path_names_length = subgraph_path_names.len();
|
let has_selection = selected_nodes.has_selected_nodes();
|
||||||
|
let selection_includes_layers = network_interface.selected_nodes(&[]).unwrap().selected_layers(network_interface.document_metadata()).count() > 0;
|
||||||
let breadcrumb_trail = BreadcrumbTrailButtons::new(subgraph_path_names).on_update(move |index| {
|
let selection_all_locked = network_interface.selected_nodes(&[]).unwrap().selected_unlocked_layers(network_interface).count() == 0;
|
||||||
DocumentMessage::ExitNestedNetwork {
|
let selection_all_visible = selected_nodes.selected_nodes().all(|id| {
|
||||||
steps_back: subgraph_path_names_length - (*index as usize) - 1,
|
if let Some(node) = network.nodes.get(id) {
|
||||||
|
node.visible
|
||||||
|
} else {
|
||||||
|
error!("Could not get node {id} in update_selection_action_buttons");
|
||||||
|
true
|
||||||
}
|
}
|
||||||
.into()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut widgets = if subgraph_path_names_length >= 2 {
|
let mut widgets = vec![
|
||||||
vec![breadcrumb_trail.widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()]
|
PopoverButton::new()
|
||||||
|
.icon(Some("Node".to_string()))
|
||||||
|
.tooltip("Add a new node")
|
||||||
|
.popover_layout({
|
||||||
|
let node_chooser = NodeCatalog::new()
|
||||||
|
.on_update(move |node_type| {
|
||||||
|
let node_id = NodeId::new();
|
||||||
|
|
||||||
|
Message::Batched(Box::new([
|
||||||
|
NodeGraphMessage::CreateNodeFromContextMenu {
|
||||||
|
node_id: Some(node_id),
|
||||||
|
node_type: node_type.clone(),
|
||||||
|
xy: None,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
NodeGraphMessage::SelectedNodesSet { nodes: vec![node_id] }.into(),
|
||||||
|
]))
|
||||||
|
})
|
||||||
|
.widget_holder();
|
||||||
|
vec![LayoutGroup::Row { widgets: vec![node_chooser] }]
|
||||||
|
})
|
||||||
|
.widget_holder(),
|
||||||
|
//
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
//
|
||||||
|
IconButton::new("NewLayer", 24)
|
||||||
|
.tooltip("New Layer")
|
||||||
|
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::CreateEmptyFolder))
|
||||||
|
.on_update(|_| DocumentMessage::CreateEmptyFolder.into())
|
||||||
|
.widget_holder(),
|
||||||
|
IconButton::new("Folder", 24)
|
||||||
|
.tooltip("Group Selected")
|
||||||
|
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GroupSelectedLayers))
|
||||||
|
.on_update(|_| DocumentMessage::GroupSelectedLayers.into())
|
||||||
|
.disabled(!has_selection)
|
||||||
|
.widget_holder(),
|
||||||
|
IconButton::new("Trash", 24)
|
||||||
|
.tooltip("Delete Selected")
|
||||||
|
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::DeleteSelectedLayers))
|
||||||
|
.on_update(|_| DocumentMessage::DeleteSelectedLayers.into())
|
||||||
|
.disabled(!has_selection)
|
||||||
|
.widget_holder(),
|
||||||
|
//
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
//
|
||||||
|
IconButton::new(if selection_all_locked { "PadlockLocked" } else { "PadlockUnlocked" }, 24)
|
||||||
|
.hover_icon(Some((if selection_all_locked { "PadlockUnlocked" } else { "PadlockLocked" }).into()))
|
||||||
|
.tooltip(if selection_all_locked { "Unlock Selected" } else { "Lock Selected" })
|
||||||
|
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedLocked))
|
||||||
|
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||||
|
.disabled(!has_selection || !selection_includes_layers)
|
||||||
|
.widget_holder(),
|
||||||
|
IconButton::new(if selection_all_visible { "EyeVisible" } else { "EyeHidden" }, 24)
|
||||||
|
.hover_icon(Some((if selection_all_visible { "EyeHide" } else { "EyeShow" }).into()))
|
||||||
|
.tooltip(if selection_all_visible { "Hide Selected" } else { "Show Selected" })
|
||||||
|
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
|
||||||
|
.on_update(|_| NodeGraphMessage::ToggleSelectedVisibility.into())
|
||||||
|
.disabled(!has_selection)
|
||||||
|
.widget_holder(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut selection = selected_nodes.selected_nodes();
|
||||||
|
let (selection, no_other_selections) = (selection.next(), selection.count() == 0);
|
||||||
|
let previewing = if matches!(network_interface.previewing(breadcrumb_network_path), Previewing::Yes { .. }) {
|
||||||
|
network.exports.iter().find_map(|export| {
|
||||||
|
let NodeInput::Node { node_id, .. } = export else { return None };
|
||||||
|
Some(*node_id)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut selection = selected_nodes.selected_nodes();
|
|
||||||
// If there is at least one selected node then show the hide or show button
|
|
||||||
if selection.next().is_some() {
|
|
||||||
// Check if any of the selected nodes are disabled
|
|
||||||
let all_visible = selected_nodes.selected_nodes().all(|id| {
|
|
||||||
if let Some(node) = network.nodes.get(id) {
|
|
||||||
node.visible
|
|
||||||
} else {
|
|
||||||
error!("Could not get node {id} in update_selection_action_buttons");
|
|
||||||
true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if multiple nodes are selected
|
|
||||||
let multiple_nodes = selection.next().is_some();
|
|
||||||
|
|
||||||
// Generate the visible/hidden button accordingly
|
|
||||||
let (hide_show_label, hide_show_icon) = if all_visible { ("Make Hidden", "EyeVisible") } else { ("Make Visible", "EyeHidden") };
|
|
||||||
let hide_button = TextButton::new(hide_show_label)
|
|
||||||
.icon(Some(hide_show_icon.to_string()))
|
|
||||||
.tooltip(if all_visible { "Hide" } else { "Show" }.to_string() + " selected " + if multiple_nodes { "nodes/layers" } else { "node/layer" })
|
|
||||||
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedVisibility))
|
|
||||||
.on_update(move |_| NodeGraphMessage::ToggleSelectedVisibility.into())
|
|
||||||
.widget_holder();
|
|
||||||
widgets.push(hide_button);
|
|
||||||
|
|
||||||
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut selection = selected_nodes.selected_nodes();
|
|
||||||
// If there is at least one selected node then show the pin or unpin button
|
|
||||||
if selection.next().is_some() {
|
|
||||||
// Check if any of the selected nodes are pinned
|
|
||||||
let all_unpinned = !selected_nodes.selected_nodes().all(|id| {
|
|
||||||
if let Some(node) = network_interface.node_metadata(id, breadcrumb_network_path) {
|
|
||||||
node.persistent_metadata.pinned
|
|
||||||
} else {
|
|
||||||
error!("Could not get node {id} in update_selection_action_buttons");
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if multiple nodes are selected
|
|
||||||
let multiple_nodes = selection.next().is_some();
|
|
||||||
|
|
||||||
// Generate the visible/hidden button accordingly
|
|
||||||
let (pin_unpin_label, pin_unpin_icon) = if all_unpinned { ("Pin", "CheckboxUnchecked") } else { ("Unpin", "CheckboxChecked") };
|
|
||||||
let pin_button = TextButton::new(pin_unpin_label)
|
|
||||||
.icon(Some(pin_unpin_icon.to_string()))
|
|
||||||
.tooltip(
|
|
||||||
if all_unpinned { "Pin" } else { "Unpin" }.to_string()
|
|
||||||
+ " selected " + if multiple_nodes { "nodes/layers" } else { "node/layer" }
|
|
||||||
+ " in the Properties panel when nothing is selected",
|
|
||||||
)
|
|
||||||
.tooltip_shortcut(action_keys!(NodeGraphMessageDiscriminant::ToggleSelectedIsPinned))
|
|
||||||
.on_update(move |_| NodeGraphMessage::ToggleSelectedIsPinned.into())
|
|
||||||
.widget_holder();
|
|
||||||
widgets.push(pin_button);
|
|
||||||
|
|
||||||
widgets.push(Separator::new(SeparatorType::Related).widget_holder());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut selection = selected_nodes.selected_nodes();
|
|
||||||
// If only one node is selected then show the preview or stop previewing button
|
// If only one node is selected then show the preview or stop previewing button
|
||||||
if let (Some(&node_id), None) = (selection.next(), selection.next()) {
|
if let Some(node_id) = previewing {
|
||||||
// Is this node the current output
|
let button = TextButton::new("End Preview")
|
||||||
let is_output = network.outputs_contain(node_id);
|
|
||||||
let is_previewing = matches!(network_interface.previewing(breadcrumb_network_path), Previewing::Yes { .. });
|
|
||||||
|
|
||||||
let output_button = TextButton::new(if is_output && is_previewing { "End Preview" } else { "Preview" })
|
|
||||||
.icon(Some("Rescale".to_string()))
|
.icon(Some("Rescale".to_string()))
|
||||||
.tooltip(if is_output { "Restore preview to the graph output" } else { "Preview selected node/layer" }.to_string() + " (Shortcut: Alt-click node/layer)")
|
.tooltip("Restore preview to the graph output")
|
||||||
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
||||||
.widget_holder();
|
.widget_holder();
|
||||||
widgets.push(output_button);
|
widgets.extend([Separator::new(SeparatorType::Unrelated).widget_holder(), button]);
|
||||||
|
} else if let Some(&node_id) = selection {
|
||||||
|
let selection_is_not_already_the_output = !network
|
||||||
|
.exports
|
||||||
|
.iter()
|
||||||
|
.any(|export| matches!(export, NodeInput::Node { node_id: export_node_id, .. } if *export_node_id == node_id));
|
||||||
|
if selection_is_not_already_the_output && no_other_selections {
|
||||||
|
let button = TextButton::new("Preview")
|
||||||
|
.icon(Some("Rescale".to_string()))
|
||||||
|
.tooltip("Preview selected node/layer (Shortcut: Alt-click node/layer)")
|
||||||
|
.on_update(move |_| NodeGraphMessage::TogglePreview { node_id }.into())
|
||||||
|
.widget_holder();
|
||||||
|
widgets.extend([Separator::new(SeparatorType::Unrelated).widget_holder(), button]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let subgraph_path_names_length = subgraph_path_names.len();
|
||||||
|
if subgraph_path_names_length >= 2 {
|
||||||
|
widgets.extend([
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
BreadcrumbTrailButtons::new(subgraph_path_names)
|
||||||
|
.on_update(move |index| {
|
||||||
|
DocumentMessage::ExitNestedNetwork {
|
||||||
|
steps_back: subgraph_path_names_length - (*index as usize) - 1,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.widget_holder(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.widgets[0] = LayoutGroup::Row { widgets };
|
self.widgets[0] = LayoutGroup::Row { widgets };
|
||||||
self.send_node_bar_layout(responses);
|
}
|
||||||
|
|
||||||
|
fn update_graph_bar_right(
|
||||||
|
&mut self,
|
||||||
|
graph_fade_artwork_percentage: f64,
|
||||||
|
network_interface: &NodeNetworkInterface,
|
||||||
|
breadcrumb_network_path: &[NodeId],
|
||||||
|
navigation_handler: &NavigationMessageHandler,
|
||||||
|
) {
|
||||||
|
let Some(node_graph_ptz) = network_interface.node_graph_ptz(breadcrumb_network_path) else {
|
||||||
|
log::error!("Could not get node graph PTZ");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut widgets = vec![
|
||||||
|
NumberInput::new(Some(graph_fade_artwork_percentage))
|
||||||
|
.percentage()
|
||||||
|
.display_decimal_places(0)
|
||||||
|
.label("Fade Artwork")
|
||||||
|
.tooltip("Opacity of the graph background that covers the artwork")
|
||||||
|
.on_update(move |number_input: &NumberInput| {
|
||||||
|
DocumentMessage::SetGraphFadeArtwork {
|
||||||
|
percentage: number_input.value.unwrap_or(graph_fade_artwork_percentage),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.widget_holder(),
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
];
|
||||||
|
widgets.extend(navigation_controls(node_graph_ptz, navigation_handler, "Node Graph"));
|
||||||
|
widgets.extend([
|
||||||
|
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
||||||
|
TextButton::new("Node Graph")
|
||||||
|
.icon(Some("GraphViewOpen".into()))
|
||||||
|
.hover_icon(Some("GraphViewClosed".into()))
|
||||||
|
.tooltip("Hide Node Graph")
|
||||||
|
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
||||||
|
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
||||||
|
.widget_holder(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
self.widgets[1] = LayoutGroup::Row { widgets };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collate the properties panel sections for a node graph
|
/// Collate the properties panel sections for a node graph
|
||||||
|
|
@ -1587,7 +1678,18 @@ impl NodeGraphMessageHandler {
|
||||||
0 => {
|
0 => {
|
||||||
let selected_nodes = nodes
|
let selected_nodes = nodes
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|node_id| network.nodes.get(node_id).map(|node| node_properties::generate_node_properties(node, *node_id, false, context)))
|
.filter_map(|node_id| {
|
||||||
|
network.nodes.get(node_id).map(|node| {
|
||||||
|
let pinned = if let Some(node) = context.network_interface.node_metadata(node_id, context.selection_network_path) {
|
||||||
|
node.persistent_metadata.pinned
|
||||||
|
} else {
|
||||||
|
error!("Could not get node {node_id} in collate_properties");
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
node_properties::generate_node_properties(node, *node_id, pinned, context)
|
||||||
|
})
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if !selected_nodes.is_empty() {
|
if !selected_nodes.is_empty() {
|
||||||
return selected_nodes;
|
return selected_nodes;
|
||||||
|
|
@ -1618,7 +1720,7 @@ impl NodeGraphMessageHandler {
|
||||||
};
|
};
|
||||||
|
|
||||||
if pinned {
|
if pinned {
|
||||||
Some(node_properties::generate_node_properties(node, *node_id, true, context))
|
Some(node_properties::generate_node_properties(node, *node_id, pinned, context))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
@ -1655,23 +1757,22 @@ impl NodeGraphMessageHandler {
|
||||||
})
|
})
|
||||||
.widget_holder(),
|
.widget_holder(),
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
{
|
PopoverButton::new()
|
||||||
let node_chooser = NodeCatalog::new()
|
.icon(Some("Node".to_string()))
|
||||||
.on_update(move |node_type| {
|
.tooltip("Add an operation to the end of this layer's chain of nodes")
|
||||||
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
.popover_layout({
|
||||||
node_type: node_type.clone(),
|
let node_chooser = NodeCatalog::new()
|
||||||
layer: LayerNodeIdentifier::new_unchecked(layer),
|
.on_update(move |node_type| {
|
||||||
}
|
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||||
.into()
|
node_type: node_type.clone(),
|
||||||
})
|
layer: LayerNodeIdentifier::new_unchecked(layer),
|
||||||
.widget_holder();
|
}
|
||||||
let popover_layout = vec![LayoutGroup::Row { widgets: vec![node_chooser] }];
|
.into()
|
||||||
PopoverButton::new()
|
})
|
||||||
.icon(Some("Node".to_string()))
|
.widget_holder();
|
||||||
.tooltip("Add an operation to the end of this layer's chain of nodes")
|
vec![LayoutGroup::Row { widgets: vec![node_chooser] }]
|
||||||
.popover_layout(popover_layout)
|
})
|
||||||
.widget_holder()
|
.widget_holder(),
|
||||||
},
|
|
||||||
Separator::new(SeparatorType::Related).widget_holder(),
|
Separator::new(SeparatorType::Related).widget_holder(),
|
||||||
],
|
],
|
||||||
}];
|
}];
|
||||||
|
|
@ -1689,7 +1790,16 @@ impl NodeGraphMessageHandler {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter_map(|(_, node_id)| network.nodes.get(&node_id).map(|node| (node, node_id)))
|
.filter_map(|(_, node_id)| network.nodes.get(&node_id).map(|node| (node, node_id)))
|
||||||
.map(|(node, node_id)| node_properties::generate_node_properties(node, node_id, false, context))
|
.map(|(node, node_id)| {
|
||||||
|
let pinned = if let Some(node) = context.network_interface.node_metadata(&node_id, context.selection_network_path) {
|
||||||
|
node.persistent_metadata.pinned
|
||||||
|
} else {
|
||||||
|
error!("Could not get node {node_id} in collate_properties");
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
node_properties::generate_node_properties(node, node_id, pinned, context)
|
||||||
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
layer_properties.extend(node_properties);
|
layer_properties.extend(node_properties);
|
||||||
|
|
@ -2125,7 +2235,7 @@ fn frontend_inputs_lookup(breadcrumb_network_path: &[NodeId], network_interface:
|
||||||
// Skip not exposed inputs for efficiency
|
// Skip not exposed inputs for efficiency
|
||||||
let Some(value) = value else { continue };
|
let Some(value) = value else { continue };
|
||||||
|
|
||||||
// Resolve the type (done in a seperate loop because it requires a mutable reference to the `network_interface`)
|
// Resolve the type (done in a separate loop because it requires a mutable reference to the `network_interface`)
|
||||||
let (ty, type_source) = network_interface.input_type(&InputConnector::node(node_id, index), breadcrumb_network_path);
|
let (ty, type_source) = network_interface.input_type(&InputConnector::node(node_id, index), breadcrumb_network_path);
|
||||||
value.ty = ty;
|
value.ty = ty;
|
||||||
value.type_source = type_source;
|
value.type_source = type_source;
|
||||||
|
|
@ -2136,24 +2246,11 @@ fn frontend_inputs_lookup(breadcrumb_network_path: &[NodeId], network_interface:
|
||||||
|
|
||||||
impl Default for NodeGraphMessageHandler {
|
impl Default for NodeGraphMessageHandler {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let right_side_widgets = vec![
|
|
||||||
// TODO: Replace this with an "Add Node" button, also next to an "Add Layer" button
|
|
||||||
TextLabel::new("Right Click in Graph to Add Nodes").italic(true).widget_holder(),
|
|
||||||
Separator::new(SeparatorType::Unrelated).widget_holder(),
|
|
||||||
TextButton::new("Node Graph")
|
|
||||||
.icon(Some("GraphViewOpen".into()))
|
|
||||||
.hover_icon(Some("GraphViewClosed".into()))
|
|
||||||
.tooltip("Hide Node Graph")
|
|
||||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::GraphViewOverlayToggle))
|
|
||||||
.on_update(move |_| DocumentMessage::GraphViewOverlayToggle.into())
|
|
||||||
.widget_holder(),
|
|
||||||
];
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
network: Vec::new(),
|
network: Vec::new(),
|
||||||
node_graph_errors: Vec::new(),
|
node_graph_errors: Vec::new(),
|
||||||
has_selection: false,
|
has_selection: false,
|
||||||
widgets: [LayoutGroup::Row { widgets: Vec::new() }, LayoutGroup::Row { widgets: right_side_widgets }],
|
widgets: [LayoutGroup::Row { widgets: Vec::new() }, LayoutGroup::Row { widgets: Vec::new() }],
|
||||||
drag_start: None,
|
drag_start: None,
|
||||||
begin_dragging: false,
|
begin_dragging: false,
|
||||||
drag_occurred: false,
|
drag_occurred: false,
|
||||||
|
|
|
||||||
|
|
@ -2569,6 +2569,14 @@ impl NodeNetworkInterface {
|
||||||
&& (input_count <= 2)
|
&& (input_count <= 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn node_graph_ptz(&self, network_path: &[NodeId]) -> Option<&PTZ> {
|
||||||
|
let Some(network_metadata) = self.network_metadata(network_path) else {
|
||||||
|
log::error!("Could not get nested network_metadata in node_graph_ptz_mut");
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
Some(&network_metadata.persistent_metadata.navigation_metadata.node_graph_ptz)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn node_graph_ptz_mut(&mut self, network_path: &[NodeId]) -> Option<&mut PTZ> {
|
pub fn node_graph_ptz_mut(&mut self, network_path: &[NodeId]) -> Option<&mut PTZ> {
|
||||||
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
|
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
|
||||||
log::error!("Could not get nested network_metadata in node_graph_ptz_mut");
|
log::error!("Could not get nested network_metadata in node_graph_ptz_mut");
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions:
|
||||||
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
|
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
|
||||||
use crate::messages::portfolio::document::DocumentMessageData;
|
use crate::messages::portfolio::document::DocumentMessageData;
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
use crate::messages::tool::utility_types::{HintData, HintGroup};
|
use crate::messages::tool::utility_types::{HintData, HintGroup, ToolType};
|
||||||
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
|
use crate::node_graph_executor::{ExportConfig, NodeGraphExecutor};
|
||||||
|
|
||||||
use graph_craft::document::value::TaggedValue;
|
use graph_craft::document::value::TaggedValue;
|
||||||
|
|
@ -25,6 +25,7 @@ use std::vec;
|
||||||
pub struct PortfolioMessageData<'a> {
|
pub struct PortfolioMessageData<'a> {
|
||||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||||
pub preferences: &'a PreferencesMessageHandler,
|
pub preferences: &'a PreferencesMessageHandler,
|
||||||
|
pub current_tool: &'a ToolType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
|
@ -41,7 +42,7 @@ pub struct PortfolioMessageHandler {
|
||||||
|
|
||||||
impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMessageHandler {
|
impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMessageHandler {
|
||||||
fn process_message(&mut self, message: PortfolioMessage, responses: &mut VecDeque<Message>, data: PortfolioMessageData) {
|
fn process_message(&mut self, message: PortfolioMessage, responses: &mut VecDeque<Message>, data: PortfolioMessageData) {
|
||||||
let PortfolioMessageData { ipp, preferences } = data;
|
let PortfolioMessageData { ipp, preferences, current_tool } = data;
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
// Sub-messages
|
// Sub-messages
|
||||||
|
|
@ -73,6 +74,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||||
ipp,
|
ipp,
|
||||||
persistent_data: &self.persistent_data,
|
persistent_data: &self.persistent_data,
|
||||||
executor: &mut self.executor,
|
executor: &mut self.executor,
|
||||||
|
current_tool,
|
||||||
};
|
};
|
||||||
document.process_message(message, responses, document_inputs)
|
document.process_message(message, responses, document_inputs)
|
||||||
}
|
}
|
||||||
|
|
@ -87,6 +89,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||||
ipp,
|
ipp,
|
||||||
persistent_data: &self.persistent_data,
|
persistent_data: &self.persistent_data,
|
||||||
executor: &mut self.executor,
|
executor: &mut self.executor,
|
||||||
|
current_tool,
|
||||||
};
|
};
|
||||||
document.process_message(message, responses, document_inputs)
|
document.process_message(message, responses, document_inputs)
|
||||||
}
|
}
|
||||||
|
|
@ -754,10 +757,14 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
||||||
responses.add(OverlaysMessage::Draw);
|
responses.add(OverlaysMessage::Draw);
|
||||||
responses.add(BroadcastEvent::ToolAbort);
|
responses.add(BroadcastEvent::ToolAbort);
|
||||||
responses.add(BroadcastEvent::SelectionChanged);
|
responses.add(BroadcastEvent::SelectionChanged);
|
||||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
|
||||||
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
|
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
|
||||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||||
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
|
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
|
||||||
|
if node_graph_open {
|
||||||
|
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||||
|
} else {
|
||||||
|
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
PortfolioMessage::SubmitDocumentExport {
|
PortfolioMessage::SubmitDocumentExport {
|
||||||
file_name,
|
file_name,
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ pub struct ToolMessageHandler {
|
||||||
pub tool_state: ToolFsmState,
|
pub tool_state: ToolFsmState,
|
||||||
pub transform_layer_handler: TransformLayerMessageHandler,
|
pub transform_layer_handler: TransformLayerMessageHandler,
|
||||||
pub shape_editor: ShapeState,
|
pub shape_editor: ShapeState,
|
||||||
|
pub tool_is_active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
||||||
|
|
@ -69,9 +70,10 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
||||||
let old_tool = tool_data.active_tool_type;
|
let old_tool = tool_data.active_tool_type;
|
||||||
|
|
||||||
// Do nothing if switching to the same tool
|
// Do nothing if switching to the same tool
|
||||||
if tool_type == old_tool {
|
if self.tool_is_active && tool_type == old_tool {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
self.tool_is_active = true;
|
||||||
|
|
||||||
// Send the old and new tools a transition to their FSM Abort states
|
// Send the old and new tools a transition to their FSM Abort states
|
||||||
let mut send_abort_to_tool = |tool_type, update_hints_and_cursor: bool| {
|
let mut send_abort_to_tool = |tool_type, update_hints_and_cursor: bool| {
|
||||||
|
|
@ -85,6 +87,7 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
||||||
shape_editor: &mut self.shape_editor,
|
shape_editor: &mut self.shape_editor,
|
||||||
node_graph,
|
node_graph,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(tool_abort_message) = tool.event_to_message_map().tool_abort {
|
if let Some(tool_abort_message) = tool.event_to_message_map().tool_abort {
|
||||||
tool.process_message(tool_abort_message, responses, &mut data);
|
tool.process_message(tool_abort_message, responses, &mut data);
|
||||||
}
|
}
|
||||||
|
|
@ -133,6 +136,11 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
||||||
responses.add(BroadcastMessage::UnsubscribeEvent { message, on });
|
responses.add(BroadcastMessage::UnsubscribeEvent { message, on });
|
||||||
|
|
||||||
responses.add(OverlaysMessage::RemoveProvider(ARTBOARD_OVERLAY_PROVIDER));
|
responses.add(OverlaysMessage::RemoveProvider(ARTBOARD_OVERLAY_PROVIDER));
|
||||||
|
|
||||||
|
responses.add(FrontendMessage::UpdateInputHints { hint_data: Default::default() });
|
||||||
|
responses.add(FrontendMessage::UpdateMouseCursor { cursor: Default::default() });
|
||||||
|
|
||||||
|
self.tool_is_active = false;
|
||||||
}
|
}
|
||||||
ToolMessage::InitTools => {
|
ToolMessage::InitTools => {
|
||||||
// Subscribe the transform layer to selection change events
|
// Subscribe the transform layer to selection change events
|
||||||
|
|
@ -141,6 +149,8 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
||||||
send: Box::new(TransformLayerMessage::SelectionChanged.into()),
|
send: Box::new(TransformLayerMessage::SelectionChanged.into()),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.tool_is_active = true;
|
||||||
|
|
||||||
let tool_data = &mut self.tool_state.tool_data;
|
let tool_data = &mut self.tool_state.tool_data;
|
||||||
let document_data = &self.tool_state.document_tool_data;
|
let document_data = &self.tool_state.document_tool_data;
|
||||||
let active_tool = &tool_data.active_tool_type;
|
let active_tool = &tool_data.active_tool_type;
|
||||||
|
|
|
||||||
|
|
@ -539,7 +539,6 @@ impl Fsm for PenToolFsmState {
|
||||||
// Perform extension of an existing path
|
// Perform extension of an existing path
|
||||||
let selected_nodes = document.network_interface.selected_nodes(&[]).unwrap();
|
let selected_nodes = document.network_interface.selected_nodes(&[]).unwrap();
|
||||||
if let Some((layer, point, position)) = should_extend(document, viewport, crate::consts::SNAP_POINT_TOLERANCE, selected_nodes.selected_layers(document.metadata())) {
|
if let Some((layer, point, position)) = should_extend(document, viewport, crate::consts::SNAP_POINT_TOLERANCE, selected_nodes.selected_layers(document.metadata())) {
|
||||||
log::debug!("Should extend: {:?}", layer);
|
|
||||||
tool_data.add_point(LastPoint {
|
tool_data.add_point(LastPoint {
|
||||||
id: point,
|
id: point,
|
||||||
pos: position,
|
pos: position,
|
||||||
|
|
@ -550,7 +549,6 @@ impl Fsm for PenToolFsmState {
|
||||||
tool_data.next_point = position;
|
tool_data.next_point = position;
|
||||||
tool_data.next_handle_start = position;
|
tool_data.next_handle_start = position;
|
||||||
} else if let (Some(layer), None) = (selected_layers.next(), selected_layers.next()) {
|
} else if let (Some(layer), None) = (selected_layers.next(), selected_layers.next()) {
|
||||||
log::debug!("Adding to layer: {:?}", layer);
|
|
||||||
// Add the first point to a new layer
|
// Add the first point to a new layer
|
||||||
// Generate first point
|
// Generate first point
|
||||||
let id = PointId::generate();
|
let id = PointId::generate();
|
||||||
|
|
@ -566,7 +564,6 @@ impl Fsm for PenToolFsmState {
|
||||||
tool_data.next_point = pos;
|
tool_data.next_point = pos;
|
||||||
tool_data.next_handle_start = pos;
|
tool_data.next_handle_start = pos;
|
||||||
} else {
|
} else {
|
||||||
log::debug!("Creating new layer");
|
|
||||||
// New path layer
|
// New path layer
|
||||||
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||||
let nodes = vec![(NodeId(0), node_type.default_node_template())];
|
let nodes = vec![(NodeId(0), node_type.default_node_template())];
|
||||||
|
|
|
||||||
|
|
@ -477,7 +477,7 @@ pub fn tool_type_to_activate_tool_message(tool_type: ToolType) -> ToolMessageDis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
pub struct HintData(pub Vec<HintGroup>);
|
pub struct HintData(pub Vec<HintGroup>);
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||||
|
|
|
||||||
|
|
@ -504,7 +504,7 @@
|
||||||
<canvas class="overlays" width={canvasWidthRoundedToEven} height={canvasHeightRoundedToEven} style:width={canvasWidthCSS} style:height={canvasHeightCSS} data-overlays-canvas>
|
<canvas class="overlays" width={canvasWidthRoundedToEven} height={canvasHeightRoundedToEven} style:width={canvasWidthCSS} style:height={canvasHeightCSS} data-overlays-canvas>
|
||||||
</canvas>
|
</canvas>
|
||||||
</div>
|
</div>
|
||||||
<div class="graph-view" class:open={$document.graphViewOverlayOpen} style:--fade-artwork="80%" data-graph>
|
<div class="graph-view" class:open={$document.graphViewOverlayOpen} style:--fade-artwork={`${$document.fadeArtwork}%`} data-graph>
|
||||||
<Graph />
|
<Graph />
|
||||||
</div>
|
</div>
|
||||||
</LayoutCol>
|
</LayoutCol>
|
||||||
|
|
|
||||||
|
|
@ -989,7 +989,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled {
|
&.disabled {
|
||||||
background: var(--color-3-darkgray);
|
background: rgba(var(--color-4-dimgray-rgb), 0.33);
|
||||||
color: var(--color-a-softgray);
|
color: var(--color-a-softgray);
|
||||||
|
|
||||||
.icon-label {
|
.icon-label {
|
||||||
|
|
@ -1041,10 +1041,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
background: rgba(var(--color-5-dullgray-rgb), 0.5);
|
background: rgba(var(--color-5-dullgray-rgb), 0.33);
|
||||||
|
|
||||||
&.in-selected-network {
|
&.in-selected-network {
|
||||||
background: rgba(var(--color-6-lowergray-rgb), 0.5);
|
background: rgba(var(--color-6-lowergray-rgb), 0.33);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,18 +27,16 @@
|
||||||
<button class="header" class:expanded on:click|stopPropagation={() => (expanded = !expanded)} tabindex="0">
|
<button class="header" class:expanded on:click|stopPropagation={() => (expanded = !expanded)} tabindex="0">
|
||||||
<div class="expand-arrow" />
|
<div class="expand-arrow" />
|
||||||
<TextLabel bold={true}>{widgetData.name}</TextLabel>
|
<TextLabel bold={true}>{widgetData.name}</TextLabel>
|
||||||
{#if widgetData.pinned}
|
<IconButton
|
||||||
<IconButton
|
icon={widgetData.pinned ? "CheckboxChecked" : "CheckboxUnchecked"}
|
||||||
icon={"CheckboxChecked"}
|
tooltip={widgetData.pinned ? "Unpin this node so it's no longer shown here when nothing is selected" : "Pin this node so it's shown here when nothing is selected"}
|
||||||
tooltip={"Unpin this node so it's no longer shown here without a selection"}
|
size={24}
|
||||||
size={24}
|
action={(e) => {
|
||||||
action={(e) => {
|
editor.handle.setNodePinned(widgetData.id, !widgetData.pinned);
|
||||||
editor.handle.unpinNode(widgetData.id);
|
e?.stopPropagation();
|
||||||
e?.stopPropagation();
|
}}
|
||||||
}}
|
class={"show-only-on-hover"}
|
||||||
class={"show-only-on-hover"}
|
/>
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={"Trash"}
|
icon={"Trash"}
|
||||||
tooltip={"Delete this node from the layer chain"}
|
tooltip={"Delete this node from the layer chain"}
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,9 @@ import {
|
||||||
UpdateToolShelfLayout,
|
UpdateToolShelfLayout,
|
||||||
UpdateWorkingColorsLayout,
|
UpdateWorkingColorsLayout,
|
||||||
UpdateNodeGraphBarLayout,
|
UpdateNodeGraphBarLayout,
|
||||||
TriggerGraphViewOverlay,
|
UpdateGraphViewOverlay,
|
||||||
TriggerDelayedZoomCanvasToFitAll,
|
TriggerDelayedZoomCanvasToFitAll,
|
||||||
|
UpdateGraphFadeArtwork,
|
||||||
} from "@graphite/wasm-communication/messages";
|
} from "@graphite/wasm-communication/messages";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
|
|
@ -27,10 +28,17 @@ export function createDocumentState(editor: Editor) {
|
||||||
nodeGraphBarLayout: defaultWidgetLayout(),
|
nodeGraphBarLayout: defaultWidgetLayout(),
|
||||||
// Graph view overlay
|
// Graph view overlay
|
||||||
graphViewOverlayOpen: false,
|
graphViewOverlayOpen: false,
|
||||||
|
fadeArtwork: 100,
|
||||||
});
|
});
|
||||||
const { subscribe, update } = state;
|
const { subscribe, update } = state;
|
||||||
|
|
||||||
// Update layouts
|
// Update layouts
|
||||||
|
editor.subscriptions.subscribeJsMessage(UpdateGraphFadeArtwork, (updateGraphFadeArtwork) => {
|
||||||
|
update((state) => {
|
||||||
|
state.fadeArtwork = updateGraphFadeArtwork.percentage;
|
||||||
|
return state;
|
||||||
|
});
|
||||||
|
});
|
||||||
editor.subscriptions.subscribeJsMessage(UpdateDocumentModeLayout, async (updateDocumentModeLayout) => {
|
editor.subscriptions.subscribeJsMessage(UpdateDocumentModeLayout, async (updateDocumentModeLayout) => {
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
|
|
@ -84,9 +92,9 @@ export function createDocumentState(editor: Editor) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show or hide the graph view overlay
|
// Show or hide the graph view overlay
|
||||||
editor.subscriptions.subscribeJsMessage(TriggerGraphViewOverlay, (triggerGraphViewOverlay) => {
|
editor.subscriptions.subscribeJsMessage(UpdateGraphViewOverlay, (updateGraphViewOverlay) => {
|
||||||
update((state) => {
|
update((state) => {
|
||||||
state.graphViewOverlayOpen = triggerGraphViewOverlay.open;
|
state.graphViewOverlayOpen = updateGraphViewOverlay.open;
|
||||||
return state;
|
return state;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -742,6 +742,14 @@ const mouseCursorIconCSSNames = {
|
||||||
export type MouseCursor = keyof typeof mouseCursorIconCSSNames;
|
export type MouseCursor = keyof typeof mouseCursorIconCSSNames;
|
||||||
export type MouseCursorIcon = (typeof mouseCursorIconCSSNames)[MouseCursor];
|
export type MouseCursorIcon = (typeof mouseCursorIconCSSNames)[MouseCursor];
|
||||||
|
|
||||||
|
export class UpdateGraphViewOverlay extends JsMessage {
|
||||||
|
open!: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateGraphFadeArtwork extends JsMessage {
|
||||||
|
readonly percentage!: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class UpdateMouseCursor extends JsMessage {
|
export class UpdateMouseCursor extends JsMessage {
|
||||||
@Transform(({ value }: { value: MouseCursor }) => mouseCursorIconCSSNames[value] || "alias")
|
@Transform(({ value }: { value: MouseCursor }) => mouseCursorIconCSSNames[value] || "alias")
|
||||||
readonly cursor!: MouseCursorIcon;
|
readonly cursor!: MouseCursorIcon;
|
||||||
|
|
@ -888,10 +896,6 @@ export class TriggerFontLoad extends JsMessage {
|
||||||
isDefault!: boolean;
|
isDefault!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerGraphViewOverlay extends JsMessage {
|
|
||||||
open!: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TriggerVisitLink extends JsMessage {
|
export class TriggerVisitLink extends JsMessage {
|
||||||
url!: string;
|
url!: string;
|
||||||
}
|
}
|
||||||
|
|
@ -1561,12 +1565,11 @@ export const messageMakers: Record<string, MessageMaker> = {
|
||||||
TriggerAboutGraphiteLocalizedCommitDate,
|
TriggerAboutGraphiteLocalizedCommitDate,
|
||||||
TriggerCopyToClipboardBlobUrl,
|
TriggerCopyToClipboardBlobUrl,
|
||||||
TriggerDelayedZoomCanvasToFitAll,
|
TriggerDelayedZoomCanvasToFitAll,
|
||||||
TriggerFetchAndOpenDocument,
|
|
||||||
TriggerDownloadBlobUrl,
|
TriggerDownloadBlobUrl,
|
||||||
TriggerDownloadImage,
|
TriggerDownloadImage,
|
||||||
TriggerDownloadTextFile,
|
TriggerDownloadTextFile,
|
||||||
|
TriggerFetchAndOpenDocument,
|
||||||
TriggerFontLoad,
|
TriggerFontLoad,
|
||||||
TriggerGraphViewOverlay,
|
|
||||||
TriggerImport,
|
TriggerImport,
|
||||||
TriggerIndexedDbRemoveDocument,
|
TriggerIndexedDbRemoveDocument,
|
||||||
TriggerIndexedDbWriteDocument,
|
TriggerIndexedDbWriteDocument,
|
||||||
|
|
@ -1584,9 +1587,6 @@ export const messageMakers: Record<string, MessageMaker> = {
|
||||||
UpdateBox,
|
UpdateBox,
|
||||||
UpdateClickTargets,
|
UpdateClickTargets,
|
||||||
UpdateContextMenuInformation,
|
UpdateContextMenuInformation,
|
||||||
UpdateInSelectedNetwork,
|
|
||||||
UpdateImportsExports,
|
|
||||||
UpdateLayerWidths,
|
|
||||||
UpdateDialogButtons,
|
UpdateDialogButtons,
|
||||||
UpdateDialogColumn1,
|
UpdateDialogColumn1,
|
||||||
UpdateDialogColumn2,
|
UpdateDialogColumn2,
|
||||||
|
|
@ -1598,8 +1598,13 @@ export const messageMakers: Record<string, MessageMaker> = {
|
||||||
UpdateDocumentRulers,
|
UpdateDocumentRulers,
|
||||||
UpdateDocumentScrollbars,
|
UpdateDocumentScrollbars,
|
||||||
UpdateEyedropperSamplingState,
|
UpdateEyedropperSamplingState,
|
||||||
|
UpdateGraphFadeArtwork,
|
||||||
|
UpdateGraphViewOverlay,
|
||||||
|
UpdateImportsExports,
|
||||||
UpdateInputHints,
|
UpdateInputHints,
|
||||||
|
UpdateInSelectedNetwork,
|
||||||
UpdateLayersPanelOptionsLayout,
|
UpdateLayersPanelOptionsLayout,
|
||||||
|
UpdateLayerWidths,
|
||||||
UpdateMenuBarLayout,
|
UpdateMenuBarLayout,
|
||||||
UpdateMouseCursor,
|
UpdateMouseCursor,
|
||||||
UpdateNodeGraph,
|
UpdateNodeGraph,
|
||||||
|
|
@ -1611,8 +1616,8 @@ export const messageMakers: Record<string, MessageMaker> = {
|
||||||
UpdatePropertyPanelSectionsLayout,
|
UpdatePropertyPanelSectionsLayout,
|
||||||
UpdateToolOptionsLayout,
|
UpdateToolOptionsLayout,
|
||||||
UpdateToolShelfLayout,
|
UpdateToolShelfLayout,
|
||||||
UpdateWorkingColorsLayout,
|
|
||||||
UpdateWirePathInProgress,
|
UpdateWirePathInProgress,
|
||||||
|
UpdateWorkingColorsLayout,
|
||||||
UpdateZoomWithScroll,
|
UpdateZoomWithScroll,
|
||||||
} as const;
|
} as const;
|
||||||
export type JsMessageType = keyof typeof messageMakers;
|
export type JsMessageType = keyof typeof messageMakers;
|
||||||
|
|
|
||||||
|
|
@ -577,8 +577,7 @@ impl EditorHandle {
|
||||||
let message = NodeGraphMessage::CreateNodeFromContextMenu {
|
let message = NodeGraphMessage::CreateNodeFromContextMenu {
|
||||||
node_id: Some(id),
|
node_id: Some(id),
|
||||||
node_type,
|
node_type,
|
||||||
x: x / 24,
|
xy: Some((x / 24, y / 24)),
|
||||||
y: y / 24,
|
|
||||||
};
|
};
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
@ -652,10 +651,10 @@ impl EditorHandle {
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unpin a node given its node ID
|
/// Pin or unpin a node given its node ID
|
||||||
#[wasm_bindgen(js_name = unpinNode)]
|
#[wasm_bindgen(js_name = setNodePinned)]
|
||||||
pub fn unpin_node(&self, id: u64) {
|
pub fn set_node_pinned(&self, id: u64, pinned: bool) {
|
||||||
self.dispatch(DocumentMessage::SetNodePinned { node_id: NodeId(id), pinned: false });
|
self.dispatch(DocumentMessage::SetNodePinned { node_id: NodeId(id), pinned });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delete a layer or node given its node ID
|
/// Delete a layer or node given its node ID
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue