Skip to content

Commit

Permalink
feat(core) window events, closes #1523 (#1726)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed May 6, 2021
1 parent e087f0f commit 9c10ccf
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changes/emit-window-events.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Emit `tauri://resize`, `tauri://move`, `tauri://close-requested`, `tauri://destroyed`, `tauri://focus`, `tauri://blur` and `tauri://scale-change` events to the window.
5 changes: 5 additions & 0 deletions .changes/window-events.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Adds `on_window_event` API to the `Window` struct.
9 changes: 6 additions & 3 deletions core/tauri/src/lib.rs
Expand Up @@ -63,9 +63,12 @@ pub use {
self::runtime::flavors::wry::Wry,
self::runtime::monitor::Monitor,
self::runtime::webview::{WebviewAttributes, WindowBuilder},
self::runtime::window::export::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
Window,
self::runtime::window::{
export::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
Window,
},
WindowEvent,
},
self::state::{State, StateManager},
};
Expand Down
92 changes: 77 additions & 15 deletions core/tauri/src/runtime/flavors/wry.rs
Expand Up @@ -13,22 +13,23 @@ use crate::{
},
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, PendingWindow,
DetachedWindow, PendingWindow, WindowEvent,
},
Dispatch, Monitor, Params, Runtime,
},
Icon,
};

use image::{GenericImageView, Pixel};
use uuid::Uuid;
use wry::{
application::{
dpi::{
LogicalPosition as WryLogicalPosition, LogicalSize as WryLogicalSize,
PhysicalPosition as WryPhysicalPosition, PhysicalSize as WryPhysicalSize,
Position as WryPosition, Size as WrySize,
},
event::{Event, WindowEvent},
event::{Event, WindowEvent as WryWindowEvent},
event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget},
monitor::MonitorHandle,
window::{Fullscreen, Icon as WindowIcon, Window, WindowBuilder as WryWindowBuilder, WindowId},
Expand All @@ -51,6 +52,8 @@ use std::{
type CreateWebviewHandler =
Box<dyn FnOnce(&EventLoopWindowTarget<Message>) -> crate::Result<WebView> + Send>;
type MainThreadTask = Box<dyn FnOnce() + Send>;
type WindowEventHandler = Box<dyn Fn(&WindowEvent) + Send>;
type WindowEventListeners = Arc<Mutex<HashMap<Uuid, WindowEventHandler>>>;

#[repr(C)]
#[derive(Debug)]
Expand Down Expand Up @@ -86,6 +89,29 @@ impl TryFrom<Icon> for WryIcon {
}
}

struct WindowEventWrapper(Option<WindowEvent>);

impl<'a> From<&WryWindowEvent<'a>> for WindowEventWrapper {
fn from(event: &WryWindowEvent<'a>) -> Self {
let event = match event {
WryWindowEvent::Resized(size) => WindowEvent::Resized((*size).into()),
WryWindowEvent::Moved(position) => WindowEvent::Moved((*position).into()),
WryWindowEvent::CloseRequested => WindowEvent::CloseRequested,
WryWindowEvent::Destroyed => WindowEvent::Destroyed,
WryWindowEvent::Focused(focused) => WindowEvent::Focused(*focused),
WryWindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size,
} => WindowEvent::ScaleFactorChanged {
scale_factor: *scale_factor,
new_inner_size: (**new_inner_size).into(),
},
_ => return Self(None),
};
Self(Some(event))
}
}

