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.deselect-all-images", &["<Control><Shift>a"]);
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("win.show-settings", &["<Control>comma"]);
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);
}
// 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
ui.back_button.connect_clicked({
let action_group = action_group.clone();
@@ -1507,6 +1519,47 @@ fn undo_last_batch(ui: &WizardUi) {
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) {
// Reset state
{
@@ -2071,6 +2124,7 @@ fn show_shortcuts_window(window: &adw::ApplicationWindow) {
]),
("File Management", &[
("Ctrl + O", "Add files"),
("Ctrl + V", "Paste image from clipboard"),
("Ctrl + A", "Select all images"),
("Ctrl + Shift + A", "Deselect all images"),
("Delete", "Remove selected images"),