include/vcl/builder.hxx | 6 ----- include/vcl/builderbase.hxx | 5 ++++ include/vcl/widgetbuilder.hxx | 6 +++++ vcl/inc/qt5/QtBuilder.hxx | 2 + vcl/qt5/QtBuilder.cxx | 28 +++++++++++++++++++++++++ vcl/source/window/builder.cxx | 46 ++++++++++++++++++++++-------------------- 6 files changed, 67 insertions(+), 26 deletions(-)
New commits: commit e32d069ba74229916c459842d0c27c5a89b82168 Author: Michael Weghorn <m.wegh...@posteo.de> AuthorDate: Fri Dec 13 22:55:50 2024 +0100 Commit: Michael Weghorn <m.wegh...@posteo.de> CommitDate: Sat Dec 14 01:03:50 2024 +0100 tdf#130857 qt weld: Evaluate radio button groups Add support for radio button groups by implementing QtBuilder::setRadioButtonGroup. Qt provides QButtonGroup [1] to explicitly group buttons. Quoting from the QRadioButton doc [2]: > Radio buttons are autoExclusive by default. If auto-exclusive is > enabled, radio buttons that belong to the same parent widget behave as > if they were part of the same exclusive button group. If you need > multiple exclusive button groups for radio buttons that belong to the > same parent widget, put them into a QButtonGroup. To make sure exactly one button group exists and it gets freed automatically, make the radio button whose ID is set for the GtkRadioButton::group property [3] the parent (and thus the owner) of the group. Set a pointer of the group as a property for that button, so it can be retrieved directly from the button without requiring another map/... to keep track. With this in place, the radio buttons in Writer's "Tools" -> "Options" -> "LibreOfficeDev Writer" -> "Mail Merge Email" -> "Server Authentication" dialog are now grouped into 2 groups as expected in a WIP branch, rather than all of them being in one group (because they all have the same grid as a parent). Support for that dialog will be declared in an upcoming commit. [1] https://doc.qt.io/qt-6/qbuttongroup.html [2] https://doc.qt.io/qt-6/qradiobutton.html [3] https://docs.gtk.org/gtk3/property.RadioButton.group.html Change-Id: I60e8b5b525411c299ac50a8712361cc8572cc403 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178444 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.wegh...@posteo.de> diff --git a/vcl/qt5/QtBuilder.cxx b/vcl/qt5/QtBuilder.cxx index 963d8e71e861..7d78acb281f8 100644 --- a/vcl/qt5/QtBuilder.cxx +++ b/vcl/qt5/QtBuilder.cxx @@ -20,6 +20,7 @@ #include <vcl/qt/QtUtils.hxx> #include <QtGui/QStandardItemModel> +#include <QtWidgets/QButtonGroup> #include <QtWidgets/QCheckBox> #include <QtWidgets/QComboBox> #include <QtWidgets/QDialog> @@ -275,6 +276,7 @@ QObject* QtBuilder::makeObject(QObject* pParent, std::u16string_view sName, std: else if (sName == u"GtkRadioButton") { pObject = new QRadioButton(pParentWidget); + extractRadioButtonGroup(sID, rMap); } else if (sName == u"GtkScrolledWindow") { @@ -473,9 +475,30 @@ void QtBuilder::setMnemonicWidget(const OUString& rLabelId, const OUString& rMne pLabel->setBuddy(static_cast<QWidget*>(pBuddy)); } -void QtBuilder::setRadioButtonGroup(const OUString&, const OUString&) +void QtBuilder::setRadioButtonGroup(const OUString& rRadioButtonId, const OUString& rRadioGroupId) { - SAL_WARN("vcl.qt", "Not implemented yet"); + // insert all buttons into a button group owned by button whose matches the group's + QRadioButton* pGroupOwner = get<QRadioButton>(rRadioGroupId); + assert(pGroupOwner && "No radio button with the given group name"); + + QButtonGroup* pButtonGroup = nullptr; + static const char* const pPropertyKey = "PROPERTY_BUTTONGROUP"; + QVariant aVariant = pGroupOwner->property(pPropertyKey); + if (aVariant.canConvert<QButtonGroup*>()) + { + pButtonGroup = aVariant.value<QButtonGroup*>(); + } + else + { + pButtonGroup = new QButtonGroup(pGroupOwner); + pButtonGroup->addButton(pGroupOwner); + } + + QRadioButton* pRadioButton = get<QRadioButton>(rRadioButtonId); + assert(pRadioButton && "No radio button with given ID"); + pButtonGroup->addButton(pRadioButton); + + pGroupOwner->setProperty(pPropertyKey, QVariant::fromValue(pButtonGroup)); } void QtBuilder::setPriority(QObject*, int) { SAL_WARN("vcl.qt", "Ignoring priority"); } commit 7f64114f90c58599c459b9b5ce622e4d97bf9388 Author: Michael Weghorn <m.wegh...@posteo.de> AuthorDate: Fri Dec 13 22:28:02 2024 +0100 Commit: Michael Weghorn <m.wegh...@posteo.de> CommitDate: Sat Dec 14 01:03:45 2024 +0100 tdf#130857 VclBuilder: Move more radio group logic to base class Add a purely virtual WidgetBuilder::setRadioButtonGroup method and call it for each of the entries in the map from WidgetBuilder::processUIFile, in line with what is already done for mnemonic widgets. Move the existing logic for handling such a pair to the new VclBuilder override, VclBuilder::setRadioButtonGroup. An "actual" implementation for QtBuilder will be added in a separate commit, just emit a warning there for now when the method gets called. Change-Id: I027278014bb18d9d5f3de8189cd2f515959bf3e7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178443 Reviewed-by: Michael Weghorn <m.wegh...@posteo.de> Tested-by: Jenkins diff --git a/include/vcl/builder.hxx b/include/vcl/builder.hxx index 38e9aab01dcc..96136b04bb4c 100644 --- a/include/vcl/builder.hxx +++ b/include/vcl/builder.hxx @@ -270,6 +270,7 @@ private: bool bToolbarItem) override; void setMnemonicWidget(const OUString& rLabelId, const OUString& rMnemonicWidgetId) override; + void setRadioButtonGroup(const OUString& rRadioButtonId, const OUString& rRadioGroupId) override; void setPriority(vcl::Window* pWindow, int nPriority) override; void setContext(vcl::Window* pWindow, std::vector<vcl::EnumContext::Context>&& aContext) override; diff --git a/include/vcl/widgetbuilder.hxx b/include/vcl/widgetbuilder.hxx index 22641cb5d552..3b191b2f52f6 100644 --- a/include/vcl/widgetbuilder.hxx +++ b/include/vcl/widgetbuilder.hxx @@ -82,6 +82,10 @@ protected: { setMnemonicWidget(rMnemonic.m_sID, rMnemonic.m_sValue); } + + // Set radiobutton groups when everything has been imported + for (const RadioButtonGroupMap& rGroup : getRadioButtonGroupMaps()) + setRadioButtonGroup(rGroup.m_sID, rGroup.m_sValue); } // either pParent or pAtkProps must be set, pParent for a child of a widget, pAtkProps for @@ -546,6 +550,8 @@ protected: = 0; virtual void setMnemonicWidget(const OUString& rLabelId, const OUString& rMnemonicWidgetId) = 0; + virtual void setRadioButtonGroup(const OUString& rRadioButtonId, const OUString& rRadioGroupId) + = 0; virtual void setPriority(Widget* pWidget, int nPriority) = 0; virtual void setContext(Widget* pWidget, std::vector<vcl::EnumContext::Context>&& aContext) = 0; diff --git a/vcl/inc/qt5/QtBuilder.hxx b/vcl/inc/qt5/QtBuilder.hxx index a69158e10a8f..743dc9961ec6 100644 --- a/vcl/inc/qt5/QtBuilder.hxx +++ b/vcl/inc/qt5/QtBuilder.hxx @@ -70,6 +70,8 @@ public: virtual void setMnemonicWidget(const OUString& rLabelId, const OUString& rMnemonicWidgetId) override; + virtual void setRadioButtonGroup(const OUString& rRadioButtonId, + const OUString& rRadioGroupId) override; virtual void setPriority(QObject* pObject, int nPriority) override; virtual void setContext(QObject* pObject, std::vector<vcl::EnumContext::Context>&& aContext) override; diff --git a/vcl/qt5/QtBuilder.cxx b/vcl/qt5/QtBuilder.cxx index 98902bb9ab5e..963d8e71e861 100644 --- a/vcl/qt5/QtBuilder.cxx +++ b/vcl/qt5/QtBuilder.cxx @@ -473,6 +473,11 @@ void QtBuilder::setMnemonicWidget(const OUString& rLabelId, const OUString& rMne pLabel->setBuddy(static_cast<QWidget*>(pBuddy)); } +void QtBuilder::setRadioButtonGroup(const OUString&, const OUString&) +{ + SAL_WARN("vcl.qt", "Not implemented yet"); +} + void QtBuilder::setPriority(QObject*, int) { SAL_WARN("vcl.qt", "Ignoring priority"); } void QtBuilder::setContext(QObject*, std::vector<vcl::EnumContext::Context>&&) diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx index 8172e3d30e99..cec44574e48c 100644 --- a/vcl/source/window/builder.cxx +++ b/vcl/source/window/builder.cxx @@ -558,24 +558,6 @@ VclBuilder::VclBuilder(vcl::Window* pParent, std::u16string_view sUIDir, const O } } - //Set radiobutton groups when everything has been imported - for (auto const& elem : getRadioButtonGroupMaps()) - { - RadioButton *pOne = get<RadioButton>(elem.m_sID); - RadioButton *pOther = get<RadioButton>(elem.m_sValue); - SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton group"); - if (pOne && pOther) - { - if (isLegacy()) - pOne->group(*pOther); - else - { - pOther->group(*pOne); - std::stable_sort(pOther->m_xGroup->begin(), pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this)); - } - } - } - #ifndef NDEBUG o3tl::sorted_vector<OUString> models; #endif @@ -2809,6 +2791,23 @@ void VclBuilder::setMnemonicWidget(const OUString& rLabelId, const OUString& rMn pOne->set_mnemonic_widget(pOther); } +void VclBuilder::setRadioButtonGroup(const OUString& rRadioButtonId, const OUString& rRadioGroupId) +{ + RadioButton *pOne = get<RadioButton>(rRadioButtonId); + RadioButton *pOther = get<RadioButton>(rRadioGroupId); + SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton group"); + if (pOne && pOther) + { + if (isLegacy()) + pOne->group(*pOther); + else + { + pOther->group(*pOne); + std::stable_sort(pOther->m_xGroup->begin(), pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this)); + } + } +} + void VclBuilder::setPriority(vcl::Window* pWindow, int nPriority) { vcl::IPrioritable* pPrioritable = dynamic_cast<vcl::IPrioritable*>(pWindow); commit ce67331daa101ce1d8fc81fe1c843d8810f7a33c Author: Michael Weghorn <m.wegh...@posteo.de> AuthorDate: Fri Dec 13 22:11:34 2024 +0100 Commit: Michael Weghorn <m.wegh...@posteo.de> CommitDate: Sat Dec 14 01:03:39 2024 +0100 tdf#130857 VclBuilder: Store radio button groups in BuilderBase Move the std::vector<RadioButtonGroupMap> and the method to extract and add to that map from VclBuilder to its base class, BuilderBase. Add a getter, BuilderBase::getRadioButtonGroupMaps instead of accessing the base class member directly in the VclBuilder subclass. Change-Id: Ic0d2abbd9faa41760d542ab22a80f2ba23136ccf Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178442 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.wegh...@posteo.de> diff --git a/include/vcl/builder.hxx b/include/vcl/builder.hxx index 254b4e52d328..38e9aab01dcc 100644 --- a/include/vcl/builder.hxx +++ b/include/vcl/builder.hxx @@ -127,8 +127,6 @@ private: }; std::vector<WinAndId> m_aChildren; - typedef StringPair RadioButtonGroupMap; - struct ButtonImageWidgetMap { OUString m_sID; @@ -179,8 +177,6 @@ private: struct VclParserState { - std::vector<RadioButtonGroupMap> m_aGroupMaps; - std::vector<ComboBoxModelMap> m_aModelMaps; std::vector<TextBufferMap> m_aTextBufferMaps; @@ -254,7 +250,6 @@ private: static int getImageSize(const stringmap &rMap); - void extractGroup(const OUString &id, stringmap &rVec); void extractModel(const OUString &id, stringmap &rVec); void extractBuffer(const OUString &id, stringmap &rVec); static bool extractAdjustmentToMap(const OUString &id, stringmap &rVec, std::vector<WidgetAdjustmentMap>& rAdjustmentMap); diff --git a/include/vcl/builderbase.hxx b/include/vcl/builderbase.hxx index 573817315f72..eb3aa847199f 100644 --- a/include/vcl/builderbase.hxx +++ b/include/vcl/builderbase.hxx @@ -77,6 +77,7 @@ protected: }; typedef StringPair MnemonicWidgetMap; + typedef StringPair RadioButtonGroupMap; static void collectPangoAttribute(xmlreader::XmlReader& reader, stringmap& rMap); static void collectAtkRelationAttribute(xmlreader::XmlReader& reader, stringmap& rMap); @@ -112,6 +113,9 @@ protected: void extractMnemonicWidget(const OUString& id, stringmap& rMap); const std::vector<MnemonicWidgetMap>& getMnemonicWidgetMaps() const; + void extractRadioButtonGroup(const OUString& id, stringmap& rVec); + const std::vector<RadioButtonGroupMap>& getRadioButtonGroupMaps() const; + OUString finalizeValue(const OString& rContext, const OString& rValue, const bool bTranslate) const; @@ -149,6 +153,7 @@ private: std::map<OUString, TextBuffer> m_aTextBuffers; std::vector<MnemonicWidgetMap> m_aMnemonicWidgetMaps; + std::vector<RadioButtonGroupMap> m_aRadioButtonGroupMaps; }; std::unique_ptr<ParserState> m_pParserState; diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx index ccfb68ca9f9e..8172e3d30e99 100644 --- a/vcl/source/window/builder.cxx +++ b/vcl/source/window/builder.cxx @@ -481,6 +481,11 @@ const std::vector<BuilderBase::MnemonicWidgetMap>& BuilderBase::getMnemonicWidge return m_pParserState->m_aMnemonicWidgetMaps; } +const std::vector<BuilderBase::RadioButtonGroupMap>& BuilderBase::getRadioButtonGroupMaps() const { + assert(m_pParserState && "parser state no more valid"); + return m_pParserState->m_aRadioButtonGroupMaps; +} + OUString BuilderBase::finalizeValue(const OString& rContext, const OString& rValue, const bool bTranslate) const { @@ -554,7 +559,7 @@ VclBuilder::VclBuilder(vcl::Window* pParent, std::u16string_view sUIDir, const O } //Set radiobutton groups when everything has been imported - for (auto const& elem : m_pVclParserState->m_aGroupMaps) + for (auto const& elem : getRadioButtonGroupMaps()) { RadioButton *pOne = get<RadioButton>(elem.m_sID); RadioButton *pOther = get<RadioButton>(elem.m_sValue); @@ -1098,7 +1103,7 @@ namespace } } -void VclBuilder::extractGroup(const OUString &id, stringmap &rMap) +void BuilderBase::extractRadioButtonGroup(const OUString &id, stringmap &rMap) { VclBuilder::stringmap::iterator aFind = rMap.find(u"group"_ustr); if (aFind != rMap.end()) @@ -1107,7 +1112,7 @@ void VclBuilder::extractGroup(const OUString &id, stringmap &rMap) sal_Int32 nDelim = sID.indexOf(':'); if (nDelim != -1) sID = sID.copy(0, nDelim); - m_pVclParserState->m_aGroupMaps.emplace_back(id, sID); + m_pParserState->m_aRadioButtonGroupMaps.emplace_back(id, sID); rMap.erase(aFind); } } @@ -1603,7 +1608,7 @@ VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OUString } else if (name == "GtkRadioButton") { - extractGroup(id, rMap); + extractRadioButtonGroup(id, rMap); WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK; VclPtr<RadioButton> xButton = VclPtr<RadioButton>::Create(pParent, true, nBits); xButton->SetImageAlign(ImageAlign::Left); //default to left