From c70a8261cb734ab77abcd6fed6dd787844b196a1 Mon Sep 17 00:00:00 2001 From: Andreas Falkenhahn Date: Fri, 14 Aug 2020 19:30:46 +0200 Subject: [PATCH 1/4] Add wxTaskBarIcon::GetPopupMenu() This is similar to CreatePopupMenu(), but the menu pointer returned by the new function won't be deleted by wxWidgets, allowing it to return the same pointer every time it is called. Closes #18886. --- include/wx/taskbar.h | 3 +++ interface/wx/taskbar.h | 28 ++++++++++++++++++++++------ src/common/taskbarcmn.cpp | 7 +++++++ src/osx/cocoa/taskbar.mm | 26 +++++++++++++++++++++++--- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/include/wx/taskbar.h b/include/wx/taskbar.h index c209a1027b..1911e65cf8 100644 --- a/include/wx/taskbar.h +++ b/include/wx/taskbar.h @@ -62,6 +62,9 @@ protected: // creates menu to be displayed when user clicks on the icon virtual wxMenu *CreatePopupMenu() { return NULL; } + // same as CreatePopupMenu but won't destroy the menu + virtual wxMenu *GetPopupMenu() { return NULL; } + private: // default events handling, calls CreatePopupMenu: void OnRightButtonDown(wxTaskBarIconEvent& event); diff --git a/interface/wx/taskbar.h b/interface/wx/taskbar.h index a7ad602dd9..b0c2ce300c 100644 --- a/interface/wx/taskbar.h +++ b/interface/wx/taskbar.h @@ -62,8 +62,9 @@ public: @beginEventEmissionTable{wxTaskBarIconEvent} Note that not all ports are required to send these events and so it's better - to override wxTaskBarIcon::CreatePopupMenu() if all that the application does - is that it shows a popup menu in reaction to mouse click. + to override wxTaskBarIcon::CreatePopupMenu() or wxTaskBarIcon::GetPopupMenu() + if all that the application does is that it shows a popup menu in reaction to + mouse click. @event{EVT_TASKBAR_MOVE(func)} Process a @c wxEVT_TASKBAR_MOVE event. @event{EVT_TASKBAR_LEFT_DOWN(func)} @@ -123,9 +124,10 @@ public: The events can be handled by a class derived from wxTaskBarIcon. @note - It is recommended to override CreatePopupMenu() callback instead of - calling this method from event handler, because some ports (e.g. wxCocoa) - may not implement PopupMenu() and mouse click events at all. + It is recommended to override the CreatePopupMenu() or GetPopupMenu() + callback instead of calling this method from event handler, because some + ports (e.g. wxCocoa) may not implement PopupMenu() and mouse click events + at all. */ virtual bool PopupMenu(wxMenu* menu); @@ -172,11 +174,25 @@ protected: Override this function in order to provide popup menu associated with the icon. If CreatePopupMenu() returns @NULL (this happens by default), no menu is shown, otherwise the menu is displayed and then deleted by the library as soon as the - user dismisses it. + user dismisses it. If you don't want the menu to get destroyed when it is dismissed, + override GetPopupMenu() instead. The events can be handled by a class derived from wxTaskBarIcon. */ virtual wxMenu* CreatePopupMenu(); + + /** + This method is called by the library when the user requests popup menu + (on Windows and Unix platforms, this is when the user right-clicks the icon). + + Override this function in order to provide popup menu associated with the icon. + If GetPopupMenu() returns @NULL (this happens by default), no menu is shown, + otherwise the menu is displayed. In contrast to CreatePopupMenu(), GetPopupMenu() + won't destroy the menu once the user dismisses it. + + The events can be handled by a class derived from wxTaskBarIcon. + */ + virtual wxMenu* GetPopupMenu(); }; diff --git a/src/common/taskbarcmn.cpp b/src/common/taskbarcmn.cpp index cc5908ad06..7bf7c6a58e 100644 --- a/src/common/taskbarcmn.cpp +++ b/src/common/taskbarcmn.cpp @@ -53,6 +53,13 @@ void wxTaskBarIconBase::OnRightButtonDown(wxTaskBarIconEvent& WXUNUSED(event)) PopupMenu(menu); delete menu; } + else + { + menu = GetPopupMenu(); + + if (menu) + PopupMenu(menu); + } } void wxTaskBarIconBase::Destroy() diff --git a/src/osx/cocoa/taskbar.mm b/src/osx/cocoa/taskbar.mm index df66491361..fb263e5565 100644 --- a/src/osx/cocoa/taskbar.mm +++ b/src/osx/cocoa/taskbar.mm @@ -68,6 +68,8 @@ public: inline wxTaskBarIcon* GetTaskBarIcon() { return m_taskBarIcon; } wxMenu * CreatePopupMenu() { return m_taskBarIcon->CreatePopupMenu(); } + wxMenu * GetPopupMenu() + { return m_taskBarIcon->GetPopupMenu(); } wxDECLARE_NO_COPY_CLASS(wxTaskBarIconImpl); @@ -100,6 +102,7 @@ protected: private: wxTaskBarIconDockImpl(); wxMenu *m_pMenu; + bool m_keepMenu; }; class wxTaskBarIconCustomStatusItemImpl; @@ -228,6 +231,7 @@ wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon) wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!")); sm_dockIcon = this; m_pMenu = NULL; + m_keepMenu = false; } wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl() @@ -247,13 +251,27 @@ WX_NSMenu wxTaskBarIconDockImpl::OSXGetDockHMenu() WX_NSMenu wxTaskBarIconDockImpl::OSXDoGetDockHMenu() { wxMenu *dockMenu = CreatePopupMenu(); + bool keepMenu; if(!dockMenu) - return nil; + { + dockMenu = GetPopupMenu(); + + if(!dockMenu) + return nil; - wxDELETE(m_pMenu); + keepMenu = true; + } + else + { + keepMenu = false; + } + + if(!m_keepMenu) + wxDELETE(m_pMenu); m_pMenu = dockMenu; + m_keepMenu = keepMenu; m_pMenu->SetInvokingWindow(m_eventWindow); @@ -271,7 +289,9 @@ bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& WXUNUSED bool wxTaskBarIconDockImpl::RemoveIcon() { - wxDELETE(m_pMenu); + if(!m_keepMenu) + wxDELETE(m_pMenu); + m_icon = wxBitmap(); [[NSApplication sharedApplication] setApplicationIconImage:nil]; return true; From cdd68da3709eb9554a76fccde315b67773006e21 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Aug 2020 19:41:53 +0200 Subject: [PATCH 2/4] Use wxScopedPtr<> instead of explicit calls to "delete" No real changes, but the code is a bit safer and hopefully more clear. --- include/wx/taskbar.h | 6 +++++- src/common/taskbarcmn.cpp | 21 +++++++++++---------- src/osx/cocoa/taskbar.mm | 26 +++++++++----------------- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/include/wx/taskbar.h b/include/wx/taskbar.h index 1911e65cf8..84e4a5c0b0 100644 --- a/include/wx/taskbar.h +++ b/include/wx/taskbar.h @@ -59,10 +59,14 @@ public: void Destroy(); protected: + // Note: only one of the following functions should be overridden, if both + // of them are, GetPopupMenu() has the priority, i.e. CreatePopupMenu() + // won't be called if GetPopupMenu() returns a non-null pointer. + // creates menu to be displayed when user clicks on the icon virtual wxMenu *CreatePopupMenu() { return NULL; } - // same as CreatePopupMenu but won't destroy the menu + // same as CreatePopupMenu but the returned menu won't be destroyed virtual wxMenu *GetPopupMenu() { return NULL; } private: diff --git a/src/common/taskbarcmn.cpp b/src/common/taskbarcmn.cpp index 7bf7c6a58e..ef2a2927e7 100644 --- a/src/common/taskbarcmn.cpp +++ b/src/common/taskbarcmn.cpp @@ -25,6 +25,8 @@ #include "wx/menu.h" #endif +#include "wx/scopedptr.h" + extern WXDLLIMPEXP_DATA_BASE(wxList) wxPendingDelete; // DLL options compatibility check: @@ -47,19 +49,18 @@ wxEND_EVENT_TABLE() void wxTaskBarIconBase::OnRightButtonDown(wxTaskBarIconEvent& WXUNUSED(event)) { - wxMenu *menu = CreatePopupMenu(); - if (menu) + wxScopedPtr menuDeleter; + wxMenu *menu = GetPopupMenu(); + if ( !menu ) { - PopupMenu(menu); - delete menu; - } - else - { - menu = GetPopupMenu(); + menu = CreatePopupMenu(); + if ( !menu ) + return; - if (menu) - PopupMenu(menu); + menuDeleter.reset(menu); } + + PopupMenu(menu); } void wxTaskBarIconBase::Destroy() diff --git a/src/osx/cocoa/taskbar.mm b/src/osx/cocoa/taskbar.mm index fb263e5565..c9a56bab90 100644 --- a/src/osx/cocoa/taskbar.mm +++ b/src/osx/cocoa/taskbar.mm @@ -19,6 +19,7 @@ #include "wx/dcclient.h" #endif +#include "wx/scopedptr.h" #include "wx/taskbar.h" #include "wx/osx/private.h" @@ -102,7 +103,7 @@ protected: private: wxTaskBarIconDockImpl(); wxMenu *m_pMenu; - bool m_keepMenu; + wxScopedPtr m_menuDeleter; }; class wxTaskBarIconCustomStatusItemImpl; @@ -231,7 +232,6 @@ wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon) wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!")); sm_dockIcon = this; m_pMenu = NULL; - m_keepMenu = false; } wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl() @@ -250,28 +250,21 @@ WX_NSMenu wxTaskBarIconDockImpl::OSXGetDockHMenu() WX_NSMenu wxTaskBarIconDockImpl::OSXDoGetDockHMenu() { - wxMenu *dockMenu = CreatePopupMenu(); - bool keepMenu; + m_menuDeleter.reset(); + + wxMenu *dockMenu = GetPopupMenu(); if(!dockMenu) { - dockMenu = GetPopupMenu(); + dockMenu = CreatePopupMenu(); if(!dockMenu) return nil; - - keepMenu = true; - } - else - { - keepMenu = false; - } - if(!m_keepMenu) - wxDELETE(m_pMenu); + m_menuDeleter.reset(dockMenu); + } m_pMenu = dockMenu; - m_keepMenu = keepMenu; m_pMenu->SetInvokingWindow(m_eventWindow); @@ -289,8 +282,7 @@ bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& WXUNUSED bool wxTaskBarIconDockImpl::RemoveIcon() { - if(!m_keepMenu) - wxDELETE(m_pMenu); + m_menuDeleter.reset(); m_icon = wxBitmap(); [[NSApplication sharedApplication] setApplicationIconImage:nil]; From c5161f6296aa80ff46a42558b68fc952390c45dc Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Aug 2020 19:43:00 +0200 Subject: [PATCH 3/4] Get rid of an unnecessary local variable No real changes, just simplify the code a bit more. --- src/osx/cocoa/taskbar.mm | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/osx/cocoa/taskbar.mm b/src/osx/cocoa/taskbar.mm index c9a56bab90..14c80efb51 100644 --- a/src/osx/cocoa/taskbar.mm +++ b/src/osx/cocoa/taskbar.mm @@ -252,25 +252,23 @@ WX_NSMenu wxTaskBarIconDockImpl::OSXDoGetDockHMenu() { m_menuDeleter.reset(); - wxMenu *dockMenu = GetPopupMenu(); + m_pMenu = GetPopupMenu(); - if(!dockMenu) + if (!m_pMenu) { - dockMenu = CreatePopupMenu(); + m_pMenu = CreatePopupMenu(); - if(!dockMenu) + if (!m_pMenu) return nil; - m_menuDeleter.reset(dockMenu); + m_menuDeleter.reset(m_pMenu); } - m_pMenu = dockMenu; - m_pMenu->SetInvokingWindow(m_eventWindow); m_pMenu->UpdateUI(); - return (WX_NSMenu)dockMenu->GetHMenu(); + return (WX_NSMenu)m_pMenu->GetHMenu(); } bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& WXUNUSED(tooltip)) From f04c904b60b1b29c80d0e76145d897780fd728f8 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Fri, 14 Aug 2020 19:48:29 +0200 Subject: [PATCH 4/4] Improve wxTaskBarIcon::Create/GetPopupMenu() documentation Try to make the text more clear and prescriptive. Also add a "@since" tag. --- interface/wx/taskbar.h | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/interface/wx/taskbar.h b/interface/wx/taskbar.h index b0c2ce300c..d9e2183b8d 100644 --- a/interface/wx/taskbar.h +++ b/interface/wx/taskbar.h @@ -168,29 +168,35 @@ public: protected: /** - This method is called by the library when the user requests popup menu - (on Windows and Unix platforms, this is when the user right-clicks the icon). + Called by the library when the user requests popup menu if + GetPopupMenu() is not overridden. - Override this function in order to provide popup menu associated with the icon. - If CreatePopupMenu() returns @NULL (this happens by default), no menu is shown, - otherwise the menu is displayed and then deleted by the library as soon as the - user dismisses it. If you don't want the menu to get destroyed when it is dismissed, - override GetPopupMenu() instead. + Override this function in order to provide popup menu associated with + the icon if you don't want to override GetPopupMenu(), i.e. if you + prefer creating a new menu every time instead of reusing the same menu. - The events can be handled by a class derived from wxTaskBarIcon. + If CreatePopupMenu() returns @NULL (this happens by default), no menu + is shown, otherwise the menu is displayed and then deleted by the + library as soon as the user dismisses it. If you don't want the menu to + be destroyed when it is dismissed, override GetPopupMenu() instead. */ virtual wxMenu* CreatePopupMenu(); /** - This method is called by the library when the user requests popup menu - (on Windows and Unix platforms, this is when the user right-clicks the icon). + Called by the library when the user requests popup menu. - Override this function in order to provide popup menu associated with the icon. - If GetPopupMenu() returns @NULL (this happens by default), no menu is shown, - otherwise the menu is displayed. In contrast to CreatePopupMenu(), GetPopupMenu() - won't destroy the menu once the user dismisses it. + On Windows and Unix platforms this happens when the user right-clicks + the icon, so overriding this method is the simplest way to implement + the standard behaviour of showing a menu when the taskbar icon is + clicked. - The events can be handled by a class derived from wxTaskBarIcon. + If GetPopupMenu() returns @NULL (this happens by default), + CreatePopupMenu() is called next and its menu is used (if non-@NULL). + Otherwise the menu returned by GetPopupMenu() is shown and, contrary to + CreatePopupMenu(), not destroyed when the user dismisses it, allowing + to reuse the same menu pointer multiple times. + + @since 3.1.5 */ virtual wxMenu* GetPopupMenu(); };