mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-14 19:27:04 -04:00
The notice bar above the pages (security, forms, XFA, repaired file, warnings, signatures) laid its message out before its buttons. In a narrow document area (a side panel open, a longer translation) the message ran under "Security settings" and on into the side panel, and it wasn't centred on the buttons. A shared notice_bar helper now lays the buttons out first, right to left, centred on a 28 px row, and gives the message the width that's left: it wraps there (cut short with the full text on hover when less than 120 px is left) and its first line is centred on the buttons' row. Regression test: editing::the_security_notice_wraps_beside_its_buttons_and_lines_up_with_them (one line, wrapped and cut-short widths; fails on main). Co-authored-by: DWAA1660 <[email protected]> Co-authored-by: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2 files changed
+81
-33
No files matched your search
@@ -2950,16 +2950,10 @@ fn notices(
|
||||
let xfa = doc.xfa.as_ref();
|
||||
if let Some((icon, color, template, arg)) = signed {
|
||||
let mut open = false;
|
||||
egui::Frame::NONE.fill(t.accent_soft).inner_margin(egui::Margin::symmetric(14, 7)).show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(icons::image(icon, 16.0, color));
|
||||
ui.label(egui::RichText::new(crate::i18n::fmt(tl!(template), &[("by", &arg)])).color(t.text));
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if crate::widgets::pill_button(ui, tl!("Signature panel"), false).clicked() {
|
||||
open = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
notice_bar(ui, t, icon, color, crate::i18n::fmt(tl!(template), &[("by", &arg)]), |ui| {
|
||||
if crate::widgets::pill_button(ui, tl!("Signature panel"), false).clicked() {
|
||||
open = true;
|
||||
}
|
||||
});
|
||||
return open.then_some(Notice::Signatures);
|
||||
}
|
||||
@@ -3010,29 +3004,23 @@ fn notices(
|
||||
None
|
||||
};
|
||||
let (icon, text, fields) = msg?;
|
||||
egui::Frame::NONE.fill(t.accent_soft).inner_margin(egui::Margin::symmetric(14, 7)).show(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(icons::image(icon, 16.0, t.accent_text));
|
||||
ui.label(egui::RichText::new(text).color(t.text));
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if icons::button(ui, "x", 22.0, false, tl!("Dismiss")).clicked() {
|
||||
view.notice_dismissed = true;
|
||||
}
|
||||
if fields {
|
||||
let label = if view.highlight_fields { tl!("Hide field highlights") } else { tl!("Highlight fields") };
|
||||
if crate::widgets::pill_button(ui, label, view.highlight_fields).clicked() {
|
||||
view.highlight_fields = !view.highlight_fields;
|
||||
toggled = Some(Notice::FieldHighlights(view.highlight_fields));
|
||||
}
|
||||
}
|
||||
if secured && crate::widgets::pill_button(ui, tl!("Security settings"), false).clicked() {
|
||||
open_security = true;
|
||||
}
|
||||
if repaired && !secured && info.fields.is_empty() && crate::widgets::pill_button(ui, tl!("Details"), false).clicked() {
|
||||
open_repairs = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
notice_bar(ui, t, icon, t.accent_text, text, |ui| {
|
||||
if icons::button(ui, "x", 22.0, false, tl!("Dismiss")).clicked() {
|
||||
view.notice_dismissed = true;
|
||||
}
|
||||
if fields {
|
||||
let label = if view.highlight_fields { tl!("Hide field highlights") } else { tl!("Highlight fields") };
|
||||
if crate::widgets::pill_button(ui, label, view.highlight_fields).clicked() {
|
||||
view.highlight_fields = !view.highlight_fields;
|
||||
toggled = Some(Notice::FieldHighlights(view.highlight_fields));
|
||||
}
|
||||
}
|
||||
if secured && crate::widgets::pill_button(ui, tl!("Security settings"), false).clicked() {
|
||||
open_security = true;
|
||||
}
|
||||
if repaired && !secured && info.fields.is_empty() && crate::widgets::pill_button(ui, tl!("Details"), false).clicked() {
|
||||
open_repairs = true;
|
||||
}
|
||||
});
|
||||
if open_repairs {
|
||||
return Some(Notice::Repairs);
|
||||
@@ -3040,6 +3028,37 @@ fn notices(
|
||||
open_security.then_some(Notice::Security).or(toggled)
|
||||
}
|
||||
|
||||
/// One notice bar: an icon and a message on the left, `buttons` (added right to left) on the
|
||||
/// right. The buttons are laid out first, so the message only gets the width they leave: it
|
||||
/// wraps there (or, when very little is left, is cut short with the full text on hover) instead
|
||||
/// of running under the buttons and past the document area into the side panel (#785). The
|
||||
/// buttons are centred on a row of button height, and the message's first line is centred on
|
||||
/// the same row; a wrapped message grows the bar downwards.
|
||||
fn notice_bar(ui: &mut egui::Ui, t: &Tokens, icon: &str, icon_color: Color32, text: String, buttons: impl FnOnce(&mut egui::Ui)) {
|
||||
const ROW: f32 = 28.0;
|
||||
const ICON: f32 = 16.0;
|
||||
egui::Frame::NONE.fill(t.accent_soft).inner_margin(egui::Margin::symmetric(14, 7)).show(ui, |ui| {
|
||||
let width = ui.available_width();
|
||||
ui.allocate_ui_with_layout(vec2(width, ROW), egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
ui.set_min_height(ROW);
|
||||
buttons(ui);
|
||||
let rest = ui.available_width().max(0.0);
|
||||
ui.allocate_ui_with_layout(vec2(rest, ROW), egui::Layout::top_down(egui::Align::Min), |ui| {
|
||||
let font = egui::TextStyle::Body.resolve(ui.style());
|
||||
let line = ui.fonts_mut(|f| f.row_height(&font)).max(ICON);
|
||||
ui.add_space(((ROW - line) / 2.0).max(0.0));
|
||||
ui.with_layout(egui::Layout::left_to_right(egui::Align::Min), |ui| {
|
||||
let (slot, _) = ui.allocate_exact_size(vec2(ICON, line), Sense::hover());
|
||||
icons::paint(ui, Rect::from_center_size(slot.center(), vec2(ICON, ICON)), icon, ICON, icon_color);
|
||||
let label = egui::Label::new(egui::RichText::new(text).color(t.text));
|
||||
// Wrapping into a sliver would stack the message a few letters per line.
|
||||
ui.add(if ui.available_width() >= 120.0 { label.wrap() } else { label.truncate() });
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// The floating quick-action bar at the left edge of the document area.
|
||||
fn quick_bar(app: &mut PdfCraftApp, area: Rect, ui: &mut egui::Ui) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
|
||||
@@ -1467,6 +1467,35 @@ fn password_prompt_opens_and_security_tab_reports_the_details() {
|
||||
h.get_by_label("User password");
|
||||
}
|
||||
|
||||
/// #785: the notice's message used to be laid out before its buttons, so in a narrow document
|
||||
/// area (a side panel open) it ran under "Security settings" and on into the panel, and it sat
|
||||
/// off the buttons' centre line.
|
||||
#[test]
|
||||
fn the_security_notice_wraps_beside_its_buttons_and_lines_up_with_them() {
|
||||
// Wide: one line. Narrower: wrapped. Narrowest: cut short (the full text is on hover).
|
||||
for (width, wraps) in [(1700.0, false), (1250.0, true), (900.0, false)] {
|
||||
let mut h = Harness::builder().with_size(egui::vec2(width, 800.0)).build_eframe(|_cc| {
|
||||
let mut app = PdfCraftApp::new();
|
||||
app.set_option("language", "en").unwrap();
|
||||
app.open_bytes("locked.pdf", None, protected("", "owner", 0b0100)).unwrap();
|
||||
app.set_option("panel", "bookmarks").unwrap();
|
||||
app
|
||||
});
|
||||
h.run_steps(4);
|
||||
let msg = h.get_by_label_contains("This document is secured").rect();
|
||||
let button = h.get_by_label("Security settings").rect();
|
||||
let dismiss = h.get_by_label("Dismiss").rect();
|
||||
assert!(msg.right() <= button.left() + 0.5, "{width}: the message stops before the buttons: {msg:?} vs {button:?}");
|
||||
assert!(button.right() <= dismiss.left() + 0.5, "{width}: {button:?} vs {dismiss:?}");
|
||||
assert!((button.center().y - dismiss.center().y).abs() <= 0.5, "{width}: the buttons share a centre line");
|
||||
assert!(msg.top() >= button.top(), "{width}: the message stays inside the bar: {msg:?} vs {button:?}");
|
||||
let lines = (msg.height() / 14.0).floor().max(1.0);
|
||||
let first_line = msg.top() + msg.height() / lines / 2.0;
|
||||
assert!((first_line - button.center().y).abs() <= 1.5, "{width}: first line centred on the buttons: {msg:?} vs {button:?}");
|
||||
assert_eq!(msg.height() > button.height(), wraps, "{width}: {msg:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restricted_documents_show_a_notice_and_block_page_changes() {
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| {
|
||||
|
||||
Reference in new issue
Block a user