Skip to content

Commit ea59f10

Browse files
seam0s-devKeavon
andauthored
Fix editor crash due to mismanaged selected points on layers (#2640)
* Add hash sets to hold ignored points in SelectedLayerState * Fix non selected anchor dragging * Update selected points when ignoring handles or anchors * Refactor selected points status logic * Refactor ignore_handles and ignore_anchors bools to ShapeState * Add back in ignore_anchors and ignore_handles in SelectedLayerState * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent 7a2144e commit ea59f10

File tree

2 files changed

+75
-51
lines changed

2 files changed

+75
-51
lines changed

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

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -44,46 +44,67 @@ pub enum ManipulatorAngle {
4444
#[derive(Clone, Debug, Default)]
4545
pub struct SelectedLayerState {
4646
selected_points: HashSet<ManipulatorPointId>,
47+
/// Keeps track of the current state; helps avoid unnecessary computation when called by [`ShapeState`].
4748
ignore_handles: bool,
4849
ignore_anchors: bool,
50+
/// Points that are selected but ignored (when their overlays are disabled) are stored here.
51+
ignored_handle_points: HashSet<ManipulatorPointId>,
52+
ignored_anchor_points: HashSet<ManipulatorPointId>,
4953
}
5054

5155
impl SelectedLayerState {
5256
pub fn selected(&self) -> impl Iterator<Item = ManipulatorPointId> + '_ {
5357
self.selected_points.iter().copied()
5458
}
59+
5560
pub fn is_selected(&self, point: ManipulatorPointId) -> bool {
5661
self.selected_points.contains(&point)
5762
}
63+
5864
pub fn select_point(&mut self, point: ManipulatorPointId) {
59-
if (point.as_handle().is_some() && self.ignore_handles) || (point.as_anchor().is_some() && self.ignore_anchors) {
60-
return;
61-
}
6265
self.selected_points.insert(point);
6366
}
67+
6468
pub fn deselect_point(&mut self, point: ManipulatorPointId) {
65-
if (point.as_handle().is_some() && self.ignore_handles) || (point.as_anchor().is_some() && self.ignore_anchors) {
66-
return;
67-
}
6869
self.selected_points.remove(&point);
6970
}
70-
pub fn set_handles_status(&mut self, ignore: bool) {
71-
self.ignore_handles = ignore;
72-
}
73-
pub fn set_anchors_status(&mut self, ignore: bool) {
74-
self.ignore_anchors = ignore;
75-
}
76-
pub fn clear_points_force(&mut self) {
77-
self.selected_points.clear();
78-
self.ignore_handles = false;
79-
self.ignore_anchors = false;
71+
72+
pub fn ignore_handles(&mut self, status: bool) {
73+
if self.ignore_handles == !status {
74+
return;
75+
}
76+
77+
self.ignore_handles = !status;
78+
79+
if self.ignore_handles {
80+
self.ignored_handle_points.extend(self.selected_points.iter().copied().filter(|point| point.as_handle().is_some()));
81+
self.selected_points.retain(|point| !self.ignored_handle_points.contains(point));
82+
} else {
83+
self.selected_points.extend(self.ignored_handle_points.iter().copied());
84+
self.ignored_handle_points.clear();
85+
}
8086
}
81-
pub fn clear_points(&mut self) {
82-
if self.ignore_handles || self.ignore_anchors {
87+
88+
pub fn ignore_anchors(&mut self, status: bool) {
89+
if self.ignore_anchors == !status {
8390
return;
8491
}
92+
93+
self.ignore_anchors = !status;
94+
95+
if self.ignore_anchors {
96+
self.ignored_anchor_points.extend(self.selected_points.iter().copied().filter(|point| point.as_anchor().is_some()));
97+
self.selected_points.retain(|point| !self.ignored_anchor_points.contains(point));
98+
} else {
99+
self.selected_points.extend(self.ignored_anchor_points.iter().copied());
100+
self.ignored_anchor_points.clear();
101+
}
102+
}
103+
104+
pub fn clear_points(&mut self) {
85105
self.selected_points.clear();
86106
}
107+
87108
pub fn selected_points_count(&self) -> usize {
88109
self.selected_points.len()
89110
}
@@ -93,8 +114,10 @@ pub type SelectedShapeState = HashMap<LayerNodeIdentifier, SelectedLayerState>;
93114

94115
#[derive(Debug, Default)]
95116
pub struct ShapeState {
96-
// The layers we can select and edit manipulators (anchors and handles) from
117+
/// The layers we can select and edit manipulators (anchors and handles) from.
97118
pub selected_shape_state: SelectedShapeState,
119+
ignore_handles: bool,
120+
ignore_anchors: bool,
98121
}
99122

100123
#[derive(Debug)]
@@ -255,6 +278,10 @@ impl ClosestSegment {
255278

256279
// TODO Consider keeping a list of selected manipulators to minimize traversals of the layers
257280
impl ShapeState {
281+
pub fn is_point_ignored(&self, point: &ManipulatorPointId) -> bool {
282+
(point.as_handle().is_some() && self.ignore_handles) || (point.as_anchor().is_some() && self.ignore_anchors)
283+
}
284+
258285
pub fn close_selected_path(&self, document: &DocumentMessageHandler, responses: &mut VecDeque<Message>) {
259286
// First collect all selected anchor points across all layers
260287
let all_selected_points: Vec<(LayerNodeIdentifier, PointId)> = self
@@ -507,8 +534,9 @@ impl ShapeState {
507534
} else {
508535
// Select all connected points
509536
while let Some(point) = selected_stack.pop() {
510-
if !state.is_selected(ManipulatorPointId::Anchor(point)) {
511-
state.select_point(ManipulatorPointId::Anchor(point));
537+
let anchor_point = ManipulatorPointId::Anchor(point);
538+
if !state.is_selected(anchor_point) {
539+
state.select_point(anchor_point);
512540
selected_stack.extend(vector_data.connected_points(point));
513541
}
514542
}
@@ -548,27 +576,17 @@ impl ShapeState {
548576
}
549577
}
550578

551-
pub fn mark_selected_anchors(&mut self) {
552-
for state in self.selected_shape_state.values_mut() {
553-
state.set_anchors_status(false);
554-
}
555-
}
556-
557-
pub fn mark_selected_handles(&mut self) {
558-
for state in self.selected_shape_state.values_mut() {
559-
state.set_handles_status(false);
560-
}
561-
}
562-
563-
pub fn ignore_selected_anchors(&mut self) {
579+
pub fn update_selected_anchors_status(&mut self, status: bool) {
564580
for state in self.selected_shape_state.values_mut() {
565-
state.set_anchors_status(true);
581+
self.ignore_anchors = !status;
582+
state.ignore_anchors(status);
566583
}
567584
}
568585

569-
pub fn ignore_selected_handles(&mut self) {
586+
pub fn update_selected_handles_status(&mut self, status: bool) {
570587
for state in self.selected_shape_state.values_mut() {
571-
state.set_handles_status(true);
588+
self.ignore_handles = !status;
589+
state.ignore_handles(status);
572590
}
573591
}
574592

@@ -675,6 +693,10 @@ impl ShapeState {
675693
layer: LayerNodeIdentifier,
676694
responses: &mut VecDeque<Message>,
677695
) -> Option<()> {
696+
if self.is_point_ignored(point) {
697+
return None;
698+
}
699+
678700
let vector_data = network_interface.compute_modified_vector(layer)?;
679701
let transform = network_interface.document_metadata().transform_to_document(layer).inverse();
680702
let position = transform.transform_point2(new_position);
@@ -924,6 +946,10 @@ impl ShapeState {
924946
let delta = delta_transform.inverse().transform_vector2(delta);
925947

926948
for &point in state.selected_points.iter() {
949+
if self.is_point_ignored(&point) {
950+
continue;
951+
}
952+
927953
let handle = match point {
928954
ManipulatorPointId::Anchor(point) => {
929955
self.move_anchor(point, &vector_data, delta, layer, Some(state), responses);
@@ -1596,7 +1622,7 @@ impl ShapeState {
15961622
pub fn select_all_in_shape(&mut self, network_interface: &NodeNetworkInterface, selection_shape: SelectionShape, selection_change: SelectionChange) {
15971623
for (&layer, state) in &mut self.selected_shape_state {
15981624
if selection_change == SelectionChange::Clear {
1599-
state.clear_points_force()
1625+
state.clear_points()
16001626
}
16011627

16021628
let vector_data = network_interface.compute_modified_vector(layer);

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

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ pub enum PathToolMessage {
100100
},
101101
SwapSelectedHandles,
102102
UpdateOptions(PathOptionsUpdate),
103+
UpdateSelectedPointsStatus {
104+
overlay_context: OverlayContext,
105+
},
103106
}
104107

105108
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Default, serde::Serialize, serde::Deserialize, specta::Type)]
@@ -989,24 +992,18 @@ impl Fsm for PathToolFsmState {
989992
shape_editor.set_selected_layers(target_layers);
990993

991994
responses.add(OverlaysMessage::Draw);
992-
993-
responses.add(PathToolMessage::SelectedPointUpdated);
994995
self
995996
}
996-
(_, PathToolMessage::Overlays(mut overlay_context)) => {
997+
(_, PathToolMessage::UpdateSelectedPointsStatus { overlay_context }) => {
997998
let display_anchors = overlay_context.visibility_settings.anchors();
998999
let display_handles = overlay_context.visibility_settings.handles();
999-
if !display_handles {
1000-
shape_editor.ignore_selected_handles();
1001-
} else {
1002-
shape_editor.mark_selected_handles();
1003-
}
1004-
if !display_anchors {
1005-
shape_editor.ignore_selected_anchors();
1006-
} else {
1007-
shape_editor.mark_selected_anchors();
1008-
}
10091000

1001+
shape_editor.update_selected_anchors_status(display_anchors);
1002+
shape_editor.update_selected_handles_status(display_handles);
1003+
1004+
self
1005+
}
1006+
(_, PathToolMessage::Overlays(mut overlay_context)) => {
10101007
// TODO: find the segment ids of which the selected points are a part of
10111008

10121009
match tool_options.path_overlay_mode {
@@ -1133,6 +1130,7 @@ impl Fsm for PathToolFsmState {
11331130
}
11341131

11351132
responses.add(PathToolMessage::SelectedPointUpdated);
1133+
responses.add(PathToolMessage::UpdateSelectedPointsStatus { overlay_context });
11361134
self
11371135
}
11381136

0 commit comments

Comments
 (0)