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) => {
|
||||
let ipp = &self.message_handlers.input_preprocessor_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
|
||||
.portfolio_message_handler
|
||||
.process_message(message, &mut queue, PortfolioMessageData { ipp, preferences });
|
||||
.process_message(message, &mut queue, PortfolioMessageData { ipp, preferences, current_tool });
|
||||
}
|
||||
Message::Preferences(message) => {
|
||||
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.key_mapping_message_handler.actions());
|
||||
list.extend(self.message_handlers.debug_message_handler.actions());
|
||||
if self.message_handlers.portfolio_message_handler.active_document().is_some() {
|
||||
list.extend(self.message_handlers.tool_message_handler.actions());
|
||||
if let Some(document) = self.message_handlers.portfolio_message_handler.active_document() {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -84,9 +84,6 @@ pub enum FrontendMessage {
|
|||
#[serde(rename = "isDefault")]
|
||||
is_default: bool,
|
||||
},
|
||||
TriggerGraphViewOverlay {
|
||||
open: bool,
|
||||
},
|
||||
TriggerImport,
|
||||
TriggerIndexedDbRemoveDocument {
|
||||
#[serde(rename = "documentId")]
|
||||
|
|
@ -153,6 +150,9 @@ pub enum FrontendMessage {
|
|||
#[serde(rename = "clickTargets")]
|
||||
click_targets: Option<FrontendClickTargets>,
|
||||
},
|
||||
UpdateGraphViewOverlay {
|
||||
open: bool,
|
||||
},
|
||||
UpdateLayerWidths {
|
||||
#[serde(rename = "layerWidths")]
|
||||
layer_widths: HashMap<NodeId, u32>,
|
||||
|
|
@ -221,6 +221,9 @@ pub enum FrontendMessage {
|
|||
#[serde(rename = "setColorChoice")]
|
||||
set_color_choice: Option<String>,
|
||||
},
|
||||
UpdateGraphFadeArtwork {
|
||||
percentage: f64,
|
||||
},
|
||||
UpdateInputHints {
|
||||
#[serde(rename = "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)
|
||||
entry!(KeyDown(KeyZ); modifiers=[Accel, MouseLeft], action_dispatch=DocumentMessage::Noop),
|
||||
//
|
||||
// NodeGraphMessage
|
||||
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}),
|
||||
|
|
@ -69,6 +70,8 @@ pub fn input_mappings() -> Mapping {
|
|||
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=NodeGraphMessage::Cut),
|
||||
entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=NodeGraphMessage::Copy),
|
||||
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(KeyC); modifiers=[Shift], action_dispatch=NodeGraphMessage::PrintSelectedNodeCoordinates),
|
||||
entry!(KeyDown(KeyC); modifiers=[Alt], action_dispatch=NodeGraphMessage::SendClickTargets),
|
||||
|
|
|
|||
|
|
@ -131,6 +131,9 @@ pub enum DocumentMessage {
|
|||
SetBlendModeForSelectedLayers {
|
||||
blend_mode: BlendMode,
|
||||
},
|
||||
SetGraphFadeArtwork {
|
||||
percentage: f64,
|
||||
},
|
||||
SetNodePinned {
|
||||
node_id: NodeId,
|
||||
pinned: bool,
|
||||
|
|
|
|||
|
|
@ -39,44 +39,7 @@ pub struct DocumentMessageData<'a> {
|
|||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
pub persistent_data: &'a PersistentData,
|
||||
pub executor: &'a mut NodeGraphExecutor,
|
||||
}
|
||||
|
||||
// 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,
|
||||
pub current_tool: &'a ToolType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
|
|
@ -121,10 +84,12 @@ pub struct DocumentMessageHandler {
|
|||
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,
|
||||
/// 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
|
||||
|
|
@ -178,6 +143,7 @@ impl Default for DocumentMessageHandler {
|
|||
rulers_visible: true,
|
||||
graph_view_overlay_open: false,
|
||||
snapping_state: SnappingState::default(),
|
||||
graph_fade_artwork_percentage: 80.,
|
||||
// =============================================
|
||||
// Fields omitted from the saved document format
|
||||
// =============================================
|
||||
|
|
@ -199,6 +165,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||
ipp,
|
||||
persistent_data,
|
||||
executor,
|
||||
current_tool,
|
||||
} = data;
|
||||
|
||||
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,
|
||||
ipp,
|
||||
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 } => {
|
||||
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
|
||||
responses.add(MenuBarMessage::SendLayout);
|
||||
|
||||
responses.add(DocumentMessage::RenderRulers);
|
||||
responses.add(DocumentMessage::RenderScrollbars);
|
||||
if open {
|
||||
responses.add(ToolMessage::DeactivateTools);
|
||||
responses.add(OverlaysMessage::Draw); // Clear the overlays
|
||||
responses.add(NavigationMessage::CanvasTiltSet { angle_radians: 0. });
|
||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
} else {
|
||||
responses.add(ToolMessage::ActivateTool { tool_type: *current_tool });
|
||||
}
|
||||
}
|
||||
DocumentMessage::GraphViewOverlayToggle => {
|
||||
|
|
@ -1019,6 +998,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||
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 } => {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
responses.add(NodeGraphMessage::SetPinned { node_id, pinned });
|
||||
|
|
@ -1338,11 +1321,13 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
|
|||
SelectedLayersRaise,
|
||||
SelectedLayersRaiseToFront,
|
||||
UngroupSelectedLayers,
|
||||
ToggleSelectedVisibility,
|
||||
ToggleSelectedLocked
|
||||
);
|
||||
if !self.graph_view_overlay_open {
|
||||
select.extend(actions!(DocumentMessageDiscriminant; NudgeSelectedLayers));
|
||||
select.extend(actions!(DocumentMessageDiscriminant;
|
||||
NudgeSelectedLayers,
|
||||
ToggleSelectedVisibility,
|
||||
));
|
||||
}
|
||||
common.extend(select);
|
||||
}
|
||||
|
|
@ -1825,68 +1810,10 @@ impl DocumentMessageHandler {
|
|||
])
|
||||
.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.);
|
||||
if tilt_value.abs() > 0.00001 {
|
||||
widgets.extend([
|
||||
|
|
@ -2059,15 +1986,15 @@ impl DocumentMessageHandler {
|
|||
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))
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedLocked))
|
||||
.on_update(|_| NodeGraphMessage::ToggleSelectedLocked.into())
|
||||
.disabled(!has_selection)
|
||||
.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())
|
||||
.tooltip_shortcut(action_keys!(DocumentMessageDiscriminant::ToggleSelectedVisibility))
|
||||
.on_update(|_| DocumentMessage::ToggleSelectedVisibility.into())
|
||||
.disabled(!has_selection)
|
||||
.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> {
|
||||
type Item = LayerNodeIdentifier;
|
||||
|
||||
|
|
@ -2266,3 +2258,41 @@ impl<'a> Iterator for ClickXRayIter<'a> {
|
|||
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.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(NodeGraphMessage::SetGridAlignedEdges);
|
||||
}
|
||||
|
|
@ -192,6 +196,9 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
|||
};
|
||||
ptz.tilt = angle_radians;
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
if !graph_view_overlay_open {
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
}
|
||||
}
|
||||
NavigationMessage::CanvasZoomDecrease { center_on_mouse } => {
|
||||
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 * Self::clamp_zoom(zoom, document_bounds, old_zoom, ipp);
|
||||
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(NodeGraphMessage::SetGridAlignedEdges);
|
||||
}
|
||||
|
|
@ -281,6 +292,11 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
|||
ptz.tilt = self.snapped_tilt(ptz.tilt);
|
||||
ptz.set_zoom(self.snapped_zoom(ptz.zoom()));
|
||||
responses.add(DocumentMessage::PTZUpdate);
|
||||
if graph_view_overlay_open {
|
||||
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||
} else {
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
}
|
||||
responses.add(NodeGraphMessage::SetGridAlignedEdges);
|
||||
// Reset the navigation operation now that it's done
|
||||
self.navigation_operation = NavigationOperation::None;
|
||||
|
|
@ -336,7 +352,11 @@ impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for Navigation
|
|||
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(NodeGraphMessage::SetGridAlignedEdges);
|
||||
}
|
||||
|
|
@ -478,11 +498,7 @@ impl NavigationMessageHandler {
|
|||
}
|
||||
|
||||
pub fn snapped_zoom(&self, zoom: f64) -> f64 {
|
||||
if matches!(self.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
|
||||
}
|
||||
snapped_zoom(&self.navigation_operation, zoom)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
node_id: Option<NodeId>,
|
||||
node_type: String,
|
||||
x: i32,
|
||||
y: i32,
|
||||
xy: Option<(i32, i32)>,
|
||||
},
|
||||
CreateWire {
|
||||
output_connector: OutputConnector,
|
||||
|
|
@ -182,6 +181,7 @@ pub enum NodeGraphMessage {
|
|||
node_graph_errors: GraphErrors,
|
||||
},
|
||||
UpdateActionButtons,
|
||||
UpdateGraphBarRight,
|
||||
UpdateInSelectedNetwork,
|
||||
SendSelectedNodes,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use super::utility_types::{BoxSelection, ContextMenuInformation, DragStart, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeWire, WirePath};
|
||||
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::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::node_graph::document_node_definitions::NodePropertiesContext;
|
||||
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 ipp: &'a InputPreprocessorMessageHandler,
|
||||
pub graph_view_overlay_open: bool,
|
||||
pub graph_fade_artwork_percentage: f64,
|
||||
pub navigation_handler: &'a NavigationMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -76,8 +80,10 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
breadcrumb_network_path,
|
||||
document_id,
|
||||
collapsed,
|
||||
graph_view_overlay_open,
|
||||
ipp,
|
||||
graph_view_overlay_open,
|
||||
graph_fade_artwork_percentage,
|
||||
navigation_handler,
|
||||
} = data;
|
||||
|
||||
match message {
|
||||
|
|
@ -149,7 +155,15 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
responses.add(PropertiesPanelMessage::Refresh);
|
||||
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 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 => {
|
||||
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 {
|
||||
in_selected_network: selection_network_path == breadcrumb_network_path,
|
||||
}),
|
||||
|
|
@ -1435,7 +1454,7 @@ impl NodeGraphMessageHandler {
|
|||
}
|
||||
|
||||
/// 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 {
|
||||
// If a node in a nested network could not be found, exit the nested network
|
||||
let breadcrumb_network_path_len = breadcrumb_network_path.len();
|
||||
|
|
@ -1457,100 +1476,172 @@ impl NodeGraphMessageHandler {
|
|||
return;
|
||||
};
|
||||
|
||||
let subgraph_path_names_length = subgraph_path_names.len();
|
||||
|
||||
let breadcrumb_trail = BreadcrumbTrailButtons::new(subgraph_path_names).on_update(move |index| {
|
||||
DocumentMessage::ExitNestedNetwork {
|
||||
steps_back: subgraph_path_names_length - (*index as usize) - 1,
|
||||
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 selection_all_locked = network_interface.selected_nodes(&[]).unwrap().selected_unlocked_layers(network_interface).count() == 0;
|
||||
let selection_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
|
||||
}
|
||||
.into()
|
||||
});
|
||||
|
||||
let mut widgets = if subgraph_path_names_length >= 2 {
|
||||
vec![breadcrumb_trail.widget_holder(), Separator::new(SeparatorType::Unrelated).widget_holder()]
|
||||
let mut widgets = vec![
|
||||
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 {
|
||||
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 let (Some(&node_id), None) = (selection.next(), selection.next()) {
|
||||
// Is this node the current output
|
||||
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" })
|
||||
if let Some(node_id) = previewing {
|
||||
let button = TextButton::new("End Preview")
|
||||
.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())
|
||||
.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.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
|
||||
|
|
@ -1587,7 +1678,18 @@ impl NodeGraphMessageHandler {
|
|||
0 => {
|
||||
let selected_nodes = nodes
|
||||
.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<_>>();
|
||||
if !selected_nodes.is_empty() {
|
||||
return selected_nodes;
|
||||
|
|
@ -1618,7 +1720,7 @@ impl NodeGraphMessageHandler {
|
|||
};
|
||||
|
||||
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 {
|
||||
None
|
||||
}
|
||||
|
|
@ -1655,23 +1757,22 @@ impl NodeGraphMessageHandler {
|
|||
})
|
||||
.widget_holder(),
|
||||
Separator::new(SeparatorType::Related).widget_holder(),
|
||||
{
|
||||
let node_chooser = NodeCatalog::new()
|
||||
.on_update(move |node_type| {
|
||||
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||
node_type: node_type.clone(),
|
||||
layer: LayerNodeIdentifier::new_unchecked(layer),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_holder();
|
||||
let popover_layout = vec![LayoutGroup::Row { widgets: vec![node_chooser] }];
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.tooltip("Add an operation to the end of this layer's chain of nodes")
|
||||
.popover_layout(popover_layout)
|
||||
.widget_holder()
|
||||
},
|
||||
PopoverButton::new()
|
||||
.icon(Some("Node".to_string()))
|
||||
.tooltip("Add an operation to the end of this layer's chain of nodes")
|
||||
.popover_layout({
|
||||
let node_chooser = NodeCatalog::new()
|
||||
.on_update(move |node_type| {
|
||||
NodeGraphMessage::CreateNodeInLayerWithTransaction {
|
||||
node_type: node_type.clone(),
|
||||
layer: LayerNodeIdentifier::new_unchecked(layer),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
.widget_holder();
|
||||
vec![LayoutGroup::Row { widgets: vec![node_chooser] }]
|
||||
})
|
||||
.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)))
|
||||
.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<_>>();
|
||||
|
||||
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
|
||||
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);
|
||||
value.ty = ty;
|
||||
value.type_source = type_source;
|
||||
|
|
@ -2136,24 +2246,11 @@ fn frontend_inputs_lookup(breadcrumb_network_path: &[NodeId], network_interface:
|
|||
|
||||
impl Default for NodeGraphMessageHandler {
|
||||
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 {
|
||||
network: Vec::new(),
|
||||
node_graph_errors: Vec::new(),
|
||||
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,
|
||||
begin_dragging: false,
|
||||
drag_occurred: false,
|
||||
|
|
|
|||
|
|
@ -2569,6 +2569,14 @@ impl NodeNetworkInterface {
|
|||
&& (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> {
|
||||
let Some(network_metadata) = self.network_metadata_mut(network_path) else {
|
||||
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::DocumentMessageData;
|
||||
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 graph_craft::document::value::TaggedValue;
|
||||
|
|
@ -25,6 +25,7 @@ use std::vec;
|
|||
pub struct PortfolioMessageData<'a> {
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
pub current_tool: &'a ToolType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
|
@ -41,7 +42,7 @@ pub struct PortfolioMessageHandler {
|
|||
|
||||
impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMessageHandler {
|
||||
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 {
|
||||
// Sub-messages
|
||||
|
|
@ -73,6 +74,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||
ipp,
|
||||
persistent_data: &self.persistent_data,
|
||||
executor: &mut self.executor,
|
||||
current_tool,
|
||||
};
|
||||
document.process_message(message, responses, document_inputs)
|
||||
}
|
||||
|
|
@ -87,6 +89,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||
ipp,
|
||||
persistent_data: &self.persistent_data,
|
||||
executor: &mut self.executor,
|
||||
current_tool,
|
||||
};
|
||||
document.process_message(message, responses, document_inputs)
|
||||
}
|
||||
|
|
@ -754,10 +757,14 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMes
|
|||
responses.add(OverlaysMessage::Draw);
|
||||
responses.add(BroadcastEvent::ToolAbort);
|
||||
responses.add(BroadcastEvent::SelectionChanged);
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
responses.add(NavigationMessage::CanvasPan { delta: (0., 0.).into() });
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
responses.add(DocumentMessage::GraphViewOverlay { open: node_graph_open });
|
||||
if node_graph_open {
|
||||
responses.add(NodeGraphMessage::UpdateGraphBarRight);
|
||||
} else {
|
||||
responses.add(PortfolioMessage::UpdateDocumentWidgets);
|
||||
}
|
||||
}
|
||||
PortfolioMessage::SubmitDocumentExport {
|
||||
file_name,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ pub struct ToolMessageHandler {
|
|||
pub tool_state: ToolFsmState,
|
||||
pub transform_layer_handler: TransformLayerMessageHandler,
|
||||
pub shape_editor: ShapeState,
|
||||
pub tool_is_active: bool,
|
||||
}
|
||||
|
||||
impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
||||
|
|
@ -69,9 +70,10 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
|||
let old_tool = tool_data.active_tool_type;
|
||||
|
||||
// Do nothing if switching to the same tool
|
||||
if tool_type == old_tool {
|
||||
if self.tool_is_active && tool_type == old_tool {
|
||||
return;
|
||||
}
|
||||
self.tool_is_active = true;
|
||||
|
||||
// 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| {
|
||||
|
|
@ -85,6 +87,7 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
|||
shape_editor: &mut self.shape_editor,
|
||||
node_graph,
|
||||
};
|
||||
|
||||
if let Some(tool_abort_message) = tool.event_to_message_map().tool_abort {
|
||||
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(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 => {
|
||||
// Subscribe the transform layer to selection change events
|
||||
|
|
@ -141,6 +149,8 @@ impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
|||
send: Box::new(TransformLayerMessage::SelectionChanged.into()),
|
||||
});
|
||||
|
||||
self.tool_is_active = true;
|
||||
|
||||
let tool_data = &mut self.tool_state.tool_data;
|
||||
let document_data = &self.tool_state.document_tool_data;
|
||||
let active_tool = &tool_data.active_tool_type;
|
||||
|
|
|
|||
|
|
@ -539,7 +539,6 @@ impl Fsm for PenToolFsmState {
|
|||
// Perform extension of an existing path
|
||||
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())) {
|
||||
log::debug!("Should extend: {:?}", layer);
|
||||
tool_data.add_point(LastPoint {
|
||||
id: point,
|
||||
pos: position,
|
||||
|
|
@ -550,7 +549,6 @@ impl Fsm for PenToolFsmState {
|
|||
tool_data.next_point = position;
|
||||
tool_data.next_handle_start = position;
|
||||
} 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
|
||||
// Generate first point
|
||||
let id = PointId::generate();
|
||||
|
|
@ -566,7 +564,6 @@ impl Fsm for PenToolFsmState {
|
|||
tool_data.next_point = pos;
|
||||
tool_data.next_handle_start = pos;
|
||||
} else {
|
||||
log::debug!("Creating new layer");
|
||||
// New path layer
|
||||
let node_type = resolve_document_node_type("Path").expect("Path node does not exist");
|
||||
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>);
|
||||
|
||||
#[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>
|
||||
</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 />
|
||||
</div>
|
||||
</LayoutCol>
|
||||
|
|
|
|||
|
|
@ -989,7 +989,7 @@
|
|||
}
|
||||
|
||||
&.disabled {
|
||||
background: var(--color-3-darkgray);
|
||||
background: rgba(var(--color-4-dimgray-rgb), 0.33);
|
||||
color: var(--color-a-softgray);
|
||||
|
||||
.icon-label {
|
||||
|
|
@ -1041,10 +1041,10 @@
|
|||
}
|
||||
|
||||
&.selected {
|
||||
background: rgba(var(--color-5-dullgray-rgb), 0.5);
|
||||
background: rgba(var(--color-5-dullgray-rgb), 0.33);
|
||||
|
||||
&.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">
|
||||
<div class="expand-arrow" />
|
||||
<TextLabel bold={true}>{widgetData.name}</TextLabel>
|
||||
{#if widgetData.pinned}
|
||||
<IconButton
|
||||
icon={"CheckboxChecked"}
|
||||
tooltip={"Unpin this node so it's no longer shown here without a selection"}
|
||||
size={24}
|
||||
action={(e) => {
|
||||
editor.handle.unpinNode(widgetData.id);
|
||||
e?.stopPropagation();
|
||||
}}
|
||||
class={"show-only-on-hover"}
|
||||
/>
|
||||
{/if}
|
||||
<IconButton
|
||||
icon={widgetData.pinned ? "CheckboxChecked" : "CheckboxUnchecked"}
|
||||
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"}
|
||||
size={24}
|
||||
action={(e) => {
|
||||
editor.handle.setNodePinned(widgetData.id, !widgetData.pinned);
|
||||
e?.stopPropagation();
|
||||
}}
|
||||
class={"show-only-on-hover"}
|
||||
/>
|
||||
<IconButton
|
||||
icon={"Trash"}
|
||||
tooltip={"Delete this node from the layer chain"}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ import {
|
|||
UpdateToolShelfLayout,
|
||||
UpdateWorkingColorsLayout,
|
||||
UpdateNodeGraphBarLayout,
|
||||
TriggerGraphViewOverlay,
|
||||
UpdateGraphViewOverlay,
|
||||
TriggerDelayedZoomCanvasToFitAll,
|
||||
UpdateGraphFadeArtwork,
|
||||
} from "@graphite/wasm-communication/messages";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
|
|
@ -27,10 +28,17 @@ export function createDocumentState(editor: Editor) {
|
|||
nodeGraphBarLayout: defaultWidgetLayout(),
|
||||
// Graph view overlay
|
||||
graphViewOverlayOpen: false,
|
||||
fadeArtwork: 100,
|
||||
});
|
||||
const { subscribe, update } = state;
|
||||
|
||||
// Update layouts
|
||||
editor.subscriptions.subscribeJsMessage(UpdateGraphFadeArtwork, (updateGraphFadeArtwork) => {
|
||||
update((state) => {
|
||||
state.fadeArtwork = updateGraphFadeArtwork.percentage;
|
||||
return state;
|
||||
});
|
||||
});
|
||||
editor.subscriptions.subscribeJsMessage(UpdateDocumentModeLayout, async (updateDocumentModeLayout) => {
|
||||
await tick();
|
||||
|
||||
|
|
@ -84,9 +92,9 @@ export function createDocumentState(editor: Editor) {
|
|||
});
|
||||
|
||||
// Show or hide the graph view overlay
|
||||
editor.subscriptions.subscribeJsMessage(TriggerGraphViewOverlay, (triggerGraphViewOverlay) => {
|
||||
editor.subscriptions.subscribeJsMessage(UpdateGraphViewOverlay, (updateGraphViewOverlay) => {
|
||||
update((state) => {
|
||||
state.graphViewOverlayOpen = triggerGraphViewOverlay.open;
|
||||
state.graphViewOverlayOpen = updateGraphViewOverlay.open;
|
||||
return state;
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -742,6 +742,14 @@ const mouseCursorIconCSSNames = {
|
|||
export type MouseCursor = keyof typeof mouseCursorIconCSSNames;
|
||||
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 {
|
||||
@Transform(({ value }: { value: MouseCursor }) => mouseCursorIconCSSNames[value] || "alias")
|
||||
readonly cursor!: MouseCursorIcon;
|
||||
|
|
@ -888,10 +896,6 @@ export class TriggerFontLoad extends JsMessage {
|
|||
isDefault!: boolean;
|
||||
}
|
||||
|
||||
export class TriggerGraphViewOverlay extends JsMessage {
|
||||
open!: boolean;
|
||||
}
|
||||
|
||||
export class TriggerVisitLink extends JsMessage {
|
||||
url!: string;
|
||||
}
|
||||
|
|
@ -1561,12 +1565,11 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||
TriggerAboutGraphiteLocalizedCommitDate,
|
||||
TriggerCopyToClipboardBlobUrl,
|
||||
TriggerDelayedZoomCanvasToFitAll,
|
||||
TriggerFetchAndOpenDocument,
|
||||
TriggerDownloadBlobUrl,
|
||||
TriggerDownloadImage,
|
||||
TriggerDownloadTextFile,
|
||||
TriggerFetchAndOpenDocument,
|
||||
TriggerFontLoad,
|
||||
TriggerGraphViewOverlay,
|
||||
TriggerImport,
|
||||
TriggerIndexedDbRemoveDocument,
|
||||
TriggerIndexedDbWriteDocument,
|
||||
|
|
@ -1584,9 +1587,6 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||
UpdateBox,
|
||||
UpdateClickTargets,
|
||||
UpdateContextMenuInformation,
|
||||
UpdateInSelectedNetwork,
|
||||
UpdateImportsExports,
|
||||
UpdateLayerWidths,
|
||||
UpdateDialogButtons,
|
||||
UpdateDialogColumn1,
|
||||
UpdateDialogColumn2,
|
||||
|
|
@ -1598,8 +1598,13 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||
UpdateDocumentRulers,
|
||||
UpdateDocumentScrollbars,
|
||||
UpdateEyedropperSamplingState,
|
||||
UpdateGraphFadeArtwork,
|
||||
UpdateGraphViewOverlay,
|
||||
UpdateImportsExports,
|
||||
UpdateInputHints,
|
||||
UpdateInSelectedNetwork,
|
||||
UpdateLayersPanelOptionsLayout,
|
||||
UpdateLayerWidths,
|
||||
UpdateMenuBarLayout,
|
||||
UpdateMouseCursor,
|
||||
UpdateNodeGraph,
|
||||
|
|
@ -1611,8 +1616,8 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||
UpdatePropertyPanelSectionsLayout,
|
||||
UpdateToolOptionsLayout,
|
||||
UpdateToolShelfLayout,
|
||||
UpdateWorkingColorsLayout,
|
||||
UpdateWirePathInProgress,
|
||||
UpdateWorkingColorsLayout,
|
||||
UpdateZoomWithScroll,
|
||||
} as const;
|
||||
export type JsMessageType = keyof typeof messageMakers;
|
||||
|
|
|
|||
|
|
@ -577,8 +577,7 @@ impl EditorHandle {
|
|||
let message = NodeGraphMessage::CreateNodeFromContextMenu {
|
||||
node_id: Some(id),
|
||||
node_type,
|
||||
x: x / 24,
|
||||
y: y / 24,
|
||||
xy: Some((x / 24, y / 24)),
|
||||
};
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
|
@ -652,10 +651,10 @@ impl EditorHandle {
|
|||
self.dispatch(message);
|
||||
}
|
||||
|
||||
/// Unpin a node given its node ID
|
||||
#[wasm_bindgen(js_name = unpinNode)]
|
||||
pub fn unpin_node(&self, id: u64) {
|
||||
self.dispatch(DocumentMessage::SetNodePinned { node_id: NodeId(id), pinned: false });
|
||||
/// Pin or unpin a node given its node ID
|
||||
#[wasm_bindgen(js_name = setNodePinned)]
|
||||
pub fn set_node_pinned(&self, id: u64, pinned: bool) {
|
||||
self.dispatch(DocumentMessage::SetNodePinned { node_id: NodeId(id), pinned });
|
||||
}
|
||||
|
||||
/// Delete a layer or node given its node ID
|
||||
|
|
|
|||
Loading…
Reference in New Issue