mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-27 11:27:21 -06:00
Integration: lock a Shift selection drag to multiples of 45° (#6254)
Photoshop constrains a Shift-mid-drag selection move to multiples of 45°
("Move a selection border"), and the Move tool's drag of selected pixels
already uses TRANSFORM_DIRECTIONS (8). selection_drag_to used
MOVE_DIRECTIONS (4), so a near-diagonal Shift drag (delta 13,11) snapped
to the horizontal axis (13,0) instead of 45° (12,12).
Test: shift_constrains_a_selection_drag_to_an_axis gains a diagonal case
(fails with 4 directions); the floating-drag test moves to (34,22) so it
stays an axis case with 8 directions.
Co-authored-by: mroxso <24775431+mroxso@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
mroxso
Claude Opus 5.5
parent
b7eabd4990
commit
4efebbe2bf
@@ -267,7 +267,7 @@ pub struct Drag {
|
||||
/// every Move-tool drag with a selection does (`move_ui::moves_selected_pixels_with`).
|
||||
pub sel_move: Option<bool>,
|
||||
/// The last ⇧-locked offset of a selection-tool drag of the selection (`move_mods`, #6231);
|
||||
/// keeps the locked axis stable between events. The Move tool's own drags use `move_mods`.
|
||||
/// keeps the locked direction stable between events. The Move tool's own drags use `move_mods`.
|
||||
pub sel_axis: Option<[f64; 2]>,
|
||||
pub lasso: Option<crate::lasso_ui::Lasso>,
|
||||
}
|
||||
@@ -5724,7 +5724,7 @@ fn tool_move(app: &mut PhotocraftApp, x: f64, y: f64, pressure: f32, mods: egui:
|
||||
// ⇧: straight 0/45/90° strokes, 45° gradient angles (stroke_constraint.rs).
|
||||
let last = d.points.last().map_or(d.start, |p| [p[0], p[1]]);
|
||||
let [x, y] = crate::stroke_constraint::constrain(d.tool, &mut d.constrain, d.start, last, [x, y], mods.shift, zoom);
|
||||
// ⇧: a selection-tool drag of the selection locks to the dominant axis (#6231). The Move
|
||||
// ⇧: a selection-tool drag of the selection locks to multiples of 45° (#6231). The Move
|
||||
// tool's own drags were already locked by `move_mods::filter_event`.
|
||||
let [x, y] = crate::move_mods::selection_drag_to(d, [x, y], mods.shift && tool != Tool::Move);
|
||||
let moved = d.points.last().is_none_or(|p| (p[0] - x).abs() + (p[1] - y).abs() > 0.25);
|
||||
|
||||
@@ -655,8 +655,8 @@ fn drag_inside_the_selection_moves_the_outline() {
|
||||
assert_eq!(sel(&app), None);
|
||||
}
|
||||
|
||||
/// #6231: ⇧ held during a selection-tool drag of the selection locks the move to the dominant
|
||||
/// axis (⇧ at the press still means add-to-selection, so the lock only applies mid-drag).
|
||||
/// #6231: ⇧ held during a selection-tool drag of the selection locks the move to multiples of
|
||||
/// 45° (⇧ at the press still means add-to-selection, so the lock only applies mid-drag).
|
||||
#[test]
|
||||
fn shift_constrains_a_selection_drag_to_an_axis() {
|
||||
use crate::canvas::{ToolEvent, tool_event};
|
||||
@@ -674,9 +674,12 @@ fn shift_constrains_a_selection_drag_to_an_axis() {
|
||||
// Without ⇧ the drag is free (the outline already sits at (30,26,50,46)).
|
||||
drag(&mut app, [40.0, 30.0], [50.0, 20.0], Modifiers::NONE);
|
||||
assert_eq!(sel(&app), Some(Rect::new(40, 16, 60, 36)), "plain drag moves both axes");
|
||||
// A near-diagonal ⇧-drag locks to 45°, as in Photoshop: (13,11) projects onto (12,12).
|
||||
drag_shift(&mut app, [50.0, 26.0], [63.0, 37.0]);
|
||||
assert_eq!(sel(&app), Some(Rect::new(52, 28, 72, 48)), "diagonal drag locks to 45°");
|
||||
}
|
||||
|
||||
/// #6231: ⇧ also locks a ⌘-drag of the selected pixels to the dominant axis.
|
||||
/// #6231: ⇧ also locks a ⌘-drag of the selected pixels to multiples of 45°.
|
||||
#[test]
|
||||
fn shift_constrains_a_floating_selection_drag() {
|
||||
use crate::canvas::{ToolEvent, tool_event};
|
||||
@@ -684,8 +687,8 @@ fn shift_constrains_a_floating_selection_drag() {
|
||||
let offset = |app: &PhotocraftApp| photocraft_engine::float_cmds::floating(app.session.active().unwrap()).map(|f| f.offset);
|
||||
let cmd_shift = Modifiers::COMMAND | Modifiers::SHIFT;
|
||||
tool_event(&mut app, ToolEvent::Down { x: 20.0, y: 20.0, pressure: 1.0 }, Modifiers::COMMAND);
|
||||
tool_event(&mut app, ToolEvent::Move { x: 34.0, y: 28.0, pressure: 1.0 }, cmd_shift);
|
||||
tool_event(&mut app, ToolEvent::Up { x: 34.0, y: 28.0 }, cmd_shift);
|
||||
tool_event(&mut app, ToolEvent::Move { x: 34.0, y: 22.0, pressure: 1.0 }, cmd_shift);
|
||||
tool_event(&mut app, ToolEvent::Up { x: 34.0, y: 22.0 }, cmd_shift);
|
||||
assert_eq!(offset(&app), Some((14, 0)), "locked to the horizontal axis");
|
||||
app.run("select.drop", json!({})).unwrap();
|
||||
}
|
||||
|
||||
@@ -152,18 +152,19 @@ fn drag_to(app: &mut PhotocraftApp, p: [f64; 2], mods: egui::Modifiers) -> [f64;
|
||||
[start[0] + d[0], start[1] + d[1]]
|
||||
}
|
||||
|
||||
/// ⇧ while a selection tool drags the selection (#6231): lock the move to the dominant axis, as
|
||||
/// the Move tool's layer drags do. The caller skips this when [`filter_event`] has already
|
||||
/// ⇧ while a selection tool drags the selection (#6231): lock the move to multiples of 45°, as
|
||||
/// Photoshop does ("Move a selection border") and as the Move tool's drags of selected pixels do
|
||||
/// ([`TRANSFORM_DIRECTIONS`]). The caller skips this when [`filter_event`] has already
|
||||
/// rewritten the event (the Move tool itself); it covers the `sel_move` drags the marquee and
|
||||
/// lasso tools start, including ⌘-drags of the selected pixels. The locked offset is kept on the
|
||||
/// drag between events so the axis doesn't flicker near 45°.
|
||||
/// drag between events so the direction doesn't flicker between two neighbours.
|
||||
pub(crate) fn selection_drag_to(d: &mut crate::canvas::Drag, p: [f64; 2], shift: bool) -> [f64; 2] {
|
||||
if d.sel_move.is_none() || !shift {
|
||||
d.sel_axis = None;
|
||||
return p;
|
||||
}
|
||||
let delta = [p[0] - d.start[0], p[1] - d.start[1]];
|
||||
let locked = constrain(delta, d.sel_axis, MOVE_DIRECTIONS);
|
||||
let locked = constrain(delta, d.sel_axis, TRANSFORM_DIRECTIONS);
|
||||
d.sel_axis = Some(locked);
|
||||
[d.start[0] + locked[0], d.start[1] + locked[1]]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user