use windows::Win32::Foundation::HWND;
use windows::Win32::UI::Shell::{
Shell_NotifyIconW, NIF_ICON, NIF_INFO, NIF_MESSAGE, NIF_TIP, NIIF_NOSOUND, NIM_ADD, NIM_DELETE,
NIM_MODIFY, NOTIFYICONDATAW,
};
use windows::Win32::UI::WindowsAndMessaging::HICON;
fn fill(buf: &mut [u16], s: &str) {
let src: Vec<u16> = s.encode_utf16().take(buf.len().saturating_sub(1)).collect();
buf[..src.len()].copy_from_slice(&src);
buf[src.len()] = 0;
for c in buf.iter_mut().skip(src.len() + 1) {
*c = 0;
}
}
pub struct Tray {
hwnd: HWND,
id: u32,
callback: u32,
added: bool,
}
impl Tray {
pub fn new(hwnd: HWND, id: u32, callback: u32) -> Self {
Tray {
hwnd,
id,
callback,
added: false,
}
}
fn base(&self) -> NOTIFYICONDATAW {
NOTIFYICONDATAW {
cbSize: std::mem::size_of::<NOTIFYICONDATAW>() as u32,
hWnd: self.hwnd,
uID: self.id,
uCallbackMessage: self.callback,
..Default::default()
}
}
pub fn set(&mut self, icon: HICON, tooltip: &str) {
let mut data = self.base();
data.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
data.hIcon = icon;
fill(&mut data.szTip, tooltip);
let msg = if self.added { NIM_MODIFY } else { NIM_ADD };
let ok = unsafe { Shell_NotifyIconW(msg, &data) }.as_bool();
if ok {
self.added = true;
} else if self.added {
self.added = false;
}
}
pub fn notify(&self, title: &str, body: &str) {
if !self.added {
return;
}
let mut data = self.base();
data.uFlags = NIF_INFO;
data.dwInfoFlags = NIIF_NOSOUND;
fill(&mut data.szInfoTitle, title);
fill(&mut data.szInfo, body);
unsafe {
let _ = Shell_NotifyIconW(NIM_MODIFY, &data);
}
}
pub fn forget(&mut self) {
self.added = false;
}
pub fn remove(&mut self) {
if self.added {
let data = self.base();
unsafe {
let _ = Shell_NotifyIconW(NIM_DELETE, &data);
}
self.added = false;
}
}
}
impl Drop for Tray {
fn drop(&mut self) {
self.remove();
}
}