Add processing, results, and settings UI screens
Processing screen with progress bar, activity log, and pause/cancel. Results screen with stats summary, error section, and action buttons. Settings dialog with General, Processing, Accessibility, Notifications.
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
mod app;
|
mod app;
|
||||||
|
mod processing;
|
||||||
|
mod settings;
|
||||||
mod step_indicator;
|
mod step_indicator;
|
||||||
mod steps;
|
mod steps;
|
||||||
mod wizard;
|
mod wizard;
|
||||||
|
|||||||
232
pixstrip-gtk/src/processing.rs
Normal file
232
pixstrip-gtk/src/processing.rs
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
use adw::prelude::*;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn build_processing_page() -> adw::NavigationPage {
|
||||||
|
let content = gtk::Box::builder()
|
||||||
|
.orientation(gtk::Orientation::Vertical)
|
||||||
|
.spacing(16)
|
||||||
|
.margin_top(24)
|
||||||
|
.margin_bottom(24)
|
||||||
|
.margin_start(24)
|
||||||
|
.margin_end(24)
|
||||||
|
.vexpand(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Title
|
||||||
|
let title = gtk::Label::builder()
|
||||||
|
.label("Processing...")
|
||||||
|
.css_classes(["title-1"])
|
||||||
|
.halign(gtk::Align::Start)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Progress info
|
||||||
|
let progress_label = gtk::Label::builder()
|
||||||
|
.label("0 / 0 images")
|
||||||
|
.css_classes(["heading"])
|
||||||
|
.halign(gtk::Align::Start)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let progress_bar = gtk::ProgressBar::builder()
|
||||||
|
.fraction(0.0)
|
||||||
|
.show_text(true)
|
||||||
|
.text("0%")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let eta_label = gtk::Label::builder()
|
||||||
|
.label("Estimating time remaining...")
|
||||||
|
.css_classes(["dim-label"])
|
||||||
|
.halign(gtk::Align::Start)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Activity log
|
||||||
|
let log_group = adw::PreferencesGroup::builder()
|
||||||
|
.title("Activity")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let log_scrolled = gtk::ScrolledWindow::builder()
|
||||||
|
.hscrollbar_policy(gtk::PolicyType::Never)
|
||||||
|
.vexpand(true)
|
||||||
|
.min_content_height(200)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let log_box = gtk::Box::builder()
|
||||||
|
.orientation(gtk::Orientation::Vertical)
|
||||||
|
.spacing(2)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
log_scrolled.set_child(Some(&log_box));
|
||||||
|
log_group.add(&log_scrolled);
|
||||||
|
|
||||||
|
// Control buttons
|
||||||
|
let button_box = gtk::Box::builder()
|
||||||
|
.orientation(gtk::Orientation::Horizontal)
|
||||||
|
.spacing(12)
|
||||||
|
.halign(gtk::Align::Center)
|
||||||
|
.margin_top(12)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let pause_button = gtk::Button::builder()
|
||||||
|
.label("Pause")
|
||||||
|
.tooltip_text("Pause after current image")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let cancel_button = gtk::Button::builder()
|
||||||
|
.label("Cancel")
|
||||||
|
.tooltip_text("Cancel processing")
|
||||||
|
.build();
|
||||||
|
cancel_button.add_css_class("destructive-action");
|
||||||
|
|
||||||
|
button_box.append(&pause_button);
|
||||||
|
button_box.append(&cancel_button);
|
||||||
|
|
||||||
|
content.append(&title);
|
||||||
|
content.append(&progress_label);
|
||||||
|
content.append(&progress_bar);
|
||||||
|
content.append(&eta_label);
|
||||||
|
content.append(&log_group);
|
||||||
|
content.append(&button_box);
|
||||||
|
|
||||||
|
let clamp = adw::Clamp::builder()
|
||||||
|
.maximum_size(600)
|
||||||
|
.child(&content)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
adw::NavigationPage::builder()
|
||||||
|
.title("Processing")
|
||||||
|
.tag("processing")
|
||||||
|
.child(&clamp)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn build_results_page() -> 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(16)
|
||||||
|
.margin_top(24)
|
||||||
|
.margin_bottom(24)
|
||||||
|
.margin_start(24)
|
||||||
|
.margin_end(24)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Success icon and title
|
||||||
|
let status_icon = gtk::Image::builder()
|
||||||
|
.icon_name("emblem-ok-symbolic")
|
||||||
|
.pixel_size(48)
|
||||||
|
.halign(gtk::Align::Center)
|
||||||
|
.css_classes(["success"])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let title = gtk::Label::builder()
|
||||||
|
.label("Processing Complete")
|
||||||
|
.css_classes(["title-1"])
|
||||||
|
.halign(gtk::Align::Center)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
content.append(&status_icon);
|
||||||
|
content.append(&title);
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
let stats_group = adw::PreferencesGroup::builder()
|
||||||
|
.title("Results")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let images_row = adw::ActionRow::builder()
|
||||||
|
.title("Images processed")
|
||||||
|
.subtitle("0 images")
|
||||||
|
.build();
|
||||||
|
images_row.add_prefix(>k::Image::from_icon_name("image-x-generic-symbolic"));
|
||||||
|
|
||||||
|
let size_before_row = adw::ActionRow::builder()
|
||||||
|
.title("Original size")
|
||||||
|
.subtitle("0 B")
|
||||||
|
.build();
|
||||||
|
size_before_row.add_prefix(>k::Image::from_icon_name("drive-harddisk-symbolic"));
|
||||||
|
|
||||||
|
let size_after_row = adw::ActionRow::builder()
|
||||||
|
.title("Output size")
|
||||||
|
.subtitle("0 B")
|
||||||
|
.build();
|
||||||
|
size_after_row.add_prefix(>k::Image::from_icon_name("drive-harddisk-symbolic"));
|
||||||
|
|
||||||
|
let savings_row = adw::ActionRow::builder()
|
||||||
|
.title("Space saved")
|
||||||
|
.subtitle("0%")
|
||||||
|
.build();
|
||||||
|
savings_row.add_prefix(>k::Image::from_icon_name("emblem-ok-symbolic"));
|
||||||
|
|
||||||
|
let time_row = adw::ActionRow::builder()
|
||||||
|
.title("Processing time")
|
||||||
|
.subtitle("0s")
|
||||||
|
.build();
|
||||||
|
time_row.add_prefix(>k::Image::from_icon_name("preferences-system-time-symbolic"));
|
||||||
|
|
||||||
|
stats_group.add(&images_row);
|
||||||
|
stats_group.add(&size_before_row);
|
||||||
|
stats_group.add(&size_after_row);
|
||||||
|
stats_group.add(&savings_row);
|
||||||
|
stats_group.add(&time_row);
|
||||||
|
content.append(&stats_group);
|
||||||
|
|
||||||
|
// Errors section (initially hidden)
|
||||||
|
let errors_group = adw::PreferencesGroup::builder()
|
||||||
|
.title("Errors")
|
||||||
|
.visible(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let errors_expander = adw::ExpanderRow::builder()
|
||||||
|
.title("0 errors occurred")
|
||||||
|
.build();
|
||||||
|
errors_group.add(&errors_expander);
|
||||||
|
content.append(&errors_group);
|
||||||
|
|
||||||
|
// Action buttons
|
||||||
|
let action_group = adw::PreferencesGroup::new();
|
||||||
|
|
||||||
|
let open_row = adw::ActionRow::builder()
|
||||||
|
.title("Open Output Folder")
|
||||||
|
.subtitle("View processed images in file manager")
|
||||||
|
.activatable(true)
|
||||||
|
.build();
|
||||||
|
open_row.add_prefix(>k::Image::from_icon_name("folder-open-symbolic"));
|
||||||
|
open_row.add_suffix(>k::Image::from_icon_name("go-next-symbolic"));
|
||||||
|
|
||||||
|
let process_more_row = adw::ActionRow::builder()
|
||||||
|
.title("Process Another Batch")
|
||||||
|
.subtitle("Start over with new images")
|
||||||
|
.activatable(true)
|
||||||
|
.build();
|
||||||
|
process_more_row.add_prefix(>k::Image::from_icon_name("view-refresh-symbolic"));
|
||||||
|
process_more_row.add_suffix(>k::Image::from_icon_name("go-next-symbolic"));
|
||||||
|
|
||||||
|
let save_preset_row = adw::ActionRow::builder()
|
||||||
|
.title("Save as Preset")
|
||||||
|
.subtitle("Save this workflow for future use")
|
||||||
|
.activatable(true)
|
||||||
|
.build();
|
||||||
|
save_preset_row.add_prefix(>k::Image::from_icon_name("document-save-symbolic"));
|
||||||
|
save_preset_row.add_suffix(>k::Image::from_icon_name("go-next-symbolic"));
|
||||||
|
|
||||||
|
action_group.add(&open_row);
|
||||||
|
action_group.add(&process_more_row);
|
||||||
|
action_group.add(&save_preset_row);
|
||||||
|
content.append(&action_group);
|
||||||
|
|
||||||
|
scrolled.set_child(Some(&content));
|
||||||
|
|
||||||
|
let clamp = adw::Clamp::builder()
|
||||||
|
.maximum_size(600)
|
||||||
|
.child(&scrolled)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
adw::NavigationPage::builder()
|
||||||
|
.title("Results")
|
||||||
|
.tag("results")
|
||||||
|
.child(&clamp)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
161
pixstrip-gtk/src/settings.rs
Normal file
161
pixstrip-gtk/src/settings.rs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
use adw::prelude::*;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn build_settings_dialog() -> adw::PreferencesDialog {
|
||||||
|
let dialog = adw::PreferencesDialog::builder()
|
||||||
|
.title("Settings")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// General page
|
||||||
|
let general_page = adw::PreferencesPage::builder()
|
||||||
|
.title("General")
|
||||||
|
.icon_name("preferences-system-symbolic")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let output_group = adw::PreferencesGroup::builder()
|
||||||
|
.title("Output")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let subfolder_row = adw::EntryRow::builder()
|
||||||
|
.title("Default output subfolder")
|
||||||
|
.text("processed")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let overwrite_row = adw::ComboRow::builder()
|
||||||
|
.title("Default overwrite behavior")
|
||||||
|
.subtitle("What to do when output files already exist")
|
||||||
|
.build();
|
||||||
|
let overwrite_model = gtk::StringList::new(&[
|
||||||
|
"Ask before overwriting",
|
||||||
|
"Auto-rename with suffix",
|
||||||
|
"Always overwrite",
|
||||||
|
"Skip existing files",
|
||||||
|
]);
|
||||||
|
overwrite_row.set_model(Some(&overwrite_model));
|
||||||
|
|
||||||
|
let remember_row = adw::SwitchRow::builder()
|
||||||
|
.title("Remember last-used settings")
|
||||||
|
.subtitle("Restore wizard state on next launch")
|
||||||
|
.active(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
output_group.add(&subfolder_row);
|
||||||
|
output_group.add(&overwrite_row);
|
||||||
|
output_group.add(&remember_row);
|
||||||
|
general_page.add(&output_group);
|
||||||
|
|
||||||
|
let ui_group = adw::PreferencesGroup::builder()
|
||||||
|
.title("Interface")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let skill_row = adw::ComboRow::builder()
|
||||||
|
.title("Detail level")
|
||||||
|
.subtitle("Controls how many options are visible by default")
|
||||||
|
.build();
|
||||||
|
let skill_model = gtk::StringList::new(&["Simple", "Detailed"]);
|
||||||
|
skill_row.set_model(Some(&skill_model));
|
||||||
|
|
||||||
|
ui_group.add(&skill_row);
|
||||||
|
general_page.add(&ui_group);
|
||||||
|
dialog.add(&general_page);
|
||||||
|
|
||||||
|
// Processing page
|
||||||
|
let processing_page = adw::PreferencesPage::builder()
|
||||||
|
.title("Processing")
|
||||||
|
.icon_name("system-run-symbolic")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let threads_group = adw::PreferencesGroup::builder()
|
||||||
|
.title("Performance")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let threads_row = adw::ComboRow::builder()
|
||||||
|
.title("Processing threads")
|
||||||
|
.subtitle("Auto uses all available CPU cores")
|
||||||
|
.build();
|
||||||
|
let threads_model = gtk::StringList::new(&["Auto", "1", "2", "4", "8"]);
|
||||||
|
threads_row.set_model(Some(&threads_model));
|
||||||
|
|
||||||
|
let error_row = adw::ComboRow::builder()
|
||||||
|
.title("On error")
|
||||||
|
.subtitle("What to do when an image fails to process")
|
||||||
|
.build();
|
||||||
|
let error_model = gtk::StringList::new(&["Skip and continue", "Pause on error"]);
|
||||||
|
error_row.set_model(Some(&error_model));
|
||||||
|
|
||||||
|
threads_group.add(&threads_row);
|
||||||
|
threads_group.add(&error_row);
|
||||||
|
processing_page.add(&threads_group);
|
||||||
|
dialog.add(&processing_page);
|
||||||
|
|
||||||
|
// Accessibility page
|
||||||
|
let a11y_page = adw::PreferencesPage::builder()
|
||||||
|
.title("Accessibility")
|
||||||
|
.icon_name("preferences-desktop-accessibility-symbolic")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let a11y_group = adw::PreferencesGroup::builder()
|
||||||
|
.title("Visual Preferences")
|
||||||
|
.description("Override system settings for this app only")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let contrast_row = adw::SwitchRow::builder()
|
||||||
|
.title("High contrast")
|
||||||
|
.subtitle("Increase visual contrast throughout the app")
|
||||||
|
.active(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let large_text_row = adw::SwitchRow::builder()
|
||||||
|
.title("Large text")
|
||||||
|
.subtitle("Increase text size throughout the app")
|
||||||
|
.active(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let motion_row = adw::SwitchRow::builder()
|
||||||
|
.title("Reduced motion")
|
||||||
|
.subtitle("Minimize animations and transitions")
|
||||||
|
.active(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
a11y_group.add(&contrast_row);
|
||||||
|
a11y_group.add(&large_text_row);
|
||||||
|
a11y_group.add(&motion_row);
|
||||||
|
a11y_page.add(&a11y_group);
|
||||||
|
dialog.add(&a11y_page);
|
||||||
|
|
||||||
|
// Notifications page
|
||||||
|
let notify_page = adw::PreferencesPage::builder()
|
||||||
|
.title("Notifications")
|
||||||
|
.icon_name("preferences-system-notifications-symbolic")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let notify_group = adw::PreferencesGroup::builder()
|
||||||
|
.title("Completion")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let desktop_notify_row = adw::SwitchRow::builder()
|
||||||
|
.title("Desktop notification")
|
||||||
|
.subtitle("Show notification when processing completes")
|
||||||
|
.active(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let sound_row = adw::SwitchRow::builder()
|
||||||
|
.title("Completion sound")
|
||||||
|
.subtitle("Play a sound when processing completes")
|
||||||
|
.active(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let auto_open_row = adw::SwitchRow::builder()
|
||||||
|
.title("Auto-open output folder")
|
||||||
|
.subtitle("Open the output folder in file manager when done")
|
||||||
|
.active(false)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
notify_group.add(&desktop_notify_row);
|
||||||
|
notify_group.add(&sound_row);
|
||||||
|
notify_group.add(&auto_open_row);
|
||||||
|
notify_page.add(¬ify_group);
|
||||||
|
dialog.add(¬ify_page);
|
||||||
|
|
||||||
|
dialog
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user