Add 'request_user_attention' to Window

This commit introduces a cross platform way to request a user attention
to the window via a 'request_user_attention' method on a Window struct.
This method is inspired by macOS's 'request_user_attention' method and
thus reuses its signature and semantics to some extent.
This commit is contained in:
Max de Danschutter 2020-11-27 03:03:08 +01:00 committed by GitHub
parent f79efec7ef
commit 0861a353d6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 136 additions and 79 deletions

View file

@ -484,6 +484,8 @@ impl Window {
pub fn set_ime_position(&self, _position: Position) {}
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {

View file

@ -22,7 +22,9 @@ use crate::{
},
monitor, view, EventLoopWindowTarget, MonitorHandle,
},
window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWindowId},
window::{
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWindowId,
},
};
pub struct Inner {
@ -260,6 +262,10 @@ impl Inner {
warn!("`Window::set_ime_position` is ignored on iOS")
}
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
warn!("`Window::request_user_attention` is ignored on iOS")
}
// Allow directly accessing the current monitor internally without unwrapping.
fn current_monitor_inner(&self) -> RootMonitorHandle {
unsafe {

View file

@ -30,7 +30,7 @@ use crate::{
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
icon::Icon,
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
window::{CursorIcon, Fullscreen, WindowAttributes},
window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes},
};
pub(crate) use crate::icon::RgbaIcon as PlatformIcon;
@ -418,6 +418,16 @@ impl Window {
x11_or_wayland!(match self; Window(w) => w.set_ime_position(position))
}
#[inline]
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
match self {
#[cfg(feature = "x11")]
&Window::X(ref w) => w.request_user_attention(_request_type),
#[cfg(feature = "wayland")]
_ => (),
}
}
#[inline]
pub fn request_redraw(&self) {
x11_or_wayland!(match self; Window(w) => w.request_redraw())

View file

@ -22,7 +22,7 @@ use crate::{
MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes,
VideoMode as PlatformVideoMode,
},
window::{CursorIcon, Fullscreen, Icon, WindowAttributes},
window::{CursorIcon, Fullscreen, Icon, UserAttentionType, WindowAttributes},
};
use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError};
@ -523,23 +523,6 @@ impl UnownedWindow {
)
}
#[inline]
pub fn set_urgent(&self, is_urgent: bool) {
let mut wm_hints = self
.xconn
.get_wm_hints(self.xwindow)
.expect("`XGetWMHints` failed");
if is_urgent {
(*wm_hints).flags |= ffi::XUrgencyHint;
} else {
(*wm_hints).flags &= !ffi::XUrgencyHint;
}
self.xconn
.set_wm_hints(self.xwindow, wm_hints)
.flush()
.expect("Failed to set urgency hint");
}
fn set_netwm(
&self,
operation: util::StateOperation,
@ -1306,6 +1289,23 @@ impl UnownedWindow {
self.set_ime_position_physical(x, y);
}
#[inline]
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
let mut wm_hints = self
.xconn
.get_wm_hints(self.xwindow)
.expect("`XGetWMHints` failed");
if request_type.is_some() {
(*wm_hints).flags |= ffi::XUrgencyHint;
} else {
(*wm_hints).flags &= !ffi::XUrgencyHint;
}
self.xconn
.set_wm_hints(self.xwindow, wm_hints)
.flush()
.expect("Failed to set urgency hint");
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId(self.xwindow)

View file

@ -16,7 +16,7 @@ use crate::{
error::{ExternalError, NotSupportedError, OsError as RootOsError},
icon::Icon,
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
platform::macos::{ActivationPolicy, RequestUserAttentionType, WindowExtMacOS},
platform::macos::{ActivationPolicy, WindowExtMacOS},
platform_impl::platform::{
app_state::AppState,
app_state::INTERRUPT_EVENT_LOOP_EXIT,
@ -28,7 +28,9 @@ use crate::{
window_delegate::new_delegate,
OsError,
},
window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWindowId},
window::{
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWindowId,
},
};
use cocoa::{
appkit::{
@ -977,6 +979,19 @@ impl UnownedWindow {
}
}
#[inline]
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
let ns_request_type = request_type.map(|ty| match ty {
UserAttentionType::Critical => NSRequestUserAttentionType::NSCriticalRequest,
UserAttentionType::Informational => NSRequestUserAttentionType::NSInformationalRequest,
});
unsafe {
if let Some(ty) = ns_request_type {
NSApp().requestUserAttention_(ty);
}
}
}
#[inline]
// Allow directly accessing the current monitor internally without unwrapping.
pub(crate) fn current_monitor_inner(&self) -> RootMonitorHandle {
@ -1030,18 +1045,6 @@ impl WindowExtMacOS for UnownedWindow {
*self.ns_view as *mut _
}
#[inline]
fn request_user_attention(&self, request_type: RequestUserAttentionType) {
unsafe {
NSApp().requestUserAttention_(match request_type {
RequestUserAttentionType::Critical => NSRequestUserAttentionType::NSCriticalRequest,
RequestUserAttentionType::Informational => {
NSRequestUserAttentionType::NSInformationalRequest
}
});
}
}
#[inline]
fn simple_fullscreen(&self) -> bool {
let shared_state_lock = self.shared_state.lock().unwrap();

View file

@ -3,7 +3,9 @@ use crate::error::{ExternalError, NotSupportedError, OsError as RootOE};
use crate::event;
use crate::icon::Icon;
use crate::monitor::MonitorHandle as RootMH;
use crate::window::{CursorIcon, Fullscreen, WindowAttributes, WindowId as RootWI};
use crate::window::{
CursorIcon, Fullscreen, UserAttentionType, WindowAttributes, WindowId as RootWI,
};
use raw_window_handle::web::WebHandle;
@ -268,6 +270,11 @@ impl Window {
// Currently a no-op as it does not seem there is good support for this on web
}
#[inline]
pub fn request_user_attention(&self, _request_type: Option<UserAttentionType>) {
// Currently an intentional no-op
}
#[inline]
// Allow directly accessing the current monitor internally without unwrapping.
fn current_monitor_inner(&self) -> RootMH {

View file

@ -43,7 +43,7 @@ use crate::{
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
PlatformSpecificWindowBuilderAttributes, WindowId,
},
window::{CursorIcon, Fullscreen, WindowAttributes},
window::{CursorIcon, Fullscreen, UserAttentionType, WindowAttributes},
};
/// The Win32 implementation of the main `Window` object.
@ -621,6 +621,37 @@ impl Window {
warn!("`Window::set_ime_position` is ignored on Windows")
}
#[inline]
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
let window = self.window.clone();
let active_window_handle = unsafe { winuser::GetActiveWindow() };
if window.0 == active_window_handle {
return;
}
self.thread_executor.execute_in_thread(move || unsafe {
let (flags, count) = request_type
.map(|ty| match ty {
UserAttentionType::Critical => {
(winuser::FLASHW_ALL | winuser::FLASHW_TIMERNOFG, u32::MAX)
}
UserAttentionType::Informational => {
(winuser::FLASHW_TRAY | winuser::FLASHW_TIMERNOFG, 0)
}
})
.unwrap_or((winuser::FLASHW_STOP, 0));
let mut flash_info = winuser::FLASHWINFO {
cbSize: mem::size_of::<winuser::FLASHWINFO>() as UINT,
hwnd: window.0,
dwFlags: flags,
uCount: count,
dwTimeout: 0,
};
winuser::FlashWindowEx(&mut flash_info);
});
}
#[inline]
pub fn is_dark_mode(&self) -> bool {
self.window_state.lock().is_dark_mode