Skip to content

Commit

Permalink
Add Path tool support for the Tab key swapping to dragging the opposi…
Browse files Browse the repository at this point in the history
…te handle (GraphiteEditor#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 <[email protected]>
  • Loading branch information
DaraghD and Keavon authored Oct 30, 2024
1 parent b7ba2c3 commit 018e983
Show file tree
Hide file tree
Showing 5 changed files with 281 additions and 102 deletions.
11 changes: 6 additions & 5 deletions editor/src/messages/input_mapper/input_mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub fn input_mappings() -> Mapping {
//
// SelectToolMessage
entry!(PointerMove; refresh_keys=[Control, Alt, Shift], action_dispatch=SelectToolMessage::PointerMove(SelectToolPointerKeys { axis_align: Shift, snap_angle: Control, center: Alt, duplicate: Alt })),
entry!(KeyDown(MouseLeft); action_dispatch=SelectToolMessage::DragStart { add_to_selection: Shift, select_deepest: Accel }),
entry!(KeyDown(MouseLeft); action_dispatch=SelectToolMessage::DragStart { extend_selection: Shift, select_deepest: Accel }),
entry!(KeyUp(MouseLeft); action_dispatch=SelectToolMessage::DragStop { remove_from_selection: Shift }),
entry!(KeyDown(Enter); action_dispatch=SelectToolMessage::Enter),
entry!(DoubleClick(MouseButton::Left); action_dispatch=SelectToolMessage::EditLayer),
Expand Down Expand Up @@ -204,19 +204,20 @@ pub fn input_mappings() -> Mapping {
entry!(KeyDown(Backspace); modifiers=[Accel], action_dispatch=PathToolMessage::DeleteAndBreakPath),
entry!(KeyDown(Delete); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::BreakPath),
entry!(KeyDown(Backspace); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::BreakPath),
entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { ctrl: Control, shift: Shift }),
entry!(KeyDown(Tab); action_dispatch=PathToolMessage::SwapSelectedHandles),
entry!(KeyDown(MouseLeft); action_dispatch=PathToolMessage::MouseDown { direct_insert_without_sliding: Control, extend_selection: Shift }),
entry!(KeyDown(MouseRight); action_dispatch=PathToolMessage::RightClick),
entry!(KeyDown(Escape); action_dispatch=PathToolMessage::Escape),
entry!(KeyDown(KeyG); action_dispatch=PathToolMessage::GRS { key: KeyG }),
entry!(KeyDown(KeyR); action_dispatch=PathToolMessage::GRS { key: KeyR }),
entry!(KeyDown(KeyS); action_dispatch=PathToolMessage::GRS { key: KeyS }),
entry!(PointerMove; refresh_keys=[Alt, Shift, Space], action_dispatch=PathToolMessage::PointerMove { alt: Alt, shift: Shift, move_anchor_and_handles: Space}),
entry!(PointerMove; refresh_keys=[KeyC, Shift, Alt, Space], action_dispatch=PathToolMessage::PointerMove { toggle_colinear: KeyC, equidistant: Alt, move_anchor_with_handles: Space}),
entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete),
entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=PathToolMessage::SelectAllAnchors),
entry!(KeyDown(KeyA); modifiers=[Accel, Shift], action_dispatch=PathToolMessage::DeselectAllPoints),
entry!(KeyDown(Backspace); action_dispatch=PathToolMessage::Delete),
entry!(KeyUp(MouseLeft); action_dispatch=PathToolMessage::DragStop { equidistant: Shift }),
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter { add_to_selection: Shift }),
entry!(KeyUp(MouseLeft); action_dispatch=PathToolMessage::DragStop { extend_selection: Shift }),
entry!(KeyDown(Enter); action_dispatch=PathToolMessage::Enter { extend_selection: Shift }),
entry!(DoubleClick(MouseButton::Left); action_dispatch=PathToolMessage::FlipSmoothSharp),
entry!(KeyDown(ArrowRight); action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: NUDGE_AMOUNT, delta_y: 0. }),
entry!(KeyDown(ArrowRight); modifiers=[Shift], action_dispatch=PathToolMessage::NudgeSelectedPoints { delta_x: BIG_NUDGE_AMOUNT, delta_y: 0. }),
Expand Down
83 changes: 75 additions & 8 deletions editor/src/messages/tool/common_functionality/shape_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::messages::portfolio::document::utility_types::document_metadata::{Doc
use crate::messages::portfolio::document::utility_types::misc::{GeometrySnapSource, SnapSource};
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
use crate::messages::prelude::*;
use crate::messages::tool::tool_messages::path_tool::PointSelectState;

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

#[derive(Debug, PartialEq, Copy, Clone)]
#[derive(Debug, PartialEq, Eq, Copy, Clone, Default)]
pub enum ManipulatorAngle {
#[default]
Colinear,
Free,
Mixed,
Expand Down Expand Up @@ -161,9 +163,9 @@ impl ClosestSegment {
midpoint
}

pub fn adjusted_insert_and_select(&self, shape_editor: &mut ShapeState, responses: &mut VecDeque<Message>, add_to_selection: bool) {
pub fn adjusted_insert_and_select(&self, shape_editor: &mut ShapeState, responses: &mut VecDeque<Message>, extend_selection: bool) {
let id = self.adjusted_insert(responses);
shape_editor.select_anchor_point_by_id(self.layer, id, add_to_selection)
shape_editor.select_anchor_point_by_id(self.layer, id, extend_selection)
}
}

Expand Down Expand Up @@ -221,7 +223,7 @@ impl ShapeState {

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

// Should we select or deselect the point?
let new_selected = if already_selected { !add_to_selection } else { true };
let new_selected = if already_selected { !extend_selection } else { true };

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

// This is selecting the manipulator only for now, next to generalize to points
if new_selected {
let retain_existing_selection = add_to_selection || already_selected;
let retain_existing_selection = extend_selection || already_selected;
if !retain_existing_selection {
self.deselect_all_points();
}
Expand All @@ -267,8 +269,8 @@ impl ShapeState {
None
}

pub fn select_anchor_point_by_id(&mut self, layer: LayerNodeIdentifier, id: PointId, add_to_selection: bool) {
if !add_to_selection {
pub fn select_anchor_point_by_id(&mut self, layer: LayerNodeIdentifier, id: PointId, extend_selection: bool) {
if !extend_selection {
self.deselect_all_points();
}
let point = ManipulatorPointId::Anchor(id);
Expand Down Expand Up @@ -1060,6 +1062,71 @@ impl ShapeState {
_ => self.sorted_selected_layers(network_interface.document_metadata()).find_map(closest_seg),
}
}
pub fn get_dragging_state(&self, network_interface: &NodeNetworkInterface) -> PointSelectState {
for &layer in self.selected_shape_state.keys() {
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };

for point in self.selected_points() {
if point.as_anchor().is_some() {
return PointSelectState::Anchor;
}
if point.get_handle_pair(&vector_data).is_some() {
return PointSelectState::HandleWithPair;
}
}
}
PointSelectState::HandleNoPair
}

/// Returns true if at least one handle with pair is selected
pub fn handle_with_pair_selected(&mut self, network_interface: &NodeNetworkInterface) -> bool {
for &layer in self.selected_shape_state.keys() {
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };

for point in self.selected_points() {
if point.as_anchor().is_some() {
return false;
}
if point.get_handle_pair(&vector_data).is_some() {
return true;
}
}
}

false
}

/// Alternate selected handles to mirrors
pub fn alternate_selected_handles(&mut self, network_interface: &NodeNetworkInterface) {
let mut handles_to_update = Vec::new();

for &layer in self.selected_shape_state.keys() {
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { continue };

for point in self.selected_points() {
if point.as_anchor().is_some() {
continue;
}
if let Some(handles) = point.get_handle_pair(&vector_data) {
// handle[0] is selected, handle[1] is opposite / mirror handle
handles_to_update.push((layer, handles[0].to_manipulator_point(), handles[1].to_manipulator_point()));
}
}
}

for (layer, handle_to_deselect, handle_to_select) in handles_to_update {
if let Some(state) = self.selected_shape_state.get_mut(&layer) {
let points = &state.selected_points;
let both_selected = points.contains(&handle_to_deselect) && points.contains(&handle_to_select);
if both_selected {
continue;
}

state.deselect_point(handle_to_deselect);
state.select_point(handle_to_select);
}
}
}

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

0 comments on commit 018e983

Please sign in to comment.