Skip to content

Commit 7dcca6e

Browse files
authored
feat(core): add request_user_attention API, closes #2023 (#2026)
* feat(core): add `request_user_attention` API * fix: api lint * fix build without window allowlist
1 parent 08c161c commit 7dcca6e

File tree

13 files changed

+167
-11
lines changed

13 files changed

+167
-11
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"api": patch
3+
---
4+
5+
Adds `requestUserAttention` API to the `window` module.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch
3+
---
4+
5+
Adds `request_user_attention` API to the `Window` struct.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri-runtime": patch
3+
"tauri-runtime-wry": patch
4+
---
5+
6+
Adds `request_user_attention` API to the `Dispatcher` trait.

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use tauri_runtime::{
1414
DetachedWindow, PendingWindow, WindowEvent,
1515
},
1616
Dispatch, Error, Icon, Params, Result, RunEvent, RunIteration, Runtime, RuntimeHandle,
17+
UserAttentionType,
1718
};
1819

1920
#[cfg(feature = "menu")]
@@ -39,7 +40,10 @@ use wry::{
3940
event::{Event, WindowEvent as WryWindowEvent},
4041
event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget},
4142
monitor::MonitorHandle,
42-
window::{Fullscreen, Icon as WindowIcon, Window, WindowBuilder as WryWindowBuilder, WindowId},
43+
window::{
44+
Fullscreen, Icon as WindowIcon, UserAttentionType as WryUserAttentionType, Window,
45+
WindowBuilder as WryWindowBuilder, WindowId,
46+
},
4347
},
4448
webview::{
4549
FileDropEvent as WryFileDropEvent, RpcRequest as WryRpcRequest, RpcResponse, WebContext,
@@ -245,6 +249,19 @@ impl From<Position> for PositionWrapper {
245249
}
246250
}
247251

