mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-24 18:18:12 -06:00
Web: the page title names the open photo and unsaved changes (#2397)
eframe's web runner doesn't handle ViewportCommand::Title, so the web build never named what was open, and a host embedding the page could only guess whether a dropped file had opened. The page title is now "<file name> — LightCraft" for the active photo, with " •" after the name while changes are not yet written to browser storage, and the window title when no photo is active. It is set only when it changes.
This commit is contained in:
@@ -383,6 +383,8 @@ struct WebApp {
|
||||
lib_dir: String,
|
||||
bench: Option<Bench>,
|
||||
first_frame_logged: bool,
|
||||
/// The page title last set (`document.title`), so it is written only when it changes.
|
||||
title: String,
|
||||
last_save: f64,
|
||||
ui_written: Vec<u8>,
|
||||
}
|
||||
@@ -475,6 +477,7 @@ impl WebApp {
|
||||
lib_dir,
|
||||
bench: opts.bench.then(|| Bench::new(origin)),
|
||||
first_frame_logged: false,
|
||||
title: String::new(),
|
||||
last_save: 0.0,
|
||||
ui_written,
|
||||
}
|
||||
@@ -660,6 +663,14 @@ impl eframe::App for WebApp {
|
||||
|
||||
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
|
||||
self.app.ui(ui);
|
||||
// eframe's web runner ignores `ViewportCommand::Title`: name the open photo in the page title
|
||||
let title = self.app.page_title(self.files.is_dirty() || self.flushing.get());
|
||||
if title != self.title
|
||||
&& let Some(doc) = window().and_then(|w| w.document())
|
||||
{
|
||||
doc.set_title(&title);
|
||||
self.title = title;
|
||||
}
|
||||
if !self.first_frame_logged && !self.app.widgets.is_empty() {
|
||||
self.first_frame_logged = true;
|
||||
log::info!("lightcraft first UI frame at {:.0} ms after navigation start", perf_now());
|
||||
|
||||
@@ -1418,6 +1418,18 @@ impl LightcraftApp {
|
||||
}
|
||||
}
|
||||
|
||||
/// The browser page's title: "<file name>[ •] — LightCraft" for the active photo (• while
|
||||
/// `unsaved`: changes not yet written to storage), else the window title. The web build has no
|
||||
/// window for `ViewportCommand::Title`; a host embedding it reads this title to tell which
|
||||
/// document a drop opened and whether it is saved (issue #2397).
|
||||
pub fn page_title(&self, unsaved: bool) -> String {
|
||||
let dot = if unsaved { " •" } else { "" };
|
||||
match self.session.active().and_then(|id| self.session.catalog.photo(id)) {
|
||||
Some(p) => format!("{}{dot} — LightCraft", p.file_name),
|
||||
None => format!("{}{dot}", self.window_title()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Lay out the whole window.
|
||||
pub fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
i18n::set_language(self.ui.language);
|
||||
|
||||
@@ -220,3 +220,22 @@ fn library_names_must_work_as_folder_names_on_every_system() {
|
||||
assert_eq!(app.session.library.as_ref().unwrap().dir, dir.join("Trip 2026"));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// The web build's page title names the active photo (a host embedding the page reads it to know
|
||||
/// which document a drop opened), with • while changes are not yet in storage (issue #2397).
|
||||
#[test]
|
||||
fn page_title_names_the_active_photo_and_unsaved_changes() {
|
||||
let mut app = LightcraftApp::new(lightcraft_engine::Session::with_demo(), Services::default());
|
||||
let ids = app.session.visible_cloned();
|
||||
let name = |app: &LightcraftApp, id: lightcraft_catalog::PhotoId| app.session.catalog.photo(id).unwrap().file_name.clone();
|
||||
app.run("library.select", json!({"ids": [ids[0].0]})).unwrap();
|
||||
assert_eq!(app.page_title(false), format!("{} — LightCraft", name(&app, ids[0])));
|
||||
assert_eq!(app.page_title(true), format!("{} • — LightCraft", name(&app, ids[0])));
|
||||
app.run("library.select", json!({"ids": [ids[1].0]})).unwrap();
|
||||
assert_eq!(app.page_title(false), format!("{} — LightCraft", name(&app, ids[1])));
|
||||
// no photo: the window title
|
||||
let empty = LightcraftApp::new(lightcraft_engine::Session::new(), Services::default());
|
||||
assert!(empty.session.active().is_none());
|
||||
assert_eq!(empty.page_title(false), "LightCraft");
|
||||
assert_eq!(empty.page_title(true), "LightCraft •");
|
||||
}
|
||||
|
||||
@@ -152,6 +152,10 @@ are the same in every version, so serve them with `Cache-Control: no-cache` (not
|
||||
- **Automation.** `await lightcraft.command("library.info", "{}")` runs any engine or UI command
|
||||
by id on the next frame and resolves to the JSON result (`web.stats` reports storage, workers
|
||||
and the render queue). This is how the headless-Chrome checks drive the page.
|
||||
- **The page title names the open photo**: `<file name> — LightCraft`, with ` •` after the name while
|
||||
changes are not yet written to browser storage (`LightcraftApp::page_title`). eframe's web runner
|
||||
ignores the window title the desktop app sets, and a host that embeds the page reads this title to
|
||||
tell which photo a drop opened and whether it is saved (issue #2397).
|
||||
|
||||
## Not yet
|
||||
|
||||
|
||||
Reference in New Issue
Block a user