Files
pixstrip/pixstrip-gtk/src/steps/step_compress.rs
lashman 4fc4ea7017 Improve Images, Compress, Output, Workflow steps
- Images step: folder drag-and-drop with recursive image scanning, per-file
  list with format and size info, total file size in header, supported
  formats label in empty state
- Compress step: per-format quality controls moved into AdwExpanderRow,
  improved quality level descriptions
- Output step: dynamic image count with total size from loaded_files,
  initial overwrite behavior from config
- Workflow step: properly handle MetadataConfig::Custom in preset import,
  mapping all custom metadata fields to JobConfig
2026-03-06 12:22:15 +02:00

183 lines
5.9 KiB
Rust

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(&gtk::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(&gtk::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(&gtk::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(&gtk::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(),
}
}