Skip to content

Commit

Permalink
feat(core): expose with_webview API to access the platform webview (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed May 4, 2022
1 parent 72e577d commit c82b476
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 18 deletions.
7 changes: 7 additions & 0 deletions .changes/webview-getters.md
@@ -0,0 +1,7 @@
---
"tauri": "patch"
"tauri-runtime": patch
"tauri-runtime-wry": patch
---

Expose methods to access the underlying native handles of the webview.
12 changes: 8 additions & 4 deletions core/tauri-runtime-wry/Cargo.toml
Expand Up @@ -13,7 +13,7 @@ exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
readme = "README.md"

[dependencies]
wry = { version = "0.15", default-features = false, features = [ "file-drop", "protocol" ] }
wry = { version = "0.16", default-features = false, features = [ "file-drop", "protocol" ] }
tauri-runtime = { version = "0.4.0", path = "../tauri-runtime" }
tauri-utils = { version = "1.0.0-rc.5", path = "../tauri-utils" }
uuid = { version = "1", features = [ "v4" ] }
Expand All @@ -22,14 +22,18 @@ rand = "0.8"
[target."cfg(windows)".dependencies]
webview2-com = "0.13.0"

[target."cfg(windows)".dependencies.windows]
version = "0.30.0"
features = [ "Win32_Foundation" ]
[target."cfg(windows)".dependencies.windows]
version = "0.30.0"
features = [ "Win32_Foundation" ]

[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
gtk = { version = "0.15", features = [ "v3_20" ] }
webkit2gtk = { version = "0.17", features = [ "v2_22" ] }
percent-encoding = "2.1"

[target."cfg(any(target_os = \"ios\", target_os = \"macos\"))".dependencies]
cocoa = "0.24"

[features]
dox = [ "wry/dox" ]
devtools = [ "wry/devtools", "tauri-runtime/devtools" ]
Expand Down
47 changes: 45 additions & 2 deletions core/tauri-runtime-wry/src/lib.rs
Expand Up @@ -100,6 +100,9 @@ use std::{

type WebviewId = u64;

mod webview;
pub use webview::Webview;

#[cfg(feature = "system-tray")]
mod system_tray;
#[cfg(feature = "system-tray")]
Expand Down Expand Up @@ -1000,8 +1003,8 @@ pub struct GtkWindow(gtk::ApplicationWindow);
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl Send for GtkWindow {}

#[derive(Debug, Clone)]
pub enum WindowMessage {
WithWebview(Box<dyn FnOnce(Webview) + Send>),
// Devtools
#[cfg(any(debug_assertions, feature = "devtools"))]
OpenDevTools,
Expand Down Expand Up @@ -1121,7 +1124,6 @@ pub enum Message<T: 'static> {
impl<T: UserEvent> Clone for Message<T> {
fn clone(&self) -> Self {
match self {
Self::Window(i, m) => Self::Window(*i, m.clone()),
Self::Webview(i, m) => Self::Webview(*i, m.clone()),
#[cfg(feature = "system-tray")]
Self::Tray(m) => Self::Tray(m.clone()),
Expand All @@ -1146,6 +1148,15 @@ pub struct WryDispatcher<T: UserEvent> {
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl<T: UserEvent> Sync for WryDispatcher<T> {}

impl<T: UserEvent> WryDispatcher<T> {
pub fn with_webview<F: FnOnce(Webview) + Send + 'static>(&self, f: F) -> Result<()> {
send_user_message(
&self.context,
Message::Window(self.window_id, WindowMessage::WithWebview(Box::new(f))),
)
}
}

impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
type Runtime = Wry<T>;
type WindowBuilder = WindowBuilderWrapper;
Expand Down Expand Up @@ -2138,6 +2149,38 @@ fn handle_user_message<T: UserEvent>(
{
let window = window_handle.window();
match window_message {
WindowMessage::WithWebview(f) => {
if let WindowHandle::Webview(w) = window_handle {
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
{
use wry::webview::WebviewExtUnix;
f(w.webview());
}
#[cfg(target_os = "macos")]
{
use wry::webview::WebviewExtMacOS;
f(Webview {
webview: w.webview(),
manager: w.manager(),
ns_window: w.ns_window(),
});
}

#[cfg(windows)]
{
f(Webview {
controller: w.controller(),
});
}
}
}

#[cfg(any(debug_assertions, feature = "devtools"))]
WindowMessage::OpenDevTools => {
if let WindowHandle::Webview(w) = &window_handle {
Expand Down
37 changes: 37 additions & 0 deletions core/tauri-runtime-wry/src/webview.rs
@@ -0,0 +1,37 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
mod imp {
use std::rc::Rc;

pub type Webview = Rc<webkit2gtk::WebView>;
}

#[cfg(target_os = "macos")]
mod imp {
use cocoa::base::id;

pub struct Webview {
pub webview: id,
pub manager: id,
pub ns_window: id,
}
}

#[cfg(windows)]
mod imp {
use webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller;
pub struct Webview {
pub controller: ICoreWebView2Controller,
}
}

pub use imp::*;
6 changes: 3 additions & 3 deletions core/tauri-runtime/src/lib.rs
Expand Up @@ -473,9 +473,6 @@ pub trait Dispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 'static
#[cfg(windows)]
fn hwnd(&self) -> Result<HWND>;

/// Returns the current window theme.
fn theme(&self) -> Result<Theme>;

/// Returns the native handle that is used by this window.
#[cfg(target_os = "macos")]
fn ns_window(&self) -> Result<*mut std::ffi::c_void>;
Expand All @@ -490,6 +487,9 @@ pub trait Dispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 'static
))]
fn gtk_window(&self) -> Result<gtk::ApplicationWindow>;

/// Returns the current window theme.
fn theme(&self) -> Result<Theme>;

// SETTERS

/// Centers the window.
Expand Down
6 changes: 6 additions & 0 deletions core/tauri/Cargo.toml
Expand Up @@ -97,9 +97,15 @@ ico = { version = "0.1", optional = true }
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
gtk = { version = "0.15", features = [ "v3_20" ] }
glib = "0.15"
webkit2gtk = { version = "0.17", features = [ "v2_22" ] }

[target."cfg(target_os = \"macos\")".dependencies]
embed_plist = "1.2"
cocoa = "0.24"
objc = "0.2"

[target."cfg(windows)".dependencies]
webview2-com = "0.13.0"

[target."cfg(windows)".dependencies.windows]
version = "0.30.0"
Expand Down
142 changes: 133 additions & 9 deletions core/tauri/src/window.rs
Expand Up @@ -543,6 +543,130 @@ impl<'de, R: Runtime> CommandArg<'de, R> for Window<R> {
}
}

/// The platform webview handle. Accessed with [`Window#method.with_webview`];
#[cfg(feature = "wry")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "wry")))]
pub struct PlatformWebview(tauri_runtime_wry::Webview);

#[cfg(feature = "wry")]
impl PlatformWebview {
/// Returns [`webkit2gtk::WebView`] handle.
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg_attr(
doc_cfg,
doc(cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)))
)]
pub fn inner(&self) -> std::rc::Rc<webkit2gtk::WebView> {
self.0.clone()
}

/// Returns the WebView2 controller.
#[cfg(windows)]
#[cfg_attr(doc_cfg, doc(cfg(windows)))]
pub fn controller(
&self,
) -> webview2_com::Microsoft::Web::WebView2::Win32::ICoreWebView2Controller {
self.0.controller.clone()
}

/// Returns the [WKWebView] handle.
///
/// [WKWebView]: https://developer.apple.com/documentation/webkit/wkwebview
#[cfg(target_os = "macos")]
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
pub fn inner(&self) -> cocoa::base::id {
self.0.webview.clone()
}

/// Returns WKWebView [controller] handle.
///
/// [controller]: https://developer.apple.com/documentation/webkit/wkusercontentcontroller
#[cfg(target_os = "macos")]
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
pub fn controller(&self) -> cocoa::base::id {
self.0.manager.clone()
}

/// Returns [NSWindow] associated with the WKWebView webview.
///
/// [NSWindow]: https://developer.apple.com/documentation/appkit/nswindow
#[cfg(target_os = "macos")]
#[cfg_attr(doc_cfg, doc(cfg(target_os = "macos")))]
pub fn ns_window(&self) -> cocoa::base::id {
self.0.ns_window.clone()
}
}

#[cfg(feature = "wry")]
impl Window<crate::Wry> {
/// Executes the closure accessing the platform's webview handle.
///
/// The closure is executed in the main thread.
///
/// # Examples
///
/// ```rust,no_run
/// #[cfg(target_os = "macos")]
/// #[macro_use]
/// extern crate objc;
/// use tauri::Manager;
///
/// fn main() {
/// tauri::Builder::default()
/// .setup(|app| {
/// let main_window = app.get_window("main").unwrap();
/// main_window.with_webview(|webview| {
/// #[cfg(target_os = "linux")]
/// {
/// // see https://docs.rs/webkit2gtk/latest/webkit2gtk/struct.WebView.html
/// // and https://docs.rs/webkit2gtk/latest/webkit2gtk/trait.WebViewExt.html
/// use webkit2gtk::traits::WebViewExt;
/// webview.inner().set_zoom_level(4.);
/// }
///
/// #[cfg(windows)]
/// unsafe {
/// // see https://docs.rs/webview2-com/latest/webview2_com/Microsoft/Web/WebView2/Win32/struct.ICoreWebView2Controller.html
/// webview.controller().SetZoomFactor(4.).unwrap();
/// }
///
/// #[cfg(target_os = "macos")]
/// unsafe {
/// let () = msg_send![webview.inner(), setPageZoom: 4.];
/// let () = msg_send![webview.controller(), removeAllUserScripts];
/// let bg_color: cocoa::base::id = msg_send![class!(NSColor), colorWithDeviceRed:0.5 green:0.2 blue:0.4 alpha:1.];
/// let () = msg_send![webview.ns_window(), setBackgroundColor: bg_color];
/// }
/// });
/// Ok(())
/// });
/// }
/// ```
#[cfg_attr(doc_cfg, doc(cfg(eature = "wry")))]
pub fn with_webview<F: FnOnce(PlatformWebview) + Send + 'static>(
&self,
f: F,
) -> crate::Result<()> {
self
.window
.dispatcher
.with_webview(|w| f(PlatformWebview(w)))
.map_err(Into::into)
}
}

impl<R: Runtime> Window<R> {
/// Create a new window that is attached to the manager.
pub(crate) fn new(
Expand Down Expand Up @@ -976,15 +1100,6 @@ impl<R: Runtime> Window<R> {
self.window.dispatcher.hwnd().map_err(Into::into)
}

/// Returns the current window theme.
///
/// ## Platform-specific
///
/// - **macOS / Linux**: Not implemented, always return [`Theme::Light`].
pub fn theme(&self) -> crate::Result<Theme> {
self.window.dispatcher.theme().map_err(Into::into)
}

/// Returns the `ApplicatonWindow` from gtk crate that is used by this window.
///
/// Note that this can only be used on the main thread.
Expand All @@ -999,6 +1114,15 @@ impl<R: Runtime> Window<R> {
self.window.dispatcher.gtk_window().map_err(Into::into)
}

/// Returns the current window theme.
///
/// ## Platform-specific
///
/// - **macOS / Linux**: Not implemented, always return [`Theme::Light`].
pub fn theme(&self) -> crate::Result<Theme> {
self.window.dispatcher.theme().map_err(Into::into)
}

// Setters

/// Centers the window.
Expand Down

0 comments on commit c82b476

Please sign in to comment.