This commit is contained in:
parent
f6caba43b2
commit
f620750934
|
|
@ -16,5 +16,5 @@ egui = { workspace = true }
|
|||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-sys = { version = "0.3", features = ["Window", "Document", "Element", "HtmlCanvasElement", "HtmlElement", "Location", "Url", "UrlSearchParams", "Response"] }
|
||||
web-sys = { version = "0.3", features = ["Window", "Document", "Element", "HtmlCanvasElement", "HtmlElement", "Location", "Url", "UrlSearchParams", "Response", "Event"] }
|
||||
js-sys = "0.3"
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@ fn main() {
|
|||
ctx.request_repaint();
|
||||
});
|
||||
}
|
||||
// eframe never raises a close request on a browser reload or tab close, so `guard_quit` never
|
||||
// prompts; ask the browser to confirm instead while a document has unsaved work (#812).
|
||||
warn_before_unload(app.unsaved_flag.clone());
|
||||
Ok(Box::new(app))
|
||||
}),
|
||||
)
|
||||
|
|
@ -61,6 +64,20 @@ fn main() {
|
|||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn warn_before_unload(unsaved: std::sync::Arc<std::sync::atomic::AtomicBool>) {
|
||||
use eframe::wasm_bindgen::{JsCast, closure::Closure};
|
||||
let Some(window) = web_sys::window() else { return };
|
||||
let handler = Closure::<dyn FnMut(web_sys::Event)>::new(move |event: web_sys::Event| {
|
||||
if unsaved.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
event.prevent_default();
|
||||
}
|
||||
});
|
||||
if window.add_event_listener_with_callback("beforeunload", handler.as_ref().unchecked_ref()).is_ok() {
|
||||
handler.forget();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn query_param(key: &str) -> Option<String> {
|
||||
let search = web_sys::window()?.location().search().ok()?;
|
||||
|
|
|
|||
|
|
@ -590,8 +590,14 @@ impl PdfCraftApp {
|
|||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
|
||||
/// Publish whether any document has unsaved work, for the browser's reload warning (#812).
|
||||
pub(crate) fn sync_unsaved_flag(&self) {
|
||||
self.unsaved_flag.store(self.first_dirty().is_some(), std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Intercept window close while documents have unsaved changes.
|
||||
pub(crate) fn guard_quit(&mut self, ctx: &egui::Context) {
|
||||
self.sync_unsaved_flag();
|
||||
if !ctx.input(|i| i.viewport().close_requested()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -926,6 +932,19 @@ mod revert_draft_tests {
|
|||
assert!(app.views[index].forms.committed.is_some() && app.views[index].pending_edit.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsaved_flag_follows_dirty_state() {
|
||||
use std::sync::atomic::Ordering;
|
||||
// `guard_quit` runs every frame, also on the web, where no close request ever arrives.
|
||||
let ctx = egui::Context::default();
|
||||
let mut app = app();
|
||||
app.guard_quit(&ctx);
|
||||
assert!(!app.unsaved_flag.load(Ordering::Relaxed));
|
||||
assert!(app.apply_edit(Edit::SetFieldValue { name: "name".into(), value: FieldValue::Text("Changed".into()) }));
|
||||
app.guard_quit(&ctx);
|
||||
assert!(app.unsaved_flag.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_revert_clears_focused_or_queued_drafts_only_on_its_document() {
|
||||
for queued in [false, true] {
|
||||
|
|
|
|||
|
|
@ -424,6 +424,8 @@ pub struct PdfCraftApp {
|
|||
startup_superseded: bool,
|
||||
/// Asynchronous opens that failed (web `?file=` fetches), shown as a notice.
|
||||
pub failed_inbox: FailedInbox,
|
||||
/// Mirrors "some document has unsaved work" each frame, so the web page's `beforeunload` handler can read it (#812).
|
||||
pub unsaved_flag: std::sync::Arc<std::sync::atomic::AtomicBool>,
|
||||
/// Requests from the operating system, polled every frame (macOS Apple events).
|
||||
pub os_events: Option<OsEventsFn>,
|
||||
/// A pending "save changes?" question (closing a dirty tab or quitting).
|
||||
|
|
@ -689,6 +691,7 @@ impl PdfCraftApp {
|
|||
startup_inbox: Default::default(),
|
||||
startup_superseded: false,
|
||||
failed_inbox: Default::default(),
|
||||
unsaved_flag: Default::default(),
|
||||
os_events: None,
|
||||
close_request: None,
|
||||
save_override: None,
|
||||
|
|
|
|||
Loading…
Reference in New Issue