From 07208dff6c1e8cff7c10780f4f7f8cee9de44a2e Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Thu, 11 Feb 2021 21:50:39 -0300 Subject: [PATCH] feat(core): add mult-window support (#1217) --- .changes/event.md | 5 + .changes/multiwindow.md | 5 + README.md | 3 +- cli/core/src/helpers/config.rs | 51 ---- cli/tauri.js/src/types/config.schema.json | 82 +++-- cli/tauri.js/src/types/config.ts | 16 +- cli/tauri.js/src/types/config.validator.ts | 77 +++-- .../jest/fixtures/app/src-tauri/src/main.rs | 12 +- .../fixtures/app/src-tauri/tauri.conf.json | 8 +- tauri-utils/src/config.rs | 152 ++++++++-- tauri/Cargo.toml | 6 +- tauri/examples/api/public/index.html | 1 - tauri/examples/api/src-tauri/Cargo.lock | 3 +- tauri/examples/api/src-tauri/src/main.rs | 14 +- tauri/examples/api/src-tauri/tauri.conf.json | 6 +- .../examples/api/src/components/Window.svelte | 5 +- .../communication/dist/communication.js | 1 + .../communication/src-tauri/src/main.rs | 14 +- .../communication/src-tauri/tauri.conf.json | 9 +- tauri/examples/multiwindow/dist/__tauri.js | 287 ++++++++++++++++++ tauri/examples/multiwindow/dist/index.html | 36 +++ tauri/examples/multiwindow/package.json | 11 + .../examples/multiwindow/src-tauri/.gitignore | 10 + .../examples/multiwindow/src-tauri/Cargo.toml | 42 +++ .../multiwindow/src-tauri/icons/128x128.png | Bin 0 -> 15920 bytes .../src-tauri/icons/128x128@2x.png | Bin 0 -> 37546 bytes .../multiwindow/src-tauri/icons/32x32.png | Bin 0 -> 2400 bytes .../src-tauri/icons/Square107x107Logo.png | Bin 0 -> 12659 bytes .../src-tauri/icons/Square142x142Logo.png | Bin 0 -> 18057 bytes .../src-tauri/icons/Square150x150Logo.png | Bin 0 -> 19312 bytes .../src-tauri/icons/Square284x284Logo.png | Bin 0 -> 41714 bytes .../src-tauri/icons/Square30x30Logo.png | Bin 0 -> 2132 bytes .../src-tauri/icons/Square310x310Logo.png | Bin 0 -> 45901 bytes .../src-tauri/icons/Square44x44Logo.png | Bin 0 -> 3763 bytes .../src-tauri/icons/Square71x71Logo.png | Bin 0 -> 7380 bytes .../src-tauri/icons/Square89x89Logo.png | Bin 0 -> 9831 bytes .../multiwindow/src-tauri/icons/StoreLogo.png | Bin 0 -> 4552 bytes .../multiwindow/src-tauri/icons/icon.icns | Bin 0 -> 512919 bytes .../multiwindow/src-tauri/icons/icon.ico | Bin 0 -> 57594 bytes .../multiwindow/src-tauri/icons/icon.png | Bin 0 -> 90074 bytes .../multiwindow/src-tauri/rustfmt.toml | 14 + .../multiwindow/src-tauri/src/build.rs | 16 + .../multiwindow/src-tauri/src/main.rs | 25 ++ .../multiwindow/src-tauri/tauri.conf.json | 44 +++ tauri/examples/multiwindow/yarn.lock | 4 + tauri/src/app.rs | 44 +-- tauri/src/{ => app}/event.rs | 32 +- tauri/src/app/runner.rs | 195 ++++++------ tauri/src/app/webview_manager.rs | 90 ++++++ tauri/src/endpoints.rs | 150 +++++---- tauri/src/endpoints/dialog.rs | 12 +- tauri/src/endpoints/event.rs | 6 +- tauri/src/endpoints/file_system.rs | 42 +-- tauri/src/endpoints/http.rs | 4 +- tauri/src/endpoints/notification.rs | 12 +- tauri/src/endpoints/path.rs | 4 +- tauri/src/endpoints/salt.rs | 6 +- tauri/src/error.rs | 3 + tauri/src/lib.rs | 57 ++-- tauri/src/plugin.rs | 26 +- tauri/src/webview.rs | 87 +++++- tauri/src/webview/wry.rs | 110 ++++--- tauri/test/fixture/src-tauri/tauri.conf.json | 4 +- 63 files changed, 1334 insertions(+), 509 deletions(-) create mode 100644 .changes/event.md create mode 100644 .changes/multiwindow.md create mode 100644 tauri/examples/multiwindow/dist/__tauri.js create mode 100644 tauri/examples/multiwindow/dist/index.html create mode 100644 tauri/examples/multiwindow/package.json create mode 100644 tauri/examples/multiwindow/src-tauri/.gitignore create mode 100644 tauri/examples/multiwindow/src-tauri/Cargo.toml create mode 100644 tauri/examples/multiwindow/src-tauri/icons/128x128.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/128x128@2x.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/32x32.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/Square107x107Logo.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/Square142x142Logo.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/Square150x150Logo.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/Square284x284Logo.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/Square30x30Logo.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/Square310x310Logo.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/Square44x44Logo.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/Square71x71Logo.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/Square89x89Logo.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/StoreLogo.png create mode 100644 tauri/examples/multiwindow/src-tauri/icons/icon.icns create mode 100644 tauri/examples/multiwindow/src-tauri/icons/icon.ico create mode 100644 tauri/examples/multiwindow/src-tauri/icons/icon.png create mode 100644 tauri/examples/multiwindow/src-tauri/rustfmt.toml create mode 100644 tauri/examples/multiwindow/src-tauri/src/build.rs create mode 100644 tauri/examples/multiwindow/src-tauri/src/main.rs create mode 100644 tauri/examples/multiwindow/src-tauri/tauri.conf.json create mode 100644 tauri/examples/multiwindow/yarn.lock rename tauri/src/{ => app}/event.rs (86%) create mode 100644 tauri/src/app/webview_manager.rs diff --git a/.changes/event.md b/.changes/event.md new file mode 100644 index 000000000..4318942a1 --- /dev/null +++ b/.changes/event.md @@ -0,0 +1,5 @@ +--- +"tauri": minor +--- + +The `tauri::event` module has been moved to a Webview manager API. diff --git a/.changes/multiwindow.md b/.changes/multiwindow.md new file mode 100644 index 000000000..6177d2e83 --- /dev/null +++ b/.changes/multiwindow.md @@ -0,0 +1,5 @@ +--- +"tauri": minor +--- + +Added support to multiple windows. diff --git a/README.md b/README.md index 21875339c..3128a24e3 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ If you are interested in making a tauri-app, please visit the [documentation web | Multithreading | Yes | No | | Bytecode Delivery | Yes | No | | Can Render PDF | Yes | No | -| Multiple Windows | Soon | Yes | +| Multiple Windows | Yes | Yes | | Auto Updater | Soon | Yes (2) | | Cross Platform | Yes | Yes | | Custom App Icon | Yes | Yes | @@ -116,7 +116,6 @@ If you are interested in making a tauri-app, please visit the [documentation web | Localhost Server | Yes | Yes | | No localhost option | Yes | No | | Desktop Tray | Soon | Yes | -| Splashscreen | Yes | Yes | | Sidecar Binaries | Yes | No | #### Notes diff --git a/cli/core/src/helpers/config.rs b/cli/core/src/helpers/config.rs index 42d8c60f9..9bb235e09 100644 --- a/cli/core/src/helpers/config.rs +++ b/cli/core/src/helpers/config.rs @@ -21,53 +21,6 @@ fn config_handle() -> &'static ConfigHandle { &CONFING_HANDLE } -/// The window configuration object. -#[derive(PartialEq, Clone, Deserialize, Serialize, Debug)] -#[serde(tag = "window", rename_all = "camelCase")] -pub struct WindowConfig { - /// The window width. - #[serde(default = "default_width")] - pub width: i32, - /// The window height. - #[serde(default = "default_height")] - pub height: i32, - /// Whether the window is resizable or not. - #[serde(default = "default_resizable")] - pub resizable: bool, - /// The window title. - #[serde(default = "default_title")] - pub title: String, - /// Whether the window starts as fullscreen or not. - #[serde(default)] - pub fullscreen: bool, -} - -fn default_width() -> i32 { - 800 -} - -fn default_height() -> i32 { - 600 -} - -fn default_resizable() -> bool { - true -} - -fn default_title() -> String { - "Tauri App".to_string() -} - -fn default_window() -> WindowConfig { - WindowConfig { - width: default_width(), - height: default_height(), - resizable: default_resizable(), - title: default_title(), - fullscreen: false, - } -} - /// The embedded server port. #[derive(PartialEq, Clone, Debug, Deserialize, Serialize)] pub enum Port { @@ -312,9 +265,6 @@ fn default_bundle() -> BundleConfig { #[derive(PartialEq, Clone, Deserialize, Serialize, Debug)] #[serde(tag = "tauri", rename_all = "camelCase")] pub struct TauriConfig { - /// The window configuration. - #[serde(default = "default_window")] - pub window: WindowConfig, /// The embeddedServer configuration. #[serde(default = "default_embedded_server")] pub embedded_server: EmbeddedServerConfig, @@ -370,7 +320,6 @@ pub struct Config { fn default_tauri() -> TauriConfig { TauriConfig { - window: default_window(), embedded_server: default_embedded_server(), cli: None, bundle: default_bundle(), diff --git a/cli/tauri.js/src/types/config.schema.json b/cli/tauri.js/src/types/config.schema.json index 9572f4781..5610d83a9 100644 --- a/cli/tauri.js/src/types/config.schema.json +++ b/cli/tauri.js/src/types/config.schema.json @@ -420,36 +420,64 @@ }, "type": "object" }, - "window": { - "additionalProperties": false, - "defaultProperties": [], - "properties": { - "fullscreen": { - "type": "boolean" - }, - "height": { - "type": "number" - }, - "resizable": { - "type": "boolean" - }, - "title": { - "type": "string" - }, - "width": { - "type": "number" - } + "windows": { + "additionalItems": { + "anyOf": [ + { + "additionalProperties": false, + "defaultProperties": [], + "properties": { + "fullscreen": { + "type": "boolean" + }, + "height": { + "type": "number" + }, + "resizable": { + "type": "boolean" + }, + "title": { + "type": "string" + }, + "width": { + "type": "number" + } + }, + "required": ["title"], + "type": "object" + } + ] }, - "required": ["title"], - "type": "object" + "items": [ + { + "additionalProperties": false, + "defaultProperties": [], + "properties": { + "fullscreen": { + "type": "boolean" + }, + "height": { + "type": "number" + }, + "resizable": { + "type": "boolean" + }, + "title": { + "type": "string" + }, + "width": { + "type": "number" + } + }, + "required": ["title"], + "type": "object" + } + ], + "minItems": 1, + "type": "array" } }, - "required": [ - "allowlist", - "bundle", - "security", - "window" - ], + "required": ["allowlist", "bundle", "security", "windows"], "type": "object" }, "verbose": { diff --git a/cli/tauri.js/src/types/config.ts b/cli/tauri.js/src/types/config.ts index 2255ebaec..50a54108e 100644 --- a/cli/tauri.js/src/types/config.ts +++ b/cli/tauri.js/src/types/config.ts @@ -277,13 +277,15 @@ export interface TauriConfig { all: boolean [index: string]: boolean } - window: { - title: string - width?: number - height?: number - resizable?: boolean - fullscreen?: boolean - } + windows: [ + { + title: string + width?: number + height?: number + resizable?: boolean + fullscreen?: boolean + } + ] security: { csp?: string } diff --git a/cli/tauri.js/src/types/config.validator.ts b/cli/tauri.js/src/types/config.validator.ts index 8250d9ea0..676f64d7a 100644 --- a/cli/tauri.js/src/types/config.validator.ts +++ b/cli/tauri.js/src/types/config.validator.ts @@ -472,31 +472,64 @@ export const TauriConfigSchema = { }, type: 'object' }, - window: { - additionalProperties: false, - defaultProperties: [], - properties: { - fullscreen: { - type: 'boolean' - }, - height: { - type: 'number' - }, - resizable: { - type: 'boolean' - }, - title: { - type: 'string' - }, - width: { - type: 'number' - } + windows: { + additionalItems: { + anyOf: [ + { + additionalProperties: false, + defaultProperties: [], + properties: { + fullscreen: { + type: 'boolean' + }, + height: { + type: 'number' + }, + resizable: { + type: 'boolean' + }, + title: { + type: 'string' + }, + width: { + type: 'number' + } + }, + required: ['title'], + type: 'object' + } + ] }, - required: ['title'], - type: 'object' + items: [ + { + additionalProperties: false, + defaultProperties: [], + properties: { + fullscreen: { + type: 'boolean' + }, + height: { + type: 'number' + }, + resizable: { + type: 'boolean' + }, + title: { + type: 'string' + }, + width: { + type: 'number' + } + }, + required: ['title'], + type: 'object' + } + ], + minItems: 1, + type: 'array' } }, - required: ['allowlist', 'bundle', 'security', 'window'], + required: ['allowlist', 'bundle', 'security', 'windows'], type: 'object' }, verbose: { diff --git a/cli/tauri.js/test/jest/fixtures/app/src-tauri/src/main.rs b/cli/tauri.js/test/jest/fixtures/app/src-tauri/src/main.rs index 3f7bf429b..65567f408 100644 --- a/cli/tauri.js/test/jest/fixtures/app/src-tauri/src/main.rs +++ b/cli/tauri.js/test/jest/fixtures/app/src-tauri/src/main.rs @@ -7,19 +7,21 @@ struct Context; fn main() { tauri::AppBuilder::::new() - .setup(|dispatcher, _| async move { - let mut dispatcher_ = dispatcher.clone(); + .setup(|webview_manager| async move { + let mut webview_manager_ = webview_manager.clone(); tauri::event::listen(String::from("hello"), move |_| { tauri::event::emit( - &mut dispatcher_, + &webview_manager_, String::from("reply"), Some("{ msg: 'TEST' }".to_string()), ) .unwrap(); }); - dispatcher.eval("window.onTauriInit && window.onTauriInit()"); + webview_manager + .current_webview() + .eval("window.onTauriInit && window.onTauriInit()"); }) - .invoke_handler(|dispatcher, arg| async move { + .invoke_handler(|webview_manager, arg| async move { use cmd::Cmd::*; match serde_json::from_str(&arg) { Err(e) => Err(e.to_string()), diff --git a/cli/tauri.js/test/jest/fixtures/app/src-tauri/tauri.conf.json b/cli/tauri.js/test/jest/fixtures/app/src-tauri/tauri.conf.json index 1595fc58c..298aa0e37 100644 --- a/cli/tauri.js/test/jest/fixtures/app/src-tauri/tauri.conf.json +++ b/cli/tauri.js/test/jest/fixtures/app/src-tauri/tauri.conf.json @@ -17,8 +17,10 @@ ], "identifier": "fixture.app" }, - "window": { - "title": "Fixture" - } + "windows": [ + { + "title": "Fixture" + } + ] } } diff --git a/tauri-utils/src/config.rs b/tauri-utils/src/config.rs index 325f3c5d4..9732be21e 100644 --- a/tauri-utils/src/config.rs +++ b/tauri-utils/src/config.rs @@ -6,16 +6,75 @@ use serde_json::Value as JsonValue; use std::collections::HashMap; +/// The window webview URL options. +#[derive(PartialEq, Debug, Clone)] +pub enum WindowUrl { + /// The app's index URL. + App, + /// A custom URL. + Custom(String), +} + +impl Default for WindowUrl { + fn default() -> Self { + Self::App + } +} + +impl<'de> Deserialize<'de> for WindowUrl { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct StringVisitor; + impl<'de> Visitor<'de> for StringVisitor { + type Value = WindowUrl; + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("a string representing an url") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + if v.to_lowercase() == "app" { + Ok(WindowUrl::App) + } else { + Ok(WindowUrl::Custom(v.to_string())) + } + } + } + deserializer.deserialize_str(StringVisitor) + } +} + /// The window configuration object. -#[derive(PartialEq, Deserialize, Debug)] -#[serde(tag = "window", rename_all = "camelCase")] +#[derive(PartialEq, Deserialize, Debug, Clone)] pub struct WindowConfig { + #[serde(default = "default_window_label")] + /// The window identifier. + pub label: String, + /// The window webview URL. + #[serde(default)] + pub url: WindowUrl, + /// The horizontal position of the window's top left corner + pub x: Option, + /// The vertical position of the window's top left corner + pub y: Option, /// The window width. #[serde(default = "default_width")] - pub width: i32, + pub width: f64, /// The window height. #[serde(default = "default_height")] - pub height: i32, + pub height: f64, + /// The min window width. + pub min_width: Option, + /// The min window height. + pub min_height: Option, + /// The max window width. + pub max_width: Option, + /// The max window height. + pub max_height: Option, /// Whether the window is resizable or not. #[serde(default = "default_resizable")] pub resizable: bool, @@ -25,20 +84,47 @@ pub struct WindowConfig { /// Whether the window starts as fullscreen or not. #[serde(default)] pub fullscreen: bool, + /// Whether the window is transparent or not. + #[serde(default)] + pub transparent: bool, + /// Whether the window is maximized or not. + #[serde(default)] + pub maximized: bool, + /// Whether the window is visible or not. + #[serde(default = "default_visible")] + pub visible: bool, + /// Whether the window should have borders and bars. + #[serde(default = "default_decorations")] + pub decorations: bool, + /// Whether the window should always be on top of other windows. + #[serde(default)] + pub always_on_top: bool, } -fn default_width() -> i32 { - 800 +fn default_window_label() -> String { + "main".to_string() } -fn default_height() -> i32 { - 600 +fn default_width() -> f64 { + 800f64 +} + +fn default_height() -> f64 { + 600f64 } fn default_resizable() -> bool { true } +fn default_visible() -> bool { + true +} + +fn default_decorations() -> bool { + true +} + fn default_title() -> String { "Tauri App".to_string() } @@ -46,11 +132,24 @@ fn default_title() -> String { impl Default for WindowConfig { fn default() -> Self { Self { + label: default_window_label(), + url: WindowUrl::App, + x: None, + y: None, width: default_width(), height: default_height(), + min_width: None, + min_height: None, + max_width: None, + max_height: None, resizable: default_resizable(), title: default_title(), fullscreen: false, + transparent: false, + maximized: false, + visible: default_visible(), + decorations: default_decorations(), + always_on_top: false, } } } @@ -335,13 +434,17 @@ impl Default for BundleConfig { } } +fn default_window_config() -> Vec { + vec![Default::default()] +} + /// The Tauri configuration object. #[derive(PartialEq, Deserialize, Debug)] #[serde(tag = "tauri", rename_all = "camelCase")] pub struct TauriConfig { /// The window configuration. - #[serde(default)] - pub window: WindowConfig, + #[serde(default = "default_window_config")] + pub windows: Vec, /// The embeddedServer configuration. #[serde(default)] pub embedded_server: EmbeddedServerConfig, @@ -356,7 +459,7 @@ pub struct TauriConfig { impl Default for TauriConfig { fn default() -> Self { Self { - window: WindowConfig::default(), + windows: default_window_config(), embedded_server: EmbeddedServerConfig::default(), cli: None, bundle: BundleConfig::default(), @@ -441,7 +544,7 @@ mod test { // get default embedded server let de_server = EmbeddedServerConfig::default(); // get default window - let d_window = WindowConfig::default(); + let d_windows = default_window_config(); // get default title let d_title = default_title(); // get default bundle @@ -449,13 +552,26 @@ mod test { // create a tauri config. let tauri = TauriConfig { - window: WindowConfig { - width: 800, - height: 600, + windows: vec![WindowConfig { + label: "main".to_string(), + url: WindowUrl::App, + x: None, + y: None, + width: 800f64, + height: 600f64, + min_width: None, + min_height: None, + max_width: None, + max_height: None, resizable: true, title: String::from("Tauri App"), fullscreen: false, - }, + transparent: false, + maximized: false, + visible: true, + decorations: true, + always_on_top: false, + }], embedded_server: EmbeddedServerConfig { host: String::from("http://127.0.0.1"), port: Port::Random, @@ -479,7 +595,7 @@ mod test { assert_eq!(de_server, tauri.embedded_server); assert_eq!(d_bundle, tauri.bundle); assert_eq!(d_path, String::from("http://localhost:8080")); - assert_eq!(d_title, tauri.window.title); - assert_eq!(d_window, tauri.window); + assert_eq!(d_title, tauri.windows[0].title); + assert_eq!(d_windows, tauri.windows); } } diff --git a/tauri/Cargo.toml b/tauri/Cargo.toml index 996e3c012..f5fee70bc 100644 --- a/tauri/Cargo.toml +++ b/tauri/Cargo.toml @@ -32,7 +32,7 @@ thiserror = "1.0.23" once_cell = "1.5.2" tauri-api = { version = "0.7.5", path = "../tauri-api" } tauri-macros = { version = "0.1", path = "../tauri-macros" } -wry = { git = "https://github.com/tauri-apps/wry", rev = "42f4f2133f7921ed5adc47908787094da8abeac5" } +wry = { git = "https://github.com/tauri-apps/wry", rev = "f4edf89de5dc40b77a94f6b94fcaf76fdac6bbf4" } [target."cfg(target_os = \"windows\")".dependencies] runas = "0.2" @@ -74,3 +74,7 @@ notification = [ "tauri-api/notification" ] [[example]] name = "communication" path = "examples/communication/src-tauri/src/main.rs" + +[[example]] +name = "multiwindow" +path = "examples/multiwindow/src-tauri/src/main.rs" diff --git a/tauri/examples/api/public/index.html b/tauri/examples/api/public/index.html index 0f9945386..a3b0fa0f9 100644 --- a/tauri/examples/api/public/index.html +++ b/tauri/examples/api/public/index.html @@ -9,7 +9,6 @@ - diff --git a/tauri/examples/api/src-tauri/Cargo.lock b/tauri/examples/api/src-tauri/Cargo.lock index 4170460e7..f43b421c8 100644 --- a/tauri/examples/api/src-tauri/Cargo.lock +++ b/tauri/examples/api/src-tauri/Cargo.lock @@ -3094,11 +3094,12 @@ dependencies = [ [[package]] name = "wry" version = "0.4.1" -source = "git+https://github.com/tauri-apps/wry?rev=42f4f2133f7921ed5adc47908787094da8abeac5#42f4f2133f7921ed5adc47908787094da8abeac5" +source = "git+https://github.com/tauri-apps/wry?rev=f4edf89de5dc40b77a94f6b94fcaf76fdac6bbf4#f4edf89de5dc40b77a94f6b94fcaf76fdac6bbf4" dependencies = [ "cc", "cocoa", "core-graphics 0.22.2", + "gdk", "gio", "glib", "gtk", diff --git a/tauri/examples/api/src-tauri/src/main.rs b/tauri/examples/api/src-tauri/src/main.rs index 2c4aa046a..3ac4a8914 100644 --- a/tauri/examples/api/src-tauri/src/main.rs +++ b/tauri/examples/api/src-tauri/src/main.rs @@ -17,19 +17,21 @@ struct Context; fn main() { tauri::AppBuilder::::new() - .setup(|dispatcher, _source| async move { - let mut dispatcher = dispatcher.clone(); - tauri::event::listen(String::from("js-event"), move |msg| { + .setup(|webview_manager| async move { + let dispatcher = webview_manager.current_webview().unwrap(); + let dispatcher_ = dispatcher.clone(); + dispatcher.listen("js-event", move |msg| { println!("got js-event with message '{:?}'", msg); let reply = Reply { data: "something else".to_string(), }; - tauri::event::emit(&mut dispatcher, String::from("rust-event"), Some(reply)) + dispatcher_ + .emit("rust-event", Some(reply)) .expect("failed to emit"); }); }) - .invoke_handler(|mut dispatcher, arg| async move { + .invoke_handler(|webview_manager, arg| async move { use cmd::Cmd::*; match serde_json::from_str(&arg) { Err(e) => Err(e.to_string()), @@ -47,7 +49,7 @@ fn main() { // tauri::execute_promise is a helper for APIs that uses the tauri.promisified JS function // so you can easily communicate between JS and Rust with promises tauri::execute_promise( - &mut dispatcher, + &webview_manager, async move { println!("{} {:?}", endpoint, body); // perform an async operation here diff --git a/tauri/examples/api/src-tauri/tauri.conf.json b/tauri/examples/api/src-tauri/tauri.conf.json index f0e9add29..41d39d9d2 100644 --- a/tauri/examples/api/src-tauri/tauri.conf.json +++ b/tauri/examples/api/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "build": { "distDir": "../public", - "devPath": "../public", + "devPath": "http://localhost:5000", "withGlobalTauri": true, "beforeBuildCommand": "yarn build", "beforeDevCommand": "yarn dev" @@ -57,9 +57,9 @@ "allowlist": { "all": true }, - "window": { + "windows": [{ "title": "Tauri API Validation" - }, + }], "security": { "csp": "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'" } diff --git a/tauri/examples/api/src/components/Window.svelte b/tauri/examples/api/src/components/Window.svelte index 8b4923fd6..d7550b801 100644 --- a/tauri/examples/api/src/components/Window.svelte +++ b/tauri/examples/api/src/components/Window.svelte @@ -2,18 +2,19 @@ import { setTitle, open } from "@tauri-apps/api/window"; let urlValue = "https://tauri.studio"; + let windowTitle = 'Awesome Tauri Example!'; function openUrl() { open(urlValue); } function setWindowTitle() { - setTitle(urlValue); + setTitle(windowTitle); }
- +
diff --git a/tauri/examples/communication/dist/communication.js b/tauri/examples/communication/dist/communication.js index 71077fabf..8d299fb81 100644 --- a/tauri/examples/communication/dist/communication.js +++ b/tauri/examples/communication/dist/communication.js @@ -1,4 +1,5 @@ document.getElementById("log").addEventListener("click", function () { + console.log('log') window.__TAURI__.tauri.invoke({ cmd: "logOperation", event: "tauri-click", diff --git a/tauri/examples/communication/src-tauri/src/main.rs b/tauri/examples/communication/src-tauri/src/main.rs index 16d8a6d6e..834e65fc2 100644 --- a/tauri/examples/communication/src-tauri/src/main.rs +++ b/tauri/examples/communication/src-tauri/src/main.rs @@ -18,19 +18,21 @@ struct Context; fn main() { tauri::AppBuilder::::new() - .setup(|dispatcher, _source| async move { - let mut dispatcher = dispatcher.clone(); - tauri::event::listen(String::from("js-event"), move |msg| { + .setup(|webview_manager| async move { + let current_webview = webview_manager.current_webview().unwrap().clone(); + let current_webview_ = current_webview.clone(); + current_webview.listen(String::from("js-event"), move |msg| { println!("got js-event with message '{:?}'", msg); let reply = Reply { data: "something else".to_string(), }; - tauri::event::emit(&mut dispatcher, String::from("rust-event"), Some(reply)) + current_webview_ + .emit(String::from("rust-event"), Some(reply)) .expect("failed to emit"); }); }) - .invoke_handler(|mut dispatcher, arg| async move { + .invoke_handler(|webview_manager, arg| async move { use cmd::Cmd::*; match serde_json::from_str(&arg) { Err(e) => Err(e.to_string()), @@ -48,7 +50,7 @@ fn main() { // tauri::execute_promise is a helper for APIs that uses the tauri.promisified JS function // so you can easily communicate between JS and Rust with promises tauri::execute_promise( - &mut dispatcher, + &webview_manager, async move { println!("{} {:?}", endpoint, body); // perform an async operation here diff --git a/tauri/examples/communication/src-tauri/tauri.conf.json b/tauri/examples/communication/src-tauri/tauri.conf.json index 083cd7e31..c1848b501 100644 --- a/tauri/examples/communication/src-tauri/tauri.conf.json +++ b/tauri/examples/communication/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "build": { "distDir": "../dist", - "devPath": "../dist", + "devPath": "http://localhost:4000", "withGlobalTauri": true }, "ctx": {}, @@ -56,9 +56,10 @@ "allowlist": { "all": true }, - "window": { - "title": "Tauri API Validation" - }, + "windows": [{ + "title": "Tauri API Validation", + "resizable": false + }], "security": { "csp": "default-src blob: data: filesystem: ws: http: https: 'unsafe-eval' 'unsafe-inline'" } diff --git a/tauri/examples/multiwindow/dist/__tauri.js b/tauri/examples/multiwindow/dist/__tauri.js new file mode 100644 index 000000000..5ce41abbf --- /dev/null +++ b/tauri/examples/multiwindow/dist/__tauri.js @@ -0,0 +1,287 @@ +function ownKeys(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function _objectSpread(e){for(var t=1;t=0;--a){var i=this.tryEntries[a],u=i.completion;if("root"===i.tryLoc)return o("end");if(i.tryLoc<=this.prev){var c=n.call(i,"catchLoc"),s=n.call(i,"finallyLoc");if(c&&s){if(this.prev=0;--r){var o=this.tryEntries[r];if(o.tryLoc<=this.prev&&n.call(o,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),j(r),m}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var o=n.arg;j(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(e,r,n){return this.delegate={iterator:O(e),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=t),m}},e}("object"===("undefined"==typeof module?"undefined":_typeof(module))?module.exports:{});try{regeneratorRuntime=t}catch(e){Function("r","regeneratorRuntime = r")(t)}function r(e){for(var t=void 0,r=e[0],n=1;n1&&void 0!==arguments[1]&&arguments[1],n=o();return Object.defineProperty(window,n,{value:function(o){return t&&Reflect.deleteProperty(window,n),r([e,"optionalCall",function(e){return e(o)}])},writable:!1,configurable:!0}),n}function u(e){return c.apply(this,arguments)}function c(){return(c=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,new Promise((function(e,r){var n=i((function(t){e(t),Reflect.deleteProperty(window,o)}),!0),o=i((function(e){r(e),Reflect.deleteProperty(window,n)}),!0);a(_objectSpread({callback:n,error:o},t))}));case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var s=Object.freeze({__proto__:null,invoke:a,transformCallback:i,promisified:u});function p(){return(p=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"cliMatches"});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var f=Object.freeze({__proto__:null,getMatches:function(){return p.apply(this,arguments)}});function l(){return(l=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t,r=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(t=r.length>0&&void 0!==r[0]?r[0]:{})&&Object.freeze(t),e.next=4,u({cmd:"openDialog",options:t});case 4:return e.abrupt("return",e.sent);case 5:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function h(){return(h=_asyncToGenerator(regeneratorRuntime.mark((function e(){var t,r=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(t=r.length>0&&void 0!==r[0]?r[0]:{})&&Object.freeze(t),e.next=4,u({cmd:"saveDialog",options:t});case 4:return e.abrupt("return",e.sent);case 5:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var y=Object.freeze({__proto__:null,open:function(){return l.apply(this,arguments)},save:function(){return h.apply(this,arguments)}});var m,d=Object.freeze({__proto__:null,listen:function(e,t){var r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];a({cmd:"listen",event:e,handler:i(t,r),once:r})},emit:function(e,t){a({cmd:"emit",event:e,payload:t})}});function v(){return(v=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.next=3,u({cmd:"readTextFile",path:t,options:r});case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function g(){return(g=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.next=3,u({cmd:"readBinaryFile",path:t,options:r});case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function w(){return(w=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(r=n.length>1&&void 0!==n[1]?n[1]:{})&&Object.freeze(r),"object"===_typeof(t)&&Object.freeze(t),e.next=5,u({cmd:"writeFile",path:t.path,contents:t.contents,options:r});case 5:return e.abrupt("return",e.sent);case 6:case"end":return e.stop()}}),e)})))).apply(this,arguments)}!function(e){e[e.Audio=1]="Audio";e[e.Cache=2]="Cache";e[e.Config=3]="Config";e[e.Data=4]="Data";e[e.LocalData=5]="LocalData";e[e.Desktop=6]="Desktop";e[e.Document=7]="Document";e[e.Download=8]="Download";e[e.Executable=9]="Executable";e[e.Font=10]="Font";e[e.Home=11]="Home";e[e.Picture=12]="Picture";e[e.Public=13]="Public";e[e.Runtime=14]="Runtime";e[e.Template=15]="Template";e[e.Video=16]="Video";e[e.Resource=17]="Resource";e[e.App=18]="App"}(m||(m={}));var b=65536;function x(e){var t=function(e){if(e.length1&&void 0!==n[1]?n[1]:{})&&Object.freeze(r),"object"===_typeof(t)&&Object.freeze(t),e.next=5,u({cmd:"writeBinaryFile",path:t.path,contents:x(t.contents),options:r});case 5:return e.abrupt("return",e.sent);case 6:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function R(){return(R=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.next=3,u({cmd:"readDir",path:t,options:r});case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function T(){return(T=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.next=3,u({cmd:"createDir",path:t,options:r});case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function P(){return(P=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.next=3,u({cmd:"removeDir",path:t,options:r});case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function k(){return(k=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){var n,o=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=o.length>2&&void 0!==o[2]?o[2]:{},e.next=3,u({cmd:"copyFile",source:t,destination:r,options:n});case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function j(){return(j=_asyncToGenerator(regeneratorRuntime.mark((function e(t){var r,n=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=n.length>1&&void 0!==n[1]?n[1]:{},e.next=3,u({cmd:"removeFile",path:t,options:r});case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function G(){return(G=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){var n,o=arguments;return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=o.length>2&&void 0!==o[2]?o[2]:{},e.next=3,u({cmd:"renameFile",oldPath:t,newPath:r,options:n});case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var O=Object.freeze({__proto__:null,get BaseDirectory(){return m},get Dir(){return m},readTextFile:function(e){return v.apply(this,arguments)},readBinaryFile:function(e){return g.apply(this,arguments)},writeFile:function(e){return w.apply(this,arguments)},writeBinaryFile:function(e){return _.apply(this,arguments)},readDir:function(e){return R.apply(this,arguments)},createDir:function(e){return T.apply(this,arguments)},removeDir:function(e){return P.apply(this,arguments)},copyFile:function(e,t){return k.apply(this,arguments)},removeFile:function(e){return j.apply(this,arguments)},renameFile:function(e,t){return G.apply(this,arguments)}});function D(){return(D=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.App});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function E(){return(E=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Audio});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function L(){return(L=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Cache});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function S(){return(S=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Config});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function F(){return(F=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Data});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function A(){return(A=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Desktop});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function N(){return(N=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Document});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function z(){return(z=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Download});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function C(){return(C=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Executable});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function B(){return(B=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Font});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function I(){return(I=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Home});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function H(){return(H=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.LocalData});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function M(){return(M=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Picture});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function q(){return(q=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Public});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function K(){return(K=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Resource});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function U(){return(U=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Runtime});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function V(){return(V=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Template});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function J(){return(J=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:"",directory:m.Video});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Y(){return(Y=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"resolvePath",path:t,directory:r});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var Q,W,X=Object.freeze({__proto__:null,appDir:function(){return D.apply(this,arguments)},audioDir:function(){return E.apply(this,arguments)},cacheDir:function(){return L.apply(this,arguments)},configDir:function(){return S.apply(this,arguments)},dataDir:function(){return F.apply(this,arguments)},desktopDir:function(){return A.apply(this,arguments)},documentDir:function(){return N.apply(this,arguments)},downloadDir:function(){return z.apply(this,arguments)},executableDir:function(){return C.apply(this,arguments)},fontDir:function(){return B.apply(this,arguments)},homeDir:function(){return I.apply(this,arguments)},localDataDir:function(){return H.apply(this,arguments)},pictureDir:function(){return M.apply(this,arguments)},publicDir:function(){return q.apply(this,arguments)},resourceDir:function(){return K.apply(this,arguments)},runtimeDir:function(){return U.apply(this,arguments)},templateDir:function(){return V.apply(this,arguments)},videoDir:function(){return J.apply(this,arguments)},resolvePath:function(e,t){return Y.apply(this,arguments)}});function Z(e){return $.apply(this,arguments)}function $(){return($=_asyncToGenerator(regeneratorRuntime.mark((function e(t){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,u({cmd:"httpRequest",options:t});case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ee(){return(ee=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Z(_objectSpread({method:"GET",url:t},r));case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function te(){return(te=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Z(_objectSpread({method:"POST",url:t,body:r},n));case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function re(){return(re=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r,n){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Z(_objectSpread({method:"PUT",url:t,body:r},n));case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function ne(){return(ne=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Z(_objectSpread({method:"PATCH",url:t},r));case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function oe(){return(oe=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,Z(_objectSpread({method:"DELETE",url:t},r));case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}!function(e){e[e.JSON=1]="JSON";e[e.Text=2]="Text";e[e.Binary=3]="Binary"}(Q||(Q={})),function(e){e[e.Form=1]="Form";e[e.File=2]="File";e[e.Auto=3]="Auto"}(W||(W={}));var ae={request:Z,get:function(e,t){return ee.apply(this,arguments)},post:function(e,t,r){return te.apply(this,arguments)},put:function(e,t,r){return re.apply(this,arguments)},patch:function(e,t){return ne.apply(this,arguments)},delete:function(e,t){return oe.apply(this,arguments)},ResponseType:Q,BodyType:W};function ie(){return(ie=_asyncToGenerator(regeneratorRuntime.mark((function e(t,r){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return"object"===_typeof(r)&&Object.freeze(r),e.next=3,u({cmd:"execute",command:t,args:"string"==typeof r?[r]:r});case 3:return e.abrupt("return",e.sent);case 4:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var ue=Object.freeze({__proto__:null,execute:function(e,t){return ie.apply(this,arguments)}});var ce=Object.freeze({__proto__:null,setTitle:function(e){a({cmd:"setTitle",title:e})},open:function(e){a({cmd:"open",uri:e})}});function se(){return(se=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if("default"===window.Notification.permission){e.next=4;break}return e.next=3,Promise.resolve("granted"===window.Notification.permission);case 3:return e.abrupt("return",e.sent);case 4:return e.next=6,u({cmd:"isNotificationPermissionGranted"});case 6:return e.abrupt("return",e.sent);case 7:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function pe(){return(pe=_asyncToGenerator(regeneratorRuntime.mark((function e(){return regeneratorRuntime.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,window.Notification.requestPermission();case 2:return e.abrupt("return",e.sent);case 3:case"end":return e.stop()}}),e)})))).apply(this,arguments)}var fe=Object.freeze({__proto__:null,sendNotification:function(e){"string"==typeof e?new window.Notification(e):new window.Notification(e.title,e)},requestPermission:function(){return pe.apply(this,arguments)},isPermissionGranted:function(){return se.apply(this,arguments)}});e.cli=f,e.dialog=y,e.event=d,e.fs=O,e.http=ae,e.notification=fe,e.path=X,e.process=ue,e.tauri=s,e.window=ce,Object.defineProperty(e,"__esModule",{value:!0})})); + + +// polyfills +if (!String.prototype.startsWith) { + String.prototype.startsWith = function (searchString, position) { + position = position || 0 + return this.substr(position, searchString.length) === searchString + } +} + +;(function () { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1) + } + + var uid = function () { + return ( + s4() + + s4() + + '-' + + s4() + + '-' + + s4() + + '-' + + s4() + + '-' + + s4() + + s4() + + s4() + ) + } + + function ownKeys(object, enumerableOnly) { + var keys = Object.keys(object) + if (Object.getOwnPropertySymbols) { + var symbols = Object.getOwnPropertySymbols(object) + if (enumerableOnly) + symbols = symbols.filter(function (sym) { + return Object.getOwnPropertyDescriptor(object, sym).enumerable + }) + keys.push.apply(keys, symbols) + } + return keys + } + + function _objectSpread(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i] != null ? arguments[i] : {} + if (i % 2) { + ownKeys(source, true).forEach(function (key) { + _defineProperty(target, key, source[key]) + }) + } else if (Object.getOwnPropertyDescriptors) { + Object.defineProperties( + target, + Object.getOwnPropertyDescriptors(source) + ) + } else { + ownKeys(source).forEach(function (key) { + Object.defineProperty( + target, + key, + Object.getOwnPropertyDescriptor(source, key) + ) + }) + } + } + return target + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }) + } else { + obj[key] = value + } + return obj + } + + if (!window.__TAURI__) { + window.__TAURI__ = {} + } + + window.__TAURI__.transformCallback = function transformCallback( + callback, + once + ) { + var identifier = uid() + + window[identifier] = function (result) { + if (once) { + delete window[identifier] + } + + return callback && callback(result) + } + + return identifier + } + + window.__TAURI__.promisified = function promisified(args) { + var _this = this + + return new Promise(function (resolve, reject) { + var callback = _this.transformCallback(function (r) { + resolve(r) + delete window[error] + }, true) + var error = _this.transformCallback(function (e) { + reject(e) + delete window[callback] + }, true) + + if (window.__TAURI_INVOKE_HANDLER__) { + window.__TAURI_INVOKE_HANDLER__( + JSON.stringify(_objectSpread( + { + callback: callback, + error: error + }, + args + )) + ) + } else { + window.addEventListener('DOMContentLoaded', function () { + window.__TAURI_INVOKE_HANDLER__( + JSON.stringify(_objectSpread( + { + callback: callback, + error: error + }, + args + )) + ) + }) + } + }) + } + + // open links with the Tauri API + function __openLinks() { + document.querySelector('body').addEventListener( + 'click', + function (e) { + var target = e.target + while (target != null) { + if ( + target.matches ? target.matches('a') : target.msMatchesSelector('a') + ) { + if ( + target.href && + target.href.startsWith('http') && + target.target === '_blank' + ) { + window.__TAURI_INVOKE_HANDLER__(JSON.stringify({ + cmd: 'open', + uri: target.href + })) + e.preventDefault() + } + break + } + target = target.parentElement + } + }, + true + ) + } + + if ( + document.readyState === 'complete' || + document.readyState === 'interactive' + ) { + __openLinks() + } else { + window.addEventListener( + 'DOMContentLoaded', + function () { + __openLinks() + }, + true + ) + } + + let permissionSettable = false + let permissionValue = 'default' + + function isPermissionGranted() { + if (window.Notification.permission !== 'default') { + return Promise.resolve(window.Notification.permission === 'granted') + } + return window.__TAURI__.promisified({ + cmd: 'isNotificationPermissionGranted' + }) + } + + function setNotificationPermission(value) { + permissionSettable = true + window.Notification.permission = value + permissionSettable = false + } + + function requestPermission() { + return window.__TAURI__ + .promisified({ + cmd: 'requestNotificationPermission' + }) + .then(function (permission) { + setNotificationPermission(permission) + return permission + }) + } + + function sendNotification(options) { + if (typeof options === 'object') { + Object.freeze(options) + } + + isPermissionGranted().then(function (permission) { + if (permission) { + return window.__TAURI__.promisified({ + cmd: 'notification', + options: + typeof options === 'string' + ? { + title: options + } + : options + }) + } + }) + } + + window.Notification = function (title, options) { + var opts = options || {} + sendNotification( + Object.assign(opts, { + title: title + }) + ) + } + + window.Notification.requestPermission = requestPermission + + Object.defineProperty(window.Notification, 'permission', { + enumerable: true, + get: function () { + return permissionValue + }, + set: function (v) { + if (!permissionSettable) { + throw new Error('Readonly property') + } + permissionValue = v + } + }) + + isPermissionGranted().then(function (response) { + if (response === null) { + setNotificationPermission('default') + } else { + setNotificationPermission(response ? 'granted' : 'denied') + } + }) + + window.alert = function (message) { + window.__TAURI_INVOKE_HANDLER__(JSON.stringify({ + cmd: 'messageDialog', + message: message + })) + } + + window.confirm = function (message) { + return window.__TAURI__.promisified({ + cmd: 'askDialog', + message: message + }) + } +})() \ No newline at end of file diff --git a/tauri/examples/multiwindow/dist/index.html b/tauri/examples/multiwindow/dist/index.html new file mode 100644 index 000000000..02a48f815 --- /dev/null +++ b/tauri/examples/multiwindow/dist/index.html @@ -0,0 +1,36 @@ + + + + +
+
+
+ + + + + \ No newline at end of file diff --git a/tauri/examples/multiwindow/package.json b/tauri/examples/multiwindow/package.json new file mode 100644 index 000000000..fd692a576 --- /dev/null +++ b/tauri/examples/multiwindow/package.json @@ -0,0 +1,11 @@ +{ + "name": "communication-example", + "version": "1.0.0", + "description": "A Tauri example showcasing the JS-Rust communication", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "build": "node ../../../cli/tauri.js/bin/tauri build" + }, + "private": true +} diff --git a/tauri/examples/multiwindow/src-tauri/.gitignore b/tauri/examples/multiwindow/src-tauri/.gitignore new file mode 100644 index 000000000..270a92d27 --- /dev/null +++ b/tauri/examples/multiwindow/src-tauri/.gitignore @@ -0,0 +1,10 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ +WixTools + +# These are backup files generated by rustfmt +**/*.rs.bk + +config.json +bundle.json diff --git a/tauri/examples/multiwindow/src-tauri/Cargo.toml b/tauri/examples/multiwindow/src-tauri/Cargo.toml new file mode 100644 index 000000000..39797f829 --- /dev/null +++ b/tauri/examples/multiwindow/src-tauri/Cargo.toml @@ -0,0 +1,42 @@ +workspace = { } + +[package] +name = "app" +version = "0.1.0" +description = "A Tauri Multiwindow App" +authors = [ "you" ] +license = "" +repository = "" +default-run = "app" +edition = "2018" +build = "src/build.rs" + +[package.metadata.bundle] +identifier = "com.tauri.dev" +icon = [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" +] + +[dependencies] +tauri = { path = "../../..", features =["all-api", "cli"]} + +[target."cfg(windows)".build-dependencies] +winres = "0.1" + +[features] +embedded-server = [ "tauri/embedded-server" ] + +[[bin]] +name = "app" +path = "src/main.rs" + +[profile.release] +panic = "abort" +codegen-units = 1 +lto = true +incremental = false +opt-level = "z" diff --git a/tauri/examples/multiwindow/src-tauri/icons/128x128.png b/tauri/examples/multiwindow/src-tauri/icons/128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..f8d9962cc21930b871430f46b7dca842cb6c6f6d GIT binary patch literal 15920 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 F{||SSwqO7N literal 0 HcmV?d00001 diff --git a/tauri/examples/multiwindow/src-tauri/icons/128x128@2x.png b/tauri/examples/multiwindow/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..44d26d04b6f60f5cc6ead44f15d30f80fd348b28 GIT binary patch literal 37546 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