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 ef372a93..9482ed63 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 b3142637..89cf2569 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 32273a96..b6c1fffe 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 eb5a7ba7..7e6d1cc0 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 11776bca..a01bb94a 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 4dc4dfb7..5182fadc 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