aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates2
diff options
context:
space:
mode:
Diffstat (limited to 'src/quicktemplates2')
-rw-r--r--src/quicktemplates2/qquickcombobox.cpp226
-rw-r--r--src/quicktemplates2/qquickcombobox_p.h14
-rw-r--r--src/quicktemplates2/qquickcontainer_p_p.h2
-rw-r--r--src/quicktemplates2/qquickmenu.cpp2
-rw-r--r--src/quicktemplates2/qquickpresshandler.cpp35
-rw-r--r--src/quicktemplates2/qquickpresshandler_p_p.h2
-rw-r--r--src/quicktemplates2/qquickstackview_p_p.h1
-rw-r--r--src/quicktemplates2/qquicktextarea_p.h2
-rw-r--r--src/quicktemplates2/qquicktextfield_p.h2
-rw-r--r--src/quicktemplates2/quicktemplates2.pro1
10 files changed, 241 insertions, 46 deletions
diff --git a/src/quicktemplates2/qquickcombobox.cpp b/src/quicktemplates2/qquickcombobox.cpp
index 8dc70934..21eecfe1 100644
--- a/src/quicktemplates2/qquickcombobox.cpp
+++ b/src/quicktemplates2/qquickcombobox.cpp
@@ -43,13 +43,14 @@
#include <QtCore/qregularexpression.h>
#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qglobal.h>
#include <QtGui/qinputmethod.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtQml/qjsvalue.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/private/qlazilyallocated_p.h>
-#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <private/qqmldelegatemodel_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qquickitemview_p.h>
@@ -103,18 +104,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
@@ -183,7 +180,7 @@ class QQuickComboBoxDelegateModel : public QQmlDelegateModel
{
public:
explicit QQuickComboBoxDelegateModel(QQuickComboBox *combo);
- QString stringValue(int index, const QString &role) override;
+ QVariant variantValue(int index, const QString &role) override;
private:
QQuickComboBox *combo = nullptr;
@@ -195,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
@@ -234,6 +231,14 @@ public:
void updateEditText();
void updateCurrentText();
+ void updateCurrentValue();
+ void updateCurrentText(bool hasDelegateModelObject);
+ void updateCurrentValue(bool hasDelegateModelObject);
+ void updateCurrentTextAndValue();
+
+ bool isValidIndex(int index) const;
+ QString fastTextAt(int index) const;
+ QVariant fastValueAt(int index) const;
void acceptInput();
QString tryComplete(const QString &inputText);
@@ -277,6 +282,8 @@ public:
QString textRole;
QString currentText;
QString displayText;
+ QString valueRole;
+ QVariant currentValue;
QQuickItem *pressedItem = nullptr;
QQmlInstanceModel *delegateModel = nullptr;
QQmlComponent *delegate = nullptr;
@@ -434,10 +441,34 @@ void QQuickComboBoxPrivate::updateEditText()
q->setEditText(text);
}
+// We have these two rather than just using default arguments
+// because QObjectPrivate::connect() doesn't accept lambdas.
void QQuickComboBoxPrivate::updateCurrentText()
{
+ updateCurrentText(false);
+}
+
+void QQuickComboBoxPrivate::updateCurrentValue()
+{
+ updateCurrentValue(false);
+}
+
+void QQuickComboBoxPrivate::updateCurrentText(bool hasDelegateModelObject)
+{
Q_Q(QQuickComboBox);
- QString text = q->textAt(currentIndex);
+ QString text;
+ // If a delegate model object was passed in, it means the calling code
+ // has decided to reuse it for several function calls to speed things up.
+ // So, use the faster (private) version in that case.
+ // For other cases, we use the version that creates the delegate model object
+ // itself in order to have neater, more convenient calling code.
+ if (isValidIndex(currentIndex)) {
+ if (hasDelegateModelObject)
+ text = fastTextAt(currentIndex);
+ else
+ text = q->textAt(currentIndex);
+ }
+
if (currentText != text) {
currentText = text;
if (!hasDisplayText)
@@ -452,6 +483,58 @@ void QQuickComboBoxPrivate::updateCurrentText()
q->setEditText(currentText);
}
+void QQuickComboBoxPrivate::updateCurrentValue(bool hasDelegateModelObject)
+{
+ Q_Q(QQuickComboBox);
+ QVariant value;
+ // If a delegate model object was passed in, it means the calling code
+ // has decided to reuse it for several function calls to speed things up.
+ // So, use the faster (private) version in that case.
+ if (isValidIndex(currentIndex)) {
+ if (hasDelegateModelObject)
+ value = fastValueAt(currentIndex);
+ else
+ value = q->valueAt(currentIndex);
+ }
+ if (currentValue == value)
+ return;
+
+ currentValue = value;
+ emit q->currentValueChanged();
+}
+
+void QQuickComboBoxPrivate::updateCurrentTextAndValue()
+{
+ QObject *object = nullptr;
+ // For performance reasons, we reuse the same delegate model object: QTBUG-76029.
+ if (isValidIndex(currentIndex))
+ object = delegateModel->object(currentIndex);
+ const bool hasDelegateModelObject = object != nullptr;
+ updateCurrentText(hasDelegateModelObject);
+ updateCurrentValue(hasDelegateModelObject);
+ if (object)
+ delegateModel->release(object);
+}
+
+bool QQuickComboBoxPrivate::isValidIndex(int index) const
+{
+ return delegateModel && index >= 0 && index < delegateModel->count();
+}
+
+// For performance reasons (QTBUG-76029), both this and valueAt assume that
+// the index is valid and delegateModel->object(index) has been called.
+QString QQuickComboBoxPrivate::fastTextAt(int index) const
+{
+ const QString effectiveTextRole = textRole.isEmpty() ? QStringLiteral("modelData") : textRole;
+ return delegateModel->stringValue(index, effectiveTextRole);
+}
+
+QVariant QQuickComboBoxPrivate::fastValueAt(int index) const
+{
+ const QString effectiveValueRole = valueRole.isEmpty() ? QStringLiteral("modelData") : valueRole;
+ return delegateModel->variantValue(index, effectiveValueRole);
+}
+
void QQuickComboBoxPrivate::acceptInput()
{
Q_Q(QQuickComboBox);
@@ -499,7 +582,7 @@ void QQuickComboBoxPrivate::setCurrentIndex(int index, Activation activate)
emit q->currentIndexChanged();
if (componentComplete)
- updateCurrentText();
+ updateCurrentTextAndValue();
if (activate)
emit q->activated(index);
@@ -820,10 +903,14 @@ void QQuickComboBox::setModel(const QVariant& m)
if (d->model == model)
return;
- if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(d->model))
- QObjectPrivate::disconnect(aim, &QAbstractItemModel::dataChanged, d, &QQuickComboBoxPrivate::updateCurrentText);
- if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(model))
- QObjectPrivate::connect(aim, &QAbstractItemModel::dataChanged, d, &QQuickComboBoxPrivate::updateCurrentText);
+ if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(d->model)) {
+ QObjectPrivate::disconnect(aim, &QAbstractItemModel::dataChanged,
+ d, QOverload<>::of(&QQuickComboBoxPrivate::updateCurrentText));
+ }
+ if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(model)) {
+ QObjectPrivate::connect(aim, &QAbstractItemModel::dataChanged,
+ d, QOverload<>::of(&QQuickComboBoxPrivate::updateCurrentText));
+ }
d->model = model;
d->createDelegateModel();
@@ -1008,6 +1095,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 +1561,58 @@ 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->isValidIndex(index))
+ return QVariant();
+
+ QObject *object = d->delegateModel->object(index);
+ QVariant value;
+ if (object) {
+ value = d->fastValueAt(index);
+ 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
@@ -1455,13 +1623,13 @@ qreal QQuickComboBox::implicitIndicatorHeight() const
QString QQuickComboBox::textAt(int index) const
{
Q_D(const QQuickComboBox);
- if (!d->delegateModel || index < 0 || index >= d->delegateModel->count())
+ if (!d->isValidIndex(index))
return QString();
- QString text;
QObject *object = d->delegateModel->object(index);
+ QString text;
if (object) {
- text = d->delegateModel->stringValue(index, d->textRole.isEmpty() ? QStringLiteral("modelData") : d->textRole);
+ text = d->fastTextAt(index);
d->delegateModel->release(object);
}
return text;
@@ -1731,7 +1899,7 @@ void QQuickComboBox::componentComplete()
if (!d->hasCurrentIndex && d->currentIndex == -1)
setCurrentIndex(0);
else
- d->updateCurrentText();
+ d->updateCurrentTextAndValue();
}
}
diff --git a/src/quicktemplates2/qquickcombobox_p.h b/src/quicktemplates2/qquickcombobox_p.h
index 75e535a9..c9063d6a 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_REVISION(14) Q_INVOKABLE QVariant valueAt(int index) const;
+ Q_REVISION(14) 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/src/quicktemplates2/qquickcontainer_p_p.h b/src/quicktemplates2/qquickcontainer_p_p.h
index 16e9c9f6..1a251b50 100644
--- a/src/quicktemplates2/qquickcontainer_p_p.h
+++ b/src/quicktemplates2/qquickcontainer_p_p.h
@@ -50,7 +50,7 @@
#include <QtQuickTemplates2/private/qquickcontainer_p.h>
#include <QtQuickTemplates2/private/qquickcontrol_p_p.h>
-#include <QtQml/private/qqmlobjectmodel_p.h>
+#include <private/qqmlobjectmodel_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quicktemplates2/qquickmenu.cpp b/src/quicktemplates2/qquickmenu.cpp
index 67e2d806..91372777 100644
--- a/src/quicktemplates2/qquickmenu.cpp
+++ b/src/quicktemplates2/qquickmenu.cpp
@@ -53,7 +53,7 @@
#include <QtQml/private/qv4scopedvalue_p.h>
#include <QtQml/private/qv4variantobject_p.h>
#include <QtQml/private/qv4qobjectwrapper_p.h>
-#include <QtQml/private/qqmlobjectmodel_p.h>
+#include <private/qqmlobjectmodel_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtQuick/private/qquickitemview_p.h>
diff --git a/src/quicktemplates2/qquickpresshandler.cpp b/src/quicktemplates2/qquickpresshandler.cpp
index 8df25a82..b9018573 100644
--- a/src/quicktemplates2/qquickpresshandler.cpp
+++ b/src/quicktemplates2/qquickpresshandler.cpp
@@ -41,6 +41,8 @@
#include <QtGui/qstylehints.h>
#include <QtQuick/qquickitem.h>
#include <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuickTemplates2/private/qquicktextarea_p.h>
+#include <QtQuickTemplates2/private/qquicktextfield_p.h>
QT_BEGIN_NAMESPACE
@@ -55,11 +57,7 @@ void QQuickPressHandler::mousePressEvent(QMouseEvent *event)
timer.stop();
}
- if (pressedSignalIndex == -1)
- pressedSignalIndex = control->metaObject()->indexOfSignal("pressed(QQuickMouseEvent*)");
- Q_ASSERT(pressedSignalIndex != -1);
-
- if (QObjectPrivate::get(control)->isSignalConnected(pressedSignalIndex)) {
+ if (isSignalConnected(control, "pressed(QQuickMouseEvent*)", pressedSignalIndex)) {
QQuickMouseEvent mev;
mev.reset(pressPos.x(), pressPos.y(), event->button(), event->buttons(),
QGuiApplication::keyboardModifiers(), false/*isClick*/, false/*wasHeld*/);
@@ -82,11 +80,7 @@ void QQuickPressHandler::mouseReleaseEvent(QMouseEvent *event)
if (!longPress) {
timer.stop();
- if (releasedSignalIndex == -1)
- releasedSignalIndex = control->metaObject()->indexOfSignal("released(QQuickMouseEvent*)");
- Q_ASSERT(releasedSignalIndex != -1);
-
- if (QObjectPrivate::get(control)->isSignalConnected(releasedSignalIndex)) {
+ if (isSignalConnected(control, "released(QQuickMouseEvent*)", releasedSignalIndex)) {
QQuickMouseEvent mev;
mev.reset(pressPos.x(), pressPos.y(), event->button(), event->buttons(),
QGuiApplication::keyboardModifiers(), false/*isClick*/, false/*wasHeld*/);
@@ -104,11 +98,7 @@ void QQuickPressHandler::timerEvent(QTimerEvent *)
timer.stop();
clearDelayedMouseEvent();
- if (pressAndHoldSignalIndex == -1)
- pressAndHoldSignalIndex = control->metaObject()->indexOfSignal("pressAndHold(QQuickMouseEvent*)");
- Q_ASSERT(pressAndHoldSignalIndex != -1);
-
- longPress = QObjectPrivate::get(control)->isSignalConnected(pressAndHoldSignalIndex);
+ longPress = isSignalConnected(control, "pressAndHold(QQuickMouseEvent*)", pressAndHoldSignalIndex);
if (longPress) {
QQuickMouseEvent mev;
mev.reset(pressPos.x(), pressPos.y(), Qt::LeftButton, Qt::LeftButton,
@@ -136,4 +126,19 @@ bool QQuickPressHandler::isActive()
return !(timer.isActive() || longPress);
}
+bool QQuickPressHandler::isSignalConnected(QQuickItem *item, const char *signalName, int &signalIndex)
+{
+ if (signalIndex == -1)
+ signalIndex = item->metaObject()->indexOfSignal(signalName);
+ Q_ASSERT(signalIndex != -1);
+ const auto signalMetaMethod = item->metaObject()->method(signalIndex);
+ if (QQuickTextArea *textArea = qobject_cast<QQuickTextArea*>(item)) {
+ return textArea->isSignalConnected(signalMetaMethod);
+ } else if (QQuickTextField *textField = qobject_cast<QQuickTextField*>(item)) {
+ return textField->isSignalConnected(signalMetaMethod);
+ }
+ qFatal("Unhandled control type for signal name: %s", signalName);
+ return false;
+}
+
QT_END_NAMESPACE
diff --git a/src/quicktemplates2/qquickpresshandler_p_p.h b/src/quicktemplates2/qquickpresshandler_p_p.h
index 99ef94bd..19312cdd 100644
--- a/src/quicktemplates2/qquickpresshandler_p_p.h
+++ b/src/quicktemplates2/qquickpresshandler_p_p.h
@@ -67,6 +67,8 @@ struct QQuickPressHandler
void clearDelayedMouseEvent();
bool isActive();
+ static bool isSignalConnected(QQuickItem *item, const char *signalName, int &signalIndex);
+
QQuickItem *control = nullptr;
QBasicTimer timer;
QPointF pressPos;
diff --git a/src/quicktemplates2/qquickstackview_p_p.h b/src/quicktemplates2/qquickstackview_p_p.h
index 74ea3e7a..c20ce776 100644
--- a/src/quicktemplates2/qquickstackview_p_p.h
+++ b/src/quicktemplates2/qquickstackview_p_p.h
@@ -54,6 +54,7 @@
#include <QtQuick/private/qquickitemchangelistener_p.h>
#include <QtQml/private/qv4value_p.h>
#include <QtCore/qset.h>
+#include <QtCore/qstack.h>
QT_BEGIN_NAMESPACE
diff --git a/src/quicktemplates2/qquicktextarea_p.h b/src/quicktemplates2/qquicktextarea_p.h
index 3c38dabf..15182a84 100644
--- a/src/quicktemplates2/qquicktextarea_p.h
+++ b/src/quicktemplates2/qquicktextarea_p.h
@@ -164,6 +164,8 @@ Q_SIGNALS:
Q_REVISION(5) void bottomInsetChanged();
protected:
+ friend struct QQuickPressHandler;
+
void classBegin() override;
void componentComplete() override;
diff --git a/src/quicktemplates2/qquicktextfield_p.h b/src/quicktemplates2/qquicktextfield_p.h
index d0d25d70..ae2681d1 100644
--- a/src/quicktemplates2/qquicktextfield_p.h
+++ b/src/quicktemplates2/qquicktextfield_p.h
@@ -159,6 +159,8 @@ Q_SIGNALS:
Q_REVISION(5) void bottomInsetChanged();
protected:
+ friend struct QQuickPressHandler;
+
void classBegin() override;
void componentComplete() override;
diff --git a/src/quicktemplates2/quicktemplates2.pro b/src/quicktemplates2/quicktemplates2.pro
index 13b4f0e8..8ed0151a 100644
--- a/src/quicktemplates2/quicktemplates2.pro
+++ b/src/quicktemplates2/quicktemplates2.pro
@@ -3,6 +3,7 @@ MODULE = quicktemplates2
QT += quick
QT_PRIVATE += core-private gui-private qml-private quick-private
+qtHaveModule(qmlmodels): QT += qmlmodels-private
DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII