diff --git a/pixstrip-gtk/src/app.rs b/pixstrip-gtk/src/app.rs index e5ba75e..59f98f8 100644 --- a/pixstrip-gtk/src/app.rs +++ b/pixstrip-gtk/src/app.rs @@ -1128,9 +1128,30 @@ fn show_results( } else { format!("{} images processed", result.succeeded) }; - let toast = adw::Toast::new(&savings); - toast.set_timeout(5); - ui.toast_overlay.add_toast(toast.clone()); + // Undo toast with savings info + let undo_toast = adw::Toast::new(&savings); + undo_toast.set_button_label(Some("Undo")); + undo_toast.set_timeout(10); + { + let output_dir = ui.state.output_dir.borrow().clone(); + undo_toast.connect_button_clicked(move |t| { + if let Some(ref dir) = output_dir { + let mut trashed = 0; + if let Ok(entries) = std::fs::read_dir(dir) { + for entry in entries.flatten() { + let gfile = gtk::gio::File::for_path(entry.path()); + if gfile.trash(gtk::gio::Cancellable::NONE).is_ok() { + trashed += 1; + } + } + } + t.dismiss(); + // Will show a new toast from the caller + let _ = trashed; + } + }); + } + ui.toast_overlay.add_toast(undo_toast); // Desktop notification (if enabled in settings) let config_store = pixstrip_core::storage::ConfigStore::new(); diff --git a/pixstrip-gtk/src/steps/step_adjustments.rs b/pixstrip-gtk/src/steps/step_adjustments.rs index d82976b..3ef591b 100644 --- a/pixstrip-gtk/src/steps/step_adjustments.rs +++ b/pixstrip-gtk/src/steps/step_adjustments.rs @@ -20,31 +20,24 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage { // Rotate let rotate_group = adw::PreferencesGroup::builder() - .title("Rotation") + .title("Orientation") + .description("Rotate and flip images") .build(); let rotate_row = adw::ComboRow::builder() .title("Rotate") - .subtitle("Rotation applied after resize") + .subtitle("Rotation applied to all images") .build(); let rotate_model = gtk::StringList::new(&[ "None", "90 clockwise", "180", "270 clockwise", - "Auto-orient (EXIF)", + "Auto-orient (from EXIF)", ]); rotate_row.set_model(Some(&rotate_model)); rotate_row.set_selected(cfg.rotation); - rotate_group.add(&rotate_row); - content.append(&rotate_group); - - // Flip - let flip_group = adw::PreferencesGroup::builder() - .title("Flip") - .build(); - let flip_row = adw::ComboRow::builder() .title("Flip") .subtitle("Mirror the image") @@ -53,8 +46,101 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage { flip_row.set_model(Some(&flip_model)); flip_row.set_selected(cfg.flip); - flip_group.add(&flip_row); - content.append(&flip_group); + rotate_group.add(&rotate_row); + rotate_group.add(&flip_row); + content.append(&rotate_group); + + // Advanced adjustments in an expander + let advanced_group = adw::PreferencesGroup::builder() + .title("Image Adjustments") + .build(); + + let advanced_expander = adw::ExpanderRow::builder() + .title("Advanced Adjustments") + .subtitle("Brightness, contrast, saturation, effects") + .show_enable_switch(false) + .build(); + + // Brightness slider (-100 to +100) + let brightness_row = adw::ActionRow::builder() + .title("Brightness") + .subtitle("0") + .build(); + let brightness_scale = gtk::Scale::with_range(gtk::Orientation::Horizontal, -100.0, 100.0, 1.0); + brightness_scale.set_value(0.0); + brightness_scale.set_hexpand(true); + brightness_scale.set_valign(gtk::Align::Center); + brightness_scale.set_size_request(200, -1); + brightness_scale.set_draw_value(false); + let br_label = brightness_row.clone(); + brightness_scale.connect_value_changed(move |scale| { + br_label.set_subtitle(&format!("{:.0}", scale.value())); + }); + brightness_row.add_suffix(&brightness_scale); + advanced_expander.add_row(&brightness_row); + + // Contrast slider (-100 to +100) + let contrast_row = adw::ActionRow::builder() + .title("Contrast") + .subtitle("0") + .build(); + let contrast_scale = gtk::Scale::with_range(gtk::Orientation::Horizontal, -100.0, 100.0, 1.0); + contrast_scale.set_value(0.0); + contrast_scale.set_hexpand(true); + contrast_scale.set_valign(gtk::Align::Center); + contrast_scale.set_size_request(200, -1); + contrast_scale.set_draw_value(false); + let ct_label = contrast_row.clone(); + contrast_scale.connect_value_changed(move |scale| { + ct_label.set_subtitle(&format!("{:.0}", scale.value())); + }); + contrast_row.add_suffix(&contrast_scale); + advanced_expander.add_row(&contrast_row); + + // Saturation slider (-100 to +100) + let saturation_row = adw::ActionRow::builder() + .title("Saturation") + .subtitle("0") + .build(); + let saturation_scale = gtk::Scale::with_range(gtk::Orientation::Horizontal, -100.0, 100.0, 1.0); + saturation_scale.set_value(0.0); + saturation_scale.set_hexpand(true); + saturation_scale.set_valign(gtk::Align::Center); + saturation_scale.set_size_request(200, -1); + saturation_scale.set_draw_value(false); + let sat_label = saturation_row.clone(); + saturation_scale.connect_value_changed(move |scale| { + sat_label.set_subtitle(&format!("{:.0}", scale.value())); + }); + saturation_row.add_suffix(&saturation_scale); + advanced_expander.add_row(&saturation_row); + + // Sharpen after resize + let sharpen_row = adw::SwitchRow::builder() + .title("Sharpen after resize") + .subtitle("Apply subtle sharpening to resized images") + .active(false) + .build(); + advanced_expander.add_row(&sharpen_row); + + // Grayscale + let grayscale_row = adw::SwitchRow::builder() + .title("Grayscale") + .subtitle("Convert images to black and white") + .active(false) + .build(); + advanced_expander.add_row(&grayscale_row); + + // Sepia + let sepia_row = adw::SwitchRow::builder() + .title("Sepia") + .subtitle("Apply a warm vintage tone") + .active(false) + .build(); + advanced_expander.add_row(&sepia_row); + + advanced_group.add(&advanced_expander); + content.append(&advanced_group); drop(cfg); diff --git a/pixstrip-gtk/src/steps/step_compress.rs b/pixstrip-gtk/src/steps/step_compress.rs index 54e313f..a34fd4d 100644 --- a/pixstrip-gtk/src/steps/step_compress.rs +++ b/pixstrip-gtk/src/steps/step_compress.rs @@ -106,9 +106,23 @@ pub fn build_compress_page(state: &AppState) -> adw::NavigationPage { .adjustment(>k::Adjustment::new(cfg.webp_quality as f64, 1.0, 100.0, 1.0, 10.0, 0.0)) .build(); + let avif_row = adw::SpinRow::builder() + .title("AVIF Quality") + .subtitle("1-100, higher is better quality") + .adjustment(>k::Adjustment::new(50.0, 1.0, 100.0, 1.0, 10.0, 0.0)) + .build(); + + let progressive_row = adw::SwitchRow::builder() + .title("Progressive JPEG") + .subtitle("Loads gradually, slightly larger files") + .active(false) + .build(); + advanced_expander.add_row(&jpeg_row); + advanced_expander.add_row(&progressive_row); advanced_expander.add_row(&png_row); advanced_expander.add_row(&webp_row); + advanced_expander.add_row(&avif_row); advanced_group.add(&advanced_expander); content.append(&advanced_group);