Skip to content

Commit 52174fa

Browse files
4adexKeavon
andauthored
Remap Path tool point sliding to G G (#2913)
* Feat: Point sliding on G G * Code cleanup * Fix gg sliding behaviour * Fix build after merge conflict resolution * Fix slide point and add hints * Fix history in segment insertion * Code review --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent 3bcec37 commit 52174fa

File tree

3 files changed

+90
-47
lines changed

3 files changed

+90
-47
lines changed

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

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ pub enum PathToolMessage {
127127
UpdateSelectedPointsStatus {
128128
overlay_context: OverlayContext,
129129
},
130+
StartSlidingPoint,
130131
Copy {
131132
clipboard: Clipboard,
132133
},
@@ -420,6 +421,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Path
420421
DeleteAndBreakPath,
421422
ClosePath,
422423
PointerMove,
424+
StartSlidingPoint,
423425
Copy,
424426
Cut,
425427
DeleteSelected,
@@ -438,6 +440,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Path
438440
BreakPath,
439441
DeleteAndBreakPath,
440442
SwapSelectedHandles,
443+
StartSlidingPoint,
441444
Copy,
442445
Cut,
443446
DeleteSelected,
@@ -456,6 +459,7 @@ impl<'a> MessageHandler<ToolMessage, &mut ToolActionMessageContext<'a>> for Path
456459
DeleteAndBreakPath,
457460
Escape,
458461
RightClick,
462+
StartSlidingPoint,
459463
TogglePointEditing,
460464
ToggleSegmentEditing
461465
),
@@ -1208,11 +1212,6 @@ impl PathToolData {
12081212
};
12091213
let Some(vector) = document.network_interface.compute_modified_vector(layer) else { return false };
12101214

1211-
// Check that the handles of anchor point are also colinear
1212-
if !vector.colinear(*anchor) {
1213-
return false;
1214-
};
1215-
12161215
let Some(point_id) = anchor.as_anchor() else { return false };
12171216

12181217
let mut connected_segments = [None, None];
@@ -2083,10 +2082,6 @@ impl Fsm for PathToolFsmState {
20832082
}
20842083

20852084
if !tool_data.update_colinear(equidistant_state, toggle_colinear_state, tool_action_data.shape_editor, tool_action_data.document, responses) {
2086-
if snap_angle_state && lock_angle_state && tool_data.start_sliding_point(tool_action_data.shape_editor, tool_action_data.document) {
2087-
return PathToolFsmState::SlidingPoint;
2088-
}
2089-
20902085
tool_data.drag(
20912086
equidistant_state,
20922087
lock_angle_state,
@@ -2382,6 +2377,8 @@ impl Fsm for PathToolFsmState {
23822377
tool_data.ghost_outline.clear();
23832378
let extend_selection = input.keyboard.get(extend_selection as usize);
23842379
let drag_occurred = tool_data.drag_start_pos.distance(input.mouse.position) > DRAG_THRESHOLD;
2380+
let mut segment_dissolved = false;
2381+
let mut point_inserted = false;
23852382

23862383
let nearest_point = shape_editor.find_nearest_visible_point_indices(
23872384
&document.network_interface,
@@ -2402,6 +2399,7 @@ impl Fsm for PathToolFsmState {
24022399
if tool_data.delete_segment_pressed {
24032400
if let Some(vector) = document.network_interface.compute_modified_vector(segment.layer()) {
24042401
shape_editor.dissolve_segment(responses, segment.layer(), &vector, segment.segment(), segment.points());
2402+
segment_dissolved = true;
24052403
}
24062404
} else {
24072405
let is_segment_selected = shape_editor
@@ -2410,18 +2408,19 @@ impl Fsm for PathToolFsmState {
24102408
.is_some_and(|state| state.is_segment_selected(segment.segment()));
24112409

24122410
segment.adjusted_insert_and_select(shape_editor, responses, extend_selection, point_mode, is_segment_selected);
2413-
tool_data.segment = None;
2414-
tool_data.molding_info = None;
2415-
tool_data.molding_segment = false;
2416-
tool_data.temporary_adjacent_handles_while_molding = None;
2417-
return PathToolFsmState::Ready;
2411+
point_inserted = true;
24182412
}
24192413
}
24202414

24212415
tool_data.segment = None;
24222416
tool_data.molding_info = None;
24232417
tool_data.molding_segment = false;
24242418
tool_data.temporary_adjacent_handles_while_molding = None;
2419+
2420+
if segment_dissolved || point_inserted {
2421+
responses.add(DocumentMessage::EndTransaction);
2422+
return PathToolFsmState::Ready;
2423+
}
24252424
}
24262425

24272426
let segment_mode = tool_options.path_editing_mode.segment_editing_mode;
@@ -2578,6 +2577,14 @@ impl Fsm for PathToolFsmState {
25782577
shape_editor.delete_point_and_break_path(document, responses);
25792578
PathToolFsmState::Ready
25802579
}
2580+
(_, PathToolMessage::StartSlidingPoint) => {
2581+
responses.add(DocumentMessage::StartTransaction);
2582+
if tool_data.start_sliding_point(shape_editor, document) {
2583+
PathToolFsmState::SlidingPoint
2584+
} else {
2585+
PathToolFsmState::Ready
2586+
}
2587+
}
25812588
(_, PathToolMessage::Copy { clipboard }) => {
25822589
// TODO: Add support for selected segments
25832590

@@ -3297,18 +3304,14 @@ fn update_dynamic_hints(
32973304
}
32983305
}
32993306

3300-
let mut drag_selected_hints = vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")];
3307+
let drag_selected_hints = vec![HintInfo::mouse(MouseMotion::LmbDrag, "Drag Selected")];
33013308
let mut delete_selected_hints = vec![HintInfo::keys([Key::Delete], "Delete Selected")];
33023309

33033310
if at_least_one_anchor_selected {
33043311
delete_selected_hints.push(HintInfo::keys([Key::Accel], "No Dissolve").prepend_plus());
33053312
delete_selected_hints.push(HintInfo::keys([Key::Shift], "Cut Anchor").prepend_plus());
33063313
}
33073314

3308-
if single_colinear_anchor_selected {
3309-
drag_selected_hints.push(HintInfo::multi_keys([[Key::Control], [Key::Shift]], "Slide").prepend_plus());
3310-
}
3311-
33123315
let segment_edit = tool_options.path_editing_mode.segment_editing_mode;
33133316
let point_edit = tool_options.path_editing_mode.point_editing_mode;
33143317

@@ -3373,9 +3376,15 @@ fn update_dynamic_hints(
33733376
let mut groups = vec![
33743377
HintGroup(drag_selected_hints),
33753378
HintGroup(vec![HintInfo::multi_keys([[Key::KeyG], [Key::KeyR], [Key::KeyS]], "Grab/Rotate/Scale Selected")]),
3376-
HintGroup(vec![HintInfo::arrow_keys("Nudge Selected"), HintInfo::keys([Key::Shift], "10x").prepend_plus()]),
3377-
HintGroup(delete_selected_hints),
33783379
];
3380+
3381+
if single_colinear_anchor_selected {
3382+
groups.push(HintGroup(vec![HintInfo::multi_keys([[Key::KeyG], [Key::KeyG]], "Slide")]));
3383+
}
3384+
3385+
groups.push(HintGroup(vec![HintInfo::arrow_keys("Nudge Selected"), HintInfo::keys([Key::Shift], "10x").prepend_plus()]));
3386+
groups.push(HintGroup(delete_selected_hints));
3387+
33793388
hint_data.append(&mut groups);
33803389
}
33813390

editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ pub struct TransformLayerMessageHandler {
6565

6666
// Ghost outlines for Path Tool
6767
ghost_outline: Vec<(Vec<ClickTargetType>, DAffine2)>,
68+
69+
was_grabbing: bool,
6870
}
6971

7072
impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for TransformLayerMessageHandler {
@@ -122,7 +124,7 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
122124
self.local_pivot = document.metadata().document_to_viewport.inverse().transform_point2(*selected.pivot);
123125
self.grab_target = self.local_pivot;
124126
}
125-
// Here vector data from all layers is not considered which can be a problem in pivot calculation
127+
// TODO: Here vector data from all layers is not considered which can be a problem in pivot calculation
126128
else if let Some(vector) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer)) {
127129
*selected.original_transforms = OriginalTransforms::default();
128130

@@ -301,6 +303,7 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
301303
responses.add(SelectToolMessage::PivotShift { offset: None, flush: true });
302304

303305
if final_transform {
306+
self.was_grabbing = false;
304307
responses.add(OverlaysMessage::RemoveProvider(TRANSFORM_GRS_OVERLAY_PROVIDER));
305308
}
306309
}
@@ -356,34 +359,64 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
356359
if (selected_points.is_empty() && selected_segments.is_empty())
357360
|| (!using_path_tool && !using_select_tool && !using_pen_tool && !using_shape_tool)
358361
|| selected_layers.is_empty()
359-
|| transform_type.equivalent_to(self.transform_operation)
362+
|| (transform_type.equivalent_to(self.transform_operation) && !self.was_grabbing)
360363
{
361364
return;
362365
}
366+
367+
if using_path_tool && transform_type == TransformType::Grab {
368+
// Check if a single point is selected and it's a colinear point
369+
let single_anchor_selected = shape_editor.selected_points().count() == 1 && shape_editor.selected_points().any(|point| point.as_anchor().is_some());
370+
371+
if single_anchor_selected && transform_type.equivalent_to(self.transform_operation) && self.was_grabbing {
372+
let selected_nodes = &document.network_interface.selected_nodes();
373+
374+
let Some(layer) = selected_nodes.selected_layers(document.metadata()).next() else { return };
375+
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { return };
376+
let Some(anchor) = shape_editor.selected_points().next() else { return };
377+
378+
if vector_data.colinear(*anchor) {
379+
responses.add(TransformLayerMessage::CancelTransformOperation);
380+
381+
// Start sliding point
382+
responses.add(DeferMessage::AfterGraphRun {
383+
messages: vec![PathToolMessage::StartSlidingPoint.into()],
384+
});
385+
}
386+
387+
return;
388+
}
389+
390+
if transform_type.equivalent_to(self.transform_operation) {
391+
return;
392+
}
393+
394+
self.was_grabbing = true;
395+
}
363396
}
364397

365-
if let Some(vector) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer)) {
366-
if let [point] = selected_points.as_slice() {
367-
if matches!(point, ManipulatorPointId::Anchor(_)) {
368-
if let Some([handle1, handle2]) = point.get_handle_pair(&vector) {
369-
let handle1_length = handle1.length(&vector);
370-
let handle2_length = handle2.length(&vector);
371-
372-
if (handle1_length == 0. && handle2_length == 0. && !using_select_tool) || (handle1_length == f64::MAX && handle2_length == f64::MAX && !using_select_tool) {
373-
// G should work for this point but not R and S
374-
if matches!(transform_type, TransformType::Rotate | TransformType::Scale) {
375-
selected.original_transforms.clear();
376-
return;
377-
}
398+
if let Some(vector) = selected_layers.first().and_then(|&layer| document.network_interface.compute_modified_vector(layer))
399+
&& let [point] = selected_points.as_slice()
400+
{
401+
if matches!(point, ManipulatorPointId::Anchor(_)) {
402+
if let Some([handle1, handle2]) = point.get_handle_pair(&vector) {
403+
let handle1_length = handle1.length(&vector);
404+
let handle2_length = handle2.length(&vector);
405+
406+
if (handle1_length == 0. && handle2_length == 0. && !using_select_tool) || (handle1_length == f64::MAX && handle2_length == f64::MAX && !using_select_tool) {
407+
// G should work for this point but not R and S
408+
if matches!(transform_type, TransformType::Rotate | TransformType::Scale) {
409+
selected.original_transforms.clear();
410+
return;
378411
}
379412
}
380-
} else {
381-
let handle_length = point.as_handle().map(|handle| handle.length(&vector));
413+
}
414+
} else {
415+
let handle_length = point.as_handle().map(|handle| handle.length(&vector));
382416

383-
if handle_length == Some(0.) {
384-
selected.original_transforms.clear();
385-
return;
386-
}
417+
if handle_length == Some(0.) {
418+
selected.original_transforms.clear();
419+
return;
387420
}
388421
}
389422
}
@@ -409,6 +442,7 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
409442
TransformLayerMessage::CancelTransformOperation => {
410443
if using_path_tool {
411444
self.ghost_outline.clear();
445+
self.was_grabbing = false;
412446
}
413447

414448
if using_pen_tool {
@@ -499,7 +533,7 @@ impl MessageHandler<TransformLayerMessage, TransformLayerMessageContext<'_>> for
499533

500534
if self.typing.digits.is_empty() || !self.transform_operation.can_begin_typing() {
501535
match self.transform_operation {
502-
TransformOperation::None => unreachable!(),
536+
TransformOperation::None => {}
503537
TransformOperation::Grabbing(translation) => {
504538
let delta_pos = input.mouse.position - self.mouse_position;
505539
let delta_pos = (self.initial_transform * document_to_viewport.inverse()).transform_vector2(delta_pos);

node-graph/gcore/src/vector/misc.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,10 @@ pub fn bezpath_to_manipulator_groups(bezpath: &BezPath) -> (Vec<ManipulatorGroup
182182
ManipulatorGroup::new(point_to_dvec2(point2), Some(point_to_dvec2(point1)), None)
183183
}
184184
kurbo::PathEl::ClosePath => {
185-
if let Some(last_manipulators) = manipulator_groups.pop() {
186-
if let Some(first_manipulators) = manipulator_groups.first_mut() {
187-
first_manipulators.out_handle = last_manipulators.in_handle;
188-
}
185+
if let Some(last_manipulators) = manipulator_groups.pop()
186+
&& let Some(first_manipulators) = manipulator_groups.first_mut()
187+
{
188+
first_manipulators.out_handle = last_manipulators.in_handle;
189189
}
190190
is_closed = true;
191191
break;

0 commit comments

Comments
 (0)