refactor: move runtime to tauri-runtime crate (#1751)

This commit is contained in:
Lucas Fernandes Nogueira
2021-05-09 15:19:37 -03:00
committed by GitHub
parent e84949524c
commit 665ec1d4a1
25 changed files with 880 additions and 700 deletions

View File

@@ -214,6 +214,14 @@
"path": "./core/tauri-utils",
"manager": "rust"
},
"tauri-runtime": {
"path": "./core/tauri-runtime",
"manager": "rust",
"dependencies": [
"tauri-utils"
],
"postversion": "node ../../.scripts/sync-prerelease.js ${ release.type }"
},
"tauri-codegen": {
"path": "./core/tauri-codegen",
"manager": "rust",
@@ -241,7 +249,8 @@
"manager": "rust",
"dependencies": [
"tauri-macros",
"tauri-utils"
"tauri-utils",
"tauri-runtime"
],
"postversion": "node ../../.scripts/sync-cli-metadata.js ${ pkg.pkg } ${ release.type }"
},
@@ -276,4 +285,4 @@
"manager": "javascript"
}
}
}
}

View File

@@ -0,0 +1,6 @@
---
"tauri-runtime": patch
"tauri": patch
---
`tauri-runtime` crate initial release.

View File

@@ -0,0 +1,31 @@
#!/usr/bin/env node
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
/*
This script is solely intended to be run as part of the `covector version` step to
keep the `tauri-release` crate version without the `beta` or `beta-rc` suffix.
*/
const { readFileSync, writeFileSync } = require("fs")
const runtimeManifestPath = '../../core/tauri-runtime/Cargo.toml'
const dependencyManifestPaths = ['../../core/tauri/Cargo.toml']
const changelogPath = '../../core/tauri-runtime/CHANGELOG.md'
const bump = process.argv[2]
let runtimeManifest = readFileSync(runtimeManifestPath, "utf-8")
runtimeManifest = runtimeManifest.replace(/version = "(\d+\.\d+\.\d+)-[^0-9\.]+\.0"/, 'version = "$1"')
writeFileSync(runtimeManifestPath, runtimeManifest)
let changelog = readFileSync(changelogPath, "utf-8")
changelog = changelog.replace(/(\d+\.\d+\.\d+)-[^0-9\.]+\.0/, '$1')
writeFileSync(changelogPath, changelog)
for (const dependencyManifestPath of dependencyManifestPaths) {
let dependencyManifest = readFileSync(dependencyManifestPath, "utf-8")
dependencyManifest = dependencyManifest.replace(/tauri-runtime = { version = "(\d+\.\d+\.\d+)-[^0-9\.]+\.0"/, 'tauri-runtime = { version = "$1"')
writeFileSync(dependencyManifestPath, dependencyManifest)
}

View File

@@ -2,6 +2,7 @@
members = [
# core
"core/tauri",
"core/tauri-runtime",
"core/tauri-macros",
"core/tauri-utils",
"core/tauri-build",

View File

@@ -0,0 +1,19 @@
[package]
name = "tauri-runtime"
version = "0.0.0"
authors = [ "Tauri Programme within The Commons Conservancy" ]
categories = [ "gui", "web-programming" ]
license = "Apache-2.0 OR MIT"
homepage = "https://tauri.studio"
repository = "https://github.com/tauri-apps/tauri"
description = "Runtime for Tauri applications"
edition = "2018"
[dependencies]
serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
thiserror = "1.0"
uuid = { version = "0.8.2", features = [ "v4" ] }
tauri-utils = { version = "1.0.0-beta-rc.1", path = "../tauri-utils" }
wry = { git = "https://github.com/tauri-apps/wry", rev = "6bc97aff525644b83a3a00537316c46d7afb985b" }
image = "0.23"

View File

@@ -5,23 +5,19 @@
//! The [`wry`] Tauri [`Runtime`].
use crate::{
api::config::WindowConfig,
runtime::{
menu::{CustomMenuItem, Menu, MenuId, MenuItem, SystemTrayMenuItem},
webview::{
FileDropEvent, FileDropHandler, RpcRequest, WebviewRpcHandler, WindowBuilder,
WindowBuilderBase,
},
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, MenuEvent, PendingWindow, WindowEvent,
},
Dispatch, Monitor, Params, Runtime, SystemTrayEvent,
menu::{CustomMenuItem, Menu, MenuId, MenuItem, SystemTrayMenuItem},
webview::{
FileDropEvent, FileDropHandler, RpcRequest, WebviewRpcHandler, WindowBuilder, WindowBuilderBase,
},
Icon,
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, MenuEvent, PendingWindow, WindowEvent,
},
Dispatch, Icon, Monitor, Params, Runtime, SystemTrayEvent,
};
use image::{GenericImageView, Pixel};
use tauri_utils::config::WindowConfig;
use uuid::Uuid;
use wry::{
application::{
@@ -1043,7 +1039,7 @@ fn create_webview<M: Params<Runtime = Wry>>(
) -> crate::Result<WebView> {
let PendingWindow {
webview_attributes,
window_attributes,
window_builder,
rpc_handler,
file_drop_handler,
label,
@@ -1051,7 +1047,7 @@ fn create_webview<M: Params<Runtime = Wry>>(
..
} = pending;
let window = window_attributes.build(event_loop).unwrap();
let window = window_builder.build(event_loop).unwrap();
let mut webview_builder = WebViewBuilder::new(window)
.map_err(|e| crate::Error::CreateWebview(Box::new(e)))?
.with_url(&url)

View File

@@ -4,15 +4,12 @@
//! Internal runtime between Tauri and the underlying webview runtime.
use crate::{
runtime::window::{DetachedWindow, PendingWindow},
Icon, Params, WindowBuilder,
};
use std::path::PathBuf;
use tauri_utils::assets::Assets;
use uuid::Uuid;
pub(crate) mod app;
pub mod flavors;
pub(crate) mod manager;
/// Create window and system tray menus.
pub mod menu;
/// Types useful for interacting with a user's monitors.
@@ -23,14 +20,78 @@ pub mod window;
use menu::{MenuId, SystemTrayMenuItem};
use monitor::Monitor;
use tag::Tag;
use webview::WindowBuilder;
use window::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
MenuEvent, WindowEvent,
DetachedWindow, MenuEvent, PendingWindow, WindowEvent,
};
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
/// Failed to create webview.
#[error("failed to create webview: {0}")]
CreateWebview(Box<dyn std::error::Error + Send>),
/// Failed to create window.
#[error("failed to create window")]
CreateWindow,
/// Failed to send message to webview.
#[error("failed to send message to the webview")]
FailedToSendMessage,
/// Failed to serialize/deserialize.
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
/// Encountered an error creating the app system tray,
#[error("error encountered during tray setup: {0}")]
SystemTray(Box<dyn std::error::Error + Send>),
/// Failed to load window icon.
#[error("invalid icon: {0}")]
InvalidIcon(Box<dyn std::error::Error + Send>),
}
/// Result type.
pub type Result<T> = std::result::Result<T, Error>;
#[doc(hidden)]
pub mod private {
pub trait ParamsBase {}
}
/// Types associated with the running Tauri application.
pub trait Params: private::ParamsBase + 'static {
/// The event type used to create and listen to events.
type Event: Tag;
/// The type used to determine the name of windows.
type Label: Tag;
/// The type used to determine window menu ids.
type MenuId: MenuId;
/// The type used to determine system tray menu ids.
type SystemTrayMenuId: MenuId;
/// Assets that Tauri should serve from itself.
type Assets: Assets;
/// The underlying webview runtime used by the Tauri application.
type Runtime: Runtime;
}
/// A icon definition.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Icon {
/// Icon from file path.
File(PathBuf),
/// Icon from raw bytes.
Raw(Vec<u8>),
}
/// A system tray event.
pub struct SystemTrayEvent {
pub(crate) menu_item_id: u32,
pub menu_item_id: u32,
}
/// The webview runtime interface.

View File

@@ -8,16 +8,18 @@ use std::{
hash::{Hash, Hasher},
};
/// A type that can be derived into a menu id.
pub trait MenuId: Hash + Eq + Debug + Clone + Send + Sync + 'static {}
use serde::Serialize;
impl<T> MenuId for T where T: Hash + Eq + Debug + Clone + Send + Sync + 'static {}
/// A type that can be derived into a menu id.
pub trait MenuId: Serialize + Hash + Eq + Debug + Clone + Send + Sync + 'static {}
impl<T> MenuId for T where T: Serialize + Hash + Eq + Debug + Clone + Send + Sync + 'static {}
/// A window menu.
#[derive(Debug, Clone)]
pub struct Menu<I: MenuId> {
pub(crate) title: String,
pub(crate) items: Vec<MenuItem<I>>,
pub title: String,
pub items: Vec<MenuItem<I>>,
}
impl<I: MenuId> Menu<I> {
@@ -29,11 +31,12 @@ impl<I: MenuId> Menu<I> {
}
}
}
/// A custom menu item.
#[derive(Debug, Clone)]
pub struct CustomMenuItem<I: MenuId> {
pub(crate) id: I,
pub(crate) name: String,
pub id: I,
pub name: String,
}
impl<I: MenuId> CustomMenuItem<I> {
@@ -43,7 +46,8 @@ impl<I: MenuId> CustomMenuItem<I> {
Self { id, name: title }
}
pub(crate) fn id_value(&self) -> u32 {
#[doc(hidden)]
pub fn id_value(&self) -> u32 {
let mut s = DefaultHasher::new();
self.id.hash(&mut s);
s.finish() as u32

View File

@@ -71,7 +71,7 @@ use std::{
/// let event: Event = "tauri://file-drop".parse().unwrap();
///
/// // show that this event type can be represented as a Tag, a requirement for using it in Tauri.
/// fn is_file_drop(tag: impl tauri::runtime::tag::Tag) {
/// fn is_file_drop(tag: impl tauri_runtime::tag::Tag) {
/// assert_eq!("tauri://file-drop", tag.to_string());
/// }
///
@@ -113,7 +113,7 @@ where
///
/// We don't want downstream users to implement this trait so that [`Tag`]s cannot be turned into
/// invalid JavaScript - regardless of their content.
pub(crate) trait ToJsString {
pub trait ToJsString {
fn to_js_string(&self) -> crate::Result<String>;
}
@@ -125,7 +125,7 @@ impl<D: Display> ToJsString for D {
}
/// Turn any collection of [`Tag`]s into a JavaScript array of strings.
pub(crate) fn tags_to_javascript_array(tags: &[impl Tag]) -> crate::Result<String> {
pub fn tags_to_javascript_array(tags: &[impl Tag]) -> crate::Result<String> {
let tags: Vec<String> = tags.iter().map(ToString::to_string).collect();
Ok(serde_json::to_string(&tags)?)
}

View File

@@ -4,26 +4,27 @@
//! Items specific to the [`Runtime`](crate::runtime::Runtime)'s webview.
use crate::runtime::Icon;
use crate::{
api::config::{WindowConfig, WindowUrl},
runtime::{
menu::{Menu, MenuId},
window::DetachedWindow,
},
menu::{Menu, MenuId},
window::DetachedWindow,
Icon,
};
use serde::Deserialize;
use serde_json::Value as JsonValue;
use tauri_utils::config::{WindowConfig, WindowUrl};
use std::{collections::HashMap, path::PathBuf};
type UriSchemeProtocol = dyn Fn(&str) -> crate::Result<Vec<u8>> + Send + Sync + 'static;
type UriSchemeProtocol =
dyn Fn(&str) -> Result<Vec<u8>, Box<dyn std::error::Error>> + Send + Sync + 'static;
/// The attributes used to create an webview.
pub struct WebviewAttributes {
pub(crate) url: WindowUrl,
pub(crate) initialization_scripts: Vec<String>,
pub(crate) data_directory: Option<PathBuf>,
pub(crate) uri_scheme_protocols: HashMap<String, Box<UriSchemeProtocol>>,
pub url: WindowUrl,
pub initialization_scripts: Vec<String>,
pub data_directory: Option<PathBuf>,
pub uri_scheme_protocols: HashMap<String, Box<UriSchemeProtocol>>,
}
impl WebviewAttributes {
@@ -65,7 +66,7 @@ impl WebviewAttributes {
/// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`.
pub fn register_uri_scheme_protocol<
N: Into<String>,
H: Fn(&str) -> crate::Result<Vec<u8>> + Send + Sync + 'static,
H: Fn(&str) -> Result<Vec<u8>, Box<dyn std::error::Error>> + Send + Sync + 'static,
>(
mut self,
uri_scheme: N,
@@ -146,7 +147,6 @@ pub trait WindowBuilder: WindowBuilderBase {
}
/// Rpc request.
#[non_exhaustive]
pub struct RpcRequest {
/// RPC command.
pub command: String,
@@ -157,7 +157,8 @@ pub struct RpcRequest {
/// Uses a custom URI scheme handler to resolve file requests
pub struct CustomProtocol {
/// Handler for protocol
pub protocol: Box<dyn Fn(&str) -> crate::Result<Vec<u8>> + Send + Sync>,
#[allow(clippy::type_complexity)]
pub protocol: Box<dyn Fn(&str) -> Result<Vec<u8>, Box<dyn std::error::Error>> + Send + Sync>,
}
/// The file drop event payload.
@@ -173,18 +174,18 @@ pub enum FileDropEvent {
}
/// Rpc handler.
pub(crate) type WebviewRpcHandler<M> = Box<dyn Fn(DetachedWindow<M>, RpcRequest) + Send>;
pub type WebviewRpcHandler<M> = Box<dyn Fn(DetachedWindow<M>, RpcRequest) + Send>;
/// File drop handler callback
/// Return `true` in the callback to block the OS' default behavior of handling a file drop.
pub(crate) type FileDropHandler<M> = Box<dyn Fn(FileDropEvent, DetachedWindow<M>) -> bool + Send>;
pub type FileDropHandler<M> = Box<dyn Fn(FileDropEvent, DetachedWindow<M>) -> bool + Send>;
#[derive(Deserialize)]
pub(crate) struct InvokePayload {
pub struct InvokePayload {
#[serde(rename = "__tauriModule")]
pub(crate) tauri_module: Option<String>,
pub(crate) callback: String,
pub(crate) error: String,
pub tauri_module: Option<String>,
pub callback: String,
pub error: String,
#[serde(flatten)]
pub(crate) inner: JsonValue,
pub inner: JsonValue,
}

View File

@@ -0,0 +1,147 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! A layer between raw [`Runtime`] webview windows and Tauri.
use crate::{
webview::{FileDropHandler, WebviewAttributes, WebviewRpcHandler},
Dispatch, Params, Runtime, WindowBuilder,
};
use serde::Serialize;
use tauri_utils::config::WindowConfig;
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 menu event.
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MenuEvent {
pub menu_item_id: u32,
}
/// A webview window that has yet to be built.
pub struct PendingWindow<M: Params> {
/// The label that the window will be named.
pub label: M::Label,
/// The [`WindowBuilder`] that the window will be created with.
pub window_builder: <<M::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
/// The [`WebviewAttributes`] that the webview will be created with.
pub webview_attributes: WebviewAttributes,
/// How to handle RPC calls on the webview window.
pub rpc_handler: Option<WebviewRpcHandler<M>>,
/// How to handle a file dropping onto the webview window.
pub file_drop_handler: Option<FileDropHandler<M>>,
/// The resolved URL to load on the webview.
pub url: String,
}
impl<M: Params> PendingWindow<M> {
/// Create a new [`PendingWindow`] with a label and starting url.
pub fn new(
window_builder: <<M::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
webview_attributes: WebviewAttributes,
label: M::Label,
) -> Self {
Self {
window_builder,
webview_attributes,
label,
rpc_handler: None,
file_drop_handler: None,
url: "tauri://localhost".to_string(),
}
}
/// Create a new [`PendingWindow`] from a [`WindowConfig`] with a label and starting url.
pub fn with_config(
window_config: WindowConfig,
webview_attributes: WebviewAttributes,
label: M::Label,
) -> Self {
Self {
window_builder:
<<<M::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder>::with_config(
window_config,
),
webview_attributes,
label,
rpc_handler: None,
file_drop_handler: None,
url: "tauri://localhost".to_string(),
}
}
}
/// A webview window that is not yet managed by Tauri.
pub struct DetachedWindow<M: Params> {
/// Name of the window
pub label: M::Label,
/// The [`Dispatch`](crate::runtime::Dispatch) associated with the window.
pub dispatcher: <M::Runtime as Runtime>::Dispatcher,
}
impl<M: Params> Clone for DetachedWindow<M> {
fn clone(&self) -> Self {
Self {
label: self.label.clone(),
dispatcher: self.dispatcher.clone(),
}
}
}
impl<M: Params> Hash for DetachedWindow<M> {
/// Only use the [`DetachedWindow`]'s label to represent its hash.
fn hash<H: Hasher>(&self, state: &mut H) {
self.label.hash(state)
}
}
impl<M: Params> Eq for DetachedWindow<M> {}
impl<M: Params> PartialEq for DetachedWindow<M> {
/// Only use the [`DetachedWindow`]'s label to compare equality.
fn eq(&self, other: &Self) -> bool {
self.label.eq(&other.label)
}
}

View File

@@ -22,9 +22,9 @@ futures = "0.3"
uuid = { version = "0.8.2", features = [ "v4" ] }
thiserror = "1.0.24"
once_cell = "1.7.2"
tauri-runtime = { version = "0.0.0", path = "../tauri-runtime" }
tauri-macros = { version = "1.0.0-beta-rc.1", path = "../tauri-macros" }
tauri-utils = { version = "1.0.0-beta-rc.1", path = "../tauri-utils" }
wry = { git = "https://github.com/tauri-apps/wry", rev = "6bc97aff525644b83a3a00537316c46d7afb985b" }
rand = "0.8"
reqwest = { version = "0.11", features = [ "json", "multipart" ] }
tempfile = "3"

View File

@@ -3,20 +3,21 @@
// SPDX-License-Identifier: MIT
use crate::{
api::{assets::Assets, config::WindowUrl},
api::assets::Assets,
api::config::WindowUrl,
hooks::{InvokeHandler, OnPageLoad, PageLoadPayload, SetupHook},
manager::{Args, WindowManager},
plugin::{Plugin, PluginStore},
runtime::{
flavors::wry::Wry,
manager::{Args, WindowManager},
menu::{Menu, MenuId, SystemTrayMenuItem},
tag::Tag,
webview::{CustomProtocol, WebviewAttributes, WindowBuilder},
window::PendingWindow,
Dispatch, Runtime,
Dispatch, Params, Runtime,
},
sealed::{ManagerBase, RuntimeOrDispatch},
Context, Invoke, Manager, Params, StateManager, Window,
Context, Invoke, Manager, StateManager, Window,
};
use std::{collections::HashMap, sync::Arc};
@@ -97,13 +98,13 @@ impl<P: Params> App<P> {
WebviewAttributes,
),
{
let (window_attributes, webview_attributes) = setup(
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_attributes, webview_attributes, label),
PendingWindow::new(window_builder, webview_attributes, label),
)?;
Ok(())
}
@@ -337,12 +338,12 @@ where
WebviewAttributes,
),
{
let (window_attributes, webview_attributes) = setup(
let (window_builder, webview_attributes) = setup(
<R::Dispatcher as Dispatch>::WindowBuilder::new(),
WebviewAttributes::new(url),
);
self.pending_windows.push(PendingWindow::new(
window_attributes,
window_builder,
webview_attributes,
label,
));
@@ -394,7 +395,7 @@ where
/// * `protocol` the protocol associated with the given URI scheme. It's a function that takes an URL such as `example://localhost/asset.css`.
pub fn register_global_uri_scheme_protocol<
N: Into<String>,
H: Fn(&str) -> crate::Result<Vec<u8>> + Send + Sync + 'static,
H: Fn(&str) -> Result<Vec<u8>, Box<dyn std::error::Error>> + Send + Sync + 'static,
>(
mut self,
uri_scheme: N,

View File

@@ -12,7 +12,7 @@ use crate::{
};
use serde::Deserialize;
use crate::Icon;
use crate::runtime::Icon;
use std::path::PathBuf;
#[derive(Deserialize)]
@@ -115,7 +115,7 @@ impl Cmd {
)
})?
.emit_others(
&crate::runtime::manager::tauri_event::<P::Event>("tauri://window-created"),
&crate::manager::tauri_event::<P::Event>("tauri://window-created"),
Some(WindowCreatedEvent {
label: label.to_string(),
}),

View File

@@ -8,6 +8,9 @@ use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
/// Runtime error.
#[error("runtime error: {0}")]
Runtime(#[from] tauri_runtime::Error),
/// Failed to create webview.
#[error("failed to create webview: {0}")]
CreateWebview(Box<dyn std::error::Error + Send>),

View File

@@ -4,7 +4,7 @@
use crate::{
api::rpc::{format_callback, format_callback_result},
runtime::app::App,
app::App,
Params, StateManager, Window,
};
use serde::{Deserialize, Serialize};

View File

@@ -16,6 +16,7 @@ pub use tauri_macros::{command, generate_handler};
/// Core API.
pub mod api;
pub(crate) mod app;
/// Async runtime.
pub mod async_runtime;
pub mod command;
@@ -24,8 +25,11 @@ mod endpoints;
mod error;
mod event;
mod hooks;
mod manager;
pub mod plugin;
pub mod runtime;
/// Tauri window.
pub mod window;
use tauri_runtime as runtime;
/// The Tauri-specific settings for your runtime e.g. notification permission status.
pub mod settings;
mod state;
@@ -40,38 +44,34 @@ pub type SyncTask = Box<dyn FnOnce() + Send>;
use crate::{
event::{Event, EventHandler},
runtime::{
tag::{Tag, TagRef},
window::PendingWindow,
Runtime,
},
runtime::window::PendingWindow,
};
use serde::Serialize;
use std::{borrow::Borrow, collections::HashMap, path::PathBuf, sync::Arc};
// Export types likely to be used by the application.
pub use {
self::api::{
assets::Assets,
config::{Config, WindowUrl},
},
self::api::assets::Assets,
self::api::config::{Config, WindowUrl},
self::app::{App, Builder, SystemTrayEvent, WindowMenuEvent},
self::hooks::{
Invoke, InvokeError, InvokeHandler, InvokeMessage, InvokeResolver, InvokeResponse, OnPageLoad,
PageLoadPayload, SetupHook,
},
self::runtime::app::{App, Builder, SystemTrayEvent, WindowMenuEvent},
self::runtime::flavors::wry::Wry,
self::runtime::menu::{CustomMenuItem, Menu, MenuId, MenuItem, SystemTrayMenuItem},
self::runtime::monitor::Monitor,
self::runtime::webview::{WebviewAttributes, WindowBuilder},
self::runtime::window::{
export::{
self::runtime::{
flavors::wry::Wry,
menu::{CustomMenuItem, Menu, MenuId, MenuItem, SystemTrayMenuItem},
monitor::Monitor,
tag::{Tag, TagRef},
webview::{WebviewAttributes, WindowBuilder},
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
Window,
WindowEvent,
},
WindowEvent,
Params,
},
self::state::{State, StateManager},
self::window::{MenuEvent, Window},
tauri_utils::platform,
};
@@ -109,16 +109,6 @@ macro_rules! tauri_build_context {
};
}
/// A icon definition.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Icon {
/// Icon from file path.
File(PathBuf),
/// Icon from raw bytes.
Raw(Vec<u8>),
}
/// User supplied data required inside of a Tauri application.
pub struct Context<A: Assets> {
/// The config the application was prepared with.
@@ -142,27 +132,6 @@ pub struct Context<A: Assets> {
pub package_info: crate::api::PackageInfo,
}
/// Types associated with the running Tauri application.
pub trait Params: sealed::ParamsBase {
/// The event type used to create and listen to events.
type Event: Tag;
/// The type used to determine the name of windows.
type Label: Tag;
/// The type used to determine window menu ids.
type MenuId: MenuId;
/// The type used to determine system tray menu ids.
type SystemTrayMenuId: MenuId;
/// Assets that Tauri should serve from itself.
type Assets: Assets;
/// The underlying webview runtime used by the Tauri application.
type Runtime: Runtime;
}
/// Manages a running application.
///
/// TODO: expand these docs
@@ -262,13 +231,10 @@ pub trait Manager<P: Params>: sealed::ManagerBase<P> {
}
}
/// Prevent implementation details from leaking out of the [`Manager`] and [`Params`] traits.
/// Prevent implementation details from leaking out of the [`Manager`] trait.
pub(crate) mod sealed {
use super::Params;
use crate::runtime::{manager::WindowManager, Runtime};
/// No downstream implementations of [`Params`].
pub trait ParamsBase: 'static {}
use crate::manager::WindowManager;
use tauri_runtime::{Params, Runtime};
/// A running [`Runtime`] or a dispatcher to it.
pub enum RuntimeOrDispatch<'r, P: Params> {
@@ -294,8 +260,10 @@ pub(crate) mod sealed {
let labels = self.manager().labels().into_iter().collect::<Vec<_>>();
let pending = self.manager().prepare_window(pending, &labels)?;
match runtime {
RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending),
RuntimeOrDispatch::Dispatch(mut dispatcher) => dispatcher.create_window(pending),
RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending).map_err(Into::into),
RuntimeOrDispatch::Dispatch(mut dispatcher) => {
dispatcher.create_window(pending).map_err(Into::into)
}
}
.map(|window| self.manager().attach_window(window))
}

View File

@@ -9,22 +9,22 @@ use crate::{
path::{resolve_path, BaseDirectory},
PackageInfo,
},
app::{GlobalMenuEventListener, WindowMenuEvent},
event::{Event, EventHandler, Listeners},
hooks::{InvokeHandler, OnPageLoad, PageLoadPayload},
plugin::PluginStore,
runtime::{
app::{GlobalMenuEventListener, WindowMenuEvent},
menu::{Menu, MenuId, MenuItem},
private::ParamsBase,
tag::{tags_to_javascript_array, Tag, TagRef, ToJsString},
webview::{
CustomProtocol, FileDropEvent, FileDropHandler, InvokePayload, WebviewRpcHandler,
WindowBuilder,
},
window::{dpi::PhysicalSize, DetachedWindow, MenuEvent, PendingWindow, WindowEvent},
Icon, Runtime,
window::{dpi::PhysicalSize, DetachedWindow, PendingWindow, WindowEvent},
Icon, Params, Runtime,
},
sealed::ParamsBase,
App, Context, Invoke, Params, StateManager, Window,
App, Context, Invoke, MenuEvent, StateManager, Window,
};
use serde::Serialize;
use serde_json::Value as JsonValue;
@@ -203,6 +203,11 @@ impl<P: Params> WindowManager<P> {
self.inner.state.clone()
}
/// Get the menu ids mapper.
pub(crate) fn menu_ids(&self) -> HashMap<u32, P::MenuId> {
self.inner.menu_ids.clone()
}
// setup content for dev-server
#[cfg(dev)]
fn get_url(&self) -> String {
@@ -243,15 +248,15 @@ impl<P: Params> WindowManager<P> {
current_window_label = label.to_js_string()?,
));
if !pending.window_attributes.has_icon() {
if !pending.window_builder.has_icon() {
if let Some(default_window_icon) = &self.inner.default_window_icon {
let icon = Icon::Raw(default_window_icon.clone());
pending.window_attributes = pending.window_attributes.icon(icon)?;
pending.window_builder = pending.window_builder.icon(icon)?;
}
}
if !pending.window_attributes.has_menu() {
pending.window_attributes = pending.window_attributes.menu(self.inner.menu.clone());
if !pending.window_builder.has_menu() {
pending.window_builder = pending.window_builder.menu(self.inner.menu.clone());
}
for (uri_scheme, protocol) in &self.inner.uri_scheme_protocols {
@@ -344,7 +349,7 @@ impl<P: Params> WindowManager<P> {
Err(e) => {
#[cfg(debug_assertions)]
eprintln!("{:?}", e); // TODO log::error!
Err(e)
Err(Box::new(e))
}
}
}),
@@ -369,6 +374,7 @@ impl<P: Params> WindowManager<P> {
&tauri_event::<P::Event>("tauri://file-drop-cancelled"),
Some(()),
),
_ => unimplemented!(),
};
});
true
@@ -394,9 +400,9 @@ impl<P: Params> WindowManager<P> {
}}
{plugin_initialization_script}
"#,
core_script = include_str!("../../scripts/core.js"),
core_script = include_str!("../scripts/core.js"),
bundle_script = if with_global_tauri {
include_str!("../../scripts/bundle.js")
include_str!("../scripts/bundle.js")
} else {
""
},
@@ -548,13 +554,12 @@ impl<P: Params> WindowManager<P> {
});
let window_ = window.clone();
let menu_event_listeners = self.inner.menu_event_listeners.clone();
let menu_ids = self.inner.menu_ids.clone();
window.on_menu_event(move |event| {
let _ = on_menu_event(&window_, event);
let _ = on_menu_event(&window_, &event);
for handler in menu_event_listeners.iter() {
handler(WindowMenuEvent {
window: window_.clone(),
menu_item_id: menu_ids.get(&event.menu_item_id).unwrap().clone(),
menu_item_id: event.menu_item_id.clone(),
});
}
});
@@ -727,6 +732,7 @@ fn on_window_event<P: Params>(window: &Window<P>, event: &WindowEvent) -> crate:
WindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size,
..
} => window.emit(
&WINDOW_SCALE_FACTOR_CHANGED_EVENT
.parse()
@@ -736,6 +742,7 @@ fn on_window_event<P: Params>(window: &Window<P>, event: &WindowEvent) -> crate:
size: new_inner_size.clone(),
}),
)?,
_ => unimplemented!(),
}
Ok(())
}
@@ -747,11 +754,11 @@ struct ScaleFactorChanged {
size: PhysicalSize<u32>,
}
fn on_menu_event<P: Params>(window: &Window<P>, event: &MenuEvent) -> crate::Result<()> {
fn on_menu_event<P: Params>(window: &Window<P>, event: &MenuEvent<P::MenuId>) -> crate::Result<()> {
window.emit(
&MENU_EVENT
.parse()
.unwrap_or_else(|_| panic!("unhandled event")),
Some(event),
Some(event.menu_item_id.clone()),
)
}

View File

@@ -1,552 +0,0 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
//! A layer between raw [`Runtime`] webview windows and Tauri.
use crate::{
api::config::{WindowConfig, WindowUrl},
event::{Event, EventHandler},
hooks::{InvokeMessage, InvokeResolver, PageLoadPayload},
runtime::{
tag::ToJsString,
webview::{FileDropHandler, InvokePayload, WebviewAttributes, WebviewRpcHandler},
Dispatch, Monitor, Runtime,
},
sealed::{ManagerBase, RuntimeOrDispatch},
Icon, Manager, Params, WindowBuilder,
};
use serde::Serialize;
use serde_json::Value as JsonValue;
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 menu event.
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct MenuEvent {
pub(crate) menu_item_id: u32,
}
/// A webview window that has yet to be built.
pub struct PendingWindow<M: Params> {
/// The label that the window will be named.
pub label: M::Label,
/// The [`WindowBuilder`] that the window will be created with.
pub window_attributes: <<M::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
/// The [`WebviewAttributes`] that the webview will be created with.
pub webview_attributes: WebviewAttributes,
/// How to handle RPC calls on the webview window.
pub rpc_handler: Option<WebviewRpcHandler<M>>,
/// How to handle a file dropping onto the webview window.
pub file_drop_handler: Option<FileDropHandler<M>>,
/// The resolved URL to load on the webview.
pub url: String,
}
impl<M: Params> PendingWindow<M> {
/// Create a new [`PendingWindow`] with a label and starting url.
pub fn new(
window_attributes: <<M::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
webview_attributes: WebviewAttributes,
label: M::Label,
) -> Self {
Self {
window_attributes,
webview_attributes,
label,
rpc_handler: None,
file_drop_handler: None,
url: "tauri://localhost".to_string(),
}
}
/// Create a new [`PendingWindow`] from a [`WindowConfig`] with a label and starting url.
pub fn with_config(
window_config: WindowConfig,
webview_attributes: WebviewAttributes,
label: M::Label,
) -> Self {
Self {
window_attributes:
<<<M::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder>::with_config(
window_config,
),
webview_attributes,
label,
rpc_handler: None,
file_drop_handler: None,
url: "tauri://localhost".to_string(),
}
}
}
/// A webview window that is not yet managed by Tauri.
pub struct DetachedWindow<M: Params> {
/// Name of the window
pub label: M::Label,
/// The [`Dispatch`](crate::runtime::Dispatch) associated with the window.
pub dispatcher: <M::Runtime as Runtime>::Dispatcher,
}
impl<M: Params> Clone for DetachedWindow<M> {
fn clone(&self) -> Self {
Self {
label: self.label.clone(),
dispatcher: self.dispatcher.clone(),
}
}
}
impl<M: Params> Hash for DetachedWindow<M> {
/// Only use the [`DetachedWindow`]'s label to represent its hash.
fn hash<H: Hasher>(&self, state: &mut H) {
self.label.hash(state)
}
}
impl<M: Params> Eq for DetachedWindow<M> {}
impl<M: Params> PartialEq for DetachedWindow<M> {
/// Only use the [`DetachedWindow`]'s label to compare equality.
fn eq(&self, other: &Self) -> bool {
self.label.eq(&other.label)
}
}
/// We want to export the runtime related window at the crate root, but not look like a re-export.
pub(crate) mod export {
pub(crate) use super::dpi;
use super::*;
use crate::command::{CommandArg, CommandItem};
use crate::runtime::{manager::WindowManager, tag::TagRef};
use crate::{Invoke, InvokeError};
use dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use std::borrow::Borrow;
/// A webview window managed by Tauri.
///
/// This type also implements [`Manager`] which allows you to manage other windows attached to
/// the same application.
///
/// TODO: expand these docs since this is a pretty important type
pub struct Window<P: Params> {
/// The webview window created by the runtime.
window: DetachedWindow<P>,
/// The manager to associate this webview window with.
manager: WindowManager<P>,
}
impl<M: Params> Clone for Window<M> {
fn clone(&self) -> Self {
Self {
window: self.window.clone(),
manager: self.manager.clone(),
}
}
}
impl<P: Params> Hash for Window<P> {
/// Only use the [`Window`]'s label to represent its hash.
fn hash<H: Hasher>(&self, state: &mut H) {
self.window.label.hash(state)
}
}
impl<P: Params> Eq for Window<P> {}
impl<P: Params> PartialEq for Window<P> {
/// Only use the [`Window`]'s label to compare equality.
fn eq(&self, other: &Self) -> bool {
self.window.label.eq(&other.window.label)
}
}
impl<P: Params> Manager<P> for Window<P> {}
impl<P: Params> ManagerBase<P> for Window<P> {
fn manager(&self) -> &WindowManager<P> {
&self.manager
}
}
impl<'de, P: Params> CommandArg<'de, P> for Window<P> {
/// Grabs the [`Window`] from the [`CommandItem`]. This will never fail.
fn from_command(command: CommandItem<'de, P>) -> Result<Self, InvokeError> {
Ok(command.message.window())
}
}
impl<P: Params> Window<P> {
/// Create a new window that is attached to the manager.
pub(crate) fn new(manager: WindowManager<P>, window: DetachedWindow<P>) -> Self {
Self { window, manager }
}
/// Creates a new webview window.
pub fn create_window<F>(
&mut self,
label: P::Label,
url: WindowUrl,
setup: F,
) -> crate::Result<Window<P>>
where
F: FnOnce(
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
WebviewAttributes,
) -> (
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder,
WebviewAttributes,
),
{
let (window_attributes, webview_attributes) = setup(
<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder::new(),
WebviewAttributes::new(url),
);
self.create_new_window(
RuntimeOrDispatch::Dispatch(self.dispatcher()),
PendingWindow::new(window_attributes, webview_attributes, label),
)
}
/// The current window's dispatcher.
pub(crate) fn dispatcher(&self) -> <P::Runtime as Runtime>::Dispatcher {
self.window.dispatcher.clone()
}
pub(crate) fn run_on_main_thread<F: FnOnce() + Send + 'static>(
&self,
f: F,
) -> crate::Result<()> {
self.window.dispatcher.run_on_main_thread(f)
}
/// How to handle this window receiving an [`InvokeMessage`].
pub(crate) fn on_message(self, command: String, payload: InvokePayload) -> crate::Result<()> {
let manager = self.manager.clone();
match command.as_str() {
"__initialized" => {
let payload: PageLoadPayload = serde_json::from_value(payload.inner)?;
manager.run_on_page_load(self, payload);
}
_ => {
let message = InvokeMessage::new(
self.clone(),
manager.state(),
command.to_string(),
payload.inner,
);
let resolver = InvokeResolver::new(self, payload.callback, payload.error);
let invoke = Invoke { message, resolver };
if let Some(module) = &payload.tauri_module {
let module = module.to_string();
crate::endpoints::handle(module, invoke, manager.config(), manager.package_info());
} else if command.starts_with("plugin:") {
manager.extend_api(invoke);
} else {
manager.run_invoke_handler(invoke);
}
}
}
Ok(())
}
/// The label of this window.
pub fn label(&self) -> &P::Label {
&self.window.label
}
pub(crate) fn emit_internal<E: ?Sized, S>(
&self,
event: &E,
payload: Option<S>,
) -> crate::Result<()>
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
S: Serialize,
{
let js_payload = match payload {
Some(payload_value) => serde_json::to_value(payload_value)?,
None => JsonValue::Null,
};
self.eval(&format!(
"window['{}']({{event: {}, payload: {}}}, '{}')",
self.manager.event_emit_function_name(),
event.to_js_string()?,
js_payload,
self.manager.generate_salt(),
))?;
Ok(())
}
/// Emits an event to the current window.
pub fn emit<E: ?Sized, S>(&self, event: &E, payload: Option<S>) -> crate::Result<()>
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
S: Serialize,
{
self.emit_internal(event, payload)
}
/// Emits an event on all windows except this one.
pub fn emit_others<E: ?Sized, S>(&self, event: &E, payload: Option<S>) -> crate::Result<()>
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
S: Serialize + Clone,
{
self.manager.emit_filter(event, payload, |w| w != self)
}
/// Listen to an event on this window.
pub fn listen<E: Into<P::Event>, F>(&self, event: E, handler: F) -> EventHandler
where
F: Fn(Event) + Send + 'static,
{
let label = self.window.label.clone();
self.manager.listen(event.into(), Some(label), handler)
}
/// Listen to a an event on this window a single time.
pub fn once<E: Into<P::Event>, F>(&self, event: E, handler: F) -> EventHandler
where
F: Fn(Event) + Send + 'static,
{
let label = self.window.label.clone();
self.manager.once(event.into(), Some(label), handler)
}
/// Triggers an event on this window.
pub fn trigger<E: ?Sized>(&self, event: &E, data: Option<String>)
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
{
let label = self.window.label.clone();
self.manager.trigger(event, Some(label), data)
}
/// Evaluates JavaScript on this window.
pub fn eval(&self, js: &str) -> crate::Result<()> {
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);
}
/// Registers a menu event listener.
pub fn on_menu_event<F: Fn(&MenuEvent) + Send + 'static>(&self, f: F) {
self.window.dispatcher.on_menu_event(f);
}
// Getters
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
pub fn scale_factor(&self) -> crate::Result<f64> {
self.window.dispatcher.scale_factor()
}
/// Returns the position of the top-left hand corner of the window's client area relative to the top-left hand corner of the desktop.
pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
self.window.dispatcher.inner_position()
}
/// Returns the position of the top-left hand corner of the window relative to the top-left hand corner of the desktop.
pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
self.window.dispatcher.outer_position()
}
/// Returns the physical size of the window's client area.
///
/// The client area is the content of the window, excluding the title bar and borders.
pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
self.window.dispatcher.inner_size()
}
/// Returns the physical size of the entire window.
///
/// These dimensions include the title bar and borders. If you don't want that (and you usually don't), use inner_size instead.
pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
self.window.dispatcher.outer_size()
}
/// Gets the window's current fullscreen state.
pub fn is_fullscreen(&self) -> crate::Result<bool> {
self.window.dispatcher.is_fullscreen()
}
/// Gets the window's current maximized state.
pub fn is_maximized(&self) -> crate::Result<bool> {
self.window.dispatcher.is_maximized()
}
/// Returns the monitor on which the window currently resides.
///
/// Returns None if current monitor can't be detected.
pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
self.window.dispatcher.current_monitor()
}
/// Returns the primary monitor of the system.
///
/// Returns None if it can't identify any monitor as a primary one.
pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
self.window.dispatcher.primary_monitor()
}
/// Returns the list of all the monitors available on the system.
pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
self.window.dispatcher.available_monitors()
}
// Setters
/// Opens the dialog to prints the contents of the webview.
/// Currently only supported on macOS on `wry`.
/// `window.print()` works on all platforms.
pub fn print(&self) -> crate::Result<()> {
self.window.dispatcher.print()
}
/// Determines if this window should be resizable.
pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> {
self.window.dispatcher.set_resizable(resizable)
}
/// Set this window's title.
pub fn set_title(&self, title: &str) -> crate::Result<()> {
self.window.dispatcher.set_title(title.to_string())
}
/// Maximizes this window.
pub fn maximize(&self) -> crate::Result<()> {
self.window.dispatcher.maximize()
}
/// Un-maximizes this window.
pub fn unmaximize(&self) -> crate::Result<()> {
self.window.dispatcher.unmaximize()
}
/// Minimizes this window.
pub fn minimize(&self) -> crate::Result<()> {
self.window.dispatcher.minimize()
}
/// Un-minimizes this window.
pub fn unminimize(&self) -> crate::Result<()> {
self.window.dispatcher.unminimize()
}
/// Show this window.
pub fn show(&self) -> crate::Result<()> {
self.window.dispatcher.show()
}
/// Hide this window.
pub fn hide(&self) -> crate::Result<()> {
self.window.dispatcher.hide()
}
/// Closes this window.
pub fn close(&self) -> crate::Result<()> {
self.window.dispatcher.close()
}
/// Determines if this window should be [decorated].
///
/// [decorated]: https://en.wikipedia.org/wiki/Window_(computing)#Window_decoration
pub fn set_decorations(&self, decorations: bool) -> crate::Result<()> {
self.window.dispatcher.set_decorations(decorations)
}
/// Determines if this window should always be on top of other windows.
pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
self.window.dispatcher.set_always_on_top(always_on_top)
}
/// Resizes this window.
pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
self.window.dispatcher.set_size(size.into())
}
/// Sets this window's minimum size.
pub fn set_min_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
self.window.dispatcher.set_min_size(size.map(|s| s.into()))
}
/// Sets this window's maximum size.
pub fn set_max_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
self.window.dispatcher.set_max_size(size.map(|s| s.into()))
}
/// Sets this window's position.
pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
self.window.dispatcher.set_position(position.into())
}
/// Determines if this window should be fullscreen.
pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
self.window.dispatcher.set_fullscreen(fullscreen)
}
/// Sets this window' icon.
pub fn set_icon(&self, icon: Icon) -> crate::Result<()> {
self.window.dispatcher.set_icon(icon)
}
/// Starts dragging the window.
pub fn start_dragging(&self) -> crate::Result<()> {
self.window.dispatcher.start_dragging()
}
pub(crate) fn verify_salt(&self, salt: String) -> bool {
self.manager.verify_salt(salt)
}
}
}

