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>
This commit is contained in:
Daragh
2024-10-30 02:48:20 -07:00
committed by GitHub
co-authored by Keavon Chambers
parent b7ba2c3637
commit 018e9839f8
5 changed files with 280 additions and 101 deletions
@@ -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;
@@ -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,
@@ -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)
}
}
@@ -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;
}
@@ -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();
}
@@ -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);
@@ -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) {