Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add Window::set_enabled and Window::is_enabled #11154

Merged
merged 10 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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