diff --git a/pixstrip-gtk/src/app.rs b/pixstrip-gtk/src/app.rs index efdb767..dd60ea3 100644 --- a/pixstrip-gtk/src/app.rs +++ b/pixstrip-gtk/src/app.rs @@ -141,9 +141,55 @@ struct WizardUi { pub fn build_app() -> adw::Application { let app = adw::Application::builder() .application_id(APP_ID) + .flags(gtk::gio::ApplicationFlags::HANDLES_OPEN) .build(); app.connect_activate(build_ui); + + // Handle files opened via file manager integration or command line + app.connect_open(|app, files, _hint| { + // Ensure the window exists (activate first if needed) + if app.active_window().is_none() { + app.activate(); + } + + // Collect valid image file paths + let image_paths: Vec = files.iter() + .filter_map(|f| f.path()) + .filter(|p| { + p.extension() + .and_then(|e| e.to_str()) + .is_some_and(|ext| { + matches!( + ext.to_lowercase().as_str(), + "jpg" | "jpeg" | "png" | "webp" | "avif" | "gif" | "tiff" | "tif" | "bmp" + ) + }) + }) + .collect(); + + if image_paths.is_empty() { + return; + } + + // Find the WizardUi through the window's action group + if let Some(window) = app.active_window() { + // Store files in a global channel for the UI to pick up + if let Some(action) = window.downcast_ref::() + .and_then(|w| w.lookup_action("load-external-files")) + { + // Serialize paths as string variant + let paths_str: String = image_paths.iter() + .map(|p| p.display().to_string()) + .collect::>() + .join("\n"); + action.downcast_ref::() + .unwrap() + .activate(Some(&paths_str.to_variant())); + } + } + }); + setup_shortcuts(&app); // App-level quit action @@ -846,6 +892,39 @@ fn setup_window_actions(window: &adw::ApplicationWindow, ui: &WizardUi) { } }); + // Load external files action - used by single-instance file open + { + let ui = ui.clone(); + let action = gtk::gio::SimpleAction::new("load-external-files", Some(&String::static_variant_type())); + action.connect_activate(move |_, param| { + if let Some(paths_str) = param.and_then(|v| v.get::()) { + let new_files: Vec = paths_str + .lines() + .map(std::path::PathBuf::from) + .filter(|p| p.exists()) + .collect(); + + if !new_files.is_empty() { + let count = new_files.len(); + ui.state.loaded_files.borrow_mut().extend(new_files); + ui.toast_overlay.add_toast(adw::Toast::new( + &format!("{} images added from file manager", count) + )); + + // Navigate to step 2 (images) if we're on step 1 or earlier + let current = ui.state.wizard.borrow().current_step; + if current <= 1 { + ui.state.wizard.borrow_mut().current_step = 1; + if let Some(page) = ui.pages.get(1) { + ui.nav_view.push(page); + } + } + } + } + }); + action_group.add_action(&action); + } + window.insert_action_group("win", Some(&action_group)); }