Skip to content

Commit 74dff53

Browse files
authored
fix(core): emit tauri://close-requested to JS, closes #2996 (#3041)
1 parent 1458ab3 commit 74dff53

12 files changed

Lines changed: 131 additions & 38 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch
3+
---
4+
5+
Prevent window closing if `tauri://close-requested` is listened on the JS layer. Users must call `appWindow.close()` manually when listening to that event.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch
3+
---
4+
5+
**Breaking change:** The `WindowEvent::CloseRequested` variant now includes `label` and `signal_tx` fields to allow preventing closing the window.

core/tauri-runtime-wry/src/lib.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pub use wry::application::platform::macos::{
8888
use std::{
8989
collections::{
9090
hash_map::Entry::{Occupied, Vacant},
91-
HashMap,
91+
HashMap, HashSet,
9292
},
9393
fmt,
9494
fs::read,
@@ -555,7 +555,6 @@ impl<'a> From<&WryWindowEvent<'a>> for WindowEventWrapper {
555555
WryWindowEvent::Moved(position) => {
556556
WindowEvent::Moved(PhysicalPositionWrapper(*position).into())
557557
}
558-
WryWindowEvent::CloseRequested => WindowEvent::CloseRequested,
559558
WryWindowEvent::Destroyed => WindowEvent::Destroyed,
560559
WryWindowEvent::ScaleFactorChanged {
561560
scale_factor,
@@ -1213,6 +1212,7 @@ impl Dispatch for WryDispatcher {
12131212
let (tx, rx) = channel();
12141213
let label = pending.label.clone();
12151214
let menu_ids = pending.menu_ids.clone();
1215+
let js_event_listeners = pending.js_event_listeners.clone();
12161216
let context = self.context.clone();
12171217

12181218
send_user_message(
@@ -1234,6 +1234,7 @@ impl Dispatch for WryDispatcher {
12341234
label,
12351235
dispatcher,
12361236
menu_ids,
1237+
js_event_listeners,
12371238
})
12381239
}
12391240

@@ -1521,6 +1522,7 @@ impl RuntimeHandle for WryHandle {
15211522
let (tx, rx) = channel();
15221523
let label = pending.label.clone();
15231524
let menu_ids = pending.menu_ids.clone();
1525+
let js_event_listeners = pending.js_event_listeners.clone();
15241526
let context = self.context.clone();
15251527
send_user_message(
15261528
&self.context,
@@ -1541,6 +1543,7 @@ impl RuntimeHandle for WryHandle {
15411543
label,
15421544
dispatcher,
15431545
menu_ids,
1546+
js_event_listeners,
15441547
})
15451548
}
15461549

@@ -1649,6 +1652,7 @@ impl Runtime for Wry {
16491652
fn create_window(&self, pending: PendingWindow<Self>) -> Result<DetachedWindow<Self>> {
16501653
let label = pending.label.clone();
16511654
let menu_ids = pending.menu_ids.clone();
1655+
let js_event_listeners = pending.js_event_listeners.clone();
16521656
let proxy = self.event_loop.create_proxy();
16531657
let webview = create_webview(
16541658
&self.event_loop,
@@ -1738,6 +1742,7 @@ impl Runtime for Wry {
17381742
label,
17391743
dispatcher,
17401744
menu_ids,
1745+
js_event_listeners,
17411746
})
17421747
}
17431748

@@ -2356,6 +2361,20 @@ fn handle_event_loop(
23562361
if let Some(w) = windows_guard.get(&window_id) {
23572362
let label = w.label.clone();
23582363
drop(windows_guard);
2364+
for handler in window_event_listeners
2365+
.lock()
2366+
.unwrap()
2367+
.get(&window_id)
2368+
.unwrap()
2369+
.lock()
2370+
.unwrap()
2371+
.values()
2372+
{
2373+
handler(&WindowEvent::CloseRequested {
2374+
label: label.clone(),
2375+
signal_tx: tx.clone(),
2376+
});
2377+
}
23592378
callback(RunEvent::CloseRequested {
23602379
label,
23612380
signal_tx: tx,
@@ -2534,6 +2553,7 @@ fn create_webview(
25342553
label,
25352554
url,
25362555
menu_ids,
2556+
js_event_listeners,
25372557
..
25382558
} = pending;
25392559

@@ -2573,6 +2593,7 @@ fn create_webview(
25732593
context.clone(),
25742594
label.clone(),
25752595
menu_ids.clone(),
2596+
js_event_listeners.clone(),
25762597
handler,
25772598
));
25782599
}
@@ -2581,6 +2602,7 @@ fn create_webview(
25812602
context,
25822603
label.clone(),
25832604
menu_ids,
2605+
js_event_listeners,
25842606
handler,
25852607
));
25862608
}
@@ -2637,6 +2659,7 @@ fn create_rpc_handler(
26372659
context: Context,
26382660
label: String,
26392661
menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
2662+
js_event_listeners: Arc<Mutex<HashMap<String, HashSet<u64>>>>,
26402663
handler: WebviewRpcHandler<Wry>,
26412664
) -> Box<dyn Fn(&Window, WryRpcRequest) -> Option<RpcResponse> + 'static> {
26422665
Box::new(move |window, request| {
@@ -2648,6 +2671,7 @@ fn create_rpc_handler(
26482671
},
26492672
label: label.clone(),
26502673
menu_ids: menu_ids.clone(),
2674+
js_event_listeners: js_event_listeners.clone(),
26512675
},
26522676
RpcRequestWrapper(request).into(),
26532677
);
@@ -2660,6 +2684,7 @@ fn create_file_drop_handler(
26602684
context: Context,
26612685
label: String,
26622686
menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
2687+
js_event_listeners: Arc<Mutex<HashMap<String, HashSet<u64>>>>,
26632688
handler: FileDropHandler<Wry>,
26642689
) -> Box<dyn Fn(&Window, WryFileDropEvent) -> bool + 'static> {
26652690
Box::new(move |window, event| {
@@ -2672,6 +2697,7 @@ fn create_file_drop_handler(
26722697
},
26732698
label: label.clone(),
26742699
menu_ids: menu_ids.clone(),
2700+
js_event_listeners: js_event_listeners.clone(),
26752701
},
26762702
)
26772703
})

core/tauri-runtime/src/window.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ use serde::Serialize;
1414
use tauri_utils::config::WindowConfig;
1515

1616
use std::{
17-
collections::HashMap,
17+
collections::{HashMap, HashSet},
1818
hash::{Hash, Hasher},
19-
sync::{Arc, Mutex},
19+
sync::{mpsc::Sender, Arc, Mutex},
2020
};
2121

2222
type UriSchemeProtocol =
@@ -34,7 +34,12 @@ pub enum WindowEvent {
3434
/// The position of the window has changed. Contains the window's new position.
3535
Moved(dpi::PhysicalPosition<i32>),
3636
/// The window has been requested to close.
37-
CloseRequested,
37+
CloseRequested {
38+
/// The window label.
39+
label: String,
40+
/// A signal sender. If a `true` value is emitted, the window won't be closed.
41+
signal_tx: Sender<bool>,
42+
},
3843
/// The window has been destroyed.
3944
Destroyed,
4045
/// The window gained or lost focus.
@@ -99,6 +104,9 @@ pub struct PendingWindow<R: Runtime> {
99104

100105
/// Maps runtime id to a string menu id.
101106
pub menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
107+
108+
/// A HashMap mapping JS event names with listener ids associated.
109+
pub js_event_listeners: Arc<Mutex<HashMap<String, HashSet<u64>>>>,
102110
}
103111

104112
impl<R: Runtime> PendingWindow<R> {
@@ -121,6 +129,7 @@ impl<R: Runtime> PendingWindow<R> {
121129
file_drop_handler: None,
122130
url: "tauri://localhost".to_string(),
123131
menu_ids: Arc::new(Mutex::new(menu_ids)),
132+
js_event_listeners: Default::default(),
124133
}
125134
}
126135

@@ -144,6 +153,7 @@ impl<R: Runtime> PendingWindow<R> {
144153
file_drop_handler: None,
145154
url: "tauri://localhost".to_string(),
146155
menu_ids: Arc::new(Mutex::new(menu_ids)),
156+
js_event_listeners: Default::default(),
147157
}
148158
}
149159

@@ -181,6 +191,9 @@ pub struct DetachedWindow<R: Runtime> {
181191

182192
/// Maps runtime id to a string menu id.
183193
pub menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
194+
195+
/// A HashMap mapping JS event names with listener ids associated.
196+
pub js_event_listeners: Arc<Mutex<HashMap<String, HashSet<u64>>>>,
184197
}
185198

186199
impl<R: Runtime> Clone for DetachedWindow<R> {
@@ -189,6 +202,7 @@ impl<R: Runtime> Clone for DetachedWindow<R> {
189202
label: self.label.clone(),
190203
dispatcher: self.dispatcher.clone(),
191204
menu_ids: self.menu_ids.clone(),
205+
js_event_listeners: self.js_event_listeners.clone(),
192206
}
193207
}
194208
}

core/tauri/src/endpoints/event.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ impl Cmd {
2929
match self {
3030
Self::Listen { event, handler } => {
3131
let event_id = rand::random();
32-
window.eval(&listen_js(&window, event, event_id, handler))?;
32+
window.eval(&listen_js(&window, event.clone(), event_id, handler))?;
33+
window.register_js_listener(event, event_id);
3334
Ok(event_id.into())
3435
}
3536
Self::Unlisten { event_id } => {
3637
window.eval(&unlisten_js(&window, event_id))?;
38+
window.unregister_js_listener(event_id);
3739
Ok(().into())
3840
}
3941
Self::Emit {

core/tauri/src/manager.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,13 @@ fn on_window_event<R: Runtime>(
815815
match event {
816816
WindowEvent::Resized(size) => window.emit_and_trigger(WINDOW_RESIZED_EVENT, size)?,
817817
WindowEvent::Moved(position) => window.emit_and_trigger(WINDOW_MOVED_EVENT, position)?,
818-
WindowEvent::CloseRequested => {
818+
WindowEvent::CloseRequested {
819+
label: _,
820+
signal_tx,
821+
} => {
822+
if window.has_js_listener(WINDOW_CLOSE_REQUESTED_EVENT) {
823+
signal_tx.send(true).unwrap();
824+
}
819825
window.emit_and_trigger(WINDOW_CLOSE_REQUESTED_EVENT, ())?;
820826
}
821827
WindowEvent::Destroyed => {

core/tauri/src/window.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,44 @@ impl<R: Runtime> Window<R> {
352352
})
353353
}
354354

355+
pub(crate) fn register_js_listener(&self, event: String, id: u64) {
356+
self
357+
.window
358+
.js_event_listeners
359+
.lock()
360+
.unwrap()
361+
.entry(event)
362+
.or_insert_with(Default::default)
363+
.insert(id);
364+
}
365+
366+
pub(crate) fn unregister_js_listener(&self, id: u64) {
367+
let mut empty = None;
368+
let mut js_listeners = self.window.js_event_listeners.lock().unwrap();
369+
for (event, ids) in js_listeners.iter_mut() {
370+
if ids.contains(&id) {
371+
ids.remove(&id);
372+
if ids.is_empty() {
373+
empty.replace(event.clone());
374+
}
375+
break;
376+
}
377+
}
378+
379+
if let Some(event) = empty {
380+
js_listeners.remove(&event);
381+
}
382+
}
383+
384+
pub(crate) fn has_js_listener(&self, event: &str) -> bool {
385+
self
386+
.window
387+
.js_event_listeners
388+
.lock()
389+
.unwrap()
390+
.contains_key(event)
391+
}
392+
355393
// Getters
356394

357395
/// Gets a handle to the window menu.

0 commit comments

Comments
 (0)