Skip to content

Commit

Permalink
feat: add reparent function (#1166)
Browse files Browse the repository at this point in the history
* feat: add reparent function

* remove mut

* windows and linux impl

* update change file

* Discard changes to examples/simple.rs

* remove ns_window field

---------

Co-authored-by: Lucas Nogueira <[email protected]>
  • Loading branch information
amrbashir and lucasfernog authored Feb 12, 2024
1 parent e2542de commit e50ce47
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changes/reparent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wry": minor
---

Added `WebViewExtMacOS::reparent`,`WebViewExtWindows::reparent` and `WebViewExtUnix::reparent`.
107 changes: 107 additions & 0 deletions examples/reparent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use tao::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{ControlFlow, EventLoop},
keyboard::Key,
window::WindowBuilder,
};
use wry::WebViewBuilder;

#[cfg(target_os = "macos")]
use {tao::platform::macos::WindowExtMacOS, wry::WebViewExtMacOS};
#[cfg(target_os = "windows")]
use {tao::platform::windows::WindowExtWindows, wry::WebViewExtWindows};

#[cfg(not(any(
target_os = "windows",
target_os = "macos",
target_os = "ios",
target_os = "android"
)))]
#[cfg(not(any(
target_os = "windows",
target_os = "macos",
target_os = "ios",
target_os = "android"
)))]
use {
tao::platform::unix::WindowExtUnix,
wry::{WebViewBuilderExtUnix, WebViewExtUnix},
};

fn main() -> wry::Result<()> {
let event_loop = EventLoop::new();
let window = WindowBuilder::new().build(&event_loop).unwrap();
let window2 = WindowBuilder::new().build(&event_loop).unwrap();

#[cfg(any(
target_os = "windows",
target_os = "macos",
target_os = "ios",
target_os = "android"
))]
let builder = WebViewBuilder::new(&window);

#[cfg(not(any(
target_os = "windows",
target_os = "macos",
target_os = "ios",
target_os = "android"
)))]
let builder = {
let vbox = window.default_vbox().unwrap();
WebViewBuilder::new_gtk(vbox)
};

let webview = builder.with_url("https://tauri.app")?.build()?;

let mut webview_container = window.id();

event_loop.run(move |event, _event_loop, control_flow| {
*control_flow = ControlFlow::Wait;

match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,

Event::WindowEvent {
event:
WindowEvent::KeyboardInput {
event:
KeyEvent {
logical_key: Key::Character("x"),
state: ElementState::Pressed,
..
},
..
},
..
} => {
let new_parent = if webview_container == window.id() {
&window2
} else {
&window
};
webview_container = new_parent.id();

#[cfg(target_os = "macos")]
webview.reparent(new_parent.ns_window() as cocoa::base::id);
#[cfg(not(any(
target_os = "windows",
target_os = "macos",
target_os = "ios",
target_os = "android"
)))]
webview.reparent(new_parent.default_vbox().unwrap());
#[cfg(target_os = "windows")]
webview.reparent(new_parent.hwnd());
}
_ => {}
}
});
}
30 changes: 29 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1458,6 +1458,9 @@ pub trait WebViewExtWindows {
/// [1]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2memoryusagetargetlevel
/// [2]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2.memoryusagetargetlevel?view=webview2-dotnet-1.0.2088.41#remarks
fn set_memory_usage_level(&self, level: MemoryUsageLevel);

/// Attaches this webview to the given HWND and removes it from the current one.
fn reparent(&self, hwnd: isize);
}

#[cfg(target_os = "windows")]
Expand All @@ -1473,6 +1476,10 @@ impl WebViewExtWindows for WebView {
fn set_memory_usage_level(&self, level: MemoryUsageLevel) {
self.webview.set_memory_usage_level(level);
}

fn reparent(&self, hwnd: isize) {
self.webview.reparent(hwnd)
}
}

/// Additional methods on `WebView` that are specific to Linux.
Expand All @@ -1494,6 +1501,11 @@ pub trait WebViewExtUnix: Sized {

/// Returns Webkit2gtk Webview handle
fn webview(&self) -> webkit2gtk::WebView;

/// Attaches this webview to the given Widget and removes it from the current one.
fn reparent<W>(&self, widget: &W)
where
W: gtk::prelude::IsA<gtk::Container>;
}

#[cfg(gtk)]
Expand All @@ -1508,6 +1520,13 @@ impl WebViewExtUnix for WebView {
fn webview(&self) -> webkit2gtk::WebView {
self.webview.webview.clone()
}

fn reparent<W>(&self, widget: &W)
where
W: gtk::prelude::IsA<gtk::Container>,
{
self.webview.reparent(widget)
}
}

