Skip to content

Commit

Permalink
feat: add Window::set_enabled and Window::is_enabled (#11154)
Browse files Browse the repository at this point in the history
* feat: add `Window::set_enabled` and `Window::is_enabled`

closes #6660

* license headers

* fix build

* fix mobile and macos

* fix macos

* again

* unsafe

* fix macos is_enabled

* update example

---------

Co-authored-by: Lucas Nogueira <lucas@tauri.app>
  • Loading branch information
amrbashir and lucasfernog authored Sep 27, 2024
1 parent a49fc99 commit de7414a
Show file tree
Hide file tree
Showing 21 changed files with 453 additions and 152 deletions.
5 changes: 5 additions & 0 deletions .changes/window-set-enabled-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tauri-apps/api": "patch:feat"
---

Add `Window::setEnabled` and `Window::isEnabled` methods
7 changes: 7 additions & 0 deletions .changes/window-set-enabled.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"tauri": "patch:feat"
"tauri-runtime": "patch:feat"
"tauri-runtime-wry": "patch:feat"
---

Add `Window::set_enabled` and `Window::is_enabled` methods
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ members = [

# examples
"examples/file-associations/src-tauri",
"examples/api/src-tauri",
"examples/resources/src-tauri",
"examples/api/src-tauri",
"examples/api/src-tauri/tauri-plugin-sample",
]
resolver = "2"
Expand Down
3 changes: 3 additions & 0 deletions crates/tauri-runtime-wry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,12 @@ percent-encoding = "2.1"
objc2 = "0.5.2"
objc2-foundation = { version = "0.2.2", features = [] }
objc2-app-kit = { version = "0.2.2", features = [
"block2",
"NSApplication",
"NSResponder",
"NSView",
"NSWindow",
"NSGraphics",
] }

[target."cfg(target_os = \"android\")".dependencies]
Expand Down
108 changes: 24 additions & 84 deletions crates/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,11 @@ type IpcHandler = dyn Fn(Request<String>) + 'static;
target_os = "openbsd"
))]
mod undecorated_resizing;

mod webview;
mod window;

pub use webview::Webview;
use window::WindowExt as _;

#[derive(Debug)]
pub struct WebContext {
Expand Down Expand Up @@ -1166,9 +1168,11 @@ pub enum WindowMessage {
GtkBox(Sender<GtkBox>),
RawWindowHandle(Sender<std::result::Result<SendRawWindowHandle, raw_window_handle::HandleError>>),
Theme(Sender<Theme>),
IsEnabled(Sender<bool>),
// Setters
Center,
RequestUserAttention(Option<UserAttentionTypeWrapper>),
SetEnabled(bool),
SetResizable(bool),
SetMaximizable(bool),
SetMinimizable(bool),
Expand Down Expand Up @@ -1700,6 +1704,10 @@ impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
window_getter!(self, WindowMessage::Theme)
}

fn is_enabled(&self) -> Result<bool> {
window_getter!(self, WindowMessage::IsEnabled)
}

#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
Expand Down Expand Up @@ -1775,6 +1783,13 @@ impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
)
}

fn set_enabled(&self, enabled: bool) -> Result<()> {
send_user_message(
&self.context,
Message::Window(self.window_id, WindowMessage::SetEnabled(enabled)),
)
}

