Skip to content

Commit

Permalink
feat(core): add request_user_attention API, closes #2023 (#2026)
Browse files Browse the repository at this point in the history
* feat(core): add `request_user_attention` API

* fix: api lint

* fix build without window allowlist
  • Loading branch information
lucasfernog authored Jun 21, 2021
1 parent 08c161c commit 7dcca6e
Show file tree
Hide file tree
Showing 13 changed files with 167 additions and 11 deletions.
5 changes: 5 additions & 0 deletions .changes/api-request-user-attention.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"api": patch
---

Adds `requestUserAttention` API to the `window` module.
5 changes: 5 additions & 0 deletions .changes/core-request-user-attention.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Adds `request_user_attention` API to the `Window` struct.
6 changes: 6 additions & 0 deletions .changes/runtime-request-user-attention.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tauri-runtime": patch
"tauri-runtime-wry": patch
---

Adds `request_user_attention` API to the `Dispatcher` trait.
34 changes: 33 additions & 1 deletion core/tauri-runtime-wry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use tauri_runtime::{
DetachedWindow, PendingWindow, WindowEvent,
},
Dispatch, Error, Icon, Params, Result, RunEvent, RunIteration, Runtime, RuntimeHandle,
UserAttentionType,
};

#[cfg(feature = "menu")]
Expand All @@ -39,7 +40,10 @@ use wry::{
event::{Event, WindowEvent as WryWindowEvent},
event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget},
monitor::MonitorHandle,
window::{Fullscreen, Icon as WindowIcon, Window, WindowBuilder as WryWindowBuilder, WindowId},
window::{
Fullscreen, Icon as WindowIcon, UserAttentionType as WryUserAttentionType, Window,
WindowBuilder as WryWindowBuilder, WindowId,
},
},
webview::{
FileDropEvent as WryFileDropEvent, RpcRequest as WryRpcRequest, RpcResponse, WebContext,
Expand Down Expand Up @@ -245,6 +249,19 @@ impl From<Position> for PositionWrapper {
}
}

#[derive(Debug, Clone)]
struct UserAttentionTypeWrapper(WryUserAttentionType);

impl From<UserAttentionType> for UserAttentionTypeWrapper {
fn from(request_type: UserAttentionType) -> UserAttentionTypeWrapper {
let o = match request_type {
UserAttentionType::Critical => WryUserAttentionType::Critical,
UserAttentionType::Informational => WryUserAttentionType::Informational,
};
Self(o)
}
}

