mirror of https://github.com/tauri-apps/tauri
parent
e087f0f937
commit
9c10ccf8d3
|
@ -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.
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"tauri": patch
|
||||
---
|
||||
|
||||
Adds `on_window_event` API to the `Window` struct.
|
|
@ -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},
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::{
|
|||
},
|
||||
window::{
|
||||
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
|
||||
DetachedWindow, PendingWindow,
|
||||
DetachedWindow, PendingWindow, WindowEvent,
|
||||
},
|
||||
Dispatch, Monitor, Params, Runtime,
|
||||
},
|
||||
|
@ -21,6 +21,7 @@ use crate::{
|
|||
};
|
||||
|
||||
use image::{GenericImageView, Pixel};
|
||||
use uuid::Uuid;
|
||||
use wry::{
|
||||
application::{
|
||||
dpi::{
|
||||
|
@ -28,7 +29,7 @@ use wry::{
|
|||
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},
|
||||
|
@ -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)]
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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> {
|
||||
|
@ -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,
|
||||
))
|
||||
|
@ -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 })
|
||||
}
|
||||
|
@ -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>,
|
||||
}
|
||||
|
||||
|
@ -634,6 +674,7 @@ impl Runtime for Wry {
|
|||
webviews: Default::default(),
|
||||
task_tx,
|
||||
task_rx,
|
||||
window_event_listeners: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -647,6 +688,7 @@ impl Runtime for Wry {
|
|||
&self.event_loop,
|
||||
proxy.clone(),
|
||||
self.task_tx.clone(),
|
||||
self.window_event_listeners.clone(),
|
||||
pending,
|
||||
)?;
|
||||
|
||||
|
@ -654,6 +696,7 @@ impl Runtime for Wry {
|
|||
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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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| {
|
||||
|
@ -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> {
|
||||
|
@ -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(),
|
||||
},
|
||||
|
@ -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> {
|
||||
|
@ -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(),
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>,
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::{
|
|||
runtime::window::{DetachedWindow, PendingWindow},
|
||||
Icon, Params, WindowBuilder,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub(crate) mod app;
|
||||
pub mod flavors;
|
||||
|
@ -19,7 +20,10 @@ pub mod webview;
|
|||
pub mod window;
|
||||
|
||||
use monitor::Monitor;
|
||||
use window::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use window::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
WindowEvent,
|
||||
};
|
||||
|
||||
/// The webview runtime interface.
|
||||
pub trait Runtime: Sized + 'static {
|
||||
|
@ -50,6 +54,9 @@ pub trait Dispatch: Clone + Send + Sized + 'static {
|
|||
/// Run a task on the main thread.
|
||||
fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()>;
|
||||
|
||||
/// Registers a window event handler.
|
||||
fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) -> Uuid;
|
||||
|
||||
// GETTERS
|
||||
|
||||
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
|
||||
|
|
|
@ -23,6 +23,38 @@ use std::hash::{Hash, Hasher};
|
|||
/// UI scaling utilities.
|
||||
pub mod dpi;
|
||||
|
||||
/// An event from a window.
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum WindowEvent {
|
||||
/// The size of the window has changed. Contains the client area's new dimensions.
|
||||
Resized(dpi::PhysicalSize<u32>),
|
||||
/// The position of the window has changed. Contains the window's new position.
|
||||
Moved(dpi::PhysicalPosition<i32>),
|
||||
/// The window has been requested to close.
|
||||
CloseRequested,
|
||||
/// The window has been destroyed.
|
||||
Destroyed,
|
||||
/// The window gained or lost focus.
|
||||
///
|
||||
/// The parameter is true if the window has gained focus, and false if it has lost focus.
|
||||
Focused(bool),
|
||||
///The window's scale factor has changed.
|
||||
///
|
||||
/// The following user actions can cause DPI changes:
|
||||
///
|
||||
/// - Changing the display's resolution.
|
||||
/// - Changing the display's scale factor (e.g. in Control Panel on Windows).
|
||||
/// - Moving the window to a display with a different scale factor.
|
||||
#[non_exhaustive]
|
||||
ScaleFactorChanged {
|
||||
/// The new scale factor.
|
||||
scale_factor: f64,
|
||||
/// The window inner size.
|
||||
new_inner_size: dpi::PhysicalSize<u32>,
|
||||
},
|
||||
}
|
||||
|
||||
/// A webview window that has yet to be built.
|
||||
pub struct PendingWindow<M: Params> {
|
||||
/// The label that the window will be named.
|
||||
|
@ -313,6 +345,11 @@ pub(crate) mod export {
|
|||
self.window.dispatcher.eval_script(js)
|
||||
}
|
||||
|
||||
/// Registers a window event listener.
|
||||
pub fn on_window_event<F: Fn(&WindowEvent) + Send + 'static>(&self, f: F) {
|
||||
self.window.dispatcher.on_window_event(f);
|
||||
}
|
||||
|
||||
// Getters
|
||||
|
||||
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
|
||||
|
|
Loading…
Reference in New Issue