Support window parenting on macOS, closes #3751 (#3754)

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
Kasper 2022-03-23 17:30:44 +01:00 committed by GitHub
parent d36c8e0322
commit 4e807a53e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 239 additions and 8 deletions

View File

@ -0,0 +1,5 @@
---
"tauri": patch
---
**Breaking change:** The `Window::hwnd` method now returns *HWND* from `windows-rs` crate instead of *c_void* on Windows.

View File

@ -0,0 +1,7 @@
---
"tauri-runtime-wry": minor
"tauri-runtime": minor
"tauri": minor
---
Support window parenting on macOS

View File

@ -877,6 +877,14 @@ impl WindowBuilder for WindowBuilderWrapper {
self
}
#[cfg(target_os = "macos")]
fn parent_window(mut self, parent: *mut std::ffi::c_void) -> Self {
use wry::application::platform::macos::WindowBuilderExtMacOS;
self.inner = self.inner.with_parent_window(parent);
self
}
#[cfg(windows)]
fn owner_window(mut self, owner: HWND) -> Self {
self.inner = self.inner.with_owner_window(owner);

View File

@ -165,6 +165,15 @@ pub trait WindowBuilder: WindowBuilderBase {
#[must_use]
fn parent_window(self, parent: HWND) -> Self;
/// Sets a parent to the window to be created.
///
/// A child window has the WS_CHILD style and is confined to the client area of its parent window.
///
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
#[cfg(target_os = "macos")]
#[must_use]
fn parent_window(self, parent: *mut std::ffi::c_void) -> Self;
/// Set an owner to the window to be created.
///
/// From MSDN:

View File

@ -277,6 +277,10 @@ name = "multiwindow"
path = "../../examples/multiwindow/src-tauri/src/main.rs"
required-features = [ "window-create" ]
[[example]]
name = "parent-window"
path = "../../examples/parent-window/src-tauri/src/main.rs"
[[example]]
name = "navigation"
path = "../../examples/navigation/src-tauri/src/main.rs"

View File

@ -238,6 +238,11 @@ impl WindowBuilder for MockWindowBuilder {
self
}
#[cfg(target_os = "macos")]
fn parent_window(self, parent: *mut std::ffi::c_void) -> Self {
self
}
#[cfg(windows)]
fn owner_window(self, owner: HWND) -> Self {
self

View File

@ -387,6 +387,14 @@ impl<R: Runtime> WindowBuilder<R> {
self
}
/// Sets a parent to the window to be created.
#[cfg(target_os = "macos")]
#[must_use]
pub fn parent_window(mut self, parent: *mut std::ffi::c_void) -> Self {
self.window_builder = self.window_builder.parent_window(parent);
self
}
/// Set an owner to the window to be created.
///
/// From MSDN:
@ -463,7 +471,7 @@ unsafe impl<R: Runtime> raw_window_handle::HasRawWindowHandle for Window<R> {
#[cfg(windows)]
fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle {
let mut handle = raw_window_handle::Win32Handle::empty();
handle.hwnd = self.hwnd().expect("failed to get window `hwnd`");
handle.hwnd = self.hwnd().expect("failed to get window `hwnd`").0 as *mut _;
raw_window_handle::RawWindowHandle::Win32(handle)
}
@ -909,13 +917,8 @@ impl<R: Runtime> Window<R> {
}
/// Returns the native handle that is used by this window.
#[cfg(windows)]
pub fn hwnd(&self) -> crate::Result<*mut std::ffi::c_void> {
self
.window
.dispatcher
.hwnd()
.map(|hwnd| hwnd.0 as *mut _)
.map_err(Into::into)
pub fn hwnd(&self) -> crate::Result<HWND> {
self.window.dispatcher.hwnd().map_err(Into::into)
}
/// Returns the `ApplicatonWindow` from gtk crate that is used by this window.

View File

@ -0,0 +1,3 @@
# Parent Window Example
Run the following at the root directory of the repository to try it out: `cargo run --example parent-window`.

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<style>
#response {
white-space: pre-wrap;
}
</style>
</head>
<body>
<div id="window-label"></div>
<div id="container"></div>
<div id="response"></div>
<script>
var WebviewWindow = window.__TAURI__.window.WebviewWindow
var thisTauriWindow = window.__TAURI__.window.getCurrent()
var windowLabel = thisTauriWindow.label
var windowLabelContainer = document.getElementById('window-label')
windowLabelContainer.innerText = 'This is the ' + windowLabel + ' window.'
var container = document.getElementById('container')
var responseContainer = document.getElementById('response')
function runCommand(commandName, args, optional) {
window.__TAURI__
.invoke(commandName, args)
.then((response) => {
responseContainer.innerText += `Ok(${response})\n\n`
})
.catch((error) => {
responseContainer.innerText += `Err(${error})\n\n`
})
}
window.__TAURI__.event.listen('tauri://window-created', function (event) {
responseContainer.innerText += 'Got window-created event\n\n'
})
var createWindowButton = document.createElement('button')
var windowNumber = 1
createWindowButton.innerHTML = 'Create child window '+windowNumber
createWindowButton.addEventListener('click', function () {
runCommand('create_child_window', { id: 'child-'+windowNumber })
windowNumber += 1
createWindowButton.innerHTML = 'Create child window '+windowNumber
})
container.appendChild(createWindowButton)
</script>
</body>
</html>

View File

@ -0,0 +1,4 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools

View File

@ -0,0 +1,3 @@
// Copyright {20\d{2}(-20\d{2})?} Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

View File

@ -0,0 +1,17 @@
[package]
name = "parent-window"
version = "0.1.0"
description = "An example Tauri Multi-Window Application"
edition = "2021"
rust-version = "1.57"
license = "Apache-2.0 OR MIT"
[build-dependencies]
tauri-build = { path = "../../../core/tauri-build" }
[dependencies]
tauri = { path = "../../../core/tauri" }
[features]
default = [ "custom-protocol" ]
custom-protocol = [ "tauri/custom-protocol" ]

View File

@ -0,0 +1,14 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use tauri_build::{try_build, Attributes, WindowsAttributes};
fn main() {
if let Err(error) = try_build(
Attributes::new()
.windows_attributes(WindowsAttributes::new().window_icon_path("../../.icons/icon.ico")),
) {
panic!("error found during tauri-build: {:#?}", error);
}
}

View File

@ -0,0 +1,22 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use tauri::{command, window, AppHandle, Manager, WindowUrl};
#[command]
pub fn create_child_window(id: String, app: AppHandle) {
#[cfg(any(windows, target_os = "macos"))]
let main = app.get_window("main").unwrap();
let child = window::WindowBuilder::new(&app, id, WindowUrl::default())
.title("Child")
.inner_size(400.0, 300.0);
#[cfg(target_os = "macos")]
let child = child.parent_window(main.ns_window().unwrap());
#[cfg(windows)]
let child = child.parent_window(main.hwnd().unwrap());
child.build().unwrap();
}

View File

@ -0,0 +1,38 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use tauri::{WindowBuilder, WindowUrl};
mod commands;
fn main() {
tauri::Builder::default()
.on_page_load(|window, _payload| {
let label = window.label().to_string();
window.listen("clicked".to_string(), move |_payload| {
println!("got 'clicked' event on window '{}'", label);
});
})
.invoke_handler(tauri::generate_handler![commands::create_child_window])
.create_window(
"main".to_string(),
WindowUrl::default(),
|window_builder, webview_attributes| {
(
window_builder.title("Main").inner_size(600.0, 400.0),
webview_attributes,
)
},
)
.unwrap() // safe to unwrap: window label is valid
.run(tauri::generate_context!(
"../../examples/parent-window/src-tauri/tauri.conf.json"
))
.expect("failed to run tauri application");
}

View File

@ -0,0 +1,36 @@
{
"build": {
"distDir": [
"../index.html"
],
"devPath": [
"../index.html"
],
"withGlobalTauri": true
},
"tauri": {
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.dev",
"icon": [
"../../.icons/32x32.png",
"../../.icons/128x128.png",
"../../.icons/128x128@2x.png",
"../../.icons/icon.icns",
"../../.icons/icon.ico"
],
"resources": [],
"externalBin": [],
"copyright": "",
"category": "DeveloperTool"
},
"allowlist": {},
"security": {
"csp": "default-src 'self'"
},
"updater": {
"active": false
}
}
}