Improve Pen tool segment drawing confirm/cancel behavior (#2555)

* implement enter to end path

* fixed_abort

* added_hints

* edge-case

* Code review

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
0SlowPoke0 2025-04-12 15:20:35 +05:30 committed by GitHub
parent 69069ef723
commit c156c0a4ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 67 additions and 12 deletions

View File

@ -1169,7 +1169,7 @@ impl NodeNetworkInterface {
if is_layer && *reference == Some("Merge".to_string()) {
"Untitled Layer".to_string()
} else {
reference.clone().unwrap_or("Untitled node".to_string())
reference.clone().unwrap_or("Untitled Node".to_string())
}
} else {
self.display_name(node_id, network_path)

View File

@ -356,6 +356,7 @@ struct PenToolData {
path_closed: bool,
handle_mode: HandleMode,
prior_segment_layer: Option<LayerNodeIdentifier>,
prior_segment_endpoint: Option<PointId>,
prior_segment: Option<SegmentId>,
handle_type: TargetHandle,
@ -380,6 +381,13 @@ impl PenToolData {
self.latest_points.push(point);
}
fn cleanup(&mut self, responses: &mut VecDeque<Message>) {
self.handle_end = None;
self.latest_points.clear();
self.point_index = 0;
self.snap_manager.cleanup(responses);
}
/// Check whether target handle is primary, end, or `self.handle_end`
fn check_end_handle_type(&self, vector_data: &VectorData) -> TargetHandle {
match (self.handle_end, self.prior_segment_endpoint, self.prior_segment, self.path_closed) {
@ -1253,9 +1261,13 @@ impl PenToolData {
let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document);
let tolerance = crate::consts::SNAP_POINT_TOLERANCE;
self.prior_segment = None;
self.prior_segment_endpoint = None;
self.prior_segment_layer = None;
if let Some((layer, point, _position)) = closest_point(document, viewport, tolerance, document.metadata().all_layers(), |_| false, preferences) {
self.prior_segment_endpoint = Some(point);
self.prior_segment_layer = Some(layer);
let vector_data = document.network_interface.compute_modified_vector(layer).unwrap();
let segment = vector_data.all_connected(point).collect::<Vec<_>>().first().map(|s| s.segment);
self.prior_segment = segment;
@ -1902,16 +1914,62 @@ impl Fsm for PenToolFsmState {
state
}
(PenToolFsmState::DraggingHandle(..) | PenToolFsmState::PlacingAnchor, PenToolMessage::Confirm) => {
(PenToolFsmState::DraggingHandle(..), PenToolMessage::Confirm) => {
// Confirm to end path
if let Some((vector_data, layer)) = layer.and_then(|layer| document.network_interface.compute_modified_vector(layer)).zip(layer) {
let single_point_in_layer = vector_data.point_domain.ids().len() == 1;
tool_data.finish_placing_handle(SnapData::new(document, input), transform, preferences, responses);
let latest_points = tool_data.latest_points.len() == 1;
if latest_points && single_point_in_layer {
responses.add(NodeGraphMessage::DeleteNodes {
node_ids: vec![layer.to_node()],
delete_children: true,
});
responses.add(NodeGraphMessage::RunDocumentGraph);
} else if (latest_points && tool_data.prior_segment_endpoint.is_none())
|| (tool_data.prior_segment_endpoint.is_some() && tool_data.prior_segment_layer != Some(layer) && latest_points)
{
let vector_modification = VectorModificationType::RemovePoint {
id: tool_data.latest_point().unwrap().id,
};
responses.add(GraphOperationMessage::Vector {
layer,
modification_type: vector_modification,
});
responses.add(PenToolMessage::Abort);
} else {
responses.add(DocumentMessage::EndTransaction);
}
}
tool_data.cleanup(responses);
tool_data.cleanup_target_selections(shape_editor, layer, document, responses);
responses.add(OverlaysMessage::Draw);
PenToolFsmState::Ready
}
(PenToolFsmState::PlacingAnchor, PenToolMessage::Confirm) => {
responses.add(DocumentMessage::EndTransaction);
tool_data.handle_end = None;
tool_data.latest_points.clear();
tool_data.point_index = 0;
tool_data.snap_manager.cleanup(responses);
tool_data.cleanup(responses);
tool_data.cleanup_target_selections(shape_editor, layer, document, responses);
PenToolFsmState::Ready
}
(PenToolFsmState::DraggingHandle(..), PenToolMessage::Abort) => {
if tool_data.handle_end.is_none() {
responses.add(DocumentMessage::AbortTransaction);
tool_data.cleanup(responses);
tool_data.cleanup_target_selections(shape_editor, layer, document, responses);
PenToolFsmState::Ready
} else {
tool_data
.place_anchor(SnapData::new(document, input), transform, input.mouse.position, preferences, responses)
.unwrap_or(PenToolFsmState::Ready)
}
}
(_, PenToolMessage::Abort) => {
let should_delete_layer = if layer.is_some() {
let vector_data = document.network_interface.compute_modified_vector(layer.unwrap()).unwrap();
@ -1921,10 +1979,7 @@ impl Fsm for PenToolFsmState {
};
responses.add(DocumentMessage::AbortTransaction);
tool_data.handle_end = None;
tool_data.latest_points.clear();
tool_data.point_index = 0;
tool_data.snap_manager.cleanup(responses);
tool_data.cleanup(responses);
tool_data.cleanup_target_selections(shape_editor, layer, document, responses);
if should_delete_layer {
@ -1986,8 +2041,8 @@ impl Fsm for PenToolFsmState {
let mut dragging_hint_data = HintData(Vec::new());
dragging_hint_data.0.push(HintGroup(vec![
HintInfo::mouse(MouseMotion::Rmb, ""),
HintInfo::keys([Key::Escape], "").prepend_slash(),
HintInfo::keys([Key::Enter], "End Path").prepend_slash(),
HintInfo::keys([Key::Escape], "Cancel Segment").prepend_slash(),
HintInfo::keys([Key::Enter], "End Path"),
]));
let mut toggle_group = match mode {