Skip to content

Commit de7414a

Browse files
feat: add Window::set_enabled and Window::is_enabled (#11154)
* 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>
1 parent a49fc99 commit de7414a

File tree

21 files changed

+453
-152
lines changed

21 files changed

+453
-152
lines changed

.changes/window-set-enabled-api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@tauri-apps/api": "patch:feat"
3+
---
4+
5+
Add `Window::setEnabled` and `Window::isEnabled` methods

.changes/window-set-enabled.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"tauri": "patch:feat"
3+
"tauri-runtime": "patch:feat"
4+
"tauri-runtime-wry": "patch:feat"
5+
---
6+
7+
Add `Window::set_enabled` and `Window::is_enabled` methods

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ members = [
3030

3131
# examples
3232
"examples/file-associations/src-tauri",
33-
"examples/api/src-tauri",
3433
"examples/resources/src-tauri",
34+
"examples/api/src-tauri",
3535
"examples/api/src-tauri/tauri-plugin-sample",
3636
]
3737
resolver = "2"

crates/tauri-runtime-wry/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,12 @@ percent-encoding = "2.1"
4949
objc2 = "0.5.2"
5050
objc2-foundation = { version = "0.2.2", features = [] }
5151
objc2-app-kit = { version = "0.2.2", features = [
52+
"block2",
53+
"NSApplication",
5254
"NSResponder",
5355
"NSView",
5456
"NSWindow",
57+
"NSGraphics",
5558
] }
5659

5760
[target."cfg(target_os = \"android\")".dependencies]

crates/tauri-runtime-wry/src/lib.rs

Lines changed: 24 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,11 @@ type IpcHandler = dyn Fn(Request<String>) + 'static;
127127
target_os = "openbsd"
128128
))]
129129
mod undecorated_resizing;
130-
131130
mod webview;
131+
mod window;
132+
132133
pub use webview::Webview;
134+
use window::WindowExt as _;
133135

134136
#[derive(Debug)]
135137
pub struct WebContext {
@@ -1166,9 +1168,11 @@ pub enum WindowMessage {
11661168
GtkBox(Sender<GtkBox>),
11671169
RawWindowHandle(Sender<std::result::Result<SendRawWindowHandle, raw_window_handle::HandleError>>),
11681170
Theme(Sender<Theme>),
1171+
IsEnabled(Sender<bool>),
11691172
// Setters
11701173
Center,
11711174
RequestUserAttention(Option<UserAttentionTypeWrapper>),
1175+
SetEnabled(bool),
11721176
SetResizable(bool),
11731177
SetMaximizable(bool),
11741178
SetMinimizable(bool),
@@ -1700,6 +1704,10 @@ impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
17001704
window_getter!(self, WindowMessage::Theme)
17011705
}
17021706

1707+
fn is_enabled(&self) -> Result<bool> {
1708+
window_getter!(self, WindowMessage::IsEnabled)
1709+
}
1710+
17031711
#[cfg(any(
17041712
target_os = "linux",
17051713
target_os = "dragonfly",
@@ -1775,6 +1783,13 @@ impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
17751783
)
17761784
}
17771785

