fix(ui): a V held while the window was away doesn't paste on release (#663)

winit sends no press for a key held across a focus change (X11's
synthetic presses are dropped by egui-winit), so letting go of V after
coming back looked like a swallowed Cmd+V. After a focus gain, a V
release only pastes with Cmd still held.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
RealtaNua
2026-10-17 00:35:46 +07:00
co-authored by Claude Opus 5.5
parent 40955aae7c
commit 4adbd6cee9
3 changed files with 33 additions and 2 deletions
+3
View File
@@ -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(),
+24
View File
@@ -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();
+6 -2
View File
@@ -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,
_ => {}
}
}