feat: Implement Badging API (#11661)

This commit is contained in:
AHQ 2024-11-20 06:29:28 +05:30 committed by GitHub
parent a09e48e396
commit 020ea05561
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 345 additions and 7 deletions

11
.changes/overlay.md Normal file
View File

@ -0,0 +1,11 @@
---
"tauri": minor:feat
"tauri-runtime-wry": minor:feat
"tauri-runtime": minor:feat
'@tauri-apps/api': minor:feat
---
Add badging APIs:
- `Window/WebviewWindow::set_badge_count` for Linux, macOS and IOS.
- `Window/WebviewWindow::set_overlay_icon` for Windows Only.
- `Window/WebviewWindow::set_badge_label`for macOS Only.

View File

@ -23,7 +23,7 @@ wry = { version = "0.47", default-features = false, features = [
"os-webview",
"linux-body",
] }
tao = { version = "0.30.6", default-features = false, features = ["rwh_06"] }
tao = { version = "0.30.8", default-features = false, features = ["rwh_06"] }
tauri-runtime = { version = "2.2.0", path = "../tauri-runtime" }
tauri-utils = { version = "2.1.0", path = "../tauri-utils" }
raw-window-handle = "0.6"

View File

@ -95,6 +95,8 @@ use wry::{
)))]
use wry::{WebViewBuilderExtUnix, WebViewExtUnix};
#[cfg(target_os = "ios")]
pub use tao::platform::ios::WindowExtIOS;
#[cfg(target_os = "macos")]
pub use tao::platform::macos::{
ActivationPolicy as TaoActivationPolicy, EventLoopExtMacOS, WindowExtMacOS,
@ -1263,6 +1265,9 @@ pub enum WindowMessage {
SetCursorIcon(CursorIcon),
SetCursorPosition(Position),
SetIgnoreCursorEvents(bool),
SetBadgeCount(Option<i64>, Option<String>),
SetBadgeLabel(Option<String>),
SetOverlayIcon(Option<TaoIcon>),
SetProgressBar(ProgressBarState),
SetTitleBarStyle(tauri_utils::TitleBarStyle),
SetTheme(Option<Theme>),
@ -2125,6 +2130,32 @@ impl<T: UserEvent> WindowDispatch<T> for WryWindowDispatcher<T> {
)
}
fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) -> Result<()> {
send_user_message(
&self.context,
Message::Window(
self.window_id,
WindowMessage::SetBadgeCount(count, desktop_filename),
),
)
}
fn set_badge_label(&self, label: Option<String>) -> Result<()> {
send_user_message(
&self.context,
Message::Window(self.window_id, WindowMessage::SetBadgeLabel(label)),
)
}
fn set_overlay_icon(&self, icon: Option<Icon>) -> Result<()> {
let icon: Result<Option<TaoIcon>> = icon.map_or(Ok(None), |x| Ok(Some(TaoIcon::try_from(x)?)));
send_user_message(
&self.context,
Message::Window(self.window_id, WindowMessage::SetOverlayIcon(icon?)),
)
}
fn set_progress_bar(&self, progress_state: ProgressBarState) -> Result<()> {
send_user_message(
&self.context,
@ -3096,6 +3127,32 @@ fn handle_user_message<T: UserEvent>(
WindowMessage::RequestRedraw => {
window.request_redraw();
}
WindowMessage::SetBadgeCount(_count, _desktop_filename) => {
#[cfg(target_os = "ios")]
window.set_badge_count(
_count.map_or(0, |x| x.clamp(i32::MIN as i64, i32::MAX as i64) as i32),
);
#[cfg(target_os = "macos")]
window.set_badge_label(_count.map(|x| x.to_string()));
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
window.set_badge_count(_count, _desktop_filename);
}
WindowMessage::SetBadgeLabel(_label) => {
#[cfg(target_os = "macos")]
window.set_badge_label(_label);
}
WindowMessage::SetOverlayIcon(_icon) => {
#[cfg(windows)]
window.set_overlay_icon(_icon.map(|x| x.0).as_ref());
}
WindowMessage::SetProgressBar(progress_state) => {
window.set_progress_bar(ProgressBarStateWrapper::from(progress_state).0);
}
@ -3431,7 +3488,6 @@ fn handle_user_message<T: UserEvent>(
}
#[cfg(target_os = "ios")]
{
use tao::platform::ios::WindowExtIOS;
use wry::WebViewExtIOS;
f(Webview {

View File

@ -816,6 +816,24 @@ pub trait WindowDispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + 's
/// Starts resize-dragging the window.
fn start_resize_dragging(&self, direction: ResizeDirection) -> Result<()>;
/// Sets the badge count on the taskbar
/// The badge count appears as a whole for the application
/// Using `0` or using `None` will remove the badge
///
/// ## Platform-specific
/// - **Windows:** Unsupported, use [`WindowDispatch::set_overlay_icon`] instead.
/// - **Android:** Unsupported.
/// - **iOS:** iOS expects i32, if the value is larger than i32::MAX, it will be clamped to i32::MAX.
fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) -> Result<()>;
/// Sets the badge count on the taskbar **macOS only**. Using `None` will remove the badge
fn set_badge_label(&self, label: Option<String>) -> Result<()>;
/// Sets the overlay icon on the taskbar **Windows only**. Using `None` will remove the icon
///
/// The overlay icon can be unique for each window.
fn set_overlay_icon(&self, icon: Option<Icon>) -> Result<()>;
/// Sets the taskbar progress state.
///
/// ## Platform-specific

View File

@ -105,6 +105,9 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
("start_dragging", false),
("start_resize_dragging", false),
("set_progress_bar", false),
("set_badge_count", false),
("set_overlay_icon", false),
("set_badge_label", false),
("set_icon", false),
("set_title_bar_style", false),
("set_theme", false),

View File

@ -898,6 +898,58 @@ Denies the set_always_on_top command without any pre-configured scope.
<tr>
<td>
`core:window:allow-set-badge-count`
</td>
<td>
Enables the set_badge_count command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`core:window:deny-set-badge-count`
</td>
<td>
Denies the set_badge_count command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`core:window:allow-set-badge-label`
</td>
<td>
Enables the set_badge_label command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`core:window:deny-set-badge-label`
</td>
<td>
Denies the set_badge_label command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`core:window:allow-set-closable`
</td>
@ -1340,6 +1392,32 @@ Denies the set_minimizable command without any pre-configured scope.
<tr>
<td>
`core:window:allow-set-overlay-icon`
</td>
<td>
Enables the set_overlay_icon command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`core:window:deny-set-overlay-icon`
</td>
<td>
Denies the set_overlay_icon command without any pre-configured scope.
</td>
</tr>
<tr>
<td>
`core:window:allow-set-position`
</td>

File diff suppressed because one or more lines are too long

View File

@ -977,6 +977,18 @@ impl<T: UserEvent> WindowDispatch<T> for MockWindowDispatcher {
Ok(())
}
fn set_badge_count(&self, count: Option<i64>, desktop_filename: Option<String>) -> Result<()> {
Ok(())
}
fn set_badge_label(&self, label: Option<String>) -> Result<()> {
Ok(())
}
fn set_overlay_icon(&self, icon: Option<Icon<'_>>) -> Result<()> {
Ok(())
}
fn set_title_bar_style(&self, style: tauri_utils::TitleBarStyle) -> Result<()> {
Ok(())
}

View File

@ -1716,6 +1716,32 @@ impl<R: Runtime> WebviewWindow<R> {
self.window.start_dragging()
}
/// Sets the overlay icon on the taskbar **Windows only**. Using `None` will remove the icon
///
/// The overlay icon can be unique for each window.
#[cfg(target_os = "windows")]
#[cfg_attr(docsrs, doc(cfg(target_os = "windows")))]
pub fn set_overlay_icon(&self, icon: Option<Image<'_>>) -> crate::Result<()> {
self.window.set_overlay_icon(icon)
}
/// Sets the taskbar badge count. Using `0` or `None` will remove the badge
///
/// ## Platform-specific
/// - **Windows:** Unsupported, use [`WebviewWindow::set_overlay_icon`] instead.
/// - **iOS:** iOS expects i32, the value will be clamped to i32::MIN, i32::MAX.
/// - **Android:** Unsupported.
pub fn set_badge_count(&self, count: Option<i64>) -> crate::Result<()> {
self.window.set_badge_count(count)
}
/// Sets the taskbar badge label **macOS only**. Using `None` will remove the badge
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
pub fn set_badge_label(&self, label: Option<String>) -> crate::Result<()> {
self.window.set_badge_label(label)
}
/// Sets the taskbar progress state.
///
/// ## Platform-specific

View File

@ -2014,6 +2014,44 @@ tauri::Builder::default()
.map_err(Into::into)
}
/// Sets the overlay icon on the taskbar **Windows only**. Using `None` to remove the overlay icon
///
/// The overlay icon can be unique for each window.
#[cfg(target_os = "windows")]
#[cfg_attr(docsrs, doc(cfg(target_os = "windows")))]
pub fn set_overlay_icon(&self, icon: Option<Image<'_>>) -> crate::Result<()> {
self
.window
.dispatcher
.set_overlay_icon(icon.map(|x| x.into()))
.map_err(Into::into)
}
/// Sets the taskbar badge count. Using `0` or `None` will remove the badge
///
/// ## Platform-specific
/// - **Windows:** Unsupported, use [`Window::set_overlay_icon`] instead.
/// - **iOS:** iOS expects i32, the value will be clamped to i32::MIN, i32::MAX.
/// - **Android:** Unsupported.
pub fn set_badge_count(&self, count: Option<i64>) -> crate::Result<()> {
self
.window
.dispatcher
.set_badge_count(count, Some(format!("{}.desktop", self.package_info().name)))
.map_err(Into::into)
}
/// Sets the taskbar badge label **macOS only**. Using `None` will remove the badge
#[cfg(target_os = "macos")]
#[cfg_attr(docsrs, doc(cfg(target_os = "macos")))]
pub fn set_badge_label(&self, label: Option<String>) -> crate::Result<()> {
self
.window
.dispatcher
.set_badge_label(label)
.map_err(Into::into)
}
/// Sets the taskbar progress state.
///
/// ## Platform-specific

View File

@ -138,12 +138,34 @@ mod desktop_commands {
setter!(start_dragging);
setter!(start_resize_dragging, ResizeDirection);
setter!(set_progress_bar, ProgressBarState);
setter!(set_badge_count, Option<i64>);
#[cfg(target_os = "macos")]
setter!(set_badge_label, Option<String>);
setter!(set_visible_on_all_workspaces, bool);
setter!(set_title_bar_style, TitleBarStyle);
setter!(set_size_constraints, WindowSizeConstraints);
setter!(set_theme, Option<Theme>);
setter!(set_enabled, bool);
#[command(root = "crate")]
#[cfg(target_os = "windows")]
pub async fn set_overlay_icon<R: Runtime>(
webview: Webview<R>,
window: Window<R>,
label: Option<String>,
value: Option<crate::image::JsImage>,
) -> crate::Result<()> {
let window = get_window(window, label)?;
let resources_table = webview.resources_table();
let value = match value {
Some(value) => Some(value.into_img(&resources_table)?.as_ref().clone()),
None => None,
};
window.set_overlay_icon(value).map_err(Into::into)
}
#[command(root = "crate")]
pub async fn set_icon<R: Runtime>(
webview: Webview<R>,
@ -290,7 +312,12 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
desktop_commands::set_ignore_cursor_events,
desktop_commands::start_dragging,
desktop_commands::start_resize_dragging,
desktop_commands::set_badge_count,
#[cfg(target_os = "macos")]
desktop_commands::set_badge_label,
desktop_commands::set_progress_bar,
#[cfg(target_os = "windows")]
desktop_commands::set_overlay_icon,
desktop_commands::set_icon,
desktop_commands::set_visible_on_all_workspaces,
desktop_commands::set_background_color,

View File

@ -56,7 +56,6 @@
"globals": "^15.4.0",
"rollup": "4.27.3",
"tslib": "^2.6.3",
"typescript": "^5.4.5",
"typescript-eslint": "^8.1.0"
}
}

View File

@ -1605,6 +1605,79 @@ class Window {
})
}
/**
* Sets the badge count. It is app wide and not specific to this window.
*
* #### Platform-specific
*
* - **Windows**: Unsupported. Use @{linkcode Window.setOverlayIcon} instead.
*
* @example
* ```typescript
* import { getCurrentWindow } from '@tauri-apps/api/window';
* await getCurrentWindow().setBadgeCount(5);
* ```
*
* @param count The badge count. Use `undefined` to remove the badge.
* @return A promise indicating the success or failure of the operation.
*/
async setBadgeCount(count?: number): Promise<void> {
return invoke('plugin:window|set_badge_count', {
label: this.label,
value: count
})
}
/**
* Sets the badge cont **macOS only**.
*
* @example
* ```typescript
* import { getCurrentWindow } from '@tauri-apps/api/window';
* await getCurrentWindow().setBadgeLabel("Hello");
* ```
*
* @param label The badge label. Use `undefined` to remove the badge.
* @return A promise indicating the success or failure of the operation.
*/
async setBadgeLabel(label?: string): Promise<void> {
return invoke('plugin:window|set_badge_label', {
label: this.label,
value: label
})
}
/**
* Sets the overlay icon. **Windows only**
* The overlay icon can be set for every window.
*
*
* Note that you may need the `image-ico` or `image-png` Cargo features to use this API.
* To enable it, change your Cargo.toml file:
*
* ```toml
* [dependencies]
* tauri = { version = "...", features = ["...", "image-png"] }
* ```
*
* @example
* ```typescript
* import { getCurrentWindow } from '@tauri-apps/api/window';
* await getCurrentWindow().setOverlayIcon("/tauri/awesome.png");
* ```
*
* @param icon Icon bytes or path to the icon file. Use `undefined` to remove the overlay icon.
* @return A promise indicating the success or failure of the operation.
*/
async setOverlayIcon(
icon?: string | Image | Uint8Array | ArrayBuffer | number[]
): Promise<void> {
return invoke('plugin:window|set_overlay_icon', {
label: this.label,
value: icon ? transformImage(icon) : undefined
})
}
/**
* Sets the taskbar progress state.
*

View File

@ -95,9 +95,6 @@ importers:
tslib:
specifier: ^2.6.3
version: 2.8.1
typescript:
specifier: ^5.4.5
version: 5.6.3
typescript-eslint:
specifier: ^8.1.0
version: 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)