/// Additional methods on `WebView` that are specific to macOS.
Expand All @@ -1519,6 +1538,8 @@ pub trait WebViewExtMacOS {
fn manager(&self) -> cocoa::base::id;
/// Returns NSWindow associated with the WKWebView webview
fn ns_window(&self) -> cocoa::base::id;
/// Attaches this webview to the given NSWindow and removes it from the current one.
fn reparent(&self, window: cocoa::base::id);
}

#[cfg(target_os = "macos")]
Expand All @@ -1532,7 +1553,14 @@ impl WebViewExtMacOS for WebView {
}

fn ns_window(&self) -> cocoa::base::id {
self.webview.ns_window
unsafe {
let ns_window: cocoa::base::id = msg_send![self.webview.webview, window];
ns_window
}
}

fn reparent(&self, window: cocoa::base::id) {
self.webview.reparent_to(window)
}
}

Expand Down
26 changes: 26 additions & 0 deletions src/webkitgtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,32 @@ impl InnerWebView {
pub fn focus(&self) {
self.webview.grab_focus();
}

pub fn reparent<W>(&self, container: &W)
where
W: gtk::prelude::IsA<gtk::Container>,
{
if let Some(parent) = self
.webview
.parent()
.and_then(|p| p.dynamic_cast::<gtk::Container>().ok())
{
parent.remove(&self.webview);
if container.type_().name() == "GtkBox" {
container
.dynamic_cast_ref::<gtk::Box>()
.unwrap()
.pack_start(&self.webview, true, true, 0);
} else if container.type_().name() == "GtkFixed" {
container
.dynamic_cast_ref::<gtk::Fixed>()
.unwrap()
.put(&self.webview, 0, 0);
} else {
container.add(&self.webview);
}
}
}
}

pub fn platform_webview_version() -> Result<String> {
Expand Down
4 changes: 4 additions & 0 deletions src/webview2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,10 @@ impl InnerWebView {
let level = COREWEBVIEW2_MEMORY_USAGE_TARGET_LEVEL(level);
let _ = unsafe { webview.SetMemoryUsageTargetLevel(level) };
}

pub fn reparent(&self, hwnd: isize) {
let _ = unsafe { self.controller.SetParentWindow(HWND(hwnd as _)) };
}
}

unsafe fn prepare_web_request_response(
Expand Down
20 changes: 11 additions & 9 deletions src/wkwebview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ const NS_JSON_WRITING_FRAGMENTS_ALLOWED: u64 = 4;

pub(crate) struct InnerWebView {
pub webview: id,
#[cfg(target_os = "macos")]
pub ns_window: id,
pub manager: id,
is_child: bool,
pending_scripts: Arc<Mutex<Option<Vec<String>>>>,
Expand Down Expand Up @@ -826,7 +824,7 @@ impl InnerWebView {

// ns window is required for the print operation
#[cfg(target_os = "macos")]
let ns_window = {
{
let ns_window: id = msg_send![ns_view, window];

let can_set_titlebar_style: BOOL = msg_send![
Expand All @@ -837,14 +835,10 @@ impl InnerWebView {
// `1` means `none`, see https://developer.apple.com/documentation/appkit/nstitlebarseparatorstyle/none
let () = msg_send![ns_window, setTitlebarSeparatorStyle: 1];
}

ns_window
};
}

let w = Self {
webview,
#[cfg(target_os = "macos")]
ns_window,
manager,
pending_scripts,
ipc_handler_ptr,
Expand Down Expand Up @@ -1075,7 +1069,8 @@ r#"Object.defineProperty(window, 'ipc', {
// Allow the modal to detach from the current thread and be non-blocker
let () = msg_send![print_operation, setCanSpawnSeparateThread: YES];
// Launch the modal
let () = msg_send![print_operation, runOperationModalForWindow: self.ns_window delegate: null::<*const c_void>() didRunSelector: null::<*const c_void>() contextInfo: null::<*const c_void>()];
let window: id = msg_send![self.webview, window];
let () = msg_send![print_operation, runOperationModalForWindow: window delegate: null::<*const c_void>() didRunSelector: null::<*const c_void>() contextInfo: null::<*const c_void>()];
}
}
}
Expand Down Expand Up @@ -1167,6 +1162,13 @@ r#"Object.defineProperty(window, 'ipc', {
let _: () = msg_send![window, makeFirstResponder: self.webview];
}
}

pub(crate) fn reparent_to(&self, window: id) {
unsafe {
let content_view: id = msg_send![window, contentView];
let _: () = msg_send![content_view, addSubview: self.webview];
}
}
}

pub fn url_from_webview(webview: id) -> String {
Expand Down

0 comments on commit e50ce47

Please sign in to comment.