mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-24 02:48:12 +08:00
Restore handle length when Shift is released while dragging opposite handle with Path tool (#1009)
* Restore path handle length to previous length when shift is released while dragging * Reset previous opposing handle length on Delete and Abort messages * Move the opposing handle length state to the path tool * Use MoveManipulatorPoint messages instead of modifying MoveSelectedManipulatorPoints + break handle mirroring on dragging both handles without anchor * Handle alt + improve reset logic + comments --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Keavon Chambers
parent
f400d2c9dd
commit
5179e8a829
@@ -212,6 +212,114 @@ impl ShapeEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The opposing handle lengths.
|
||||||
|
pub fn opposing_handle_lengths(&self, document: &Document) -> HashMap<Vec<LayerId>, HashMap<u64, f64>> {
|
||||||
|
self.selected_layers()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|path| document.layer(path).ok().map(|layer| (path, layer)))
|
||||||
|
.filter_map(|(path, shape)| shape.as_subpath().map(|subpath| (path, subpath)))
|
||||||
|
.map(|(path, shape)| {
|
||||||
|
let opposing_handle_lengths = shape
|
||||||
|
.manipulator_groups()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(id, manipulator_group)| {
|
||||||
|
// We will keep track of the opposing handle length when:
|
||||||
|
// i) Both handles exist and exactly one is selected.
|
||||||
|
// ii) The anchor is not selected.
|
||||||
|
// iii) We have to mirror the angle between handles.
|
||||||
|
|
||||||
|
if !manipulator_group.editor_state.mirror_angle_between_handles {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut selected_handles = manipulator_group.selected_handles();
|
||||||
|
let handle = selected_handles.next()?;
|
||||||
|
|
||||||
|
// Check that handle is the only selected handle.
|
||||||
|
if selected_handles.next().is_none() {
|
||||||
|
let opposing_handle_position = manipulator_group.opposing_handle(handle)?.position;
|
||||||
|
let anchor = manipulator_group.points[ManipulatorType::Anchor].as_ref()?;
|
||||||
|
if !anchor.is_selected() {
|
||||||
|
let opposing_handle_length = opposing_handle_position.distance(anchor.position);
|
||||||
|
Some((*id, opposing_handle_length))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
(path.clone(), opposing_handle_lengths)
|
||||||
|
})
|
||||||
|
.collect::<HashMap<_, _>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the opposing handle lengths.
|
||||||
|
pub fn reset_opposing_handle_lengths(&self, document: &Document, opposing_handle_lengths: &HashMap<Vec<LayerId>, HashMap<u64, f64>>, responses: &mut VecDeque<Message>) {
|
||||||
|
self.selected_layers()
|
||||||
|
.iter()
|
||||||
|
.filter_map(|path| document.layer(path).ok().map(|layer| (path, layer)))
|
||||||
|
.filter_map(|(path, shape)| shape.as_subpath().map(|subpath| (path, subpath)))
|
||||||
|
.filter_map(|(path, shape)| opposing_handle_lengths.get(path).map(|layer_opposing_handle_lengths| (path, shape, layer_opposing_handle_lengths)))
|
||||||
|
.flat_map(|(path, shape, layer_opposing_handle_lengths)| {
|
||||||
|
shape
|
||||||
|
.manipulator_groups()
|
||||||
|
.enumerate()
|
||||||
|
.map(move |(id, manipulator_group)| (path, layer_opposing_handle_lengths, id, manipulator_group))
|
||||||
|
})
|
||||||
|
.for_each(|(path, layer_opposing_handle_lengths, id, manipulator_group)| {
|
||||||
|
if !manipulator_group.editor_state.mirror_angle_between_handles {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let opposing_handle_length = if let Some(length) = layer_opposing_handle_lengths.get(id) {
|
||||||
|
length
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut selected_handles = manipulator_group.selected_handles();
|
||||||
|
let handle = if let Some(handle) = selected_handles.next() {
|
||||||
|
handle
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check that handle is the only selected handle.
|
||||||
|
if selected_handles.next().is_none() {
|
||||||
|
let opposing_handle = if let Some(opposing_handle) = manipulator_group.opposing_handle(handle) {
|
||||||
|
opposing_handle
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let anchor = if let Some(anchor) = manipulator_group.points[ManipulatorType::Anchor].as_ref() {
|
||||||
|
anchor
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if anchor.is_selected() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(offset) = (opposing_handle.position - anchor.position).try_normalize() {
|
||||||
|
let new_opposing_handle_position = anchor.position + offset * (*opposing_handle_length);
|
||||||
|
assert!(new_opposing_handle_position.is_finite(), "Opposing handle not finite!");
|
||||||
|
responses.push_back(
|
||||||
|
Operation::MoveManipulatorPoint {
|
||||||
|
layer_path: path.clone(),
|
||||||
|
id: *id,
|
||||||
|
manipulator_type: opposing_handle.manipulator_type,
|
||||||
|
position: new_opposing_handle_position.into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Dissolve the selected points.
|
/// Dissolve the selected points.
|
||||||
pub fn delete_selected_points(&self, responses: &mut VecDeque<Message>) {
|
pub fn delete_selected_points(&self, responses: &mut VecDeque<Message>) {
|
||||||
responses.push_back(DocumentMessage::DeleteSelectedManipulatorPoints.into());
|
responses.push_back(DocumentMessage::DeleteSelectedManipulatorPoints.into());
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use crate::messages::tool::common_functionality::snapping::SnapManager;
|
|||||||
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, HintData, HintGroup, HintInfo, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, HintData, HintGroup, HintInfo, ToolActionHandlerData, ToolMetadata, ToolTransition, ToolType};
|
||||||
|
|
||||||
use document_legacy::intersection::Quad;
|
use document_legacy::intersection::Quad;
|
||||||
|
use document_legacy::LayerId;
|
||||||
use graphene_std::vector::consts::ManipulatorType;
|
use graphene_std::vector::consts::ManipulatorType;
|
||||||
|
|
||||||
use glam::DVec2;
|
use glam::DVec2;
|
||||||
@@ -112,6 +113,7 @@ struct PathToolData {
|
|||||||
drag_start_pos: DVec2,
|
drag_start_pos: DVec2,
|
||||||
previous_mouse_position: DVec2,
|
previous_mouse_position: DVec2,
|
||||||
alt_debounce: bool,
|
alt_debounce: bool,
|
||||||
|
opposing_handle_lengths: Option<HashMap<Vec<LayerId>, HashMap<u64, f64>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fsm for PathToolFsmState {
|
impl Fsm for PathToolFsmState {
|
||||||
@@ -142,6 +144,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
tool_data.overlay_renderer.render_subpath_overlays(&document.document_legacy, layer_path.to_vec(), responses);
|
tool_data.overlay_renderer.render_subpath_overlays(&document.document_legacy, layer_path.to_vec(), responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tool_data.opposing_handle_lengths = None;
|
||||||
// This can happen in any state (which is why we return self)
|
// This can happen in any state (which is why we return self)
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -158,6 +161,8 @@ impl Fsm for PathToolFsmState {
|
|||||||
(_, PathToolMessage::DragStart { add_to_selection }) => {
|
(_, PathToolMessage::DragStart { add_to_selection }) => {
|
||||||
let shift_pressed = input.keyboard.get(add_to_selection as usize);
|
let shift_pressed = input.keyboard.get(add_to_selection as usize);
|
||||||
|
|
||||||
|
tool_data.opposing_handle_lengths = None;
|
||||||
|
|
||||||
// Select the first point within the threshold (in pixels)
|
// Select the first point within the threshold (in pixels)
|
||||||
if let Some(mut selected_points) = tool_data
|
if let Some(mut selected_points) = tool_data
|
||||||
.shape_editor
|
.shape_editor
|
||||||
@@ -240,6 +245,7 @@ impl Fsm for PathToolFsmState {
|
|||||||
tool_data.alt_debounce = alt_pressed;
|
tool_data.alt_debounce = alt_pressed;
|
||||||
// Only on alt down
|
// Only on alt down
|
||||||
if alt_pressed {
|
if alt_pressed {
|
||||||
|
tool_data.opposing_handle_lengths = None;
|
||||||
tool_data.shape_editor.toggle_handle_mirroring_on_selected(true, responses);
|
tool_data.shape_editor.toggle_handle_mirroring_on_selected(true, responses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,6 +253,17 @@ impl Fsm for PathToolFsmState {
|
|||||||
// Determine when shift state changes
|
// Determine when shift state changes
|
||||||
let shift_pressed = input.keyboard.get(shift_mirror_distance as usize);
|
let shift_pressed = input.keyboard.get(shift_mirror_distance as usize);
|
||||||
|
|
||||||
|
if shift_pressed {
|
||||||
|
if tool_data.opposing_handle_lengths.is_none() {
|
||||||
|
tool_data.opposing_handle_lengths = Some(tool_data.shape_editor.opposing_handle_lengths(&document.document_legacy));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Some(opposing_handle_lengths) = &tool_data.opposing_handle_lengths {
|
||||||
|
tool_data.shape_editor.reset_opposing_handle_lengths(&document.document_legacy, opposing_handle_lengths, responses);
|
||||||
|
tool_data.opposing_handle_lengths = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Move the selected points by the mouse position
|
// Move the selected points by the mouse position
|
||||||
let snapped_position = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
|
let snapped_position = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
|
||||||
tool_data
|
tool_data
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ impl ManipulatorGroup {
|
|||||||
|
|
||||||
// If the anchor isn't selected, but both handles are, drag only handles
|
// If the anchor isn't selected, but both handles are, drag only handles
|
||||||
if self.both_handles_selected() {
|
if self.both_handles_selected() {
|
||||||
|
self.editor_state.mirror_angle_between_handles = false;
|
||||||
for point in self.selected_handles_mut() {
|
for point in self.selected_handles_mut() {
|
||||||
move_point(point, delta);
|
move_point(point, delta);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user