mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-16 06:38:03 +08:00
Fix the Pen tool's preview being misplaced and angle-locked after undo or redo (#4215)
* Fix the Pen tool's preview being misplaced and angle-locked after undo or redo * Clear the Pen tool's stale angle-lock flag after undo or redo
This commit is contained in:
@@ -418,6 +418,10 @@ struct PenToolData {
|
||||
/// avoiding unintended direction changes. Specifically handles the case where a handle is being dragged,
|
||||
/// and Ctrl is pressed near the anchor to make it colinear with its opposite handle.
|
||||
angle_locked: bool,
|
||||
/// Ignores the lock-angle key (Ctrl) still held from the undo/redo shortcut until it is released and pressed again, so the restored preview isn't forced onto the previously locked angle.
|
||||
suppress_lock_angle: bool,
|
||||
/// Same as `suppress_lock_angle`, for the angle-snap key (Shift) held during the Ctrl+Shift+Z redo.
|
||||
suppress_snap_angle: bool,
|
||||
path_closed: bool,
|
||||
|
||||
handle_mode: HandleMode,
|
||||
@@ -454,9 +458,56 @@ impl PenToolData {
|
||||
self.handle_end = None;
|
||||
self.latest_points.clear();
|
||||
self.point_index = 0;
|
||||
self.suppress_lock_angle = false;
|
||||
self.suppress_snap_angle = false;
|
||||
self.snap_manager.cleanup(responses);
|
||||
}
|
||||
|
||||
/// Reads the modifier key states, applying the post-undo/redo angle-lock and angle-snap suppression.
|
||||
fn read_modifiers(&mut self, input: &InputPreprocessorMessageHandler, snap_angle: Key, break_handle: Key, lock_angle: Key, colinear: Key, move_anchor_with_handles: Key) -> ModifierState {
|
||||
let lock_angle_held = input.keyboard.key(lock_angle);
|
||||
if self.suppress_lock_angle && !lock_angle_held {
|
||||
self.suppress_lock_angle = false;
|
||||
}
|
||||
|
||||
let snap_angle_held = input.keyboard.key(snap_angle);
|
||||
if self.suppress_snap_angle && !snap_angle_held {
|
||||
self.suppress_snap_angle = false;
|
||||
}
|
||||
|
||||
ModifierState {
|
||||
snap_angle: snap_angle_held && !self.suppress_snap_angle,
|
||||
lock_angle: lock_angle_held && !self.suppress_lock_angle,
|
||||
break_handle: input.keyboard.key(break_handle),
|
||||
colinear: input.keyboard.key(colinear),
|
||||
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-renders the preview after an undo/redo once the graph has re-evaluated, since the layer transform is
|
||||
/// briefly stale until then and would place the preview off in the wrong space. Also suppresses the angle
|
||||
/// modifiers (Ctrl, Shift) still held from the shortcut until each is released and pressed again.
|
||||
fn refresh_preview_after_history(&mut self, responses: &mut VecDeque<Message>) {
|
||||
self.suppress_lock_angle = true;
|
||||
self.suppress_snap_angle = true;
|
||||
self.modifiers.lock_angle = false;
|
||||
self.modifiers.snap_angle = false;
|
||||
self.angle_locked = false;
|
||||
|
||||
responses.add(DeferMessage::AfterGraphRun {
|
||||
messages: vec![
|
||||
PenToolMessage::PointerMove {
|
||||
snap_angle: Key::Shift,
|
||||
break_handle: Key::Alt,
|
||||
lock_angle: Key::Control,
|
||||
colinear: Key::KeyC,
|
||||
move_anchor_with_handles: Key::Space,
|
||||
}
|
||||
.into(),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
/// Check whether target handle is primary, end, or `self.handle_end`
|
||||
fn check_end_handle_type(&self, vector: &Vector) -> TargetHandle {
|
||||
match (self.handle_end, self.prior_segment_endpoint, self.prior_segment, self.path_closed) {
|
||||
@@ -1134,9 +1185,7 @@ impl PenToolData {
|
||||
let mut selected_layers = selected_nodes.selected_layers(document.metadata());
|
||||
let layer = selected_layers.next().filter(|_| selected_layers.next().is_none()).or(self.current_layer)?;
|
||||
|
||||
// The modified vector can be momentarily unavailable while the node graph re-evaluates after a document undo.
|
||||
// Snapping onto an existing point is only a convenience, so skip it when the vector is missing rather than
|
||||
// returning `None`, which would let a caller like PointerMove's `unwrap_or(Ready)` end the session.
|
||||
// The vector may be momentarily unavailable while the graph re-evaluates (e.g. during undo), so snapping onto an existing point is best-effort rather than a reason to bail out
|
||||
if let Some(vector) = document.network_interface.compute_modified_vector(layer) {
|
||||
let transform = document.metadata().document_to_viewport * transform;
|
||||
for point in vector.anchor_points() {
|
||||
@@ -1930,13 +1979,7 @@ impl Fsm for PenToolFsmState {
|
||||
move_anchor_with_handles,
|
||||
},
|
||||
) => {
|
||||
tool_data.modifiers = ModifierState {
|
||||
snap_angle: input.keyboard.key(snap_angle),
|
||||
lock_angle: input.keyboard.key(lock_angle),
|
||||
break_handle: input.keyboard.key(break_handle),
|
||||
colinear: input.keyboard.key(colinear),
|
||||
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
||||
};
|
||||
tool_data.modifiers = tool_data.read_modifiers(input, snap_angle, break_handle, lock_angle, colinear, move_anchor_with_handles);
|
||||
|
||||
let snap_data = SnapData::new(document, input, viewport);
|
||||
if tool_data.modifiers.colinear && !tool_data.toggle_colinear_debounce {
|
||||
@@ -1985,8 +2028,7 @@ impl Fsm for PenToolFsmState {
|
||||
tool_data.angle_locked = false;
|
||||
}
|
||||
|
||||
// A pointer move must never end the path: keep dragging if the handle can't be resolved this frame
|
||||
// (the vector is briefly unavailable while the graph re-evaluates after an undo)
|
||||
// Don't end the path if the handle can't be resolved this frame
|
||||
let state = tool_data.drag_handle(snap_data, transform, input.mouse.position, responses, layer, input, viewport).unwrap_or(self);
|
||||
|
||||
if tool_data.handle_swapped {
|
||||
@@ -2028,14 +2070,8 @@ impl Fsm for PenToolFsmState {
|
||||
) => {
|
||||
tool_data.switch_to_free_on_ctrl_release = false;
|
||||
tool_data.alt_pressed = false;
|
||||
tool_data.modifiers = ModifierState {
|
||||
snap_angle: input.keyboard.key(snap_angle),
|
||||
lock_angle: input.keyboard.key(lock_angle),
|
||||
break_handle: input.keyboard.key(break_handle),
|
||||
colinear: input.keyboard.key(colinear),
|
||||
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
||||
};
|
||||
// A pointer move must never end the path: keep placing if the anchor can't be resolved this frame
|
||||
tool_data.modifiers = tool_data.read_modifiers(input, snap_angle, break_handle, lock_angle, colinear, move_anchor_with_handles);
|
||||
// Don't end the path if the anchor can't be resolved this frame
|
||||
let state = tool_data
|
||||
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, responses)
|
||||
.unwrap_or(self);
|
||||
@@ -2081,13 +2117,7 @@ impl Fsm for PenToolFsmState {
|
||||
move_anchor_with_handles,
|
||||
},
|
||||
) => {
|
||||
tool_data.modifiers = ModifierState {
|
||||
snap_angle: input.keyboard.key(snap_angle),
|
||||
lock_angle: input.keyboard.key(lock_angle),
|
||||
break_handle: input.keyboard.key(break_handle),
|
||||
colinear: input.keyboard.key(colinear),
|
||||
move_anchor_with_handles: input.keyboard.key(move_anchor_with_handles),
|
||||
};
|
||||
tool_data.modifiers = tool_data.read_modifiers(input, snap_angle, break_handle, lock_angle, colinear, move_anchor_with_handles);
|
||||
tool_data.snap_manager.preview_draw(&SnapData::new(document, input, viewport), input.mouse.position);
|
||||
responses.add(OverlaysMessage::Draw);
|
||||
self
|
||||
@@ -2221,9 +2251,8 @@ impl Fsm for PenToolFsmState {
|
||||
(PenToolFsmState::DraggingHandle(..) | PenToolFsmState::PlacingAnchor, PenToolMessage::Undo) => {
|
||||
if tool_data.point_index > 0 {
|
||||
tool_data.point_index -= 1;
|
||||
tool_data
|
||||
.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, responses)
|
||||
.unwrap_or(PenToolFsmState::PlacingAnchor)
|
||||
tool_data.refresh_preview_after_history(responses);
|
||||
PenToolFsmState::PlacingAnchor
|
||||
} else {
|
||||
responses.add(PenToolMessage::Abort);
|
||||
self
|
||||
@@ -2231,7 +2260,7 @@ impl Fsm for PenToolFsmState {
|
||||
}
|
||||
(_, PenToolMessage::Redo) => {
|
||||
tool_data.point_index = (tool_data.point_index + 1).min(tool_data.latest_points.len().saturating_sub(1));
|
||||
tool_data.place_anchor(SnapData::new(document, input, viewport), transform, input.mouse.position, responses);
|
||||
tool_data.refresh_preview_after_history(responses);
|
||||
match tool_data.point_index {
|
||||
0 => PenToolFsmState::Ready,
|
||||
_ => PenToolFsmState::PlacingAnchor,
|
||||
|
||||
Reference in New Issue
Block a user