174 lines
5.5 KiB
Rust
174 lines
5.5 KiB
Rust
use adw::prelude::*;
|
|
use crate::app::AppState;
|
|
|
|
pub fn build_adjustments_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();
|
|
|
|
// Rotate
|
|
let rotate_group = adw::PreferencesGroup::builder()
|
|
.title("Orientation")
|
|
.description("Rotate and flip images")
|
|
.build();
|
|
|
|
let rotate_row = adw::ComboRow::builder()
|
|
.title("Rotate")
|
|
.subtitle("Rotation applied to all images")
|
|
.build();
|
|
let rotate_model = gtk::StringList::new(&[
|
|
"None",
|
|
"90 clockwise",
|
|
"180",
|
|
"270 clockwise",
|
|
"Auto-orient (from EXIF)",
|
|
]);
|
|
rotate_row.set_model(Some(&rotate_model));
|
|
rotate_row.set_selected(cfg.rotation);
|
|
|
|
let flip_row = adw::ComboRow::builder()
|
|
.title("Flip")
|
|
.subtitle("Mirror the image")
|
|
.build();
|
|
let flip_model = gtk::StringList::new(&["None", "Horizontal", "Vertical"]);
|
|
flip_row.set_model(Some(&flip_model));
|
|
flip_row.set_selected(cfg.flip);
|
|
|
|
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);
|
|
|
|
// Wire signals
|
|
{
|
|
let jc = state.job_config.clone();
|
|
rotate_row.connect_selected_notify(move |row| {
|
|
jc.borrow_mut().rotation = row.selected();
|
|
});
|
|
}
|
|
{
|
|
let jc = state.job_config.clone();
|
|
flip_row.connect_selected_notify(move |row| {
|
|
jc.borrow_mut().flip = row.selected();
|
|
});
|
|
}
|
|
|
|
scrolled.set_child(Some(&content));
|
|
|
|
let clamp = adw::Clamp::builder()
|
|
.maximum_size(600)
|
|
.child(&scrolled)
|
|
.build();
|
|
|
|
adw::NavigationPage::builder()
|
|
.title("Adjustments")
|
|
.tag("step-adjustments")
|
|
.child(&clamp)
|
|
.build()
|
|
}
|