mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-15 03:36:48 -04:00
⇧⌘N opens the New Layer dialog, as Layer › New › Layer… does in Photoshop. Photoshop's companion shortcut, the same keys with ⌥ (Ctrl+Alt+Shift+N on Windows and Linux), makes the layer at once; PhotoCraft didn't handle it, so there was no way to get 0.7.0's dialog-free behaviour from the keyboard. New Layer's shortcut with ⌥ added now runs layer.new.layer directly. It follows a rebound New Layer shortcut, and a command bound to that key itself keeps it. Fixes #4794 Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
e4d281eb20
commit
05d361a367
3 files changed
+75
-2
No files matched your search
@@ -1,5 +1,6 @@
|
||||
//! The Layers panel New Layer button: a click makes a layer at once, ⌥-click opens the New Layer
|
||||
//! dialog first, as in Photoshop.
|
||||
//! dialog first, as in Photoshop. Its shortcut is the same pair the other way round: ⇧⌘N asks
|
||||
//! first, ⌥⇧⌘N makes the layer at once (#4794).
|
||||
|
||||
use egui::Modifiers;
|
||||
use egui_kittest::kittest::Queryable;
|
||||
@@ -41,3 +42,46 @@ fn alt_click_opens_the_new_layer_dialog() {
|
||||
crate::dialogs::confirm(h.state_mut(), id).unwrap();
|
||||
assert_eq!(layers(&h), 2);
|
||||
}
|
||||
|
||||
fn dialog_open(h: &egui_kittest::Harness<'static, PhotocraftApp>) -> bool {
|
||||
h.state().ui.dialogs.iter().any(|d| crate::new_layer_ui::owns(&d.fields))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shortcut_asks_first_and_alt_shortcut_makes_a_layer_at_once() {
|
||||
let mut h = panel();
|
||||
h.key_press_modifiers(Modifiers::COMMAND | Modifiers::SHIFT, egui::Key::N);
|
||||
h.run_steps(4);
|
||||
assert_eq!(layers(&h), 1, "⇧⌘N asks first");
|
||||
assert!(dialog_open(&h), "the New Layer dialog");
|
||||
|
||||
let mut h = panel();
|
||||
h.key_press_modifiers(Modifiers::COMMAND | Modifiers::SHIFT | Modifiers::ALT, egui::Key::N);
|
||||
h.run_steps(4);
|
||||
assert_eq!(layers(&h), 2, "⌥⇧⌘N makes the layer");
|
||||
assert!(!dialog_open(&h));
|
||||
h.key_press_modifiers(Modifiers::COMMAND | Modifiers::SHIFT | Modifiers::ALT, egui::Key::N);
|
||||
h.run_steps(4);
|
||||
assert_eq!(layers(&h), 3, "once per press");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alt_shortcut_follows_a_custom_new_layer_shortcut_and_yields_to_a_bound_command() {
|
||||
// Rebound to F2: ⌥F2 makes the layer at once.
|
||||
let mut h = panel();
|
||||
h.state_mut().session.prefs.edit(|p| p.shortcuts.insert("layer.new.layer".into(), "F2".into()));
|
||||
h.key_press_modifiers(Modifiers::ALT, egui::Key::F2);
|
||||
h.run_steps(4);
|
||||
assert_eq!(layers(&h), 2);
|
||||
assert!(!dialog_open(&h));
|
||||
// A command bound to ⌥⇧⌘N itself keeps the key.
|
||||
let mut h = panel();
|
||||
h.state_mut().session.prefs.edit(|p| p.shortcuts.insert("layer.new.layerViaCopy".into(), "Cmd+Alt+Shift+N".into()));
|
||||
h.key_press_modifiers(Modifiers::COMMAND | Modifiers::SHIFT | Modifiers::ALT, egui::Key::N);
|
||||
h.run_steps(4);
|
||||
assert_eq!(layers(&h), 2);
|
||||
assert!(!dialog_open(&h));
|
||||
let doc = &h.state().session.active().unwrap().doc;
|
||||
let top = doc.layers.last().unwrap().surface().unwrap();
|
||||
assert!(!top.content_bounds().is_empty(), "Layer via Copy ran (the white background copied), not New Layer");
|
||||
}
|
||||
@@ -162,6 +162,31 @@ pub fn consume(ctx: &egui::Context, sc: &KeyboardShortcut) -> bool {
|
||||
pub const NAV_COMMANDS: [&str; 4] = ["view.zoomIn", "view.zoomOut", "view.fitOnScreen", "view.actualPixels"];
|
||||
|
||||
/// ⌘+ / ⌘- / ⌘0 / ⌘1 (or their Edit › Keyboard Shortcuts overrides). Returns true when one ran.
|
||||
/// Layer › New › Layer's shortcut with ⌥ added (⌥⇧⌘N by default) makes the layer at once, as in
|
||||
/// Photoshop: the plain shortcut asks with the New Layer dialog first, like the menu item, and
|
||||
/// the Layers panel button's click and ⌥-click are the same pair the other way round (#4794).
|
||||
/// A command bound to that key itself keeps it.
|
||||
fn new_layer_without_dialog(app: &mut PhotocraftApp, ctx: &egui::Context) -> bool {
|
||||
const NEW_LAYER: &str = crate::new_layer_ui::COMMAND;
|
||||
let Some(mut sc) = effective_shortcut(app, NEW_LAYER, default_shortcut(NEW_LAYER).as_deref()).and_then(|s| parse(&s)) else {
|
||||
return false;
|
||||
};
|
||||
if sc.modifiers.alt {
|
||||
return false;
|
||||
}
|
||||
sc.modifiers.alt = true;
|
||||
let pressed =
|
||||
ctx.input(|i| i.events.iter().any(|e| matches!(e, egui::Event::Key { key, pressed: true, modifiers, .. } if key_matches(&sc, *key, *modifiers))));
|
||||
if !pressed || crate::shortcut_dispatch::bindings(app).iter().any(|(_, b)| *b == sc) || !consume(ctx, &sc) {
|
||||
return false;
|
||||
}
|
||||
if let Err(e) = app.run(NEW_LAYER, json!({})) {
|
||||
app.ui.status = e;
|
||||
app.ui.status_error = true;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn nav_keys(app: &mut PhotocraftApp, ctx: &egui::Context) -> bool {
|
||||
for id in NAV_COMMANDS {
|
||||
let sc = crate::menus::UI_COMMANDS.iter().find(|c| c.0 == id).and_then(|c| parse(app.session.prefs().shortcut(id, c.3)?));
|
||||
@@ -480,6 +505,10 @@ pub fn handle(app: &mut PhotocraftApp, ctx: &egui::Context) {
|
||||
}
|
||||
// Inline type editing eats text and navigation keys; ⌘-shortcuts still reach the menus.
|
||||
let editing = crate::type_tool::handle_keys(app, ctx);
|
||||
// ⌥⇧⌘N: a new layer at once, without the New Layer dialog ⇧⌘N opens (#4794).
|
||||
if new_layer_without_dialog(app, ctx) {
|
||||
return;
|
||||
}
|
||||
// Registry, UI and menu-catalogue shortcuts (see [`crate::shortcut_dispatch::bindings`]).
|
||||
if dispatch_pressed(app, ctx, focus, editing) {
|
||||
return;
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ Dispatch order matches `dialogs::show` in `dialogs.rs` (check earlier rows befor
|
||||
| `__stroke` | `stroke_ui.rs` | Edit › Stroke… |
|
||||
| `__createShape` | `shape_dialog.rs` | Click-with-shape-tool creation (rectangle, ellipse, …) |
|
||||
| `__deleteLayer` | `delete_layer_prompt.rs` | Layers panel trash confirmation |
|
||||
| `__newLayer` | `new_layer_ui.rs` | Layer › New › Layer… (⇧⌘N) |
|
||||
| `__newLayer` | `new_layer_ui.rs` | Layer › New › Layer… (⇧⌘N; ⌥⇧⌘N makes the layer without it) |
|
||||
| `__rasterize` | `rasterize_prompt.rs` | Rasterize-before-paint prompt |
|
||||
| `__resetAllTools` | `tool_reset.rs` | Reset All Tools confirmation |
|
||||
| `__cropPresetNew` / `__cropPresetDelete` | `crop_presets.rs` | New / delete crop preset |
|
||||
|
||||
Reference in new issue
Block a user