diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index bbc61149..9fbd17cc 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -500,6 +500,8 @@ pub struct UiState { /// (`shortcuts::swallowed_paste`). pub v_pressed: bool, pub paste_event: bool, + /// The window got the focus back since the last V release. + pub refocused: bool, /// When (UI clock, seconds) open documents next get an autosave checkpoint. pub next_autosave: f64, /// With auto-save on: when (UI clock, seconds) files with unsaved changes are next saved, and @@ -595,6 +597,7 @@ impl Default for UiState { last_copy: None, v_pressed: false, paste_event: false, + refocused: false, next_autosave: 0.0, auto_save_at: None, auto_saving: Vec::new(), diff --git a/crates/ui-egui/src/paste_image_tests.rs b/crates/ui-egui/src/paste_image_tests.rs index 6e202df5..fa797fb9 100644 --- a/crates/ui-egui/src/paste_image_tests.rs +++ b/crates/ui-egui/src/paste_image_tests.rs @@ -200,6 +200,30 @@ fn a_v_press_lost_to_another_window_does_not_block_the_next_cmd_v() { assert_eq!(w.named("Pasted image"), 1); } +#[test] +fn v_held_while_the_window_was_away_pastes_nothing_on_release() { + let mut w = Win::new(); + w.copy_image_elsewhere(); + // Pressed in another window: only the focus and the release arrive here. + w.frame(vec![Event::WindowFocused(true)]); + w.frame(vec![key(Key::V, false, Modifiers::NONE)]); + w.frame(vec![]); + assert_eq!(w.named("Pasted image"), 0); + w.cmd_v(CMD); + assert_eq!(w.named("Pasted image"), 1, "the next Cmd+V still pastes"); +} + +#[test] +fn cmd_v_in_a_panel_field_does_not_paste_on_the_canvas() { + let mut w = Win::new(); + w.copy_image_elsewhere(); + w.frame(vec![]); + w.app.field_focus = true; + w.frame(vec![key(Key::V, false, CMD)]); + w.frame(vec![]); + assert_eq!(w.named("Pasted image"), 0); +} + #[test] fn paste_here_prefers_a_newer_image_over_copied_layers() { let mut w = Win::new(); diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 1ac2824f..f28a2433 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -146,10 +146,13 @@ pub fn swallowed_paste(app: &mut FigApp, ctx: &Context) -> bool { Event::Paste(_) => ui.paste_event = true, Event::Key { key: Key::V, pressed: true, .. } => ui.v_pressed = true, Event::Key { key: Key::V, pressed: false, modifiers, .. } => { - // Shift or Alt with it is another command (paste over, paste properties). - paste = !ui.v_pressed && !ui.paste_event && !modifiers.shift && !modifiers.alt; + // Shift or Alt with it is another command (paste over, paste properties). A V + // held while the window was away comes back as a release alone: without Cmd + // held, that's no paste. + paste = !ui.v_pressed && !ui.paste_event && !modifiers.shift && !modifiers.alt && (modifiers.command || !ui.refocused); ui.v_pressed = false; ui.paste_event = false; + ui.refocused = false; } // The other keys that paste (Shift+Insert, a Paste key) end their paste event. Event::Key { key: Key::Insert | Key::Paste, pressed: false, .. } => ui.paste_event = false, @@ -158,6 +161,7 @@ pub fn swallowed_paste(app: &mut FigApp, ctx: &Context) -> bool { ui.v_pressed = false; ui.paste_event = false; } + Event::WindowFocused(true) => ui.refocused = true, _ => {} } }