mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-10 21:11:52 +00:00
Wrapping the title (#161) kept the prompt narrow, but a long enough name still grew it taller than the window and pushed Save, Cancel and Don't save off-screen. On the web a `?file=` URL names the document, so the name has no length limit. Past 80 characters the name now gives way in the middle ("start…end"), keeping the start and the extension and version suffix; the full name shows on hover. The new test checks the title and buttons stay inside the desktop's minimum window (820×520) for long Latin, Thai and CJK names and a 2000-character one. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
2 files changed
+61
-2
No files matched your search
@@ -1344,18 +1344,24 @@ fn save_prompt(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
||||
return;
|
||||
};
|
||||
let t = Tokens::get(ctx);
|
||||
// Wrapping alone (#161) still let a long enough name (a web `?file=` URL has no limit) grow
|
||||
// the prompt taller than the window (#236); 80 characters wrap to a few lines.
|
||||
let shown = shorten_middle(&name, 80);
|
||||
let mut choice: Option<Option<bool>> = None;
|
||||
let modal = egui::Modal::new(egui::Id::new("save_prompt")).show(ctx, |ui| {
|
||||
ui.set_width(420.0);
|
||||
ui.horizontal(|ui| {
|
||||
ui.add(crate::icons::image("save", 22.0, t.accent));
|
||||
ui.add(
|
||||
let title = ui.add(
|
||||
egui::Label::new(
|
||||
egui::RichText::new(crate::i18n::fmt(tl!("Save changes to “{name}” before closing?"), &[("name", &name)]))
|
||||
egui::RichText::new(crate::i18n::fmt(tl!("Save changes to “{name}” before closing?"), &[("name", &shown)]))
|
||||
.font(theme::semibold(16.0)),
|
||||
)
|
||||
.wrap(),
|
||||
);
|
||||
if shown != name {
|
||||
title.on_hover_text(&name);
|
||||
}
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
ui.label(egui::RichText::new(tl!("Your changes will be lost if you don't save them.")).color(t.text_muted));
|
||||
@@ -1381,6 +1387,19 @@ fn save_prompt(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/// `name` cut to at most `max` characters by replacing its middle with "…", keeping the start and
|
||||
/// the end, where the extension and version suffixes sit. Counts `char`s, so it never splits one.
|
||||
fn shorten_middle(name: &str, max: usize) -> String {
|
||||
let count = name.chars().count();
|
||||
if count <= max {
|
||||
return name.to_string();
|
||||
}
|
||||
let tail = max / 4;
|
||||
let head: String = name.chars().take(max.saturating_sub(tail + 1)).collect();
|
||||
let end: String = name.chars().skip(count.saturating_sub(tail)).collect();
|
||||
format!("{head}…{end}")
|
||||
}
|
||||
|
||||
/// "Open this web page?" when a document's link, button or script asks to open an address
|
||||
/// (#90, #91). Shows where the address really goes and the whole address; Cancel is the default,
|
||||
/// and Escape or clicking outside cancels.
|
||||
|
||||
@@ -411,6 +411,46 @@ fn save_prompt_stays_inside_the_screen_for_a_long_filename() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_prompt_fits_the_smallest_window_whatever_the_name() {
|
||||
// Issue #236: wrapping (#161) kept the prompt narrow, but a long enough name still grew it
|
||||
// taller than the window and pushed the buttons off-screen. On the web a `?file=` URL names
|
||||
// the document, so the name has no length limit. Long names now give way in the middle.
|
||||
let names = [
|
||||
format!("{}.pdf", "a".repeat(400)),
|
||||
format!("{}.pdf", "Quarterly_Report_FY2026_Final_v3_".repeat(12)),
|
||||
format!("{}.pdf", "รายงานประจำปีงบประมาณ".repeat(15)),
|
||||
format!("{}.pdf", "年".repeat(251)),
|
||||
format!("{}.pdf", "W".repeat(2000)),
|
||||
];
|
||||
// The desktop window's minimum inner size (apps/pdfcraft/src/main.rs).
|
||||
let size = egui::vec2(820.0, 520.0);
|
||||
for name in names {
|
||||
let start: String = name.chars().take(10).collect();
|
||||
let mut h = Harness::builder().with_size(size).build_eframe(move |_cc| {
|
||||
let mut app = PdfCraftApp::new();
|
||||
app.open_bytes(&name, None, fixture(1)).expect("fixture opens");
|
||||
app.close_request = Some(CloseRequest::Tab(app.views[0].id));
|
||||
app
|
||||
});
|
||||
h.run_steps(4);
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, size);
|
||||
let inside = |r: egui::Rect| screen.contains(r.min) && screen.contains(r.max);
|
||||
let title = h.get_by_label_contains("Save changes to");
|
||||
let title_rect = title.rect();
|
||||
assert!(inside(title_rect), "{start}…: the title rect {title_rect:?} leaves the screen");
|
||||
let opening = format!("Save changes to “{start}");
|
||||
assert!(
|
||||
h.query_by(|n| n.value().is_some_and(|l| l.starts_with(&opening) && l.contains('…') && l.contains(".pdf” before closing?"))).is_some(),
|
||||
"{start}…: the title keeps the start and the extension"
|
||||
);
|
||||
for button in ["Save", "Cancel", "Don't save"] {
|
||||
let rect = h.get_by_label(button).rect();
|
||||
assert!(inside(rect), "{start}…: the {button} button rect {rect:?} leaves the screen");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn document_properties_edit_is_one_undoable_step() {
|
||||
let mut h = harness(1, |app| app.set_option("dialog", "properties").unwrap());
|
||||
|
||||
Reference in new issue
Block a user