M3: Tabs and the window title show the document title when Initial View asks for it (DisplayDocTitle); parity for setting the document language

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Brandon Thomas 2026-10-02 04:34:26 -04:00
parent d26821fdcd
commit 6a9dd32ef9
7 changed files with 65 additions and 12 deletions

View File

@ -267,6 +267,18 @@ impl Document {
self.editor.as_ref().and_then(|e| printcraft_organize::info(&e.cos, key))
}
/// The name shown on the tab and window: the document title when the document asks for it
/// (Initial View ▸ Show: Document Title) and has one, else the file name.
pub fn display_name(&self) -> String {
self.editor
.as_ref()
.filter(|e| printcraft_organize::displays_doc_title(&e.cos))
.and_then(|e| printcraft_organize::info(&e.cos, "Title"))
.map(|t| t.trim().to_owned())
.filter(|t| !t.is_empty())
.unwrap_or_else(|| self.name.clone())
}
/// Notes about damage repaired while opening (Document Properties ▸ Advanced, notices).
pub fn repair_log(&self) -> Vec<String> {
self.editor.as_ref().map(|e| e.cos.repair_log().to_vec()).unwrap_or_default()

View File

@ -25,7 +25,7 @@ pub use labels::{LabelRange, LabelStyle, number_pages, page_label_ranges, page_l
pub use outline::{
Bookmark, OutlineError, add_bookmark, bookmarks, delete_bookmark, move_bookmark, rename_bookmark, set_bookmark_open, set_bookmark_page,
};
pub use view::{InitialView, initial_view, set_initial_view};
pub use view::{InitialView, displays_doc_title, initial_view, set_initial_view};
#[derive(Debug, thiserror::Error, PartialEq)]
pub enum OrganizeError {

View File

@ -138,6 +138,16 @@ pub fn initial_view(doc: &Document) -> InitialView {
v
}
/// Whether viewers should show the document title rather than the file name
/// (`/ViewerPreferences /DisplayDocTitle`), without reading the rest of the initial view.
pub fn displays_doc_title(doc: &Document) -> bool {
let Ok((_, c)) = catalog(doc) else { return false };
c.get(b"ViewerPreferences")
.map(|o| doc.resolve(o))
.and_then(|o| o.as_dict().map(|vp| matches!(vp.get(b"DisplayDocTitle").map(|x| doc.resolve(x)).as_deref(), Some(Object::Bool(true)))))
.unwrap_or(false)
}
/// Write the initial view. An opening action that isn't a plain destination (a script, say) is
/// kept unless the page or magnification asks for a destination.
pub fn set_initial_view(doc: &mut Document, v: &InitialView) -> Result<(), OrganizeError> {

View File

@ -30,7 +30,7 @@ pub fn tab_strip(app: &mut PrintCraftApp, ui: &mut egui::Ui) {
let mut close = None;
for i in 0..app.views.len() {
let Some(doc) = app.session.get(app.views[i].id) else { continue };
let (name, dirty) = (doc.name.clone(), doc.dirty);
let (name, dirty) = (doc.display_name(), doc.dirty);
if tab(ui, &t, &name, dirty, app.active == Some(i), &mut close, i).clicked() {
app.active = Some(i);
}

View File

@ -294,6 +294,8 @@ pub struct PrintCraftApp {
pending_theme: Option<ThemeKind>,
styled: bool,
fonts_ready: bool,
/// The window title last sent to the platform.
pub window_title: String,
/// The UI control channel, when enabled (`--control`; off by default).
control: Option<control::Control>,
/// A bookmark being renamed in the Bookmarks panel: (path, text so far).
@ -391,6 +393,7 @@ impl PrintCraftApp {
attach_override: None,
duplicate_draft: None,
form_preview: false,
window_title: String::new(),
recovery: None,
recoverable: Vec::new(),
recovery_keys: Default::default(),
@ -976,6 +979,15 @@ impl eframe::App for PrintCraftApp {
ctx.request_repaint();
return;
}
// The window shows the active document's name (or title, if it asks for that).
let title = self
.active
.and_then(|i| self.session.get(self.views[i].id))
.map_or_else(|| "PrintCraft".to_owned(), |d| format!("{} — PrintCraft", d.display_name()));
if title != self.window_title {
ctx.send_viewport_cmd(egui::ViewportCommand::Title(title.clone()));
self.window_title = title;
}
if self.full_screen && self.active.is_some() {
// Full screen: the page, nothing else (Esc or ⌘L to leave).
let t = theme::Tokens::get(&ctx);

View File

@ -279,3 +279,22 @@ fn fit_visible_zooms_to_the_content_width() {
let vp = v.viewport_rect();
assert!((ink_left - vp.left() - 16.0).abs() < 12.0, "ink at {ink_left}, viewport {vp:?}");
}
#[test]
fn tab_and_window_show_the_document_title_when_asked() {
use printcraft_engine::Edit;
let mut h = harness();
h.run_steps(2);
assert_eq!(h.state().window_title, "b.pdf — PrintCraft");
{
let s = h.state_mut();
let id = s.views[s.active.unwrap()].id;
s.session.apply(id, Edit::SetInfo { key: "Title".into(), value: "Quarterly report".into() }).unwrap();
let mut v = s.session.get(id).unwrap().initial_view();
v.display_title = true;
s.session.apply(id, Edit::SetInitialView(Box::new(v))).unwrap();
}
h.run_steps(2);
assert_eq!(h.state().window_title, "Quarterly report — PrintCraft");
h.get_by_label_contains("Quarterly report");
}

View File

@ -8863,11 +8863,11 @@ title = "Set document language"
area = "M"
tier = "P1"
milestone = "M12"
status = "planned"
commands = []
tools = []
evidence = []
notes = ""
status = "shipped"
commands = ["file.properties"]
tools = ["doc_initial_view"]
evidence = ["crates/organize/src/tests.rs::initial_view_round_trips_and_keeps_scripts", "crates/automation/tests/automation.rs::initial_view_through_tools", "crates/ui-egui/tests/viewer.rs::initial_view_is_edited_and_honoured_on_open"]
notes = "Document Properties ▸ Initial View ▸ Language (catalog /Lang)"
[[feature]]
id = "a11y.set-title-display"
@ -8875,11 +8875,11 @@ title = "Show the document title in the window"
area = "M"
tier = "P1"
milestone = "M12"
status = "planned"
commands = []
tools = []
evidence = []
notes = ""
status = "shipped"
commands = ["file.properties"]
tools = ["doc_initial_view"]
evidence = ["crates/automation/tests/automation.rs::initial_view_through_tools", "crates/ui-egui/tests/viewer.rs::tab_and_window_show_the_document_title_when_asked"]
notes = "Initial View ▸ Show: Document Title (/DisplayDocTitle); the tab and window then show the title"
[[feature]]
id = "a11y.make-accessible-action"