View File

@@ -339,7 +339,7 @@ mod error;
pub use self::error::Error;
use crate::runtime::manager::tauri_event;
use crate::manager::tauri_event;
use crate::{
api::{
config::UpdaterConfig,

478
core/tauri/src/window.rs Normal file
View File

@@ -0,0 +1,478 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::{
api::config::WindowUrl,
command::{CommandArg, CommandItem},
event::{Event, EventHandler},
manager::WindowManager,
runtime::{
menu::MenuId,
monitor::Monitor,
tag::{TagRef, ToJsString},
webview::{InvokePayload, WebviewAttributes, WindowBuilder},
window::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, PendingWindow, WindowEvent,
},
Dispatch, Icon, Params, Runtime,
},
sealed::ManagerBase,
sealed::RuntimeOrDispatch,
Invoke, InvokeError, InvokeMessage, InvokeResolver, Manager, PageLoadPayload,
};
use serde::Serialize;
use serde_json::Value as JsonValue;
use std::{
borrow::Borrow,
hash::{Hash, Hasher},
};
/// The window menu event.
#[derive(Debug, Clone)]
pub struct MenuEvent<I: MenuId> {
pub(crate) menu_item_id: I,
}
impl<I: MenuId> MenuEvent<I> {
/// The menu item id.
pub fn menu_item_id(&self) -> &I {
&self.menu_item_id
}
}
/// A webview window managed by Tauri.
///
/// This type also implements [`Manager`] which allows you to manage other windows attached to
/// the same application.
///
/// TODO: expand these docs since this is a pretty important type
pub struct Window<P: Params> {
/// The webview window created by the runtime.
window: DetachedWindow<P>,
/// The manager to associate this webview window with.
manager: WindowManager<P>,
}
impl<M: Params> Clone for Window<M> {
fn clone(&self) -> Self {
Self {
window: self.window.clone(),
manager: self.manager.clone(),
}
}
}
impl<P: Params> Hash for Window<P> {
/// Only use the [`Window`]'s label to represent its hash.
fn hash<H: Hasher>(&self, state: &mut H) {
self.window.label.hash(state)
}
}
impl<P: Params> Eq for Window<P> {}
impl<P: Params> PartialEq for Window<P> {
/// Only use the [`Window`]'s label to compare equality.
fn eq(&self, other: &Self) -> bool {
self.window.label.eq(&other.window.label)
}
}
impl<P: Params> Manager<P> for Window<P> {}
impl<P: Params> ManagerBase<P> for Window<P> {
fn manager(&self) -> &WindowManager<P> {
&self.manager
}
}
impl<'de, P: Params> CommandArg<'de, P> for Window<P> {
/// Grabs the [`Window`] from the [`CommandItem`]. This will never fail.
fn from_command(command: CommandItem<'de, P>) -> Result<Self, InvokeError> {
Ok(command.message.window())
}
}
impl<P: Params> Window<P> {
/// Create a new window that is attached to the manager.
pub(crate) fn new(manager: WindowManager<P>, window: DetachedWindow<P>) -> Self {
Self { window, manager }
}
/// Creates a new webview window.
pub fn create_window<F>(
&mut self,
label: P::Label,
url: WindowUrl,
setup: F,
) -> crate::Result<Window<P>>
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::Dispatch(self.dispatcher()),
PendingWindow::new(window_builder, webview_attributes, label),
)
}
/// The current window's dispatcher.
pub(crate) fn dispatcher(&self) -> <P::Runtime as Runtime>::Dispatcher {
self.window.dispatcher.clone()
}
pub(crate) fn run_on_main_thread<F: FnOnce() + Send + 'static>(&self, f: F) -> crate::Result<()> {
self
.window
.dispatcher
.run_on_main_thread(f)
.map_err(Into::into)
}
/// How to handle this window receiving an [`InvokeMessage`].
pub(crate) fn on_message(self, command: String, payload: InvokePayload) -> crate::Result<()> {
let manager = self.manager.clone();
match command.as_str() {
"__initialized" => {
let payload: PageLoadPayload = serde_json::from_value(payload.inner)?;
manager.run_on_page_load(self, payload);
}
_ => {
let message = InvokeMessage::new(
self.clone(),
manager.state(),
command.to_string(),
payload.inner,
);
let resolver = InvokeResolver::new(self, payload.callback, payload.error);
let invoke = Invoke { message, resolver };
if let Some(module) = &payload.tauri_module {
let module = module.to_string();
crate::endpoints::handle(module, invoke, manager.config(), manager.package_info());
} else if command.starts_with("plugin:") {
manager.extend_api(invoke);
} else {
manager.run_invoke_handler(invoke);
}
}
}
Ok(())
}
/// The label of this window.
pub fn label(&self) -> &P::Label {
&self.window.label
}
pub(crate) fn emit_internal<E: ?Sized, S>(
&self,
event: &E,
payload: Option<S>,
) -> crate::Result<()>
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
S: Serialize,
{
let js_payload = match payload {
Some(payload_value) => serde_json::to_value(payload_value)?,
None => JsonValue::Null,
};
self.eval(&format!(
"window['{}']({{event: {}, payload: {}}}, '{}')",
self.manager.event_emit_function_name(),
event.to_js_string()?,
js_payload,
self.manager.generate_salt(),
))?;
Ok(())
}
/// Emits an event to the current window.
pub fn emit<E: ?Sized, S>(&self, event: &E, payload: Option<S>) -> crate::Result<()>
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
S: Serialize,
{
self.emit_internal(event, payload)
}
/// Emits an event on all windows except this one.
pub fn emit_others<E: ?Sized, S>(&self, event: &E, payload: Option<S>) -> crate::Result<()>
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
S: Serialize + Clone,
{
self.manager.emit_filter(event, payload, |w| w != self)
}
/// Listen to an event on this window.
pub fn listen<E: Into<P::Event>, F>(&self, event: E, handler: F) -> EventHandler
where
F: Fn(Event) + Send + 'static,
{
let label = self.window.label.clone();
self.manager.listen(event.into(), Some(label), handler)
}
/// Listen to a an event on this window a single time.
pub fn once<E: Into<P::Event>, F>(&self, event: E, handler: F) -> EventHandler
where
F: Fn(Event) + Send + 'static,
{
let label = self.window.label.clone();
self.manager.once(event.into(), Some(label), handler)
}
/// Triggers an event on this window.
pub fn trigger<E: ?Sized>(&self, event: &E, data: Option<String>)
where
P::Event: Borrow<E>,
E: TagRef<P::Event>,
{
let label = self.window.label.clone();
self.manager.trigger(event, Some(label), data)
}
/// Evaluates JavaScript on this window.
pub fn eval(&self, js: &str) -> crate::Result<()> {
self.window.dispatcher.eval_script(js).map_err(Into::into)
}
/// 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);
}
/// Registers a menu event listener.
pub fn on_menu_event<F: Fn(MenuEvent<P::MenuId>) + Send + 'static>(&self, f: F) {
let menu_ids = self.manager.menu_ids();
self.window.dispatcher.on_menu_event(move |event| {
f(MenuEvent {
menu_item_id: menu_ids.get(&event.menu_item_id).unwrap().clone(),
})
});
}
// Getters
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
pub fn scale_factor(&self) -> crate::Result<f64> {
self.window.dispatcher.scale_factor().map_err(Into::into)
}
/// Returns the position of the top-left hand corner of the window's client area relative to the top-left hand corner of the desktop.
pub fn inner_position(&self) -> crate::Result<PhysicalPosition<i32>> {
self.window.dispatcher.inner_position().map_err(Into::into)
}
/// Returns the position of the top-left hand corner of the window relative to the top-left hand corner of the desktop.
pub fn outer_position(&self) -> crate::Result<PhysicalPosition<i32>> {
self.window.dispatcher.outer_position().map_err(Into::into)
}
/// Returns the physical size of the window's client area.
///
/// The client area is the content of the window, excluding the title bar and borders.
pub fn inner_size(&self) -> crate::Result<PhysicalSize<u32>> {
self.window.dispatcher.inner_size().map_err(Into::into)
}
/// Returns the physical size of the entire window.
///
/// These dimensions include the title bar and borders. If you don't want that (and you usually don't), use inner_size instead.
pub fn outer_size(&self) -> crate::Result<PhysicalSize<u32>> {
self.window.dispatcher.outer_size().map_err(Into::into)
}
/// Gets the window's current fullscreen state.
pub fn is_fullscreen(&self) -> crate::Result<bool> {
self.window.dispatcher.is_fullscreen().map_err(Into::into)
}
/// Gets the window's current maximized state.
pub fn is_maximized(&self) -> crate::Result<bool> {
self.window.dispatcher.is_maximized().map_err(Into::into)
}
/// Returns the monitor on which the window currently resides.
///
/// Returns None if current monitor can't be detected.
pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
self.window.dispatcher.current_monitor().map_err(Into::into)
}
/// Returns the primary monitor of the system.
///
/// Returns None if it can't identify any monitor as a primary one.
pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
self.window.dispatcher.primary_monitor().map_err(Into::into)
}
/// Returns the list of all the monitors available on the system.
pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
self
.window
.dispatcher
.available_monitors()
.map_err(Into::into)
}
// Setters
/// Opens the dialog to prints the contents of the webview.
/// Currently only supported on macOS on `wry`.
/// `window.print()` works on all platforms.
pub fn print(&self) -> crate::Result<()> {
self.window.dispatcher.print().map_err(Into::into)
}
/// Determines if this window should be resizable.
pub fn set_resizable(&self, resizable: bool) -> crate::Result<()> {
self
.window
.dispatcher
.set_resizable(resizable)
.map_err(Into::into)
}
/// Set this window's title.
pub fn set_title(&self, title: &str) -> crate::Result<()> {
self
.window
.dispatcher
.set_title(title.to_string())
.map_err(Into::into)
}
/// Maximizes this window.
pub fn maximize(&self) -> crate::Result<()> {
self.window.dispatcher.maximize().map_err(Into::into)
}
/// Un-maximizes this window.
pub fn unmaximize(&self) -> crate::Result<()> {
self.window.dispatcher.unmaximize().map_err(Into::into)
}
/// Minimizes this window.
pub fn minimize(&self) -> crate::Result<()> {
self.window.dispatcher.minimize().map_err(Into::into)
}
/// Un-minimizes this window.
pub fn unminimize(&self) -> crate::Result<()> {
self.window.dispatcher.unminimize().map_err(Into::into)
}
/// Show this window.
pub fn show(&self) -> crate::Result<()> {
self.window.dispatcher.show().map_err(Into::into)
}
/// Hide this window.
pub fn hide(&self) -> crate::Result<()> {
self.window.dispatcher.hide().map_err(Into::into)
}
/// Closes this window.
pub fn close(&self) -> crate::Result<()> {
self.window.dispatcher.close().map_err(Into::into)
}
/// Determines if this window should be [decorated].
///
/// [decorated]: https://en.wikipedia.org/wiki/Window_(computing)#Window_decoration
pub fn set_decorations(&self, decorations: bool) -> crate::Result<()> {
self
.window
.dispatcher
.set_decorations(decorations)
.map_err(Into::into)
}
/// Determines if this window should always be on top of other windows.
pub fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
self
.window
.dispatcher
.set_always_on_top(always_on_top)
.map_err(Into::into)
}
/// Resizes this window.
pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
self
.window
.dispatcher
.set_size(size.into())
.map_err(Into::into)
}
/// Sets this window's minimum size.
pub fn set_min_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
self
.window
.dispatcher
.set_min_size(size.map(|s| s.into()))
.map_err(Into::into)
}
/// Sets this window's maximum size.
pub fn set_max_size<S: Into<Size>>(&self, size: Option<S>) -> crate::Result<()> {
self
.window
.dispatcher
.set_max_size(size.map(|s| s.into()))
.map_err(Into::into)
}
/// Sets this window's position.
pub fn set_position<Pos: Into<Position>>(&self, position: Pos) -> crate::Result<()> {
self
.window
.dispatcher
.set_position(position.into())
.map_err(Into::into)
}
/// Determines if this window should be fullscreen.
pub fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
self
.window
.dispatcher
.set_fullscreen(fullscreen)
.map_err(Into::into)
}
/// Sets this window' icon.
pub fn set_icon(&self, icon: Icon) -> crate::Result<()> {
self.window.dispatcher.set_icon(icon).map_err(Into::into)
}
/// Starts dragging the window.
pub fn start_dragging(&self) -> crate::Result<()> {
self.window.dispatcher.start_dragging().map_err(Into::into)
}
pub(crate) fn verify_salt(&self, salt: String) -> bool {
self.manager.verify_salt(salt)
}
}

View File

@@ -20,8 +20,8 @@ fn main() {
.create_window(
"Rust".to_string(),
tauri::WindowUrl::App("index.html".into()),
|window_attributes, webview_attributes| {
(window_attributes.title("Tauri - Rust"), webview_attributes)
|window_builder, webview_attributes| {
(window_builder.title("Tauri - Rust"), webview_attributes)
},
)
.run(tauri::generate_context!())