This commit is contained in:
parent
33728243fb
commit
2ca12e17b5
|
|
@ -7,6 +7,7 @@
|
|||
//! canvas to marquee-select anchors, click empty canvas to deselect.
|
||||
//! * Drag selected anchors to move them, a direction handle to turn it (a smooth point keeps its
|
||||
//! handles collinear; ⌥ breaks them apart), or a segment to bend it (straight segments move).
|
||||
//! The arrow keys nudge the selected anchors 1 px (⇧ 10 px), one history step per press.
|
||||
//! * Convert Point (the tool, or Pen + ⌥): click an anchor to make it a corner, drag from it to
|
||||
//! pull out smooth handles, drag a handle to move it alone. The tool selects the anchor it
|
||||
//! converts, so its handles show; a press on empty canvas or a segment does nothing, and ⌘/Ctrl
|
||||
|
|
@ -346,6 +347,37 @@ fn marquee(app: &mut PhotocraftApp, start: [f64; 2], end: [f64; 2], add: bool) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Arrow keys with Direct Selection and selected anchors (#4486), as in Photoshop: nudge them
|
||||
/// 1 px (⇧ 10 px) through `path.moveAnchors`, one history step per press. Not while a drag is
|
||||
/// in progress. Returns true when a key was used.
|
||||
pub fn arrow_keys(app: &mut PhotocraftApp, ctx: &egui::Context) -> bool {
|
||||
let ds = &app.ui.direct_selection;
|
||||
let Some(target) = ds.target.filter(|_| app.ui.tool == Tool::DirectSelection && !ds.anchors.is_empty() && ds.drag.is_none()) else {
|
||||
return false;
|
||||
};
|
||||
let anchors = ds.anchors.clone();
|
||||
use egui::Key;
|
||||
let keys = [(Key::ArrowLeft, -1.0, 0.0), (Key::ArrowRight, 1.0, 0.0), (Key::ArrowUp, 0.0, -1.0), (Key::ArrowDown, 0.0, 1.0)];
|
||||
for (key, ux, uy) in keys {
|
||||
// ⇧ first: `consume_key` ignores an extra ⇧.
|
||||
for mods in [Modifiers::SHIFT, Modifiers::NONE] {
|
||||
if ctx.input_mut(|i| i.consume_key(mods, key)) {
|
||||
let k = if mods.shift { 10.0 } else { 1.0 };
|
||||
let mut params = json!({"anchors": anchors, "move": [ux * k, uy * k]});
|
||||
if let (Some(p), Some(t)) = (params.as_object_mut(), target.params().as_object()) {
|
||||
p.extend(t.clone());
|
||||
}
|
||||
if let Err(e) = app.run("path.moveAnchors", params) {
|
||||
app.ui.status = e;
|
||||
app.ui.status_error = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Paths with their anchors (selected ones filled), the shown handles, the drag's preview in
|
||||
/// place of its path, and the marquee. `paths` false (View › Show › Target Path off) leaves only
|
||||
/// the marquee.
|
||||
|
|
|
|||
|
|
@ -225,3 +225,39 @@ fn the_convert_point_tool_shows_the_path_overlay() {
|
|||
assert!(!super::shows(&app, Modifiers::NONE));
|
||||
assert!(super::shows(&app, Modifiers::COMMAND));
|
||||
}
|
||||
|
||||
/// One key press through the canvas arrow keys, as egui delivers it.
|
||||
fn press(app: &mut PhotocraftApp, key: egui::Key, mods: Modifiers) -> bool {
|
||||
let ctx = egui::Context::default();
|
||||
ctx.input_mut(|i| {
|
||||
i.modifiers = mods;
|
||||
i.events.push(egui::Event::Key { key, physical_key: None, pressed: true, repeat: false, modifiers: mods });
|
||||
});
|
||||
crate::move_mods::arrow_keys(app, &ctx)
|
||||
}
|
||||
|
||||
/// #4486: the arrow keys nudge the selected anchors 1 px (⇧ 10 px), one history step per press,
|
||||
/// as in Photoshop. With no anchor selected they leave the path alone.
|
||||
#[test]
|
||||
fn arrow_keys_nudge_the_selected_anchors() {
|
||||
let mut app = app(Tool::DirectSelection);
|
||||
with_path(&mut app);
|
||||
// The path stays targeted after a click on empty canvas, but no anchor is selected.
|
||||
click(&mut app, [40.0, 120.0], Modifiers::NONE);
|
||||
click(&mut app, [80.0, 80.0], Modifiers::NONE);
|
||||
assert_eq!(app.ui.direct_selection.target, Some(PathRef::Work));
|
||||
let n = steps(&app);
|
||||
assert!(!press(&mut app, egui::Key::ArrowRight, Modifiers::NONE), "no anchor selected: not Direct Selection's key");
|
||||
assert_eq!(steps(&app), n);
|
||||
click(&mut app, [40.0, 120.0], Modifiers::NONE);
|
||||
click(&mut app, [120.0, 120.0], Modifiers::SHIFT);
|
||||
let (before, n) = (work(&app), steps(&app));
|
||||
assert!(press(&mut app, egui::Key::ArrowRight, Modifiers::NONE));
|
||||
assert_eq!((anchor(&app, 2), anchor(&app, 3)), (Point::new(121.0, 120.0), Point::new(41.0, 120.0)));
|
||||
assert!(press(&mut app, egui::Key::ArrowDown, Modifiers::SHIFT));
|
||||
assert_eq!((anchor(&app, 2), anchor(&app, 3)), (Point::new(121.0, 130.0), Point::new(41.0, 130.0)), "⇧ nudges 10 px");
|
||||
assert_eq!(anchor(&app, 0), Point::new(40.0, 40.0), "unselected anchors stay");
|
||||
assert_eq!(steps(&app), n + 2, "one history step per press");
|
||||
assert!(app.session.undo() && app.session.undo());
|
||||
assert_eq!(work(&app), before);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ pub fn arrow_keys(app: &mut PhotocraftApp, ctx: &egui::Context) -> bool {
|
|||
use egui::{Key, Modifiers};
|
||||
let cmd = ctx.input(|i| i.modifiers.command) && crate::hold_keys::cmd_nudges(app);
|
||||
if app.ui.tool != Tool::Move && app.ui.transform.is_none() && !cmd {
|
||||
return selection_arrow_keys(app, ctx) || brush_arrow_keys(app, ctx);
|
||||
return selection_arrow_keys(app, ctx) || crate::direct_select::arrow_keys(app, ctx) || brush_arrow_keys(app, ctx);
|
||||
}
|
||||
// `consume_key` never ignores an extra ⌘, so it is part of every pattern while it moves.
|
||||
let base = if cmd { Modifiers::COMMAND } else { Modifiers::NONE };
|
||||
|
|
|
|||
Loading…
Reference in New Issue