Add Ctrl+V clipboard paste for images

Reads image textures from the system clipboard, saves as temporary
PNG files, and adds them to the batch. Shows toast notification
on success or when no image is found.
This commit is contained in:
2026-03-06 13:46:20 +02:00
parent 0ca15536ae
commit 257fd53bbc

View File

@@ -131,6 +131,7 @@ fn setup_shortcuts(app: &adw::Application) {
app.set_accels_for_action("win.select-all-images", &["<Control>a"]); app.set_accels_for_action("win.select-all-images", &["<Control>a"]);
app.set_accels_for_action("win.deselect-all-images", &["<Control><Shift>a"]); app.set_accels_for_action("win.deselect-all-images", &["<Control><Shift>a"]);
app.set_accels_for_action("win.undo-last-batch", &["<Control>z"]); app.set_accels_for_action("win.undo-last-batch", &["<Control>z"]);
app.set_accels_for_action("win.paste-images", &["<Control>v"]);
app.set_accels_for_action("app.quit", &["<Control>q"]); app.set_accels_for_action("app.quit", &["<Control>q"]);
app.set_accels_for_action("win.show-settings", &["<Control>comma"]); app.set_accels_for_action("win.show-settings", &["<Control>comma"]);
app.set_accels_for_action("win.show-shortcuts", &["<Control>question", "F1"]); app.set_accels_for_action("win.show-shortcuts", &["<Control>question", "F1"]);
@@ -576,6 +577,17 @@ fn setup_window_actions(window: &adw::ApplicationWindow, ui: &WizardUi) {
action_group.add_action(&action); action_group.add_action(&action);
} }
// Paste images from clipboard (Ctrl+V)
{
let window = window.clone();
let ui = ui.clone();
let action = gtk::gio::SimpleAction::new("paste-images", None);
action.connect_activate(move |_, _| {
paste_images_from_clipboard(&window, &ui);
});
action_group.add_action(&action);
}
// Connect button clicks // Connect button clicks
ui.back_button.connect_clicked({ ui.back_button.connect_clicked({
let action_group = action_group.clone(); let action_group = action_group.clone();
@@ -1507,6 +1519,47 @@ fn undo_last_batch(ui: &WizardUi) {
ui.toast_overlay.add_toast(toast); ui.toast_overlay.add_toast(toast);
} }
fn paste_images_from_clipboard(window: &adw::ApplicationWindow, ui: &WizardUi) {
let clipboard = window.clipboard();
let ui = ui.clone();
// Try to read a texture (image) from clipboard
clipboard.read_texture_async(gtk::gio::Cancellable::NONE, move |result| {
if let Ok(Some(texture)) = result {
// Save the texture to a temp file
let temp_dir = std::env::temp_dir().join("pixstrip-clipboard");
if std::fs::create_dir_all(&temp_dir).is_err() {
ui.toast_overlay.add_toast(adw::Toast::new("Failed to create temporary directory"));
return;
}
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis();
let temp_path = temp_dir.join(format!("clipboard-{}.png", timestamp));
let bytes = texture.save_to_png_bytes();
if std::fs::write(&temp_path, bytes.as_ref()).is_ok() {
let mut files = ui.state.loaded_files.borrow_mut();
if !files.contains(&temp_path) {
files.push(temp_path);
}
let count = files.len();
drop(files);
update_images_count_label(&ui, count);
let toast = adw::Toast::new("Pasted image from clipboard");
toast.set_timeout(2);
ui.toast_overlay.add_toast(toast);
} else {
ui.toast_overlay.add_toast(adw::Toast::new("Failed to save clipboard image"));
}
} else {
ui.toast_overlay.add_toast(adw::Toast::new("No image found in clipboard"));
}
});
}
fn reset_wizard(ui: &WizardUi) { fn reset_wizard(ui: &WizardUi) {
// Reset state // Reset state
{ {
@@ -2071,6 +2124,7 @@ fn show_shortcuts_window(window: &adw::ApplicationWindow) {
]), ]),
("File Management", &[ ("File Management", &[
("Ctrl + O", "Add files"), ("Ctrl + O", "Add files"),
("Ctrl + V", "Paste image from clipboard"),
("Ctrl + A", "Select all images"), ("Ctrl + A", "Select all images"),
("Ctrl + Shift + A", "Deselect all images"), ("Ctrl + Shift + A", "Deselect all images"),
("Delete", "Remove selected images"), ("Delete", "Remove selected images"),