diff --git a/pixstrip-gtk/src/app.rs b/pixstrip-gtk/src/app.rs index 37eebe9..3cd5225 100644 --- a/pixstrip-gtk/src/app.rs +++ b/pixstrip-gtk/src/app.rs @@ -2047,40 +2047,128 @@ fn import_preset(window: &adw::ApplicationWindow, ui: &WizardUi) { } fn save_preset_dialog(window: &adw::ApplicationWindow, ui: &WizardUi) { - let dialog = adw::AlertDialog::builder() - .heading("Save as Preset") - .body("Enter a name for this workflow preset") + let dialog = adw::Dialog::builder() + .title("Save as Preset") + .content_width(400) + .content_height(500) .build(); - let entry = gtk::Entry::builder() - .placeholder_text("My Workflow") + let toolbar = adw::ToolbarView::new(); + let header = adw::HeaderBar::new(); + toolbar.add_top_bar(&header); + + let scrolled = gtk::ScrolledWindow::builder() + .hscrollbar_policy(gtk::PolicyType::Never) + .vexpand(true) + .build(); + + let content = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .spacing(12) + .margin_top(12) + .margin_bottom(12) .margin_start(24) .margin_end(24) .build(); - dialog.set_extra_child(Some(&entry)); - dialog.add_response("cancel", "Cancel"); - dialog.add_response("save", "Save"); - dialog.set_response_appearance("save", adw::ResponseAppearance::Suggested); - dialog.set_default_response(Some("save")); + // Show summary of current settings + let cfg = ui.state.job_config.borrow(); + let summary = build_preset_description(&cfg); + drop(cfg); - let ui = ui.clone(); - dialog.connect_response(None, move |dlg, response| { - if response == "save" { - let extra = dlg.extra_child(); - let name = extra - .as_ref() - .and_then(|w| w.downcast_ref::()) - .map(|e| e.text().to_string()) - .unwrap_or_default(); + let summary_group = adw::PreferencesGroup::builder() + .title("Workflow Summary") + .description(&summary) + .build(); + content.append(&summary_group); + // Name entry + let name_group = adw::PreferencesGroup::builder() + .title("Save as New Preset") + .build(); + + let name_entry = adw::EntryRow::builder() + .title("Preset Name") + .build(); + name_group.add(&name_entry); + + let save_new_button = gtk::Button::builder() + .label("Save New Preset") + .halign(gtk::Align::Center) + .margin_top(8) + .build(); + save_new_button.add_css_class("suggested-action"); + save_new_button.add_css_class("pill"); + + content.append(&name_group); + content.append(&save_new_button); + + // "Update existing" section - show user presets + let store = pixstrip_core::storage::PresetStore::new(); + let user_presets: Vec = store + .list() + .unwrap_or_default() + .into_iter() + .filter(|p| p.is_custom) + .map(|p| p.name) + .collect(); + + if !user_presets.is_empty() { + let update_group = adw::PreferencesGroup::builder() + .title("Or Update Existing Preset") + .description("Overwrite an existing user preset with current settings") + .build(); + + for preset_name in &user_presets { + let row = adw::ActionRow::builder() + .title(preset_name) + .activatable(true) + .build(); + row.add_prefix(>k::Image::from_icon_name("document-save-symbolic")); + row.add_suffix(>k::Image::from_icon_name("go-next-symbolic")); + + let ui_c = ui.clone(); + let dlg_c = dialog.clone(); + let pname = preset_name.clone(); + row.connect_activated(move |_| { + let cfg = ui_c.state.job_config.borrow(); + let preset = build_preset_from_config(&cfg, &pname); + drop(cfg); + + let store = pixstrip_core::storage::PresetStore::new(); + match store.save(&preset) { + Ok(()) => { + let toast = adw::Toast::new(&format!("Updated preset: {}", pname)); + ui_c.toast_overlay.add_toast(toast); + } + Err(e) => { + let toast = adw::Toast::new(&format!("Failed to update: {}", e)); + ui_c.toast_overlay.add_toast(toast); + } + } + dlg_c.close(); + }); + + update_group.add(&row); + } + + content.append(&update_group); + } + + // Wire save new button + { + let ui_c = ui.clone(); + let dlg_c = dialog.clone(); + let entry_c = name_entry.clone(); + save_new_button.connect_clicked(move |_| { + let name = entry_c.text().to_string(); if name.trim().is_empty() { let toast = adw::Toast::new("Please enter a name for the preset"); - ui.toast_overlay.add_toast(toast); + ui_c.toast_overlay.add_toast(toast); return; } - let cfg = ui.state.job_config.borrow(); + let cfg = ui_c.state.job_config.borrow(); let preset = build_preset_from_config(&cfg, &name); drop(cfg); @@ -2088,15 +2176,20 @@ fn save_preset_dialog(window: &adw::ApplicationWindow, ui: &WizardUi) { match store.save(&preset) { Ok(()) => { let toast = adw::Toast::new(&format!("Saved preset: {}", name)); - ui.toast_overlay.add_toast(toast); + ui_c.toast_overlay.add_toast(toast); } Err(e) => { let toast = adw::Toast::new(&format!("Failed to save: {}", e)); - ui.toast_overlay.add_toast(toast); + ui_c.toast_overlay.add_toast(toast); } } - } - }); + dlg_c.close(); + }); + } + + scrolled.set_child(Some(&content)); + toolbar.set_content(Some(&scrolled)); + dialog.set_child(Some(&toolbar)); dialog.present(Some(window)); }