impl From<MonitorHandle> for Monitor {
fn from(monitor: MonitorHandle) -> Monitor {
Self {
Expand Down Expand Up @@ -332,6 +358,7 @@ pub struct WryDispatcher {
window_id: WindowId,
proxy: EventLoopProxy<Message>,
task_tx: Sender<MainThreadTask>,
window_event_listeners: WindowEventListeners,
}

macro_rules! dispatcher_getter {
Expand Down Expand Up @@ -375,6 +402,16 @@ impl Dispatch for WryDispatcher {
.map_err(|_| crate::Error::FailedToSendMessage)
}

fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> Uuid {
let id = Uuid::new_v4();
self
.window_event_listeners
.lock()
.unwrap()
.insert(id, Box::new(f));
id
}

// GETTERS

fn scale_factor(&self) -> crate::Result<f64> {
Expand Down Expand Up @@ -430,11 +467,12 @@ impl Dispatch for WryDispatcher {
let label = pending.label.clone();
let proxy = self.proxy.clone();
let task_tx = self.task_tx.clone();
let window_event_listeners = self.window_event_listeners.clone();
self
.proxy
.send_event(Message::CreateWebview(
Arc::new(Mutex::new(Some(Box::new(move |event_loop| {
create_webview(event_loop, proxy, task_tx, pending)
create_webview(event_loop, proxy, task_tx, window_event_listeners, pending)
})))),
tx,
))
Expand All @@ -444,6 +482,7 @@ impl Dispatch for WryDispatcher {
window_id,
proxy: self.proxy.clone(),
task_tx: self.task_tx.clone(),
window_event_listeners: self.window_event_listeners.clone(),
};
Ok(DetachedWindow { label, dispatcher })
}
Expand Down Expand Up @@ -620,6 +659,7 @@ pub struct Wry {
event_loop: EventLoop<Message>,
webviews: HashMap<WindowId, WebView>,
task_tx: Sender<MainThreadTask>,
window_event_listeners: WindowEventListeners,
task_rx: Receiver<MainThreadTask>,
}

Expand All @@ -634,6 +674,7 @@ impl Runtime for Wry {
webviews: Default::default(),
task_tx,
task_rx,
window_event_listeners: Default::default(),
})
}

Expand All @@ -647,13 +688,15 @@ impl Runtime for Wry {
&self.event_loop,
proxy.clone(),
self.task_tx.clone(),
self.window_event_listeners.clone(),
pending,
)?;

let dispatcher = WryDispatcher {
window_id: webview.window().id(),
proxy,
task_tx: self.task_tx.clone(),
window_event_listeners: self.window_event_listeners.clone(),
};

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

Expand All @@ -678,20 +722,27 @@ impl Runtime for Wry {
}

match event {
Event::WindowEvent { event, window_id } => match event {
WindowEvent::CloseRequested => {
webviews.remove(&window_id);
if webviews.is_empty() {
*control_flow = ControlFlow::Exit;
Event::WindowEvent { event, window_id } => {
if let Some(event) = WindowEventWrapper::from(&event).0 {
for handler in window_event_listeners.lock().unwrap().values() {
handler(&event);
}
}
WindowEvent::Resized(_) => {
if let Err(e) = webviews[&window_id].resize() {
eprintln!("{}", e);
match event {
WryWindowEvent::CloseRequested => {
webviews.remove(&window_id);
if webviews.is_empty() {
*control_flow = ControlFlow::Exit;
}
}
WryWindowEvent::Resized(_) => {
if let Err(e) = webviews[&window_id].resize() {
eprintln!("{}", e);
}
}
_ => {}
}
_ => {}
},
}
Event::UserEvent(message) => match message {
Message::Window(id, window_message) => {
if let Some(webview) = webviews.get_mut(&id) {
Expand Down Expand Up @@ -797,6 +848,7 @@ fn create_webview<M: Params<Runtime = Wry>>(
event_loop: &EventLoopWindowTarget<Message>,
proxy: EventLoopProxy<Message>,
task_tx: Sender<MainThreadTask>,
window_event_listeners: WindowEventListeners,
pending: PendingWindow<M>,
) -> crate::Result<WebView> {
let PendingWindow {
Expand All @@ -818,13 +870,19 @@ fn create_webview<M: Params<Runtime = Wry>>(
webview_builder = webview_builder.with_rpc_handler(create_rpc_handler(
proxy.clone(),
task_tx.clone(),
window_event_listeners.clone(),
label.clone(),
handler,
));
}
if let Some(handler) = file_drop_handler {
webview_builder = webview_builder
.with_file_drop_handler(create_file_drop_handler(proxy, task_tx, label, handler));
webview_builder = webview_builder.with_file_drop_handler(create_file_drop_handler(
proxy,
task_tx,
window_event_listeners,
label,
handler,
));
}
for (scheme, protocol) in webview_attributes.uri_scheme_protocols {
webview_builder = webview_builder.with_custom_protocol(scheme, move |_window, url| {
Expand All @@ -847,6 +905,7 @@ fn create_webview<M: Params<Runtime = Wry>>(
fn create_rpc_handler<M: Params<Runtime = Wry>>(
proxy: EventLoopProxy<Message>,
task_tx: Sender<MainThreadTask>,
window_event_listeners: WindowEventListeners,
label: M::Label,
handler: WebviewRpcHandler<M>,
) -> Box<dyn Fn(&Window, WryRpcRequest) -> Option<RpcResponse> + 'static> {
Expand All @@ -857,6 +916,7 @@ fn create_rpc_handler<M: Params<Runtime = Wry>>(
window_id: window.id(),
proxy: proxy.clone(),
task_tx: task_tx.clone(),
window_event_listeners: window_event_listeners.clone(),
},
label: label.clone(),
},
Expand All @@ -870,6 +930,7 @@ fn create_rpc_handler<M: Params<Runtime = Wry>>(
fn create_file_drop_handler<M: Params<Runtime = Wry>>(
proxy: EventLoopProxy<Message>,
task_tx: Sender<MainThreadTask>,
window_event_listeners: WindowEventListeners,
label: M::Label,
handler: FileDropHandler<M>,
) -> Box<dyn Fn(&Window, WryFileDropEvent) -> bool + 'static> {
Expand All @@ -881,6 +942,7 @@ fn create_file_drop_handler<M: Params<Runtime = Wry>>(
window_id: window.id(),
proxy: proxy.clone(),
task_tx: task_tx.clone(),
window_event_listeners: window_event_listeners.clone(),
},
label: label.clone(),
},
Expand Down
77 changes: 76 additions & 1 deletion core/tauri/src/runtime/manager.rs
Expand Up @@ -18,7 +18,7 @@ use crate::{
CustomProtocol, FileDropEvent, FileDropHandler, InvokePayload, WebviewRpcHandler,
WindowBuilder,
},
window::{DetachedWindow, PendingWindow},
window::{dpi::PhysicalSize, DetachedWindow, PendingWindow, WindowEvent},
Icon, Runtime,
},
sealed::ParamsBase,
Expand All @@ -36,6 +36,14 @@ use std::{
};
use uuid::Uuid;

const WINDOW_RESIZED_EVENT: &str = "tauri://resize";
const WINDOW_MOVED_EVENT: &str = "tauri://move";
const WINDOW_CLOSE_REQUESTED_EVENT: &str = "tauri://close-requested";
const WINDOW_DESTROYED_EVENT: &str = "tauri://destroyed";
const WINDOW_FOCUS_EVENT: &str = "tauri://focus";
const WINDOW_BLUR_EVENT: &str = "tauri://blur";
const WINDOW_SCALE_FACTOR_CHANGED_EVENT: &str = "tauri://scale-change";

/// Parse a string representing an internal tauri event into [`Params::Event`]
///
/// # Panics
Expand Down Expand Up @@ -482,9 +490,15 @@ impl<P: Params> WindowManager<P> {

Ok(pending)
}

pub fn attach_window(&self, window: DetachedWindow<P>) -> Window<P> {
let window = Window::new(self.clone(), window);

let window_ = window.clone();
window.on_window_event(move |event| {
let _ = on_window_event(&window_, event);
});

// insert the window into our manager
{
self
Expand Down Expand Up @@ -611,3 +625,64 @@ impl<P: Params> WindowManager<P> {
self.windows_lock().clone()
}
}

fn on_window_event<P: Params>(window: &Window<P>, event: &WindowEvent) -> crate::Result<()> {
match event {
WindowEvent::Resized(size) => window.emit(
&WINDOW_RESIZED_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(size),
)?,
WindowEvent::Moved(position) => window.emit(
&WINDOW_MOVED_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(position),
)?,
WindowEvent::CloseRequested => window.emit(
&WINDOW_CLOSE_REQUESTED_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(()),
)?,
WindowEvent::Destroyed => window.emit(
&WINDOW_DESTROYED_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(()),
)?,
WindowEvent::Focused(focused) => window.emit(
&if *focused {
WINDOW_FOCUS_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event"))
} else {
WINDOW_BLUR_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event"))
},
Some(()),
)?,
WindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size,
} => window.emit(
&WINDOW_SCALE_FACTOR_CHANGED_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(ScaleFactorChanged {
scale_factor: *scale_factor,
size: new_inner_size.clone(),
}),
)?,
}
Ok(())
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct ScaleFactorChanged {
scale_factor: f64,
size: PhysicalSize<u32>,
}

0 comments on commit 9c10ccf

Please sign in to comment.