- Welcome dialog buttons navigate between pages - Done button closes dialog and marks first_run_complete - Show welcome dialog on first launch only - Add first_run_complete field to AppConfig with serde(default)
229 lines
6.9 KiB
Rust
229 lines
6.9 KiB
Rust
use adw::prelude::*;
|
|
|
|
pub fn show_welcome_if_first_launch(window: &adw::ApplicationWindow) {
|
|
let config = pixstrip_core::storage::ConfigStore::new();
|
|
if let Ok(cfg) = config.load()
|
|
&& cfg.first_run_complete
|
|
{
|
|
return;
|
|
}
|
|
|
|
let dialog = build_welcome_dialog();
|
|
let win = window.clone();
|
|
dialog.connect_closed(move |_| {
|
|
let config = pixstrip_core::storage::ConfigStore::new();
|
|
if let Ok(mut cfg) = config.load() {
|
|
cfg.first_run_complete = true;
|
|
let _ = config.save(&cfg);
|
|
}
|
|
});
|
|
dialog.present(Some(&win));
|
|
}
|
|
|
|
fn build_welcome_dialog() -> adw::Dialog {
|
|
let dialog = adw::Dialog::builder()
|
|
.title("Welcome to Pixstrip")
|
|
.content_width(500)
|
|
.content_height(450)
|
|
.build();
|
|
|
|
let nav_view = adw::NavigationView::new();
|
|
|
|
let welcome_page = build_welcome_page(&nav_view);
|
|
nav_view.add(&welcome_page);
|
|
|
|
let skill_page = build_skill_page(&nav_view);
|
|
nav_view.add(&skill_page);
|
|
|
|
let output_page = build_output_page(&dialog);
|
|
nav_view.add(&output_page);
|
|
|
|
dialog.set_child(Some(&nav_view));
|
|
dialog
|
|
}
|
|
|
|
fn build_welcome_page(nav_view: &adw::NavigationView) -> adw::NavigationPage {
|
|
let content = gtk::Box::builder()
|
|
.orientation(gtk::Orientation::Vertical)
|
|
.spacing(12)
|
|
.margin_top(24)
|
|
.margin_bottom(24)
|
|
.margin_start(24)
|
|
.margin_end(24)
|
|
.build();
|
|
|
|
let status = adw::StatusPage::builder()
|
|
.title("Welcome to Pixstrip")
|
|
.description("A quick and powerful batch image processor.\nResize, convert, compress, strip metadata, watermark, and rename - all in a simple wizard.")
|
|
.icon_name("image-x-generic-symbolic")
|
|
.vexpand(true)
|
|
.build();
|
|
|
|
let next_button = gtk::Button::builder()
|
|
.label("Get Started")
|
|
.halign(gtk::Align::Center)
|
|
.build();
|
|
next_button.add_css_class("suggested-action");
|
|
next_button.add_css_class("pill");
|
|
|
|
let nav = nav_view.clone();
|
|
next_button.connect_clicked(move |_| {
|
|
nav.push_by_tag("skill-level");
|
|
});
|
|
|
|
content.append(&status);
|
|
content.append(&next_button);
|
|
|
|
adw::NavigationPage::builder()
|
|
.title("Welcome")
|
|
.tag("welcome")
|
|
.child(&content)
|
|
.build()
|
|
}
|
|
|
|
fn build_skill_page(nav_view: &adw::NavigationView) -> adw::NavigationPage {
|
|
let content = gtk::Box::builder()
|
|
.orientation(gtk::Orientation::Vertical)
|
|
.spacing(12)
|
|
.margin_top(24)
|
|
.margin_bottom(24)
|
|
.margin_start(24)
|
|
.margin_end(24)
|
|
.build();
|
|
|
|
let title = gtk::Label::builder()
|
|
.label("How much detail do you want?")
|
|
.css_classes(["title-2"])
|
|
.halign(gtk::Align::Start)
|
|
.build();
|
|
|
|
let subtitle = gtk::Label::builder()
|
|
.label("You can change this anytime in Settings.")
|
|
.css_classes(["dim-label"])
|
|
.halign(gtk::Align::Start)
|
|
.build();
|
|
|
|
let group = adw::PreferencesGroup::new();
|
|
|
|
let simple_row = adw::ActionRow::builder()
|
|
.title("Simple")
|
|
.subtitle("Fewer options visible, great for quick tasks. Advanced options are still available behind expanders.")
|
|
.activatable(true)
|
|
.build();
|
|
simple_row.add_prefix(>k::Image::from_icon_name("emblem-ok-symbolic"));
|
|
let simple_check = gtk::CheckButton::new();
|
|
simple_check.set_active(true);
|
|
simple_row.add_suffix(&simple_check);
|
|
simple_row.set_activatable_widget(Some(&simple_check));
|
|
|
|
let detailed_row = adw::ActionRow::builder()
|
|
.title("Detailed")
|
|
.subtitle("All options visible by default. Best for power users who want full control.")
|
|
.activatable(true)
|
|
.build();
|
|
detailed_row.add_prefix(>k::Image::from_icon_name("preferences-system-symbolic"));
|
|
let detailed_check = gtk::CheckButton::new();
|
|
detailed_check.set_group(Some(&simple_check));
|
|
detailed_row.add_suffix(&detailed_check);
|
|
detailed_row.set_activatable_widget(Some(&detailed_check));
|
|
|
|
group.add(&simple_row);
|
|
group.add(&detailed_row);
|
|
|
|
let next_button = gtk::Button::builder()
|
|
.label("Continue")
|
|
.halign(gtk::Align::Center)
|
|
.build();
|
|
next_button.add_css_class("suggested-action");
|
|
next_button.add_css_class("pill");
|
|
|
|
let nav = nav_view.clone();
|
|
next_button.connect_clicked(move |_| {
|
|
nav.push_by_tag("output-location");
|
|
});
|
|
|
|
content.append(&title);
|
|
content.append(&subtitle);
|
|
content.append(&group);
|
|
content.append(&next_button);
|
|
|
|
adw::NavigationPage::builder()
|
|
.title("Skill Level")
|
|
.tag("skill-level")
|
|
.child(&content)
|
|
.build()
|
|
}
|
|
|
|
fn build_output_page(dialog: &adw::Dialog) -> adw::NavigationPage {
|
|
let content = gtk::Box::builder()
|
|
.orientation(gtk::Orientation::Vertical)
|
|
.spacing(12)
|
|
.margin_top(24)
|
|
.margin_bottom(24)
|
|
.margin_start(24)
|
|
.margin_end(24)
|
|
.build();
|
|
|
|
let title = gtk::Label::builder()
|
|
.label("Where should processed images go?")
|
|
.css_classes(["title-2"])
|
|
.halign(gtk::Align::Start)
|
|
.build();
|
|
|
|
let subtitle = gtk::Label::builder()
|
|
.label("You can change this per-batch or in Settings.")
|
|
.css_classes(["dim-label"])
|
|
.halign(gtk::Align::Start)
|
|
.build();
|
|
|
|
let group = adw::PreferencesGroup::new();
|
|
|
|
let subfolder_row = adw::ActionRow::builder()
|
|
.title("Subfolder next to originals")
|
|
.subtitle("Creates a 'processed' folder next to your images")
|
|
.activatable(true)
|
|
.build();
|
|
subfolder_row.add_prefix(>k::Image::from_icon_name("folder-symbolic"));
|
|
let subfolder_check = gtk::CheckButton::new();
|
|
subfolder_check.set_active(true);
|
|
subfolder_row.add_suffix(&subfolder_check);
|
|
subfolder_row.set_activatable_widget(Some(&subfolder_check));
|
|
|
|
let fixed_row = adw::ActionRow::builder()
|
|
.title("Fixed output folder")
|
|
.subtitle("Always save to the same folder")
|
|
.activatable(true)
|
|
.build();
|
|
fixed_row.add_prefix(>k::Image::from_icon_name("folder-open-symbolic"));
|
|
let fixed_check = gtk::CheckButton::new();
|
|
fixed_check.set_group(Some(&subfolder_check));
|
|
fixed_row.add_suffix(&fixed_check);
|
|
fixed_row.set_activatable_widget(Some(&fixed_check));
|
|
|
|
group.add(&subfolder_row);
|
|
group.add(&fixed_row);
|
|
|
|
let done_button = gtk::Button::builder()
|
|
.label("Start Using Pixstrip")
|
|
.halign(gtk::Align::Center)
|
|
.build();
|
|
done_button.add_css_class("suggested-action");
|
|
done_button.add_css_class("pill");
|
|
|
|
let dlg = dialog.clone();
|
|
done_button.connect_clicked(move |_| {
|
|
dlg.close();
|
|
});
|
|
|
|
content.append(&title);
|
|
content.append(&subtitle);
|
|
content.append(&group);
|
|
content.append(&done_button);
|
|
|
|
adw::NavigationPage::builder()
|
|
.title("Output Location")
|
|
.tag("output-location")
|
|
.child(&content)
|
|
.build()
|
|
}
|