From 116fa51262633516c0ed17d41eb8d65604cee11c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Bartalos?= Date: Sun, 11 Oct 2026 21:38:14 +0200 Subject: [PATCH] Give dialogs roomier text fields and padding (#487) * Give dialogs roomier text fields and padding Single-line fields in dialogs and side panels were only as tall as one line of text, and modals used the popup frame's tight margin. Fields are now 30 points with more inset, and every modal shares one padded card so titles and buttons sit clear of the edge. * Keep long labels inside their rows and dialogs Painted names ran past the edge of fixed rows, and a page hover tip was drawn above open dialogs. Labels now end in an ellipsis, tab rows wrap, and those tips stay hidden while a dialog is open. --------- Co-authored-by: Brandon Thomas --- crates/ui-egui/src/a11y_ui.rs | 3 +- crates/ui-egui/src/actions_ui.rs | 6 +- crates/ui-egui/src/canvas.rs | 35 ++++++-- crates/ui-egui/src/combine_ui.rs | 7 +- crates/ui-egui/src/comment_props.rs | 6 +- crates/ui-egui/src/comments_panel.rs | 13 ++- crates/ui-egui/src/dialogs.rs | 48 +++------- crates/ui-egui/src/js_ui.rs | 9 +- crates/ui-egui/src/link_ui.rs | 2 +- crates/ui-egui/src/marks_ui.rs | 8 +- crates/ui-egui/src/measure_ui.rs | 7 +- crates/ui-egui/src/optimize_ui.rs | 2 +- crates/ui-egui/src/palette.rs | 15 +++- crates/ui-egui/src/panels.rs | 53 +++++++---- crates/ui-egui/src/prepare.rs | 20 ++--- crates/ui-egui/src/print_ui.rs | 4 +- crates/ui-egui/src/protect.rs | 10 +-- crates/ui-egui/src/redact_ui.rs | 4 +- crates/ui-egui/src/search_ui.rs | 5 +- crates/ui-egui/src/sign_ui.rs | 18 ++-- crates/ui-egui/src/stamps_ui.rs | 4 +- crates/ui-egui/src/updates.rs | 2 +- crates/ui-egui/src/widgets.rs | 124 ++++++++++++++++++++++++++ crates/ui-egui/tests/comments.rs | 5 ++ crates/ui-egui/tests/dialog_chrome.rs | 83 +++++++++++++++++ crates/ui-egui/tests/signing.rs | 3 + crates/ui-egui/tests/viewer.rs | 4 + 27 files changed, 368 insertions(+), 132 deletions(-) create mode 100644 crates/ui-egui/tests/dialog_chrome.rs diff --git a/crates/ui-egui/src/a11y_ui.rs b/crates/ui-egui/src/a11y_ui.rs index ef372a933..9482ed635 100644 --- a/crates/ui-egui/src/a11y_ui.rs +++ b/crates/ui-egui/src/a11y_ui.rs @@ -493,8 +493,7 @@ pub(crate) fn alt_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> ui.add_space(8.0); ui.add_enabled_ui(!d.decorative[i], |ui| { let l = ui.label(tl!("Alternate text")); - ui.add(egui::TextEdit::multiline(&mut d.texts[i]).desired_rows(3).desired_width(380.0).hint_text(tl!("Describe the figure"))) - .labelled_by(l.id); + ui.add(crate::widgets::block(&mut d.texts[i]).desired_rows(3).desired_width(380.0).hint_text(tl!("Describe the figure"))).labelled_by(l.id); }); ui.checkbox(&mut d.decorative[i], tl!("Decorative figure")); ui.add_space(10.0); diff --git a/crates/ui-egui/src/actions_ui.rs b/crates/ui-egui/src/actions_ui.rs index b31426373..89cf2569e 100644 --- a/crates/ui-egui/src/actions_ui.rs +++ b/crates/ui-egui/src/actions_ui.rs @@ -307,10 +307,10 @@ fn edit_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> bool { ui.add_space(8.0); egui::Grid::new("action-edit").num_columns(2).spacing([12.0, 8.0]).show(ui, |ui| { ui.label(tl!("Action name:")); - ui.add(egui::TextEdit::singleline(&mut a.name).desired_width(320.0).id_salt("action-name")); + ui.add(crate::widgets::line(&mut a.name).desired_width(320.0).id_salt("action-name")); ui.end_row(); ui.label(tl!("Description:")); - ui.add(egui::TextEdit::singleline(&mut a.description).desired_width(320.0).id_salt("action-description")); + ui.add(crate::widgets::line(&mut a.description).desired_width(320.0).id_salt("action-description")); ui.end_row(); }); ui.add_space(6.0); @@ -326,7 +326,7 @@ fn edit_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> bool { ui.horizontal(|ui| { ui.label(format!("{}. {}", i + 1, tl!(s.label()))); if let Some(arg) = s.arg_mut() { - ui.add(egui::TextEdit::singleline(arg).desired_width(220.0)); + ui.add(crate::widgets::line(arg).desired_width(220.0)); } ui.with_layout(Layout::right_to_left(Align::Center), |ui| { if ui.button(tl!("Remove")).clicked() { diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 32273a96f..b6c1fffee 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -2190,6 +2190,14 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) { }; let mut open_signature = false; let mut hover_text: Option<(Pos2, String)> = None; + // A dialog, save prompt or palette covers the page. Hover tips are drawn above those + // windows, so a comment under the pointer would otherwise float over the dialog. + let page_covered = app.dialog.is_some() + || app.close_request.is_some() + || app.palette_open + || app.password_prompt.is_some() + || app.pending_link.is_some() + || app.updates.open; let mut clicked_link: Option = None; let mut canvas_action: Option = None; let mut field_menu: Option = None; @@ -2734,7 +2742,8 @@ pub fn document_area(app: &mut PdfCraftApp, index: usize, ui: &mut egui::Ui) { } } // Fill & Sign keeps its placement cursor clear of link/comment hover feedback. - if !matches!(tool, QuickTool::Fill(_)) + if !page_covered + && !matches!(tool, QuickTool::Fill(_)) && let Some(p) = pointer { for l in info.links.iter().filter(|l| l.page == i) { @@ -3479,12 +3488,14 @@ fn quick_bar(app: &mut PdfCraftApp, area: Rect, ui: &mut egui::Ui) { ui.painter().rect_filled(row, CornerRadius::same(4), t.hover); } icons::paint(ui, Rect::from_min_size(row.min + vec2(8.0, 6.0), vec2(16.0, 16.0)), tool.icon(), 16.0, t.text); - ui.painter().text( + let reserve = if on { 28.0 } else { 8.0 }; + crate::widgets::paint_left( + ui, row.left_center() + vec2(34.0, 0.0), - Align2::LEFT_CENTER, tl!(tool.label()), theme::regular(13.0), t.text, + (row.width() - 34.0 - reserve).max(0.0), ); if on { icons::paint( @@ -3544,12 +3555,14 @@ fn quick_bar(app: &mut PdfCraftApp, area: Rect, ui: &mut egui::Ui) { ui.painter().rect_filled(row, CornerRadius::same(4), t.hover); } icons::paint(ui, Rect::from_min_size(row.min + vec2(8.0, 6.0), vec2(16.0, 16.0)), tool.icon(), 16.0, t.text); - ui.painter().text( + let reserve = if on { 28.0 } else { 8.0 }; + crate::widgets::paint_left( + ui, row.left_center() + vec2(34.0, 0.0), - Align2::LEFT_CENTER, tl!(tool.label()), theme::regular(13.0), t.text, + (row.width() - 34.0 - reserve).max(0.0), ); if on { icons::paint( @@ -3834,7 +3847,17 @@ fn organize_grid(view: &mut DocView, info: &DocInfo, editable: bool, dirty: bool ui.painter().image(tex.id(), pr, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), Color32::WHITE); } ui.painter().rect_stroke(pr, CornerRadius::ZERO, Stroke::new(1.0, t.border), egui::StrokeKind::Outside); - ui.painter().text(pos2(c.center().x, c.bottom() - 48.0), Align2::CENTER_CENTER, &p.label, theme::medium(12.0), t.text_muted); + let label_font = theme::medium(12.0); + let label_w = (c.width() - 12.0).max(0.0); + let page_label = crate::widgets::fit_line(&p.label, label_w, |s| crate::widgets::text_width(ui, s, &label_font)); + let label_pos = pos2(c.center().x, c.bottom() - 48.0); + ui.painter().with_clip_rect(Rect::from_center_size(label_pos, vec2(label_w, 18.0))).text( + label_pos, + Align2::CENTER_CENTER, + page_label, + label_font, + t.text_muted, + ); // The checkbox toggles just this page without requiring a modifier. Keep it // outside the page image, including on very tall or wide pages. let mut checked = view.selected.contains(&i); diff --git a/crates/ui-egui/src/combine_ui.rs b/crates/ui-egui/src/combine_ui.rs index eb5a7ba7c..7e6d1cc09 100644 --- a/crates/ui-egui/src/combine_ui.rs +++ b/crates/ui-egui/src/combine_ui.rs @@ -898,8 +898,8 @@ fn unlock_prompt(app: &mut PdfCraftApp, ctx: &egui::Context) { } ui.add_space(8.0); ui.horizontal(|ui| { - let field = - ui.add(egui::TextEdit::singleline(&mut prompt.input.0).password(!prompt.show).hint_text(tl!("Password")).desired_width(290.0)); + crate::widgets::begin_dialog(ui); + let field = ui.add(crate::widgets::line(&mut prompt.input.0).password(!prompt.show).hint_text(tl!("Password")).desired_width(290.0)); if !prompt.focused { field.request_focus(); prompt.focused = true; @@ -1374,9 +1374,10 @@ fn table_body( } // The range field and how many pages it takes. SortKey::Pages => { + crate::widgets::begin_dialog(ui); let edit = ui.add_enabled( f.problem.is_none(), - egui::TextEdit::singleline(&mut f.range).hint_text(tl!("All pages")).desired_width(88.0), + crate::widgets::line(&mut f.range).frame(egui::Frame::NONE).hint_text(tl!("All pages")).desired_width(88.0), ); // A visible field on plain and striped rows alike; red when the range is wrong. let border = if check.is_err() { Stroke::new(1.5, ERROR) } else { Stroke::new(1.0, t.border) }; diff --git a/crates/ui-egui/src/comment_props.rs b/crates/ui-egui/src/comment_props.rs index 11776bcaf..a01bb94a2 100644 --- a/crates/ui-egui/src/comment_props.rs +++ b/crates/ui-egui/src/comment_props.rs @@ -167,7 +167,7 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo let kind = tl!(crate::comments_panel::subtype_label(&d.edited.subtype)).to_string(); ui.label(egui::RichText::new(crate::i18n::fmt(tl!("{kind} Properties"), &[("kind", &kind)])).font(theme::semibold(18.0))); ui.add_space(6.0); - ui.horizontal(|ui| { + ui.horizontal_wrapped(|ui| { for (tab, label) in [(PropsTab::Appearance, tl!("Appearance")), (PropsTab::General, tl!("General")), (PropsTab::ReviewHistory, tl!("Review History"))] { @@ -240,10 +240,10 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo PropsTab::General => { egui::Grid::new("comment-general").num_columns(2).spacing([12.0, 10.0]).show(ui, |ui| { let l = ui.label(tl!("Author")); - ui.add(egui::TextEdit::singleline(&mut e.author).desired_width(280.0)).labelled_by(l.id); + ui.add(crate::widgets::line(&mut e.author).desired_width(280.0)).labelled_by(l.id); ui.end_row(); let l = ui.label(tl!("Subject")); - ui.add(egui::TextEdit::singleline(&mut e.subject).desired_width(280.0)).labelled_by(l.id); + ui.add(crate::widgets::line(&mut e.subject).desired_width(280.0)).labelled_by(l.id); ui.end_row(); ui.label(tl!("Modified")); ui.label(e.modified.as_deref().map(pdfcraft_render::pretty_date).unwrap_or_else(|| "—".into())); diff --git a/crates/ui-egui/src/comments_panel.rs b/crates/ui-egui/src/comments_panel.rs index 4dc4dfb71..5182fadcf 100644 --- a/crates/ui-egui/src/comments_panel.rs +++ b/crates/ui-egui/src/comments_panel.rs @@ -109,10 +109,11 @@ pub(crate) fn show( allowed: bool, nav: &mut Option