aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ-P Nurmi <jpnurmi@theqtcompany.com>2015-11-01 19:18:31 +0100
committerJ-P Nurmi <jpnurmi@theqtcompany.com>2015-11-03 09:35:49 +0000
commita108298bb9529dd1ad4b195849f3c1e12daa82bf (patch)
tree73510314439ab8c13764d1ed906b7e9154d53d15
parent92aa5333846081b294fc332e64f7e49b329edf92 (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.cpp93
-rw-r--r--src/templates/qquickabstractbutton_p.h6
-rw-r--r--src/templates/qquickabstractbutton_p_p.h9
-rw-r--r--src/templates/qquickradiobutton.cpp11
-rw-r--r--src/templates/qquicktabbutton.cpp2
-rw-r--r--tests/auto/controls/data/tst_radiobutton.qml68
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()
+ }
}