Skip to content

Commit 09ece94

Browse files
4adexKeavon
andauthored
Fix undo/redo history related issues with the Path tool (#3111)
* Fix transactions * A little cleanup * Fix bug in delete * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent 9a32e79 commit 09ece94

3 files changed

Lines changed: 57 additions & 16 deletions

File tree

editor/src/messages/tool/common_functionality/shape_editor.rs

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1383,7 +1383,9 @@ impl ShapeState {
13831383
}
13841384

13851385
/// Dissolve the selected points.
1386-
pub fn delete_selected_points(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
1386+
pub fn delete_selected_points(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, start_transaction: bool) {
1387+
let mut transaction_started = false;
1388+
13871389
for (&layer, state) in &mut self.selected_shape_state {
13881390
let mut missing_anchors = HashMap::new();
13891391
let mut deleted_anchors = HashSet::new();
@@ -1392,6 +1394,11 @@ impl ShapeState {
13921394
let selected_segments = &state.selected_segments;
13931395

13941396
for point in std::mem::take(&mut state.selected_points) {
1397+
if !transaction_started && start_transaction {
1398+
responses.add(DocumentMessage::AddTransaction);
1399+
transaction_started = true;
1400+
}
1401+
13951402
match point {
13961403
ManipulatorPointId::Anchor(anchor) => {
13971404
if let Some(handles) = Self::dissolve_anchor(anchor, responses, layer, &vector) {
@@ -1488,34 +1495,51 @@ impl ShapeState {
14881495
}
14891496
}
14901497

1491-
pub fn delete_selected_segments(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
1498+
pub fn delete_selected_segments(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, start_transaction: bool) -> bool {
1499+
let mut transaction_started = false;
1500+
14921501
for (&layer, state) in &self.selected_shape_state {
14931502
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
14941503

14951504
for (segment, _, start, end) in vector.segment_bezier_iter() {
14961505
if state.selected_segments.contains(&segment) {
1506+
if start_transaction && !transaction_started {
1507+
responses.add(DocumentMessage::AddTransaction);
1508+
transaction_started = true;
1509+
}
14971510
self.dissolve_segment(responses, layer, &vector, segment, [start, end]);
14981511
}
14991512
}
15001513
}
1514+
1515+
transaction_started
15011516
}
15021517

1503-
pub fn delete_hanging_selected_anchors(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
1518+
pub fn delete_hanging_selected_anchors(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>, start_transaction: bool) {
1519+
let mut transaction_started = false;
1520+
15041521
for (&layer, state) in &self.selected_shape_state {
15051522
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
15061523

15071524
for point in &state.selected_points {
1508-
if let ManipulatorPointId::Anchor(anchor) = point {
1509-
if vector.all_connected(*anchor).all(|segment| state.is_segment_selected(segment.segment)) {
1510-
let modification_type = VectorModificationType::RemovePoint { id: *anchor };
1511-
responses.add(GraphOperationMessage::Vector { layer, modification_type });
1525+
if let ManipulatorPointId::Anchor(anchor) = point
1526+
&& vector.all_connected(*anchor).all(|segment| state.is_segment_selected(segment.segment))
1527+
{
1528+
if !transaction_started && start_transaction {
1529+
responses.add(DocumentMessage::AddTransaction);
1530+
transaction_started = true
15121531
}
1532+
let modification_type = VectorModificationType::RemovePoint { id: *anchor };
1533+
responses.add(GraphOperationMessage::Vector { layer, modification_type });
15131534
}
15141535
}
15151536
}
15161537
}
15171538

1539+
/// Note: this also adds a history transaction if there is some change in state.
15181540
pub fn break_path_at_selected_point(&self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
1541+
let mut transaction_started = false;
1542+
15191543
for (&layer, state) in &self.selected_shape_state {
15201544
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
15211545

@@ -1527,6 +1551,11 @@ impl ShapeState {
15271551
for handle in vector.all_connected(point) {
15281552
// Disable the g1 continuous
15291553
for &handles in &vector.colinear_manipulators {
1554+
if !transaction_started {
1555+
responses.add(DocumentMessage::AddTransaction);
1556+
transaction_started = true;
1557+
}
1558+
15301559
if handles.contains(&handle) {
15311560
let modification_type = VectorModificationType::SetG1Continuous { handles, enabled: false };
15321561
responses.add(GraphOperationMessage::Vector { layer, modification_type });
@@ -1539,6 +1568,11 @@ impl ShapeState {
15391568
continue;
15401569
}
15411570

1571+
if !transaction_started {
1572+
responses.add(DocumentMessage::AddTransaction);
1573+
transaction_started = true;
1574+
}
1575+
15421576
// Create new point
15431577
let id = PointId::generate();
15441578
let modification_type = VectorModificationType::InsertPoint { id, position: pos };
@@ -1559,13 +1593,20 @@ impl ShapeState {
15591593
}
15601594

15611595
/// Delete point(s) and adjacent segments.
1562-
pub fn delete_point_and_break_path(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
1596+
/// Note: this also adds a history transaction if there is some change in state, and true is returned if so.
1597+
pub fn delete_point_and_break_path(&mut self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) -> bool {
1598+
let mut transaction_started = false;
1599+
15631600
for (&layer, state) in &mut self.selected_shape_state {
15641601
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { continue };
15651602

15661603
for delete in std::mem::take(&mut state.selected_points) {
15671604
let Some(point) = delete.get_anchor(&vector) else { continue };
15681605

1606+
if !transaction_started {
1607+
responses.add(DocumentMessage::AddTransaction);
1608+
transaction_started = true;
1609+
}
15691610
// Delete point
15701611
let modification_type = VectorModificationType::RemovePoint { id: point };
15711612
responses.add(GraphOperationMessage::Vector { layer, modification_type });
@@ -1577,6 +1618,8 @@ impl ShapeState {
15771618
}
15781619
}
15791620
}
1621+
1622+
transaction_started
15801623
}
15811624

15821625
/// Disable colinear handles colinear.

editor/src/messages/tool/tool_messages/path_tool.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2611,17 +2611,15 @@ impl Fsm for PathToolFsmState {
26112611
// Delete key
26122612
(_, PathToolMessage::Delete) => {
26132613
// Delete the selected points and clean up overlays
2614-
responses.add(DocumentMessage::AddTransaction);
26152614
let point_mode = tool_options.path_editing_mode.point_editing_mode;
26162615
let segment_mode = tool_options.path_editing_mode.segment_editing_mode;
2617-
26182616
let only_segment_mode = segment_mode && !point_mode;
26192617

2620-
shape_editor.delete_selected_segments(document, responses);
2618+
let transaction_started = shape_editor.delete_selected_segments(document, responses, true);
26212619
if only_segment_mode {
2622-
shape_editor.delete_hanging_selected_anchors(document, responses);
2620+
shape_editor.delete_hanging_selected_anchors(document, responses, !transaction_started);
26232621
} else {
2624-
shape_editor.delete_selected_points(document, responses);
2622+
shape_editor.delete_selected_points(document, responses, !transaction_started);
26252623
}
26262624
responses.add(PathToolMessage::SelectionChanged);
26272625

@@ -2841,8 +2839,8 @@ impl Fsm for PathToolFsmState {
28412839
}
28422840
(_, PathToolMessage::DeleteSelected) => {
28432841
// Delete the selected points and segments
2844-
shape_editor.delete_point_and_break_path(document, responses);
2845-
shape_editor.delete_selected_segments(document, responses);
2842+
let deleted_some_point = shape_editor.delete_point_and_break_path(document, responses);
2843+
shape_editor.delete_selected_segments(document, responses, !deleted_some_point);
28462844

28472845
PathToolFsmState::Ready
28482846
}

editor/src/messages/tool/tool_messages/pen_tool.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ impl PenToolData {
599599
self.g1_continuous = true;
600600
let document = snap_data.document;
601601
self.next_handle_start = self.next_point;
602-
let vector = document.network_interface.compute_modified_vector(layer).unwrap();
602+
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return };
603603
self.update_handle_type(TargetHandle::FuturePreviewOutHandle);
604604
self.handle_mode = HandleMode::ColinearLocked;
605605

0 commit comments

Comments
 (0)