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:
@@ -25,6 +25,7 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
.title("Enable Watermark")
|
||||
.subtitle("Add text or image watermark to processed images")
|
||||
.active(cfg.watermark_enabled)
|
||||
.tooltip_text("Toggle watermark on or off")
|
||||
.build();
|
||||
enable_group.add(&enable_row);
|
||||
outer.append(&enable_group);
|
||||
@@ -37,6 +38,10 @@ pub fn build_watermark_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("Watermark preview - press Space to cycle images"),
|
||||
]);
|
||||
|
||||
let info_label = gtk::Label::builder()
|
||||
.label("No images loaded")
|
||||
@@ -78,6 +83,7 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
.title("Type")
|
||||
.subtitle("Choose text or image watermark")
|
||||
.use_subtitle(true)
|
||||
.tooltip_text("Choose between text or image/logo overlay")
|
||||
.build();
|
||||
type_row.set_model(Some(>k::StringList::new(&["Text Watermark", "Image Watermark"])));
|
||||
type_row.set_list_factory(Some(&super::full_text_list_factory()));
|
||||
@@ -95,6 +101,7 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
let text_row = adw::EntryRow::builder()
|
||||
.title("Watermark Text")
|
||||
.text(&cfg.watermark_text)
|
||||
.tooltip_text("The text that appears as a watermark on each image")
|
||||
.build();
|
||||
|
||||
let font_row = adw::ActionRow::builder()
|
||||
@@ -115,12 +122,16 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
let desc = gtk::pango::FontDescription::from_string(&cfg.watermark_font_family);
|
||||
font_button.set_font_desc(&desc);
|
||||
}
|
||||
font_button.update_property(&[
|
||||
gtk::accessible::Property::Label("Choose watermark font"),
|
||||
]);
|
||||
font_row.add_suffix(&font_button);
|
||||
|
||||
let font_size_row = adw::SpinRow::builder()
|
||||
.title("Font Size")
|
||||
.subtitle("Size in pixels")
|
||||
.adjustment(>k::Adjustment::new(cfg.watermark_font_size as f64, 8.0, 200.0, 1.0, 10.0, 0.0))
|
||||
.tooltip_text("Size of watermark text in pixels")
|
||||
.build();
|
||||
|
||||
text_group.add(&text_row);
|
||||
@@ -144,7 +155,9 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
)
|
||||
.activatable(true)
|
||||
.build();
|
||||
image_path_row.add_prefix(>k::Image::from_icon_name("image-x-generic-symbolic"));
|
||||
let image_prefix_icon = gtk::Image::from_icon_name("image-x-generic-symbolic");
|
||||
image_prefix_icon.set_accessible_role(gtk::AccessibleRole::Presentation);
|
||||
image_path_row.add_prefix(&image_prefix_icon);
|
||||
|
||||
let choose_image_button = gtk::Button::builder()
|
||||
.icon_name("document-open-symbolic")
|
||||
@@ -152,6 +165,9 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
.valign(gtk::Align::Center)
|
||||
.has_frame(false)
|
||||
.build();
|
||||
choose_image_button.update_property(&[
|
||||
gtk::accessible::Property::Label("Choose logo image"),
|
||||
]);
|
||||
image_path_row.add_suffix(&choose_image_button);
|
||||
|
||||
image_group.add(&image_path_row);
|
||||
@@ -287,6 +303,9 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
.rgba(&initial_color)
|
||||
.valign(gtk::Align::Center)
|
||||
.build();
|
||||
color_button.update_property(&[
|
||||
gtk::accessible::Property::Label("Choose watermark text color"),
|
||||
]);
|
||||
color_row.add_suffix(&color_button);
|
||||
|
||||
// Opacity slider + reset
|
||||
@@ -300,12 +319,18 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
opacity_scale.set_hexpand(false);
|
||||
opacity_scale.set_valign(gtk::Align::Center);
|
||||
opacity_scale.set_width_request(180);
|
||||
opacity_scale.update_property(&[
|
||||
gtk::accessible::Property::Label("Watermark opacity, 0 to 100 percent"),
|
||||
]);
|
||||
let opacity_reset = gtk::Button::builder()
|
||||
.icon_name("edit-undo-symbolic")
|
||||
.valign(gtk::Align::Center)
|
||||
.tooltip_text("Reset to 50%")
|
||||
.has_frame(false)
|
||||
.build();
|
||||
opacity_reset.update_property(&[
|
||||
gtk::accessible::Property::Label("Reset opacity to 50%"),
|
||||
]);
|
||||
opacity_reset.set_sensitive((cfg.watermark_opacity - 0.5).abs() > 0.01);
|
||||
opacity_row.add_suffix(&opacity_scale);
|
||||
opacity_row.add_suffix(&opacity_reset);
|
||||
@@ -321,12 +346,18 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
rotation_scale.set_hexpand(false);
|
||||
rotation_scale.set_valign(gtk::Align::Center);
|
||||
rotation_scale.set_width_request(180);
|
||||
rotation_scale.update_property(&[
|
||||
gtk::accessible::Property::Label("Watermark rotation, -180 to +180 degrees"),
|
||||
]);
|
||||
let rotation_reset = gtk::Button::builder()
|
||||
.icon_name("edit-undo-symbolic")
|
||||
.valign(gtk::Align::Center)
|
||||
.tooltip_text("Reset to 0 degrees")
|
||||
.has_frame(false)
|
||||
.build();
|
||||
rotation_reset.update_property(&[
|
||||
gtk::accessible::Property::Label("Reset rotation to 0 degrees"),
|
||||
]);
|
||||
rotation_reset.set_sensitive(cfg.watermark_rotation != 0);
|
||||
rotation_row.add_suffix(&rotation_scale);
|
||||
rotation_row.add_suffix(&rotation_reset);
|
||||
@@ -336,6 +367,7 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
.title("Tiled / Repeated")
|
||||
.subtitle("Repeat watermark across the entire image")
|
||||
.active(cfg.watermark_tiled)
|
||||
.tooltip_text("Repeat the watermark in a grid pattern across the entire image")
|
||||
.build();
|
||||
|
||||
// Margin slider + reset
|
||||
@@ -349,12 +381,18 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
margin_scale.set_hexpand(false);
|
||||
margin_scale.set_valign(gtk::Align::Center);
|
||||
margin_scale.set_width_request(180);
|
||||
margin_scale.update_property(&[
|
||||
gtk::accessible::Property::Label("Watermark margin from edges, 0 to 200 pixels"),
|
||||
]);
|
||||
let margin_reset = gtk::Button::builder()
|
||||
.icon_name("edit-undo-symbolic")
|
||||
.valign(gtk::Align::Center)
|
||||
.tooltip_text("Reset to 10 px")
|
||||
.has_frame(false)
|
||||
.build();
|
||||
margin_reset.update_property(&[
|
||||
gtk::accessible::Property::Label("Reset margin to 10 pixels"),
|
||||
]);
|
||||
margin_reset.set_sensitive(cfg.watermark_margin != 10);
|
||||
margin_row.add_suffix(&margin_scale);
|
||||
margin_row.add_suffix(&margin_reset);
|
||||
@@ -371,12 +409,18 @@ pub fn build_watermark_page(state: &AppState) -> adw::NavigationPage {
|
||||
scale_scale.set_hexpand(false);
|
||||
scale_scale.set_valign(gtk::Align::Center);
|
||||
scale_scale.set_width_request(180);
|
||||
scale_scale.update_property(&[
|
||||
gtk::accessible::Property::Label("Watermark scale, 1 to 100 percent of image"),
|
||||
]);
|
||||
let scale_reset = gtk::Button::builder()
|
||||
.icon_name("edit-undo-symbolic")
|
||||
.valign(gtk::Align::Center)
|
||||
.tooltip_text("Reset to 20%")
|
||||
.has_frame(false)
|
||||
.build();
|
||||
scale_reset.update_property(&[
|
||||
gtk::accessible::Property::Label("Reset scale to 20%"),
|
||||
]);
|
||||
scale_reset.set_sensitive((cfg.watermark_scale - 20.0).abs() > 0.5);
|
||||
scale_row.add_suffix(&scale_scale);
|
||||
scale_row.add_suffix(&scale_reset);
|
||||
@@ -573,6 +617,27 @@ pub fn build_watermark_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 gtk::glib::Propagation::Stop;
|
||||
}
|
||||
gtk::glib::Propagation::Proceed
|
||||
});
|
||||
preview_picture.add_controller(key);
|
||||
}
|
||||
|
||||
// === Wire signals ===
|
||||
|
||||
// Enable toggle
|
||||
@@ -857,12 +922,16 @@ pub fn build_watermark_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().watermark_enabled;
|
||||
er.set_active(enabled);
|
||||
ctrl.set_sensitive(!lf.borrow().is_empty());
|
||||
up();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user