Skip to content

Commit 9c10ccf

Browse files
authored
feat(core) window events, closes #1523 (#1726)
1 parent e087f0f commit 9c10ccf

File tree

7 files changed

+214
-20
lines changed

7 files changed

+214
-20
lines changed

.changes/emit-window-events.md

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+
Emit `tauri://resize`, `tauri://move`, `tauri://close-requested`, `tauri://destroyed`, `tauri://focus`, `tauri://blur` and `tauri://scale-change` events to the window.

.changes/window-events.md

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+
Adds `on_window_event` API to the `Window` struct.

core/tauri/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,12 @@ pub use {
6363
self::runtime::flavors::wry::Wry,
6464
self::runtime::monitor::Monitor,
6565
self::runtime::webview::{WebviewAttributes, WindowBuilder},
66-
self::runtime::window::export::{
67-
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
68-
Window,
66+
self::runtime::window::{
67+
export::{
68+
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
69+
Window,
70+
},
71+
WindowEvent,
6972
},
7073
self::state::{State, StateManager},
7174
};

core/tauri/src/runtime/flavors/wry.rs

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,23 @@ use crate::{
1313
},
1414
window::{
1515
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
16-
DetachedWindow, PendingWindow,
16+
DetachedWindow, PendingWindow, WindowEvent,
1717
},
1818
Dispatch, Monitor, Params, Runtime,
1919
},
2020
Icon,
2121
};
2222

