use adw::prelude::*; use crate::app::{AppState, MetadataMode}; pub fn build_metadata_page(state: &AppState) -> adw::NavigationPage { 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(); let cfg = state.job_config.borrow(); // Enable toggle let enable_row = adw::SwitchRow::builder() .title("Enable Metadata Handling") .subtitle("Control what image metadata to keep or remove") .active(cfg.metadata_enabled) .tooltip_text("Toggle metadata handling on or off") .build(); let enable_group = adw::PreferencesGroup::new(); enable_group.add(&enable_row); content.append(&enable_group); // Quick presets let presets_group = adw::PreferencesGroup::builder() .title("Metadata Mode") .build(); let strip_all_row = adw::ActionRow::builder() .title("Strip All") .subtitle("Remove all metadata - smallest files, maximum privacy") .activatable(true) .build(); let strip_all_icon = gtk::Image::from_icon_name("user-trash-symbolic"); strip_all_icon.set_accessible_role(gtk::AccessibleRole::Presentation); strip_all_row.add_prefix(&strip_all_icon); let strip_all_check = gtk::CheckButton::new(); strip_all_check.set_active(cfg.metadata_mode == MetadataMode::StripAll); strip_all_row.add_suffix(&strip_all_check); strip_all_row.set_activatable_widget(Some(&strip_all_check)); let privacy_row = adw::ActionRow::builder() .title("Privacy Mode") .subtitle("Strip GPS and camera serial, keep copyright") .activatable(true) .build(); let privacy_icon = gtk::Image::from_icon_name("security-medium-symbolic"); privacy_icon.set_accessible_role(gtk::AccessibleRole::Presentation); privacy_row.add_prefix(&privacy_icon); let privacy_check = gtk::CheckButton::new(); privacy_check.set_group(Some(&strip_all_check)); privacy_check.set_active(cfg.metadata_mode == MetadataMode::Privacy); privacy_row.add_suffix(&privacy_check); privacy_row.set_activatable_widget(Some(&privacy_check)); let keep_all_row = adw::ActionRow::builder() .title("Keep All") .subtitle("Preserve all original metadata") .activatable(true) .build(); let keep_all_icon = gtk::Image::from_icon_name("emblem-ok-symbolic"); keep_all_icon.set_accessible_role(gtk::AccessibleRole::Presentation); keep_all_row.add_prefix(&keep_all_icon); let keep_all_check = gtk::CheckButton::new(); keep_all_check.set_group(Some(&strip_all_check)); keep_all_check.set_active(cfg.metadata_mode == MetadataMode::KeepAll); keep_all_row.add_suffix(&keep_all_check); keep_all_row.set_activatable_widget(Some(&keep_all_check)); let photographer_row = adw::ActionRow::builder() .title("Photographer") .subtitle("Keep copyright and camera model, strip GPS and software") .activatable(true) .build(); let photographer_icon = gtk::Image::from_icon_name("camera-photo-symbolic"); photographer_icon.set_accessible_role(gtk::AccessibleRole::Presentation); photographer_row.add_prefix(&photographer_icon); let photographer_check = gtk::CheckButton::new(); photographer_check.set_group(Some(&strip_all_check)); photographer_row.add_suffix(&photographer_check); photographer_row.set_activatable_widget(Some(&photographer_check)); let custom_row = adw::ActionRow::builder() .title("Custom") .subtitle("Choose exactly which metadata categories to strip") .activatable(true) .build(); let custom_icon = gtk::Image::from_icon_name("emblem-system-symbolic"); custom_icon.set_accessible_role(gtk::AccessibleRole::Presentation); custom_row.add_prefix(&custom_icon); let custom_check = gtk::CheckButton::new(); custom_check.set_group(Some(&strip_all_check)); custom_check.set_active(cfg.metadata_mode == MetadataMode::Custom); custom_row.add_suffix(&custom_check); custom_row.set_activatable_widget(Some(&custom_check)); presets_group.add(&strip_all_row); presets_group.add(&privacy_row); presets_group.add(&photographer_row); presets_group.add(&keep_all_row); presets_group.add(&custom_row); content.append(&presets_group); // Custom category checkboxes let custom_group = adw::PreferencesGroup::builder() .title("Custom Categories") .description("Select which metadata categories to strip") .build(); let gps_row = adw::SwitchRow::builder() .title("GPS / Location") .subtitle("GPS coordinates, location name, altitude") .active(cfg.strip_gps) .tooltip_text("Strip GPS coordinates, location name, and altitude") .build(); let camera_row = adw::SwitchRow::builder() .title("Camera Info") .subtitle("Camera model, serial number, lens data") .active(cfg.strip_camera) .tooltip_text("Strip camera model, serial number, and lens data") .build(); let software_row = adw::SwitchRow::builder() .title("Software") .subtitle("Editing software, processing history") .active(cfg.strip_software) .tooltip_text("Strip editing software and processing history") .build(); let timestamps_row = adw::SwitchRow::builder() .title("Timestamps") .subtitle("Date taken, date modified, date digitized") .active(cfg.strip_timestamps) .tooltip_text("Strip date taken, date modified, date digitized") .build(); let copyright_row = adw::SwitchRow::builder() .title("Copyright / Author") .subtitle("Copyright notice, artist name, credits") .active(cfg.strip_copyright) .tooltip_text("Strip copyright notice, artist name, and credits") .build(); custom_group.add(&gps_row); custom_group.add(&camera_row); custom_group.add(&software_row); custom_group.add(×tamps_row); custom_group.add(©right_row); // Only show custom group when Custom mode is selected custom_group.set_visible(cfg.metadata_mode == MetadataMode::Custom); content.append(&custom_group); drop(cfg); // Wire signals { let jc = state.job_config.clone(); enable_row.connect_active_notify(move |row| { jc.borrow_mut().metadata_enabled = row.is_active(); }); } { let jc = state.job_config.clone(); let cg = custom_group.clone(); strip_all_check.connect_toggled(move |check| { if check.is_active() { jc.borrow_mut().metadata_mode = MetadataMode::StripAll; cg.set_visible(false); } }); } { let jc = state.job_config.clone(); let cg = custom_group.clone(); privacy_check.connect_toggled(move |check| { if check.is_active() { jc.borrow_mut().metadata_mode = MetadataMode::Privacy; cg.set_visible(false); } }); } { let jc = state.job_config.clone(); let cg = custom_group.clone(); let gps_c = gps_row.clone(); let camera_c = camera_row.clone(); let software_c = software_row.clone(); let timestamps_c = timestamps_row.clone(); let copyright_c = copyright_row.clone(); photographer_check.connect_toggled(move |check| { if check.is_active() { { let mut cfg = jc.borrow_mut(); cfg.metadata_mode = MetadataMode::Custom; // Photographer: keep copyright + camera model, strip GPS + software cfg.strip_gps = true; cfg.strip_camera = false; cfg.strip_software = true; cfg.strip_timestamps = false; cfg.strip_copyright = false; } // Update UI to match (after dropping borrow to avoid re-entrancy) gps_c.set_active(true); camera_c.set_active(false); software_c.set_active(true); timestamps_c.set_active(false); copyright_c.set_active(false); cg.set_visible(false); } }); } { let jc = state.job_config.clone(); let cg = custom_group.clone(); keep_all_check.connect_toggled(move |check| { if check.is_active() { jc.borrow_mut().metadata_mode = MetadataMode::KeepAll; cg.set_visible(false); } }); } { let jc = state.job_config.clone(); let cg = custom_group; custom_check.connect_toggled(move |check| { if check.is_active() { jc.borrow_mut().metadata_mode = MetadataMode::Custom; cg.set_visible(true); } }); } // Wire custom category toggles { let jc = state.job_config.clone(); gps_row.connect_active_notify(move |row| { jc.borrow_mut().strip_gps = row.is_active(); }); } { let jc = state.job_config.clone(); camera_row.connect_active_notify(move |row| { jc.borrow_mut().strip_camera = row.is_active(); }); } { let jc = state.job_config.clone(); software_row.connect_active_notify(move |row| { jc.borrow_mut().strip_software = row.is_active(); }); } { let jc = state.job_config.clone(); timestamps_row.connect_active_notify(move |row| { jc.borrow_mut().strip_timestamps = row.is_active(); }); } { let jc = state.job_config.clone(); copyright_row.connect_active_notify(move |row| { jc.borrow_mut().strip_copyright = row.is_active(); }); } scrolled.set_child(Some(&content)); let page = adw::NavigationPage::builder() .title("Metadata") .tag("step-metadata") .child(&scrolled) .build(); // Sync enable toggle when navigating to this page { let jc = state.job_config.clone(); let er = enable_row.clone(); page.connect_map(move |_| { let enabled = jc.borrow().metadata_enabled; er.set_active(enabled); }); } page }