Add ETA calculation, activity log, keyboard shortcuts, expand format options
- Processing: ETA calculated from elapsed time and progress, per-image log entries added to activity log with auto-scroll - Keyboard shortcuts: Ctrl+Q quit, Ctrl+, settings, Ctrl+?/F1 shortcuts dialog - Shortcuts dialog: AdwAlertDialog with all keyboard shortcuts listed - Hamburger menu: added Keyboard Shortcuts entry - Convert step: added AVIF, GIF, TIFF format options with descriptions - Resize step: removed duplicate rotate/flip (now in Adjustments step), added missing social media presets (PeerTube, Friendica, Funkwhale, Instagram Portrait, Facebook Cover/Profile, LinkedIn Cover/Profile, TikTok, YouTube Channel Art, Threads, Twitter/X, 4K, etc.)
This commit is contained in:
@@ -29,9 +29,10 @@ pub fn build_resize_page(state: &AppState) -> adw::NavigationPage {
|
||||
enable_group.add(&enable_row);
|
||||
content.append(&enable_group);
|
||||
|
||||
// Resize mode
|
||||
// Resize dimensions
|
||||
let mode_group = adw::PreferencesGroup::builder()
|
||||
.title("Resize Mode")
|
||||
.title("Dimensions")
|
||||
.description("Set target width and height. Set height to 0 to preserve aspect ratio.")
|
||||
.build();
|
||||
|
||||
let width_row = adw::SpinRow::builder()
|
||||
@@ -53,132 +54,100 @@ pub fn build_resize_page(state: &AppState) -> adw::NavigationPage {
|
||||
// Social media presets
|
||||
let presets_group = adw::PreferencesGroup::builder()
|
||||
.title("Quick Dimension Presets")
|
||||
.description("Click a preset to fill in the dimensions above")
|
||||
.build();
|
||||
|
||||
let fedi_expander = adw::ExpanderRow::builder()
|
||||
.title("Fediverse / Open Platforms")
|
||||
.subtitle("Mastodon, Pixelfed, Bluesky, Lemmy")
|
||||
.build();
|
||||
|
||||
let fedi_presets: Vec<(&str, u32, u32)> = vec![
|
||||
("Mastodon Post", 1920, 1080),
|
||||
("Mastodon Profile", 400, 400),
|
||||
("Mastodon Header", 1500, 500),
|
||||
("Pixelfed Post", 1080, 1080),
|
||||
("Pixelfed Story", 1080, 1920),
|
||||
("Bluesky Post", 1200, 630),
|
||||
("Bluesky Profile", 400, 400),
|
||||
("Lemmy Post", 1200, 630),
|
||||
];
|
||||
|
||||
for (name, w, h) in &fedi_presets {
|
||||
let row = adw::ActionRow::builder()
|
||||
.title(*name)
|
||||
.subtitle(format!("{} x {}", w, h))
|
||||
.activatable(true)
|
||||
// Helper to build preset expander sections
|
||||
let build_preset_section = |title: &str, subtitle: &str, presets: &[(&str, u32, u32)]| -> adw::ExpanderRow {
|
||||
let expander = adw::ExpanderRow::builder()
|
||||
.title(title)
|
||||
.subtitle(subtitle)
|
||||
.build();
|
||||
row.add_suffix(>k::Image::from_icon_name("go-next-symbolic"));
|
||||
let width_row_c = width_row.clone();
|
||||
let height_row_c = height_row.clone();
|
||||
let w = *w;
|
||||
let h = *h;
|
||||
row.connect_activated(move |_| {
|
||||
width_row_c.set_value(w as f64);
|
||||
height_row_c.set_value(h as f64);
|
||||
});
|
||||
fedi_expander.add_row(&row);
|
||||
}
|
||||
|
||||
let mainstream_expander = adw::ExpanderRow::builder()
|
||||
.title("Mainstream Platforms")
|
||||
.subtitle("Instagram, YouTube, LinkedIn, Pinterest")
|
||||
.build();
|
||||
for (name, w, h) in presets {
|
||||
let row = adw::ActionRow::builder()
|
||||
.title(*name)
|
||||
.subtitle(if *h == 0 { format!("{} wide", w) } else { format!("{} x {}", w, h) })
|
||||
.activatable(true)
|
||||
.build();
|
||||
row.add_suffix(>k::Image::from_icon_name("go-next-symbolic"));
|
||||
let width_c = width_row.clone();
|
||||
let height_c = height_row.clone();
|
||||
let w = *w;
|
||||
let h = *h;
|
||||
row.connect_activated(move |_| {
|
||||
width_c.set_value(w as f64);
|
||||
height_c.set_value(h as f64);
|
||||
});
|
||||
expander.add_row(&row);
|
||||
}
|
||||
|
||||
let mainstream_presets: Vec<(&str, u32, u32)> = vec![
|
||||
("Instagram Post", 1080, 1080),
|
||||
("Instagram Story/Reel", 1080, 1920),
|
||||
("Facebook Post", 1200, 630),
|
||||
("YouTube Thumbnail", 1280, 720),
|
||||
("LinkedIn Post", 1200, 627),
|
||||
("Pinterest Pin", 1000, 1500),
|
||||
];
|
||||
expander
|
||||
};
|
||||
|
||||
for (name, w, h) in &mainstream_presets {
|
||||
let row = adw::ActionRow::builder()
|
||||
.title(*name)
|
||||
.subtitle(format!("{} x {}", w, h))
|
||||
.activatable(true)
|
||||
.build();
|
||||
row.add_suffix(>k::Image::from_icon_name("go-next-symbolic"));
|
||||
let width_row_c = width_row.clone();
|
||||
let height_row_c = height_row.clone();
|
||||
let w = *w;
|
||||
let h = *h;
|
||||
row.connect_activated(move |_| {
|
||||
width_row_c.set_value(w as f64);
|
||||
height_row_c.set_value(h as f64);
|
||||
});
|
||||
mainstream_expander.add_row(&row);
|
||||
}
|
||||
let fedi_expander = build_preset_section(
|
||||
"Fediverse / Open Platforms",
|
||||
"Mastodon, Pixelfed, Bluesky, Lemmy, PeerTube",
|
||||
&[
|
||||
("Mastodon Post", 1920, 1080),
|
||||
("Mastodon Profile", 400, 400),
|
||||
("Mastodon Header", 1500, 500),
|
||||
("Pixelfed Post", 1080, 1080),
|
||||
("Pixelfed Story", 1080, 1920),
|
||||
("Bluesky Post", 1200, 630),
|
||||
("Bluesky Profile", 400, 400),
|
||||
("Lemmy Post", 1200, 630),
|
||||
("PeerTube Thumbnail", 1280, 720),
|
||||
("Friendica Post", 1200, 630),
|
||||
("Funkwhale Cover", 500, 500),
|
||||
],
|
||||
);
|
||||
|
||||
let other_expander = adw::ExpanderRow::builder()
|
||||
.title("Common Sizes")
|
||||
.subtitle("HD, Blog, Thumbnail")
|
||||
.build();
|
||||
let mainstream_expander = build_preset_section(
|
||||
"Mainstream Platforms",
|
||||
"Instagram, YouTube, LinkedIn, Facebook, TikTok",
|
||||
&[
|
||||
("Instagram Post", 1080, 1080),
|
||||
("Instagram Portrait", 1080, 1350),
|
||||
("Instagram Story/Reel", 1080, 1920),
|
||||
("Facebook Post", 1200, 630),
|
||||
("Facebook Cover", 820, 312),
|
||||
("Facebook Profile", 170, 170),
|
||||
("YouTube Thumbnail", 1280, 720),
|
||||
("YouTube Channel Art", 2560, 1440),
|
||||
("LinkedIn Post", 1200, 627),
|
||||
("LinkedIn Cover", 1584, 396),
|
||||
("LinkedIn Profile", 400, 400),
|
||||
("Pinterest Pin", 1000, 1500),
|
||||
("TikTok Profile", 200, 200),
|
||||
("Threads Post", 1080, 1080),
|
||||
("Twitter/X Post", 1200, 675),
|
||||
("Twitter/X Header", 1500, 500),
|
||||
],
|
||||
);
|
||||
|
||||
let other_presets: Vec<(&str, u32, u32)> = vec![
|
||||
("Full HD", 1920, 1080),
|
||||
("Blog Image", 800, 0),
|
||||
("Thumbnail", 150, 150),
|
||||
];
|
||||
|
||||
for (name, w, h) in &other_presets {
|
||||
let row = adw::ActionRow::builder()
|
||||
.title(*name)
|
||||
.subtitle(if *h == 0 { format!("{} wide", w) } else { format!("{} x {}", w, h) })
|
||||
.activatable(true)
|
||||
.build();
|
||||
row.add_suffix(>k::Image::from_icon_name("go-next-symbolic"));
|
||||
let width_row_c = width_row.clone();
|
||||
let height_row_c = height_row.clone();
|
||||
let w = *w;
|
||||
let h = *h;
|
||||
row.connect_activated(move |_| {
|
||||
width_row_c.set_value(w as f64);
|
||||
height_row_c.set_value(h as f64);
|
||||
});
|
||||
other_expander.add_row(&row);
|
||||
}
|
||||
let common_expander = build_preset_section(
|
||||
"Common Sizes",
|
||||
"HD, 4K, Blog, Thumbnails",
|
||||
&[
|
||||
("4K UHD", 3840, 2160),
|
||||
("Full HD", 1920, 1080),
|
||||
("HD Ready", 1280, 720),
|
||||
("Blog Wide", 800, 0),
|
||||
("Blog Standard", 800, 600),
|
||||
("Email Header", 600, 200),
|
||||
("Large Thumbnail", 300, 300),
|
||||
("Small Thumbnail", 150, 150),
|
||||
("Favicon", 32, 32),
|
||||
],
|
||||
);
|
||||
|
||||
presets_group.add(&fedi_expander);
|
||||
presets_group.add(&mainstream_expander);
|
||||
presets_group.add(&other_expander);
|
||||
presets_group.add(&common_expander);
|
||||
content.append(&presets_group);
|
||||
|
||||
// Basic adjustments
|
||||
let adjust_group = adw::PreferencesGroup::builder()
|
||||
.title("Basic Adjustments")
|
||||
.build();
|
||||
|
||||
let rotate_row = adw::ComboRow::builder()
|
||||
.title("Rotate")
|
||||
.subtitle("Rotation applied after resize")
|
||||
.build();
|
||||
let rotate_model = gtk::StringList::new(&["None", "90 clockwise", "180", "270 clockwise", "Auto-orient (EXIF)"]);
|
||||
rotate_row.set_model(Some(&rotate_model));
|
||||
|
||||
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));
|
||||
|
||||
adjust_group.add(&rotate_row);
|
||||
adjust_group.add(&flip_row);
|
||||
content.append(&adjust_group);
|
||||
|
||||
// Advanced
|
||||
// Advanced options
|
||||
let advanced_group = adw::PreferencesGroup::builder()
|
||||
.title("Advanced Options")
|
||||
.build();
|
||||
@@ -194,7 +163,7 @@ pub fn build_resize_page(state: &AppState) -> adw::NavigationPage {
|
||||
|
||||
drop(cfg);
|
||||
|
||||
// Wire signals to update JobConfig
|
||||
// Wire signals
|
||||
{
|
||||
let jc = state.job_config.clone();
|
||||
enable_row.connect_active_notify(move |row| {
|
||||
|
||||
Reference in New Issue
Block a user