diff --git a/pixstrip-gtk/src/settings.rs b/pixstrip-gtk/src/settings.rs index 7f55c44..494d67f 100644 --- a/pixstrip-gtk/src/settings.rs +++ b/pixstrip-gtk/src/settings.rs @@ -20,11 +20,84 @@ pub fn build_settings_dialog() -> adw::PreferencesDialog { .title("Output") .build(); + // Output mode: subfolder or fixed path + let output_mode_row = adw::ComboRow::builder() + .title("Default output location") + .subtitle("Where processed images are saved by default") + .build(); + let output_mode_model = gtk::StringList::new(&[ + "Subfolder next to originals", + "Fixed output folder", + ]); + output_mode_row.set_model(Some(&output_mode_model)); + output_mode_row.set_selected(if config.output_fixed_path.is_some() { 1 } else { 0 }); + let subfolder_row = adw::EntryRow::builder() .title("Default output subfolder") .text(&config.output_subfolder) + .visible(config.output_fixed_path.is_none()) .build(); + let fixed_path_row = adw::ActionRow::builder() + .title("Fixed output folder") + .subtitle( + config.output_fixed_path + .as_deref() + .unwrap_or("No folder selected"), + ) + .activatable(true) + .visible(config.output_fixed_path.is_some()) + .build(); + fixed_path_row.add_prefix(>k::Image::from_icon_name("folder-open-symbolic")); + + let choose_fixed_btn = gtk::Button::builder() + .icon_name("document-open-symbolic") + .tooltip_text("Choose output folder") + .valign(gtk::Align::Center) + .build(); + choose_fixed_btn.add_css_class("flat"); + fixed_path_row.add_suffix(&choose_fixed_btn); + + // Shared state for fixed path + let fixed_path_state: std::rc::Rc>> = + std::rc::Rc::new(std::cell::RefCell::new(config.output_fixed_path.clone())); + + // Wire output mode toggle + { + let sf = subfolder_row.clone(); + let fp = fixed_path_row.clone(); + output_mode_row.connect_selected_notify(move |row| { + let is_fixed = row.selected() == 1; + sf.set_visible(!is_fixed); + fp.set_visible(is_fixed); + }); + } + + // Wire fixed path chooser + { + let fps = fixed_path_state.clone(); + let fpr = fixed_path_row.clone(); + choose_fixed_btn.connect_clicked(move |btn| { + let fps = fps.clone(); + let fpr = fpr.clone(); + let dialog = gtk::FileDialog::builder() + .title("Choose Output Folder") + .modal(true) + .build(); + if let Some(window) = btn.root().and_then(|r| r.downcast::().ok()) { + dialog.select_folder(Some(&window), gtk::gio::Cancellable::NONE, move |result| { + if let Ok(file) = result + && let Some(path) = file.path() + { + let path_str = path.display().to_string(); + fpr.set_subtitle(&path_str); + *fps.borrow_mut() = Some(path_str); + } + }); + } + }); + } + let overwrite_row = adw::ComboRow::builder() .title("Default overwrite behavior") .subtitle("What to do when output files already exist") @@ -49,7 +122,9 @@ pub fn build_settings_dialog() -> adw::PreferencesDialog { .active(config.remember_settings) .build(); + output_group.add(&output_mode_row); output_group.add(&subfolder_row); + output_group.add(&fixed_path_row); output_group.add(&overwrite_row); output_group.add(&remember_row); general_page.add(&output_group); @@ -366,6 +441,8 @@ pub fn build_settings_dialog() -> adw::PreferencesDialog { let notify = desktop_notify_row.clone(); let sound = sound_row.clone(); let auto_open = auto_open_row.clone(); + let output_mode = output_mode_row.clone(); + let fps_reset = fixed_path_state.clone(); reset_button.connect_clicked(move |_| { let defaults = AppConfig::default(); subfolder.set_text(&defaults.output_subfolder); @@ -380,6 +457,8 @@ pub fn build_settings_dialog() -> adw::PreferencesDialog { notify.set_active(defaults.notify_on_completion); sound.set_active(defaults.play_completion_sound); auto_open.set_active(defaults.auto_open_output); + output_mode.set_selected(0); + *fps_reset.borrow_mut() = None; }); } @@ -393,7 +472,11 @@ pub fn build_settings_dialog() -> adw::PreferencesDialog { first_run_complete: true, tutorial_complete: true, // preserve if settings are being saved output_subfolder: subfolder_row.text().to_string(), - output_fixed_path: None, + output_fixed_path: if output_mode_row.selected() == 1 { + fixed_path_state.borrow().clone() + } else { + None + }, overwrite_behavior: match overwrite_row.selected() { 1 => OverwriteBehavior::AutoRename, 2 => OverwriteBehavior::Overwrite,