1
1
use super :: select_tool:: extend_lasso;
2
2
use super :: tool_prelude:: * ;
3
3
use crate :: consts:: {
4
- COLOR_OVERLAY_BLUE , COLOR_OVERLAY_GREEN , COLOR_OVERLAY_RED , DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD , DRAG_THRESHOLD , HANDLE_ROTATE_SNAP_ANGLE , SEGMENT_INSERTION_DISTANCE ,
5
- SEGMENT_OVERLAY_SIZE , SELECTION_THRESHOLD , SELECTION_TOLERANCE ,
4
+ COLOR_OVERLAY_BLUE , COLOR_OVERLAY_GREEN , COLOR_OVERLAY_RED , DOUBLE_CLICK_MILLISECONDS , DRAG_DIRECTION_MODE_DETERMINATION_THRESHOLD , DRAG_THRESHOLD , HANDLE_ROTATE_SNAP_ANGLE ,
5
+ SEGMENT_INSERTION_DISTANCE , SEGMENT_OVERLAY_SIZE , SELECTION_THRESHOLD , SELECTION_TOLERANCE ,
6
6
} ;
7
7
use crate :: messages:: portfolio:: document:: overlays:: utility_functions:: { path_overlays, selected_segments} ;
8
8
use crate :: messages:: portfolio:: document:: overlays:: utility_types:: { DrawHandles , OverlayContext } ;
@@ -12,7 +12,7 @@ use crate::messages::portfolio::document::utility_types::transformation::Axis;
12
12
use crate :: messages:: preferences:: SelectionMode ;
13
13
use crate :: messages:: tool:: common_functionality:: auto_panning:: AutoPanning ;
14
14
use crate :: messages:: tool:: common_functionality:: shape_editor:: {
15
- ClosestSegment , ManipulatorAngle , OpposingHandleLengths , SelectedPointsInfo , SelectionChange , SelectionShape , SelectionShapeType , ShapeState ,
15
+ ClosestSegment , ManipulatorAngle , OpposingHandleLengths , SelectedLayerState , SelectedPointsInfo , SelectionChange , SelectionShape , SelectionShapeType , ShapeState ,
16
16
} ;
17
17
use crate :: messages:: tool:: common_functionality:: snapping:: { SnapCache , SnapCandidatePoint , SnapConstraint , SnapData , SnapManager } ;
18
18
use crate :: messages:: tool:: common_functionality:: utility_functions:: { calculate_segment_angle, find_two_param_best_approximate} ;
@@ -58,7 +58,10 @@ pub enum PathToolMessage {
58
58
} ,
59
59
Escape ,
60
60
ClosePath ,
61
- FlipSmoothSharp ,
61
+ DoubleClick {
62
+ extend_selection : Key ,
63
+ shrink_selection : Key ,
64
+ } ,
62
65
GRS {
63
66
// Should be `Key::KeyG` (Grab), `Key::KeyR` (Rotate), or `Key::KeyS` (Scale)
64
67
key : Key ,
@@ -319,7 +322,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
319
322
fn actions ( & self ) -> ActionList {
320
323
match self . fsm_state {
321
324
PathToolFsmState :: Ready => actions ! ( PathToolMessageDiscriminant ;
322
- FlipSmoothSharp ,
325
+ DoubleClick ,
323
326
MouseDown ,
324
327
Delete ,
325
328
NudgeSelectedPoints ,
@@ -334,7 +337,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
334
337
PathToolFsmState :: Dragging ( _) => actions ! ( PathToolMessageDiscriminant ;
335
338
Escape ,
336
339
RightClick ,
337
- FlipSmoothSharp ,
340
+ DoubleClick ,
338
341
DragStop ,
339
342
PointerMove ,
340
343
Delete ,
@@ -343,7 +346,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathToo
343
346
SwapSelectedHandles ,
344
347
) ,
345
348
PathToolFsmState :: Drawing { .. } => actions ! ( PathToolMessageDiscriminant ;
346
- FlipSmoothSharp ,
349
+ DoubleClick ,
347
350
DragStop ,
348
351
PointerMove ,
349
352
Delete ,
@@ -462,6 +465,8 @@ struct PathToolData {
462
465
adjacent_anchor_offset : Option < DVec2 > ,
463
466
sliding_point_info : Option < SlidingPointInfo > ,
464
467
started_drawing_from_inside : bool ,
468
+ first_selected_with_single_click : bool ,
469
+ stored_selection : Option < HashMap < LayerNodeIdentifier , SelectedLayerState > > ,
465
470
}
466
471
467
472
impl PathToolData {
@@ -544,8 +549,9 @@ impl PathToolData {
544
549
545
550
self . drag_start_pos = input. mouse . position ;
546
551
547
- if ! self . saved_points_before_anchor_convert_smooth_sharp . is_empty ( ) && ( input. time - self . last_click_time > 500 ) {
552
+ if input. time - self . last_click_time > DOUBLE_CLICK_MILLISECONDS {
548
553
self . saved_points_before_anchor_convert_smooth_sharp . clear ( ) ;
554
+ self . stored_selection = None ;
549
555
}
550
556
551
557
self . last_click_time = input. time ;
@@ -685,20 +691,18 @@ impl PathToolData {
685
691
PathToolFsmState :: MoldingSegment
686
692
}
687
693
}
688
- // We didn't find a segment, so consider selecting the nearest shape instead and start drawing
694
+ // If no other layers are selected and this is a single-click, then also select the layer (exception)
689
695
else if let Some ( layer) = document. click ( input) {
690
- shape_editor. deselect_all_points ( ) ;
691
- shape_editor. deselect_all_segments ( ) ;
692
- if extend_selection {
693
- responses. add ( NodeGraphMessage :: SelectedNodesAdd { nodes : vec ! [ layer. to_node( ) ] } ) ;
694
- } else {
696
+ if shape_editor. selected_shape_state . is_empty ( ) {
697
+ self . first_selected_with_single_click = true ;
695
698
responses. add ( NodeGraphMessage :: SelectedNodesSet { nodes : vec ! [ layer. to_node( ) ] } ) ;
696
699
}
697
- self . drag_start_pos = input. mouse . position ;
698
- self . previous_mouse_position = document. metadata ( ) . document_to_viewport . inverse ( ) . transform_point2 ( input. mouse . position ) ;
699
700
700
701
self . started_drawing_from_inside = true ;
701
702
703
+ self . drag_start_pos = input. mouse . position ;
704
+ self . previous_mouse_position = document. metadata ( ) . document_to_viewport . inverse ( ) . transform_point2 ( input. mouse . position ) ;
705
+
702
706
let selection_shape = if lasso_select { SelectionShapeType :: Lasso } else { SelectionShapeType :: Box } ;
703
707
PathToolFsmState :: Drawing { selection_shape }
704
708
}
@@ -1557,7 +1561,9 @@ impl Fsm for PathToolFsmState {
1557
1561
} ,
1558
1562
) => {
1559
1563
tool_data. previous_mouse_position = document. metadata ( ) . document_to_viewport . inverse ( ) . transform_point2 ( input. mouse . position ) ;
1564
+
1560
1565
tool_data. started_drawing_from_inside = false ;
1566
+ tool_data. stored_selection = None ;
1561
1567
1562
1568
if selection_shape == SelectionShapeType :: Lasso {
1563
1569
extend_lasso ( & mut tool_data. lasso_polygon , input. mouse . position ) ;
@@ -1604,6 +1610,7 @@ impl Fsm for PathToolFsmState {
1604
1610
break_colinear_molding,
1605
1611
} ,
1606
1612
) => {
1613
+ tool_data. stored_selection = None ;
1607
1614
let mut selected_only_handles = true ;
1608
1615
1609
1616
let selected_points = shape_editor. selected_points ( ) ;
@@ -1727,6 +1734,7 @@ impl Fsm for PathToolFsmState {
1727
1734
if tool_data. adjacent_anchor_offset . is_some ( ) {
1728
1735
tool_data. adjacent_anchor_offset = None ;
1729
1736
}
1737
+ tool_data. stored_selection = None ;
1730
1738
1731
1739
responses. add ( OverlaysMessage :: Draw ) ;
1732
1740
@@ -1895,12 +1903,16 @@ impl Fsm for PathToolFsmState {
1895
1903
SelectionMode :: Directional => tool_data. calculate_selection_mode_from_direction ( document. metadata ( ) ) ,
1896
1904
selection_mode => selection_mode,
1897
1905
} ;
1906
+ tool_data. started_drawing_from_inside = false ;
1898
1907
1899
1908
if tool_data. drag_start_pos . distance ( previous_mouse) < 1e-8 {
1900
- // If click happens inside of a shape then don't set selected nodes to empty
1901
- if document. click ( input) . is_none ( ) {
1902
- responses . add ( NodeGraphMessage :: SelectedNodesSet { nodes : vec ! [ ] } ) ;
1909
+ // Clicked inside or outside the shape then deselect all of the points/segments
1910
+ if document. click ( input) . is_some ( ) && tool_data . stored_selection . is_none ( ) {
1911
+ tool_data . stored_selection = Some ( shape_editor . selected_shape_state . clone ( ) ) ;
1903
1912
}
1913
+
1914
+ shape_editor. deselect_all_points ( ) ;
1915
+ shape_editor. deselect_all_segments ( ) ;
1904
1916
} else {
1905
1917
match selection_shape {
1906
1918
SelectionShapeType :: Box => {
@@ -2072,8 +2084,8 @@ impl Fsm for PathToolFsmState {
2072
2084
shape_editor. delete_point_and_break_path ( document, responses) ;
2073
2085
PathToolFsmState :: Ready
2074
2086
}
2075
- ( _, PathToolMessage :: FlipSmoothSharp ) => {
2076
- // Double-clicked on a point
2087
+ ( _, PathToolMessage :: DoubleClick { extend_selection , shrink_selection } ) => {
2088
+ // Double-clicked on a point (flip smooth/sharp behavior)
2077
2089
let nearest_point = shape_editor. find_nearest_point_indices ( & document. network_interface , input. mouse . position , SELECTION_THRESHOLD ) ;
2078
2090
if nearest_point. is_some ( ) {
2079
2091
// Flip the selected point between smooth and sharp
@@ -2090,13 +2102,70 @@ impl Fsm for PathToolFsmState {
2090
2102
2091
2103
return PathToolFsmState :: Ready ;
2092
2104
}
2093
-
2094
2105
// Double-clicked on a filled region
2095
- if let Some ( layer) = document. click ( input) {
2096
- // Select all points in the layer
2097
- shape_editor. select_connected_anchors ( document, layer, input. mouse . position ) ;
2106
+ else if let Some ( layer) = document. click ( input) {
2107
+ let extend_selection = input. keyboard . get ( extend_selection as usize ) ;
2108
+ let shrink_selection = input. keyboard . get ( shrink_selection as usize ) ;
2109
+
2110
+ if shape_editor. is_selected_layer ( layer) {
2111
+ if extend_selection && !tool_data. first_selected_with_single_click {
2112
+ responses. add ( NodeGraphMessage :: SelectedNodesRemove { nodes : vec ! [ layer. to_node( ) ] } ) ;
2113
+
2114
+ if let Some ( selection) = & tool_data. stored_selection {
2115
+ let mut selection = selection. clone ( ) ;
2116
+ selection. remove ( & layer) ;
2117
+ shape_editor. selected_shape_state = selection;
2118
+ tool_data. stored_selection = None ;
2119
+ }
2120
+ } else if shrink_selection && !tool_data. first_selected_with_single_click {
2121
+ // Only deselect all the points of the double clicked layer
2122
+ if let Some ( selection) = & tool_data. stored_selection {
2123
+ let selection = selection. clone ( ) ;
2124
+ shape_editor. selected_shape_state = selection;
2125
+ tool_data. stored_selection = None ;
2126
+ }
2127
+
2128
+ let state = shape_editor. selected_shape_state . get_mut ( & layer) . expect ( "No state for selected layer" ) ;
2129
+ state. deselect_all_points_in_layer ( ) ;
2130
+ state. deselect_all_segments_in_layer ( ) ;
2131
+ } else if !tool_data. first_selected_with_single_click {
2132
+ // Select according to the selected editing mode
2133
+ let point_editing_mode = tool_options. path_editing_mode . point_editing_mode ;
2134
+ let segment_editing_mode = tool_options. path_editing_mode . segment_editing_mode ;
2135
+ shape_editor. select_connected ( document, layer, input. mouse . position , point_editing_mode, segment_editing_mode) ;
2136
+
2137
+ // Select all the other layers back again
2138
+ if let Some ( selection) = & tool_data. stored_selection {
2139
+ let mut selection = selection. clone ( ) ;
2140
+ selection. remove ( & layer) ;
2141
+
2142
+ for ( layer, state) in selection {
2143
+ shape_editor. selected_shape_state . insert ( layer, state) ;
2144
+ }
2145
+ tool_data. stored_selection = None ;
2146
+ }
2147
+ }
2148
+
2149
+ // If it was the very first click without there being an existing selection,
2150
+ // then the single-click behavior and double-click behavior should not collide
2151
+ tool_data. first_selected_with_single_click = false ;
2152
+ } else if extend_selection {
2153
+ responses. add ( NodeGraphMessage :: SelectedNodesAdd { nodes : vec ! [ layer. to_node( ) ] } ) ;
2154
+
2155
+ if let Some ( selection) = & tool_data. stored_selection {
2156
+ shape_editor. selected_shape_state = selection. clone ( ) ;
2157
+ tool_data. stored_selection = None ;
2158
+ }
2159
+ } else {
2160
+ responses. add ( NodeGraphMessage :: SelectedNodesSet { nodes : vec ! [ layer. to_node( ) ] } ) ;
2161
+ }
2162
+
2098
2163
responses. add ( OverlaysMessage :: Draw ) ;
2099
2164
}
2165
+ // Double clicked on the background
2166
+ else {
2167
+ responses. add ( NodeGraphMessage :: SelectedNodesSet { nodes : vec ! [ ] } ) ;
2168
+ }
2100
2169
2101
2170
PathToolFsmState :: Ready
2102
2171
}
0 commit comments