Improve UX, add popover tour, metadata, and hicolor icons

- Redesign tutorial tour from modal dialogs to popovers pointing at actual UI elements
- Add beginner-friendly improvements: help buttons, tooltips, welcome wizard enhancements
- Add AppStream metainfo with screenshots, branding, categories, keywords, provides
- Update desktop file with GTK category and SingleMainWindow
- Add hicolor icon theme with all sizes (16-512px)
- Fix debounce SourceId panic in rename step
- Various step UI improvements and bug fixes
This commit is contained in:
2026-03-08 14:18:15 +02:00
parent 8d754017fa
commit f3668c45c3
26 changed files with 2292 additions and 473 deletions

View File

@@ -25,6 +25,7 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage {
.title("Enable Adjustments")
.subtitle("Rotate, flip, brightness, contrast, effects")
.active(cfg.adjustments_enabled)
.tooltip_text("Toggle image adjustments on or off")
.build();
enable_group.add(&enable_row);
outer.append(&enable_group);
@@ -37,6 +38,10 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage {
.vexpand(true)
.build();
preview_picture.set_can_target(true);
preview_picture.set_focusable(true);
preview_picture.update_property(&[
gtk::accessible::Property::Label("Adjustments preview - press Space to cycle images"),
]);
let info_label = gtk::Label::builder()
.label("No images loaded")
@@ -78,6 +83,7 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage {
.title("Rotate")
.subtitle("Rotation applied to all images")
.use_subtitle(true)
.tooltip_text("Rotate all images by a fixed angle or auto-orient from EXIF")
.build();
rotate_row.set_model(Some(&gtk::StringList::new(&[
"None",
@@ -93,6 +99,7 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage {
.title("Flip")
.subtitle("Mirror the image")
.use_subtitle(true)
.tooltip_text("Mirror images horizontally or vertically")
.build();
flip_row.set_model(Some(&gtk::StringList::new(&["None", "Horizontal", "Vertical"])));
flip_row.set_list_factory(Some(&super::full_text_list_factory()));
@@ -130,6 +137,9 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage {
.tooltip_text("Reset to 0")
.has_frame(false)
.build();
reset_btn.update_property(&[
gtk::accessible::Property::Label(&format!("Reset {} to 0", title)),
]);
reset_btn.set_sensitive(value != 0);
row.add_suffix(&scale);
@@ -204,6 +214,7 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage {
.title("Crop to Aspect Ratio")
.subtitle("Crop from center to a specific ratio")
.use_subtitle(true)
.tooltip_text("Crop from center to a specific aspect ratio")
.build();
crop_row.set_model(Some(&gtk::StringList::new(&[
"None",
@@ -222,12 +233,14 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage {
.title("Trim Whitespace")
.subtitle("Remove uniform borders around the image")
.active(cfg.trim_whitespace)
.tooltip_text("Detect and remove uniform borders around the image")
.build();
let padding_row = adw::SpinRow::builder()
.title("Canvas Padding")
.subtitle("Add uniform padding (pixels)")
.adjustment(&gtk::Adjustment::new(cfg.canvas_padding as f64, 0.0, 500.0, 1.0, 10.0, 0.0))
.tooltip_text("Add a white border around each image in pixels")
.build();
crop_group.add(&crop_row);
@@ -506,6 +519,27 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage {
preview_picture.add_controller(click);
}
// Keyboard support for preview cycling (Space/Enter)
{
let key = gtk::EventControllerKey::new();
let pidx = preview_index.clone();
let files = state.loaded_files.clone();
let up = update_preview.clone();
key.connect_key_pressed(move |_, keyval, _, _| {
if keyval == gtk::gdk::Key::space || keyval == gtk::gdk::Key::Return {
let loaded = files.borrow();
if loaded.len() > 1 {
let next = (pidx.get() + 1) % loaded.len();
pidx.set(next);
up();
}
return glib::Propagation::Stop;
}
glib::Propagation::Proceed
});
preview_picture.add_controller(key);
}
// === Wire signals ===
{
@@ -694,12 +728,16 @@ pub fn build_adjustments_page(state: &AppState) -> adw::NavigationPage {
.child(&outer)
.build();
// Refresh preview and sensitivity when navigating to this page
// Sync enable toggle, refresh preview and sensitivity when navigating to this page
{
let up = update_preview.clone();
let lf = state.loaded_files.clone();
let ctrl = controls.clone();
let jc = state.job_config.clone();
let er = enable_row.clone();
page.connect_map(move |_| {
let enabled = jc.borrow().adjustments_enabled;
er.set_active(enabled);
ctrl.set_sensitive(!lf.borrow().is_empty());
up();
});