tauri
tauri copied to clipboard
[feat] Support Badging API
Describe the problem
Display number of messages on dock or taskbar
Describe the solution you'd like
https://developer.mozilla.org/en-US/docs/Web/API/Badging_API
windows:
- https://github.com/microsoft/windows-rs/blob/75400268554bbc1a6c59124ac480bc67ad223835/crates/libs/windows/src/Windows/UI/Notifications/mod.rs#L169
- https://docs.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/badges
macos: https://developer.apple.com/documentation/appkit/nsdocktile linux unity: https://wiki.ubuntu.com/Unity/LauncherAPI#Count electron
Alternatives considered
No response
Additional context
No response
macOS implement: https://github.com/servo/core-foundation-rs/pull/519
#[cfg(target_os = "macos")]
use cocoa::{appkit::NSApp, base::nil, foundation::NSString};
#[command]
pub fn set_badge(count: i32) {
#[cfg(target_os = "macos")]
unsafe {
let label = if count == 0 {
nil
} else {
NSString::alloc(nil).init_str(&format!("{}", count))
};
let dock_tile: cocoa::base::id = msg_send![NSApp(), dockTile];
let _: cocoa::base::id = msg_send![dock_tile, setBadgeLabel: label];
}
}
usage:
window.__TAURI__?.tauri.invoke('set_badge', { count: 9 })
macOS implement: servo/core-foundation-rs#519
Has anyone managed to implement something similar for Windows and would be so kind to share it?
@aghster and anyone else interested, I didn't end up using the badging API, but if you'd like to recreate this in Windows, you can do so similarly with overlay icons (#2969 may also appreciate this). In my project, I have different icons depending on in-app notification count (up to 9), but here is my (simplified) solution:
#[cfg(target_os = "windows")]
pub unsafe fn set_notif_icon(window: &tauri::Window) {
use windows::Win32::{
System::Com::{
CoCreateInstance,
CLSCTX_ALL,
CoInitialize,
CoUninitialize
},
UI::{
Shell::{ITaskbarList3, TaskbarList},
WindowsAndMessaging::{
CreateIconFromResourceEx,
LR_DEFAULTCOLOR
},
}
};
// init COM
CoInitialize(std::ptr::null()).unwrap_or_default();
let hwnd = window.hwnd();
if hwnd.is_err() {
println!("Failed to get window handle: {:?}", hwnd.err());
return;
}
// Get the bytes of your icon image (eg. a PNG files contents)
let icon = get_my_icon_somehow();
let taskbar_list: Result<ITaskbarList3, windows::core::Error> = CoCreateInstance(
// Do NOT use &ITaskbarList1/2/3/4 in here, only &TaskbarList
&TaskbarList,
None,
CLSCTX_ALL,
);
// check
if taskbar_list.is_err() {
println!(
"Failed to get taskbar list: {:?}",
taskbar_list.err()
);
return;
}
let taskbar_list = taskbar_list.unwrap();
taskbar_list.HrInit().unwrap_or_default();
// do some better error handling here if you think the try_into() may fail.
let hicon = CreateIconFromResourceEx(ico.as_ptr(), ico.len().try_into().unwrap(), true, 0x30000, 32, 32, LR_DEFAULTCOLOR);
if hicon.is_err() {
println!("Failed to create icon: {:?}", hicon.err()));
// this will clear the icon, so it doesn't stay stuck
taskbar_list
.SetOverlayIcon(hwnd, None, None)
.unwrap_or_default();
return;
}
let hicon = hicon.unwrap();
taskbar_list
.SetOverlayIcon(hwnd, hicon, None)
.unwrap_or_default();
CoUninitialize();
}
Make sure to read the docs for the Windows API calls, you might need to change values around (for example, this sets the icon size to 32x32). If you learn better from real-world examples, you can look at the even uglier implementation in Dorion :P
Thanks for Windows and MacOs solution! Maybe anyone knows how to solve it in Linux?
Adding my solution for iOS:
#[cfg(target_os = "ios")]
use objc::runtime::{Class, Object};
use objc::{msg_send, sel, sel_impl};
#[tauri::command]
fn set_badge(count: i32) {
#[cfg(target_os = "ios")]
unsafe {
let ui_application =
Class::get("UIApplication").expect("Failed to get UIApplication class");
let app: *mut Object = msg_send![ui_application, sharedApplication];
let _: () = msg_send![app, setApplicationIconBadgeNumber:count];
}
}