1786+
fn set_enabled(&self, enabled: bool) -> Result<()> {
1787+
send_user_message(
1788+
&self.context,
1789+
Message::Window(self.window_id, WindowMessage::SetEnabled(enabled)),
1790+
)
1791+
}
1792+
17781793
fn set_maximizable(&self, maximizable: bool) -> Result<()> {
17791794
send_user_message(
17801795
&self.context,
@@ -2865,40 +2880,10 @@ fn handle_user_message<T: UserEvent>(
28652880
WindowMessage::Theme(tx) => {
28662881
tx.send(map_theme(&window.theme())).unwrap();
28672882
}
2868-
// Setters
2869-
WindowMessage::Center => {
2870-
#[cfg(not(target_os = "macos"))]
2871-
if let Some(monitor) = window.current_monitor() {
2872-
#[allow(unused_mut)]
2873-
let mut window_size = window.outer_size();
2874-
#[cfg(windows)]
2875-
if window.is_decorated() {
2876-
use windows::Win32::Foundation::RECT;
2877-
use windows::Win32::Graphics::Dwm::{
2878-
DwmGetWindowAttribute, DWMWA_EXTENDED_FRAME_BOUNDS,
2879-
};
2880-
let mut rect = RECT::default();
2881-
let result = unsafe {
2882-
DwmGetWindowAttribute(
2883-
HWND(window.hwnd() as _),
2884-
DWMWA_EXTENDED_FRAME_BOUNDS,
2885-
&mut rect as *mut _ as *mut _,
2886-
std::mem::size_of::<RECT>() as u32,
2887-
)
2888-
};
2889-
if result.is_ok() {
2890-
window_size.height = (rect.bottom - rect.top) as u32;
2891-
}
2892-
}
2893-
window.set_outer_position(calculate_window_center_position(window_size, monitor));
2894-
}
2883+
WindowMessage::IsEnabled(tx) => tx.send(window.is_enabled()).unwrap(),
28952884

2896-
#[cfg(target_os = "macos")]
2897-
{
2898-
let ns_window: &objc2_app_kit::NSWindow = unsafe { &*window.ns_window().cast() };
2899-
ns_window.center();
2900-
}
2901-
}
2885+
// Setters
2886+
WindowMessage::Center => window.center(),
29022887
WindowMessage::RequestUserAttention(request_type) => {
29032888
window.request_user_attention(request_type.map(|r| r.0));
29042889
}
@@ -2919,6 +2904,7 @@ fn handle_user_message<T: UserEvent>(
29192904
WindowMessage::Unmaximize => window.set_maximized(false),
29202905
WindowMessage::Minimize => window.set_minimized(true),
29212906
WindowMessage::Unminimize => window.set_minimized(false),
2907+
WindowMessage::SetEnabled(enabled) => window.set_enabled(enabled),
29222908
WindowMessage::Show => window.set_visible(true),
29232909
WindowMessage::Hide => window.set_visible(false),
29242910
WindowMessage::Close => {
@@ -3421,7 +3407,7 @@ fn handle_user_message<T: UserEvent>(
34213407
let surface = if is_window_transparent {
34223408
if let Ok(context) = softbuffer::Context::new(window.clone()) {
34233409
if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) {
3424-
clear_window_surface(&window, &mut surface);
3410+
window.clear_surface(&mut surface);
34253411
Some(surface)
34263412
} else {
34273413
None
@@ -3499,7 +3485,7 @@ fn handle_event_loop<T: UserEvent>(
34993485
if window.is_window_transparent {
35003486
if let Some(surface) = &mut window.surface {
35013487
if let Some(window) = &window.inner {
3502-
clear_window_surface(window, surface)
3488+
window.clear_surface(surface);
35033489
}
35043490
}
35053491
}
@@ -3842,7 +3828,7 @@ fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
38423828
}
38433829
}
38443830
}
3845-
let position = calculate_window_center_position(window_size, monitor);
3831+
let position = window::calculate_window_center_position(window_size, monitor);
38463832
let logical_position = position.to_logical::<f64>(scale_factor);
38473833
window_builder = window_builder.position(logical_position.x, logical_position.y);
38483834
}
@@ -3914,7 +3900,7 @@ fn create_window<T: UserEvent, F: Fn(RawWindow) + Send + 'static>(
39143900
let surface = if is_window_transparent {
39153901
if let Ok(context) = softbuffer::Context::new(window.clone()) {
39163902
if let Ok(mut surface) = softbuffer::Surface::new(&context, window.clone()) {
3917-
clear_window_surface(&window, &mut surface);
3903+
window.clear_surface(&mut surface);
39183904
Some(surface)
39193905
} else {
39203906
None
@@ -4398,49 +4384,3 @@ fn inner_size(
43984384
) -> TaoPhysicalSize<u32> {
43994385
window.inner_size()
44004386
}
4401-
4402-
fn calculate_window_center_position(
4403-
window_size: TaoPhysicalSize<u32>,
4404-
target_monitor: MonitorHandle,
4405-
) -> TaoPhysicalPosition<i32> {
4406-
#[cfg(windows)]
4407-
{
4408-
use tao::platform::windows::MonitorHandleExtWindows;
4409-
use windows::Win32::Graphics::Gdi::{GetMonitorInfoW, HMONITOR, MONITORINFO};
4410-
let mut monitor_info = MONITORINFO {
4411-
cbSize: std::mem::size_of::<MONITORINFO>() as u32,
4412-
..Default::default()
4413-
};
4414-
let status =
4415-
unsafe { GetMonitorInfoW(HMONITOR(target_monitor.hmonitor() as _), &mut monitor_info) };
4416-
if status.into() {
4417-
let available_width = monitor_info.rcWork.right - monitor_info.rcWork.left;
4418-
let available_height = monitor_info.rcWork.bottom - monitor_info.rcWork.top;
4419-
let x = (available_width - window_size.width as i32) / 2 + monitor_info.rcWork.left;
4420-
let y = (available_height - window_size.height as i32) / 2 + monitor_info.rcWork.top;
4421-
return TaoPhysicalPosition::new(x, y);
4422-
}
4423-
}
4424-
let screen_size = target_monitor.size();
4425-
let monitor_pos = target_monitor.position();
4426-
let x = (screen_size.width as i32 - window_size.width as i32) / 2 + monitor_pos.x;
4427-
let y = (screen_size.height as i32 - window_size.height as i32) / 2 + monitor_pos.y;
4428-
TaoPhysicalPosition::new(x, y)
4429-
}
4430-
4431-
#[cfg(windows)]
4432-
fn clear_window_surface(
4433-
window: &Window,
4434-
surface: &mut softbuffer::Surface<Arc<Window>, Arc<Window>>,
4435-
) {
4436-
let size = window.inner_size();
4437-
if let (Some(width), Some(height)) = (
4438-
std::num::NonZeroU32::new(size.width),
4439-
std::num::NonZeroU32::new(size.height),
4440-
) {
4441-
surface.resize(width, height).unwrap();
4442-
let mut buffer = surface.buffer_mut().unwrap();
4443-
buffer.fill(0);
4444-
let _ = buffer.present();
4445-
}
4446-
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use gtk::prelude::*;
6+
#[cfg(any(
7+
target_os = "linux",
8+
target_os = "dragonfly",
9+
target_os = "freebsd",
10+
target_os = "netbsd",
11+
target_os = "openbsd"
12+
))]
13+
use tao::platform::unix::WindowExtUnix;
14+
15+
impl super::WindowExt for tao::window::Window {
16+
fn set_enabled(&self, enabled: bool) {
17+
self.gtk_window().set_sensitive(enabled);
18+
}
19+
20+
fn is_enabled(&self) -> bool {
21+
self.gtk_window().is_sensitive()
22+
}
23+
24+
fn center(&self) {
25+
if let Some(monitor) = self.current_monitor() {
26+
let window_size = self.outer_size();
27+
let new_pos = super::calculate_window_center_position(window_size, monitor);
28+
self.set_outer_position(new_pos);
29+
}
30+
}
31+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use objc2_app_kit::{NSBackingStoreType, NSWindow, NSWindowStyleMask};
6+
use objc2_foundation::MainThreadMarker;
7+
use tao::platform::macos::WindowExtMacOS;
8+
9+
impl super::WindowExt for tao::window::Window {
10+
// based on electron implementation
11+
// https://github.com/electron/electron/blob/15db63e26df3e3d59ce6281f030624f746518511/shell/browser/native_window_mac.mm#L474
12+
fn set_enabled(&self, enabled: bool) {
13+
let ns_window: &NSWindow = unsafe { &*self.ns_window().cast() };
14+
if !enabled {
15+
let frame = ns_window.frame();
16+
let mtm = MainThreadMarker::new()
17+
.expect("`Window::set_enabled` can only be called on the main thread");
18+
let sheet = unsafe {
19+
NSWindow::initWithContentRect_styleMask_backing_defer(
20+
mtm.alloc(),
21+
frame,
22+
NSWindowStyleMask::Titled,
23+
NSBackingStoreType::NSBackingStoreBuffered,
24+
false,
25+
)
26+
};
27+
unsafe { sheet.setAlphaValue(0.5) };
28+
unsafe { ns_window.beginSheet_completionHandler(&sheet, None) };
29+
} else if let Some(attached) = unsafe { ns_window.attachedSheet() } {
30+
unsafe { ns_window.endSheet(&attached) };
31+
}
32+
}
33+
34+
fn is_enabled(&self) -> bool {
35+
let ns_window: &NSWindow = unsafe { &*self.ns_window().cast() };
36+
unsafe { ns_window.attachedSheet() }.is_none()
37+
}
38+
39+
fn center(&self) {
40+
let ns_window: &NSWindow = unsafe { &*self.ns_window().cast() };
41+
ns_window.center();
42+
}
43+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
#[cfg(any(
6+
target_os = "linux",
7+
target_os = "dragonfly",
8+
target_os = "freebsd",
9+
target_os = "netbsd",
10+
target_os = "openbsd"
11+
))]
12+
mod linux;
13+
#[cfg(target_os = "macos")]
14+
mod macos;
15+
#[cfg(windows)]
16+
mod windows;
17+
18+
pub trait WindowExt {
19+
/// Enable or disable the window
20+
///
21+
/// ## Platform-specific:
22+
///
23+
/// - **Android / iOS**: Unsupported.
24+
fn set_enabled(&self, enabled: bool);
25+
26+
/// Whether the window is enabled or disabled.
27+
///
28+
/// ## Platform-specific:
29+
///
30+
/// - **Android / iOS**: Unsupported, always returns `true`.
31+
fn is_enabled(&self) -> bool;
32+
33+
/// Center the window
34+
///
35+
/// ## Platform-specific:
36+
///
37+
/// - **Android / iOS**: Unsupported.
38+
fn center(&self) {}
39+
40+
/// Clears the window sufrace. i.e make it it transparent.
41+
#[cfg(windows)]
42+
fn clear_surface(
43+
&self,
44+
surface: &mut softbuffer::Surface<
45+
std::sync::Arc<tao::window::Window>,
46+
std::sync::Arc<tao::window::Window>,
47+
>,
48+
);
49+
}
50+
51+
#[cfg(mobile)]
52+
impl WindowExt for tao::window::Window {
53+
fn set_enabled(&self, _: bool) {}
54+
fn is_enabled(&self) -> bool {
55+
true
56+
}
57+
}
58+
59+
pub fn calculate_window_center_position(
60+
window_size: tao::dpi::PhysicalSize<u32>,
61+
target_monitor: tao::monitor::MonitorHandle,
62+
) -> tao::dpi::PhysicalPosition<i32> {
63+
#[cfg(windows)]
64+
{
65+
use ::windows::Win32::Graphics::Gdi::{GetMonitorInfoW, HMONITOR, MONITORINFO};
66+
use tao::platform::windows::MonitorHandleExtWindows;
67+
68+
let mut monitor_info = MONITORINFO {
69+
cbSize: std::mem::size_of::<MONITORINFO>() as u32,
70+
..Default::default()
71+
};
72+
let hmonitor = target_monitor.hmonitor();
73+
let status = unsafe { GetMonitorInfoW(HMONITOR(hmonitor as _), &mut monitor_info) };
74+
if status.into() {
75+
let available_width = monitor_info.rcWork.right - monitor_info.rcWork.left;
76+
let available_height = monitor_info.rcWork.bottom - monitor_info.rcWork.top;
77+
let x = (available_width - window_size.width as i32) / 2 + monitor_info.rcWork.left;
78+
let y = (available_height - window_size.height as i32) / 2 + monitor_info.rcWork.top;
79+
return tao::dpi::PhysicalPosition::new(x, y);
80+
}
81+
}
82+
83+
let screen_size = target_monitor.size();
84+
let monitor_pos = target_monitor.position();
85+
let x = (screen_size.width as i32 - window_size.width as i32) / 2 + monitor_pos.x;
86+
let y = (screen_size.height as i32 - window_size.height as i32) / 2 + monitor_pos.y;
87+
tao::dpi::PhysicalPosition::new(x, y)
88+
}

0 commit comments

Comments
 (0)