diff options
-rw-r--r-- | src/imports/controls/CheckBox.qml | 1 | ||||
-rw-r--r-- | src/templates/qquickabstractbutton.cpp | 20 | ||||
-rw-r--r-- | src/templates/qquickabstractbutton_p.h | 3 | ||||
-rw-r--r-- | src/templates/qquickcheckbox.cpp | 83 | ||||
-rw-r--r-- | src/templates/qquickcheckbox_p.h | 22 | ||||
-rw-r--r-- | tests/auto/controls/data/tst_checkbox.qml | 231 |
6 files changed, 333 insertions, 27 deletions
diff --git a/src/imports/controls/CheckBox.qml b/src/imports/controls/CheckBox.qml index a24e73ae..565187a4 100644 --- a/src/imports/controls/CheckBox.qml +++ b/src/imports/controls/CheckBox.qml @@ -69,6 +69,7 @@ T.CheckBox { y: (parent.height - height) / 2 width: 12 height: 12 + opacity: control.tristate && control.checkState === Qt.PartiallyChecked ? 0.5 : 1.0 color: Qt.tint(control.checked && !control.enabled ? control.Theme.disabledColor : control.checked && control.activeFocus ? control.Theme.focusColor : control.checked ? control.Theme.accentColor : control.Theme.baseColor, diff --git a/src/templates/qquickabstractbutton.cpp b/src/templates/qquickabstractbutton.cpp index 8f3e8af8..ec3bff83 100644 --- a/src/templates/qquickabstractbutton.cpp +++ b/src/templates/qquickabstractbutton.cpp @@ -180,6 +180,7 @@ void QQuickAbstractButton::setChecked(bool checked) if (d->checked != checked) { d->checked = checked; setAccessibleProperty("checked", checked); + checkStateSet(); emit checkedChanged(); } } @@ -303,7 +304,6 @@ void QQuickAbstractButton::keyPressEvent(QKeyEvent *event) void QQuickAbstractButton::keyReleaseEvent(QKeyEvent *event) { - Q_D(QQuickAbstractButton); QQuickControl::keyReleaseEvent(event); if (event->key() == Qt::Key_Space) { setPressed(false); @@ -312,8 +312,7 @@ void QQuickAbstractButton::keyReleaseEvent(QKeyEvent *event) emit released(&mre); QQuickMouseEvent mce(width() / 2, height() / 2, Qt::NoButton, Qt::NoButton, event->modifiers(), true /* isClick */); emit clicked(&mce); - if (d->checkable) - setChecked(d->exclusive || !d->checked); + nextCheckState(); event->setAccepted(mre.isAccepted() || mce.isAccepted()); } } @@ -350,8 +349,8 @@ void QQuickAbstractButton::mouseReleaseEvent(QMouseEvent *event) } else { emit canceled(); } - if (d->checkable && contains(event->pos())) - setChecked(d->exclusive || !d->checked); + if (contains(event->pos())) + nextCheckState(); } void QQuickAbstractButton::mouseDoubleClickEvent(QMouseEvent *event) @@ -373,4 +372,15 @@ void QQuickAbstractButton::mouseUngrabEvent() } } +void QQuickAbstractButton::checkStateSet() +{ +} + +void QQuickAbstractButton::nextCheckState() +{ + Q_D(QQuickAbstractButton); + if (d->checkable) + setChecked(d->exclusive || !d->checked); +} + QT_END_NAMESPACE diff --git a/src/templates/qquickabstractbutton_p.h b/src/templates/qquickabstractbutton_p.h index 80d8be4f..949117ae 100644 --- a/src/templates/qquickabstractbutton_p.h +++ b/src/templates/qquickabstractbutton_p.h @@ -117,6 +117,9 @@ protected: void mouseDoubleClickEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mouseUngrabEvent() Q_DECL_OVERRIDE; + virtual void checkStateSet(); + virtual void nextCheckState(); + private: Q_DISABLE_COPY(QQuickAbstractButton) Q_DECLARE_PRIVATE(QQuickAbstractButton) diff --git a/src/templates/qquickcheckbox.cpp b/src/templates/qquickcheckbox.cpp index 1ced357a..2f70be3e 100644 --- a/src/templates/qquickcheckbox.cpp +++ b/src/templates/qquickcheckbox.cpp @@ -35,6 +35,7 @@ ****************************************************************************/ #include "qquickcheckbox_p.h" +#include "qquickabstractbutton_p_p.h" QT_BEGIN_NAMESPACE @@ -80,11 +81,91 @@ QT_BEGIN_NAMESPACE \sa {Customizing CheckBox} */ +class QQuickCheckBoxPrivate : public QQuickAbstractButtonPrivate +{ + Q_DECLARE_PUBLIC(QQuickCheckBox) + +public: + QQuickCheckBoxPrivate() : tristate(false), checkState(Qt::Unchecked) { } + + bool tristate; + Qt::CheckState checkState; +}; + QQuickCheckBox::QQuickCheckBox(QQuickItem *parent) : - QQuickAbstractButton(parent) + QQuickAbstractButton(*(new QQuickCheckBoxPrivate), parent) { setCheckable(true); setAccessibleRole(0x0000002C); //QAccessible::CheckBox } +/*! + \qmlproperty bool Qt.labs.controls::CheckBox::tristate + + This property holds whether the checkbox is a tri-state checkbox. + + The default is \c false, i.e., the checkbox has only two states. +*/ +bool QQuickCheckBox::isTristate() const +{ + Q_D(const QQuickCheckBox); + return d->tristate; +} + +void QQuickCheckBox::setTristate(bool tristate) +{ + Q_D(QQuickCheckBox); + if (d->tristate != tristate) { + d->tristate = tristate; + emit tristateChanged(); + } +} + +/*! + \qmlproperty enumeration Qt.labs.controls::CheckBox::checkState + + This property holds the check state of the checkbox. + + Available states: + \value Qt.Unchecked The checkbox is unchecked. + \value Qt.PartiallyChecked The checkbox is partially checked. This state is only used when \l tristate is enabled. + \value Qt.Checked The checkbox is checked. + + \sa tristate, {AbstractButton::checked}{checked} +*/ +Qt::CheckState QQuickCheckBox::checkState() const +{ + Q_D(const QQuickCheckBox); + return d->checkState; +} + +void QQuickCheckBox::setCheckState(Qt::CheckState state) +{ + Q_D(QQuickCheckBox); + if (!d->tristate && state == Qt::PartiallyChecked) + setTristate(true); + if (d->checkState != state) { + bool wasChecked = isChecked(); + d->checked = state != Qt::Unchecked; + d->checkState = state; + emit checkStateChanged(); + if (d->checked != wasChecked) + emit checkedChanged(); + } +} + +void QQuickCheckBox::checkStateSet() +{ + setCheckState(isChecked() ? Qt::Checked : Qt::Unchecked); +} + +void QQuickCheckBox::nextCheckState() +{ + Q_D(QQuickCheckBox); + if (d->tristate) + setCheckState(static_cast<Qt::CheckState>((d->checkState + 1) % 3)); + else + QQuickAbstractButton::nextCheckState(); +} + QT_END_NAMESPACE diff --git a/src/templates/qquickcheckbox_p.h b/src/templates/qquickcheckbox_p.h index c0ff4c44..b2f98e92 100644 --- a/src/templates/qquickcheckbox_p.h +++ b/src/templates/qquickcheckbox_p.h @@ -52,12 +52,34 @@ QT_BEGIN_NAMESPACE +class QQuickCheckBoxPrivate; + class Q_LABSTEMPLATES_EXPORT QQuickCheckBox : public QQuickAbstractButton { Q_OBJECT + Q_PROPERTY(bool tristate READ isTristate WRITE setTristate NOTIFY tristateChanged FINAL) + Q_PROPERTY(Qt::CheckState checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL) public: explicit QQuickCheckBox(QQuickItem *parent = Q_NULLPTR); + + bool isTristate() const; + void setTristate(bool tristate); + + Qt::CheckState checkState() const; + void setCheckState(Qt::CheckState state); + +Q_SIGNALS: + void tristateChanged(); + void checkStateChanged(); + +protected: + void checkStateSet() Q_DECL_OVERRIDE; + void nextCheckState() Q_DECL_OVERRIDE; + +private: + Q_DISABLE_COPY(QQuickCheckBox) + Q_DECLARE_PRIVATE(QQuickCheckBox) }; Q_DECLARE_TYPEINFO(QQuickCheckBox, Q_COMPLEX_TYPE); diff --git a/tests/auto/controls/data/tst_checkbox.qml b/tests/auto/controls/data/tst_checkbox.qml index caf6658b..c61cab38 100644 --- a/tests/auto/controls/data/tst_checkbox.qml +++ b/tests/auto/controls/data/tst_checkbox.qml @@ -57,7 +57,7 @@ TestCase { property ControlSpy spy: ControlSpy { target: control - signals: ["pressed", "released", "canceled", "clicked", "pressedChanged", "checkedChanged"] + signals: ["pressed", "released", "canceled", "clicked", "pressedChanged", "checkedChanged", "checkStateChanged"] } } } @@ -81,16 +81,47 @@ TestCase { control.spy.expectedSequence = [] compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) verify(control.spy.success) - control.spy.expectedSequence = [["checkedChanged", { "checked": true }]] + control.spy.expectedSequence = [["checkStateChanged", { "checked": true, "checkState": Qt.Checked }], + ["checkedChanged", { "checked": true, "checkState": Qt.Checked }]] control.checked = true compare(control.checked, true) + compare(control.checkState, Qt.Checked) verify(control.spy.success) - control.spy.expectedSequence = [["checkedChanged", { "checked": false }]] + control.spy.expectedSequence = [["checkStateChanged", { "checked": false, "checkState": Qt.Unchecked }], + ["checkedChanged", { "checked": false, "checkState": Qt.Unchecked }]] control.checked = false compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) + verify(control.spy.success) + + control.destroy() + } + + function test_checkState() { + var control = checkBox.createObject(testCase) + verify(control) + + control.spy.expectedSequence = [] + compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) + verify(control.spy.success) + + control.spy.expectedSequence = [["checkStateChanged", { "checked": true, "checkState": Qt.Checked }], + ["checkedChanged", { "checked": true, "checkState": Qt.Checked }]] + control.checkState = Qt.Checked + compare(control.checked, true) + compare(control.checkState, Qt.Checked) + verify(control.spy.success) + + control.spy.expectedSequence = [["checkStateChanged", { "checked": false, "checkState": Qt.Unchecked }], + ["checkedChanged", { "checked": false, "checkState": Qt.Unchecked }]] + control.checkState = Qt.Unchecked + compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) verify(control.spy.success) control.destroy() @@ -101,49 +132,54 @@ TestCase { verify(control) // check - control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false, "checkState": Qt.Unchecked }], "pressed"] mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) compare(control.pressed, true) verify(control.spy.success) - control.spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }], + control.spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }], "released", "clicked", - ["checkedChanged", { "pressed": false, "checked": true }]] + ["checkStateChanged", { "pressed": false, "checked": true, "checkState": Qt.Checked }], + ["checkedChanged", { "pressed": false, "checked": true, "checkState": Qt.Checked }]] mouseRelease(control, control.width / 2, control.height / 2, Qt.LeftButton) compare(control.checked, true) + compare(control.checkState, Qt.Checked) compare(control.pressed, false) verify(control.spy.success) // uncheck - control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }], + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true, "checkState": Qt.Checked }], "pressed"] mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) compare(control.pressed, true) verify(control.spy.success) - control.spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true }], + control.spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": true, "checkState": Qt.Checked }], "released", "clicked", - ["checkedChanged", { "pressed": false, "checked": false }]] + ["checkStateChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }], + ["checkedChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }]] mouseRelease(control, control.width / 2, control.height / 2, Qt.LeftButton) compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) compare(control.pressed, false) verify(control.spy.success) // release outside - control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false, "checkState": Qt.Unchecked }], "pressed"] mousePress(control, control.width / 2, control.height / 2, Qt.LeftButton) compare(control.pressed, true) verify(control.spy.success) - control.spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false }]] + control.spy.expectedSequence = [["pressedChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }]] mouseMove(control, control.width * 2, control.height * 2, 0, Qt.LeftButton) compare(control.pressed, false) verify(control.spy.success) - control.spy.expectedSequence = [["canceled", { "pressed": false, "checked": false }]] + control.spy.expectedSequence = [["canceled", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }]] mouseRelease(control, control.width * 2, control.height * 2, Qt.LeftButton) compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) compare(control.pressed, false) verify(control.spy.success) @@ -153,6 +189,7 @@ TestCase { compare(control.pressed, false) mouseRelease(control, control.width / 2, control.height / 2, Qt.RightButton) compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) compare(control.pressed, false) verify(control.spy.success) @@ -169,25 +206,29 @@ TestCase { verify(control.spy.success) // check - control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false }], + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false, "checkState": Qt.Unchecked }], "pressed", - ["pressedChanged", { "pressed": false, "checked": false }], + ["pressedChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }], "released", "clicked", - ["checkedChanged", { "pressed": false, "checked": true }]] + ["checkStateChanged", { "pressed": false, "checked": true, "checkState": Qt.Checked }], + ["checkedChanged", { "pressed": false, "checked": true, "checkState": Qt.Checked }]] keyClick(Qt.Key_Space) compare(control.checked, true) + compare(control.checkState, Qt.Checked) verify(control.spy.success) // uncheck - control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true }], + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true, "checkState": Qt.Checked }], "pressed", - ["pressedChanged", { "pressed": false, "checked": true }], + ["pressedChanged", { "pressed": false, "checked": true, "checkState": Qt.Checked }], "released", "clicked", - ["checkedChanged", { "pressed": false, "checked": false }]] + ["checkStateChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }], + ["checkedChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }]] keyClick(Qt.Key_Space) compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) verify(control.spy.success) // no change @@ -204,28 +245,176 @@ TestCase { } Component { - id: twoCheckBoxes + id: checkedBoundBoxes Item { property CheckBox cb1: CheckBox { id: cb1 } property CheckBox cb2: CheckBox { id: cb2; checked: cb1.checked; enabled: false } } } - function test_binding() { - var container = twoCheckBoxes.createObject(testCase) + function test_checked_binding() { + var container = checkedBoundBoxes.createObject(testCase) verify(container) compare(container.cb1.checked, false) + compare(container.cb1.checkState, Qt.Unchecked) compare(container.cb2.checked, false) + compare(container.cb2.checkState, Qt.Unchecked) container.cb1.checked = true compare(container.cb1.checked, true) + compare(container.cb1.checkState, Qt.Checked) compare(container.cb2.checked, true) + compare(container.cb2.checkState, Qt.Checked) container.cb1.checked = false compare(container.cb1.checked, false) + compare(container.cb1.checkState, Qt.Unchecked) compare(container.cb2.checked, false) + compare(container.cb2.checkState, Qt.Unchecked) container.destroy() } + + Component { + id: checkStateBoundBoxes + Item { + property CheckBox cb1: CheckBox { id: cb1 } + property CheckBox cb2: CheckBox { id: cb2; checkState: cb1.checkState; enabled: false } + } + } + + function test_checkState_binding() { + var container = checkStateBoundBoxes.createObject(testCase) + verify(container) + + compare(container.cb1.checked, false) + compare(container.cb1.checkState, Qt.Unchecked) + compare(container.cb2.checked, false) + compare(container.cb2.checkState, Qt.Unchecked) + + container.cb1.checkState = Qt.Checked + compare(container.cb1.checked, true) + compare(container.cb1.checkState, Qt.Checked) + compare(container.cb2.checked, true) + compare(container.cb2.checkState, Qt.Checked) + + container.cb1.checkState = Qt.Unchecked + compare(container.cb1.checked, false) + compare(container.cb1.checkState, Qt.Unchecked) + compare(container.cb2.checked, false) + compare(container.cb2.checkState, Qt.Unchecked) + + compare(container.cb1.tristate, false) + compare(container.cb2.tristate, false) + + container.cb1.checkState = Qt.PartiallyChecked + compare(container.cb1.checked, true) + compare(container.cb1.checkState, Qt.PartiallyChecked) + compare(container.cb2.checked, true) + compare(container.cb2.checkState, Qt.PartiallyChecked) + + compare(container.cb1.tristate, true) + compare(container.cb2.tristate, true) + + container.destroy() + } + + function test_tristate() { + var control = checkBox.createObject(testCase) + + control.spy.expectedSequence = [] + control.forceActiveFocus() + verify(control.activeFocus) + + compare(control.tristate, false) + compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) + + control.spy.expectedSequence = [["checkStateChanged", { "pressed": false, "checked": true, "checkState": Qt.PartiallyChecked }], + ["checkedChanged", { "pressed": false, "checked": true, "checkState": Qt.PartiallyChecked }]] + control.checkState = Qt.PartiallyChecked + compare(control.tristate, true) + compare(control.checked, true) + compare(control.checkState, Qt.PartiallyChecked) + verify(control.spy.success) + + // key: partial -> checked + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true, "checkState": Qt.PartiallyChecked }], + "pressed", + ["pressedChanged", { "pressed": false, "checked": true, "checkState": Qt.PartiallyChecked }], + "released", + "clicked", + ["checkStateChanged", { "pressed": false, "checked": true, "checkState": Qt.Checked }]] + keyClick(Qt.Key_Space) + compare(control.checked, true) + compare(control.checkState, Qt.Checked) + verify(control.spy.success) + + // key: checked -> unchecked + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true, "checkState": Qt.Checked }], + "pressed", + ["pressedChanged", { "pressed": false, "checked": true, "checkState": Qt.Checked }], + "released", + "clicked", + ["checkStateChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }], + ["checkedChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }]] + keyClick(Qt.Key_Space) + compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) + verify(control.spy.success) + + // key: unchecked -> partial + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false, "checkState": Qt.Unchecked }], + "pressed", + ["pressedChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }], + "released", + "clicked", + ["checkStateChanged", { "pressed": false, "checked": true, "checkState": Qt.PartiallyChecked }], + ["checkedChanged", { "pressed": false, "checked": true, "checkState": Qt.PartiallyChecked }]] + keyClick(Qt.Key_Space) + compare(control.checked, true) + compare(control.checkState, Qt.PartiallyChecked) + verify(control.spy.success) + + // mouse: partial -> checked + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true, "checkState": Qt.PartiallyChecked }], + "pressed", + ["pressedChanged", { "pressed": false, "checked": true, "checkState": Qt.PartiallyChecked }], + "released", + "clicked", + ["checkStateChanged", { "pressed": false, "checked": true, "checkState": Qt.Checked }]] + mouseClick(control) + compare(control.checked, true) + compare(control.checkState, Qt.Checked) + verify(control.spy.success) + + // mouse: checked -> unchecked + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": true, "checkState": Qt.Checked }], + "pressed", + ["pressedChanged", { "pressed": false, "checked": true, "checkState": Qt.Checked }], + "released", + "clicked", + ["checkStateChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }], + ["checkedChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }]] + mouseClick(control) + compare(control.checked, false) + compare(control.checkState, Qt.Unchecked) + verify(control.spy.success) + + // mouse: unchecked -> partial + control.spy.expectedSequence = [["pressedChanged", { "pressed": true, "checked": false, "checkState": Qt.Unchecked }], + "pressed", + ["pressedChanged", { "pressed": false, "checked": false, "checkState": Qt.Unchecked }], + "released", + "clicked", + ["checkStateChanged", { "pressed": false, "checked": true, "checkState": Qt.PartiallyChecked }], + ["checkedChanged", { "pressed": false, "checked": true, "checkState": Qt.PartiallyChecked }]] + mouseClick(control) + compare(control.checked, true) + compare(control.checkState, Qt.PartiallyChecked) + verify(control.spy.success) + + control.destroy() + } } |