Add single-instance file handling via GIO HANDLES_OPEN
- App flag HANDLES_OPEN enables receiving files from file manager - connect_open handler filters for image files and activates window - load-external-files action receives paths and adds to loaded files - Auto-navigates to step 2 (images) when files arrive externally - Second instance sends files to existing window instead of launching
This commit is contained in:
@@ -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<std::path::PathBuf> = 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::<adw::ApplicationWindow>()
|
||||
.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::<Vec<_>>()
|
||||
.join("\n");
|
||||
action.downcast_ref::<gtk::gio::SimpleAction>()
|
||||
.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::<String>()) {
|
||||
let new_files: Vec<std::path::PathBuf> = 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));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user