aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMitch Curtis <mitch.curtis@qt.io>2019-03-04 11:12:05 +0100
committerMitch Curtis <mitch.curtis@qt.io>2019-04-23 11:11:18 +0000
commitf462312365d4955fc82b247b72f84e1c77d8104d (patch)
tree0b240fda0fe60d5110b91aecc6da413f5b086b81
parent8b61881469901a793e9ac8eec7caefad547258a2 (diff)
Add valueRole API to ComboBox
This allows the user to conveniently manage data for a role associated with the text role. A common example of this is an enum stored in a backend with nicely formatted text displayed to the user. Before this patch, developers would have to write code like this: ComboBox { textRole: "text" onActivated: backend.modifier = model[currentIndex].value Component.onCompleted: currentIndex = findValue(backend.modifier) model: [ { value: Qt.NoModifier, text: qsTr("No modifier") }, { value: Qt.ShiftModifier, text: qsTr("Shift") }, { value: Qt.ControlModifier, text: qsTr("Control") } ] function findValue(value) { for (var i = 0; i < model.length; ++i) { if (model[i].value === value) return i } return -1 } } With this patch, the code becomes much simpler: ComboBox { textRole: "text" valueRole: "value" onActivated: backend.modifier = currentValue Component.onCompleted: currentIndex = indexOfValue(backend.modifier) model: [ { value: Qt.NoModifier, text: qsTr("No modifier") }, { value: Qt.ShiftModifier, text: qsTr("Shift") }, { value: Qt.ControlModifier, text: qsTr("Control") } ] } [ChangeLog][Controls][ComboBox] Added valueRole, currentValue and indexOfValue(). These allow convenient management of data for a role associated with the text role. Change-Id: I0ed19bd0ba9cf6b044a8113ff1a8782d43065449 Fixes: QTBUG-73491 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-rw-r--r--src/imports/controls/ComboBox.qml10
-rw-r--r--src/imports/controls/doc/snippets/qtquickcontrols2-combobox-valuerole.qml58
-rw-r--r--src/imports/controls/fusion/ComboBox.qml14
-rw-r--r--src/imports/controls/imagine/ComboBox.qml12
-rw-r--r--src/imports/controls/material/ComboBox.qml14
-rw-r--r--src/imports/controls/universal/ComboBox.qml12
-rw-r--r--src/imports/templates/qtquicktemplates2plugin.cpp3
-rw-r--r--src/quicktemplates2/qquickcombobox.cpp139
-rw-r--r--src/quicktemplates2/qquickcombobox_p.h14
-rw-r--r--tests/auto/controls/data/tst_combobox.qml66
10 files changed, 289 insertions, 53 deletions
diff --git a/src/imports/controls/ComboBox.qml b/src/imports/controls/ComboBox.qml
index 3bca9c02..8eefc686 100644
--- a/src/imports/controls/ComboBox.qml
+++ b/src/imports/controls/ComboBox.qml
@@ -34,11 +34,11 @@
**
****************************************************************************/
-import QtQuick 2.12
-import QtQuick.Window 2.12
-import QtQuick.Controls 2.12
-import QtQuick.Controls.impl 2.12
-import QtQuick.Templates 2.12 as T
+import QtQuick 2.14
+import QtQuick.Window 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Controls.impl 2.14
+import QtQuick.Templates 2.14 as T
T.ComboBox {
id: control
diff --git a/src/imports/controls/doc/snippets/qtquickcontrols2-combobox-valuerole.qml b/src/imports/controls/doc/snippets/qtquickcontrols2-combobox-valuerole.qml
new file mode 100644
index 00000000..4d7ae3d3
--- /dev/null
+++ b/src/imports/controls/doc/snippets/qtquickcontrols2-combobox-valuerole.qml
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.14
+import QtQuick.Controls 2.14
+
+//! [file]
+ApplicationWindow {
+ width: 640
+ height: 480
+ visible: true
+
+ // Used as an example of a backend - this would usually be
+ // e.g. a C++ type exposed to QML.
+ QtObject {
+ id: backend
+ property int modifier
+ }
+
+ ComboBox {
+ textRole: "text"
+ valueRole: "value"
+ // When an item is selected, update the backend.
+ onActivated: backend.modifier = currentValue
+ // Set the initial currentIndex to the value stored in the backend.
+ Component.onCompleted: currentIndex = indexOfValue(backend.modifier)
+ model: [
+ { value: Qt.NoModifier, text: qsTr("No modifier") },
+ { value: Qt.ShiftModifier, text: qsTr("Shift") },
+ { value: Qt.ControlModifier, text: qsTr("Control") }
+ ]
+ }
+}
+//! [file]
diff --git a/src/imports/controls/fusion/ComboBox.qml b/src/imports/controls/fusion/ComboBox.qml
index 3ecb0cf2..58764809 100644
--- a/src/imports/controls/fusion/ComboBox.qml
+++ b/src/imports/controls/fusion/ComboBox.qml
@@ -34,13 +34,13 @@
**
****************************************************************************/
-import QtQuick 2.12
-import QtQuick.Window 2.12
-import QtQuick.Templates 2.12 as T
-import QtQuick.Controls 2.12
-import QtQuick.Controls.impl 2.12
-import QtQuick.Controls.Fusion 2.12
-import QtQuick.Controls.Fusion.impl 2.12
+import QtQuick 2.14
+import QtQuick.Window 2.14
+import QtQuick.Templates 2.14 as T
+import QtQuick.Controls 2.14
+import QtQuick.Controls.impl 2.14
+import QtQuick.Controls.Fusion 2.14
+import QtQuick.Controls.Fusion.impl 2.14
T.ComboBox {
id: control
diff --git a/src/imports/controls/imagine/ComboBox.qml b/src/imports/controls/imagine/ComboBox.qml
index 3a3ae682..2d582e98 100644
--- a/src/imports/controls/imagine/ComboBox.qml
+++ b/src/imports/controls/imagine/ComboBox.qml
@@ -34,12 +34,12 @@
**
****************************************************************************/
-import QtQuick 2.12
-import QtQuick.Window 2.12
-import QtQuick.Templates 2.12 as T
-import QtQuick.Controls 2.12
-import QtQuick.Controls.Imagine 2.12
-import QtQuick.Controls.Imagine.impl 2.12
+import QtQuick 2.14
+import QtQuick.Window 2.14
+import QtQuick.Templates 2.14 as T
+import QtQuick.Controls 2.14
+import QtQuick.Controls.Imagine 2.14
+import QtQuick.Controls.Imagine.impl 2.14
T.ComboBox {
id: control
diff --git a/src/imports/controls/material/ComboBox.qml b/src/imports/controls/material/ComboBox.qml
index 223f8fca..7d635902 100644
--- a/src/imports/controls/material/ComboBox.qml
+++ b/src/imports/controls/material/ComboBox.qml
@@ -34,13 +34,13 @@
**
****************************************************************************/
-import QtQuick 2.12
-import QtQuick.Window 2.12
-import QtQuick.Controls 2.12
-import QtQuick.Controls.impl 2.12
-import QtQuick.Templates 2.12 as T
-import QtQuick.Controls.Material 2.12
-import QtQuick.Controls.Material.impl 2.12
+import QtQuick 2.14
+import QtQuick.Window 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Controls.impl 2.14
+import QtQuick.Templates 2.14 as T
+import QtQuick.Controls.Material 2.14
+import QtQuick.Controls.Material.impl 2.14
T.ComboBox {
id: control
diff --git a/src/imports/controls/universal/ComboBox.qml b/src/imports/controls/universal/ComboBox.qml
index 3ec7e98b..9a4e119b 100644
--- a/src/imports/controls/universal/ComboBox.qml
+++ b/src/imports/controls/universal/ComboBox.qml
@@ -34,12 +34,12 @@
**
****************************************************************************/
-import QtQuick 2.12
-import QtQuick.Window 2.12
-import QtQuick.Controls 2.12
-import QtQuick.Controls.impl 2.12
-import QtQuick.Templates 2.12 as T
-import QtQuick.Controls.Universal 2.12
+import QtQuick 2.14
+import QtQuick.Window 2.14
+import QtQuick.Controls 2.14
+import QtQuick.Controls.impl 2.14
+import QtQuick.Templates 2.14 as T
+import QtQuick.Controls.Universal 2.14
T.ComboBox {
id: control
diff --git a/src/imports/templates/qtquicktemplates2plugin.cpp b/src/imports/templates/qtquicktemplates2plugin.cpp
index 10f9b8dd..008293a2 100644
--- a/src/imports/templates/qtquicktemplates2plugin.cpp
+++ b/src/imports/templates/qtquicktemplates2plugin.cpp
@@ -355,6 +355,9 @@ void QtQuickTemplates2Plugin::registerTypes(const char *uri)
qmlRegisterUncreatableType<QQuickSplitHandleAttached>(uri, 2, 13, "SplitHandle",
QStringLiteral("SplitHandle is only available as an attached property."));
qmlRegisterType<QQuickSplitHandleAttached>();
+
+ // QtQuick.Templates 2.14 (new types and revisions in Qt 5.14)
+ qmlRegisterType<QQuickComboBox, 14>(uri, 2, 14, "ComboBox");
}
QT_END_NAMESPACE
diff --git a/src/quicktemplates2/qquickcombobox.cpp b/src/quicktemplates2/qquickcombobox.cpp
index 0ffd76ab..9286da81 100644
--- a/src/quicktemplates2/qquickcombobox.cpp
+++ b/src/quicktemplates2/qquickcombobox.cpp
@@ -117,18 +117,14 @@ QT_BEGIN_NAMESPACE
When using models that have multiple named roles, ComboBox must be configured
to use a specific \l {textRole}{text role} for its \l {displayText}{display text}
- and \l delegate instances.
+ and \l delegate instances. If you want to use a role of the model item
+ that corresponds to the text role, set \l valueRole. The \l currentValue
+ property and \l indexOfValue() method can then be used to get information
+ about those values.
- \code
- ComboBox {
- textRole: "key"
- model: ListModel {
- ListElement { key: "First"; value: 123 }
- ListElement { key: "Second"; value: 456 }
- ListElement { key: "Third"; value: 789 }
- }
- }
- \endcode
+ For example:
+
+ \snippet qtquickcontrols2-combobox-valuerole.qml file
\note If ComboBox is assigned a data model that has multiple named roles, but
\l textRole is not defined, ComboBox is unable to visualize it and throws a
@@ -184,7 +180,7 @@ class QQuickComboBoxDelegateModel : public QQmlDelegateModel
{
public:
explicit QQuickComboBoxDelegateModel(QQuickComboBox *combo);
- QString stringValue(int index, const QString &role);
+ QVariant variantValue(int index, const QString &role) override;
private:
QQuickComboBox *combo = nullptr;
@@ -196,23 +192,23 @@ QQuickComboBoxDelegateModel::QQuickComboBoxDelegateModel(QQuickComboBox *combo)
{
}
-QString QQuickComboBoxDelegateModel::stringValue(int index, const QString &role)
+QVariant QQuickComboBoxDelegateModel::variantValue(int index, const QString &role)
{
- QVariant model = combo->model();
+ const QVariant model = combo->model();
if (model.userType() == QMetaType::QVariantList) {
QVariant object = model.toList().value(index);
if (object.userType() == QMetaType::QVariantMap) {
const QVariantMap data = object.toMap();
if (data.count() == 1 && role == QLatin1String("modelData"))
- return data.first().toString();
- return data.value(role).toString();
+ return data.first();
+ return data.value(role);
} else if (object.userType() == QMetaType::QObjectStar) {
const QObject *data = object.value<QObject *>();
if (data && role != QLatin1String("modelData"))
- return data->property(role.toUtf8()).toString();
+ return data->property(role.toUtf8());
}
}
- return QQmlDelegateModel::stringValue(index, role);
+ return QQmlDelegateModel::variantValue(index, role);
}
class QQuickComboBoxPrivate : public QQuickControlPrivate
@@ -235,6 +231,7 @@ public:
void updateEditText();
void updateCurrentText();
+ void updateCurrentValue();
void acceptInput();
QString tryComplete(const QString &inputText);
@@ -278,6 +275,8 @@ public:
QString textRole;
QString currentText;
QString displayText;
+ QString valueRole;
+ QVariant currentValue;
QQuickItem *pressedItem = nullptr;
QQmlInstanceModel *delegateModel = nullptr;
QQmlComponent *delegate = nullptr;
@@ -453,6 +452,17 @@ void QQuickComboBoxPrivate::updateCurrentText()
q->setEditText(currentText);
}
+void QQuickComboBoxPrivate::updateCurrentValue()
+{
+ Q_Q(QQuickComboBox);
+ const QVariant value = q->valueAt(currentIndex);
+ if (currentValue == value)
+ return;
+
+ currentValue = value;
+ emit q->currentValueChanged();
+}
+
void QQuickComboBoxPrivate::acceptInput()
{
Q_Q(QQuickComboBox);
@@ -499,8 +509,10 @@ void QQuickComboBoxPrivate::setCurrentIndex(int index, Activation activate)
currentIndex = index;
emit q->currentIndexChanged();
- if (componentComplete)
+ if (componentComplete) {
updateCurrentText();
+ updateCurrentValue();
+ }
if (activate)
emit q->activated(index);
@@ -1008,6 +1020,35 @@ void QQuickComboBox::setTextRole(const QString &role)
}
/*!
+ \since QtQuick.Controls 2.14 (Qt 5.14)
+ \qmlproperty string QtQuick.Controls::ComboBox::valueRole
+
+ This property holds the model role used for storing the value associated
+ with each item in the model.
+
+ For an example of how to use this property, see \l {ComboBox Model Roles}.
+
+ \sa model, currentValue
+*/
+QString QQuickComboBox::valueRole() const
+{
+ Q_D(const QQuickComboBox);
+ return d->valueRole;
+}
+
+void QQuickComboBox::setValueRole(const QString &role)
+{
+ Q_D(QQuickComboBox);
+ if (d->valueRole == role)
+ return;
+
+ d->valueRole = role;
+ if (isComponentComplete())
+ d->updateCurrentValue();
+ emit valueRoleChanged();
+}
+
+/*!
\qmlproperty Component QtQuick.Controls::ComboBox::delegate
This property holds a delegate that presents an item in the combo box popup.
@@ -1445,6 +1486,60 @@ qreal QQuickComboBox::implicitIndicatorHeight() const
}
/*!
+ \readonly
+ \since QtQuick.Controls 2.14 (Qt 5.14)
+ \qmlproperty string QtQuick.Controls::ComboBox::currentValue
+
+ This property holds the value of the current item in the combo box.
+
+ For an example of how to use this property, see \l {ComboBox Model Roles}.
+
+ \sa currentIndex, currentText, valueRole
+*/
+QVariant QQuickComboBox::currentValue() const
+{
+ Q_D(const QQuickComboBox);
+ return d->currentValue;
+}
+
+QVariant QQuickComboBox::valueAt(int index) const
+{
+ Q_D(const QQuickComboBox);
+ if (!d->delegateModel || index < 0 || index >= d->delegateModel->count())
+ return QVariant();
+
+ // We use QVariant because the model API uses QVariant.
+ QVariant value;
+ QObject *object = d->delegateModel->object(index);
+ if (object) {
+ const QString role = d->valueRole.isEmpty() ? QStringLiteral("modelData") : d->valueRole;
+ value = d->delegateModel->variantValue(index, role);
+ d->delegateModel->release(object);
+ }
+ return value;
+}
+
+/*!
+ \since QtQuick.Controls 2.14 (Qt 5.14)
+ \qmlmethod int QtQuick.Controls::ComboBox::indexOfValue(object value)
+
+ Returns the index of the specified \a value, or \c -1 if no match is found.
+
+ For an example of how to use this method, see \l {ComboBox Model Roles}.
+
+ \sa find(), currentValue, currentIndex, valueRole
+*/
+int QQuickComboBox::indexOfValue(const QVariant &value) const
+{
+ for (int i = 0; i < count(); ++i) {
+ const QVariant ourValue = valueAt(i);
+ if (value == ourValue)
+ return i;
+ }
+ return -1;
+}
+
+/*!
\qmlmethod string QtQuick.Controls::ComboBox::textAt(int index)
Returns the text for the specified \a index, or an empty string
@@ -1728,10 +1823,12 @@ void QQuickComboBox::componentComplete()
static_cast<QQmlDelegateModel *>(d->delegateModel)->componentComplete();
if (count() > 0) {
- if (!d->hasCurrentIndex && d->currentIndex == -1)
+ if (!d->hasCurrentIndex && d->currentIndex == -1) {
setCurrentIndex(0);
- else
+ } else {
d->updateCurrentText();
+ d->updateCurrentValue();
+ }
}
}
diff --git a/src/quicktemplates2/qquickcombobox_p.h b/src/quicktemplates2/qquickcombobox_p.h
index 75e535a9..a55541d4 100644
--- a/src/quicktemplates2/qquickcombobox_p.h
+++ b/src/quicktemplates2/qquickcombobox_p.h
@@ -86,6 +86,9 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickComboBox : public QQuickControl
Q_PROPERTY(qreal implicitIndicatorWidth READ implicitIndicatorWidth NOTIFY implicitIndicatorWidthChanged FINAL REVISION 5)
Q_PROPERTY(qreal implicitIndicatorHeight READ implicitIndicatorHeight NOTIFY implicitIndicatorHeightChanged FINAL REVISION 5)
Q_CLASSINFO("DeferredPropertyNames", "background,contentItem,indicator,popup")
+ // 2.14 (Qt 5.14)
+ Q_PROPERTY(QVariant currentValue READ currentValue NOTIFY currentValueChanged FINAL REVISION 14)
+ Q_PROPERTY(QString valueRole READ valueRole WRITE setValueRole NOTIFY valueRoleChanged FINAL REVISION 14)
public:
explicit QQuickComboBox(QQuickItem *parent = nullptr);
@@ -114,6 +117,9 @@ public:
QString textRole() const;
void setTextRole(const QString &role);
+ QString valueRole() const;
+ void setValueRole(const QString &role);
+
QQmlComponent *delegate() const;
void setDelegate(QQmlComponent *delegate);
@@ -155,6 +161,11 @@ public:
qreal implicitIndicatorWidth() const;
qreal implicitIndicatorHeight() const;
+ // 2.14 (Qt 5.14)
+ QVariant currentValue() const;
+ Q_INVOKABLE QVariant valueAt(int index) const;
+ Q_INVOKABLE int indexOfValue(const QVariant &value) const;
+
public Q_SLOTS:
void incrementCurrentIndex();
void decrementCurrentIndex();
@@ -189,6 +200,9 @@ Q_SIGNALS:
// 2.5 (Qt 5.12)
Q_REVISION(5) void implicitIndicatorWidthChanged();
Q_REVISION(5) void implicitIndicatorHeightChanged();
+ // 2.14 (Qt 5.14)
+ Q_REVISION(14) void valueRoleChanged();
+ Q_REVISION(14) void currentValueChanged();
protected:
bool eventFilter(QObject *object, QEvent *event) override;
diff --git a/tests/auto/controls/data/tst_combobox.qml b/tests/auto/controls/data/tst_combobox.qml
index 7f061a2b..7d65b698 100644
--- a/tests/auto/controls/data/tst_combobox.qml
+++ b/tests/auto/controls/data/tst_combobox.qml
@@ -351,9 +351,73 @@ TestCase {
compare(control.find(data.term, data.flags), data.index)
}
+ function test_valueRole_data() {
+ return [
+ { tag: "ListModel", model: fruitmodel },
+ { tag: "ObjectArray", model: fruitarray }
+ ]
+ }
+
+ function test_valueRole(data) {
+ var control = createTemporaryObject(emptyBox, testCase,
+ { model: data.model, valueRole: "color" })
+ verify(control)
+ compare(control.count, 3)
+ compare(control.currentIndex, 0)
+ compare(control.currentValue, "red")
+
+ control.valueRole = "name"
+ compare(control.currentValue, "Apple")
+
+ control.currentIndex = 1
+ compare(control.currentIndex, 1)
+ compare(control.currentValue, "Orange")
+
+ control.valueRole = "color"
+ compare(control.currentValue, "orange")
+
+ control.model = null
+ compare(control.currentIndex, -1)
+ // An invalid QVariant is represented as undefined.
+ compare(control.currentValue, undefined)
+
+ control.valueRole = ""
+ compare(control.currentValue, undefined)
+ }
+
+ function test_valueAt() {
+ var control = createTemporaryObject(comboBox, testCase,
+ { model: fruitmodel, textRole: "name", valueRole: "color" })
+ verify(control)
+
+ compare(control.valueAt(0), "red")
+ compare(control.valueAt(1), "orange")
+ compare(control.valueAt(2), "yellow")
+ compare(control.valueAt(-1), undefined)
+ compare(control.valueAt(5), undefined)
+ }
+
+ function test_indexOfValue_data() {
+ return [
+ { tag: "red", expectedIndex: 0 },
+ { tag: "orange", expectedIndex: 1 },
+ { tag: "yellow", expectedIndex: 2 },
+ { tag: "brown", expectedIndex: -1 },
+ ]
+ }
+
+ function test_indexOfValue(data) {
+ var control = createTemporaryObject(comboBox, testCase,
+ { model: fruitmodel, textRole: "name", valueRole: "color" })
+ verify(control)
+
+ compare(control.indexOfValue(data.tag), data.expectedIndex)
+ }
+
function test_arrowKeys() {
- var control = createTemporaryObject(comboBox, testCase, {model: 3})
+ var control = createTemporaryObject(comboBox, testCase,
+ { model: fruitmodel, textRole: "name", valueRole: "color" })
verify(control)
var activatedSpy = signalSpy.createObject(control, {target: control, signalName: "activated"})