Add per-step contextual help button in header bar
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user