From 12e294347fd44cd9707fac9d9907ae70acc2a69b Mon Sep 17 00:00:00 2001 From: z4122 <412213484@qq.com> Date: Wed, 17 Mar 2021 00:32:59 +0800 Subject: [PATCH] feat: add set_ignore_cursor_events --- CHANGELOG.md | 1 + examples/cursor_grab.rs | 1 + src/platform_impl/android/mod.rs | 4 ++++ src/platform_impl/ios/window.rs | 4 ++++ src/platform_impl/linux/mod.rs | 5 +++++ src/platform_impl/linux/wayland/window/mod.rs | 5 +++++ src/platform_impl/macos/util/async.rs | 9 ++++++++- src/platform_impl/macos/window.rs | 9 +++++++++ src/platform_impl/windows/window.rs | 13 +++++++++++++ src/platform_impl/windows/window_state.rs | 5 +++++ src/window.rs | 14 ++++++++++++++ 11 files changed, 69 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdd48cce4c3..16003e4acc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - On Web, added support for `DeviceEvent::MouseMotion` to listen for relative mouse movements. - Added `Window::drag_window`. Implemented on Windows, macOS, X11 and Wayland. - On X11, bump `mio` to 0.7. +- On macOS and Windows, added `set_ignore_cursor_events` to make window catch or ignore mouse events. # 0.24.0 (2020-12-09) diff --git a/examples/cursor_grab.rs b/examples/cursor_grab.rs index 90a94764de1..7656cb28efc 100644 --- a/examples/cursor_grab.rs +++ b/examples/cursor_grab.rs @@ -36,6 +36,7 @@ fn main() { Escape => *control_flow = ControlFlow::Exit, G => window.set_cursor_grab(!modifiers.shift()).unwrap(), H => window.set_cursor_visible(modifiers.shift()), + I => window.set_ignore_cursor_events(!modifiers.shift()).unwrap(), _ => (), } } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 543bca1b033..699306f7474 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -562,6 +562,10 @@ impl Window { )) } + pub fn set_ignore_cursor_events(&self) -> Result<(), ExternalError> { + Err(ExternalError::NotSupported(NotSupportedError::new())) + } + pub fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { let a_native_window = if let Some(native_window) = ndk_glue::native_window().as_ref() { unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ } diff --git a/src/platform_impl/ios/window.rs b/src/platform_impl/ios/window.rs index 1f6e5591691..ae43c2053ef 100644 --- a/src/platform_impl/ios/window.rs +++ b/src/platform_impl/ios/window.rs @@ -186,6 +186,10 @@ impl Inner { Err(ExternalError::NotSupported(NotSupportedError::new())) } + pub fn set_ignore_cursor_events(&self) -> Result<(), ExternalError> { + Err(ExternalError::NotSupported(NotSupportedError::new())) + } + pub fn set_minimized(&self, _minimized: bool) { warn!("`Window::set_minimized` is ignored on iOS") } diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 480fc8af844..2d16eae2647 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -363,6 +363,11 @@ impl Window { x11_or_wayland!(match self; Window(window) => window.drag_window()) } + #[inline] + pub fn set_ignore_cursor_events(&self) -> Result<(), ExternalError> { + Err(ExternalError::NotSupported(NotSupportedError::new())) + } + #[inline] pub fn scale_factor(&self) -> f64 { x11_or_wayland!(match self; Window(w) => w.scale_factor() as f64) diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 5f59651edbe..fb0ce3b120f 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -598,6 +598,11 @@ impl Window { Ok(()) } + #[inline] + pub fn set_ignore_cursor_events(&self) -> Result<(), ExternalError> { + Err(ExternalError::NotSupported(NotSupportedError::new())) + } + #[inline] pub fn set_ime_position(&self, position: Position) { let scale_factor = self.scale_factor() as f64; diff --git a/src/platform_impl/macos/util/async.rs b/src/platform_impl/macos/util/async.rs index 7f8e7b87145..f0cddeb41a9 100644 --- a/src/platform_impl/macos/util/async.rs +++ b/src/platform_impl/macos/util/async.rs @@ -10,7 +10,7 @@ use cocoa::{ }; use dispatch::Queue; use objc::rc::autoreleasepool; -use objc::runtime::NO; +use objc::runtime::{NO, YES}; use crate::{ dpi::LogicalSize, @@ -88,6 +88,13 @@ pub unsafe fn set_level_async(ns_window: id, level: ffi::NSWindowLevel) { }); } +pub unsafe fn set_window_ignore_cursor_events(ns_window: id, ignore: bool) { + let ns_window = MainThreadSafe(ns_window); + Queue::main().exec_async(move || { + ns_window.setIgnoresMouseEvents_(if ignore { YES } else { NO }); + }); +} + // `toggleFullScreen` is thread-safe, but our additional logic to account for // window styles isn't. pub unsafe fn toggle_full_screen_async( diff --git a/src/platform_impl/macos/window.rs b/src/platform_impl/macos/window.rs index b57fd5ff3cb..9c6ebdb365b 100644 --- a/src/platform_impl/macos/window.rs +++ b/src/platform_impl/macos/window.rs @@ -646,6 +646,15 @@ impl UnownedWindow { Ok(()) } + #[inline] + pub fn set_ignore_cursor_events(&self, ignore: bool) -> Result<(), ExternalError> { + unsafe { + util::set_window_ignore_cursor_events(*self.ns_window, ignore); + } + + ok(()) + } + pub(crate) fn is_zoomed(&self) -> bool { // because `isZoomed` doesn't work if the window's borderless, // we make it resizable temporalily. diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 775fa7b402a..773519b2601 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -381,6 +381,19 @@ impl Window { Ok(()) } + #[inline] + pub fn set_ignore_cursor_events(&self, ignore: bool) -> Result<(), ExternalError> { + let window = self.window.clone(); + let window_state = Arc::clone(&self.window_state); + self.thread_executor.execute_in_thread(move || { + WindowState::set_window_flags(window_state.lock(), window.0, |f| { + f.set(WindowFlags::IGNORE_CURSOR_EVENT, ignore) + }); + }); + + Ok(()) + } + #[inline] pub fn id(&self) -> WindowId { WindowId(self.window.0) diff --git a/src/platform_impl/windows/window_state.rs b/src/platform_impl/windows/window_state.rs index 9ef55232390..5fb455433a8 100644 --- a/src/platform_impl/windows/window_state.rs +++ b/src/platform_impl/windows/window_state.rs @@ -85,6 +85,8 @@ bitflags! { const MINIMIZED = 1 << 12; + const IGNORE_CURSOR_EVENT = 1 << 14; + const EXCLUSIVE_FULLSCREEN_OR_MASK = WindowFlags::ALWAYS_ON_TOP.bits; const NO_DECORATIONS_AND_MASK = !WindowFlags::RESIZABLE.bits; const INVISIBLE_AND_MASK = !WindowFlags::MAXIMIZED.bits; @@ -219,6 +221,9 @@ impl WindowFlags { if self.contains(WindowFlags::MAXIMIZED) { style |= WS_MAXIMIZE; } + if self.contains(WindowFlags::IGNORE_CURSOR_EVENT) { + style_ex |= WS_EX_TRANSPARENT | WS_EX_LAYERED; + } style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_SYSMENU; style_ex |= WS_EX_ACCEPTFILES; diff --git a/src/window.rs b/src/window.rs index fd7f295c154..774caccfea3 100644 --- a/src/window.rs +++ b/src/window.rs @@ -780,6 +780,20 @@ impl Window { pub fn drag_window(&self) -> Result<(), ExternalError> { self.window.drag_window() } + + /// Modifies whether the window catches cursor events. + /// + /// If `false`, this will catch the cursor events. If `true`, this will ignore the cursor. + /// + /// ## Platform-specific + /// + /// - **:** The cursor is only hidden within the confines of the window. + /// - **:** The cursor is only hidden within the confines of the window. + /// - **iOS / Android / Web / X11 / Wayland:** Unsupported. + #[inline] + pub fn set_ignore_cursor_events(&self, ignore: bool) -> Result<(), ExternalError> { + self.window.set_ignore_cursor_events(ignore) + } } /// Monitor info functions.