Improve history states (#932)
* Add some more history states
* Fix undo whilst drawing
* Paste image history
* Toggle output and preview history
* Code review nits
* Remove extra '{'
* Fix typo
* Fix about.toml
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
6e142627a3
commit
2bcc3d3baf
|
|
@ -17,9 +17,10 @@ ignore-dev-dependencies = true
|
||||||
|
|
||||||
workarounds = ["ring"]
|
workarounds = ["ring"]
|
||||||
|
|
||||||
# https://raw.githubusercontent.com/briansmith/webpki/main/LICENSE looks like MIT to me
|
# https://raw.githubusercontent.com/briansmith/webpki/main/LICENSE
|
||||||
|
# is the ISC license but test code within the repo is BSD-3-Clause, but is not compiled into the crate when we use it
|
||||||
[webpki.clarify]
|
[webpki.clarify]
|
||||||
license = "MIT"
|
license = "ISC"
|
||||||
|
|
||||||
[[webpki.clarify.files]]
|
[[webpki.clarify.files]]
|
||||||
path = 'LICENSE'
|
path = 'LICENSE'
|
||||||
|
|
|
||||||
|
|
@ -187,6 +187,7 @@ pub enum DocumentMessage {
|
||||||
toggle_angle: bool,
|
toggle_angle: bool,
|
||||||
},
|
},
|
||||||
Undo,
|
Undo,
|
||||||
|
UndoFinished,
|
||||||
UngroupLayers {
|
UngroupLayers {
|
||||||
folder_path: Vec<LayerId>,
|
folder_path: Vec<LayerId>,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,9 @@ pub struct DocumentMessageHandler {
|
||||||
pub document_undo_history: VecDeque<DocumentSave>,
|
pub document_undo_history: VecDeque<DocumentSave>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub document_redo_history: VecDeque<DocumentSave>,
|
pub document_redo_history: VecDeque<DocumentSave>,
|
||||||
|
/// Don't allow aborting transactions whilst undoing to avoid #559
|
||||||
|
#[serde(skip)]
|
||||||
|
undo_in_progress: bool,
|
||||||
|
|
||||||
#[serde(with = "vectorize_layer_metadata")]
|
#[serde(with = "vectorize_layer_metadata")]
|
||||||
pub layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
|
pub layer_metadata: HashMap<Vec<LayerId>, LayerMetadata>,
|
||||||
|
|
@ -85,6 +88,7 @@ impl Default for DocumentMessageHandler {
|
||||||
|
|
||||||
document_undo_history: VecDeque::new(),
|
document_undo_history: VecDeque::new(),
|
||||||
document_redo_history: VecDeque::new(),
|
document_redo_history: VecDeque::new(),
|
||||||
|
undo_in_progress: false,
|
||||||
|
|
||||||
layer_metadata: vec![(vec![], LayerMetadata::new(true))].into_iter().collect(),
|
layer_metadata: vec![(vec![], LayerMetadata::new(true))].into_iter().collect(),
|
||||||
layer_range_selection_reference: Vec::new(),
|
layer_range_selection_reference: Vec::new(),
|
||||||
|
|
@ -190,8 +194,10 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
|
|
||||||
// Messages
|
// Messages
|
||||||
AbortTransaction => {
|
AbortTransaction => {
|
||||||
self.undo(responses).unwrap_or_else(|e| warn!("{}", e));
|
if !self.undo_in_progress {
|
||||||
responses.extend([RenderDocument.into(), DocumentStructureChanged.into()]);
|
self.undo(responses).unwrap_or_else(|e| warn!("{}", e));
|
||||||
|
responses.extend([RenderDocument.into(), DocumentStructureChanged.into()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
AddSelectedLayers { additional_layers } => {
|
AddSelectedLayers { additional_layers } => {
|
||||||
for layer_path in &additional_layers {
|
for layer_path in &additional_layers {
|
||||||
|
|
@ -509,7 +515,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
// Set a random seed input
|
// Set a random seed input
|
||||||
responses.push_back(
|
responses.push_back(
|
||||||
NodeGraphMessage::SetInputValue {
|
NodeGraphMessage::SetInputValue {
|
||||||
node: *imaginate_node.last().unwrap(),
|
node_id: *imaginate_node.last().unwrap(),
|
||||||
input_index: 1,
|
input_index: 1,
|
||||||
value: graph_craft::document::value::TaggedValue::F64((generate_uuid() >> 1) as f64),
|
value: graph_craft::document::value::TaggedValue::F64((generate_uuid() >> 1) as f64),
|
||||||
}
|
}
|
||||||
|
|
@ -541,6 +547,8 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
||||||
}
|
}
|
||||||
PasteImage { mime, image_data, mouse } => {
|
PasteImage { mime, image_data, mouse } => {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
|
||||||
let path = vec![generate_uuid()];
|
let path = vec![generate_uuid()];
|
||||||
responses.push_back(
|
responses.push_back(
|
||||||
DocumentOperation::AddImage {
|
DocumentOperation::AddImage {
|
||||||
|
|
@ -833,12 +841,15 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Undo => {
|
Undo => {
|
||||||
|
self.undo_in_progress = true;
|
||||||
responses.push_back(BroadcastEvent::ToolAbort.into());
|
responses.push_back(BroadcastEvent::ToolAbort.into());
|
||||||
responses.push_back(DocumentHistoryBackward.into());
|
responses.push_back(DocumentHistoryBackward.into());
|
||||||
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
||||||
responses.push_back(RenderDocument.into());
|
responses.push_back(RenderDocument.into());
|
||||||
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
responses.push_back(FolderChanged { affected_folder_path: vec![] }.into());
|
||||||
|
responses.push_back(UndoFinished.into());
|
||||||
}
|
}
|
||||||
|
UndoFinished => self.undo_in_progress = false,
|
||||||
UngroupLayers { folder_path } => {
|
UngroupLayers { folder_path } => {
|
||||||
// Select all the children of the folder
|
// Select all the children of the folder
|
||||||
let select = self.document_legacy.folder_children_paths(&folder_path);
|
let select = self.document_legacy.folder_children_paths(&folder_path);
|
||||||
|
|
@ -1353,6 +1364,8 @@ impl DocumentMessageHandler {
|
||||||
responses.push_back(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() }.into())
|
responses.push_back(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() }.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
responses.push_back(NodeGraphMessage::SendGraph.into());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
None => Err(EditorError::NoTransactionInProgress),
|
None => Err(EditorError::NoTransactionInProgress),
|
||||||
|
|
@ -1391,6 +1404,8 @@ impl DocumentMessageHandler {
|
||||||
responses.push_back(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() }.into())
|
responses.push_back(DocumentMessage::LayerChanged { affected_layer_path: layer.clone() }.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
responses.push_back(NodeGraphMessage::SendGraph.into());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
None => Err(EditorError::NoTransactionInProgress),
|
None => Err(EditorError::NoTransactionInProgress),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::messages::prelude::*;
|
use crate::messages::prelude::*;
|
||||||
|
|
||||||
use document_legacy::LayerId;
|
use document_legacy::LayerId;
|
||||||
use graph_craft::document::{value::TaggedValue, NodeId};
|
use graph_craft::document::value::TaggedValue;
|
||||||
|
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
||||||
|
|
||||||
#[remain::sorted]
|
#[remain::sorted]
|
||||||
#[impl_message(Message, DocumentMessage, NodeGraph)]
|
#[impl_message(Message, DocumentMessage, NodeGraph)]
|
||||||
|
|
@ -43,6 +44,10 @@ pub enum NodeGraphMessage {
|
||||||
input_index: usize,
|
input_index: usize,
|
||||||
new_exposed: bool,
|
new_exposed: bool,
|
||||||
},
|
},
|
||||||
|
InsertNode {
|
||||||
|
node_id: NodeId,
|
||||||
|
document_node: DocumentNode,
|
||||||
|
},
|
||||||
MoveSelectedNodes {
|
MoveSelectedNodes {
|
||||||
displacement_x: i32,
|
displacement_x: i32,
|
||||||
displacement_y: i32,
|
displacement_y: i32,
|
||||||
|
|
@ -56,14 +61,20 @@ pub enum NodeGraphMessage {
|
||||||
SelectNodes {
|
SelectNodes {
|
||||||
nodes: Vec<NodeId>,
|
nodes: Vec<NodeId>,
|
||||||
},
|
},
|
||||||
|
SendGraph,
|
||||||
SetDrawing {
|
SetDrawing {
|
||||||
new_drawing: bool,
|
new_drawing: bool,
|
||||||
},
|
},
|
||||||
SetInputValue {
|
SetInputValue {
|
||||||
node: NodeId,
|
node_id: NodeId,
|
||||||
input_index: usize,
|
input_index: usize,
|
||||||
value: TaggedValue,
|
value: TaggedValue,
|
||||||
},
|
},
|
||||||
|
SetNodeInput {
|
||||||
|
node_id: NodeId,
|
||||||
|
input_index: usize,
|
||||||
|
input: NodeInput,
|
||||||
|
},
|
||||||
SetQualifiedInputValue {
|
SetQualifiedInputValue {
|
||||||
layer_path: Vec<LayerId>,
|
layer_path: Vec<LayerId>,
|
||||||
node_path: Vec<NodeId>,
|
node_path: Vec<NodeId>,
|
||||||
|
|
@ -74,7 +85,11 @@ pub enum NodeGraphMessage {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
},
|
},
|
||||||
ToggleHidden,
|
ToggleHidden,
|
||||||
|
ToggleHiddenImpl,
|
||||||
TogglePreview {
|
TogglePreview {
|
||||||
node_id: NodeId,
|
node_id: NodeId,
|
||||||
},
|
},
|
||||||
|
TogglePreviewImpl {
|
||||||
|
node_id: NodeId,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,16 @@ impl NodeGraphMessageHandler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the active graph_craft NodeNetwork struct
|
||||||
|
fn get_active_network<'a>(&self, document: &'a Document) -> Option<&'a graph_craft::document::NodeNetwork> {
|
||||||
|
let mut network = self.get_root_network(document);
|
||||||
|
|
||||||
|
for segement in &self.nested_path {
|
||||||
|
network = network.and_then(|network| network.nodes.get(segement)).and_then(|node| node.implementation.get_network());
|
||||||
|
}
|
||||||
|
network
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the active graph_craft NodeNetwork struct
|
/// Get the active graph_craft NodeNetwork struct
|
||||||
fn get_active_network_mut<'a>(&self, document: &'a mut Document) -> Option<&'a mut graph_craft::document::NodeNetwork> {
|
fn get_active_network_mut<'a>(&self, document: &'a mut Document) -> Option<&'a mut graph_craft::document::NodeNetwork> {
|
||||||
let mut network = self.get_root_network_mut(document);
|
let mut network = self.get_root_network_mut(document);
|
||||||
|
|
@ -179,7 +189,7 @@ impl NodeGraphMessageHandler {
|
||||||
|
|
||||||
/// Updates the buttons for disable and preview
|
/// Updates the buttons for disable and preview
|
||||||
fn update_selection_action_buttons(&mut self, document: &mut Document, responses: &mut VecDeque<Message>) {
|
fn update_selection_action_buttons(&mut self, document: &mut Document, responses: &mut VecDeque<Message>) {
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
if let Some(network) = self.get_active_network(document) {
|
||||||
let mut widgets = Vec::new();
|
let mut widgets = Vec::new();
|
||||||
|
|
||||||
// Don't allow disabling input or output nodes
|
// Don't allow disabling input or output nodes
|
||||||
|
|
@ -281,7 +291,7 @@ impl NodeGraphMessageHandler {
|
||||||
|
|
||||||
let mut nodes = Vec::new();
|
let mut nodes = Vec::new();
|
||||||
for (id, node) in &network.nodes {
|
for (id, node) in &network.nodes {
|
||||||
let Some(node_type) = document_node_types::resolve_document_node_type(&node.name) else{
|
let Some(node_type) = document_node_types::resolve_document_node_type(&node.name) else {
|
||||||
warn!("Node '{}' does not exist in library", node.name);
|
warn!("Node '{}' does not exist in library", node.name);
|
||||||
continue
|
continue
|
||||||
};
|
};
|
||||||
|
|
@ -406,7 +416,6 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
if let Some(_old_layer_path) = self.layer_path.take() {
|
if let Some(_old_layer_path) = self.layer_path.take() {
|
||||||
responses.push_back(FrontendMessage::UpdateNodeGraphVisibility { visible: false }.into());
|
responses.push_back(FrontendMessage::UpdateNodeGraphVisibility { visible: false }.into());
|
||||||
responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into());
|
responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into());
|
||||||
// TODO: Close UI and clean up old node graph
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeGraphMessage::ConnectNodesByLink {
|
NodeGraphMessage::ConnectNodesByLink {
|
||||||
|
|
@ -415,26 +424,30 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
input_node_connector_index,
|
input_node_connector_index,
|
||||||
} => {
|
} => {
|
||||||
log::debug!("Connect primary output from node {output_node} to input of index {input_node_connector_index} on node {input_node}.");
|
log::debug!("Connect primary output from node {output_node} to input of index {input_node_connector_index} on node {input_node}.");
|
||||||
|
let node_id = input_node;
|
||||||
|
|
||||||
let Some(network) = self.get_active_network_mut(document) else {
|
let Some(network) = self.get_active_network(document) else {
|
||||||
error!("No network");
|
error!("No network");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(input_node) = network.nodes.get_mut(&input_node) else {
|
let Some(input_node) = network.nodes.get(&input_node) else {
|
||||||
error!("No to");
|
error!("No to");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some((actual_index, _)) = input_node.inputs.iter().enumerate().filter(|input|input.1.is_exposed()).nth(input_node_connector_index) else {
|
let Some((input_index, _)) = input_node.inputs.iter().enumerate().filter(|input|input.1.is_exposed()).nth(input_node_connector_index) else {
|
||||||
error!("Failed to find actual index of connector indes {input_node_connector_index} on node {input_node:#?}");
|
error!("Failed to find actual index of connector indes {input_node_connector_index} on node {input_node:#?}");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
input_node.inputs[actual_index] = NodeInput::Node(output_node);
|
|
||||||
|
|
||||||
Self::send_graph(network, responses);
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
|
||||||
|
let input = NodeInput::Node(output_node);
|
||||||
|
responses.push_back(NodeGraphMessage::SetNodeInput { node_id, input_index, input }.into());
|
||||||
|
|
||||||
|
responses.push_back(NodeGraphMessage::SendGraph.into());
|
||||||
}
|
}
|
||||||
NodeGraphMessage::Copy => {
|
NodeGraphMessage::Copy => {
|
||||||
let Some(network) = self.get_active_network_mut(document) else {
|
let Some(network) = self.get_active_network(document) else {
|
||||||
error!("No network");
|
error!("No network");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -451,12 +464,8 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
}
|
}
|
||||||
NodeGraphMessage::CreateNode { node_id, node_type, x, y } => {
|
NodeGraphMessage::CreateNode { node_id, node_type, x, y } => {
|
||||||
let node_id = node_id.unwrap_or_else(crate::application::generate_uuid);
|
let node_id = node_id.unwrap_or_else(crate::application::generate_uuid);
|
||||||
let Some(network) = self.get_active_network_mut(document) else{
|
|
||||||
warn!("No network");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(document_node_type) = document_node_types::resolve_document_node_type(&node_type) else{
|
let Some(document_node_type) = document_node_types::resolve_document_node_type(&node_type) else {
|
||||||
responses.push_back(DialogMessage::DisplayDialogError { title: "Cannot insert node".to_string(), description: format!("The document node '{node_type}' does not exist in the document node list") }.into());
|
responses.push_back(DialogMessage::DisplayDialogError { title: "Cannot insert node".to_string(), description: format!("The document node '{node_type}' does not exist in the document node list") }.into());
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -481,17 +490,18 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
network.nodes.insert(
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
node_id,
|
|
||||||
DocumentNode {
|
let document_node = DocumentNode {
|
||||||
name: node_type.clone(),
|
name: node_type.clone(),
|
||||||
inputs: document_node_type.inputs.iter().map(|input| input.default.clone()).collect(),
|
inputs: document_node_type.inputs.iter().map(|input| input.default.clone()).collect(),
|
||||||
// TODO: Allow inserting nodes that contain other nodes.
|
// TODO: Allow inserting nodes that contain other nodes.
|
||||||
implementation: DocumentNodeImplementation::Network(inner_network),
|
implementation: DocumentNodeImplementation::Network(inner_network),
|
||||||
metadata: graph_craft::document::DocumentNodeMetadata { position: (x, y).into() },
|
metadata: graph_craft::document::DocumentNodeMetadata { position: (x, y).into() },
|
||||||
},
|
};
|
||||||
);
|
responses.push_back(NodeGraphMessage::InsertNode { node_id, document_node }.into());
|
||||||
Self::send_graph(network, responses);
|
|
||||||
|
responses.push_back(NodeGraphMessage::SendGraph.into());
|
||||||
}
|
}
|
||||||
NodeGraphMessage::Cut => {
|
NodeGraphMessage::Cut => {
|
||||||
responses.push_back(NodeGraphMessage::Copy.into());
|
responses.push_back(NodeGraphMessage::Copy.into());
|
||||||
|
|
@ -499,32 +509,26 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
}
|
}
|
||||||
NodeGraphMessage::DeleteNode { node_id } => {
|
NodeGraphMessage::DeleteNode { node_id } => {
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
if let Some(network) = self.get_active_network_mut(document) {
|
||||||
if self.remove_node(network, node_id) {
|
self.remove_node(network, node_id);
|
||||||
Self::send_graph(network, responses);
|
|
||||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.update_selected(document, responses);
|
self.update_selected(document, responses);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::DeleteSelectedNodes => {
|
NodeGraphMessage::DeleteSelectedNodes => {
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
let mut modified = false;
|
|
||||||
for node_id in self.selected_nodes.clone() {
|
for node_id in self.selected_nodes.clone() {
|
||||||
modified = modified || self.remove_node(network, node_id);
|
responses.push_back(NodeGraphMessage::DeleteNode { node_id }.into());
|
||||||
}
|
|
||||||
if modified {
|
|
||||||
Self::send_graph(network, responses);
|
|
||||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.update_selected(document, responses);
|
|
||||||
|
responses.push_back(NodeGraphMessage::SendGraph.into());
|
||||||
|
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||||
}
|
}
|
||||||
NodeGraphMessage::DisconnectNodes { node_id, input_index } => {
|
NodeGraphMessage::DisconnectNodes { node_id, input_index } => {
|
||||||
let Some(network) = self.get_active_network_mut(document) else {
|
let Some(network) = self.get_active_network(document) else {
|
||||||
warn!("No network");
|
warn!("No network");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(node) = network.nodes.get_mut(&node_id) else {
|
let Some(node) = network.nodes.get(&node_id) else {
|
||||||
warn!("Invalid node");
|
warn!("Invalid node");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -532,37 +536,46 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
warn!("Node {} not in library", node.name);
|
warn!("Node {} not in library", node.name);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
node.inputs[input_index] = node_type.inputs[input_index].default.clone();
|
|
||||||
Self::send_graph(network, responses);
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
|
||||||
|
let input = node_type.inputs[input_index].default.clone();
|
||||||
|
responses.push_back(NodeGraphMessage::SetNodeInput { node_id, input_index, input }.into());
|
||||||
|
|
||||||
|
responses.push_back(NodeGraphMessage::SendGraph.into());
|
||||||
}
|
}
|
||||||
NodeGraphMessage::DoubleClickNode { node } => {
|
NodeGraphMessage::DoubleClickNode { node } => {
|
||||||
self.selected_nodes.clear();
|
if let Some(network) = self.get_active_network(document) {
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
|
||||||
if network.nodes.get(&node).and_then(|node| node.implementation.get_network()).is_some() {
|
if network.nodes.get(&node).and_then(|node| node.implementation.get_network()).is_some() {
|
||||||
self.nested_path.push(node);
|
self.nested_path.push(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
if let Some(network) = self.get_active_network(document) {
|
||||||
Self::send_graph(network, responses);
|
Self::send_graph(network, responses);
|
||||||
}
|
}
|
||||||
self.collect_nested_addresses(document, responses);
|
self.collect_nested_addresses(document, responses);
|
||||||
self.update_selected(document, responses);
|
self.update_selected(document, responses);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::DuplicateSelectedNodes => {
|
NodeGraphMessage::DuplicateSelectedNodes => {
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
if let Some(network) = self.get_active_network(document) {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
|
||||||
let new_ids = &self.selected_nodes.iter().map(|&id| (id, crate::application::generate_uuid())).collect();
|
let new_ids = &self.selected_nodes.iter().map(|&id| (id, crate::application::generate_uuid())).collect();
|
||||||
self.selected_nodes.clear();
|
self.selected_nodes.clear();
|
||||||
|
|
||||||
// Copy the selected nodes
|
// Copy the selected nodes
|
||||||
let copied_nodes = Self::copy_nodes(network, new_ids).collect::<Vec<_>>();
|
let copied_nodes = Self::copy_nodes(network, new_ids).collect::<Vec<_>>();
|
||||||
for (new_id, mut node) in copied_nodes {
|
for (node_id, mut document_node) in copied_nodes {
|
||||||
// Shift duplicated node
|
// Shift duplicated node
|
||||||
node.metadata.position += IVec2::splat(2);
|
document_node.metadata.position += IVec2::splat(2);
|
||||||
|
|
||||||
// Add new node to the list
|
// Add new node to the list
|
||||||
self.selected_nodes.push(new_id);
|
self.selected_nodes.push(node_id);
|
||||||
network.nodes.insert(new_id, node);
|
|
||||||
|
// Insert new node into graph
|
||||||
|
responses.push_back(NodeGraphMessage::InsertNode { node_id, document_node }.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::send_graph(network, responses);
|
Self::send_graph(network, responses);
|
||||||
self.update_selected(document, responses);
|
self.update_selected(document, responses);
|
||||||
}
|
}
|
||||||
|
|
@ -572,38 +585,48 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
for _ in 0..depth_of_nesting {
|
for _ in 0..depth_of_nesting {
|
||||||
self.nested_path.pop();
|
self.nested_path.pop();
|
||||||
}
|
}
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
if let Some(network) = self.get_active_network(document) {
|
||||||
Self::send_graph(network, responses);
|
Self::send_graph(network, responses);
|
||||||
}
|
}
|
||||||
self.collect_nested_addresses(document, responses);
|
self.collect_nested_addresses(document, responses);
|
||||||
self.update_selected(document, responses);
|
self.update_selected(document, responses);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::ExposeInput { node_id, input_index, new_exposed } => {
|
NodeGraphMessage::ExposeInput { node_id, input_index, new_exposed } => {
|
||||||
let Some(network) = self.get_active_network_mut(document) else{
|
let Some(network) = self.get_active_network(document) else {
|
||||||
warn!("No network");
|
warn!("No network");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(node) = network.nodes.get_mut(&node_id) else {
|
let Some(node) = network.nodes.get(&node_id) else {
|
||||||
warn!("No node");
|
warn!("No node");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if let NodeInput::Value { exposed, .. } = &mut node.inputs[input_index] {
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
|
||||||
|
let mut input = node.inputs[input_index].clone();
|
||||||
|
if let NodeInput::Value { exposed, .. } = &mut input {
|
||||||
*exposed = new_exposed;
|
*exposed = new_exposed;
|
||||||
} else if let Some(node_type) = document_node_types::resolve_document_node_type(&node.name) {
|
} else if let Some(node_type) = document_node_types::resolve_document_node_type(&node.name) {
|
||||||
if let NodeInput::Value { tagged_value, .. } = &node_type.inputs[input_index].default {
|
if let NodeInput::Value { tagged_value, .. } = &node_type.inputs[input_index].default {
|
||||||
node.inputs[input_index] = NodeInput::Value {
|
input = NodeInput::Value {
|
||||||
tagged_value: tagged_value.clone(),
|
tagged_value: tagged_value.clone(),
|
||||||
exposed: new_exposed,
|
exposed: new_exposed,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::send_graph(network, responses);
|
responses.push_back(NodeGraphMessage::SetNodeInput { node_id, input_index, input }.into());
|
||||||
|
|
||||||
|
responses.push_back(NodeGraphMessage::SendGraph.into());
|
||||||
responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into());
|
responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into());
|
||||||
}
|
}
|
||||||
|
NodeGraphMessage::InsertNode { node_id, document_node } => {
|
||||||
|
if let Some(network) = self.get_active_network_mut(document) {
|
||||||
|
network.nodes.insert(node_id, document_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
NodeGraphMessage::MoveSelectedNodes { displacement_x, displacement_y } => {
|
NodeGraphMessage::MoveSelectedNodes { displacement_x, displacement_y } => {
|
||||||
let Some(network) = self.get_active_network_mut(document) else{
|
let Some(network) = self.get_active_network_mut(document) else {
|
||||||
warn!("No network");
|
warn!("No network");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -621,11 +644,9 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(_old_layer_path) = self.layer_path.replace(layer_path) {
|
self.layer_path = Some(layer_path);
|
||||||
// TODO: Necessary cleanup of old node graph
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
if let Some(network) = self.get_active_network(document) {
|
||||||
self.selected_nodes.clear();
|
self.selected_nodes.clear();
|
||||||
responses.push_back(FrontendMessage::UpdateNodeGraphVisibility { visible: true }.into());
|
responses.push_back(FrontendMessage::UpdateNodeGraphVisibility { visible: true }.into());
|
||||||
|
|
||||||
|
|
@ -638,7 +659,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
self.update_selected(document, responses);
|
self.update_selected(document, responses);
|
||||||
}
|
}
|
||||||
NodeGraphMessage::PasteNodes { serialized_nodes } => {
|
NodeGraphMessage::PasteNodes { serialized_nodes } => {
|
||||||
let Some(network) = self.get_active_network_mut(document) else{
|
let Some(network) = self.get_active_network(document) else {
|
||||||
warn!("No network");
|
warn!("No network");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -660,31 +681,38 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
shift += IVec2::splat(2);
|
shift += IVec2::splat(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.selected_nodes.clear();
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
|
||||||
let new_ids: HashMap<_, _> = data.iter().map(|&(id, _)| (id, crate::application::generate_uuid())).collect();
|
let new_ids: HashMap<_, _> = data.iter().map(|&(id, _)| (id, crate::application::generate_uuid())).collect();
|
||||||
for (old_id, mut node) in data {
|
for (old_id, mut document_node) in data {
|
||||||
// Shift copied node
|
// Shift copied node
|
||||||
node.metadata.position += shift;
|
document_node.metadata.position += shift;
|
||||||
|
|
||||||
// Get the new, non-conflicting id
|
// Get the new, non-conflicting id
|
||||||
let new_id = *new_ids.get(&old_id).unwrap();
|
let node_id = *new_ids.get(&old_id).unwrap();
|
||||||
|
document_node = document_node.map_ids(Self::default_node_input, &new_ids);
|
||||||
|
|
||||||
// Insert node into network
|
// Insert node into network
|
||||||
network.nodes.insert(new_id, node.map_ids(Self::default_node_input, &new_ids));
|
responses.push_back(NodeGraphMessage::InsertNode { node_id, document_node }.into());
|
||||||
|
|
||||||
// Select the newly pasted node
|
|
||||||
self.selected_nodes.push(new_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::send_graph(network, responses);
|
let nodes = new_ids.values().copied().collect();
|
||||||
self.update_selected(document, responses);
|
responses.push_back(NodeGraphMessage::SelectNodes { nodes }.into());
|
||||||
|
|
||||||
|
responses.push_back(NodeGraphMessage::SendGraph.into());
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SelectNodes { nodes } => {
|
NodeGraphMessage::SelectNodes { nodes } => {
|
||||||
self.selected_nodes = nodes;
|
self.selected_nodes = nodes;
|
||||||
self.update_selection_action_buttons(document, responses);
|
self.update_selection_action_buttons(document, responses);
|
||||||
|
self.update_selected(document, responses);
|
||||||
responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into());
|
responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into());
|
||||||
}
|
}
|
||||||
|
NodeGraphMessage::SendGraph => {
|
||||||
|
if let Some(network) = self.get_active_network(document) {
|
||||||
|
Self::send_graph(network, responses);
|
||||||
|
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
NodeGraphMessage::SetDrawing { new_drawing } => {
|
NodeGraphMessage::SetDrawing { new_drawing } => {
|
||||||
let selected: Vec<_> = selected.collect();
|
let selected: Vec<_> = selected.collect();
|
||||||
// Check if we stopped drawing a node graph frame
|
// Check if we stopped drawing a node graph frame
|
||||||
|
|
@ -704,14 +732,13 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
}
|
}
|
||||||
self.is_drawing_node_graph_frame = new_drawing
|
self.is_drawing_node_graph_frame = new_drawing
|
||||||
}
|
}
|
||||||
NodeGraphMessage::SetInputValue { node, input_index, value } => {
|
NodeGraphMessage::SetInputValue { node_id, input_index, value } => {
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
if let Some(network) = self.get_active_network(document) {
|
||||||
if let Some(node) = network.nodes.get_mut(&node) {
|
if let Some(node) = network.nodes.get(&node_id) {
|
||||||
// Extend number of inputs if not already large enough
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
if input_index >= node.inputs.len() {
|
|
||||||
node.inputs.extend(((node.inputs.len() - 1)..input_index).map(|_| NodeInput::Network));
|
let input = NodeInput::Value { tagged_value: value, exposed: false };
|
||||||
}
|
responses.push_back(NodeGraphMessage::SetNodeInput { node_id, input_index, input }.into());
|
||||||
node.inputs[input_index] = NodeInput::Value { tagged_value: value, exposed: false };
|
|
||||||
responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into());
|
responses.push_back(PropertiesPanelMessage::ResendActiveProperties.into());
|
||||||
if node.name != "Imaginate" || input_index == 0 {
|
if node.name != "Imaginate" || input_index == 0 {
|
||||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||||
|
|
@ -719,6 +746,13 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NodeGraphMessage::SetNodeInput { node_id, input_index, input } => {
|
||||||
|
if let Some(network) = self.get_active_network_mut(document) {
|
||||||
|
if let Some(node) = network.nodes.get_mut(&node_id) {
|
||||||
|
node.inputs[input_index] = input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
NodeGraphMessage::SetQualifiedInputValue {
|
NodeGraphMessage::SetQualifiedInputValue {
|
||||||
layer_path,
|
layer_path,
|
||||||
node_path,
|
node_path,
|
||||||
|
|
@ -750,7 +784,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NodeGraphMessage::ShiftNode { node_id } => {
|
NodeGraphMessage::ShiftNode { node_id } => {
|
||||||
let Some(network) = self.get_active_network_mut(document) else{
|
let Some(network) = self.get_active_network_mut(document) else {
|
||||||
warn!("No network");
|
warn!("No network");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
@ -794,9 +828,13 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
stack.extend(outwards_links.get(&id).unwrap_or(&Vec::new()).iter().copied())
|
stack.extend(outwards_links.get(&id).unwrap_or(&Vec::new()).iter().copied())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::send_graph(network, responses);
|
responses.push_back(NodeGraphMessage::SendGraph.into());
|
||||||
}
|
}
|
||||||
NodeGraphMessage::ToggleHidden => {
|
NodeGraphMessage::ToggleHidden => {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
responses.push_back(NodeGraphMessage::ToggleHiddenImpl.into());
|
||||||
|
}
|
||||||
|
NodeGraphMessage::ToggleHiddenImpl => {
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
if let Some(network) = self.get_active_network_mut(document) {
|
||||||
// Check if any of the selected nodes are hidden
|
// Check if any of the selected nodes are hidden
|
||||||
if self.selected_nodes.iter().any(|id| network.disabled.contains(id)) {
|
if self.selected_nodes.iter().any(|id| network.disabled.contains(id)) {
|
||||||
|
|
@ -813,6 +851,10 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &mut dyn Iterator<Item = &
|
||||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||||
}
|
}
|
||||||
NodeGraphMessage::TogglePreview { node_id } => {
|
NodeGraphMessage::TogglePreview { node_id } => {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
responses.push_back(NodeGraphMessage::TogglePreviewImpl { node_id }.into());
|
||||||
|
}
|
||||||
|
NodeGraphMessage::TogglePreviewImpl { node_id } => {
|
||||||
if let Some(network) = self.get_active_network_mut(document) {
|
if let Some(network) = self.get_active_network_mut(document) {
|
||||||
// Check if the node is not already
|
// Check if the node is not already
|
||||||
if network.output != node_id {
|
if network.output != node_id {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ pub fn string_properties(text: impl Into<String>) -> Vec<LayoutGroup> {
|
||||||
fn update_value<T, F: Fn(&T) -> TaggedValue + 'static + Send + Sync>(value: F, node_id: NodeId, input_index: usize) -> WidgetCallback<T> {
|
fn update_value<T, F: Fn(&T) -> TaggedValue + 'static + Send + Sync>(value: F, node_id: NodeId, input_index: usize) -> WidgetCallback<T> {
|
||||||
WidgetCallback::new(move |input_value: &T| {
|
WidgetCallback::new(move |input_value: &T| {
|
||||||
NodeGraphMessage::SetInputValue {
|
NodeGraphMessage::SetInputValue {
|
||||||
node: node_id,
|
node_id,
|
||||||
input_index,
|
input_index,
|
||||||
value: value(input_value),
|
value: value(input_value),
|
||||||
}
|
}
|
||||||
|
|
@ -398,13 +398,13 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
|
||||||
LayoutGroup::Row { widgets }.with_tooltip("Connection status to the server that computes generated images")
|
LayoutGroup::Row { widgets }.with_tooltip("Connection status to the server that computes generated images")
|
||||||
};
|
};
|
||||||
|
|
||||||
let &NodeInput::Value {tagged_value: TaggedValue::ImaginateStatus( imaginate_status),..} = status_value else{
|
let &NodeInput::Value {tagged_value: TaggedValue::ImaginateStatus( imaginate_status),..} = status_value else {
|
||||||
panic!("Invalid status input")
|
panic!("Invalid status input")
|
||||||
};
|
};
|
||||||
let NodeInput::Value {tagged_value: TaggedValue::RcImage( cached_data),..} = cached_value else{
|
let NodeInput::Value {tagged_value: TaggedValue::RcImage( cached_data),..} = cached_value else {
|
||||||
panic!("Invalid cached image input")
|
panic!("Invalid cached image input")
|
||||||
};
|
};
|
||||||
let &NodeInput::Value {tagged_value: TaggedValue::F64( percent_complete),..} = complete_value else{
|
let &NodeInput::Value {tagged_value: TaggedValue::F64( percent_complete),..} = complete_value else {
|
||||||
panic!("Invalid percent complete input")
|
panic!("Invalid percent complete input")
|
||||||
};
|
};
|
||||||
let use_base_image = if let &NodeInput::Value {
|
let use_base_image = if let &NodeInput::Value {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||||
ModifyFont { font_family, font_style, size } => {
|
ModifyFont { font_family, font_style, size } => {
|
||||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||||
|
|
||||||
responses.push_back(self.create_document_operation(Operation::ModifyFont { path, font_family, font_style, size }));
|
self.create_document_operation(Operation::ModifyFont { path, font_family, font_style, size }, true, responses);
|
||||||
responses.push_back(ResendActiveProperties.into());
|
responses.push_back(ResendActiveProperties.into());
|
||||||
}
|
}
|
||||||
ModifyTransform { value, transform_op } => {
|
ModifyTransform { value, transform_op } => {
|
||||||
|
|
@ -101,33 +101,34 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||||
|
|
||||||
let transform = apply_transform_operation(layer, transform_op, value, &persistent_data.font_cache);
|
let transform = apply_transform_operation(layer, transform_op, value, &persistent_data.font_cache);
|
||||||
|
|
||||||
responses.push_back(self.create_document_operation(Operation::SetLayerTransform { path: path.clone(), transform }));
|
self.create_document_operation(Operation::SetLayerTransform { path: path.clone(), transform }, true, responses);
|
||||||
}
|
}
|
||||||
ModifyName { name } => {
|
ModifyName { name } => {
|
||||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||||
responses.push_back(self.create_document_operation(Operation::SetLayerName { path, name }))
|
self.create_document_operation(Operation::SetLayerName { path, name }, true, responses);
|
||||||
}
|
}
|
||||||
ModifyPreserveAspect { preserve_aspect } => {
|
ModifyPreserveAspect { preserve_aspect } => {
|
||||||
let (layer_path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
let (layer_path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||||
responses.push_back(self.create_document_operation(Operation::SetLayerPreserveAspect { layer_path, preserve_aspect }))
|
self.create_document_operation(Operation::SetLayerPreserveAspect { layer_path, preserve_aspect }, true, responses);
|
||||||
}
|
}
|
||||||
ModifyFill { fill } => {
|
ModifyFill { fill } => {
|
||||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||||
responses.push_back(self.create_document_operation(Operation::SetLayerFill { path, fill }));
|
self.create_document_operation(Operation::SetLayerFill { path, fill }, true, responses);
|
||||||
}
|
}
|
||||||
ModifyStroke { stroke } => {
|
ModifyStroke { stroke } => {
|
||||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||||
responses.push_back(self.create_document_operation(Operation::SetLayerStroke { path, stroke }))
|
self.create_document_operation(Operation::SetLayerStroke { path, stroke }, true, responses);
|
||||||
}
|
}
|
||||||
ModifyText { new_text } => {
|
ModifyText { new_text } => {
|
||||||
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
let (path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||||
responses.push_back(Operation::SetTextContent { path, new_text }.into())
|
self.create_document_operation(Operation::SetTextContent { path, new_text }, true, responses);
|
||||||
}
|
}
|
||||||
SetPivot { new_position } => {
|
SetPivot { new_position } => {
|
||||||
let (layer_path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
let (layer_path, _) = self.active_selection.clone().expect("Received update for properties panel with no active layer");
|
||||||
let position: Option<glam::DVec2> = new_position.into();
|
let position: Option<glam::DVec2> = new_position.into();
|
||||||
let pivot = position.unwrap().into();
|
let pivot = position.unwrap().into();
|
||||||
|
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
responses.push_back(Operation::SetPivot { layer_path, pivot }.into());
|
responses.push_back(Operation::SetPivot { layer_path, pivot }.into());
|
||||||
}
|
}
|
||||||
CheckSelectedWasUpdated { path } => {
|
CheckSelectedWasUpdated { path } => {
|
||||||
|
|
@ -187,11 +188,24 @@ impl PropertiesPanelMessageHandler {
|
||||||
matches!((last_active_path_id, last_modified), (Some(active_last), Some(modified_last)) if active_last == modified_last)
|
matches!((last_active_path_id, last_modified), (Some(active_last), Some(modified_last)) if active_last == modified_last)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_document_operation(&self, operation: Operation) -> Message {
|
fn create_document_operation(&self, operation: Operation, commit_history: bool, responses: &mut VecDeque<Message>) {
|
||||||
let (_, target_document) = self.active_selection.as_ref().unwrap();
|
let (_, target_document) = self.active_selection.as_ref().unwrap();
|
||||||
match *target_document {
|
match *target_document {
|
||||||
TargetDocument::Artboard => ArtboardMessage::DispatchOperation(Box::new(operation)).into(),
|
TargetDocument::Artboard => {
|
||||||
TargetDocument::Artwork => DocumentMessage::DispatchOperation(Box::new(operation)).into(),
|
// Commit history is not respected as the artboard document is not saved in the history system.
|
||||||
|
|
||||||
|
// Dispatch the relevant operation to the artboard document
|
||||||
|
responses.push_back(ArtboardMessage::DispatchOperation(Box::new(operation)).into())
|
||||||
|
}
|
||||||
|
TargetDocument::Artwork => {
|
||||||
|
// Commit to history before the modification
|
||||||
|
if commit_history {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch the relevant operation to the main document
|
||||||
|
responses.push_back(DocumentMessage::DispatchOperation(Box::new(operation)).into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -706,7 +706,7 @@ impl PortfolioMessageHandler {
|
||||||
let node_id = node_path[index];
|
let node_id = node_path[index];
|
||||||
inner_network.output = node_id;
|
inner_network.output = node_id;
|
||||||
|
|
||||||
let Some(new_inner) = inner_network.nodes.get_mut(&node_id).and_then(|node| node.implementation.get_network_mut()) else{
|
let Some(new_inner) = inner_network.nodes.get_mut(&node_id).and_then(|node| node.implementation.get_network_mut()) else {
|
||||||
return Err("Failed to find network".to_string());
|
return Err("Failed to find network".to_string());
|
||||||
};
|
};
|
||||||
inner_network = new_inner;
|
inner_network = new_inner;
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,8 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
|
||||||
// Send the DocumentIsDirty message to the active tool's sub-tool message handler
|
// Send the DocumentIsDirty message to the active tool's sub-tool message handler
|
||||||
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
responses.push_back(BroadcastEvent::DocumentIsDirty.into());
|
||||||
|
|
||||||
// Send Properties to the frontend
|
// Send tool options to the frontend
|
||||||
tool_data.tools.get(&tool_type).unwrap().register_properties(responses, LayoutTarget::ToolOptions);
|
responses.push_back(ToolMessage::RefreshToolOptions.into());
|
||||||
|
|
||||||
// Notify the frontend about the new active tool to be displayed
|
// Notify the frontend about the new active tool to be displayed
|
||||||
tool_data.register_properties(responses, LayoutTarget::ToolShelf);
|
tool_data.register_properties(responses, LayoutTarget::ToolShelf);
|
||||||
|
|
|
||||||
|
|
@ -310,7 +310,7 @@ impl SelectedGradient {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Clear the gradient if layer deleted
|
// Clear the gradient if layer deleted
|
||||||
let Ok(layer) = document.document_legacy.layer(&inner_gradient.path) else{
|
let Ok(layer) = document.document_legacy.layer(&inner_gradient.path) else {
|
||||||
responses.push_back(ToolMessage::RefreshToolOptions.into());
|
responses.push_back(ToolMessage::RefreshToolOptions.into());
|
||||||
*gradient = None;
|
*gradient = None;
|
||||||
return;
|
return;
|
||||||
|
|
@ -320,7 +320,7 @@ impl SelectedGradient {
|
||||||
inner_gradient.transform = gradient_space_transform(&inner_gradient.path, layer, document, font_cache);
|
inner_gradient.transform = gradient_space_transform(&inner_gradient.path, layer, document, font_cache);
|
||||||
|
|
||||||
// Clear if no longer a gradient
|
// Clear if no longer a gradient
|
||||||
let Some(gradient) = layer.style().ok().and_then(|style|style.fill().as_gradient()) else{
|
let Some(gradient) = layer.style().ok().and_then(|style|style.fill().as_gradient()) else {
|
||||||
responses.push_back(ToolMessage::RefreshToolOptions.into());
|
responses.push_back(ToolMessage::RefreshToolOptions.into());
|
||||||
*gradient = None;
|
*gradient = None;
|
||||||
return;
|
return;
|
||||||
|
|
@ -462,7 +462,7 @@ impl Fsm for GradientToolFsmState {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(GradientToolFsmState::Ready, GradientToolMessage::DeleteStop) => {
|
(GradientToolFsmState::Ready, GradientToolMessage::DeleteStop) => {
|
||||||
let Some(selected_gradient) = &mut tool_data.selected_gradient else{
|
let Some(selected_gradient) = &mut tool_data.selected_gradient else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -473,11 +473,15 @@ impl Fsm for SelectToolFsmState {
|
||||||
// If the user clicks on new shape, make that layer their new selection.
|
// If the user clicks on new shape, make that layer their new selection.
|
||||||
// Otherwise enter the box select mode
|
// Otherwise enter the box select mode
|
||||||
let state = if tool_data.pivot.is_over(input.mouse.position) {
|
let state = if tool_data.pivot.is_over(input.mouse.position) {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
|
||||||
tool_data.snap_manager.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true);
|
tool_data.snap_manager.start_snap(document, document.bounding_boxes(None, None, font_cache), true, true);
|
||||||
tool_data.snap_manager.add_all_document_handles(document, &[], &[], &[]);
|
tool_data.snap_manager.add_all_document_handles(document, &[], &[], &[]);
|
||||||
|
|
||||||
DraggingPivot
|
DraggingPivot
|
||||||
} else if let Some(selected_edges) = dragging_bounds {
|
} else if let Some(selected_edges) = dragging_bounds {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
|
||||||
let snap_x = selected_edges.2 || selected_edges.3;
|
let snap_x = selected_edges.2 || selected_edges.3;
|
||||||
let snap_y = selected_edges.0 || selected_edges.1;
|
let snap_y = selected_edges.0 || selected_edges.1;
|
||||||
|
|
||||||
|
|
@ -498,6 +502,8 @@ impl Fsm for SelectToolFsmState {
|
||||||
|
|
||||||
ResizingBounds
|
ResizingBounds
|
||||||
} else if rotating_bounds {
|
} else if rotating_bounds {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
|
||||||
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
||||||
let selected = selected.iter().collect::<Vec<_>>();
|
let selected = selected.iter().collect::<Vec<_>>();
|
||||||
let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.center_of_transformation, &selected, responses, &document.document_legacy);
|
let mut selected = Selected::new(&mut bounds.original_transforms, &mut bounds.center_of_transformation, &selected, responses, &document.document_legacy);
|
||||||
|
|
@ -679,6 +685,12 @@ impl Fsm for SelectToolFsmState {
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
(ResizingBounds, DragStop) => {
|
(ResizingBounds, DragStop) => {
|
||||||
|
let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON {
|
||||||
|
true => DocumentMessage::Undo,
|
||||||
|
false => DocumentMessage::CommitTransaction,
|
||||||
|
};
|
||||||
|
responses.push_back(response.into());
|
||||||
|
|
||||||
tool_data.snap_manager.cleanup(responses);
|
tool_data.snap_manager.cleanup(responses);
|
||||||
|
|
||||||
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
||||||
|
|
@ -688,6 +700,12 @@ impl Fsm for SelectToolFsmState {
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
(RotatingBounds, DragStop) => {
|
(RotatingBounds, DragStop) => {
|
||||||
|
let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON {
|
||||||
|
true => DocumentMessage::Undo,
|
||||||
|
false => DocumentMessage::CommitTransaction,
|
||||||
|
};
|
||||||
|
responses.push_back(response.into());
|
||||||
|
|
||||||
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
if let Some(bounds) = &mut tool_data.bounding_box_overlays {
|
||||||
bounds.original_transforms.clear();
|
bounds.original_transforms.clear();
|
||||||
}
|
}
|
||||||
|
|
@ -695,6 +713,12 @@ impl Fsm for SelectToolFsmState {
|
||||||
Ready
|
Ready
|
||||||
}
|
}
|
||||||
(DraggingPivot, DragStop) => {
|
(DraggingPivot, DragStop) => {
|
||||||
|
let response = match input.mouse.position.distance(tool_data.drag_start) < 10. * f64::EPSILON {
|
||||||
|
true => DocumentMessage::Undo,
|
||||||
|
false => DocumentMessage::CommitTransaction,
|
||||||
|
};
|
||||||
|
responses.push_back(response.into());
|
||||||
|
|
||||||
tool_data.snap_manager.cleanup(responses);
|
tool_data.snap_manager.cleanup(responses);
|
||||||
|
|
||||||
Ready
|
Ready
|
||||||
|
|
@ -769,6 +793,8 @@ impl Fsm for SelectToolFsmState {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
(_, SetPivot { position }) => {
|
(_, SetPivot { position }) => {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
|
||||||
let pos: Option<DVec2> = position.into();
|
let pos: Option<DVec2> = position.into();
|
||||||
tool_data.pivot.set_normalized_position(pos.unwrap(), document, font_cache, responses);
|
tool_data.pivot.set_normalized_position(pos.unwrap(), document, font_cache, responses);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -210,10 +210,18 @@ impl Default for TextToolFsmState {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct TextToolData {
|
struct TextToolData {
|
||||||
path: Vec<LayerId>,
|
layer_path: Vec<LayerId>,
|
||||||
overlays: Vec<Vec<LayerId>>,
|
overlays: Vec<Vec<LayerId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TextToolData {
|
||||||
|
/// Set the editing state of the currently modifying layer
|
||||||
|
fn set_editing(&self, editable: bool, responses: &mut VecDeque<Message>) {
|
||||||
|
let path = self.layer_path.clone();
|
||||||
|
responses.push_back(DocumentMessage::SetTextboxEditability { path, editable }.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn transform_from_box(pos1: DVec2, pos2: DVec2) -> [f64; 6] {
|
fn transform_from_box(pos1: DVec2, pos2: DVec2) -> [f64; 6] {
|
||||||
DAffine2::from_scale_angle_translation((pos2 - pos1).round(), 0., pos1.round() - DVec2::splat(0.5)).to_cols_array()
|
DAffine2::from_scale_angle_translation((pos2 - pos1).round(), 0., pos1.round() - DVec2::splat(0.5)).to_cols_array()
|
||||||
}
|
}
|
||||||
|
|
@ -293,55 +301,44 @@ impl Fsm for TextToolFsmState {
|
||||||
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
|
let tolerance = DVec2::splat(SELECTION_TOLERANCE);
|
||||||
let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);
|
let quad = Quad::from_box([mouse_pos - tolerance, mouse_pos + tolerance]);
|
||||||
|
|
||||||
let new_state = if let Some(l) = document
|
// Check if the user has selected an existing text layer
|
||||||
|
let new_state = if let Some(clicked_text_layer_path) = document
|
||||||
.document_legacy
|
.document_legacy
|
||||||
.intersects_quad_root(quad, font_cache)
|
.intersects_quad_root(quad, font_cache)
|
||||||
.last()
|
.last()
|
||||||
.filter(|l| document.document_legacy.layer(l).map(|l| l.as_text().is_ok()).unwrap_or(false))
|
.filter(|l| document.document_legacy.layer(l).map(|l| l.as_text().is_ok()).unwrap_or(false))
|
||||||
// Editing existing text
|
|
||||||
{
|
{
|
||||||
if state == TextToolFsmState::Editing {
|
if state == TextToolFsmState::Editing {
|
||||||
responses.push_back(
|
tool_data.set_editing(false, responses);
|
||||||
DocumentMessage::SetTextboxEditability {
|
|
||||||
path: tool_data.path.clone(),
|
|
||||||
editable: false,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tool_data.path = l.clone();
|
tool_data.layer_path = clicked_text_layer_path.clone();
|
||||||
|
|
||||||
responses.push_back(
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
DocumentMessage::SetTextboxEditability {
|
|
||||||
path: tool_data.path.clone(),
|
tool_data.set_editing(true, responses);
|
||||||
editable: true,
|
|
||||||
}
|
let replacement_selected_layers = vec![tool_data.layer_path.clone()];
|
||||||
.into(),
|
responses.push_back(DocumentMessage::SetSelectedLayers { replacement_selected_layers }.into());
|
||||||
);
|
|
||||||
responses.push_back(
|
|
||||||
DocumentMessage::SetSelectedLayers {
|
|
||||||
replacement_selected_layers: vec![tool_data.path.clone()],
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Editing
|
Editing
|
||||||
}
|
}
|
||||||
// Creating new text
|
// Create new text
|
||||||
else if state == TextToolFsmState::Ready {
|
else if state == TextToolFsmState::Ready {
|
||||||
|
responses.push_back(DocumentMessage::StartTransaction.into());
|
||||||
|
|
||||||
let transform = DAffine2::from_translation(input.mouse.position).to_cols_array();
|
let transform = DAffine2::from_translation(input.mouse.position).to_cols_array();
|
||||||
let font_size = tool_options.font_size;
|
let font_size = tool_options.font_size;
|
||||||
let font_name = tool_options.font_name.clone();
|
let font_name = tool_options.font_name.clone();
|
||||||
let font_style = tool_options.font_style.clone();
|
let font_style = tool_options.font_style.clone();
|
||||||
tool_data.path = document.get_path_for_new_layer();
|
tool_data.layer_path = document.get_path_for_new_layer();
|
||||||
|
|
||||||
responses.push_back(
|
responses.push_back(
|
||||||
Operation::AddText {
|
Operation::AddText {
|
||||||
path: tool_data.path.clone(),
|
path: tool_data.layer_path.clone(),
|
||||||
transform: DAffine2::ZERO.to_cols_array(),
|
transform: DAffine2::ZERO.to_cols_array(),
|
||||||
insert_index: -1,
|
insert_index: -1,
|
||||||
text: r#""#.to_string(),
|
text: String::new(),
|
||||||
style: style::PathStyle::new(None, Fill::solid(global_tool_data.primary_color)),
|
style: style::PathStyle::new(None, Fill::solid(global_tool_data.primary_color)),
|
||||||
size: font_size as f64,
|
size: font_size as f64,
|
||||||
font_name,
|
font_name,
|
||||||
|
|
@ -351,37 +348,22 @@ impl Fsm for TextToolFsmState {
|
||||||
);
|
);
|
||||||
responses.push_back(
|
responses.push_back(
|
||||||
Operation::SetLayerTransformInViewport {
|
Operation::SetLayerTransformInViewport {
|
||||||
path: tool_data.path.clone(),
|
path: tool_data.layer_path.clone(),
|
||||||
transform,
|
transform,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
responses.push_back(
|
tool_data.set_editing(true, responses);
|
||||||
DocumentMessage::SetTextboxEditability {
|
|
||||||
path: tool_data.path.clone(),
|
|
||||||
editable: true,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
responses.push_back(
|
let replacement_selected_layers = vec![tool_data.layer_path.clone()];
|
||||||
DocumentMessage::SetSelectedLayers {
|
|
||||||
replacement_selected_layers: vec![tool_data.path.clone()],
|
responses.push_back(DocumentMessage::SetSelectedLayers { replacement_selected_layers }.into());
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Editing
|
Editing
|
||||||
} else {
|
} else {
|
||||||
// Removing old text as editable
|
// Removing old text as editable
|
||||||
responses.push_back(
|
tool_data.set_editing(false, responses);
|
||||||
DocumentMessage::SetTextboxEditability {
|
|
||||||
path: tool_data.path.clone(),
|
|
||||||
editable: false,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
resize_overlays(&mut tool_data.overlays, responses, 0);
|
resize_overlays(&mut tool_data.overlays, responses, 0);
|
||||||
|
|
||||||
|
|
@ -392,13 +374,7 @@ impl Fsm for TextToolFsmState {
|
||||||
}
|
}
|
||||||
(state, Abort) => {
|
(state, Abort) => {
|
||||||
if state == TextToolFsmState::Editing {
|
if state == TextToolFsmState::Editing {
|
||||||
responses.push_back(
|
tool_data.set_editing(false, responses);
|
||||||
DocumentMessage::SetTextboxEditability {
|
|
||||||
path: tool_data.path.clone(),
|
|
||||||
editable: false,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resize_overlays(&mut tool_data.overlays, responses, 0);
|
resize_overlays(&mut tool_data.overlays, responses, 0);
|
||||||
|
|
@ -411,21 +387,10 @@ impl Fsm for TextToolFsmState {
|
||||||
Editing
|
Editing
|
||||||
}
|
}
|
||||||
(Editing, TextChange { new_text }) => {
|
(Editing, TextChange { new_text }) => {
|
||||||
responses.push_back(
|
let path = tool_data.layer_path.clone();
|
||||||
Operation::SetTextContent {
|
responses.push_back(Operation::SetTextContent { path, new_text }.into());
|
||||||
path: tool_data.path.clone(),
|
|
||||||
new_text,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
responses.push_back(
|
tool_data.set_editing(false, responses);
|
||||||
DocumentMessage::SetTextboxEditability {
|
|
||||||
path: tool_data.path.clone(),
|
|
||||||
editable: false,
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
|
|
||||||
resize_overlays(&mut tool_data.overlays, responses, 0);
|
resize_overlays(&mut tool_data.overlays, responses, 0);
|
||||||
|
|
||||||
|
|
@ -433,10 +398,10 @@ impl Fsm for TextToolFsmState {
|
||||||
}
|
}
|
||||||
(Editing, UpdateBounds { new_text }) => {
|
(Editing, UpdateBounds { new_text }) => {
|
||||||
resize_overlays(&mut tool_data.overlays, responses, 1);
|
resize_overlays(&mut tool_data.overlays, responses, 1);
|
||||||
let text = document.document_legacy.layer(&tool_data.path).unwrap().as_text().unwrap();
|
let text = document.document_legacy.layer(&tool_data.layer_path).unwrap().as_text().unwrap();
|
||||||
let quad = text.bounding_box(&new_text, text.load_face(font_cache));
|
let quad = text.bounding_box(&new_text, text.load_face(font_cache));
|
||||||
|
|
||||||
let transformed_quad = document.document_legacy.multiply_transforms(&tool_data.path).unwrap() * quad;
|
let transformed_quad = document.document_legacy.multiply_transforms(&tool_data.layer_path).unwrap() * quad;
|
||||||
let bounds = transformed_quad.bounding_box();
|
let bounds = transformed_quad.bounding_box();
|
||||||
|
|
||||||
let operation = Operation::SetLayerTransformInViewport {
|
let operation = Operation::SetLayerTransformInViewport {
|
||||||
|
|
|
||||||
|
|
@ -593,11 +593,17 @@ export default defineComponent({
|
||||||
|
|
||||||
// Clicked on a node
|
// Clicked on a node
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
|
let modifiedSelected = false;
|
||||||
|
|
||||||
const id = BigInt(nodeId);
|
const id = BigInt(nodeId);
|
||||||
if (e.shiftKey || e.ctrlKey) {
|
if (e.shiftKey || e.ctrlKey) {
|
||||||
|
modifiedSelected = true;
|
||||||
|
|
||||||
if (this.selected.includes(id)) this.selected.splice(this.selected.lastIndexOf(id), 1);
|
if (this.selected.includes(id)) this.selected.splice(this.selected.lastIndexOf(id), 1);
|
||||||
else this.selected.push(id);
|
else this.selected.push(id);
|
||||||
} else if (!this.selected.includes(id)) {
|
} else if (!this.selected.includes(id)) {
|
||||||
|
modifiedSelected = true;
|
||||||
|
|
||||||
this.selected = [id];
|
this.selected = [id];
|
||||||
} else {
|
} else {
|
||||||
this.selectIfNotDragged = id;
|
this.selectIfNotDragged = id;
|
||||||
|
|
@ -607,15 +613,17 @@ export default defineComponent({
|
||||||
this.draggingNodes = { startX: e.x, startY: e.y, roundX: 0, roundY: 0 };
|
this.draggingNodes = { startX: e.x, startY: e.y, roundX: 0, roundY: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
this.editor.instance.selectNodes(new BigUint64Array(this.selected));
|
if (modifiedSelected) this.editor.instance.selectNodes(new BigUint64Array(this.selected));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clicked on the graph background
|
// Clicked on the graph background
|
||||||
this.panning = true;
|
this.panning = true;
|
||||||
this.selected = [];
|
if (this.selected.length !== 0) {
|
||||||
this.editor.instance.selectNodes(new BigUint64Array(this.selected));
|
this.selected = [];
|
||||||
|
this.editor.instance.selectNodes(new BigUint64Array(this.selected));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
doubleClick(e: MouseEvent) {
|
doubleClick(e: MouseEvent) {
|
||||||
const node = (e.target as HTMLElement).closest("[data-node]") as HTMLElement | undefined;
|
const node = (e.target as HTMLElement).closest("[data-node]") as HTMLElement | undefined;
|
||||||
|
|
@ -677,12 +685,14 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
} else if (this.draggingNodes) {
|
} else if (this.draggingNodes) {
|
||||||
if (this.draggingNodes.startX === e.x || this.draggingNodes.startY === e.y) {
|
if (this.draggingNodes.startX === e.x || this.draggingNodes.startY === e.y) {
|
||||||
if (this.selectIfNotDragged !== undefined) {
|
if (this.selectIfNotDragged !== undefined && (this.selected.length !== 1 || this.selected[0] !== this.selectIfNotDragged)) {
|
||||||
this.selected = [this.selectIfNotDragged];
|
this.selected = [this.selectIfNotDragged];
|
||||||
this.editor.instance.selectNodes(new BigUint64Array(this.selected));
|
this.editor.instance.selectNodes(new BigUint64Array(this.selected));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.editor.instance.moveSelectedNodes(this.draggingNodes.roundX, this.draggingNodes.roundY);
|
|
||||||
|
if (this.selected.length > 0 && this.draggingNodes.roundX !== 0 && this.draggingNodes.roundY !== 0)
|
||||||
|
this.editor.instance.moveSelectedNodes(this.draggingNodes.roundX, this.draggingNodes.roundY);
|
||||||
|
|
||||||
// Check if this node should be inserted between two other nodes
|
// Check if this node should be inserted between two other nodes
|
||||||
if (this.selected.length === 1) {
|
if (this.selected.length === 1) {
|
||||||
|
|
|
||||||
|
|
@ -644,6 +644,9 @@ impl JsEditorHandle {
|
||||||
/// Notifies the backend that the selected nodes have been moved
|
/// Notifies the backend that the selected nodes have been moved
|
||||||
#[wasm_bindgen(js_name = moveSelectedNodes)]
|
#[wasm_bindgen(js_name = moveSelectedNodes)]
|
||||||
pub fn move_selected_nodes(&self, displacement_x: i32, displacement_y: i32) {
|
pub fn move_selected_nodes(&self, displacement_x: i32, displacement_y: i32) {
|
||||||
|
let message = DocumentMessage::StartTransaction;
|
||||||
|
self.dispatch(message);
|
||||||
|
|
||||||
let message = NodeGraphMessage::MoveSelectedNodes { displacement_x, displacement_y };
|
let message = NodeGraphMessage::MoveSelectedNodes { displacement_x, displacement_y };
|
||||||
self.dispatch(message);
|
self.dispatch(message);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ pub mod dynamic {
|
||||||
alloc::boxed::Box::new($node.eval(($(*dyn_any::downcast::<$t>($arg).unwrap()),*)) ) as Dynamic
|
alloc::boxed::Box::new($node.eval(($(*dyn_any::downcast::<$t>($arg).unwrap()),*)) ) as Dynamic
|
||||||
}
|
}
|
||||||
)else*
|
)else*
|
||||||
else{
|
else {
|
||||||
panic!("Unhandled type"); // TODO: Exit neatly (although this should probably not happen)
|
panic!("Unhandled type"); // TODO: Exit neatly (although this should probably not happen)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub fn node_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
// Extract primary input as first argument
|
// Extract primary input as first argument
|
||||||
let primary_input = function_inputs.next().expect("Primary input required - set to `()` if not needed.");
|
let primary_input = function_inputs.next().expect("Primary input required - set to `()` if not needed.");
|
||||||
let Pat::Ident(PatIdent{ident: primary_input_ident,..} ) =&*primary_input.pat else{
|
let Pat::Ident(PatIdent{ident: primary_input_ident,..} ) =&*primary_input.pat else {
|
||||||
panic!("Expected ident as primary input.");
|
panic!("Expected ident as primary input.");
|
||||||
};
|
};
|
||||||
let primary_input_ty = &primary_input.ty;
|
let primary_input_ty = &primary_input.ty;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue