From 38106e7755c0425e173e058dd8b2c57e7e68dd1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Ho=C5=A1ek?= Date: Thu, 20 Oct 2022 20:35:10 +0200 Subject: [PATCH] support multiple tray icons per parent Just like a parent (HWND) can have multiple timers, it can have multiple notification area icons (TrayNotifications) differentiated by an ID. Use similar functionality as with timers to extend support to multiple icons per parent. --- native-windows-gui/src/controls/control_base.rs | 4 ++-- native-windows-gui/src/controls/control_handle.rs | 10 +++++----- .../src/controls/tray_notification.rs | 15 ++++++++++----- native-windows-gui/src/win32/window.rs | 10 +++++++++- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/native-windows-gui/src/controls/control_base.rs b/native-windows-gui/src/controls/control_base.rs index 3bf0cd19..c0b9b726 100644 --- a/native-windows-gui/src/controls/control_base.rs +++ b/native-windows-gui/src/controls/control_base.rs @@ -1,7 +1,7 @@ use winapi::shared::minwindef::DWORD; use winapi::shared::windef::{HWND}; use super::ControlHandle; -use crate::win32::window::{build_hwnd_control, build_timer, build_notice}; +use crate::win32::window::{build_hwnd_control, build_timer, build_notice, build_tray}; use crate::{NwgError}; #[cfg(feature = "menu")] use crate::win32::menu::build_hmenu_control; @@ -277,7 +277,7 @@ impl OtherBuilder { let handle = self.parent.expect("Internal error. Control without window parent"); let base = match self.ty { NOTICE => build_notice(handle), - TRAY => ControlHandle::SystemTray(handle), + TRAY => build_tray(handle), _ => unreachable!() }; diff --git a/native-windows-gui/src/controls/control_handle.rs b/native-windows-gui/src/controls/control_handle.rs index c563a1e6..d0c2f01b 100644 --- a/native-windows-gui/src/controls/control_handle.rs +++ b/native-windows-gui/src/controls/control_handle.rs @@ -27,7 +27,7 @@ pub enum ControlHandle { Timer(HWND, u32), /// System tray control - SystemTray(HWND) + SystemTray(HWND, u32), } impl ControlHandle { @@ -95,9 +95,9 @@ impl ControlHandle { } } - pub fn tray(&self) -> Option { + pub fn tray(&self) -> Option<(HWND, u32)> { match self { - &ControlHandle::SystemTray(h) => Some(h), + &ControlHandle::SystemTray(h, i) => Some((h, i)), _ => None, } } @@ -152,8 +152,8 @@ impl PartialEq for ControlHandle { _ => false }, // System tray - &ControlHandle::SystemTray(hwnd1) => match other { - &ControlHandle::SystemTray(hwnd2) => hwnd1 == hwnd2, + &ControlHandle::SystemTray(hwnd1, id1) => match other { + &ControlHandle::SystemTray(hwnd2, id2) => hwnd1 == hwnd2 && id1 == id2, _ => false } } diff --git a/native-windows-gui/src/controls/tray_notification.rs b/native-windows-gui/src/controls/tray_notification.rs index 7c704f15..122ce483 100644 --- a/native-windows-gui/src/controls/tray_notification.rs +++ b/native-windows-gui/src/controls/tray_notification.rs @@ -218,11 +218,11 @@ impl TrayNotification { fn notify_default(&self) -> NOTIFYICONDATAW { unsafe { - let parent = self.handle.tray().unwrap(); + let (parent, id) = self.handle.tray().unwrap(); NOTIFYICONDATAW { cbSize: mem::size_of::() as u32, hWnd: parent, - uID: 0, + uID: id, uFlags: 0, uCallbackMessage: 0, hIcon: ptr::null_mut(), @@ -332,7 +332,7 @@ impl<'a> TrayNotificationBuilder<'a> { pub fn build(self, out: &mut TrayNotification) -> Result<(), NwgError> { use winapi::um::shellapi::{NIM_ADD, NIF_ICON, NIF_TIP, NIF_SHOWTIP, NIF_INFO, NOTIFYICONDATAW_u, NOTIFYICON_VERSION_4, - NIF_REALTIME, NIF_MESSAGE, NIS_HIDDEN, NIF_STATE}; + NIF_REALTIME, NIF_MESSAGE, NIS_HIDDEN, NIF_STATE, NIM_SETVERSION}; use winapi::shared::windef::HICON; use winapi::um::winnt::WCHAR; @@ -379,6 +379,7 @@ impl<'a> TrayNotificationBuilder<'a> { let handle = ControlBase::build_tray_notification() .parent(parent) .build()?; + let id = handle.tray().unwrap().1; // Tips or infos let mut tip: [WCHAR; 128] = [0; 128]; @@ -411,12 +412,12 @@ impl<'a> TrayNotificationBuilder<'a> { // Creation unsafe { let mut u: NOTIFYICONDATAW_u = mem::zeroed(); - *u.uVersion_mut() = version; + *u.uTimeout_mut() = 0; let mut data = NOTIFYICONDATAW { cbSize: mem::size_of::() as u32, hWnd: parent, - uID: 0, + uID: id, uFlags: flags, uCallbackMessage: wh::NWG_TRAY, hIcon: icon, @@ -432,6 +433,10 @@ impl<'a> TrayNotificationBuilder<'a> { }; Shell_NotifyIconW(NIM_ADD, &mut data); + + // ensure that incoming messages follow the v4 format + *data.u.uVersion_mut() = version; + Shell_NotifyIconW(NIM_SETVERSION, &mut data); } diff --git a/native-windows-gui/src/win32/window.rs b/native-windows-gui/src/win32/window.rs index 115176bf..48a26634 100644 --- a/native-windows-gui/src/win32/window.rs +++ b/native-windows-gui/src/win32/window.rs @@ -22,6 +22,7 @@ use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering}; static TIMER_ID: AtomicU32 = AtomicU32::new(1); static NOTICE_ID: AtomicU32 = AtomicU32::new(1); +static TRAY_ID: AtomicU32 = AtomicU32::new(1); static EVENT_HANDLER_ID: AtomicUsize = AtomicUsize::new(1); const NO_DATA: EventData = EventData::NoData; @@ -70,6 +71,12 @@ pub unsafe fn build_timer(parent: HWND, interval: u32, stopped: bool) -> Control ControlHandle::Timer(parent, id) } + +pub fn build_tray(parent: HWND) -> ControlHandle { + let id = TRAY_ID.fetch_add(1, Ordering::SeqCst); + ControlHandle::SystemTray(parent, id) +} + /** Hook the window subclass with the default event dispatcher. The hook is applied to the window and all it's children (recursively). @@ -665,7 +672,8 @@ unsafe extern "system" fn process_events(hwnd: HWND, msg: UINT, w: WPARAM, l: LP }, NWG_TRAY => { let msg = LOWORD(l as u32) as u32; - let handle = ControlHandle::SystemTray(hwnd); + let icon_id = HIWORD(l as u32) as u32; + let handle = ControlHandle::SystemTray(hwnd, icon_id); match msg { NIN_BALLOONSHOW => callback(Event::OnTrayNotificationShow, NO_DATA, handle),