mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-04-01 10:01:07 +02:00
feat(core): expose AppHandle, add create_window API (#1855)
This commit is contained in:
committed by
GitHub
parent
3d8dcbbf81
commit
95d518afa1
5
.changes/app-handle-create-window.md
Normal file
5
.changes/app-handle-create-window.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": patch
|
||||
---
|
||||
|
||||
Adds `create_window` API to the `AppHandle` struct.
|
||||
5
.changes/app-handle.md
Normal file
5
.changes/app-handle.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": patch
|
||||
---
|
||||
|
||||
Adds a `handle` function to the `App` struct, which returns a `Send` handle to the app instance.
|
||||
@@ -13,7 +13,7 @@ use tauri_runtime::{
|
||||
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
|
||||
DetachedWindow, PendingWindow, WindowEvent,
|
||||
},
|
||||
Dispatch, Error, Icon, Params, Result, Runtime,
|
||||
Dispatch, Error, Icon, Params, Result, Runtime, RuntimeHandle,
|
||||
};
|
||||
|
||||
#[cfg(feature = "menu")]
|
||||
@@ -539,6 +539,8 @@ impl Dispatch for WryDispatcher {
|
||||
.map_err(|_| Error::FailedToSendMessage)
|
||||
}
|
||||
|
||||
// Creates a window by dispatching a message to the event loop.
|
||||
// Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
|
||||
fn create_window<P: Params<Runtime = Self::Runtime>>(
|
||||
&mut self,
|
||||
pending: PendingWindow<P>,
|
||||
@@ -763,8 +765,46 @@ pub struct Wry {
|
||||
task_rx: Receiver<MainThreadTask>,
|
||||
}
|
||||
|
||||
/// A handle to the Wry runtime.
|
||||
#[derive(Clone)]
|
||||
pub struct WryHandle {
|
||||
dispatcher_context: DispatcherContext,
|
||||
}
|
||||
|
||||
impl RuntimeHandle for WryHandle {
|
||||
type Runtime = Wry;
|
||||
|
||||
// Creates a window by dispatching a message to the event loop.
|
||||
// Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
|
||||
fn create_window<P: Params<Runtime = Self::Runtime>>(
|
||||
&self,
|
||||
pending: PendingWindow<P>,
|
||||
) -> Result<DetachedWindow<P>> {
|
||||
let (tx, rx) = channel();
|
||||
let label = pending.label.clone();
|
||||
let dispatcher_context = self.dispatcher_context.clone();
|
||||
self
|
||||
.dispatcher_context
|
||||
.proxy
|
||||
.send_event(Message::CreateWebview(
|
||||
Arc::new(Mutex::new(Some(Box::new(move |event_loop| {
|
||||
create_webview(event_loop, dispatcher_context, pending)
|
||||
})))),
|
||||
tx,
|
||||
))
|
||||
.map_err(|_| Error::FailedToSendMessage)?;
|
||||
let window_id = rx.recv().unwrap();
|
||||
let dispatcher = WryDispatcher {
|
||||
window_id,
|
||||
context: self.dispatcher_context.clone(),
|
||||
};
|
||||
Ok(DetachedWindow { label, dispatcher })
|
||||
}
|
||||
}
|
||||
|
||||
impl Runtime for Wry {
|
||||
type Dispatcher = WryDispatcher;
|
||||
type Handle = WryHandle;
|
||||
|
||||
fn new() -> Result<Self> {
|
||||
let event_loop = EventLoop::<Message>::with_user_event();
|
||||
@@ -782,6 +822,18 @@ impl Runtime for Wry {
|
||||
})
|
||||
}
|
||||
|
||||
fn handle(&self) -> Self::Handle {
|
||||
WryHandle {
|
||||
dispatcher_context: DispatcherContext {
|
||||
proxy: self.event_loop.create_proxy(),
|
||||
task_tx: self.task_tx.clone(),
|
||||
window_event_listeners: self.window_event_listeners.clone(),
|
||||
#[cfg(feature = "menu")]
|
||||
menu_event_listeners: self.menu_event_listeners.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn create_window<P: Params<Runtime = Self>>(
|
||||
&self,
|
||||
pending: PendingWindow<P>,
|
||||
|
||||
@@ -104,14 +104,29 @@ pub struct SystemTrayEvent {
|
||||
pub menu_item_id: u32,
|
||||
}
|
||||
|
||||
/// A [`Send`] handle to the runtime.
|
||||
pub trait RuntimeHandle: Send + Sized + Clone + 'static {
|
||||
type Runtime: Runtime<Handle = Self>;
|
||||
/// Create a new webview window.
|
||||
fn create_window<P: Params<Runtime = Self::Runtime>>(
|
||||
&self,
|
||||
pending: PendingWindow<P>,
|
||||
) -> crate::Result<DetachedWindow<P>>;
|
||||
}
|
||||
|
||||
/// The webview runtime interface.
|
||||
pub trait Runtime: Sized + 'static {
|
||||
/// The message dispatcher.
|
||||
type Dispatcher: Dispatch<Runtime = Self>;
|
||||
/// The runtime handle type.
|
||||
type Handle: RuntimeHandle<Runtime = Self>;
|
||||
|
||||
/// Creates a new webview runtime.
|
||||
fn new() -> crate::Result<Self>;
|
||||
|
||||
/// Gets a runtime handle.
|
||||
fn handle(&self) -> Self::Handle;
|
||||
|
||||
/// Create a new webview window.
|
||||
fn create_window<P: Params<Runtime = Self>>(
|
||||
&self,
|
||||
|
||||
@@ -95,16 +95,32 @@ impl<P: Params> GlobalWindowEvent<P> {
|
||||
|
||||
crate::manager::default_args! {
|
||||
/// A handle to the currently running application.
|
||||
///
|
||||
/// This type implements [`Manager`] which allows for manipulation of global application items.
|
||||
pub struct AppHandle<P: Params> {
|
||||
runtime_handle: <P::Runtime as Runtime>::Handle,
|
||||
manager: WindowManager<P>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Params> Clone for AppHandle<P> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
runtime_handle: self.runtime_handle.clone(),
|
||||
manager: self.manager.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Params> Manager<P> for AppHandle<P> {}
|
||||
impl<P: Params> ManagerBase<P> for AppHandle<P> {
|
||||
fn manager(&self) -> &WindowManager<P> {
|
||||
&self.manager
|
||||
}
|
||||
|
||||
fn runtime(&self) -> RuntimeOrDispatch<'_, P> {
|
||||
RuntimeOrDispatch::RuntimeHandle(self.runtime_handle.clone())
|
||||
}
|
||||
}
|
||||
|
||||
crate::manager::default_args! {
|
||||
@@ -122,29 +138,51 @@ impl<P: Params> ManagerBase<P> for App<P> {
|
||||
fn manager(&self) -> &WindowManager<P> {
|
||||
&self.manager
|
||||
}
|
||||
|
||||
fn runtime(&self) -> RuntimeOrDispatch<'_, P> {
|
||||
RuntimeOrDispatch::Runtime(&self.runtime)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! shared_app_impl {
|
||||
($app: ty) => {
|
||||
impl<P: Params> $app {
|
||||
/// Creates a new webview window.
|
||||
pub fn create_window<F>(&self, label: P::Label, url: WindowUrl, setup: F) -> crate::Result<()>
|
||||
where
|
||||
F: FnOnce(
|
||||
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
|
||||
WebviewAttributes,
|
||||
) -> (
|
||||
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
|
||||
WebviewAttributes,
|
||||
),
|
||||
{
|
||||
let (window_builder, webview_attributes) = setup(
|
||||
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder::new(),
|
||||
WebviewAttributes::new(url),
|
||||
);
|
||||
self.create_new_window(PendingWindow::new(
|
||||
window_builder,
|
||||
webview_attributes,
|
||||
label,
|
||||
))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
shared_app_impl!(App<P>);
|
||||
shared_app_impl!(AppHandle<P>);
|
||||
|
||||
impl<P: Params> App<P> {
|
||||
/// Creates a new webview window.
|
||||
pub fn create_window<F>(&mut self, label: P::Label, url: WindowUrl, setup: F) -> crate::Result<()>
|
||||
where
|
||||
F: FnOnce(
|
||||
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
|
||||
WebviewAttributes,
|
||||
) -> (
|
||||
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
|
||||
WebviewAttributes,
|
||||
),
|
||||
{
|
||||
let (window_builder, webview_attributes) = setup(
|
||||
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder::new(),
|
||||
WebviewAttributes::new(url),
|
||||
);
|
||||
self.create_new_window(
|
||||
RuntimeOrDispatch::Runtime(&self.runtime),
|
||||
PendingWindow::new(window_builder, webview_attributes, label),
|
||||
)?;
|
||||
Ok(())
|
||||
/// Gets a handle to the application instance.
|
||||
pub fn handle(&self) -> AppHandle<P> {
|
||||
AppHandle {
|
||||
runtime_handle: self.runtime.handle(),
|
||||
manager: self.manager.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,17 +615,16 @@ where
|
||||
)
|
||||
.expect("failed to run tray");
|
||||
for listener in self.system_tray_event_listeners {
|
||||
let app_handle = AppHandle {
|
||||
manager: app.manager.clone(),
|
||||
};
|
||||
let app_handle = app.handle();
|
||||
let ids = ids.clone();
|
||||
let listener = Arc::new(std::sync::Mutex::new(listener));
|
||||
app.runtime.on_system_tray_event(move |event| {
|
||||
listener(
|
||||
&app_handle,
|
||||
SystemTrayEvent {
|
||||
menu_item_id: ids.get(&event.menu_item_id).unwrap().clone(),
|
||||
},
|
||||
);
|
||||
let app_handle = app_handle.clone();
|
||||
let menu_item_id = ids.get(&event.menu_item_id).unwrap().clone();
|
||||
let listener = listener.clone();
|
||||
crate::async_runtime::spawn(async move {
|
||||
listener.lock().unwrap()(&app_handle, SystemTrayEvent { menu_item_id });
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ pub use {
|
||||
config::{Config, WindowUrl},
|
||||
PackageInfo,
|
||||
},
|
||||
self::app::{App, Builder, GlobalWindowEvent},
|
||||
self::app::{App, AppHandle, Builder, GlobalWindowEvent},
|
||||
self::hooks::{
|
||||
Invoke, InvokeError, InvokeHandler, InvokeMessage, InvokeResolver, InvokeResponse, OnPageLoad,
|
||||
PageLoadPayload, SetupHook,
|
||||
@@ -316,13 +316,16 @@ pub trait Manager<P: Params>: sealed::ManagerBase<P> {
|
||||
/// Prevent implementation details from leaking out of the [`Manager`] trait.
|
||||
pub(crate) mod sealed {
|
||||
use crate::manager::WindowManager;
|
||||
use tauri_runtime::{Params, Runtime};
|
||||
use tauri_runtime::{Params, Runtime, RuntimeHandle};
|
||||
|
||||
/// A running [`Runtime`] or a dispatcher to it.
|
||||
pub enum RuntimeOrDispatch<'r, P: Params> {
|
||||
/// Reference to the running [`Runtime`].
|
||||
Runtime(&'r P::Runtime),
|
||||
|
||||
/// Handle to the running [`Runtime`].
|
||||
RuntimeHandle(<P::Runtime as Runtime>::Handle),
|
||||
|
||||
/// A dispatcher to the running [`Runtime`].
|
||||
Dispatch(<P::Runtime as Runtime>::Dispatcher),
|
||||
}
|
||||
@@ -332,17 +335,21 @@ pub(crate) mod sealed {
|
||||
/// The manager behind the [`Managed`] item.
|
||||
fn manager(&self) -> &WindowManager<P>;
|
||||
|
||||
fn runtime(&self) -> RuntimeOrDispatch<'_, P>;
|
||||
|
||||
/// Creates a new [`Window`] on the [`Runtime`] and attaches it to the [`Manager`].
|
||||
fn create_new_window(
|
||||
&self,
|
||||
runtime: RuntimeOrDispatch<'_, P>,
|
||||
pending: crate::PendingWindow<P>,
|
||||
) -> crate::Result<crate::Window<P>> {
|
||||
use crate::runtime::Dispatch;
|
||||
let labels = self.manager().labels().into_iter().collect::<Vec<_>>();
|
||||
let pending = self.manager().prepare_window(pending, &labels)?;
|
||||
match runtime {
|
||||
match self.runtime() {
|
||||
RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending).map_err(Into::into),
|
||||
RuntimeOrDispatch::RuntimeHandle(handle) => {
|
||||
handle.create_window(pending).map_err(Into::into)
|
||||
}
|
||||
RuntimeOrDispatch::Dispatch(mut dispatcher) => {
|
||||
dispatcher.create_window(pending).map_err(Into::into)
|
||||
}
|
||||
|
||||
@@ -136,6 +136,10 @@ impl<P: Params> ManagerBase<P> for Window<P> {
|
||||
fn manager(&self) -> &WindowManager<P> {
|
||||
&self.manager
|
||||
}
|
||||
|
||||
fn runtime(&self) -> RuntimeOrDispatch<'_, P> {
|
||||
RuntimeOrDispatch::Dispatch(self.dispatcher())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, P: Params> CommandArg<'de, P> for Window<P> {
|
||||
@@ -171,10 +175,11 @@ impl<P: Params> Window<P> {
|
||||
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder::new(),
|
||||
WebviewAttributes::new(url),
|
||||
);
|
||||
self.create_new_window(
|
||||
RuntimeOrDispatch::Dispatch(self.dispatcher()),
|
||||
PendingWindow::new(window_builder, webview_attributes, label),
|
||||
)
|
||||
self.create_new_window(PendingWindow::new(
|
||||
window_builder,
|
||||
webview_attributes,
|
||||
label,
|
||||
))
|
||||
}
|
||||
|
||||
/// The current window's dispatcher.
|
||||
|
||||
@@ -11,7 +11,7 @@ mod cmd;
|
||||
mod menu;
|
||||
|
||||
use serde::Serialize;
|
||||
use tauri::{CustomMenuItem, Manager, SystemTrayMenuItem};
|
||||
use tauri::{CustomMenuItem, Manager, SystemTrayMenuItem, WindowBuilder, WindowUrl};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Reply {
|
||||
@@ -37,15 +37,27 @@ fn main() {
|
||||
.on_menu_event(|event| {
|
||||
println!("{:?}", event.menu_item_id());
|
||||
})
|
||||
.system_tray(vec![SystemTrayMenuItem::Custom(CustomMenuItem::new(
|
||||
"toggle".into(),
|
||||
"Toggle",
|
||||
))])
|
||||
.system_tray(vec![
|
||||
SystemTrayMenuItem::Custom(CustomMenuItem::new("toggle".into(), "Toggle")),
|
||||
SystemTrayMenuItem::Custom(CustomMenuItem::new("new".into(), "New window")),
|
||||
])
|
||||
.on_system_tray_event(|app, event| {
|
||||
if event.menu_item_id() == "toggle" {
|
||||
let window = app.get_window("main").unwrap();
|
||||
// TODO: window.is_visible API
|
||||
window.hide().unwrap();
|
||||
match event.menu_item_id().as_str() {
|
||||
"toggle" => {
|
||||
let window = app.get_window("main").unwrap();
|
||||
// TODO: window.is_visible API
|
||||
window.hide().unwrap();
|
||||
}
|
||||
"new" => app
|
||||
.create_window(
|
||||
"new".into(),
|
||||
WindowUrl::App("index.html".into()),
|
||||
|window_builder, webview_attributes| {
|
||||
(window_builder.title("Tauri"), webview_attributes)
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
|
||||
Reference in New Issue
Block a user