use adw::prelude::*; use crate::app::AppState; use pixstrip_core::types::QualityPreset; pub fn build_compress_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 Compression") .subtitle("Reduce file size with quality control") .active(cfg.compress_enabled) .build(); let enable_group = adw::PreferencesGroup::new(); enable_group.add(&enable_row); content.append(&enable_group); // Quality slider let quality_group = adw::PreferencesGroup::builder() .title("Quality Level") .description("Higher quality means larger files. This sets the overall quality target.") .build(); let initial_val = match cfg.quality_preset { QualityPreset::WebOptimized => 1.0, QualityPreset::Low => 2.0, QualityPreset::Medium => 3.0, QualityPreset::High => 4.0, QualityPreset::Maximum => 5.0, }; let quality_scale = gtk::Scale::builder() .orientation(gtk::Orientation::Horizontal) .adjustment(>k::Adjustment::new(initial_val, 1.0, 5.0, 1.0, 1.0, 0.0)) .draw_value(false) .hexpand(true) .build(); quality_scale.set_round_digits(0); quality_scale.add_mark(1.0, gtk::PositionType::Bottom, Some("Web")); quality_scale.add_mark(2.0, gtk::PositionType::Bottom, Some("Low")); quality_scale.add_mark(3.0, gtk::PositionType::Bottom, Some("Medium")); quality_scale.add_mark(4.0, gtk::PositionType::Bottom, Some("High")); quality_scale.add_mark(5.0, gtk::PositionType::Bottom, Some("Maximum")); let quality_label = gtk::Label::builder() .label(quality_description(initial_val as u32)) .css_classes(["dim-label"]) .margin_top(4) .build(); let quality_box = gtk::Box::builder() .orientation(gtk::Orientation::Vertical) .spacing(4) .margin_top(8) .margin_bottom(8) .margin_start(12) .margin_end(12) .build(); quality_box.append(&quality_scale); quality_box.append(&quality_label); quality_group.add(&quality_box); content.append(&quality_group); // Advanced options in expander let advanced_group = adw::PreferencesGroup::builder() .title("Advanced Options") .build(); let advanced_expander = adw::ExpanderRow::builder() .title("Per-Format Quality") .subtitle("Fine-tune quality for each format individually") .build(); let jpeg_row = adw::SpinRow::builder() .title("JPEG Quality") .subtitle("1-100, higher is better quality") .adjustment(>k::Adjustment::new(cfg.jpeg_quality as f64, 1.0, 100.0, 1.0, 10.0, 0.0)) .build(); let png_row = adw::SpinRow::builder() .title("PNG Compression Level") .subtitle("1-6, higher is slower but smaller") .adjustment(>k::Adjustment::new(cfg.png_level as f64, 1.0, 6.0, 1.0, 1.0, 0.0)) .build(); let webp_row = adw::SpinRow::builder() .title("WebP Quality") .subtitle("1-100, higher is better quality") .adjustment(>k::Adjustment::new(cfg.webp_quality as f64, 1.0, 100.0, 1.0, 10.0, 0.0)) .build(); advanced_expander.add_row(&jpeg_row); advanced_expander.add_row(&png_row); advanced_expander.add_row(&webp_row); advanced_group.add(&advanced_expander); content.append(&advanced_group); drop(cfg); // Wire signals { let jc = state.job_config.clone(); enable_row.connect_active_notify(move |row| { jc.borrow_mut().compress_enabled = row.is_active(); }); } { let jc = state.job_config.clone(); let label = quality_label; quality_scale.connect_value_changed(move |scale| { let val = scale.value().round() as u32; let mut c = jc.borrow_mut(); c.quality_preset = match val { 1 => QualityPreset::WebOptimized, 2 => QualityPreset::Low, 3 => QualityPreset::Medium, 4 => QualityPreset::High, _ => QualityPreset::Maximum, }; label.set_label(&quality_description(val)); }); } { let jc = state.job_config.clone(); jpeg_row.connect_value_notify(move |row| { jc.borrow_mut().jpeg_quality = row.value() as u8; }); } { let jc = state.job_config.clone(); png_row.connect_value_notify(move |row| { jc.borrow_mut().png_level = row.value() as u8; }); } { let jc = state.job_config.clone(); webp_row.connect_value_notify(move |row| { jc.borrow_mut().webp_quality = row.value() as u8; }); } scrolled.set_child(Some(&content)); let clamp = adw::Clamp::builder() .maximum_size(600) .child(&scrolled) .build(); adw::NavigationPage::builder() .title("Compress") .tag("step-compress") .child(&clamp) .build() } fn quality_description(val: u32) -> String { match val { 1 => "Web Optimized - smallest files, noticeable quality loss. Best for thumbnails.".into(), 2 => "Low - small files, some quality loss. Good for email attachments.".into(), 3 => "Medium - good balance of quality and size. Recommended for most uses.".into(), 4 => "High - large files, minimal quality loss. Good for printing.".into(), _ => "Maximum - largest files, best possible quality. Archival use.".into(), } }