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(core): add request_user_attention API, closes #2023 #2026

Merged
merged 3 commits into from
Jun 21, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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