include/vcl/menu.hxx | 3 + sfx2/source/dialog/StyleList.cxx | 12 ++++--- sfx2/source/inc/StyleList.hxx | 1 vcl/inc/jsdialog/enabled.hxx | 1 vcl/inc/jsdialog/jsdialogbuilder.hxx | 3 + vcl/inc/jsdialog/jsdialogmessages.hxx | 18 +++++++++- vcl/inc/jsdialog/jsdialogsender.hxx | 1 vcl/jsdialog/enabled.cxx | 9 +++++ vcl/jsdialog/executor.cxx | 19 +++++++---- vcl/jsdialog/jsdialogbuilder.cxx | 13 ++++--- vcl/jsdialog/jsdialogsender.cxx | 58 +++++++++++++++++++++++++++++----- vcl/source/window/builder.cxx | 2 + vcl/source/window/menu.cxx | 21 ++++++++++++ 13 files changed, 136 insertions(+), 25 deletions(-)
New commits: commit a5149eadb23825a9fb5beeda81836ad7940c3083 Author: Szymon Kłos <szymon.k...@collabora.com> AuthorDate: Wed Dec 4 10:41:22 2024 +0100 Commit: Szymon Kłos <szymon.k...@collabora.com> CommitDate: Wed Dec 11 21:51:54 2024 +0100 jsdialog: send message for weld::Menu - we need to setup correct notifier - enable context menu in the style sidebar in Writer - handle menu actions - base of vcl::Window and PopupMenu is VclReferenceBase - prepare template function sendMessage to handle both Change-Id: I54c8dc468856c0f98495bdaf4ddd504c2f56562f Signed-off-by: Szymon Kłos <szymon.k...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178319 Tested-by: Jenkins diff --git a/include/vcl/menu.hxx b/include/vcl/menu.hxx index e241609fcdb6..21344525e2d0 100644 --- a/include/vcl/menu.hxx +++ b/include/vcl/menu.hxx @@ -32,6 +32,7 @@ #include <vcl/vclreferencebase.hxx> #include <com/sun/star/uno/Reference.hxx> #include <o3tl/typed_flags_set.hxx> +#include <tools/json_writer.hxx> #include <list> class OutputDevice; @@ -402,6 +403,8 @@ public: */ const OUString& get_id() const { return maID; } + virtual void DumpAsPropertyTree(tools::JsonWriter&) const; + private: css::uno::Reference<css::accessibility::XAccessible> CreateAccessible(); }; diff --git a/sfx2/source/dialog/StyleList.cxx b/sfx2/source/dialog/StyleList.cxx index 8fa08c908715..d62c7a28e27c 100644 --- a/sfx2/source/dialog/StyleList.cxx +++ b/sfx2/source/dialog/StyleList.cxx @@ -198,7 +198,7 @@ void StyleList::CreateContextMenu() m_bBindingUpdate = false; } mxMenu.reset(); - mxMenuBuilder = Application::CreateBuilder(nullptr, u"sfx/ui/stylecontextmenu.ui"_ustr); + mxMenuBuilder = Application::CreateBuilder(m_pContainer, u"sfx/ui/stylecontextmenu.ui"_ustr); mxMenu = mxMenuBuilder->weld_menu(u"menu"_ustr); mxMenu->set_sensitive(u"edit"_ustr, m_bCanEdit); mxMenu->set_sensitive(u"delete"_ustr, m_bCanDel); @@ -1541,13 +1541,14 @@ IMPL_LINK_NOARG(StyleList, Clear, void*, void) i.reset(); } +IMPL_LINK(StyleList, OnPopupEnd, const OUString&, sCommand, void) { MenuSelect(sCommand); } + void StyleList::ShowMenu(const CommandEvent& rCEvt) { CreateContextMenu(); weld::TreeView* pTreeView = m_xTreeBox->get_visible() ? m_xTreeBox.get() : m_xFmtLb.get(); - OUString sCommand( - mxMenu->popup_at_rect(pTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1)))); - MenuSelect(sCommand); + mxMenu->connect_activate(LINK(this, StyleList, OnPopupEnd)); + mxMenu->popup_at_rect(pTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1, 1))); } void StyleList::MenuSelect(const OUString& rIdent) @@ -1680,6 +1681,9 @@ IMPL_LINK_NOARG(StyleList, MenuSelectAsyncHdl, void*, void) HideHdl(); else if (sLastItemIdent == "show") ShowHdl(); + + mxMenu.reset(); + mxMenuBuilder.reset(); } // Double-click on a style sheet in the ListBox is applied. diff --git a/sfx2/source/inc/StyleList.hxx b/sfx2/source/inc/StyleList.hxx index ef2696e5fd34..bc8a90ada248 100644 --- a/sfx2/source/inc/StyleList.hxx +++ b/sfx2/source/inc/StyleList.hxx @@ -129,6 +129,7 @@ public: void FilterSelect(sal_uInt16 nActFilter, bool bsetFilter); DECL_LINK(NewMenuExecuteAction, void*, void); + DECL_LINK(OnPopupEnd, const OUString&, void); bool HasStylesHighlighterFeature() { return m_bModuleHasStylesHighlighterFeature; } void SetHighlightParaStyles(bool bSet) { m_bHighlightParaStyles = bSet; } diff --git a/vcl/inc/jsdialog/enabled.hxx b/vcl/inc/jsdialog/enabled.hxx index 6354b70a8de7..70a867f0acb3 100644 --- a/vcl/inc/jsdialog/enabled.hxx +++ b/vcl/inc/jsdialog/enabled.hxx @@ -15,6 +15,7 @@ namespace jsdialog { bool isBuilderEnabled(std::u16string_view rUIFile, bool bMobile); bool isBuilderEnabledForPopup(std::u16string_view rUIFile); +bool isBuilderEnabledForMenu(std::u16string_view rUIFile); bool isBuilderEnabledForSidebar(std::u16string_view rUIFile); bool isInterimBuilderEnabledForNotebookbar(std::u16string_view rUIFile); } diff --git a/vcl/inc/jsdialog/jsdialogbuilder.hxx b/vcl/inc/jsdialog/jsdialogbuilder.hxx index 34ff37ed48d6..d7fd64388b4b 100644 --- a/vcl/inc/jsdialog/jsdialogbuilder.hxx +++ b/vcl/inc/jsdialog/jsdialogbuilder.hxx @@ -754,6 +754,9 @@ public: class JSMenu final : public SalInstanceMenu { + VclPtr<PopupMenu> m_pPopupMenu; + JSDialogSender* m_pSender; + public: JSMenu(JSDialogSender* pSender, PopupMenu* pMenu, SalInstanceBuilder* pBuilder, bool bTakeOwnership); diff --git a/vcl/inc/jsdialog/jsdialogmessages.hxx b/vcl/inc/jsdialog/jsdialogmessages.hxx index fffd67fe8ac5..ab0d016ca1af 100644 --- a/vcl/inc/jsdialog/jsdialogmessages.hxx +++ b/vcl/inc/jsdialog/jsdialogmessages.hxx @@ -15,6 +15,7 @@ #include <rtl/ustring.hxx> #include <vcl/idle.hxx> #include <vcl/jsdialog/executor.hxx> +#include <vcl/menu.hxx> #include <vcl/window.hxx> namespace jsdialog @@ -26,7 +27,8 @@ enum MessageType Close, Action, Popup, - PopupClose + PopupClose, + Menu, }; } @@ -36,6 +38,7 @@ class JSDialogMessageInfo public: jsdialog::MessageType m_eType; VclPtr<vcl::Window> m_pWindow; + VclPtr<PopupMenu> m_pMenu; std::unique_ptr<jsdialog::ActionDataMap> m_pData; private: @@ -43,6 +46,7 @@ private: { this->m_eType = rInfo.m_eType; this->m_pWindow = rInfo.m_pWindow; + this->m_pMenu = rInfo.m_pMenu; if (rInfo.m_pData) { std::unique_ptr<jsdialog::ActionDataMap> pData( @@ -60,6 +64,14 @@ public: { } + JSDialogMessageInfo(jsdialog::MessageType eType, VclPtr<PopupMenu> pMenu, + std::unique_ptr<jsdialog::ActionDataMap> pData) + : m_eType(eType) + , m_pMenu(std::move(pMenu)) + , m_pData(std::move(pData)) + { + } + JSDialogMessageInfo(const JSDialogMessageInfo& rInfo) { copy(rInfo); } JSDialogMessageInfo& operator=(JSDialogMessageInfo aInfo) @@ -93,7 +105,8 @@ public: void clearQueue(); void forceUpdate(); - void sendMessage(jsdialog::MessageType eType, const VclPtr<vcl::Window>& pWindow, + template <class VclType> + void sendMessage(jsdialog::MessageType eType, const VclPtr<VclType>& pTarget, std::unique_ptr<jsdialog::ActionDataMap> pData = nullptr); private: @@ -106,6 +119,7 @@ private: OString generatePopupMessage(VclPtr<vcl::Window> pWindow, const rtl::OUString& sParentId, const rtl::OUString& sCloseId) const; OString generateClosePopupMessage(const rtl::OUString& sWindowId) const; + OString generateMenuMessage(const VclPtr<PopupMenu>& pMenu) const; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/inc/jsdialog/jsdialogsender.hxx b/vcl/inc/jsdialog/jsdialogsender.hxx index f6a899a23d90..85066e10f37d 100644 --- a/vcl/inc/jsdialog/jsdialogsender.hxx +++ b/vcl/inc/jsdialog/jsdialogsender.hxx @@ -63,6 +63,7 @@ public: virtual void sendAction(VclPtr<vcl::Window> pWindow, std::unique_ptr<jsdialog::ActionDataMap> pData); virtual void sendPopup(VclPtr<vcl::Window> pWindow, OUString sParentId, OUString sCloseId); + virtual void sendMenu(const VclPtr<PopupMenu>& pMenu); virtual void sendClosePopup(vcl::LOKWindowId nWindowId); void flush() { mpIdleNotify->Invoke(); } diff --git a/vcl/jsdialog/enabled.cxx b/vcl/jsdialog/enabled.cxx index 172c4504ed92..cba8047ffd3d 100644 --- a/vcl/jsdialog/enabled.cxx +++ b/vcl/jsdialog/enabled.cxx @@ -338,6 +338,15 @@ bool isBuilderEnabledForPopup(std::u16string_view rUIFile) return false; } +bool isBuilderEnabledForMenu(std::u16string_view rUIFile) +{ + if (// sfx2 + rUIFile == u"sfx/ui/stylecontextmenu.ui") + return true; + + return false; +} + bool isBuilderEnabledForSidebar(std::u16string_view rUIFile) { return // scalc diff --git a/vcl/jsdialog/executor.cxx b/vcl/jsdialog/executor.cxx index 10bdad905db5..2385c19f4a4a 100644 --- a/vcl/jsdialog/executor.cxx +++ b/vcl/jsdialog/executor.cxx @@ -81,10 +81,10 @@ bool ExecuteAction(const OUString& nWindowId, const OUString& rWidget, StringMap if (pWidget == nullptr) { // weld::Menu doesn't have base of weld::Widget - if (sControlType == "menu") + if (rWidget == "__MENU__") { weld::Menu* pMenu = JSInstanceBuilder::Menus().Find(nWindowId); - if (pMenu && sAction == "activated") + if (pMenu && sAction == "select") { LOKTrigger::trigger_activated(*pMenu, rData["data"]); return true; @@ -581,12 +581,17 @@ bool ExecuteAction(const OUString& nWindowId, const OUString& rWidget, StringMap 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); + if (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); + LOKTrigger::trigger_popup_menu(*pTreeView, aCommand); + } + else + SAL_WARN("vcl", "No absolute position found for " << nEntryAbsPos + << " in treeview"); + return true; } } } diff --git a/vcl/jsdialog/jsdialogbuilder.cxx b/vcl/jsdialog/jsdialogbuilder.cxx index 501f14cea09a..117b7b97cad6 100644 --- a/vcl/jsdialog/jsdialogbuilder.cxx +++ b/vcl/jsdialog/jsdialogbuilder.cxx @@ -364,7 +364,7 @@ JSInstanceBuilder::~JSInstanceBuilder() jsdialog::SendFullUpdate(OUString::number(m_nWindowId), u"__DIALOG__"_ustr); } - if (m_sTypeOfJSON == "popup") + if (m_sTypeOfJSON == "popup" || m_sTypeOfJSON == "menu") sendClosePopup(m_nWindowId); if (m_aWindowToRelease) @@ -388,6 +388,7 @@ JSInstanceBuilder::~JSInstanceBuilder() } JSInstanceBuilder::Popups().Forget(OUString::number(m_nWindowId)); + JSInstanceBuilder::Menus().Forget(OUString::number(m_nWindowId)); } const OUString& JSInstanceBuilder::GetTypeOfJSON() const { return m_sTypeOfJSON; } @@ -1950,19 +1951,21 @@ void JSMenuButton::set_active(bool bActive) } } -JSMenu::JSMenu(JSDialogSender* /*pSender*/, PopupMenu* pPopupMenu, SalInstanceBuilder* /*pBuilder*/, +JSMenu::JSMenu(JSDialogSender* pSender, PopupMenu* pPopupMenu, SalInstanceBuilder* /*pBuilder*/, bool bTakeOwnership) : SalInstanceMenu(pPopupMenu, bTakeOwnership) + , m_pPopupMenu(pPopupMenu) + , m_pSender(pSender) { } OUString JSMenu::popup_at_rect(weld::Widget* /*pParent*/, const tools::Rectangle& /*rRect*/, weld::Placement /*ePlace*/) { - // TODO: send message + // Do not block with SalInstanceMenu::popup_at_rect(pParent, rRect, ePlace); + m_pSender->sendMenu(m_pPopupMenu); - // first only send menu and cancel menu - // no return SalInstanceMenu::popup_at_rect(pParent, rRect, ePlace); + // Don't return any action - simulate canceled menu return ""; } diff --git a/vcl/jsdialog/jsdialogsender.cxx b/vcl/jsdialog/jsdialogsender.cxx index 04c0d2a1e639..d63e8e38b8ed 100644 --- a/vcl/jsdialog/jsdialogsender.cxx +++ b/vcl/jsdialog/jsdialogsender.cxx @@ -45,8 +45,8 @@ void JSDialogNotifyIdle::send(const OString& sMsg) } } -void JSDialogNotifyIdle::sendMessage(jsdialog::MessageType eType, - const VclPtr<vcl::Window>& pWindow, +template <class VclType> +void JSDialogNotifyIdle::sendMessage(jsdialog::MessageType eType, const VclPtr<VclType>& pTarget, std::unique_ptr<jsdialog::ActionDataMap> pData) { std::scoped_lock aGuard(m_aQueueMutex); @@ -54,10 +54,14 @@ void JSDialogNotifyIdle::sendMessage(jsdialog::MessageType eType, // we want only the latest update of same type // TODO: also if we met full update - previous updates are not valid auto it = m_aMessageQueue.begin(); + const VclReferenceBase* pRawTarget = static_cast<VclReferenceBase*>(pTarget); while (it != m_aMessageQueue.end()) { - if (it->m_eType == eType && it->m_pWindow == pWindow) + const VclReferenceBase* pRawWindow = static_cast<VclReferenceBase*>(it->m_pWindow.get()); + const VclReferenceBase* pRawMenu = it->m_pMenu.get(); + + if (it->m_eType == eType && (pRawWindow == pRawTarget || pRawMenu == pRawTarget)) { // actions should be always sent, eg. rendering of custom entries in combobox if (eType == jsdialog::MessageType::Action) @@ -71,7 +75,7 @@ void JSDialogNotifyIdle::sendMessage(jsdialog::MessageType eType, it++; } - JSDialogMessageInfo aMessage(eType, pWindow, std::move(pData)); + JSDialogMessageInfo aMessage(eType, pTarget, std::move(pData)); m_aMessageQueue.push_back(aMessage); } @@ -207,6 +211,30 @@ OString JSDialogNotifyIdle::generateClosePopupMessage(const OUString& sWindowId) return aJsonWriter.finishAndGetAsOString(); } +OString JSDialogNotifyIdle::generateMenuMessage(const VclPtr<PopupMenu>& pMenu) const +{ + if (!pMenu || !m_aNotifierWindow) + return OString(); + + tools::JsonWriter aJsonWriter; + + { + auto aChildren = aJsonWriter.startArray("children"); + { + auto aStruct = aJsonWriter.startStruct(); + pMenu->DumpAsPropertyTree(aJsonWriter); + } + } + + aJsonWriter.put("jsontype", "dialog"); + aJsonWriter.put("type", "dropdown"); + aJsonWriter.put("cancellable", true); + aJsonWriter.put("popupParent", m_aNotifierWindow->get_id()); + aJsonWriter.put("id", m_aNotifierWindow->GetLOKWindowId()); + + return aJsonWriter.finishAndGetAsOString(); +} + void JSDialogNotifyIdle::Invoke() { std::deque<JSDialogMessageInfo> aMessageQueue; @@ -250,6 +278,12 @@ void JSDialogNotifyIdle::Invoke() case jsdialog::MessageType::PopupClose: send(generateClosePopupMessage((*rMessage.m_pData)[WINDOW_ID ""_ostr])); break; + + case jsdialog::MessageType::Menu: + { + send(generateMenuMessage(rMessage.m_pMenu)); + break; + } } } } @@ -275,7 +309,7 @@ void JSDialogSender::sendFullUpdate(bool bForce) if (bForce) mpIdleNotify->forceUpdate(); - mpIdleNotify->sendMessage(jsdialog::MessageType::FullUpdate, nullptr); + mpIdleNotify->sendMessage(jsdialog::MessageType::FullUpdate, VclPtr<vcl::Window>(nullptr)); mpIdleNotify->Start(); } @@ -285,7 +319,7 @@ void JSDialogSender::sendClose() return; mpIdleNotify->clearQueue(); - mpIdleNotify->sendMessage(jsdialog::MessageType::Close, nullptr); + mpIdleNotify->sendMessage(jsdialog::MessageType::Close, VclPtr<vcl::Window>(nullptr)); flush(); } @@ -330,8 +364,18 @@ void JSDialogSender::sendClosePopup(vcl::LOKWindowId nWindowId) std::unique_ptr<jsdialog::ActionDataMap> pData = std::make_unique<jsdialog::ActionDataMap>(); (*pData)[WINDOW_ID ""_ostr] = OUString::number(nWindowId); - mpIdleNotify->sendMessage(jsdialog::MessageType::PopupClose, nullptr, std::move(pData)); + mpIdleNotify->sendMessage(jsdialog::MessageType::PopupClose, VclPtr<vcl::Window>(nullptr), + std::move(pData)); flush(); } +void JSDialogSender::sendMenu(const VclPtr<PopupMenu>& pMenu) +{ + if (!mpIdleNotify) + return; + + mpIdleNotify->sendMessage(jsdialog::MessageType::Menu, pMenu); + mpIdleNotify->Start(); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx index 7ee79fe30b93..ccfb68ca9f9e 100644 --- a/vcl/source/window/builder.cxx +++ b/vcl/source/window/builder.cxx @@ -191,6 +191,8 @@ std::unique_ptr<weld::Builder> Application::CreateBuilder(weld::Widget* pParent, return JSInstanceBuilder::CreateSidebarBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, nLOKWindowId); else if (jsdialog::isBuilderEnabledForPopup(rUIFile)) return JSInstanceBuilder::CreatePopupBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile); + else if (jsdialog::isBuilderEnabledForMenu(rUIFile)) + return JSInstanceBuilder::CreateMenuBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile); else if (jsdialog::isBuilderEnabled(rUIFile, bMobile)) return JSInstanceBuilder::CreateDialogBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile); } diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx index 12c4490aa9df..d61736fac5f4 100644 --- a/vcl/source/window/menu.cxx +++ b/vcl/source/window/menu.cxx @@ -2390,6 +2390,27 @@ void Menu::HighlightItem( sal_uInt16 nItemPos ) } } +void Menu::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter) const +{ + rJsonWriter.put("id", "__MENU__"); // we have single instance of menu at the time per session + rJsonWriter.put("type", "menu"); + rJsonWriter.put("count", GetItemCount()); + { + auto aEntries = rJsonWriter.startArray("entries"); + for (size_t i = 0; i < GetItemCount(); i++) + { + auto aEntry = rJsonWriter.startStruct(); + sal_uInt16 nId = GetItemId(i); + rJsonWriter.put("row", GetItemIdent(nId)); + { + auto aColumns = rJsonWriter.startArray("columns"); + auto aColumn = rJsonWriter.startStruct(); + rJsonWriter.put("text", GetItemText(nId)); + } + } + } +} + MenuBarWindow* MenuBar::getMenuBarWindow() { // so far just a dynamic_cast, hopefully to be turned into something saner