#[derive(Debug, Clone, Default)]
pub struct WindowBuilderWrapper {
inner: WryWindowBuilder,
Expand Down Expand Up @@ -467,6 +484,7 @@ enum WindowMessage {
Hwnd(Sender<Hwnd>),
// Setters
Center(Sender<Result<()>>),
RequestUserAttention(Option<UserAttentionTypeWrapper>),
SetResizable(bool),
SetTitle(String),
Maximize,
Expand Down Expand Up @@ -680,6 +698,17 @@ impl Dispatch for WryDispatcher {
.map_err(|_| Error::FailedToSendMessage)
}

fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> Result<()> {
self
.context
.proxy
.send_event(Message::Window(
self.window_id,
WindowMessage::RequestUserAttention(request_type.map(Into::into)),
))
.map_err(|_| Error::FailedToSendMessage)
}

// Creates a window by dispatching a message to the event loop.
// Note that this must be called from a separate thread, otherwise the channel will introduce a deadlock.
fn create_window<P: Params<Runtime = Self::Runtime>>(
Expand Down Expand Up @@ -1341,6 +1370,9 @@ fn handle_event_loop(
WindowMessage::Center(tx) => {
tx.send(center_window(window)).unwrap();
}
WindowMessage::RequestUserAttention(request_type) => {
window.request_user_attention(request_type.map(|r| r.0));
}
WindowMessage::SetResizable(resizable) => window.set_resizable(resizable),
WindowMessage::SetTitle(title) => window.set_title(&title),
WindowMessage::Maximize => window.set_maximized(true),
Expand Down
21 changes: 20 additions & 1 deletion core/tauri-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

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

use serde::Serialize;
use serde::{Deserialize, Serialize};
use tauri_utils::assets::Assets;
use uuid::Uuid;

Expand Down Expand Up @@ -76,6 +76,20 @@ impl<I: MenuId> SystemTray<I> {
}
}

/// Type of user attention requested on a window.
#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
#[serde(tag = "type")]
pub enum UserAttentionType {
/// ## Platform-specific
/// - **macOS:** Bounces the dock icon until the application is in focus.
/// - **Windows:** Flashes both the window and the taskbar button until the application is in focus.
Critical,
/// ## Platform-specific
/// - **macOS:** Bounces the dock icon once.
/// - **Windows:** Flashes the taskbar button until the application is in focus.
Informational,
}

#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
Expand Down Expand Up @@ -338,6 +352,11 @@ pub trait Dispatch: Clone + Send + Sized + 'static {
/// Opens the dialog to prints the contents of the webview.
fn print(&self) -> crate::Result<()>;

/// Requests user attention to the window.
///
/// Providing `None` will unset the request for user attention.
fn request_user_attention(&self, request_type: Option<UserAttentionType>) -> crate::Result<()>;

/// Create a new webview window.
fn create_window<P: Params<Runtime = Self::Runtime>>(
&mut self,
Expand Down
2 changes: 1 addition & 1 deletion core/tauri/scripts/bundle.js

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion core/tauri/src/endpoints/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use crate::runtime::{webview::WindowBuilder, Dispatch, Runtime};
use crate::{
api::config::WindowConfig,
endpoints::InvokeResponse,
runtime::window::dpi::{Position, Size},
runtime::{
window::dpi::{Position, Size},
UserAttentionType,
},
Params, Window,
};
use serde::Deserialize;
Expand Down Expand Up @@ -54,6 +57,7 @@ pub enum Cmd {
AvailableMonitors,
// Setters
Center,
RequestUserAttention(Option<UserAttentionType>),
SetResizable(bool),
SetTitle(String),
Maximize,
Expand Down Expand Up @@ -143,6 +147,7 @@ impl Cmd {
Self::AvailableMonitors => return Ok(window.available_monitors()?.into()),
// Setters
Self::Center => window.center()?,
Self::RequestUserAttention(request_type) => window.request_user_attention(request_type)?,
Self::SetResizable(resizable) => window.set_resizable(resizable)?,
Self::SetTitle(title) => window.set_title(&title)?,
Self::Maximize => window.maximize()?,
Expand Down
2 changes: 1 addition & 1 deletion core/tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub use {
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
WindowEvent,
},
Icon, MenuId, Params, RunIteration,
Icon, MenuId, Params, RunIteration, UserAttentionType,
},
self::state::{State, StateManager},
self::window::{Monitor, Window},
Expand Down
23 changes: 22 additions & 1 deletion core/tauri/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
DetachedWindow, PendingWindow, WindowEvent,
},
Dispatch, Icon, Params, Runtime,
Dispatch, Icon, Params, Runtime, UserAttentionType,
},
sealed::ManagerBase,
sealed::RuntimeOrDispatch,
Expand Down Expand Up @@ -490,6 +490,27 @@ impl<P: Params> Window<P> {
self.window.dispatcher.center().map_err(Into::into)
}

/// Requests user attention to the window, this has no effect if the application
/// is already focused. How requesting for user attention manifests is platform dependent,
/// see `UserAttentionType` for details.
///
/// Providing `None` will unset the request for user attention. Unsetting the request for
/// user attention might not be done automatically by the WM when the window receives input.
///
/// ## Platform-specific
///
/// - **macOS:** `None` has no effect.
pub fn request_user_attention(
&self,
request_type: Option<UserAttentionType>,
) -> crate::Result<()> {
self
.window
.dispatcher
.request_user_attention(request_type)
.map_err(Into::into)
}

/// Opens the dialog to prints the contents of the webview.
/// Currently only supported on macOS on `wry`.
/// `window.print()` works on all platforms.
Expand Down
Loading

0 comments on commit 7dcca6e

Please sign in to comment.