From bcf57927e98d161f447dd86023bbaf1bc6be7fe2 Mon Sep 17 00:00:00 2001 From: lashman Date: Fri, 6 Mar 2026 13:48:13 +0200 Subject: [PATCH] Add per-step contextual help button in header bar A "?" button in the header bar shows an AlertDialog with help text specific to the current wizard step, explaining what each step does and key shortcuts available. --- pixstrip-gtk/src/app.rs | 98 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/pixstrip-gtk/src/app.rs b/pixstrip-gtk/src/app.rs index fd8f2e3..cd1203d 100644 --- a/pixstrip-gtk/src/app.rs +++ b/pixstrip-gtk/src/app.rs @@ -203,6 +203,14 @@ fn build_ui(app: &adw::Application) { let title = adw::WindowTitle::new("Pixstrip", "Batch Image Processor"); header.set_title_widget(Some(&title)); + // Help button for per-step contextual help + let help_button = gtk::Button::builder() + .icon_name("help-about-symbolic") + .tooltip_text("Help for this step") + .build(); + help_button.add_css_class("flat"); + header.pack_end(&help_button); + // Hamburger menu let menu = build_menu(); let menu_button = gtk::MenuButton::builder() @@ -334,6 +342,16 @@ fn build_ui(app: &adw::Application) { state: app_state, }; + // Wire help button to show contextual help for current step + { + let wizard = ui.state.wizard.clone(); + let window_ref = window.clone(); + help_button.connect_clicked(move |_| { + let step = wizard.borrow().current_step; + show_step_help(&window_ref, step); + }); + } + setup_window_actions(&window, &ui); update_nav_buttons( &ui.state.wizard.borrow(), @@ -2203,6 +2221,86 @@ fn format_bytes(bytes: u64) -> String { } } +fn show_step_help(window: &adw::ApplicationWindow, step: usize) { + let (title, body) = match step { + 0 => ("Workflow", concat!( + "Choose a preset to start quickly, or configure each step manually.\n\n", + "Presets apply recommended settings for common tasks like web optimization, ", + "social media, or print preparation. You can customize any preset after applying it.\n\n", + "Use Import/Export to share presets with others." + )), + 1 => ("Images", concat!( + "Add the images you want to process.\n\n", + "- Drag and drop files or folders onto this area\n", + "- Use Browse to pick files from a file dialog\n", + "- Press Ctrl+V to paste from clipboard\n\n", + "Use checkboxes to include or exclude individual images. ", + "Ctrl+A selects all, Ctrl+Shift+A deselects all." + )), + 2 => ("Resize", concat!( + "Scale images to specific dimensions.\n\n", + "Choose a preset size or enter custom dimensions. Width-only or height-only ", + "resizing preserves the original aspect ratio.\n\n", + "Enable 'Allow upscale' if you need images smaller than the target to be enlarged." + )), + 3 => ("Adjustments", concat!( + "Fine-tune image appearance.\n\n", + "Adjust brightness, contrast, and saturation with sliders. ", + "Apply rotation, flipping, grayscale, or sepia effects.\n\n", + "Crop to a specific aspect ratio or trim whitespace borders automatically." + )), + 4 => ("Convert", concat!( + "Change image file format.\n\n", + "Convert between JPEG, PNG, WebP, AVIF, GIF, TIFF, and BMP. ", + "Each format has trade-offs between quality, file size, and compatibility.\n\n", + "WebP and AVIF offer the best compression for web use." + )), + 5 => ("Compress", concat!( + "Reduce file size while preserving quality.\n\n", + "Choose a quality preset (Lossless, High, Balanced, Small, Tiny) or set custom ", + "quality values per format.\n\n", + "Expand Advanced Options for fine control over WebP encoding effort and AVIF speed." + )), + 6 => ("Metadata", concat!( + "Control what metadata is kept or removed.\n\n", + "Strip All removes everything. Privacy mode keeps copyright and camera info but ", + "removes GPS and timestamps. Custom mode lets you pick exactly what to strip.\n\n", + "Removing metadata reduces file size and protects privacy." + )), + 7 => ("Watermark", concat!( + "Add a text or image watermark.\n\n", + "Choose text or logo mode. Position the watermark using the visual grid. ", + "Expand Advanced Options for opacity, rotation, tiling, margin, and scale controls.\n\n", + "Logo watermarks support PNG images with transparency." + )), + 8 => ("Rename", concat!( + "Rename output files using patterns.\n\n", + "Add a prefix, suffix, or use a full template with placeholders:\n", + "- {name} - original filename\n", + "- {n} - counter number\n", + "- {date} - current date\n", + "- {ext} - original extension\n\n", + "Expand Advanced Options for case conversion and find-and-replace." + )), + 9 => ("Output", concat!( + "Review settings and choose where to save.\n\n", + "The summary shows all operations that will be applied. ", + "Choose an output folder or use the default 'processed' subfolder.\n\n", + "Set overwrite behavior for when output files already exist. ", + "Press Process or Ctrl+Enter to start." + )), + _ => ("Help", "No help available for this step."), + }; + + let dialog = adw::AlertDialog::builder() + .heading(format!("Help: {}", title)) + .body(body) + .build(); + dialog.add_response("ok", "Got it"); + dialog.set_default_response(Some("ok")); + dialog.present(Some(window)); +} + fn format_duration(ms: u64) -> String { if ms < 1000 { format!("{}ms", ms)