diff options
author | Jan-Marek Glogowski <glogow@fbihome.de> | 2021-11-17 01:57:33 +0100 |
---|---|---|
committer | Thorsten Behrens <thorsten.behrens@allotropia.de> | 2023-11-19 18:12:23 +0100 |
commit | 0a626ba2073772940fd9a4610d447e00f9afa3f9 (patch) | |
tree | bf83c25a6cf671223c1dda7808a1a6dce5fc0d14 | |
parent | f8730aed0a5481c970cca8bd9f4f217d37bfb856 (diff) |
WIP: async popup menus
A first patch to get some feedback. Asnyc LO popup menus are still
buggy, as I'm not sure where to call the PopupMenu::Finish for
them.
Also the XDialogClosedListener is currently just for convenience
and might want some separate notifier.
Fun fact, that ImplExecute / ImplPopup is called for submenu, but
then doesn't execute. And generally the naming of some variables
is IMHO wrong; I might also prefix Menu members with "m_" for
easier readability.
Change-Id: Id8b413aa6b4699201e58db0113649c6b224d33b6
-rw-r--r-- | include/sfx2/dispatch.hxx | 12 | ||||
-rw-r--r-- | include/toolkit/awt/vclxmenu.hxx | 18 | ||||
-rw-r--r-- | include/vcl/menu.hxx | 15 | ||||
-rw-r--r-- | offapi/UnoApi_offapi.mk | 1 | ||||
-rw-r--r-- | offapi/com/sun/star/awt/XPopupMenuAsync.idl | 51 | ||||
-rw-r--r-- | sd/source/ui/view/drviews4.cxx | 2 | ||||
-rw-r--r-- | sfx2/source/control/dispatch.cxx | 94 | ||||
-rw-r--r-- | sw/source/uibase/docvw/edtwin.cxx | 2 | ||||
-rw-r--r-- | toolkit/inc/helper/unowrapper.hxx | 2 | ||||
-rw-r--r-- | toolkit/source/awt/vclxmenu.cxx | 28 | ||||
-rw-r--r-- | toolkit/source/helper/unowrapper.cxx | 2 | ||||
-rw-r--r-- | vcl/inc/osx/salmenu.h | 4 | ||||
-rw-r--r-- | vcl/inc/qt5/QtMenu.hxx | 10 | ||||
-rw-r--r-- | vcl/inc/salmenu.hxx | 13 | ||||
-rw-r--r-- | vcl/inc/unx/gtk/gtksalmenu.hxx | 3 | ||||
-rw-r--r-- | vcl/osx/salmenu.cxx | 7 | ||||
-rw-r--r-- | vcl/qt5/QtMenu.cxx | 27 | ||||
-rw-r--r-- | vcl/source/app/salvtables.cxx | 4 | ||||
-rw-r--r-- | vcl/source/control/managedmenubutton.cxx | 1 | ||||
-rw-r--r-- | vcl/source/window/menu.cxx | 79 | ||||
-rw-r--r-- | vcl/source/window/menufloatingwindow.cxx | 31 | ||||
-rw-r--r-- | vcl/source/window/menufloatingwindow.hxx | 4 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtksalmenu.cxx | 8 |
23 files changed, 374 insertions, 44 deletions
diff --git a/include/sfx2/dispatch.hxx b/include/sfx2/dispatch.hxx index cfa94a318d5f..36fcc89cc85c 100644 --- a/include/sfx2/dispatch.hxx +++ b/include/sfx2/dispatch.hxx @@ -21,6 +21,7 @@ #include <memory> #include <span> +#include <functional> #include <sal/config.h> #include <rtl/ref.hxx> #include <sfx2/dllapi.h> @@ -44,6 +45,7 @@ class VCLXPopupMenu; namespace com::sun::star::awt { class XPopupMenu; } namespace vcl { class Window; } +namespace com::sun::star::ui::dialogs { struct DialogClosedEvent; } enum class SfxDispatcherPopFlags { @@ -138,8 +140,14 @@ public: SfxViewFrame* GetFrame() const; SfxModule* GetModule() const; - void ExecutePopup( const OUString &rResName, vcl::Window *pWin = nullptr, const Point *pPos = nullptr ); - static void ExecutePopup( vcl::Window *pWin = nullptr, const Point *pPosPixel = nullptr ); + /** + * @param rCloseFunc + * If this is !nullptr, the popup will be just shown / run async and rCloseFunc will be called on close. + */ + void ExecutePopup(const OUString &rResName, vcl::Window *pWin = nullptr, const Point *pPos = nullptr, + const std::function<void(sal_Int16)>& rCloseFunc = nullptr); + static void ExecutePopup(vcl::Window *pWin = nullptr, const Point *pPosPixel = nullptr, + const std::function<void(sal_Int16)>& rCloseFunc = nullptr); bool IsAppDispatcher() const; bool IsFlushed() const; diff --git a/include/toolkit/awt/vclxmenu.hxx b/include/toolkit/awt/vclxmenu.hxx index 3498e8dabe57..68f628f81cd8 100644 --- a/include/toolkit/awt/vclxmenu.hxx +++ b/include/toolkit/awt/vclxmenu.hxx @@ -25,9 +25,10 @@ #include <toolkit/helper/listenermultiplexer.hxx> #include <com/sun/star/awt/XMenuBar.hpp> -#include <com/sun/star/awt/XPopupMenu.hpp> +#include <com/sun/star/awt/XPopupMenuAsync.hpp> #include <com/sun/star/lang/XServiceInfo.hpp> #include <com/sun/star/lang/XTypeProvider.hpp> +#include <com/sun/star/ui/dialogs/XDialogClosedListener.hpp> #include <cppuhelper/weak.hxx> #include <mutex> @@ -37,6 +38,7 @@ #include <vector> +struct DialogClosedEvent; class Menu; class MenuBar; class PopupMenu; @@ -49,9 +51,10 @@ typedef ::std::vector< typedef void (*MenuUserDataReleaseFunction)(void*); class TOOLKIT_DLLPUBLIC VCLXMenu : public css::awt::XMenuBar, - public css::awt::XPopupMenu, + public css::awt::XPopupMenuAsync, public css::lang::XServiceInfo, public css::lang::XTypeProvider, + public css::ui::dialogs::XDialogClosedListener, public ::cppu::OWeakObject { private: @@ -73,7 +76,6 @@ public: VCLXMenu( Menu* pMenu ); virtual ~VCLXMenu() override; - Menu* GetMenu() const { return mpMenu; } bool IsPopupMenu() const; void setUserValue(sal_uInt16 nItemId, void* nUserValue, MenuUserDataReleaseFunction aFunc); @@ -131,10 +133,20 @@ public: virtual void SAL_CALL setItemImage( ::sal_Int16 nItemId, const css::uno::Reference< css::graphic::XGraphic >& xGraphic, sal_Bool bScale ) override; virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL getItemImage( ::sal_Int16 nItemId ) override; + // css::awt::XPopupMenuAsync + virtual sal_Bool SAL_CALL popup(const css::uno::Reference< css::awt::XWindowPeer >& Parent, const css::awt::Rectangle& Position, + ::sal_Int16 Direction, const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>& xListener) override; + // css::lang::XServiceInfo virtual OUString SAL_CALL getImplementationName( ) override; virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // css::ui::dialogs::XDialogClosedListener + virtual void SAL_CALL dialogClosed(const css::ui::dialogs::DialogClosedEvent& aEvent) override; + + // XEventListener (base of XDialogClosedListener) + virtual void SAL_CALL disposing(css::lang::EventObject const & Source) override; }; class UNLESS_MERGELIBS(TOOLKIT_DLLPUBLIC) VCLXMenuBar final : public VCLXMenu diff --git a/include/vcl/menu.hxx b/include/vcl/menu.hxx index 2442eef9202f..c71f4513e5a1 100644 --- a/include/vcl/menu.hxx +++ b/include/vcl/menu.hxx @@ -44,6 +44,7 @@ class Menu; class MenuItemList; class Image; class PopupMenu; +struct PopupMenuFinishState; class KeyEvent; class MenuFloatingWindow; class SalMenu; @@ -54,6 +55,7 @@ enum class FloatWinPopupFlags; enum class VclEventId; namespace com::sun::star::awt { class XPopupMenu; } +namespace com::sun::star::ui::dialogs { class XDialogClosedListener; } namespace com::sun::star::accessibility { class XAccessible; } namespace vcl @@ -499,11 +501,16 @@ class VCL_DLLPUBLIC PopupMenu final : public Menu friend struct MenuItemData; private: + struct PopupMenuFinishState* m_pState; + SAL_DLLPRIVATE MenuFloatingWindow * ImplGetFloatingWindow() const; SAL_DLLPRIVATE bool PrepareRun(const VclPtr<vcl::Window>& pParentWin, tools::Rectangle& rRect, FloatWinPopupFlags& nPopupModeFlags, Menu* pSFrom, bool& bRealExecute, VclPtr<MenuFloatingWindow>&); - SAL_DLLPRIVATE bool Run(const VclPtr<MenuFloatingWindow>&, bool bRealExecute, bool bPreSelectFirst, FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, const tools::Rectangle& rRect); + SAL_DLLPRIVATE bool Run(const VclPtr<MenuFloatingWindow>&, bool bRealExecute, bool bPreSelectFirst, FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, const tools::Rectangle& rRect, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>* xListener); SAL_DLLPRIVATE void FinishRun(const VclPtr<MenuFloatingWindow>&, const VclPtr<vcl::Window>& pParentWin, bool bRealExecute, bool bIsNativeMenu); SAL_DLLPRIVATE sal_uInt16 ImplExecute(const VclPtr<vcl::Window>& pParentWin, const tools::Rectangle& rRect, FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst); + SAL_DLLPRIVATE bool ImplPopup(const VclPtr<vcl::Window>& pParentWin, const tools::Rectangle& rRect, FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>&); SAL_DLLPRIVATE void ImplFlushPendingSelect(); SAL_DLLPRIVATE tools::Long ImplCalcHeight( sal_uInt16 nEntries ) const; SAL_DLLPRIVATE sal_uInt16 ImplCalcVisEntries( tools::Long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible = nullptr ) const; @@ -526,6 +533,12 @@ public: sal_uInt16 Execute( vcl::Window* pWindow, const Point& rPopupPos ); sal_uInt16 Execute( vcl::Window* pWindow, const tools::Rectangle& rRect, PopupMenuFlags nFlags = PopupMenuFlags::NONE ); + bool Popup(vcl::Window* pParentWin, const Point& rPopupPos, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>&); + bool Popup(vcl::Window* pParentWin, const tools::Rectangle& rRect, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>&, PopupMenuFlags = PopupMenuFlags::NONE); + void Finish(); + // for the TestTool void EndExecute(); virtual void SelectItem(sal_uInt16 nId) override; diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk index 2e2ee40aa8b4..3eb7f5224f68 100644 --- a/offapi/UnoApi_offapi.mk +++ b/offapi/UnoApi_offapi.mk @@ -1860,6 +1860,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/awt,\ XPatternField \ XPointer \ XPopupMenu \ + XPopupMenuAsync \ XPrinter \ XPrinterPropertySet \ XPrinterServer \ diff --git a/offapi/com/sun/star/awt/XPopupMenuAsync.idl b/offapi/com/sun/star/awt/XPopupMenuAsync.idl new file mode 100644 index 000000000000..d3f7b317f086 --- /dev/null +++ b/offapi/com/sun/star/awt/XPopupMenuAsync.idl @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ +#ifndef __com_sun_star_awt_XPopupMenuAsync_idl__ +#define __com_sun_star_awt_XPopupMenuAsync_idl__ + +#include <com/sun/star/awt/XPopupMenu.idl> +#include <com/sun/star/ui/dialogs/XDialogClosedListener.idl> + +module com { module sun { module star { module awt { + +/** shows a pop-up menu without blocking. + */ +interface XPopupMenuAsync: XPopupMenu +{ + /** just shows the popup menu without blocking and calls the + XDialogClosedListener when closed, + + @param Parent + the parent window. + + @param Position + a Rectangle representing the coordinates system + where the popup menu should be executed. + + @param Direction + the direction in which a popup menu will grow, as specified + by one of the PopupMenuDirection constants. + + @param xListener + notified, if the popup is closed. + + @return + returns true, if the popup has started to run async. + May fail, if the native backend doesn't implement async popups. + */ + boolean popup([in] XWindowPeer Parent, [in] Rectangle Position, [in] short Direction, + [in] ::com::sun::star::ui::dialogs::XDialogClosedListener xListener); +}; + +}; }; }; }; + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews4.cxx b/sd/source/ui/view/drviews4.cxx index 4226f0e4e8d8..a338c525f746 100644 --- a/sd/source/ui/view/drviews4.cxx +++ b/sd/source/ui/view/drviews4.cxx @@ -805,7 +805,7 @@ void DrawViewShell::Command(const CommandEvent& rCEvt, ::sd::Window* pWin) GetActiveWindow()->ReleaseMouse(); if(rCEvt.IsMouseEvent()) - GetViewFrame()->GetDispatcher()->ExecutePopup( aPopupId ); + GetViewFrame()->GetDispatcher()->ExecutePopup(aPopupId, nullptr, nullptr, [](sal_Int16){}); else { //don't open contextmenu at mouse position if not opened via mouse diff --git a/sfx2/source/control/dispatch.cxx b/sfx2/source/control/dispatch.cxx index 6a492bffda7e..dd028723acc8 100644 --- a/sfx2/source/control/dispatch.cxx +++ b/sfx2/source/control/dispatch.cxx @@ -60,6 +60,7 @@ #include <svl/eitem.hxx> #include <svl/itemiter.hxx> #include <svl/itempool.hxx> +#include <svtools/dialogclosedlistener.hxx> #include <toolkit/awt/vclxmenu.hxx> #include <toolkit/helper/vclunohelper.hxx> #include <tools/debug.hxx> @@ -137,8 +138,12 @@ struct SfxDispatcher_Impl SfxDisableFlags nDisableFlags; bool bFlushed; std::deque< std::deque<SfxToDo_Impl> > aToDoCopyStack; + + static css::uno::Reference<css::frame::XPopupMenuController>* m_pActivePopupController; }; +css::uno::Reference<css::frame::XPopupMenuController>* SfxDispatcher_Impl::m_pActivePopupController = nullptr; + /** This method checks if the stack of the SfxDispatchers is flushed, or if push- or pop- commands are pending. */ @@ -1713,7 +1718,52 @@ bool SfxDispatcher::FillState_(const SfxSlotServer& rSvr, SfxItemSet& rState, return false; } -void SfxDispatcher::ExecutePopup( vcl::Window *pWin, const Point *pPos ) +namespace { + +static void lcl_FinishPopupDispatch(css::uno::Reference<css::frame::XPopupMenuController> xPopupController) +{ + css::uno::Reference<css::lang::XComponent> xComponent(xPopupController, css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + SfxDispatcher_Impl::m_pActivePopupController = nullptr; +} + +struct SfxDispatcherPopupFinish final +{ + css::uno::Reference<css::frame::XPopupMenuController> m_xPopupController; + std::function<void(sal_Int16)> m_aCloseFunc; + rtl::Reference<::svt::DialogClosedListener> m_xDialogListener; + css::uno::Reference<css::awt::XPopupMenu> m_xPopupMenu; + + DECL_LINK(PopupClosedHdl, css::ui::dialogs::DialogClosedEvent*, void); + + SfxDispatcherPopupFinish(css::uno::Reference<css::frame::XPopupMenuController> xPopupController, + const std::function<void(sal_Int16)>& rCloseFunc, + css::uno::Reference<css::awt::XPopupMenu> xPopupMenu) + : m_xPopupController(xPopupController) + , m_aCloseFunc(rCloseFunc) + , m_xDialogListener(new ::svt::DialogClosedListener()) + , m_xPopupMenu(xPopupMenu) + { + m_xDialogListener->SetDialogClosedLink(LINK(this, SfxDispatcherPopupFinish, PopupClosedHdl)); + } +}; + +IMPL_LINK(SfxDispatcherPopupFinish, PopupClosedHdl, css::ui::dialogs::DialogClosedEvent*, pEvt, void) +{ + assert(m_xPopupController.is()); + if (!comphelper::LibreOfficeKit::isActive()) + assert(SfxDispatcher_Impl::m_pActivePopupController == &m_xPopupController); + if (m_aCloseFunc) + m_aCloseFunc(pEvt ? pEvt->DialogResult : 0); + lcl_FinishPopupDispatch(m_xPopupController); + delete this; +} + +} // anon namespace + +void SfxDispatcher::ExecutePopup(vcl::Window *pWin, const Point *pPos, + const std::function<void(sal_Int16)>& rCloseFunc) { SfxDispatcher &rDisp = *SfxGetpApp()->GetDispatcher_Impl(); sal_uInt16 nShLevel = 0; @@ -1727,7 +1777,7 @@ void SfxDispatcher::ExecutePopup( vcl::Window *pWin, const Point *pPos ) const OUString& rResName = pSh->GetInterface()->GetPopupMenuName(); if ( !rResName.isEmpty() ) { - rDisp.ExecutePopup( rResName, pWin, pPos ); + rDisp.ExecutePopup(rResName, pWin, pPos, rCloseFunc); return; } } @@ -1835,7 +1885,8 @@ boost::property_tree::ptree SfxDispatcher::fillPopupMenu(const rtl::Reference<VC return ::fillPopupMenu(pVCLMenu); } -void SfxDispatcher::ExecutePopup( const OUString& rResName, vcl::Window* pWin, const Point* pPos ) +void SfxDispatcher::ExecutePopup( const OUString& rResName, vcl::Window* pWin, const Point* pPos, + const std::function<void(sal_Int16)>& rCloseFunc) { css::uno::Sequence< css::uno::Any > aArgs{ css::uno::Any(comphelper::makePropertyValue( "Value", rResName )), @@ -1844,15 +1895,29 @@ void SfxDispatcher::ExecutePopup( const OUString& rResName, vcl::Window* pWin, c }; css::uno::Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); - css::uno::Reference< css::frame::XPopupMenuController > xPopupController( - xContext->getServiceManager()->createInstanceWithArgumentsAndContext( - "com.sun.star.comp.framework.ResourceMenuController", aArgs, xContext ), css::uno::UNO_QUERY ); + + if (!comphelper::LibreOfficeKit::isActive()) + assert(!xImp->m_pActivePopupController); + if (!comphelper::LibreOfficeKit::isActive() && xImp->m_pActivePopupController) + return; + + css::uno::Reference<css::frame::XPopupMenuController> xPopupController = + css::uno::Reference<css::frame::XPopupMenuController>( + xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.framework.ResourceMenuController", aArgs, xContext), css::uno::UNO_QUERY); + if (!xPopupController.is()) + return; + SfxDispatcher_Impl::m_pActivePopupController = &xPopupController; rtl::Reference< VCLXPopupMenu > xPopupMenu = new VCLXPopupMenu(); - if ( !xPopupController.is() || !xPopupMenu.is() ) + if ( !xPopupMenu.is() ) + { + lcl_FinishPopupDispatch(xPopupController); return; + } + struct SfxDispatcherPopupFinish* pFin = nullptr; vcl::Window* pWindow = pWin ? pWin : xImp->pFrame->GetFrame().GetWorkWindow_Impl()->GetWindow(); Point aPos = pPos ? *pPos : pWindow->GetPointerPosPixel(); @@ -1878,14 +1943,21 @@ void SfxDispatcher::ExecutePopup( const OUString& rResName, vcl::Window* pWin, c OUString aMenuURL = "private:resource/popupmenu/" + rResName; if (GetFrame()->GetViewShell()->TryContextMenuInterception(xPopupMenu, aMenuURL, aEvent)) { + const sal_Int16 nFlags = css::awt::PopupMenuDirection::EXECUTE_DOWN; + const css::awt::Rectangle aRect(aPos.X(), aPos.Y(), 1, 1); css::uno::Reference<css::awt::XWindowPeer> xParent(aEvent.SourceWindow, css::uno::UNO_QUERY); - xPopupMenu->execute(xParent, css::awt::Rectangle(aPos.X(), aPos.Y(), 1, 1), css::awt::PopupMenuDirection::EXECUTE_DOWN); + pFin = new SfxDispatcherPopupFinish(xPopupController, rCloseFunc, xPopupMenu); + if (!rCloseFunc || !xPopupMenu->popup(xParent, aRect, nFlags, pFin->m_xDialogListener)) + { + delete pFin; + pFin = nullptr; + xPopupMenu->execute(xParent, aRect, nFlags); + } } } - css::uno::Reference< css::lang::XComponent > xComponent( xPopupController, css::uno::UNO_QUERY ); - if ( xComponent.is() ) - xComponent->dispose(); + if (!pFin) + lcl_FinishPopupDispatch(xPopupController); } /** With this method the SfxDispatcher can be locked and released. A locked diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx index 322928eb4ead..12567a862441 100644 --- a/sw/source/uibase/docvw/edtwin.cxx +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -5644,7 +5644,7 @@ void SwEditWin::Command( const CommandEvent& rCEvt ) } } else if ( !m_rView.ExecSpellPopup( aDocPos ) ) - SfxDispatcher::ExecutePopup(this, &aPixPos); + SfxDispatcher::ExecutePopup(this, &aPixPos, [](sal_Int16){}); } else if (m_pApplyTempl->nUndo < rSh.GetDoc()->GetIDocumentUndoRedo().GetUndoActionCount()) { diff --git a/toolkit/inc/helper/unowrapper.hxx b/toolkit/inc/helper/unowrapper.hxx index dfed1b1215f1..d1377228d772 100644 --- a/toolkit/inc/helper/unowrapper.hxx +++ b/toolkit/inc/helper/unowrapper.hxx @@ -56,7 +56,7 @@ public: virtual VclPtr<vcl::Window> GetWindow(const css::uno::Reference<css::awt::XWindow>& rxWindow) override; // Menu - virtual css::uno::Reference<css::awt::XPopupMenu> CreateMenuInterface( PopupMenu* pPopupMenu ) override; + virtual css::uno::Reference<css::awt::XPopupMenu> CreateMenuInterface(PopupMenu* pPopupMenu) override; void WindowDestroyed( vcl::Window* pWindow ) override; diff --git a/toolkit/source/awt/vclxmenu.cxx b/toolkit/source/awt/vclxmenu.cxx index 37785849c551..7e4358123897 100644 --- a/toolkit/source/awt/vclxmenu.cxx +++ b/toolkit/source/awt/vclxmenu.cxx @@ -35,6 +35,7 @@ #include <vcl/window.hxx> #include <com/sun/star/awt/KeyModifier.hpp> +#include <com/sun/star/ui/dialogs/DialogClosedEvent.hpp> VCLXMenu::VCLXMenu() : maMenuListeners( *this ) @@ -488,6 +489,33 @@ sal_Int16 VCLXMenu::execute( static_cast<PopupMenuFlags>(nFlags) | PopupMenuFlags::NoMouseUpClose ); } +sal_Bool VCLXMenu::popup( + const css::uno::Reference< css::awt::XWindowPeer >& rxWindowPeer, + const css::awt::Rectangle& rPos, sal_Int16 nFlags, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>& xListener) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + if (!mpMenu || !IsPopupMenu()) + return false; + + return static_cast<PopupMenu*>(mpMenu.get())->Popup(VCLUnoHelper::GetWindow(rxWindowPeer), + VCLRectangle(rPos), xListener.is() ? xListener : this, + static_cast<PopupMenuFlags>(nFlags) | PopupMenuFlags::NoMouseUpClose); +} + +void SAL_CALL VCLXMenu::dialogClosed(const css::ui::dialogs::DialogClosedEvent&) +{ + SolarMutexGuard aSolarGuard; + std::unique_lock aGuard( maMutex ); + + assert(mpMenu && IsPopupMenu()); + if (mpMenu && IsPopupMenu()) + static_cast<PopupMenu*>(mpMenu.get())->Finish(); +} + +void SAL_CALL VCLXMenu::disposing(css::lang::EventObject const&) {} void SAL_CALL VCLXMenu::setCommand( sal_Int16 nItemId, diff --git a/toolkit/source/helper/unowrapper.cxx b/toolkit/source/helper/unowrapper.cxx index 82b4dd17338a..b818377ffa27 100644 --- a/toolkit/source/helper/unowrapper.cxx +++ b/toolkit/source/helper/unowrapper.cxx @@ -196,7 +196,7 @@ void UnoWrapper::SetWindowInterface( vcl::Window* pWindow, const css::uno::Refer } } -css::uno::Reference<css::awt::XPopupMenu> UnoWrapper::CreateMenuInterface( PopupMenu* pPopupMenu ) +css::uno::Reference<css::awt::XPopupMenu> UnoWrapper::CreateMenuInterface(PopupMenu* pPopupMenu) { return new VCLXPopupMenu(pPopupMenu); } diff --git a/vcl/inc/osx/salmenu.h b/vcl/inc/osx/salmenu.h index 597180cc1ac3..dae1a1035cae 100644 --- a/vcl/inc/osx/salmenu.h +++ b/vcl/inc/osx/salmenu.h @@ -67,7 +67,9 @@ public: virtual void SetItemImage( unsigned nPos, SalMenuItem* pSalMenuItem, const Image& rImage) override; virtual void SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const vcl::KeyCode& rKeyCode, const OUString& rKeyName ) override; virtual void GetSystemMenuData( SystemMenuData* pData ) override; - virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags) override; + virtual bool ShowNativePopupMenu( + FloatingWindow*, const tools::Rectangle&, FloatWinPopupFlags, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>* = nullptr) override; virtual bool AddMenuBarButton( const SalMenuButtonItem& ) override; virtual void RemoveMenuBarButton( sal_uInt16 nId ) override; virtual tools::Rectangle GetMenuBarButtonRectPixel( sal_uInt16 i_nItemId, SalFrame* i_pReferenceFrame ) override; diff --git a/vcl/inc/qt5/QtMenu.hxx b/vcl/inc/qt5/QtMenu.hxx index 587e1cfea8d1..1ae079308009 100644 --- a/vcl/inc/qt5/QtMenu.hxx +++ b/vcl/inc/qt5/QtMenu.hxx @@ -40,7 +40,7 @@ class QtFrame; class QtMenu : public QObject, public SalMenu { Q_OBJECT -private: + std::vector<QtMenuItem*> maItems; VclPtr<Menu> mpVCLMenu; QtMenu* mpParentSalMenu; @@ -56,6 +56,9 @@ private: // help ID of currently/last selected item static OUString m_sCurrentHelpId; + css::uno::Reference<css::ui::dialogs::XDialogClosedListener> m_xListener; + FloatingWindow* m_pWin; + void DoFullMenuUpdate(Menu* pMenuBar); static void NativeItemText(OUString& rItemText); @@ -81,8 +84,9 @@ public: virtual void SetSubMenu(SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos) override; virtual void SetFrame(const SalFrame* pFrame) override; virtual void ShowMenuBar(bool bVisible) override; - virtual bool ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangle& rRect, - FloatWinPopupFlags nFlags) override; + virtual bool ShowNativePopupMenu( + FloatingWindow*, const tools::Rectangle&, FloatWinPopupFlags, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>* = nullptr) override; QtMenu* GetTopLevel(); virtual void SetItemBits(unsigned nPos, MenuItemBits nBits) override; virtual void CheckItem(unsigned nPos, bool bCheck) override; diff --git a/vcl/inc/salmenu.hxx b/vcl/inc/salmenu.hxx index 975df9391eae..20bd203c5ad3 100644 --- a/vcl/inc/salmenu.hxx +++ b/vcl/inc/salmenu.hxx @@ -23,6 +23,7 @@ #include <utility> #include <vcl/menu.hxx> #include <vcl/image.hxx> +#include <com/sun/star/ui/dialogs/XDialogClosedListener.hpp> struct SystemMenuData; class FloatingWindow; @@ -74,7 +75,17 @@ public: virtual void SetItemImage( unsigned nPos, SalMenuItem* pSalMenuItem, const Image& rImage ) = 0; virtual void SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const vcl::KeyCode& rKeyCode, const OUString& rKeyName ) = 0; virtual void GetSystemMenuData( SystemMenuData* pData ) = 0; - virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags); + /** + * @param pListener + * if !nullptr, the menu is supposed to be only shown and it'll run async. + * Mainly means, when the menu is hidden, it must call the listener's + * dialogClosed. The listener will destroy the SalMenu! + * + * @return + * true, if the feature is implemented and was successful. + */ + virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>* pListener = nullptr); virtual void ShowCloseButton(bool bShow); virtual bool AddMenuBarButton( const SalMenuButtonItem& ); // return false if not implemented or failure virtual void RemoveMenuBarButton( sal_uInt16 nId ); diff --git a/vcl/inc/unx/gtk/gtksalmenu.hxx b/vcl/inc/unx/gtk/gtksalmenu.hxx index 2b18466b3dbf..a9de5e598d5b 100644 --- a/vcl/inc/unx/gtk/gtksalmenu.hxx +++ b/vcl/inc/unx/gtk/gtksalmenu.hxx @@ -132,7 +132,8 @@ public: #endif void ReturnFocus(); - virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags) override; + virtual bool ShowNativePopupMenu(FloatingWindow * pWin, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>* = nullptr) override; virtual void ShowCloseButton(bool bShow) override; virtual bool AddMenuBarButton( const SalMenuButtonItem& rNewItem ) override; virtual void RemoveMenuBarButton( sal_uInt16 nId ) override; diff --git a/vcl/osx/salmenu.cxx b/vcl/osx/salmenu.cxx index b3d02587f46b..ae7e4d3db7b6 100644 --- a/vcl/osx/salmenu.cxx +++ b/vcl/osx/salmenu.cxx @@ -282,8 +282,13 @@ AquaSalMenu::~AquaSalMenu() } } -bool AquaSalMenu::ShowNativePopupMenu(FloatingWindow * pWin, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags) +bool AquaSalMenu::ShowNativePopupMenu( + FloatingWindow* pWin, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>* pListener) { + if (pListener) + return false; + // set offsets for positioning const float offset = 9.0; diff --git a/vcl/qt5/QtMenu.cxx b/vcl/qt5/QtMenu.cxx index 93f3d6f5a378..a6ca0445fc74 100644 --- a/vcl/qt5/QtMenu.cxx +++ b/vcl/qt5/QtMenu.cxx @@ -117,6 +117,16 @@ void QtMenu::InsertMenuItem(QtMenuItem* pSalMenuItem, unsigned nPos) mpOwnedQMenu.reset(new QMenu); mpQMenu = mpOwnedQMenu.get(); connectHelpSignalSlots(mpQMenu, pSalMenuItem); + + connect(mpQMenu, &QMenu::aboutToHide, this, [this] { + if (m_pWin && m_xListener.is()) + { + QMenu* pMenu = mpOwnedQMenu.release(); + css::ui::dialogs::DialogClosedEvent aEvent(m_pWin->GetComponentInterface(), 0); + m_xListener->dialogClosed(aEvent); + pMenu->deleteLater(); + } + }); } if (pSalMenuItem->mpSubMenu) @@ -869,7 +879,8 @@ void QtMenu::ShowCloseButton(bool bShow) } bool QtMenu::ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangle& rRect, - FloatWinPopupFlags nFlags) + FloatWinPopupFlags nFlags, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>* pListener) { assert(mpQMenu); DoFullMenuUpdate(mpVCLMenu); @@ -884,7 +895,19 @@ bool QtMenu::ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangle& r aFloatRect.SetPosY(aFloatRect.getY() + pFrame->menuBarOffset()); const QRect aRect = toQRect(aFloatRect, 1 / pFrame->devicePixelRatioF()); - mpQMenu->exec(aRect.bottomLeft()); + + if (pListener) + { + m_xListener = *pListener; + m_pWin = pWin; + mpQMenu->popup(aRect.bottomLeft()); + } + else + { + m_xListener = nullptr; + m_pWin = nullptr; + mpQMenu->exec(aRect.bottomLeft()); + } return true; } diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx index fa9e7fa6d476..696e72800a36 100644 --- a/vcl/source/app/salvtables.cxx +++ b/vcl/source/app/salvtables.cxx @@ -215,7 +215,9 @@ SalObject::~SalObject() {} SalMenu::~SalMenu() {} -bool SalMenu::ShowNativePopupMenu(FloatingWindow*, const tools::Rectangle&, FloatWinPopupFlags) +bool SalMenu::ShowNativePopupMenu( + FloatingWindow*, const tools::Rectangle&, FloatWinPopupFlags, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>*) { return false; } diff --git a/vcl/source/control/managedmenubutton.cxx b/vcl/source/control/managedmenubutton.cxx index 7545dba9b374..574328702cd3 100644 --- a/vcl/source/control/managedmenubutton.cxx +++ b/vcl/source/control/managedmenubutton.cxx @@ -13,6 +13,7 @@ #include <managedmenubutton.hxx> #include <vcl/menu.hxx> +#include <com/sun/star/awt/XPopupMenuAsync.hpp> #include <com/sun/star/frame/ModuleManager.hpp> #include <com/sun/star/frame/theDesktop.hpp> #include <com/sun/star/frame/thePopupMenuControllerFactory.hpp> diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx index 3a66b962ebd6..fa907019e958 100644 --- a/vcl/source/window/menu.cxx +++ b/vcl/source/window/menu.cxx @@ -2690,6 +2690,7 @@ MenuFloatingWindow * PopupMenu::ImplGetFloatingWindow() const { } PopupMenu::PopupMenu() + : m_pState(nullptr) { mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this); } @@ -2702,6 +2703,7 @@ PopupMenu::PopupMenu( const PopupMenu& rMenu ) PopupMenu::~PopupMenu() { + assert(!m_pState); disposeOnce(); } @@ -2799,6 +2801,36 @@ sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const tools::Rectangle& return ImplExecute( pExecWindow, rRect, lcl_TranslateFlags(nFlags), nullptr, false ); } +struct PopupMenuFinishState final +{ + VclPtr<PopupMenu> pSelf; + VclPtr<vcl::Window> pParentWin; + VclPtr<MenuFloatingWindow> pWin; + bool bRealExecute; + bool bIsNativeMenu; + + void clean() + { + pWin = nullptr; + pParentWin = nullptr; + pSelf = nullptr; + } +}; + +bool PopupMenu::Popup(vcl::Window* pExecWindow, const Point& rPopupPos, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>& listener) +{ + return Popup(pExecWindow, tools::Rectangle(rPopupPos, rPopupPos), listener, PopupMenuFlags::ExecuteDown); +} + +bool PopupMenu::Popup(vcl::Window* pExecWindow, const tools::Rectangle& rRect, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>& listener, PopupMenuFlags nFlags) +{ + assert(!m_pState); + ENSURE_OR_RETURN(pExecWindow, "PopupMenu::Popup: need a non-NULL window!", false); + return ImplPopup(pExecWindow, rRect, lcl_TranslateFlags(nFlags), nullptr, false, listener); +} + void PopupMenu::ImplFlushPendingSelect() { // is there still Select? @@ -2954,7 +2986,7 @@ bool PopupMenu::PrepareRun(const VclPtr<vcl::Window>& pParentWin, tools::Rectang if (pStartedFrom && pStartedFrom->IsMenuBar()) nMaxHeight -= pParentWin->GetSizePixel().Height(); sal_Int32 nLeft, nTop, nRight, nBottom; - pWindow->GetBorder( nLeft, nTop, nRight, nBottom ); + pWindow->GetBorder(nLeft, nTop, nRight, nBottom); nMaxHeight -= nTop+nBottom; if ( aSz.Height() > nMaxHeight ) { @@ -2970,10 +3002,11 @@ bool PopupMenu::PrepareRun(const VclPtr<vcl::Window>& pParentWin, tools::Rectang } bool PopupMenu::Run(const VclPtr<MenuFloatingWindow>& pWin, const bool bRealExecute, const bool bPreSelectFirst, - const FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, const tools::Rectangle& rRect) + const FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, const tools::Rectangle& rRect, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>* xListener) { SalMenu* pMenu = ImplGetSalMenu(); - if (pMenu && bRealExecute && pMenu->ShowNativePopupMenu(pWin, rRect, nPopupModeFlags)) + if (pMenu && bRealExecute && pMenu->ShowNativePopupMenu(pWin, rRect, nPopupModeFlags, xListener)) return true; pWin->StartPopupMode(rRect, nPopupModeFlags); @@ -3009,7 +3042,12 @@ bool PopupMenu::Run(const VclPtr<MenuFloatingWindow>& pWin, const bool bRealExec } if (bRealExecute) - pWin->Execute(); + { + if (!xListener) + pWin->Execute(); + else + pWin->Popup(*xListener); + } return false; } @@ -3054,11 +3092,42 @@ sal_uInt16 PopupMenu::ImplExecute(const VclPtr<vcl::Window>& pParentWin, const t VclPtr<MenuFloatingWindow> pWin; if (!PrepareRun(pParentWin, aRect, nPopupModeFlags, pSFrom, bRealExecute, pWin)) return 0; - const bool bNative = Run(pWin, bRealExecute, bPreSelectFirst, nPopupModeFlags, pSFrom, aRect); + const bool bNative = Run(pWin, bRealExecute, bPreSelectFirst, nPopupModeFlags, pSFrom, aRect, nullptr); FinishRun(pWin, pParentWin, bRealExecute, bNative); return nSelectedId; } +bool PopupMenu::ImplPopup(const VclPtr<vcl::Window>& pParentWin, const tools::Rectangle& rRect, + FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>& xListener) +{ + // tdf#126054 hold this until after function completes + VclPtr<PopupMenu> xThis(this); + bool bRealExecute = false; + tools::Rectangle aRect(rRect); + VclPtr<MenuFloatingWindow> pWin; + if (!PrepareRun(pParentWin, aRect, nPopupModeFlags, pSFrom, bRealExecute, pWin)) + return false; + assert(!m_pState); + m_pState = new PopupMenuFinishState; + m_pState->bIsNativeMenu = Run(pWin, bRealExecute, bPreSelectFirst, nPopupModeFlags, pSFrom, aRect, &xListener); + m_pState->pWin = pWin; + m_pState->pParentWin = pParentWin; + m_pState->bRealExecute = bRealExecute; + m_pState->pSelf = xThis; + return true; +} + +void PopupMenu::Finish() +{ + if (!m_pState) + return; + FinishRun(m_pState->pWin, m_pState->pParentWin, m_pState->bRealExecute, m_pState->bIsNativeMenu); + m_pState->clean(); + delete m_pState; + m_pState = nullptr; +} + sal_uInt16 PopupMenu::ImplCalcVisEntries( tools::Long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible ) const { nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight(); diff --git a/vcl/source/window/menufloatingwindow.cxx b/vcl/source/window/menufloatingwindow.cxx index 95a0d3f4d02a..c0a03cca1baa 100644 --- a/vcl/source/window/menufloatingwindow.cxx +++ b/vcl/source/window/menufloatingwindow.cxx @@ -308,8 +308,15 @@ IMPL_LINK_NOARG(MenuFloatingWindow, PopupEnd, FloatingWindow*, void) pMenu->pStartedFrom->ClosePopup(pMenu); } - if ( pM ) + if (pM) + { pM->pStartedFrom = nullptr; + if (m_xListener.is()) + { + css::ui::dialogs::DialogClosedEvent aEvent(GetComponentInterface(), pM->GetCurItemId()); + m_xListener->dialogClosed(aEvent); + } + } } IMPL_LINK_NOARG(MenuFloatingWindow, AutoScroll, Timer *, void) @@ -443,21 +450,31 @@ void MenuFloatingWindow::End() Window::EndSaveFocus(xFocusId); } + Finish(); + bInExecute = false; } -void MenuFloatingWindow::Execute() +void MenuFloatingWindow::Popup(const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>& xListener) { ImplSVData* pSVData = ImplGetSVData(); - pSVData->maAppData.mpActivePopupMenu = static_cast<PopupMenu*>(pMenu.get()); - + m_xListener = xListener; Start(); +} +void MenuFloatingWindow::Finish() +{ + ImplSVData* pSVData = ImplGetSVData(); + pSVData->maAppData.mpActivePopupMenu = nullptr; +} + +void MenuFloatingWindow::Execute() +{ + Popup(); while (bInExecute && !Application::IsQuit()) Application::Yield(); - - pSVData->maAppData.mpActivePopupMenu = nullptr; + Finish(); } void MenuFloatingWindow::StopExecute() @@ -474,6 +491,7 @@ void MenuFloatingWindow::StopExecute() // notify parent, needed for accessibility if( pMenu && pMenu->pStartedFrom ) pMenu->pStartedFrom->ImplCallEventListeners( VclEventId::MenuSubmenuDeactivate, nPosInParent ); + Finish(); } void MenuFloatingWindow::KillActivePopup( PopupMenu* pThisOnly ) @@ -502,6 +520,7 @@ void MenuFloatingWindow::KillActivePopup( PopupMenu* pThisOnly ) PaintImmediately(); } + pPopup->Finish(); } void MenuFloatingWindow::EndExecute() diff --git a/vcl/source/window/menufloatingwindow.hxx b/vcl/source/window/menufloatingwindow.hxx index f26fb50373ca..b6c8b54738ce 100644 --- a/vcl/source/window/menufloatingwindow.hxx +++ b/vcl/source/window/menufloatingwindow.hxx @@ -46,6 +46,7 @@ private: sal_uInt16 nScrollerHeight; sal_uInt16 nFirstEntry; sal_uInt16 nPosInParent; + css::uno::Reference<css::ui::dialogs::XDialogClosedListener> m_xListener; bool bInExecute : 1; bool bScrollMenu : 1; @@ -67,6 +68,7 @@ private: void Start(); void End(); + static void Finish(); protected: vcl::Region ImplCalcClipRegion() const; @@ -107,6 +109,8 @@ public: bool IsScrollMenu() const { return bScrollMenu; } sal_uInt16 GetScrollerHeight() const { return nScrollerHeight; } + void Popup(const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>& xListener = nullptr); + void Execute(); void StopExecute(); void EndExecute(); diff --git a/vcl/unx/gtk3/gtksalmenu.cxx b/vcl/unx/gtk3/gtksalmenu.cxx index a510473650d6..b24342ded305 100644 --- a/vcl/unx/gtk3/gtksalmenu.cxx +++ b/vcl/unx/gtk3/gtksalmenu.cxx @@ -423,9 +423,13 @@ static void MenuClosed(GtkPopover* pWidget, GMainLoop* pLoop) g_main_loop_quit(pLoop); } -bool GtkSalMenu::ShowNativePopupMenu(FloatingWindow* pWin, const tools::Rectangle& rRect, - FloatWinPopupFlags nFlags) +bool GtkSalMenu::ShowNativePopupMenu + (FloatingWindow* pWin, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags, + const css::uno::Reference<css::ui::dialogs::XDialogClosedListener>* pListener) { + if (pListener) + return false; + VclPtr<vcl::Window> xParent = pWin->ImplGetWindowImpl()->mpRealParent; mpFrame = static_cast<GtkSalFrame*>(xParent->ImplGetFrame()); |