Skip to content

Commit 018e983

Browse files
DaraghDKeavon
andauthored
Add Path tool support for the Tab key swapping to dragging the opposite handle (#2058)
* feat: tab alternates between handles * fix: handle hints, remove anchor to handle switch Added specific handle hints, Can no longer switch to handle if just anchor is selected typo fix * fix: no longer deselect on esc/rclick * feat: hides cursor when switching A pointerlock implementation would be ideal in the future to keep the screen from panning, * fix: tidy up dynamic tool hints switch colinear to V * fix: can no longer hide cursor if anchor selected remove debug statement * fix: clippy * Solve some issues and remap V to C to toggle colinear * Cleanup + change equidistant key from Shift to Alt --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent b7ba2c3 commit 018e983

File tree

5 files changed

+281
-102
lines changed

5 files changed

+281
-102
lines changed

editor/src/messages/input_mapper/input_mappings.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ pub fn input_mappings() -> Mapping {
9393
//
9494
// SelectToolMessage
9595
entry!(PointerMove; refresh_keys=[Control, Alt, Shift], action_dispatch=SelectToolMessage::PointerMove(SelectToolPointerKeys { axis_align: Shift, snap_angle: Control, center: Alt, duplicate: Alt })),
96-
entry!(KeyDown(MouseLeft); action_dispatch=SelectToolMessage::DragStart { add_to_selection: Shift, select_deepest: Accel }),
96+
entry!(KeyDown(MouseLeft); action_dispatch=SelectToolMessage::DragStart { extend_selection: Shift, select_deepest: Accel }),
9797
entry!(KeyUp(MouseLeft); action_dispatch=SelectToolMessage::DragStop { remove_from_selection: Shift }),
9898
entry!(KeyDown(Enter); action_dispatch=SelectToolMessage::Enter),
9999
entry!(DoubleClick(MouseButton::Left); action_dispatch=SelectToolMessage::EditLayer),
@@ -204,19 +204,20 @@ pub fn input_mappings() -> Mapping {
204204
entry!(KeyDown(Backspace); modifiers=[Accel], action_dispatch=PathToolMessage::DeleteAndBreakPath),
205205
entry!(KeyDown(Delete); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::BreakPath),
206206
entry!(KeyDown(Backspace); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::BreakPath),
207-
entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { ctrl: Control, shift: Shift }),
207+
entry!(KeyDown(Tab); action_dispatch=PathToolMessage::SwapSelectedHandles),
208+
entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { direct_insert_without_sliding: Control, extend_selection: Shift }),
208209
entry!(KeyDown(MouseRight); action_dispatch=PathToolMessage::RightClick),
209210
entry!(KeyDown(Escape); action_dispatch=PathToolMessage::Escape),
210211
entry!(KeyDown(KeyG); action_dispatch=PathToolMessage::GRS { key: KeyG }),
211212
entry!(KeyDown(KeyR); action_dispatch=PathToolMessage::GRS { key: KeyR }),
212213
entry!(KeyDown(KeyS); action_dispatch=PathToolMessage::GRS { key: KeyS }),
213-
entry!(PointerMove; refresh_keys=[Alt, Shift, Space], action_dispatch=PathToolMessage::PointerMove { alt: Alt, shift: Shift, move_anchor_and_handles: Space}),
214+
entry!(PointerMove; refresh_keys=[KeyC, Shift, Alt, Space], action_dispatch=PathToolMessage::PointerMove { toggle_colinear: KeyC, equidistant: Alt, move_anchor_with_handles: Space}),
214215
entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete),
215216
entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=PathToolMessage::SelectAllAnchors),
216217
entry!(KeyDown(KeyA); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::DeselectAllPoints),
217218
entry!(KeyDown(Backspace); action_dispatch=PathToolMessage::Delete),
218-
entry!(KeyUp(MouseLeft); action_dispatch=PathToolMessage::DragStop { equidistant: Shift }),
219-
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter { add_to_selection: Shift }),
219+
entry!(KeyUp(MouseLeft); action_dispatch=PathToolMessage::DragStop { extend_selection: Shift }),
220+
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter { extend_selection: Shift }),
220221
entry!(DoubleClick(MouseButton::Left); action_dispatch=PathToolMessage::FlipSmoothSharp),
221222
entry!(KeyDown(ArrowRight); action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: 0. }),
222223
entry!(KeyDown(ArrowRight); modifiers=[Shift], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: 0. }),

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

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::{Doc
44
use crate::messages::portfolio::document::utility_types::misc::{GeometrySnapSource, SnapSource};
55
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
66
use crate::messages::prelude::*;
7+
use crate::messages::tool::tool_messages::path_tool::PointSelectState;
78

89
use bezier_rs::{Bezier, BezierHandles, TValue};
910
use graphene_core::transform::Transform;
@@ -12,8 +13,9 @@ use graphene_core::vector::{ManipulatorPointId, PointId, VectorData, VectorModif
1213
use glam::DVec2;
1314
use graphene_std::vector::{HandleId, SegmentId};
1415

15-
#[derive(Debug, PartialEq, Copy, Clone)]
16+
#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
1617
pub enum ManipulatorAngle {
18+
#[default]
1719
Colinear,
1820
Free,
1921
Mixed,
@@ -161,9 +163,9 @@ impl ClosestSegment {
161163
midpoint
162164
}
163165

164-
pub fn adjusted_insert_and_select(&self, shape_editor: &mut ShapeState, responses: &mut VecDeque<Message>, add_to_selection: bool) {
166+
pub fn adjusted_insert_and_select(&self, shape_editor: &mut ShapeState, responses: &mut VecDeque<Message>, extend_selection: bool) {
165167
let id = self.adjusted_insert(responses);
166-
shape_editor.select_anchor_point_by_id(self.layer, id, add_to_selection)
168+
shape_editor.select_anchor_point_by_id(self.layer, id, extend_selection)
167169
}
168170
}
169171

@@ -221,7 +223,7 @@ impl ShapeState {
221223

222224
/// Select/deselect the first point within the selection threshold.
223225
/// Returns a tuple of the points if found and the offset, or `None` otherwise.
224-
pub fn change_point_selection(&mut self, network_interface: &NodeNetworkInterface, mouse_position: DVec2, select_threshold: f64, add_to_selection: bool) -> Option<Option<SelectedPointsInfo>> {
226+
pub fn change_point_selection(&mut self, network_interface: &NodeNetworkInterface, mouse_position: DVec2, select_threshold: f64, extend_selection: bool) -> Option<Option<SelectedPointsInfo>> {
225227
if self.selected_shape_state.is_empty() {
226228
return None;
227229
}
@@ -234,14 +236,14 @@ impl ShapeState {
234236
let already_selected = selected_shape_state.is_selected(manipulator_point_id);
235237

236238
// Should we select or deselect the point?
237-
let new_selected = if already_selected { !add_to_selection } else { true };
239+
let new_selected = if already_selected { !extend_selection } else { true };
238240

239241
// Offset to snap the selected point to the cursor
240242
let offset = mouse_position - network_interface.document_metadata().transform_to_viewport(layer).transform_point2(point_position);
241243

242244
// This is selecting the manipulator only for now, next to generalize to points
243245
if new_selected {
244-
let retain_existing_selection = add_to_selection || already_selected;
246+
let retain_existing_selection = extend_selection || already_selected;
245247
if !retain_existing_selection {
246248
self.deselect_all_points();
247249
}
@@ -267,8 +269,8 @@ impl ShapeState {
267269
None
268270
}
269271

270-
pub fn select_anchor_point_by_id(&mut self, layer: LayerNodeIdentifier, id: PointId, add_to_selection: bool) {
271-
if !add_to_selection {
272+
pub fn select_anchor_point_by_id(&mut self, layer: LayerNodeIdentifier, id: PointId, extend_selection: bool) {
273+
if !extend_selection {
272274
self.deselect_all_points();
273275
}
274276
let point = ManipulatorPointId::Anchor(id);
@@ -1060,6 +1062,71 @@ impl ShapeState {
10601062
_ => self.sorted_selected_layers(network_interface.document_metadata()).find_map(closest_seg),
10611063
}
10621064
}
1065+
pub fn get_dragging_state(&self, network_interface: &NodeNetworkInterface) -> PointSelectState {
1066+
for &layer in self.selected_shape_state.keys() {
1067+
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };
1068+
1069+
for point in self.selected_points() {
1070+
if point.as_anchor().is_some() {
1071+
return PointSelectState::Anchor;
1072+
}
1073+
if point.get_handle_pair(&vector_data).is_some() {
1074+
return PointSelectState::HandleWithPair;
1075+
}
1076+
}
1077+
}
1078+
PointSelectState::HandleNoPair
1079+
}
1080+
1081+
/// Returns true if at least one handle with pair is selected
1082+
pub fn handle_with_pair_selected(&mut self, network_interface: &NodeNetworkInterface) -> bool {
1083+
for &layer in self.selected_shape_state.keys() {
1084+
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };
1085+
1086+
for point in self.selected_points() {
1087+
if point.as_anchor().is_some() {
1088+
return false;
1089+
}
1090+
if point.get_handle_pair(&vector_data).is_some() {
1091+
return true;
1092+
}
1093+
}
1094+
}
1095+
1096+
false
1097+
}
1098+
1099+
/// Alternate selected handles to mirrors
1100+
pub fn alternate_selected_handles(&mut self, network_interface: &NodeNetworkInterface) {
1101+
let mut handles_to_update = Vec::new();
1102+
1103+
for &layer in self.selected_shape_state.keys() {
1104+
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };
1105+
1106+
for point in self.selected_points() {
1107+
if point.as_anchor().is_some() {
1108+
continue;
1109+
}
1110+
if let Some(handles) = point.get_handle_pair(&vector_data) {
1111+
// handle[0] is selected, handle[1] is opposite / mirror handle
1112+
handles_to_update.push((layer, handles[0].to_manipulator_point(), handles[1].to_manipulator_point()));
1113+
}
1114+
}
1115+
}
1116+
1117+
for (layer, handle_to_deselect, handle_to_select) in handles_to_update {
1118+
if let Some(state) = self.selected_shape_state.get_mut(&layer) {
1119+
let points = &state.selected_points;
1120+
let both_selected = points.contains(&handle_to_deselect) && points.contains(&handle_to_select);
1121+
if both_selected {
1122+
continue;
1123+
}
1124+
1125+
state.deselect_point(handle_to_deselect);
1126+
state.select_point(handle_to_select);
1127+
}
1128+
}
1129+
}
10631130

10641131
/// Selects handles and anchor connected to current handle
10651132
pub fn select_handles_and_anchor_connected_to_current_handle(&mut self, network_interface: &NodeNetworkInterface) {

0 commit comments

Comments
 (0)