summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2021-06-04 14:44:44 +0100
committerCaolán McNamara <caolanm@redhat.com>2021-06-04 18:16:55 +0200
commitb32582d4bc21953a43c469872d58445ee1a5ff2b (patch)
tree4d10ab47598fd631e3b96444161616cbbb578dc4
parent04716690f6c5193f15868bc71e7d17c53e085a54 (diff)
gtk4: support radio groups in GtkMenuButton menus
Change-Id: I4e6b61e2c16b208a91490cfbaf5dc933d45c1df0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/116718 Tested-by: Caolán McNamara <caolanm@redhat.com> Reviewed-by: Caolán McNamara <caolanm@redhat.com>
-rw-r--r--vcl/unx/gtk3/gtkinst.cxx74
-rw-r--r--vcl/unx/gtk4/convert3to4.cxx56
2 files changed, 113 insertions, 17 deletions
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