mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Allow the Pen tool to connect layers by their endpoints, merging into a single layer (#2076)
* Merge vector data layers * Fix early return * Fix layer space multiplication error * Recalculate positions when changing layer space * Add transform node * Remove pen tool layer state
This commit is contained in:
parent
018e9839f8
commit
c7b08246c2
8 changed files with 229 additions and 33 deletions
|
|
@ -1,7 +1,7 @@
|
|||
use super::transform_utils;
|
||||
use super::utility_types::ModifyInputsContext;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface};
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::{InputConnector, NodeNetworkInterface, OutputConnector};
|
||||
use crate::messages::portfolio::document::utility_types::nodes::CollapsedLayers;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
|
|
@ -95,14 +95,7 @@ impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for Gr
|
|||
}
|
||||
}
|
||||
GraphOperationMessage::SetUpstreamToChain { layer } => {
|
||||
let Some(first_chain_node) = network_interface
|
||||
.upstream_flow_back_from_nodes(
|
||||
vec![layer.to_node()],
|
||||
&[],
|
||||
crate::messages::portfolio::document::utility_types::network_interface::FlowType::HorizontalFlow,
|
||||
)
|
||||
.nth(1)
|
||||
else {
|
||||
let Some(OutputConnector::Node { node_id: first_chain_node, .. }) = network_interface.upstream_output_connector(&InputConnector::node(layer.to_node(), 1), &[]) else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ pub enum NodeGraphMessage {
|
|||
output_connector: OutputConnector,
|
||||
input_connector: InputConnector,
|
||||
},
|
||||
ConnectUpstreamOutputToInput {
|
||||
downstream_input: InputConnector,
|
||||
input_connector: InputConnector,
|
||||
},
|
||||
Cut,
|
||||
DeleteNodes {
|
||||
node_ids: Vec<NodeId>,
|
||||
|
|
@ -70,6 +74,10 @@ pub enum NodeGraphMessage {
|
|||
parent: LayerNodeIdentifier,
|
||||
insert_index: usize,
|
||||
},
|
||||
MoveNodeToChainStart {
|
||||
node_id: NodeId,
|
||||
parent: LayerNodeIdentifier,
|
||||
},
|
||||
PasteNodes {
|
||||
serialized_nodes: String,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -210,6 +210,16 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
});
|
||||
responses.add(NodeGraphMessage::SendGraph);
|
||||
}
|
||||
NodeGraphMessage::ConnectUpstreamOutputToInput { downstream_input, input_connector } => {
|
||||
let Some(upstream_node) = network_interface.upstream_output_connector(&downstream_input, selection_network_path) else {
|
||||
log::error!("Failed to find upstream node for downstream_input");
|
||||
return;
|
||||
};
|
||||
responses.add(NodeGraphMessage::CreateWire {
|
||||
output_connector: upstream_node,
|
||||
input_connector,
|
||||
});
|
||||
}
|
||||
NodeGraphMessage::Cut => {
|
||||
responses.add(NodeGraphMessage::Copy);
|
||||
responses.add(NodeGraphMessage::DeleteSelectedNodes { delete_children: true });
|
||||
|
|
@ -323,6 +333,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
|
|||
NodeGraphMessage::MoveLayerToStack { layer, parent, insert_index } => {
|
||||
network_interface.move_layer_to_stack(layer, parent, insert_index, selection_network_path);
|
||||
}
|
||||
NodeGraphMessage::MoveNodeToChainStart { node_id, parent } => {
|
||||
network_interface.move_node_to_chain_start(&node_id, parent, selection_network_path);
|
||||
}
|
||||
NodeGraphMessage::PasteNodes { serialized_nodes } => {
|
||||
let data = match serde_json::from_str::<Vec<(NodeId, NodeTemplate)>>(&serialized_nodes) {
|
||||
Ok(d) => d,
|
||||
|
|
|
|||
|
|
@ -88,6 +88,10 @@ impl DocumentMetadata {
|
|||
self.upstream_transforms.get(&node_id).copied().map(|(_, transform)| transform).unwrap_or(DAffine2::IDENTITY)
|
||||
}
|
||||
|
||||
pub fn downstream_transform_to_document(&self, layer: LayerNodeIdentifier) -> DAffine2 {
|
||||
self.document_to_viewport.inverse() * self.downstream_transform_to_viewport(layer)
|
||||
}
|
||||
|
||||
pub fn downstream_transform_to_viewport(&self, layer: LayerNodeIdentifier) -> DAffine2 {
|
||||
if layer == LayerNodeIdentifier::ROOT_PARENT {
|
||||
return self.transform_to_viewport(layer);
|
||||
|
|
|
|||
|
|
@ -4894,6 +4894,32 @@ impl NodeNetworkInterface {
|
|||
self.create_wire(&OutputConnector::node(*node_id, 0), &InputConnector::node(parent.to_node(), 1), network_path);
|
||||
self.set_chain_position(node_id, network_path);
|
||||
} else {
|
||||
// TODO: Implement a more robust horizontal shift system when inserting a node into a chain.
|
||||
// This should be done by breaking the chain and shifting the sole dependents for each node upstream of the insertion.
|
||||
// Before inserting the node, shift the layer right 7 units so that all sole dependents are also shifted
|
||||
// let input_connector = InputConnector::node(parent.to_node(), 0);
|
||||
// let old_upstream = self.upstream_output_connector(&input_connector, network_path);
|
||||
// This also needs to disconnect from the downstream layer
|
||||
// self.disconnect_input(&input_connector, network_path);
|
||||
// let Some(selected_nodes) = self.selected_nodes_mut(network_path) else {
|
||||
// log::error!("Could not get selected nodes in move_layer_to_stack");
|
||||
// return;
|
||||
// };
|
||||
// let old_selected_nodes = selected_nodes.replace_with(vec![parent.to_node()]);
|
||||
|
||||
// for _ in 0..7 {
|
||||
// self.shift_selected_nodes(Direction::Left, false, network_path);
|
||||
// }
|
||||
// // Grip drag it back to the right
|
||||
// for _ in 0..7 {
|
||||
// self.shift_selected_nodes(Direction::Right, true, network_path);
|
||||
// }
|
||||
// let _ = self.selected_nodes_mut(network_path).unwrap().replace_with(old_selected_nodes);
|
||||
// if let Some(old_upstream) = old_upstream {
|
||||
// self.create_wire(&old_upstream, &input_connector, network_path);
|
||||
// }
|
||||
|
||||
// Insert the node in the gap and set the upstream to a chain
|
||||
self.insert_node_between(node_id, &InputConnector::node(parent.to_node(), 1), 0, network_path);
|
||||
self.force_set_upstream_to_chain(node_id, network_path);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ use graphene_std::vector::PointId;
|
|||
use glam::DVec2;
|
||||
|
||||
/// Determines if a path should be extended. Goal in viewport space. Returns the path and if it is extending from the start, if applicable.
|
||||
pub fn should_extend(document: &DocumentMessageHandler, goal: DVec2, tolerance: f64) -> Option<(LayerNodeIdentifier, PointId, DVec2)> {
|
||||
pub fn should_extend(document: &DocumentMessageHandler, goal: DVec2, tolerance: f64, layers: impl Iterator<Item = LayerNodeIdentifier>) -> Option<(LayerNodeIdentifier, PointId, DVec2)> {
|
||||
let mut best = None;
|
||||
let mut best_distance_squared = tolerance * tolerance;
|
||||
|
||||
for layer in document.network_interface.selected_nodes(&[]).unwrap().selected_layers(document.metadata()) {
|
||||
for layer in layers {
|
||||
let viewspace = document.metadata().transform_to_viewport(layer);
|
||||
|
||||
let vector_data = document.network_interface.compute_modified_vector(layer)?;
|
||||
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
|
||||
continue;
|
||||
};
|
||||
for id in vector_data.single_connected_points() {
|
||||
let Some(point) = vector_data.point_domain.position_from_id(id) else { continue };
|
||||
|
||||
|
|
|
|||
|
|
@ -211,7 +211,8 @@ impl Fsm for FreehandToolFsmState {
|
|||
tool_data.weight = tool_options.line_weight;
|
||||
|
||||
// Extend an endpoint of the selected path
|
||||
if let Some((layer, point, position)) = should_extend(document, input.mouse.position, crate::consts::SNAP_POINT_TOLERANCE) {
|
||||
let selected_nodes = document.network_interface.selected_nodes(&[]).unwrap();
|
||||
if let Some((layer, point, position)) = should_extend(document, input.mouse.position, crate::consts::SNAP_POINT_TOLERANCE, selected_nodes.selected_layers(document.metadata())) {
|
||||
tool_data.layer = Some(layer);
|
||||
tool_data.end_point = Some((position, point));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use super::tool_prelude::*;
|
||||
use crate::consts::{DEFAULT_STROKE_WIDTH, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE};
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::{self, resolve_document_node_type};
|
||||
use crate::messages::portfolio::document::overlays::utility_functions::path_overlays;
|
||||
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::InputConnector;
|
||||
use crate::messages::tool::common_functionality::auto_panning::AutoPanning;
|
||||
use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType};
|
||||
use crate::messages::tool::common_functionality::graph_modification_utils;
|
||||
|
|
@ -57,6 +58,7 @@ pub enum PenToolMessage {
|
|||
Redo,
|
||||
Undo,
|
||||
UpdateOptions(PenOptionsUpdate),
|
||||
RecalculateLatestPointsPosition,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
|
|
@ -202,7 +204,6 @@ struct LastPoint {
|
|||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct PenToolData {
|
||||
layer: Option<LayerNodeIdentifier>,
|
||||
snap_manager: SnapManager,
|
||||
latest_points: Vec<LastPoint>,
|
||||
point_index: usize,
|
||||
|
|
@ -215,6 +216,8 @@ struct PenToolData {
|
|||
angle: f64,
|
||||
auto_panning: AutoPanning,
|
||||
modifiers: ModifierState,
|
||||
|
||||
buffering_merged_vector: bool,
|
||||
}
|
||||
impl PenToolData {
|
||||
fn latest_point(&self) -> Option<&LastPoint> {
|
||||
|
|
@ -231,6 +234,24 @@ impl PenToolData {
|
|||
self.latest_points.push(point);
|
||||
}
|
||||
|
||||
// When the vector data transform changes, the positions of the points must be recalculated.
|
||||
fn recalculate_latest_points_position(&mut self, document: &DocumentMessageHandler) {
|
||||
let selected_nodes = document.network_interface.selected_nodes(&[]).unwrap();
|
||||
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
||||
if let (Some(layer), None) = (selected_layers.next(), selected_layers.next()) {
|
||||
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
|
||||
return;
|
||||
};
|
||||
for point in &mut self.latest_points {
|
||||
let Some(pos) = vector_data.point_domain.position_from_id(point.id) else {
|
||||
continue;
|
||||
};
|
||||
point.pos = pos;
|
||||
point.handle_start = point.pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If the user places the anchor on top of the previous anchor, it becomes sharp and the outgoing handle may be dragged.
|
||||
fn bend_from_previous_point(&mut self, snap_data: SnapData, transform: DAffine2) {
|
||||
self.g1_continuous = true;
|
||||
|
|
@ -266,7 +287,9 @@ impl PenToolData {
|
|||
|
||||
// Get close path
|
||||
let mut end = None;
|
||||
let layer = self.layer?;
|
||||
let selected_nodes = document.network_interface.selected_nodes(&[]).unwrap();
|
||||
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
||||
let layer = selected_layers.next().filter(|_| selected_layers.next().is_none())?;
|
||||
let vector_data = document.network_interface.compute_modified_vector(layer)?;
|
||||
let start = self.latest_point()?.id;
|
||||
let transform = document.metadata().document_to_viewport * transform;
|
||||
|
|
@ -426,13 +449,13 @@ impl Fsm for PenToolFsmState {
|
|||
..
|
||||
} = tool_action_data;
|
||||
|
||||
let mut transform = tool_data.layer.map(|layer| document.metadata().transform_to_document(layer)).unwrap_or_default();
|
||||
let selected_nodes = document.network_interface.selected_nodes(&[]).unwrap();
|
||||
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
||||
let layer = selected_layers.next().filter(|_| selected_layers.next().is_none());
|
||||
let mut transform = layer.map(|layer| document.metadata().transform_to_document(layer)).unwrap_or_default();
|
||||
|
||||
if !transform.inverse().is_finite() {
|
||||
let parent_transform = tool_data
|
||||
.layer
|
||||
.and_then(|layer| layer.parent(document.metadata()))
|
||||
.map(|layer| document.metadata().transform_to_document(layer));
|
||||
let parent_transform = layer.and_then(|layer| layer.parent(document.metadata())).map(|layer| document.metadata().transform_to_document(layer));
|
||||
|
||||
transform = parent_transform.unwrap_or(DAffine2::IDENTITY);
|
||||
}
|
||||
|
|
@ -511,19 +534,23 @@ impl Fsm for PenToolFsmState {
|
|||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, None, false);
|
||||
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
|
||||
let selected_nodes = document.network_interface.selected_nodes(&[]).unwrap();
|
||||
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
||||
// Perform extension of an existing path
|
||||
if let Some((layer, point, position)) = should_extend(document, viewport, crate::consts::SNAP_POINT_TOLERANCE) {
|
||||
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,
|
||||
in_segment: None,
|
||||
handle_start: position,
|
||||
});
|
||||
tool_data.layer = Some(layer);
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
|
||||
tool_data.next_point = position;
|
||||
tool_data.next_handle_start = position;
|
||||
} else if let Some(layer) = tool_data.layer {
|
||||
} 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();
|
||||
|
|
@ -539,6 +566,7 @@ 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())];
|
||||
|
|
@ -547,7 +575,7 @@ impl Fsm for PenToolFsmState {
|
|||
let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, parent, responses);
|
||||
tool_options.fill.apply_fill(layer, responses);
|
||||
tool_options.stroke.apply_stroke(tool_options.line_weight, layer, responses);
|
||||
tool_data.layer = Some(layer);
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
|
||||
responses.add(Message::StartBuffer);
|
||||
responses.add(PenToolMessage::DragStart);
|
||||
return PenToolFsmState::Ready;
|
||||
|
|
@ -556,12 +584,135 @@ impl Fsm for PenToolFsmState {
|
|||
// Enter the dragging handle state while the mouse is held down, allowing the user to move the mouse and position the handle
|
||||
PenToolFsmState::DraggingHandle
|
||||
}
|
||||
(state, PenToolMessage::RecalculateLatestPointsPosition) => {
|
||||
tool_data.recalculate_latest_points_position(document);
|
||||
state
|
||||
}
|
||||
(PenToolFsmState::PlacingAnchor, PenToolMessage::DragStart) => {
|
||||
if tool_data.handle_end.is_some() {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position));
|
||||
let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, None, false);
|
||||
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
|
||||
// Early return if the buffer was started and this message is being run again after the buffer (so that place_anchor updates the state with the newly merged vector)
|
||||
if tool_data.buffering_merged_vector {
|
||||
tool_data.buffering_merged_vector = false;
|
||||
tool_data.bend_from_previous_point(SnapData::new(document, input), transform);
|
||||
tool_data.place_anchor(SnapData::new(document, input), transform, input.mouse.position, responses);
|
||||
tool_data.buffering_merged_vector = false;
|
||||
PenToolFsmState::DraggingHandle
|
||||
} else {
|
||||
if tool_data.handle_end.is_some() {
|
||||
responses.add(DocumentMessage::StartTransaction);
|
||||
}
|
||||
// Merge two layers if the point is connected to the end point of another path
|
||||
|
||||
// This might not be the correct solution to artboards being included as the other layer, which occurs due to the compute_modified_vector call in should_extend using the click targets for a layer instead of vector data.
|
||||
let layers = LayerNodeIdentifier::ROOT_PARENT
|
||||
.descendants(document.metadata())
|
||||
.filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[]));
|
||||
if let Some((other_layer, _, _)) = should_extend(document, viewport, crate::consts::SNAP_POINT_TOLERANCE, layers) {
|
||||
let selected_nodes = document.network_interface.selected_nodes(&[]).unwrap();
|
||||
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
||||
if let Some(current_layer) = selected_layers.next().filter(|current_layer| selected_layers.next().is_none() && *current_layer != other_layer) {
|
||||
// Calculate the downstream transforms in order to bring the other vector data into the same layer space
|
||||
let current_transform = document.metadata().downstream_transform_to_document(current_layer);
|
||||
let other_transform = document.metadata().downstream_transform_to_document(other_layer);
|
||||
// Represents the change in position that would occur if the other layer was moved below the current layer
|
||||
let transform_delta = current_transform * other_transform.inverse();
|
||||
let offset = transform_delta.inverse();
|
||||
responses.add(GraphOperationMessage::TransformChange {
|
||||
layer: other_layer,
|
||||
transform: offset,
|
||||
transform_in: crate::messages::portfolio::document::graph_operation::utility_types::TransformIn::Local,
|
||||
skip_rerender: false,
|
||||
});
|
||||
|
||||
// Move the other layer below the current layer for positioning purposes
|
||||
let current_layer_parent = current_layer.parent(document.metadata()).unwrap();
|
||||
let current_layer_index = current_layer_parent.children(document.metadata()).position(|child| child == current_layer).unwrap();
|
||||
responses.add(NodeGraphMessage::MoveLayerToStack {
|
||||
layer: other_layer,
|
||||
parent: current_layer_parent,
|
||||
insert_index: current_layer_index + 1,
|
||||
});
|
||||
|
||||
// Merge the inputs of the two layers
|
||||
let merge_node_id = NodeId::new();
|
||||
let merge_node = document_node_definitions::resolve_document_node_type("Merge")
|
||||
.expect("Failed to create merge node")
|
||||
.default_node_template();
|
||||
responses.add(NodeGraphMessage::InsertNode {
|
||||
node_id: merge_node_id,
|
||||
node_template: merge_node,
|
||||
});
|
||||
responses.add(NodeGraphMessage::SetToNodeOrLayer {
|
||||
node_id: merge_node_id,
|
||||
is_layer: false,
|
||||
});
|
||||
responses.add(NodeGraphMessage::MoveNodeToChainStart {
|
||||
node_id: merge_node_id,
|
||||
parent: current_layer,
|
||||
});
|
||||
responses.add(NodeGraphMessage::ConnectUpstreamOutputToInput {
|
||||
downstream_input: InputConnector::node(other_layer.to_node(), 1),
|
||||
input_connector: InputConnector::node(merge_node_id, 1),
|
||||
});
|
||||
responses.add(NodeGraphMessage::DeleteNodes {
|
||||
node_ids: vec![other_layer.to_node()],
|
||||
delete_children: false,
|
||||
});
|
||||
|
||||
// Add a flatten vector elements node after the merge
|
||||
let flatten_node_id = NodeId::new();
|
||||
let flatten_node = document_node_definitions::resolve_document_node_type("Flatten Vector Elements")
|
||||
.expect("Failed to create flatten node")
|
||||
.default_node_template();
|
||||
responses.add(NodeGraphMessage::InsertNode {
|
||||
node_id: flatten_node_id,
|
||||
node_template: flatten_node,
|
||||
});
|
||||
responses.add(NodeGraphMessage::MoveNodeToChainStart {
|
||||
node_id: flatten_node_id,
|
||||
parent: current_layer,
|
||||
});
|
||||
|
||||
// Add a path node after the flatten node
|
||||
let path_node_id = NodeId::new();
|
||||
let path_node = document_node_definitions::resolve_document_node_type("Path")
|
||||
.expect("Failed to create path node")
|
||||
.default_node_template();
|
||||
responses.add(NodeGraphMessage::InsertNode {
|
||||
node_id: path_node_id,
|
||||
node_template: path_node,
|
||||
});
|
||||
responses.add(NodeGraphMessage::MoveNodeToChainStart {
|
||||
node_id: path_node_id,
|
||||
parent: current_layer,
|
||||
});
|
||||
|
||||
// Add a transform node to ensure correct tooling modifications
|
||||
let transform_node_id = NodeId::new();
|
||||
let transform_node = document_node_definitions::resolve_document_node_type("Transform")
|
||||
.expect("Failed to create transform node")
|
||||
.default_node_template();
|
||||
responses.add(NodeGraphMessage::InsertNode {
|
||||
node_id: transform_node_id,
|
||||
node_template: transform_node,
|
||||
});
|
||||
responses.add(NodeGraphMessage::MoveNodeToChainStart {
|
||||
node_id: transform_node_id,
|
||||
parent: current_layer,
|
||||
});
|
||||
|
||||
responses.add(NodeGraphMessage::RunDocumentGraph);
|
||||
responses.add(Message::StartBuffer);
|
||||
responses.add(PenToolMessage::RecalculateLatestPointsPosition);
|
||||
}
|
||||
}
|
||||
// Even if no buffer was started, the message still has to be run again in order to call bend_from_previous_point
|
||||
tool_data.buffering_merged_vector = true;
|
||||
responses.add(PenToolMessage::DragStart);
|
||||
PenToolFsmState::PlacingAnchor
|
||||
}
|
||||
tool_data.bend_from_previous_point(SnapData::new(document, input), transform);
|
||||
PenToolFsmState::DraggingHandle
|
||||
}
|
||||
(PenToolFsmState::DraggingHandle, PenToolMessage::DragStop) => tool_data
|
||||
.finish_placing_handle(SnapData::new(document, input), transform, responses)
|
||||
|
|
@ -633,7 +784,7 @@ impl Fsm for PenToolFsmState {
|
|||
}
|
||||
(PenToolFsmState::DraggingHandle | PenToolFsmState::PlacingAnchor, PenToolMessage::Abort | PenToolMessage::Confirm) => {
|
||||
responses.add(DocumentMessage::EndTransaction);
|
||||
tool_data.layer = None;
|
||||
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: Vec::new() });
|
||||
tool_data.handle_end = None;
|
||||
tool_data.latest_points.clear();
|
||||
tool_data.point_index = 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue