diff options
author | J-P Nurmi <jpnurmi@theqtcompany.com> | 2015-11-01 19:18:31 +0100 |
---|---|---|
committer | J-P Nurmi <jpnurmi@theqtcompany.com> | 2015-11-03 09:35:49 +0000 |
commit | a108298bb9529dd1ad4b195849f3c1e12daa82bf (patch) | |
tree | 73510314439ab8c13764d1ed906b7e9154d53d15 | |
parent | 92aa5333846081b294fc332e64f7e49b329edf92 (diff) |
Add AbstractButton::autoExclusive
This feature is adopted from QtWidgets' QAbstractButton. It's no longer
necessary to create an ExclusiveGroup to manage a simple list of radio
buttons:
Column {
RadioButton { text: "Option 1" }
RadioButton { text: "Option 2" }
}
Change-Id: Ib4cb718c3b3034c9c956b2f23db4b06b00547b8e
Reviewed-by: Mitch Curtis <mitch.curtis@theqtcompany.com>
-rw-r--r-- | src/templates/qquickabstractbutton.cpp | 93 | ||||
-rw-r--r-- | src/templates/qquickabstractbutton_p.h | 6 | ||||
-rw-r--r-- | src/templates/qquickabstractbutton_p_p.h | 9 | ||||
-rw-r--r-- | src/templates/qquickradiobutton.cpp | 11 | ||||
-rw-r--r-- | src/templates/qquicktabbutton.cpp | 2 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_radiobutton.qml | 68 |
6 files changed, 173 insertions, 16 deletions
diff --git a/src/templates/qquickabstractbutton.cpp b/src/templates/qquickabstractbutton.cpp index 849d2340..0b211e46 100644 --- a/src/templates/qquickabstractbutton.cpp +++ b/src/templates/qquickabstractbutton.cpp @@ -36,9 +36,11 @@ #include "qquickabstractbutton_p.h" #include "qquickabstractbutton_p_p.h" +#include "qquickexclusivegroup_p.h" #include <QtGui/qguiapplication.h> #include <QtQuick/private/qquickevents_p_p.h> +#include <QtQml/qqmllist.h> QT_BEGIN_NAMESPACE @@ -100,7 +102,7 @@ static const int AUTO_REPEAT_INTERVAL = 100; */ QQuickAbstractButtonPrivate::QQuickAbstractButtonPrivate() : - pressed(false), checked(false), checkable(false), exclusive(false), autoRepeat(false), + pressed(false), checked(false), checkable(false), autoExclusive(false), autoRepeat(false), delayTimer(0), repeatTimer(0), repeatButton(Qt::NoButton), label(Q_NULLPTR), indicator(Q_NULLPTR) { } @@ -132,6 +134,60 @@ void QQuickAbstractButtonPrivate::stopPressRepeat() } } +QQuickExclusiveGroup *QQuickAbstractButtonPrivate::exclusiveGroup() const +{ + Q_Q(const QQuickAbstractButton); + QQuickExclusiveGroupAttached *attached = qobject_cast<QQuickExclusiveGroupAttached *>(qmlAttachedPropertiesObject<QQuickExclusiveGroup>(q, false)); + if (attached) + return attached->group(); + return Q_NULLPTR; +} + +QQuickAbstractButton *QQuickAbstractButtonPrivate::findCheckedButton() const +{ + Q_Q(const QQuickAbstractButton); + QQuickExclusiveGroup *group = exclusiveGroup(); + if (group) + return qobject_cast<QQuickAbstractButton *>(group->current()); + + QList<QQuickAbstractButton *> buttons = findExclusiveButtons(); + // TODO: A singular QRadioButton can be unchecked, which seems logical, + // because there's nothing to be exclusive with. However, a RadioButton + // from QtQuick.Controls 1.x can never be unchecked, which is the behavior + // that QQuickRadioButton adopted. Uncommenting the following count check + // gives the QRadioButton behavior. Notice that tst_radiobutton.qml needs + // to be updated. + if (!autoExclusive /*|| buttons.count() == 1*/) + return Q_NULLPTR; + + foreach (QQuickAbstractButton *button, buttons) { + if (button->isChecked() && button != q) + return button; + } + return checked ? const_cast<QQuickAbstractButton *>(q) : Q_NULLPTR; +} + +QList<QQuickAbstractButton *> QQuickAbstractButtonPrivate::findExclusiveButtons() const +{ + QList<QQuickAbstractButton *> buttons; + QQuickExclusiveGroup *group = exclusiveGroup(); + if (group) { + QQmlListProperty<QObject> checkables = group->checkables(); + int count = checkables.count(&checkables); + for (int i = 0; i < count; ++i) { + QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(checkables.at(&checkables, i)); + if (button) + buttons += button; + } + } else if (parentItem) { + foreach (QQuickAbstractButton *button, parentItem->findChildren<QQuickAbstractButton *>()) { + if (button->autoExclusive() && !button->d_func()->exclusiveGroup()) + buttons += button; + } + } + return buttons; +} + QQuickAbstractButton::QQuickAbstractButton(QQuickItem *parent) : QQuickControl(*(new QQuickAbstractButtonPrivate), parent) { @@ -236,16 +292,33 @@ void QQuickAbstractButton::setCheckable(bool checkable) } } -bool QQuickAbstractButton::isExclusive() const +/*! + \qmlproperty bool Qt.labs.controls::AbstractButton::autoExclusive + + This property holds whether auto-exclusivity is enabled. + + If auto-exclusivity is enabled, checkable buttons that belong to the same + parent item behave as if they were part of the same ExclusiveGroup. Only + one button can be checked at any time; checking another button automatically + unchecks the previously checked one. + + \note The property has no effect on buttons that belong to an ExclusiveGroup. + + RadioButton and TabButton are auto-exclusive by default. +*/ +bool QQuickAbstractButton::autoExclusive() const { Q_D(const QQuickAbstractButton); - return d->exclusive; + return d->autoExclusive; } -void QQuickAbstractButton::setExclusive(bool exclusive) +void QQuickAbstractButton::setAutoExclusive(bool exclusive) { Q_D(QQuickAbstractButton); - d->exclusive = exclusive; + if (d->autoExclusive != exclusive) { + d->autoExclusive = exclusive; + emit autoExclusiveChanged(); + } } /*! @@ -471,13 +544,19 @@ void QQuickAbstractButton::timerEvent(QTimerEvent *event) void QQuickAbstractButton::checkStateSet() { + Q_D(QQuickAbstractButton); + if (d->checked) { + QQuickAbstractButton *button = d->findCheckedButton(); + if (button && button != this) + button->setChecked(false); + } } void QQuickAbstractButton::nextCheckState() { Q_D(QQuickAbstractButton); - if (d->checkable) - setChecked(d->exclusive || !d->checked); + if (d->checkable && (!d->checked || d->findCheckedButton() != this)) + setChecked(!d->checked); } #ifndef QT_NO_ACCESSIBILITY diff --git a/src/templates/qquickabstractbutton_p.h b/src/templates/qquickabstractbutton_p.h index 5ab24a23..53d17af3 100644 --- a/src/templates/qquickabstractbutton_p.h +++ b/src/templates/qquickabstractbutton_p.h @@ -62,6 +62,7 @@ class Q_LABSTEMPLATES_EXPORT QQuickAbstractButton : public QQuickControl Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL) Q_PROPERTY(bool checked READ isChecked WRITE setChecked NOTIFY checkedChanged FINAL) Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable NOTIFY checkableChanged FINAL) + Q_PROPERTY(bool autoExclusive READ autoExclusive WRITE setAutoExclusive NOTIFY autoExclusiveChanged FINAL) Q_PROPERTY(bool autoRepeat READ autoRepeat WRITE setAutoRepeat NOTIFY autoRepeatChanged FINAL) Q_PROPERTY(QQuickItem *indicator READ indicator WRITE setIndicator NOTIFY indicatorChanged FINAL) Q_PROPERTY(QQuickItem *label READ label WRITE setLabel NOTIFY labelChanged FINAL) @@ -81,8 +82,8 @@ public: bool isCheckable() const; void setCheckable(bool checkable); - bool isExclusive() const; - void setExclusive(bool exclusive); + bool autoExclusive() const; + void setAutoExclusive(bool exclusive); bool autoRepeat() const; void setAutoRepeat(bool repeat); @@ -106,6 +107,7 @@ Q_SIGNALS: void pressedChanged(); void checkedChanged(); void checkableChanged(); + void autoExclusiveChanged(); void autoRepeatChanged(); void indicatorChanged(); void labelChanged(); diff --git a/src/templates/qquickabstractbutton_p_p.h b/src/templates/qquickabstractbutton_p_p.h index bfdadb19..dfa87e7f 100644 --- a/src/templates/qquickabstractbutton_p_p.h +++ b/src/templates/qquickabstractbutton_p_p.h @@ -52,6 +52,9 @@ QT_BEGIN_NAMESPACE +class QQuickAbstractButton; +class QQuickExclusiveGroup; + class QQuickAbstractButtonPrivate : public QQuickControlPrivate { Q_DECLARE_PUBLIC(QQuickAbstractButton) @@ -63,11 +66,15 @@ public: void startPressRepeat(); void stopPressRepeat(); + QQuickExclusiveGroup *exclusiveGroup() const; + QQuickAbstractButton *findCheckedButton() const; + QList<QQuickAbstractButton *> findExclusiveButtons() const; + QString text; bool pressed; bool checked; bool checkable; - bool exclusive; + bool autoExclusive; bool autoRepeat; int delayTimer; int repeatTimer; diff --git a/src/templates/qquickradiobutton.cpp b/src/templates/qquickradiobutton.cpp index 7a94248a..92a14394 100644 --- a/src/templates/qquickradiobutton.cpp +++ b/src/templates/qquickradiobutton.cpp @@ -61,21 +61,22 @@ QT_BEGIN_NAMESPACE \li A radio button that is disabled. \endtable + Radio buttons are \l {AbstractButton::autoExclusive}{auto-exclusive} + by default. Only one button can be checked at any time amongst radio + buttons that belong to the same parent item; checking another button + automatically unchecks the previously checked one. + \code ColumnLayout { - ExclusiveGroup { id: group } RadioButton { checked: true text: qsTr("First") - ExclusiveGroup.group: group } RadioButton { text: qsTr("Second") - ExclusiveGroup.group: group } RadioButton { text: qsTr("Third") - ExclusiveGroup.group: group } } \endcode @@ -87,7 +88,7 @@ QQuickRadioButton::QQuickRadioButton(QQuickItem *parent) : QQuickAbstractButton(parent) { setCheckable(true); - setExclusive(true); + setAutoExclusive(true); } #ifndef QT_NO_ACCESSIBILITY diff --git a/src/templates/qquicktabbutton.cpp b/src/templates/qquicktabbutton.cpp index a301cc62..23e8a221 100644 --- a/src/templates/qquicktabbutton.cpp +++ b/src/templates/qquicktabbutton.cpp @@ -59,7 +59,7 @@ QQuickTabButton::QQuickTabButton(QQuickItem *parent) : QQuickAbstractButton(parent) { setCheckable(true); - setExclusive(true); + setAutoExclusive(true); } #ifndef QT_NO_ACCESSIBILITY diff --git a/tests/auto/controls/data/tst_radiobutton.qml b/tests/auto/controls/data/tst_radiobutton.qml index d76a8c38..2df3542d 100644 --- a/tests/auto/controls/data/tst_radiobutton.qml +++ b/tests/auto/controls/data/tst_radiobutton.qml @@ -225,4 +225,72 @@ TestCase { container.destroy() } + + Component { + id: radioButtonGroup + Column { + // auto-exclusive buttons behave as if they were in their own exclusive group + RadioButton { } + RadioButton { } + + // explicitly grouped buttons are only exclusive with each other, not with + // auto-exclusive buttons, and the autoExclusive property is ignored + ExclusiveGroup { id: eg } + RadioButton { ExclusiveGroup.group: eg } + RadioButton { ExclusiveGroup.group: eg; autoExclusive: false } + + // non-exclusive buttons don't affect the others + RadioButton { autoExclusive: false } + RadioButton { autoExclusive: false } + } + } + + function test_autoExclusive() { + var container = radioButtonGroup.createObject(testCase) + compare(container.children.length, 6) + + var checkStates = [false, false, false, false, false, false] + for (var i = 0; i < 6; ++i) + compare(container.children[i].checked, checkStates[i]) + + container.children[0].checked = true + checkStates[0] = true + for (i = 0; i < 6; ++i) + compare(container.children[i].checked, checkStates[i]) + + container.children[1].checked = true + checkStates[0] = false + checkStates[1] = true + for (i = 0; i < 6; ++i) + compare(container.children[i].checked, checkStates[i]) + + container.children[2].checked = true + checkStates[2] = true + for (i = 0; i < 6; ++i) + compare(container.children[i].checked, checkStates[i]) + + container.children[3].checked = true + checkStates[2] = false + checkStates[3] = true + for (i = 0; i < 6; ++i) + compare(container.children[i].checked, checkStates[i]) + + container.children[4].checked = true + checkStates[4] = true + for (i = 0; i < 6; ++i) + compare(container.children[i].checked, checkStates[i]) + + container.children[5].checked = true + checkStates[5] = true + for (i = 0; i < 6; ++i) + compare(container.children[i].checked, checkStates[i]) + + container.children[0].checked = true + checkStates[0] = true + checkStates[1] = false + for (i = 0; i < 6; ++i) + compare(container.children[i].checked, checkStates[i]) + + container.destroy() + } } |