2323
use image::{GenericImageView, Pixel};
24+
use uuid::Uuid;
2425
use wry::{
2526
application::{
2627
dpi::{
2728
LogicalPosition as WryLogicalPosition, LogicalSize as WryLogicalSize,
2829
PhysicalPosition as WryPhysicalPosition, PhysicalSize as WryPhysicalSize,
2930
Position as WryPosition, Size as WrySize,
3031
},
31-
event::{Event, WindowEvent},
32+
event::{Event, WindowEvent as WryWindowEvent},
3233
event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget},
3334
monitor::MonitorHandle,
3435
window::{Fullscreen, Icon as WindowIcon, Window, WindowBuilder as WryWindowBuilder, WindowId},
@@ -51,6 +52,8 @@ use std::{
5152
type CreateWebviewHandler =
5253
Box<dyn FnOnce(&EventLoopWindowTarget<Message>) -> crate::Result<WebView> + Send>;
5354
type MainThreadTask = Box<dyn FnOnce() + Send>;
55+
type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
56+
type WindowEventListeners = Arc<Mutex<HashMap<Uuid, WindowEventHandler>>>;
5457

5558
#[repr(C)]
5659
#[derive(Debug)]
@@ -86,6 +89,29 @@ impl TryFrom<Icon> for WryIcon {
8689
}
8790
}
8891

92+
struct WindowEventWrapper(Option<WindowEvent>);
93+
94+
impl<'a> From<&WryWindowEvent<'a>> for WindowEventWrapper {
95+
fn from(event: &WryWindowEvent<'a>) -> Self {
96+
let event = match event {
97+
WryWindowEvent::Resized(size) => WindowEvent::Resized((*size).into()),
98+
WryWindowEvent::Moved(position) => WindowEvent::Moved((*position).into()),
99+
WryWindowEvent::CloseRequested => WindowEvent::CloseRequested,
100+
WryWindowEvent::Destroyed => WindowEvent::Destroyed,
101+
WryWindowEvent::Focused(focused) => WindowEvent::Focused(*focused),
102+
WryWindowEvent::ScaleFactorChanged {
103+
scale_factor,
104+
new_inner_size,
105+
} => WindowEvent::ScaleFactorChanged {
106+
scale_factor: *scale_factor,
107+
new_inner_size: (**new_inner_size).into(),
108+
},
109+
_ => return Self(None),
110+
};
111+
Self(Some(event))
112+
}
113+
}
114+
89115
impl From<MonitorHandle> for Monitor {
90116
fn from(monitor: MonitorHandle) -> Monitor {
91117
Self {
@@ -332,6 +358,7 @@ pub struct WryDispatcher {
332358
window_id: WindowId,
333359
proxy: EventLoopProxy<Message>,
334360
task_tx: Sender<MainThreadTask>,
361+
window_event_listeners: WindowEventListeners,
335362
}
336363

337364
macro_rules! dispatcher_getter {
@@ -375,6 +402,16 @@ impl Dispatch for WryDispatcher {
375402
.map_err(|_| crate::Error::FailedToSendMessage)
376403
}
377404

405+
fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> Uuid {
406+
let id = Uuid::new_v4();
407+
self
408+
.window_event_listeners
409+
.lock()
410+
.unwrap()
411+
.insert(id, Box::new(f));
412+
id
413+
}
414+
378415
// GETTERS
379416

380417
fn scale_factor(&self) -> crate::Result<f64> {
@@ -430,11 +467,12 @@ impl Dispatch for WryDispatcher {
430467
let label = pending.label.clone();
431468
let proxy = self.proxy.clone();
432469
let task_tx = self.task_tx.clone();
470+
let window_event_listeners = self.window_event_listeners.clone();
433471
self
434472
.proxy
435473
.send_event(Message::CreateWebview(
436474
Arc::new(Mutex::new(Some(Box::new(move |event_loop| {
437-
create_webview(event_loop, proxy, task_tx, pending)
475+
create_webview(event_loop, proxy, task_tx, window_event_listeners, pending)
438476
})))),
439477
tx,
440478
))
@@ -444,6 +482,7 @@ impl Dispatch for WryDispatcher {
444482
window_id,
445483
proxy: self.proxy.clone(),
446484
task_tx: self.task_tx.clone(),
485+
window_event_listeners: self.window_event_listeners.clone(),
447486
};
448487
Ok(DetachedWindow { label, dispatcher })
449488
}
@@ -620,6 +659,7 @@ pub struct Wry {
620659
event_loop: EventLoop<Message>,
621660
webviews: HashMap<WindowId, WebView>,
622661
task_tx: Sender<MainThreadTask>,
662+
window_event_listeners: WindowEventListeners,
623663
task_rx: Receiver<MainThreadTask>,
624664
}
625665

@@ -634,6 +674,7 @@ impl Runtime for Wry {
634674
webviews: Default::default(),
635675
task_tx,
636676
task_rx,
677+
window_event_listeners: Default::default(),
637678
})
638679
}
639680

@@ -647,13 +688,15 @@ impl Runtime for Wry {
647688
&self.event_loop,
648689
proxy.clone(),
649690
self.task_tx.clone(),
691+
self.window_event_listeners.clone(),
650692
pending,
651693
)?;
652694

653695
let dispatcher = WryDispatcher {
654696
window_id: webview.window().id(),
655697
proxy,
656698
task_tx: self.task_tx.clone(),
699+
window_event_listeners: self.window_event_listeners.clone(),
657700
};
658701

659702
self.webviews.insert(webview.window().id(), webview);
@@ -664,6 +707,7 @@ impl Runtime for Wry {
664707
fn run(self) {
665708
let mut webviews = self.webviews;
666709
let task_rx = self.task_rx;
710+
let window_event_listeners = self.window_event_listeners.clone();
667711
self.event_loop.run(move |event, event_loop, control_flow| {
668712
*control_flow = ControlFlow::Wait;
669713

@@ -678,20 +722,27 @@ impl Runtime for Wry {
678722
}
679723

680724
match event {
681-
Event::WindowEvent { event, window_id } => match event {
682-
WindowEvent::CloseRequested => {
683-
webviews.remove(&window_id);
684-
if webviews.is_empty() {
685-
*control_flow = ControlFlow::Exit;
725+
Event::WindowEvent { event, window_id } => {
726+
if let Some(event) = WindowEventWrapper::from(&event).0 {
727+
for handler in window_event_listeners.lock().unwrap().values() {
728+
handler(&event);
686729
}
687730
}
688-
WindowEvent::Resized(_) => {
689-
if let Err(e) = webviews[&window_id].resize() {
690-
eprintln!("{}", e);
731+
match event {
732+
WryWindowEvent::CloseRequested => {
733+
webviews.remove(&window_id);
734+
if webviews.is_empty() {
735+
*control_flow = ControlFlow::Exit;
736+
}
691737
}
738+
WryWindowEvent::Resized(_) => {
739+
if let Err(e) = webviews[&window_id].resize() {
740+
eprintln!("{}", e);
741+
}
742+
}
743+
_ => {}
692744
}
693-
_ => {}
694-
},
745+
}
695746
Event::UserEvent(message) => match message {
696747
Message::Window(id, window_message) => {
697748
if let Some(webview) = webviews.get_mut(&id) {
@@ -797,6 +848,7 @@ fn create_webview<M: Params<Runtime = Wry>>(
797848
event_loop: &EventLoopWindowTarget<Message>,
798849
proxy: EventLoopProxy<Message>,
799850
task_tx: Sender<MainThreadTask>,
851+
window_event_listeners: WindowEventListeners,
800852
pending: PendingWindow<M>,
801853
) -> crate::Result<WebView> {
802854
let PendingWindow {
@@ -818,13 +870,19 @@ fn create_webview<M: Params<Runtime = Wry>>(
818870
webview_builder = webview_builder.with_rpc_handler(create_rpc_handler(
819871
proxy.clone(),
820872
task_tx.clone(),
873+
window_event_listeners.clone(),
821874
label.clone(),
822875
handler,
823876
));
824877
}
825878
if let Some(handler) = file_drop_handler {
826-
webview_builder = webview_builder
827-
.with_file_drop_handler(create_file_drop_handler(proxy, task_tx, label, handler));
879+
webview_builder = webview_builder.with_file_drop_handler(create_file_drop_handler(
880+
proxy,
881+
task_tx,
882+
window_event_listeners,
883+
label,
884+
handler,
885+
));
828886
}
829887
for (scheme, protocol) in webview_attributes.uri_scheme_protocols {
830888
webview_builder = webview_builder.with_custom_protocol(scheme, move |_window, url| {
@@ -847,6 +905,7 @@ fn create_webview<M: Params<Runtime = Wry>>(
847905
fn create_rpc_handler<M: Params<Runtime = Wry>>(
848906
proxy: EventLoopProxy<Message>,
849907
task_tx: Sender<MainThreadTask>,
908+
window_event_listeners: WindowEventListeners,
850909
label: M::Label,
851910
handler: WebviewRpcHandler<M>,
852911
) -> Box<dyn Fn(&Window, WryRpcRequest) -> Option<RpcResponse> + 'static> {
@@ -857,6 +916,7 @@ fn create_rpc_handler<M: Params<Runtime = Wry>>(
857916
window_id: window.id(),
858917
proxy: proxy.clone(),
859918
task_tx: task_tx.clone(),
919+
window_event_listeners: window_event_listeners.clone(),
860920
},
861921
label: label.clone(),
862922
},
@@ -870,6 +930,7 @@ fn create_rpc_handler<M: Params<Runtime = Wry>>(
870930
fn create_file_drop_handler<M: Params<Runtime = Wry>>(
871931
proxy: EventLoopProxy<Message>,
872932
task_tx: Sender<MainThreadTask>,
933+
window_event_listeners: WindowEventListeners,
873934
label: M::Label,
874935
handler: FileDropHandler<M>,
875936
) -> Box<dyn Fn(&Window, WryFileDropEvent) -> bool + 'static> {
@@ -881,6 +942,7 @@ fn create_file_drop_handler<M: Params<Runtime = Wry>>(
881942
window_id: window.id(),
882943
proxy: proxy.clone(),
883944
task_tx: task_tx.clone(),
945+
window_event_listeners: window_event_listeners.clone(),
884946
},
885947
label: label.clone(),
886948
},

core/tauri/src/runtime/manager.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::{
1818
CustomProtocol, FileDropEvent, FileDropHandler, InvokePayload, WebviewRpcHandler,
1919
WindowBuilder,
2020
},
21-
window::{DetachedWindow, PendingWindow},
21+
window::{dpi::PhysicalSize, DetachedWindow, PendingWindow, WindowEvent},
2222
Icon, Runtime,
2323
},
2424
sealed::ParamsBase,
@@ -36,6 +36,14 @@ use std::{
3636
};
3737
use uuid::Uuid;
3838

39+
const WINDOW_RESIZED_EVENT: &str = "tauri://resize";
40+
const WINDOW_MOVED_EVENT: &str = "tauri://move";
41+
const WINDOW_CLOSE_REQUESTED_EVENT: &str = "tauri://close-requested";
42+
const WINDOW_DESTROYED_EVENT: &str = "tauri://destroyed";
43+
const WINDOW_FOCUS_EVENT: &str = "tauri://focus";
44+
const WINDOW_BLUR_EVENT: &str = "tauri://blur";
45+
const WINDOW_SCALE_FACTOR_CHANGED_EVENT: &str = "tauri://scale-change";
46+
3947
/// Parse a string representing an internal tauri event into [`Params::Event`]
4048
///
4149
/// # Panics
@@ -482,9 +490,15 @@ impl<P: Params> WindowManager<P> {
482490

483491
Ok(pending)
484492
}
493+
485494
pub fn attach_window(&self, window: DetachedWindow<P>) -> Window<P> {
486495
let window = Window::new(self.clone(), window);
487496

497+
let window_ = window.clone();
498+
window.on_window_event(move |event| {
499+
let _ = on_window_event(&window_, event);
500+
});
501+
488502
// insert the window into our manager
489503
{
490504
self
@@ -611,3 +625,64 @@ impl<P: Params> WindowManager<P> {
611625
self.windows_lock().clone()
612626
}
613627
}
628+
629+
fn on_window_event<P: Params>(window: &Window<P>, event: &WindowEvent) -> crate::Result<()> {
630+
match event {
631+
WindowEvent::Resized(size) => window.emit(
632+
&WINDOW_RESIZED_EVENT
633+
.parse()
634+
.unwrap_or_else(|_| panic!("unhandled event")),
635+
Some(size),
636+
)?,
637+
WindowEvent::Moved(position) => window.emit(
638+
&WINDOW_MOVED_EVENT
639+
.parse()
640+
.unwrap_or_else(|_| panic!("unhandled event")),
641+
Some(position),
642+
)?,
643+
WindowEvent::CloseRequested => window.emit(
644+
&WINDOW_CLOSE_REQUESTED_EVENT
645+
.parse()
646+
.unwrap_or_else(|_| panic!("unhandled event")),
647+
Some(()),
648+
)?,
649+
WindowEvent::Destroyed => window.emit(
650+
&WINDOW_DESTROYED_EVENT
651+
.parse()
652+
.unwrap_or_else(|_| panic!("unhandled event")),
653+
Some(()),
654+
)?,
655+
WindowEvent::Focused(focused) => window.emit(
656+
&if *focused {
657+
WINDOW_FOCUS_EVENT
658+
.parse()
659+
.unwrap_or_else(|_| panic!("unhandled event"))
660+
} else {
661+
WINDOW_BLUR_EVENT
662+
.parse()
663+
.unwrap_or_else(|_| panic!("unhandled event"))
664+
},
665+
Some(()),
666+
)?,
667+
WindowEvent::ScaleFactorChanged {
668+
scale_factor,
669+
new_inner_size,
670+
} => window.emit(
671+
&WINDOW_SCALE_FACTOR_CHANGED_EVENT
672+
.parse()
673+
.unwrap_or_else(|_| panic!("unhandled event")),
674+
Some(ScaleFactorChanged {
675+
scale_factor: *scale_factor,
676+
size: new_inner_size.clone(),
677+
}),
678+
)?,
679+
}
680+
Ok(())
681+
}
682+
683+
#[derive(Serialize)]
684+
#[serde(rename_all = "camelCase")]
685+
struct ScaleFactorChanged {
686+
scale_factor: f64,
687+
size: PhysicalSize<u32>,
688+
}

0 commit comments

Comments
 (0)