mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-26 11:09:23 -06:00
KEY-CLIPBOARD: Ctrl+C / Ctrl+V copy and paste settings on Windows and Linux
egui-winit turns Ctrl+C / Ctrl+X / Ctrl+V (with any other modifiers) into clipboard events and drops the key press, so outside text fields Copy Settings, Paste Settings and ⇧Ctrl+V (Paste Selected Settings) never fired there; macOS was spared because its menu bar takes ⌘C / ⌘V first. The shortcuts now read those events as the keys they were, as the keymap editor already did when recording a shortcut. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a92c38fdd4
commit
f9a6b85a83
@@ -260,24 +260,31 @@ pub fn parse(s: &str) -> Option<(Modifiers, Key)> {
|
||||
}
|
||||
|
||||
fn matches(i: &egui::InputState, m: Modifiers, k: Key) -> bool {
|
||||
i.events.iter().any(|e| match e {
|
||||
egui::Event::Key { key, physical_key, pressed: true, modifiers, .. } => {
|
||||
// `Ctrl` is the physical Control key (on macOS distinct from Cmd; elsewhere Cmd = Ctrl);
|
||||
// "Delete" (⌫ = Backspace) also matches forward-delete
|
||||
let ctrl_ok = if m.ctrl { modifiers.ctrl } else { !modifiers.ctrl || modifiers.command };
|
||||
let cmd_ok = m.ctrl || modifiers.command == m.command;
|
||||
// winit can report shifted digits as punctuation (Shift+1 = `!`). Recognize the
|
||||
// physical number key for culling, without changing layout-aware letter shortcuts.
|
||||
let shifted_digit = m.shift
|
||||
&& *physical_key == Some(k)
|
||||
&& matches!(k, Key::Num0 | Key::Num1 | Key::Num2 | Key::Num3 | Key::Num4 | Key::Num5 | Key::Num6 | Key::Num7 | Key::Num8 | Key::Num9);
|
||||
(*key == k || shifted_digit || (k == Key::Backspace && *key == Key::Delete))
|
||||
&& ctrl_ok
|
||||
&& cmd_ok
|
||||
&& modifiers.shift == m.shift
|
||||
&& modifiers.alt == m.alt
|
||||
}
|
||||
_ => false,
|
||||
// the windowing layer turns ⌘C / ⌘X / ⌘V (with any other modifiers held) and the keyboard's
|
||||
// Copy / Cut / Paste keys into these on Windows and Linux (macOS's menu bar takes them first)
|
||||
let clipboard = Modifiers { command: true, ..i.modifiers };
|
||||
i.events.iter().any(|e| {
|
||||
let (key, physical_key, modifiers) = match e {
|
||||
egui::Event::Key { key, physical_key, pressed: true, modifiers, .. } => (key, physical_key, modifiers),
|
||||
egui::Event::Copy => (&Key::C, &None, &clipboard),
|
||||
egui::Event::Cut => (&Key::X, &None, &clipboard),
|
||||
egui::Event::Paste(_) => (&Key::V, &None, &clipboard),
|
||||
_ => return false,
|
||||
};
|
||||
// `Ctrl` is the physical Control key (on macOS distinct from Cmd; elsewhere Cmd = Ctrl);
|
||||
// "Delete" (⌫ = Backspace) also matches forward-delete
|
||||
let ctrl_ok = if m.ctrl { modifiers.ctrl } else { !modifiers.ctrl || modifiers.command };
|
||||
let cmd_ok = m.ctrl || modifiers.command == m.command;
|
||||
// winit can report shifted digits as punctuation (Shift+1 = `!`). Recognize the
|
||||
// physical number key for culling, without changing layout-aware letter shortcuts.
|
||||
let shifted_digit = m.shift
|
||||
&& *physical_key == Some(k)
|
||||
&& matches!(k, Key::Num0 | Key::Num1 | Key::Num2 | Key::Num3 | Key::Num4 | Key::Num5 | Key::Num6 | Key::Num7 | Key::Num8 | Key::Num9);
|
||||
(*key == k || shifted_digit || (k == Key::Backspace && *key == Key::Delete))
|
||||
&& ctrl_ok
|
||||
&& cmd_ok
|
||||
&& modifiers.shift == m.shift
|
||||
&& modifiers.alt == m.alt
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -146,3 +146,19 @@ fn modifiers_wait_for_their_key_and_clipboard_keys_record() {
|
||||
// a saved modifier-only shortcut (from the bug) means no shortcut
|
||||
assert_eq!(crate::shortcuts::parse("Cmd+SuperLeft"), None);
|
||||
}
|
||||
|
||||
/// On Windows and Linux the windowing layer turns Ctrl+C / Ctrl+V into clipboard events, not key
|
||||
/// presses: outside a text field they still copy the edit settings, and with ⇧ open Paste Selected
|
||||
/// Settings.
|
||||
#[test]
|
||||
fn clipboard_events_run_the_copy_and_paste_shortcuts() {
|
||||
let mut h = demo();
|
||||
assert!(h.app.session.clipboard.is_none());
|
||||
h.events.push(egui::Event::Copy);
|
||||
h.settle(SETTLE);
|
||||
assert!(h.app.session.clipboard.is_some(), "Ctrl+C copied the edit settings");
|
||||
let shift_cmd = egui::Modifiers::SHIFT | egui::Modifiers::COMMAND;
|
||||
h.events.extend([egui::Event::ModifiersChanged(shift_cmd), egui::Event::Paste("text".into())]);
|
||||
h.settle(SETTLE);
|
||||
assert!(matches!(h.app.ui.dialog, Some(Dialog::PasteSettings { .. })), "⇧Ctrl+V: {:?}", h.app.ui.dialog);
|
||||
}
|
||||
|
||||
+1
-1
@@ -650,7 +650,7 @@ From `06-shortcuts.md` part 1. Evidence is our binding; conflicts are explained
|
||||
| KEY-GRID | Grid — G | P0 | ✅ | `cmd:view.gridToggle`, `cmd:view.photoGrid` | G toggles Photo Grid ↔ Square Grid |
|
||||
| KEY-INFO | Info — I | P0 | ✅ | `cmd:panel.info` | |
|
||||
| KEY-KEYWORDS | Keywords — K | P0 | ✅ | `cmd:panel.keywords` | |
|
||||
| KEY-CLIPBOARD | Copy / paste edit settings — ⌘C / ⌘V | P0 | ✅ | `cmd:develop.copy`, `cmd:develop.paste` | ⌘X has nothing to cut outside text fields |
|
||||
| KEY-CLIPBOARD | Copy / paste edit settings — ⌘C / ⌘V | P0 | ✅ | `cmd:develop.copy`, `cmd:develop.paste`, `crates/ui-egui/src/shortcuts.rs` (`matches`) | ⌘X has nothing to cut outside text fields; on Windows and Linux, where the windowing layer sends Ctrl+C / Ctrl+V as clipboard events rather than key presses, those run the shortcuts too |
|
||||
| KEY-UNDOREDO | Undo / redo — ⌘Z / ⇧⌘Z | P0 | ✅ | `cmd:edit.undo`, `cmd:edit.redo` | |
|
||||
| KEY-MINIMIZE | Minimize — ⌘M | P1 | ✅ | `apps/lightcraft/src/native_menu.rs` | native Window ▸ Minimize |
|
||||
| KEY-AUTO | Auto — ⇧A | P0 | ✅ | `cmd:develop.auto` | |
|
||||
|
||||
Reference in New Issue
Block a user