include/vcl/jsdialog/executor.hxx | 10 +++++ include/vcl/weld.hxx | 4 ++ vcl/inc/jsdialog/jsdialogbuilder.hxx | 16 ++++++++ vcl/inc/salvtables.hxx | 5 +- vcl/jsdialog/executor.cxx | 28 ++++++++++++++- vcl/jsdialog/jsdialogbuilder.cxx | 64 +++++++++++++++++++++++++++++++++++ 6 files changed, 124 insertions(+), 3 deletions(-)
New commits: commit efeb511607dacce991866bcf328c96a01ab594f9 Author: Szymon Kłos <szymon.k...@collabora.com> AuthorDate: Fri Nov 22 15:04:03 2024 +0100 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Thu Dec 5 11:29:38 2024 +0100 jsdialog: weld::Menu - weld::Menu doesn't have base with weld::Widget - use separate container, only one menu per WindowId - first only send menu and cancel, when user activates option we will send another request and execute entry as menu can be blocking - there is connect_activated way of async setup too Change-Id: Iea03f82a91ecc19af67b91f85364675c119056ea Reviewed-on: https://gerrit.libreoffice.org/c/core/+/177722 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/include/vcl/jsdialog/executor.hxx b/include/vcl/jsdialog/executor.hxx index ef667a70ce26..260be394d6b8 100644 --- a/include/vcl/jsdialog/executor.hxx +++ b/include/vcl/jsdialog/executor.hxx @@ -44,6 +44,16 @@ public: rTreeView.signal_row_activated(); } + static void trigger_popup_menu(weld::TreeView& rTreeView, const CommandEvent& rCommand) + { + rTreeView.signal_popup_menu(rCommand); + } + + static void trigger_activated(weld::Menu& rMenu, const OUString& rIdent) + { + rMenu.signal_activate(rIdent); + } + static void trigger_item_activated(weld::IconView& rIconView) { rIconView.signal_item_activated(); diff --git a/include/vcl/weld.hxx b/include/vcl/weld.hxx index 16d27fd6e399..79b810d9f9c2 100644 --- a/include/vcl/weld.hxx +++ b/include/vcl/weld.hxx @@ -977,6 +977,8 @@ protected: return m_aEditingDoneHdl.Call(rIterText); } + void signal_popup_menu(const CommandEvent& rCommand) { m_aPopupMenuHdl.Call(rCommand); } + Link<const TreeIter&, OUString> m_aQueryTooltipHdl; OUString signal_query_tooltip(const TreeIter& rIter) { return m_aQueryTooltipHdl.Call(rIter); } @@ -2415,6 +2417,8 @@ enum class Placement class VCL_DLLPUBLIC Menu { + friend class ::LOKTrigger; + Link<const OUString&, void> m_aActivateHdl; protected: diff --git a/vcl/inc/jsdialog/jsdialogbuilder.hxx b/vcl/inc/jsdialog/jsdialogbuilder.hxx index 7d327c76a363..6d9088bcc358 100644 --- a/vcl/inc/jsdialog/jsdialogbuilder.hxx +++ b/vcl/inc/jsdialog/jsdialogbuilder.hxx @@ -313,6 +313,7 @@ public: virtual std::unique_ptr<weld::RadioButton> weld_radio_button(const OUString& id) override; virtual std::unique_ptr<weld::Frame> weld_frame(const OUString& id) override; virtual std::unique_ptr<weld::MenuButton> weld_menu_button(const OUString& id) override; + virtual std::unique_ptr<weld::Menu> weld_menu(const OUString& id) override; virtual std::unique_ptr<weld::Popover> weld_popover(const OUString& id) override; virtual std::unique_ptr<weld::Box> weld_box(const OUString& id) override; virtual std::unique_ptr<weld::Widget> weld_widget(const OUString& id) override; @@ -334,6 +335,11 @@ public: static void ForgetPopup(const OUString& nWindowId); static vcl::Window* FindPopup(const OUString& nWindowId); + // menus in separate container as they don't share base class with weld::Widget + static void RememberMenu(const OUString& nWindowId, weld::Menu* pMenu); + static void ForgetMenu(const OUString& nWindowId); + static weld::Menu* FindMenu(const OUString& nWindowId); + private: const OUString& GetTypeOfJSON() const; VclPtr<vcl::Window>& GetContentWindow(); @@ -873,6 +879,16 @@ public: virtual void set_active(bool active) override; }; +class JSMenu final : public SalInstanceMenu +{ +public: + JSMenu(JSDialogSender* pSender, PopupMenu* pMenu, SalInstanceBuilder* pBuilder, + bool bTakeOwnership); + + virtual OUString popup_at_rect(weld::Widget* pParent, const tools::Rectangle& rRect, + weld::Placement ePlace = weld::Placement::Under) override; +}; + class JSPopover : public JSWidget<SalInstancePopover, DockingWindow> { vcl::LOKWindowId mnWindowId; diff --git a/vcl/inc/salvtables.hxx b/vcl/inc/salvtables.hxx index 7cc26c25d39e..b7bf29e7b04b 100644 --- a/vcl/inc/salvtables.hxx +++ b/vcl/inc/salvtables.hxx @@ -146,11 +146,12 @@ public: virtual ~SalInstanceBuilder() override; }; -class SAL_DLLPUBLIC_RTTI SalInstanceMenu final : public weld::Menu +class SAL_DLLPUBLIC_RTTI SalInstanceMenu : public weld::Menu { -private: +protected: VclPtr<PopupMenu> m_xMenu; +private: bool m_bTakeOwnership; sal_uInt16 m_nLastId; diff --git a/vcl/jsdialog/executor.cxx b/vcl/jsdialog/executor.cxx index 891e7cedd378..7667970acee9 100644 --- a/vcl/jsdialog/executor.cxx +++ b/vcl/jsdialog/executor.cxx @@ -74,7 +74,21 @@ bool ExecuteAction(const OUString& nWindowId, const OUString& rWidget, StringMap } } - if (pWidget != nullptr) + if (pWidget == nullptr) + { + // weld::Menu doesn't have base of weld::Widget + if (sControlType == "menu") + { + weld::Menu* pMenu = JSInstanceBuilder::FindMenu(nWindowId); + if (pMenu && sAction == "activated") + { + LOKTrigger::trigger_activated(*pMenu, rData["data"]); + return true; + } + } + return false; + } + else { // shared actions @@ -558,6 +572,18 @@ bool ExecuteAction(const OUString& nWindowId, const OUString& rWidget, StringMap pTreeView->drag_end(); return true; } + else if (sAction == "contextmenu") + { + sal_Int32 nEntryAbsPos = o3tl::toInt32(rData["data"]); + + std::unique_ptr<weld::TreeIter> itEntry(pTreeView->make_iterator()); + pTreeView->get_iter_abs_pos(*itEntry, nEntryAbsPos); + + tools::Rectangle aRect = pTreeView->get_row_area(*itEntry); + CommandEvent aCommand(aRect.Center(), CommandEventId::ContextMenu); + + LOKTrigger::trigger_popup_menu(*pTreeView, aCommand); + } } } else if (sControlType == "iconview") diff --git a/vcl/jsdialog/jsdialogbuilder.cxx b/vcl/jsdialog/jsdialogbuilder.cxx index bc5d7d21fd57..cd9af74211ef 100644 --- a/vcl/jsdialog/jsdialogbuilder.cxx +++ b/vcl/jsdialog/jsdialogbuilder.cxx @@ -13,6 +13,7 @@ #include <comphelper/lok.hxx> #include <iconview.hxx> #include <utility> +#include <vcl/menu.hxx> #include <vcl/svapp.hxx> #include <vcl/tabpage.hxx> #include <vcl/toolbox.hxx> @@ -45,6 +46,14 @@ static std::map<OUString, vcl::Window*>& GetLOKPopupsMap() return s_aLOKPopupsMap; } +static std::map<OUString, weld::Menu*>& GetLOKMenusMap() +{ + // Map to remember the LOKWindowId <-> weld menu binding. + static std::map<OUString, weld::Menu*> s_aLOKMenusMap; + + return s_aLOKMenusMap; +} + namespace { void response_help(vcl::Window* pWindow) @@ -322,7 +331,10 @@ JSDialogSender::~JSDialogSender() COVERITY_NOEXCEPT_FALSE void JSDialogSender::sendFullUpdate(bool bForce) { if (!mpIdleNotify) + { + assert(false); return; + } if (bForce) mpIdleNotify->forceUpdate(); @@ -813,6 +825,28 @@ void JSInstanceBuilder::ForgetPopup(const OUString& nWindowId) GetLOKPopupsMap().erase(it); } +void JSInstanceBuilder::RememberMenu(const OUString& nWindowId, weld::Menu* pMenu) +{ + GetLOKMenusMap()[nWindowId] = pMenu; +} + +void JSInstanceBuilder::ForgetMenu(const OUString& nWindowId) +{ + auto it = GetLOKMenusMap().find(nWindowId); + if (it != GetLOKMenusMap().end()) + GetLOKMenusMap().erase(it); +} + +weld::Menu* JSInstanceBuilder::FindMenu(const OUString& nWindowId) +{ + const auto it = GetLOKMenusMap().find(nWindowId); + + if (it != GetLOKMenusMap().end()) + return it->second; + + return nullptr; +} + vcl::Window* JSInstanceBuilder::FindPopup(const OUString& nWindowId) { const auto it = GetLOKPopupsMap().find(nWindowId); @@ -1216,6 +1250,20 @@ std::unique_ptr<weld::MenuButton> JSInstanceBuilder::weld_menu_button(const OUSt return pWeldWidget; } +std::unique_ptr<weld::Menu> JSInstanceBuilder::weld_menu(const OUString& id) +{ + PopupMenu* pPopupMenu = m_xBuilder->get_menu(id); + + JSMenu* pMenu = pPopupMenu ? new JSMenu(this, pPopupMenu, this, false) : nullptr; + + std::unique_ptr<weld::Menu> pWeldWidget(pMenu); + + if (pWeldWidget) + RememberMenu(getMapIdFromWindowId(), pWeldWidget.get()); + + return pWeldWidget; +} + std::unique_ptr<weld::Popover> JSInstanceBuilder::weld_popover(const OUString& id) { DockingWindow* pDockingWindow = m_xBuilder->get<DockingWindow>(id); @@ -2362,6 +2410,22 @@ void JSMenuButton::set_active(bool bActive) } } +JSMenu::JSMenu(JSDialogSender* /*pSender*/, PopupMenu* pPopupMenu, SalInstanceBuilder* /*pBuilder*/, + bool bTakeOwnership) + : SalInstanceMenu(pPopupMenu, bTakeOwnership) +{ +} + +OUString JSMenu::popup_at_rect(weld::Widget* /*pParent*/, const tools::Rectangle& /*rRect*/, + weld::Placement /*ePlace*/) +{ + // TODO: send message + + // first only send menu and cancel menu + // no return SalInstanceMenu::popup_at_rect(pParent, rRect, ePlace); + return ""; +} + JSPopover::JSPopover(JSDialogSender* pSender, DockingWindow* pDockingWindow, SalInstanceBuilder* pBuilder, bool bTakeOwnership) : JSWidget<SalInstancePopover, DockingWindow>(pSender, pDockingWindow, pBuilder, bTakeOwnership)