@@ -100,7 +100,9 @@ func (ni *NotifyIcon) wndProc(hwnd win.HWND, msg uint16, wParam uintptr) {
100100 case win .WM_LBUTTONDOWN :
101101 ni .mouseDownPublisher .Publish (int (win .GET_X_LPARAM (wParam )), int (win .GET_Y_LPARAM (wParam )), LeftButton )
102102
103- case win .WM_LBUTTONUP :
103+ // We treat keyboard selection of the icon identically to a left-click.
104+ // All three messages use the same format for wParam.
105+ case win .NIN_KEYSELECT , win .NIN_SELECT , win .WM_LBUTTONUP :
104106 if ni .activeContextMenus > 0 {
105107 win .PostMessage (hwnd , win .WM_CANCELMODE , 0 , 0 )
106108 break
@@ -197,8 +199,9 @@ func (ni *NotifyIcon) doContextMenu(hwnd win.HWND, x, y int32) {
197199}
198200
199201func isTaskbarPresent () bool {
200- var abd win.APPBARDATA
201- abd .CbSize = uint32 (unsafe .Sizeof (abd ))
202+ abd := win.APPBARDATA {
203+ CbSize : uint32 (unsafe .Sizeof (win.APPBARDATA {})),
204+ }
202205 return win .SHAppBarMessage (win .ABM_GETTASKBARPOS , & abd ) != 0
203206}
204207
@@ -227,7 +230,7 @@ func newNotificationIconWindow() (*notifyIconWindow, error) {
227230 niwCfg := windowCfg {
228231 Window : niw ,
229232 ClassName : notifyIconWindowClass ,
230- Style : win .WS_OVERLAPPEDWINDOW ,
233+ Style : win .WS_OVERLAPPEDWINDOW | win . WS_DISABLED ,
231234 // Always create the window at the origin, thus ensuring that the window
232235 // resides on the desktop's primary monitor, which is the same monitor where
233236 // the taskbar notification area resides. This ensures that the window's
@@ -239,6 +242,10 @@ func newNotificationIconWindow() (*notifyIconWindow, error) {
239242 if err := initWindowWithCfg (& niwCfg ); err != nil {
240243 return nil , err
241244 }
245+
246+ // By default the window has the "client" role, which suggests content.
247+ // Assigning the "window" role instead.
248+ niw .Accessibility ().SetRole (AccRoleWindow )
242249 return niw , nil
243250}
244251
@@ -275,19 +282,10 @@ func newShellNotificationIcon(guid *windows.GUID) (*shellNotificationIcon, error
275282 return shellIcon , nil
276283 }
277284
278- if guid != nil {
279- // If we're using a GUID, an add operation can fail if a previous instance
280- // using this GUID terminated abnormally and its notification icon was left
281- // behind on the taskbar. Preemptively delete any pre-existing icon.
282- if delCmd := shellIcon .newCmd (win .NIM_DELETE ); delCmd != nil {
283- // The previous instance would have used a different, now-defunct HWND, so
284- // we can't use one here...
285- delCmd .nid .HWnd = win .HWND (0 )
286- // We expect delCmd.execute() to fail if there isn't a pre-existing icon,
287- // so no error checking for this call.
288- delCmd .execute ()
289- }
290- }
285+ // If we're using a GUID, an add operation can fail if a previous instance
286+ // using this GUID terminated abnormally and its notification icon was left
287+ // behind on the taskbar. Preemptively delete any pre-existing icon.
288+ shellIcon .clearAnyPreExisting ()
291289
292290 // Add our notify icon to the status area and make sure it is hidden.
293291 addCmd := shellIcon .newCmd (win .NIM_ADD )
@@ -300,13 +298,32 @@ func newShellNotificationIcon(guid *windows.GUID) (*shellNotificationIcon, error
300298 return shellIcon , nil
301299}
302300
301+ // clearAnyPreExisting deletes any GUID-based notification icon that might
302+ // still exist after either the shell restarts or this app restarts. Either
303+ // way, re-adding an icon with the same GUID will fail unless we delete the
304+ // previous instance first.
305+ func (i * shellNotificationIcon ) clearAnyPreExisting () {
306+ // Only meaningful for GUID-based icons.
307+ if i .guid == nil {
308+ return
309+ }
310+
311+ if delCmd := i .newCmd (win .NIM_DELETE ); delCmd != nil {
312+ // The previous instance would have used a different, now-defunct HWND, so
313+ // we can't use one here...
314+ delCmd .nid .HWnd = win .HWND (0 )
315+ // We expect delCmd.execute() to fail if there isn't a pre-existing icon,
316+ // so no error checking for this call.
317+ delCmd .execute ()
318+ }
319+ }
320+
303321func (i * shellNotificationIcon ) setOwner (ni * NotifyIcon ) {
304322 // Only icons identified via GUID use the owner field; non-GUID icons share
305323 // the same window and thus need to be looked up via notifyIconIDs.
306- if i .guid = = nil {
307- return
324+ if i .guid ! = nil {
325+ i . window . owner = ni
308326 }
309- i .window .owner = ni
310327}
311328
312329func (i * shellNotificationIcon ) Dispose () error {
@@ -455,6 +472,13 @@ func (cmd *niCmd) setVisible(v bool) {
455472}
456473
457474func (cmd * niCmd ) execute () error {
475+ var addShowTip bool
476+ if cmd .op == win .NIM_ADD && (cmd .nid .UFlags & win .NIF_SHOWTIP ) != 0 {
477+ // NIF_SHOWTIP is a v4 flag. Don't include it in flags for NIM_ADD, which
478+ // is a v1 operation. We add it back in below, after we've upgraded to v4.
479+ addShowTip = true
480+ cmd .nid .UFlags ^= win .NIF_SHOWTIP
481+ }
458482 if ! win .Shell_NotifyIcon (cmd .op , & cmd .nid ) {
459483 return lastError (fmt .Sprintf ("Shell_NotifyIcon(%d, %#v)" , cmd .op , cmd .nid ))
460484 }
@@ -473,7 +497,14 @@ func (cmd *niCmd) execute() error {
473497 verCmd .op = win .NIM_SETVERSION
474498 // Use Vista+ behaviour.
475499 verCmd .nid .UVersion = win .NOTIFYICON_VERSION_4
476- return verCmd .execute ()
500+ if err := verCmd .execute (); err != nil || ! addShowTip {
501+ return err
502+ }
503+
504+ showTipCmd := * cmd
505+ showTipCmd .op = win .NIM_MODIFY
506+ showTipCmd .nid .UFlags |= win .NIF_SHOWTIP
507+ return showTipCmd .execute ()
477508}
478509
479510// NotifyIcon represents an icon in the taskbar notification area.
@@ -551,6 +582,11 @@ func (ni *NotifyIcon) reAddToTaskbar() {
551582 // track this once the add command successfully executes.
552583 prevID := ni .shellIcon .id
553584
585+ // If we're using a GUID, an add operation can fail if a previous instance
586+ // using this GUID terminated abnormally and its notification icon was left
587+ // behind on the taskbar. Preemptively delete any pre-existing icon.
588+ ni .shellIcon .clearAnyPreExisting ()
589+
554590 cmd := ni .shellIcon .newCmd (win .NIM_ADD )
555591 cmd .setCallbackMessage (notifyIconMessageID )
556592 cmd .setVisible (ni .visible )
0 commit comments