Skip to content

Commit

Permalink
Support window parenting on macOS, closes #3751 (#3754)
Browse files Browse the repository at this point in the history
Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
  • Loading branch information
3 people committed Mar 23, 2022
1 parent d36c8e0 commit 4e807a5
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changes/parent-window-hwnd.md
Original file line number Diff line number Diff line change
@@ -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.
7 changes: 7 additions & 0 deletions .changes/parent-window-macos.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"tauri-runtime-wry": minor
"tauri-runtime": minor
"tauri": minor
---

Support window parenting on macOS
8 changes: 8 additions & 0 deletions core/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 9 additions & 0 deletions core/tauri-runtime/src/webview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions core/tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 5 additions & 0 deletions core/tauri/src/test/mock_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 11 additions & 8 deletions core/tauri/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions examples/parent-window/README.md
Original file line number Diff line number Diff line change
@@ -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`.
53 changes: 53 additions & 0 deletions examples/parent-window/index.html
Original file line number Diff line number Diff line change
@@ -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>
4 changes: 4 additions & 0 deletions examples/parent-window/src-tauri/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Generated by Cargo
# will have compiled files and executables
/target/
WixTools
3 changes: 3 additions & 0 deletions examples/parent-window/src-tauri/.license_template
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions examples/parent-window/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" ]
14 changes: 14 additions & 0 deletions examples/parent-window/src-tauri/build.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
22 changes: 22 additions & 0 deletions examples/parent-window/src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
@@ -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();
}
38 changes: 38 additions & 0 deletions examples/parent-window/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
@@ -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");
}
36 changes: 36 additions & 0 deletions examples/parent-window/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -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
}
}
}

0 comments on commit 4e807a5

Please sign in to comment.