fn set_maximizable(&self, maximizable: bool) -> Result<()> {
send_user_message(
&self.context,
Expand Down Expand Up @@ -2865,40 +2880,10 @@ fn handle_user_message<T: UserEvent>(
WindowMessage::Theme(tx) => {
tx.send(map_theme(&window.theme())).unwrap();
}
// Setters
WindowMessage::Center => {
#[cfg(not(target_os = "macos"))]
if let Some(monitor) = window.current_monitor() {
#[allow(unused_mut)]
let mut window_size = window.outer_size();
#[cfg(windows)]
if window.is_decorated() {
use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Dwm::{
DwmGetWindowAttribute, DWMWA_EXTENDED_FRAME_BOUNDS,
};
let mut rect = RECT::default();
let result = unsafe {
DwmGetWindowAttribute(
HWND(window.hwnd() as _),
DWMWA_EXTENDED_FRAME_BOUNDS,
&mut rect as *mut _ as *mut _,
std::mem::size_of::<RECT>() as u32,
)
};
if result.is_ok() {
window_size.height = (rect.bottom - rect.top) as u32;
}
}
window.set_outer_position(calculate_window_center_position(window_size, monitor));
}
WindowMessage::IsEnabled(tx) => tx.send(window.is_enabled()).unwrap(),

#[cfg(target_os = "macos")]
{
let ns_window: &objc2_app_kit::NSWindow = unsafe { &*window.ns_window().cast() };
ns_window.center();
}
}
// Setters
WindowMessage::Center => window.center(),
WindowMessage::RequestUserAttention(request_type) => {
window.request_user_attention(request_type.map(|r| r.0));
}
Expand All @@ -2919,6 +2904,7 @@ fn handle_user_message<T: UserEvent>(
WindowMessage::Unmaximize => window.set_maximized(false),
WindowMessage::Minimize => window.set_minimized(true),
WindowMessage::Unminimize => window.set_minimized(false),
WindowMessage::SetEnabled(enabled) => window.set_enabled(enabled),
WindowMessage::Show => window.set_visible(true),
WindowMessage::Hide => window.set_visible(false),
WindowMessage::Close => {
Expand Down Expand Up @@ -3421,7 +3407,7 @@ fn handle_user_message<T: UserEvent>(
let surface = if is_window_transparent {
if let Ok(context) = softbuffer::Context::new(window.clone()) {
if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) {
clear_window_surface(&window, &mut surface);
window.clear_surface(&mut surface);
Some(surface)
} else {
None
Expand Down Expand Up @@ -3499,7 +3485,7 @@ fn handle_event_loop<T: UserEvent>(
if window.is_window_transparent {
if let Some(surface) = &mut window.surface {
if let Some(window) = &window.inner {
clear_window_surface(window, surface)
window.clear_surface(surface);
}
}
}
Expand Down Expand Up @@ -3842,7 +3828,7 @@ fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
}
}
}
let position = calculate_window_center_position(window_size, monitor);
let position = window::calculate_window_center_position(window_size, monitor);
let logical_position = position.to_logical::<f64>(scale_factor);
window_builder = window_builder.position(logical_position.x, logical_position.y);
}
Expand Down Expand Up @@ -3914,7 +3900,7 @@ fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
let surface = if is_window_transparent {
if let Ok(context) = softbuffer::Context::new(window.clone()) {
if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) {
clear_window_surface(&window, &mut surface);
window.clear_surface(&mut surface);
Some(surface)
} else {
None
Expand Down Expand Up @@ -4398,49 +4384,3 @@ fn inner_size(
) -> TaoPhysicalSize<u32> {
window.inner_size()
}

fn calculate_window_center_position(
window_size: TaoPhysicalSize<u32>,
target_monitor: MonitorHandle,
) -> TaoPhysicalPosition<i32> {
#[cfg(windows)]
{
use tao::platform::windows::MonitorHandleExtWindows;
use windows::Win32::Graphics::Gdi::{GetMonitorInfoW, HMONITOR, MONITORINFO};
let mut monitor_info = MONITORINFO {
cbSize: std::mem::size_of::<MONITORINFO>() as u32,
..Default::default()
};
let status =
unsafe { GetMonitorInfoW(HMONITOR(target_monitor.hmonitor() as _), &mut monitor_info) };
if status.into() {
let available_width = monitor_info.rcWork.right - monitor_info.rcWork.left;
let available_height = monitor_info.rcWork.bottom - monitor_info.rcWork.top;
let x = (available_width - window_size.width as i32) / 2 + monitor_info.rcWork.left;
let y = (available_height - window_size.height as i32) / 2 + monitor_info.rcWork.top;
return TaoPhysicalPosition::new(x, y);
}
}
let screen_size = target_monitor.size();
let monitor_pos = target_monitor.position();
let x = (screen_size.width as i32 - window_size.width as i32) / 2 + monitor_pos.x;
let y = (screen_size.height as i32 - window_size.height as i32) / 2 + monitor_pos.y;
TaoPhysicalPosition::new(x, y)
}

#[cfg(windows)]
fn clear_window_surface(
window: &Window,
surface: &mut softbuffer::Surface<Arc<Window>, Arc<Window>>,
) {
let size = window.inner_size();
if let (Some(width), Some(height)) = (
std::num::NonZeroU32::new(size.width),
std::num::NonZeroU32::new(size.height),
) {
surface.resize(width, height).unwrap();
let mut buffer = surface.buffer_mut().unwrap();
buffer.fill(0);
let _ = buffer.present();
}
}
31 changes: 31 additions & 0 deletions crates/tauri-runtime-wry/src/window/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use gtk::prelude::*;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
use tao::platform::unix::WindowExtUnix;

impl super::WindowExt for tao::window::Window {
fn set_enabled(&self, enabled: bool) {
self.gtk_window().set_sensitive(enabled);
}

fn is_enabled(&self) -> bool {
self.gtk_window().is_sensitive()
}

fn center(&self) {
if let Some(monitor) = self.current_monitor() {
let window_size = self.outer_size();
let new_pos = super::calculate_window_center_position(window_size, monitor);
self.set_outer_position(new_pos);
}
}
}
43 changes: 43 additions & 0 deletions crates/tauri-runtime-wry/src/window/macos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use objc2_app_kit::{NSBackingStoreType, NSWindow, NSWindowStyleMask};
use objc2_foundation::MainThreadMarker;
use tao::platform::macos::WindowExtMacOS;

impl super::WindowExt for tao::window::Window {
// based on electron implementation
// https://github.com/electron/electron/blob/15db63e26df3e3d59ce6281f030624f746518511/shell/browser/native_window_mac.mm#L474
fn set_enabled(&self, enabled: bool) {
let ns_window: &NSWindow = unsafe { &*self.ns_window().cast() };
if !enabled {
let frame = ns_window.frame();
let mtm = MainThreadMarker::new()
.expect("`Window::set_enabled` can only be called on the main thread");
let sheet = unsafe {
NSWindow::initWithContentRect_styleMask_backing_defer(
mtm.alloc(),
frame,
NSWindowStyleMask::Titled,
NSBackingStoreType::NSBackingStoreBuffered,
false,
)
};
unsafe { sheet.setAlphaValue(0.5) };
unsafe { ns_window.beginSheet_completionHandler(&sheet, None) };
} else if let Some(attached) = unsafe { ns_window.attachedSheet() } {
unsafe { ns_window.endSheet(&attached) };
}
}

fn is_enabled(&self) -> bool {
let ns_window: &NSWindow = unsafe { &*self.ns_window().cast() };
unsafe { ns_window.attachedSheet() }.is_none()
}

fn center(&self) {
let ns_window: &NSWindow = unsafe { &*self.ns_window().cast() };
ns_window.center();
}
}
88 changes: 88 additions & 0 deletions crates/tauri-runtime-wry/src/window/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2019-2024 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 linux;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(windows)]
mod windows;

pub trait WindowExt {
/// Enable or disable the window
///
/// ## Platform-specific:
///
/// - **Android / iOS**: Unsupported.
fn set_enabled(&self, enabled: bool);

/// Whether the window is enabled or disabled.
///
/// ## Platform-specific:
///
/// - **Android / iOS**: Unsupported, always returns `true`.
fn is_enabled(&self) -> bool;

/// Center the window
///
/// ## Platform-specific:
///
/// - **Android / iOS**: Unsupported.
fn center(&self) {}

/// Clears the window sufrace. i.e make it it transparent.
#[cfg(windows)]
fn clear_surface(
&self,
surface: &mut softbuffer::Surface<
std::sync::Arc<tao::window::Window>,
std::sync::Arc<tao::window::Window>,
>,
);
}

#[cfg(mobile)]
impl WindowExt for tao::window::Window {
fn set_enabled(&self, _: bool) {}
fn is_enabled(&self) -> bool {
true
}
}

pub fn calculate_window_center_position(
window_size: tao::dpi::PhysicalSize<u32>,
target_monitor: tao::monitor::MonitorHandle,
) -> tao::dpi::PhysicalPosition<i32> {
#[cfg(windows)]
{
use ::windows::Win32::Graphics::Gdi::{GetMonitorInfoW, HMONITOR, MONITORINFO};
use tao::platform::windows::MonitorHandleExtWindows;

let mut monitor_info = MONITORINFO {
cbSize: std::mem::size_of::<MONITORINFO>() as u32,
..Default::default()
};
let hmonitor = target_monitor.hmonitor();
let status = unsafe { GetMonitorInfoW(HMONITOR(hmonitor as _), &mut monitor_info) };
if status.into() {
let available_width = monitor_info.rcWork.right - monitor_info.rcWork.left;
let available_height = monitor_info.rcWork.bottom - monitor_info.rcWork.top;
let x = (available_width - window_size.width as i32) / 2 + monitor_info.rcWork.left;
let y = (available_height - window_size.height as i32) / 2 + monitor_info.rcWork.top;
return tao::dpi::PhysicalPosition::new(x, y);
}
}

let screen_size = target_monitor.size();
let monitor_pos = target_monitor.position();
let x = (screen_size.width as i32 - window_size.width as i32) / 2 + monitor_pos.x;
let y = (screen_size.height as i32 - window_size.height as i32) / 2 + monitor_pos.y;
tao::dpi::PhysicalPosition::new(x, y)
}
Loading

0 comments on commit de7414a

Please sign in to comment.