252+
#[derive(Debug, Clone)]
253+
struct UserAttentionTypeWrapper(WryUserAttentionType);
254+
255+
impl From<UserAttentionType> for UserAttentionTypeWrapper {
256+
fn from(request_type: UserAttentionType) -> UserAttentionTypeWrapper {
257+
let o = match request_type {
258+
UserAttentionType::Critical => WryUserAttentionType::Critical,
259+
UserAttentionType::Informational => WryUserAttentionType::Informational,
260+
};
261+
Self(o)
262+
}
263+
}
264+
248265
#[derive(Debug, Clone, Default)]
249266
pub struct WindowBuilderWrapper {
250267
inner: WryWindowBuilder,
@@ -467,6 +484,7 @@ enum WindowMessage {
467484
Hwnd(Sender<Hwnd>),
468485
// Setters
469486
Center(Sender<Result<()>>),
487+
RequestUserAttention(Option<UserAttentionTypeWrapper>),
470488
SetResizable(bool),
471489
SetTitle(String),
472490
Maximize,
@@ -680,6 +698,17 @@ impl Dispatch for WryDispatcher {
680698
.map_err(|_| Error::FailedToSendMessage)
681699
}
682700

701+
fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
702+
self
703+
.context
704+
.proxy
705+
.send_event(Message::Window(
706+
self.window_id,
707+
WindowMessage::RequestUserAttention(request_type.map(Into::into)),
708+
))
709+
.map_err(|_| Error::FailedToSendMessage)
710+
}
711+
683712
// Creates a window by dispatching a message to the event loop.
684713
// Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
685714
fn create_window<P: Params<Runtime = Self::Runtime>>(
@@ -1341,6 +1370,9 @@ fn handle_event_loop(
13411370
WindowMessage::Center(tx) => {
13421371
tx.send(center_window(window)).unwrap();
13431372
}
1373+
WindowMessage::RequestUserAttention(request_type) => {
1374+
window.request_user_attention(request_type.map(|r| r.0));
1375+
}
13441376
WindowMessage::SetResizable(resizable) => window.set_resizable(resizable),
13451377
WindowMessage::SetTitle(title) => window.set_title(&title),
13461378
WindowMessage::Maximize => window.set_maximized(true),

core/tauri-runtime/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
use std::{fmt::Debug, hash::Hash, path::PathBuf};
1010

11-
use serde::Serialize;
11+
use serde::{Deserialize, Serialize};
1212
use tauri_utils::assets::Assets;
1313
use uuid::Uuid;
1414

@@ -76,6 +76,20 @@ impl<I: MenuId> SystemTray<I> {
7676
}
7777
}
7878

79+
/// Type of user attention requested on a window.
80+
#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
81+
#[serde(tag = "type")]
82+
pub enum UserAttentionType {
83+
/// ## Platform-specific
84+
/// - **macOS:** Bounces the dock icon until the application is in focus.
85+
/// - **Windows:** Flashes both the window and the taskbar button until the application is in focus.
86+
Critical,
87+
/// ## Platform-specific
88+
/// - **macOS:** Bounces the dock icon once.
89+
/// - **Windows:** Flashes the taskbar button until the application is in focus.
90+
Informational,
91+
}
92+
7993
#[derive(Debug, thiserror::Error)]
8094
#[non_exhaustive]
8195
pub enum Error {
@@ -338,6 +352,11 @@ pub trait Dispatch: Clone + Send + Sized + 'static {
338352
/// Opens the dialog to prints the contents of the webview.
339353
fn print(&self) -> crate::Result<()>;
340354

355+
/// Requests user attention to the window.
356+
///
357+
/// Providing `None` will unset the request for user attention.
358+
fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> crate::Result<()>;
359+
341360
/// Create a new webview window.
342361
fn create_window<P: Params<Runtime = Self::Runtime>>(
343362
&mut self,

core/tauri/scripts/bundle.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/tauri/src/endpoints/window.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ use crate::runtime::{webview::WindowBuilder, Dispatch, Runtime};
77
use crate::{
88
api::config::WindowConfig,
99
endpoints::InvokeResponse,
10-
runtime::window::dpi::{Position, Size},
10+
runtime::{
11+
window::dpi::{Position, Size},
12+
UserAttentionType,
13+
},
1114
Params, Window,
1215
};
1316
use serde::Deserialize;
@@ -54,6 +57,7 @@ pub enum Cmd {
5457
AvailableMonitors,
5558
// Setters
5659
Center,
60+
RequestUserAttention(Option<UserAttentionType>),
5761
SetResizable(bool),
5862
SetTitle(String),
5963
Maximize,
@@ -143,6 +147,7 @@ impl Cmd {
143147
Self::AvailableMonitors => return Ok(window.available_monitors()?.into()),
144148
// Setters
145149
Self::Center => window.center()?,
150+
Self::RequestUserAttention(request_type) => window.request_user_attention(request_type)?,
146151
Self::SetResizable(resizable) => window.set_resizable(resizable)?,
147152
Self::SetTitle(title) => window.set_title(&title)?,
148153
Self::Maximize => window.maximize()?,

core/tauri/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ pub use {
9191
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
9292
WindowEvent,
9393
},
94-
Icon, MenuId, Params, RunIteration,
94+
Icon, MenuId, Params, RunIteration, UserAttentionType,
9595
},
9696
self::state::{State, StateManager},
9797
self::window::{Monitor, Window},

core/tauri/src/window.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
2020
DetachedWindow, PendingWindow, WindowEvent,
2121
},
22-
Dispatch, Icon, Params, Runtime,
22+
Dispatch, Icon, Params, Runtime, UserAttentionType,
2323
},
2424
sealed::ManagerBase,
2525
sealed::RuntimeOrDispatch,
@@ -490,6 +490,27 @@ impl<P: Params> Window<P> {
490490
self.window.dispatcher.center().map_err(Into::into)
491491
}
492492

493+
/// Requests user attention to the window, this has no effect if the application
494+
/// is already focused. How requesting for user attention manifests is platform dependent,
495+
/// see `UserAttentionType` for details.
496+
///
497+
/// Providing `None` will unset the request for user attention. Unsetting the request for
498+
/// user attention might not be done automatically by the WM when the window receives input.
499+
///
500+
/// ## Platform-specific
501+
///
502+
/// - **macOS:** `None` has no effect.
503+
pub fn request_user_attention(
504+
&self,
505+
request_type: Option<UserAttentionType>,
506+
) -> crate::Result<()> {
507+
self
508+
.window
509+
.dispatcher
510+
.request_user_attention(request_type)
511+
.map_err(Into::into)
512+
}
513+
493514
/// Opens the dialog to prints the contents of the webview.
494515
/// Currently only supported on macOS on `wry`.
495516
/// `window.print()` works on all platforms.

0 commit comments

Comments
 (0)