vcl/unx/gtk3/gtkinst.cxx | 74 ++++++++++++++++++++++++++++++++++++++----- vcl/unx/gtk4/convert3to4.cxx | 56 +++++++++++++++++++++++++++----- 2 files changed, 113 insertions(+), 17 deletions(-)
New commits: commit b32582d4bc21953a43c469872d58445ee1a5ff2b Author: Caolán McNamara <caol...@redhat.com> AuthorDate: Fri Jun 4 14:44:44 2021 +0100 Commit: Caolán McNamara <caol...@redhat.com> CommitDate: Fri Jun 4 18:16:55 2021 +0200 gtk4: support radio groups in GtkMenuButton menus Change-Id: I4e6b61e2c16b208a91490cfbaf5dc933d45c1df0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/116718 Tested-by: Caolán McNamara <caol...@redhat.com> Reviewed-by: Caolán McNamara <caol...@redhat.com> diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx index 9615c36dac09..5b225ac558b3 100644 --- a/vcl/unx/gtk3/gtkinst.cxx +++ b/vcl/unx/gtk3/gtkinst.cxx @@ -9195,6 +9195,10 @@ private: GtkImage* m_pImage; #else GtkPicture* m_pImage; + o3tl::sorted_vector<OString> m_aInsertedActions; // must outlive m_aActionEntries + std::map<OString, OString> m_aIdToAction; + std::vector<GActionEntry> m_aActionEntries; + GActionGroup* m_pActionGroup; #endif GtkWidget* m_pLabel; #if !GTK_CHECK_VERSION(4, 0, 0) @@ -9390,6 +9394,59 @@ private: #endif } +#if GTK_CHECK_VERSION(4, 0, 0) + // build an action group for the menu, "action" is the normal menu entry case + // the others are radiogroups + void update_action_group_from_popover_model() + { + for (const auto& rAction : m_aActionEntries) + g_action_map_remove_action(G_ACTION_MAP(m_pActionGroup), rAction.name); + m_aActionEntries.clear(); + m_aInsertedActions.clear(); + m_aIdToAction.clear(); + + m_aActionEntries.push_back({"action", action_activated, "s", nullptr, nullptr, {}}); + + GtkPopover* pPopover = gtk_menu_button_get_popover(m_pMenuButton); + if (GMenuModel* pMenuModel = GTK_IS_POPOVER_MENU(pPopover) ? + gtk_popover_menu_get_menu_model(GTK_POPOVER_MENU(pPopover)) : + nullptr) + { + for (int i = 0, nCount = g_menu_model_get_n_items(pMenuModel); i < nCount; ++i) + { + OString sAction, sTarget; + char *id; + if (g_menu_model_get_item_attribute(pMenuModel, i, "action", "s", &id)) + { + assert(OString(id).startsWith("menu.")); + + sAction = OString(id + 5); + + auto res = m_aInsertedActions.insert(sAction); + if (res.second) + { + // the const char* arg isn't copied by anything so it must continue to exist for the life time of + // the action group + m_aActionEntries.push_back({res.first->getStr(), action_activated, "s", "'none'", nullptr, {}}); + } + + g_free(id); + } + + if (g_menu_model_get_item_attribute(pMenuModel, i, "target", "s", &id)) + { + sTarget = OString(id); + g_free(id); + } + + m_aIdToAction[sTarget] = sAction; + } + } + + g_action_map_add_action_entries(G_ACTION_MAP(m_pActionGroup), m_aActionEntries.data(), m_aActionEntries.size(), this); + } +#endif + public: #if !GTK_CHECK_VERSION(4, 0, 0) GtkInstanceMenuButton(GtkMenuButton* pMenuButton, GtkWidget* pMenuAlign, GtkInstanceBuilder* pBuilder, bool bTakeOwnership) @@ -9421,14 +9478,10 @@ public: m_pBox = formatMenuButton(m_pLabel); #if GTK_CHECK_VERSION(4, 0, 0) - static GActionEntry entries[] = - { - { "action", action_activated, "s", nullptr, nullptr } - }; + m_pActionGroup = G_ACTION_GROUP(g_simple_action_group_new()); + gtk_widget_insert_action_group(GTK_WIDGET(m_pMenuButton), "menu", m_pActionGroup); - GActionGroup* pActions = G_ACTION_GROUP(g_simple_action_group_new()); - g_action_map_add_action_entries(G_ACTION_MAP(pActions), entries, SAL_N_ELEMENTS(entries), this); - gtk_widget_insert_action_group(GTK_WIDGET(m_pMenuButton), "menu", pActions); + update_action_group_from_popover_model(); #endif } @@ -9559,7 +9612,10 @@ public: virtual void set_item_active(const OString& rIdent, bool bActive) override { -#if !GTK_CHECK_VERSION(4, 0, 0) +#if GTK_CHECK_VERSION(4, 0, 0) + g_action_group_change_action_state(m_pActionGroup, m_aIdToAction[rIdent].getStr(), + g_variant_new_string(rIdent.getStr())); +#else MenuHelper::set_item_active(rIdent, bActive); #endif } @@ -9633,6 +9689,7 @@ public: #if GTK_CHECK_VERSION(4, 0, 0) gtk_menu_button_set_popover(m_pMenuButton, m_pPopover); + update_action_group_from_popover_model(); return; #else @@ -21988,6 +22045,7 @@ weld::Builder* GtkInstance::CreateBuilder(weld::Widget* pParent, const OUString& rUIFile != "modules/smath/ui/fontsizedialog.ui" && rUIFile != "modules/smath/ui/fonttypedialog.ui" && rUIFile != "modules/smath/ui/savedefaultsdialog.ui" && + rUIFile != "modules/smath/ui/spacingdialog.ui" && rUIFile != "modules/swriter/ui/bibliographyentry.ui" && rUIFile != "modules/swriter/ui/columndialog.ui" && rUIFile != "modules/swriter/ui/columnpage.ui" && diff --git a/vcl/unx/gtk4/convert3to4.cxx b/vcl/unx/gtk4/convert3to4.cxx index c2c2872b0897..33a3c4d5adc5 100644 --- a/vcl/unx/gtk4/convert3to4.cxx +++ b/vcl/unx/gtk4/convert3to4.cxx @@ -113,10 +113,26 @@ void AddBorderAsMargins(const css::uno::Reference<css::xml::dom::XNode>& xNode, xNode->insertBefore(CreateProperty(xDoc, "margin-start", rBorderWidth), xMarginEnd); } -css::uno::Reference<css::xml::dom::XNode> -ConvertMenu(const css::uno::Reference<css::xml::dom::XNode>& xMenu, - const css::uno::Reference<css::xml::dom::XNode>& xNode) +struct MenuEntry +{ + bool m_bDrawAsRadio; + OUString m_sRadioGroup; + css::uno::Reference<css::xml::dom::XNode> m_xPropertyLabel; + + MenuEntry(bool bDrawAsRadio, const OUString& rRadioGroup, + const css::uno::Reference<css::xml::dom::XNode>& rPropertyLabel) + : m_bDrawAsRadio(bDrawAsRadio) + , m_sRadioGroup(rRadioGroup) + , m_xPropertyLabel(rPropertyLabel) + { + } +}; + +MenuEntry ConvertMenu(const css::uno::Reference<css::xml::dom::XNode>& xMenu, + const css::uno::Reference<css::xml::dom::XNode>& xNode) { + bool bDrawAsRadio = false; + OUString sRadioGroup; css::uno::Reference<css::xml::dom::XNode> xPropertyLabel; css::uno::Reference<css::xml::dom::XNode> xChild = xNode->getFirstChild(); @@ -132,14 +148,27 @@ ConvertMenu(const css::uno::Reference<css::xml::dom::XNode>& xMenu, { xPropertyLabel = xChild; } + else if (sName == "draw-as-radio") + { + bDrawAsRadio = toBool(xChild->getFirstChild()->getNodeValue()); + } + else if (sName == "group") + { + sRadioGroup = xChild->getFirstChild()->getNodeValue(); + } } auto xNextChild = xChild->getNextSibling(); + bool bChildDrawAsRadio = false; + OUString sChildRadioGroup; css::uno::Reference<css::xml::dom::XNode> xChildPropertyLabel; if (xChild->hasChildNodes()) { - xChildPropertyLabel = ConvertMenu(xMenu, xChild); + MenuEntry aEntry = ConvertMenu(xMenu, xChild); + bChildDrawAsRadio = aEntry.m_bDrawAsRadio; + sChildRadioGroup = aEntry.m_sRadioGroup; + xChildPropertyLabel = aEntry.m_xPropertyLabel; } if (xChild->getNodeName() == "object") @@ -150,8 +179,11 @@ ConvertMenu(const css::uno::Reference<css::xml::dom::XNode>& xMenu, css::uno::Reference<css::xml::dom::XNode> xClass = xMap->getNamedItem("class"); OUString sClass(xClass->getNodeValue()); - if (sClass == "GtkMenuItem") + if (sClass == "GtkMenuItem" || sClass == "GtkRadioMenuItem") { + css::uno::Reference<css::xml::dom::XNode> xId = xMap->getNamedItem("id"); + OUString sId = xId->getNodeValue(); + /* <item> <attribute name='label' translatable='yes'>whatever</attribute> @@ -192,7 +224,14 @@ ConvertMenu(const css::uno::Reference<css::xml::dom::XNode>& xMenu, = xDoc->createAttribute("name"); xActionName->setValue("action"); xActionAttr->setAttributeNode(xActionName); - xActionAttr->appendChild(xDoc->createTextNode("menu.action")); + if (bChildDrawAsRadio) + { + if (sChildRadioGroup.isEmpty()) + sChildRadioGroup = sId; + xActionAttr->appendChild(xDoc->createTextNode("menu." + sChildRadioGroup)); + } + else + xActionAttr->appendChild(xDoc->createTextNode("menu.action")); xItem->appendChild(xActionAttr); css::uno::Reference<css::xml::dom::XElement> xTargetAttr @@ -201,8 +240,7 @@ ConvertMenu(const css::uno::Reference<css::xml::dom::XNode>& xMenu, = xDoc->createAttribute("name"); xTargetName->setValue("target"); xTargetAttr->setAttributeNode(xTargetName); - css::uno::Reference<css::xml::dom::XNode> xId = xMap->getNamedItem("id"); - xTargetAttr->appendChild(xDoc->createTextNode(xId->getNodeValue())); + xTargetAttr->appendChild(xDoc->createTextNode(sId)); xItem->appendChild(xTargetAttr); } } @@ -210,7 +248,7 @@ ConvertMenu(const css::uno::Reference<css::xml::dom::XNode>& xMenu, xChild = xNextChild; } - return xPropertyLabel; + return MenuEntry(bDrawAsRadio, sRadioGroup, xPropertyLabel); } struct ConvertResult _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits