mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Node 'Disable' and 'Preview' UX improvements (#918)
* Don't allow disabling input or output nodes * Shortcuts to show and preview node * Update preview tooltip and label Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
509aab72c1
commit
c552edd525
6 changed files with 81 additions and 43 deletions
|
|
@ -33,6 +33,7 @@ pub fn default_mapping() -> 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::ToggleHidden),
|
||||
//
|
||||
// TransformLayerMessage
|
||||
entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation),
|
||||
|
|
|
|||
|
|
@ -67,13 +67,11 @@ pub enum NodeGraphMessage {
|
|||
input_index: usize,
|
||||
value: TaggedValue,
|
||||
},
|
||||
SetSelectedEnabled {
|
||||
enabled: bool,
|
||||
},
|
||||
SetSelectedOutput {
|
||||
output: bool,
|
||||
},
|
||||
ShiftNode {
|
||||
node_id: NodeId,
|
||||
},
|
||||
ToggleHidden,
|
||||
TogglePreview {
|
||||
node_id: NodeId,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||
use crate::messages::layout::utility_types::layout_widget::{Layout, LayoutGroup, Widget, WidgetCallback, WidgetHolder, WidgetLayout};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::{BreadcrumbTrailButtons, TextButton};
|
||||
use crate::messages::prelude::*;
|
||||
|
|
@ -175,25 +176,40 @@ impl NodeGraphMessageHandler {
|
|||
if let Some(network) = self.get_active_network_mut(document) {
|
||||
let mut widgets = Vec::new();
|
||||
|
||||
// Show an enable or disable nodes button if there is a selection
|
||||
if !self.selected_nodes.is_empty() {
|
||||
let is_enabled = self.selected_nodes.iter().any(|id| !network.disabled.contains(id));
|
||||
let enable_button = WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: if is_enabled { "Disable" } else { "Enable" }.to_string(),
|
||||
on_update: WidgetCallback::new(move |_| NodeGraphMessage::SetSelectedEnabled { enabled: !is_enabled }.into()),
|
||||
// Don't allow disabling input or output nodes
|
||||
let mut selected_nodes = self.selected_nodes.iter().filter(|&&id| !network.inputs.contains(&id) && network.original_output() != id);
|
||||
|
||||
// If there is at least one other selected node then show the hide or show button
|
||||
if selected_nodes.next().is_some() {
|
||||
// Check if any of the selected nodes are disabled
|
||||
let is_hidden = self.selected_nodes.iter().any(|id| network.disabled.contains(id));
|
||||
|
||||
// Check if multiple nodes are selected
|
||||
let mutliple_nodes = selected_nodes.next().is_some();
|
||||
|
||||
// Generate the enable or disable button accordingly
|
||||
let hide_button = WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: if is_hidden { "Show" } else { "Hide" }.to_string(),
|
||||
tooltip: if is_hidden { "Show node" } else { "Hide node" }.to_string() + if mutliple_nodes { "s" } else { "" },
|
||||
tooltip_shortcut: action_keys!(NodeGraphMessageDiscriminant::ToggleHidden),
|
||||
on_update: WidgetCallback::new(move |_| NodeGraphMessage::ToggleHidden.into()),
|
||||
..Default::default()
|
||||
}));
|
||||
widgets.push(enable_button);
|
||||
widgets.push(hide_button);
|
||||
}
|
||||
|
||||
// If only one node is selected then show the preview or stop previewing button
|
||||
if self.selected_nodes.len() == 1 {
|
||||
let is_output = network.output == self.selected_nodes[0];
|
||||
// Don't show stop previewing on the output node
|
||||
let node_id = self.selected_nodes[0];
|
||||
// Is this node the current output
|
||||
let is_output = network.output == node_id;
|
||||
|
||||
// Don't show stop previewing button on the origional output node
|
||||
if !(is_output && network.previous_output.filter(|&id| id != self.selected_nodes[0]).is_none()) {
|
||||
let output_button = WidgetHolder::new(Widget::TextButton(TextButton {
|
||||
label: if is_output { "Stop Previewing" } else { "Preview" }.to_string(),
|
||||
on_update: WidgetCallback::new(move |_| NodeGraphMessage::SetSelectedOutput { output: !is_output }.into()),
|
||||
label: if is_output { "End Preview" } else { "Preview" }.to_string(),
|
||||
tooltip: if is_output { "Restore preview to Output node" } else { "Preview node" }.to_string() + " (shortcut: Alt+click node)",
|
||||
on_update: WidgetCallback::new(move |_| NodeGraphMessage::TogglePreview { node_id }.into()),
|
||||
..Default::default()
|
||||
}));
|
||||
widgets.push(output_button);
|
||||
|
|
@ -675,31 +691,6 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
|||
}
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::SetSelectedEnabled { enabled } => {
|
||||
if let Some(network) = self.get_active_network_mut(document) {
|
||||
if enabled {
|
||||
network.disabled.retain(|id| !self.selected_nodes.contains(id));
|
||||
} else {
|
||||
network.disabled.extend(&self.selected_nodes);
|
||||
}
|
||||
Self::send_graph(network, responses);
|
||||
}
|
||||
self.update_selection_action_buttons(document, responses);
|
||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||
}
|
||||
NodeGraphMessage::SetSelectedOutput { output } => {
|
||||
if let Some(network) = self.get_active_network_mut(document) {
|
||||
if output {
|
||||
network.previous_output = Some(network.previous_output.unwrap_or(network.output));
|
||||
network.output = self.selected_nodes[0];
|
||||
} else if let Some(output) = network.previous_output.take() {
|
||||
network.output = output
|
||||
}
|
||||
Self::send_graph(network, responses);
|
||||
}
|
||||
self.update_selection_action_buttons(document, responses);
|
||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||
}
|
||||
NodeGraphMessage::ShiftNode { node_id } => {
|
||||
let Some(network) = self.get_active_network_mut(document) else{
|
||||
warn!("No network");
|
||||
|
|
@ -747,12 +738,42 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
|||
}
|
||||
Self::send_graph(network, responses);
|
||||
}
|
||||
NodeGraphMessage::ToggleHidden => {
|
||||
if let Some(network) = self.get_active_network_mut(document) {
|
||||
// Check if any of the selected nodes are hidden
|
||||
if self.selected_nodes.iter().any(|id| network.disabled.contains(id)) {
|
||||
// Remove all selected nodes from the disabled list
|
||||
network.disabled.retain(|id| !self.selected_nodes.contains(id));
|
||||
} else {
|
||||
let original_output = network.original_output();
|
||||
// Add all selected nodes to the disabled list (excluding input or output nodes)
|
||||
network.disabled.extend(self.selected_nodes.iter().filter(|&id| !network.inputs.contains(id) && original_output != *id));
|
||||
}
|
||||
Self::send_graph(network, responses);
|
||||
}
|
||||
self.update_selection_action_buttons(document, responses);
|
||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||
}
|
||||
NodeGraphMessage::TogglePreview { node_id } => {
|
||||
if let Some(network) = self.get_active_network_mut(document) {
|
||||
// Check if the node is not already
|
||||
if network.output != node_id {
|
||||
network.previous_output = Some(network.previous_output.unwrap_or(network.output));
|
||||
network.output = node_id;
|
||||
} else if let Some(output) = network.previous_output.take() {
|
||||
network.output = output
|
||||
}
|
||||
Self::send_graph(network, responses);
|
||||
}
|
||||
self.update_selection_action_buttons(document, responses);
|
||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn actions(&self) -> ActionList {
|
||||
if self.layer_path.is_some() && !self.selected_nodes.is_empty() {
|
||||
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes)
|
||||
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes, Cut, Copy, DuplicateSelectedNodes, ToggleHidden)
|
||||
} else {
|
||||
actions!(NodeGraphMessageDiscriminant;)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -534,8 +534,10 @@ export default defineComponent({
|
|||
},
|
||||
// TODO: Move the event listener from the graph to the window so dragging outside the graph area (or even the browser window) works
|
||||
pointerDown(e: PointerEvent) {
|
||||
// Exit the add node popup by clicking elsewhere in the graph
|
||||
if (this.nodeListLocation && !(e.target as HTMLElement).closest("[data-node-list]")) this.nodeListLocation = undefined;
|
||||
|
||||
// Handle the add node popup on right click
|
||||
if (e.button === 2) {
|
||||
const graphDiv: HTMLDivElement | undefined = (this.$refs.graph as typeof LayoutCol | undefined)?.$el;
|
||||
const graph = graphDiv?.getBoundingClientRect() || new DOMRect();
|
||||
|
|
@ -558,6 +560,10 @@ export default defineComponent({
|
|||
// If the user is clicking on the add nodes list, exit here
|
||||
if (nodeList) return;
|
||||
|
||||
if (e.altKey && nodeId) {
|
||||
this.editor.instance.togglePreview(BigInt(nodeId));
|
||||
}
|
||||
|
||||
// Clicked on a port dot
|
||||
if (port && node) {
|
||||
const isOutput = Boolean(port.getAttribute("data-port") === "output");
|
||||
|
|
|
|||
|
|
@ -648,6 +648,13 @@ impl JsEditorHandle {
|
|||
self.dispatch(message);
|
||||
}
|
||||
|
||||
/// Toggle preview on node
|
||||
#[wasm_bindgen(js_name = togglePreview)]
|
||||
pub fn toggle_preview(&self, node_id: NodeId) {
|
||||
let message = NodeGraphMessage::TogglePreview { node_id };
|
||||
self.dispatch(message);
|
||||
}
|
||||
|
||||
/// Pastes an image
|
||||
#[wasm_bindgen(js_name = pasteImage)]
|
||||
pub fn paste_image(&self, mime: String, image_data: Vec<u8>, mouse_x: Option<f64>, mouse_y: Option<f64>) {
|
||||
|
|
|
|||
|
|
@ -277,6 +277,11 @@ impl NodeNetwork {
|
|||
nodes,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the original output node of this network, ignoring any preview node
|
||||
pub fn original_output(&self) -> NodeId {
|
||||
self.previous_output.unwrap_or(self.output)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue