Selecting an individual anchor point deselects each of the other points (#963)

* Selecting an individual anchor point deselects each of the other points

* git gra

* Selecting intersecting shapes,selects top shape

* Merge commit

* cargo fmt

* fixed: can't drag multiple selected shapes.

* Merge commit

* Merge commit

* Code review tweaks

* Merge commit

* Fixed topmost layer and previous_mouse_position

* Fixed issues when clicking on anchors

* Works

* Function name change

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
Co-authored-by: Dennis Kobert <dennis@kobert.dev>
This commit is contained in:
Christopher Mendoza 2023-02-19 23:26:02 -08:00 committed by Keavon Chambers
parent 964cf6df15
commit b7f2163998
5 changed files with 57 additions and 13 deletions

View File

@ -1062,6 +1062,13 @@ impl Document {
}
Some(vec![LayerChanged { path: layer_path.clone() }])
}
Operation::SelectAllAnchors { layer_path } => {
let layer = self.layer_mut(&layer_path)?;
if let Some(subpath) = layer.as_subpath_mut() {
subpath.select_all_anchors();
}
Some(vec![LayerChanged { path: layer_path.clone() }])
}
Operation::DeselectAllManipulatorPoints { layer_path } => {
let layer = self.layer_mut(&layer_path)?;
if let Some(shape) = layer.as_subpath_mut() {

View File

@ -125,6 +125,9 @@ pub enum Operation {
DeselectAllManipulatorPoints {
layer_path: Vec<LayerId>,
},
SelectAllAnchors {
layer_path: Vec<LayerId>,
},
DuplicateLayer {
path: Vec<LayerId>,
},

View File

@ -167,7 +167,7 @@ pub fn default_mapping() -> Mapping {
entry!(PointerMove; refresh_keys=[Alt, Shift], action_dispatch=PathToolMessage::PointerMove { alt_mirror_angle: Alt, shift_mirror_distance: Shift }),
entry!(KeyDown(Delete); action_dispatch=PathToolMessage::Delete),
entry!(KeyDown(Backspace); action_dispatch=PathToolMessage::Delete),
entry!(KeyUp(Lmb); action_dispatch=PathToolMessage::DragStop),
entry!(KeyUp(Lmb); action_dispatch=PathToolMessage::DragStop { shift_mirror_distance: Shift }),
entry!(DoubleClick; action_dispatch=PathToolMessage::InsertPoint),
//
// PenToolMessage

View File

@ -103,6 +103,7 @@ impl ShapeEditor {
manipulator_group_id,
manipulator_type: ManipulatorType::from_index(manipulator_point_index),
};
points.push(point_info);
responses.push_back(
Operation::SelectManipulatorPoints {
@ -187,6 +188,11 @@ impl ShapeEditor {
self.iter(document).flat_map(|shape| shape.manipulator_groups().iter())
}
// Sets the selected points to all points for the corresponding intersection
pub fn select_all_anchors(&self, responses: &mut VecDeque<Message>, itersections: Vec<u64>) {
responses.push_back(Operation::SelectAllAnchors { layer_path: itersections }.into());
}
/// Provide the currently selected points by reference.
pub fn selected_points<'a>(&'a self, document: &'a Document) -> impl Iterator<Item = &'a ManipulatorPoint> {
self.selected_manipulator_groups(document).flat_map(|manipulator_group| manipulator_group.selected_points())

View File

@ -1,4 +1,4 @@
use crate::consts::{SELECTION_THRESHOLD, SELECTION_TOLERANCE};
use crate::consts::{DRAG_THRESHOLD, SELECTION_THRESHOLD, SELECTION_TOLERANCE};
use crate::messages::frontend::utility_types::MouseCursorIcon;
use crate::messages::input_mapper::utility_types::input_keyboard::{Key, MouseMotion};
use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
@ -6,6 +6,7 @@ use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::overlay_renderer::OverlayRenderer;
use crate::messages::tool::common_functionality::shape_editor::{ManipulatorPointInfo, ShapeEditor};
use crate::messages::tool::common_functionality::snapping::SnapManager;
use crate::messages::tool::common_functionality::transformation_cage::axis_align_drag;
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
@ -38,7 +39,9 @@ pub enum PathToolMessage {
DragStart {
add_to_selection: Key,
},
DragStop,
DragStop {
shift_mirror_distance: Key,
},
InsertPoint,
PointerMove {
alt_mirror_angle: Key,
@ -109,6 +112,7 @@ struct PathToolData {
snap_manager: SnapManager,
drag_start_pos: DVec2,
previous_mouse_position: DVec2,
alt_debounce: bool,
}
@ -154,12 +158,12 @@ impl Fsm for PathToolFsmState {
}
// Mouse down
(_, PathToolMessage::DragStart { add_to_selection }) => {
let toggle_add_to_selection = input.keyboard.get(add_to_selection as usize);
let shift_pressed = input.keyboard.get(add_to_selection as usize);
// Select the first point within the threshold (in pixels)
if let Some(mut selected_points) = tool_data
.shape_editor
.select_point(&document.document_legacy, input.mouse.position, SELECTION_THRESHOLD, toggle_add_to_selection, responses)
.select_point(&document.document_legacy, input.mouse.position, SELECTION_THRESHOLD, shift_pressed, responses)
{
responses.push_back(DocumentMessage::StartTransaction.into());
@ -186,8 +190,8 @@ impl Fsm for PathToolFsmState {
let include_handles = tool_data.shape_editor.selected_layers_ref();
tool_data.snap_manager.add_all_document_handles(document, input, &include_handles, &[], &selected_points.points);
tool_data.drag_start_pos = input.mouse.position - selected_points.offset;
tool_data.drag_start_pos = input.mouse.position;
tool_data.previous_mouse_position = input.mouse.position - selected_points.offset;
PathToolFsmState::Dragging
}
// We didn't find a point nearby, so consider selecting the nearest shape instead
@ -198,19 +202,26 @@ impl Fsm for PathToolFsmState {
.document_legacy
.intersects_quad_root(Quad::from_box([input.mouse.position - selection_size, input.mouse.position + selection_size]), render_data);
if !intersection.is_empty() {
if toggle_add_to_selection {
if shift_pressed {
responses.push_back(DocumentMessage::AddSelectedLayers { additional_layers: intersection }.into());
} else {
// Selects the topmost layer when selecting intersecting shapes
let top_most_intersection = intersection[intersection.len() - 1].clone();
responses.push_back(
DocumentMessage::SetSelectedLayers {
replacement_selected_layers: intersection,
replacement_selected_layers: vec![top_most_intersection.clone()],
}
.into(),
);
tool_data.drag_start_pos = input.mouse.position;
tool_data.previous_mouse_position = input.mouse.position;
// Selects all the anchor points when clicking in a filled area of shape. If two shapes intersect we pick the topmost layer.
tool_data.shape_editor.select_all_anchors(responses, top_most_intersection);
return PathToolFsmState::Dragging;
}
} else {
// Clear the previous selection if we didn't find anything
if !input.keyboard.get(toggle_add_to_selection as usize) {
if !input.keyboard.get(shift_pressed as usize) {
responses.push_back(DocumentMessage::DeselectAllLayers.into());
}
}
@ -240,12 +251,29 @@ impl Fsm for PathToolFsmState {
// Move the selected points by the mouse position
let snapped_position = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
tool_data.shape_editor.move_selected_points(snapped_position - tool_data.drag_start_pos, shift_pressed, responses);
tool_data.drag_start_pos = snapped_position;
tool_data
.shape_editor
.move_selected_points(snapped_position - tool_data.previous_mouse_position, shift_pressed, responses);
tool_data.previous_mouse_position = snapped_position;
PathToolFsmState::Dragging
}
// Mouse up
(_, PathToolMessage::DragStop) => {
(_, PathToolMessage::DragStop { shift_mirror_distance }) => {
let selected_points = tool_data.shape_editor.selected_points(&document.document_legacy);
let nearest_point = tool_data.shape_editor.find_nearest_point(&document.document_legacy, input.mouse.position, SELECTION_THRESHOLD);
let shift_pressed = input.keyboard.get(shift_mirror_distance as usize);
if tool_data.drag_start_pos.distance(input.mouse.position) <= DRAG_THRESHOLD && !shift_pressed {
for point in selected_points {
if nearest_point == Some(point) {
responses.push_back(DocumentMessage::DeselectAllManipulatorPoints.into());
tool_data
.shape_editor
.select_point(&document.document_legacy, input.mouse.position, SELECTION_THRESHOLD, false, responses);
}
}
}
tool_data.snap_manager.cleanup(responses);
PathToolFsmState::Ready
}