aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates2/qquickcombobox.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quicktemplates2/qquickcombobox.cpp')
-rw-r--r--src/quicktemplates2/qquickcombobox.cpp179
1 files changed, 120 insertions, 59 deletions
diff --git a/src/quicktemplates2/qquickcombobox.cpp b/src/quicktemplates2/qquickcombobox.cpp
index 71b4eaf1..f8b91b4e 100644
--- a/src/quicktemplates2/qquickcombobox.cpp
+++ b/src/quicktemplates2/qquickcombobox.cpp
@@ -43,6 +43,7 @@
#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>
@@ -50,6 +51,7 @@
#include <QtQml/qqmlcontext.h>
#include <QtQml/private/qlazilyallocated_p.h>
#include <private/qqmldelegatemodel_p.h>
+#include <QtQuick/private/qquickaccessibleattached_p.h>
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquicktextinput_p.h>
#include <QtQuick/private/qquickitemview_p.h>
@@ -59,7 +61,7 @@ QT_BEGIN_NAMESPACE
/*!
\qmltype ComboBox
\inherits Control
- \instantiates QQuickComboBox
+//! \instantiates QQuickComboBox
\inqmlmodule QtQuick.Controls
\since 5.7
\ingroup qtquickcontrols2-input
@@ -90,21 +92,7 @@ QT_BEGIN_NAMESPACE
The following example demonstrates appending content to an editable
combo box by reacting to the \l accepted signal.
- \code
- ComboBox {
- editable: true
- model: ListModel {
- id: model
- ListElement { text: "Banana" }
- ListElement { text: "Apple" }
- ListElement { text: "Coconut" }
- }
- onAccepted: {
- if (find(editText) === -1)
- model.append({text: editText})
- }
- }
- \endcode
+ \snippet qtquickcontrols2-combobox-accepted.qml combobox
\section1 ComboBox Model Roles
@@ -130,7 +118,7 @@ QT_BEGIN_NAMESPACE
\l textRole is not defined, ComboBox is unable to visualize it and throws a
\c {ReferenceError: modelData is not defined}.
- \sa {Customizing ComboBox}, {Input Controls}, {Focus Management in Qt Quick Controls 2}
+ \sa {Customizing ComboBox}, {Input Controls}, {Focus Management in Qt Quick Controls}
*/
/*!
@@ -163,9 +151,22 @@ QT_BEGIN_NAMESPACE
\qmlsignal void QtQuick.Controls::ComboBox::accepted()
This signal is emitted when the \uicontrol Return or \uicontrol Enter key is pressed
- on an \l editable combo box. If the confirmed string is not currently in the model,
- the \l currentIndex will be set to \c -1 and the \l currentText will be updated
- accordingly.
+ on an \l editable combo box.
+
+ You can handle this signal in order to add the newly entered
+ item to the model, for example:
+
+ \snippet qtquickcontrols2-combobox-accepted.qml combobox
+
+ Before the signal is emitted, a check is done to see if the string
+ exists in the model. If it does, \l currentIndex will be set to its index,
+ and \l currentText to the string itself.
+
+ After the signal has been emitted, and if the first check failed (that is,
+ the item did not exist), another check will be done to see if the item was
+ added by the signal handler. If it was, the \l currentIndex and
+ \l currentText are updated accordingly. Otherwise, they will be set to
+ \c -1 and \c "", respectively.
\note If there is a \l validator set on the combo box, the signal will only be
emitted if the input is in an acceptable state.
@@ -232,6 +233,9 @@ public:
void updateEditText();
void updateCurrentText();
void updateCurrentValue();
+ void updateCurrentTextAndValue();
+
+ bool isValidIndex(int index) const;
void acceptInput();
QString tryComplete(const QString &inputText);
@@ -261,6 +265,8 @@ public:
void itemImplicitWidthChanged(QQuickItem *item) override;
void itemImplicitHeightChanged(QQuickItem *item) override;
+ static void hideOldPopup(QQuickPopup *popup);
+
bool flat = false;
bool down = false;
bool hasDown = false;
@@ -287,6 +293,7 @@ public:
bool editable = false;
bool accepting = false;
bool allowComplete = false;
+ bool selectTextByMouse = false;
Qt::InputMethodHints inputMethodHints = Qt::ImhNone;
QString editText;
QValidator *validator = nullptr;
@@ -437,11 +444,11 @@ void QQuickComboBoxPrivate::updateEditText()
void QQuickComboBoxPrivate::updateCurrentText()
{
Q_Q(QQuickComboBox);
- QString text = q->textAt(currentIndex);
+ const QString text = q->textAt(currentIndex);
if (currentText != text) {
currentText = text;
if (!hasDisplayText)
- q->setAccessibleName(text);
+ q->maybeSetAccessibleName(text);
emit q->currentTextChanged();
}
if (!hasDisplayText && displayText != text) {
@@ -463,6 +470,17 @@ void QQuickComboBoxPrivate::updateCurrentValue()
emit q->currentValueChanged();
}
+void QQuickComboBoxPrivate::updateCurrentTextAndValue()
+{
+ updateCurrentText();
+ updateCurrentValue();
+}
+
+bool QQuickComboBoxPrivate::isValidIndex(int index) const
+{
+ return delegateModel && index >= 0 && index < delegateModel->count();
+}
+
void QQuickComboBoxPrivate::acceptInput()
{
Q_Q(QQuickComboBox);
@@ -509,10 +527,8 @@ void QQuickComboBoxPrivate::setCurrentIndex(int index, Activation activate)
currentIndex = index;
emit q->currentIndexChanged();
- if (componentComplete) {
- updateCurrentText();
- updateCurrentValue();
- }
+ if (componentComplete)
+ updateCurrentTextAndValue();
if (activate)
emit q->activated(index);
@@ -762,6 +778,21 @@ void QQuickComboBoxPrivate::itemImplicitHeightChanged(QQuickItem *item)
emit q->implicitIndicatorHeightChanged();
}
+void QQuickComboBoxPrivate::hideOldPopup(QQuickPopup *popup)
+{
+ if (!popup)
+ return;
+
+ qCDebug(lcItemManagement) << "hiding old popup" << popup;
+
+ popup->setVisible(false);
+ popup->setParentItem(nullptr);
+ // Remove the item from the accessibility tree.
+ QQuickAccessibleAttached *accessible = accessibleAttached(popup);
+ if (accessible)
+ accessible->setIgnored(true);
+}
+
QQuickComboBox::QQuickComboBox(QQuickItem *parent)
: QQuickControl(*(new QQuickComboBoxPrivate), parent)
{
@@ -782,7 +813,7 @@ QQuickComboBox::~QQuickComboBox()
// Disconnect visibleChanged() to avoid a spurious highlightedIndexChanged() signal
// emission during the destruction of the (visible) popup. (QTBUG-57650)
QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::popupVisibleChanged);
- delete d->popup;
+ QQuickComboBoxPrivate::hideOldPopup(d->popup);
d->popup = nullptr;
}
}
@@ -833,10 +864,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();
@@ -977,7 +1012,7 @@ void QQuickComboBox::setDisplayText(const QString &text)
return;
d->displayText = text;
- setAccessibleName(text);
+ maybeSetAccessibleName(text);
emit displayTextChanged();
}
@@ -1120,7 +1155,7 @@ void QQuickComboBox::setIndicator(QQuickItem *indicator)
const qreal oldImplicitIndicatorHeight = implicitIndicatorHeight();
d->removeImplicitSizeListener(d->indicator);
- delete d->indicator;
+ QQuickControlPrivate::hideOldItem(d->indicator);
d->indicator = indicator;
if (indicator) {
if (!indicator->parentItem())
@@ -1168,7 +1203,7 @@ void QQuickComboBox::setPopup(QQuickPopup *popup)
if (d->popup) {
QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::popupVisibleChanged);
- delete d->popup;
+ QQuickComboBoxPrivate::hideOldPopup(d->popup);
}
if (popup) {
QQuickPopupPrivate::get(popup)->allowVerticalFlip = true;
@@ -1506,18 +1541,11 @@ QVariant QQuickComboBox::currentValue() const
QVariant QQuickComboBox::valueAt(int index) const
{
Q_D(const QQuickComboBox);
- if (!d->delegateModel || index < 0 || index >= d->delegateModel->count())
+ if (!d->isValidIndex(index))
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;
+ const QString effectiveValueRole = d->valueRole.isEmpty() ? QStringLiteral("modelData") : d->valueRole;
+ return d->delegateModel->variantValue(index, effectiveValueRole);
}
/*!
@@ -1541,6 +1569,30 @@ int QQuickComboBox::indexOfValue(const QVariant &value) const
}
/*!
+ \since QtQuick.Controls 2.15 (Qt 5.15)
+ \qmlproperty bool QtQuick.Controls::ComboBox::selectTextByMouse
+
+ This property holds whether the text field for an editable ComboBox
+ can be selected with the mouse.
+
+ The default value is \c false.
+*/
+bool QQuickComboBox::selectTextByMouse() const
+{
+ Q_D(const QQuickComboBox);
+ return d->extra.isAllocated() ? d->extra->selectTextByMouse : false;
+}
+
+void QQuickComboBox::setSelectTextByMouse(bool canSelect)
+{
+ Q_D(QQuickComboBox);
+ if (canSelect == selectTextByMouse())
+ return;
+
+ d->extra.value().selectTextByMouse = canSelect;
+ emit selectTextByMouseChanged();
+}
+/*!
\qmlmethod string QtQuick.Controls::ComboBox::textAt(int index)
Returns the text for the specified \a index, or an empty string
@@ -1551,20 +1603,15 @@ int QQuickComboBox::indexOfValue(const QVariant &value) 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);
- if (object) {
- text = d->delegateModel->stringValue(index, d->textRole.isEmpty() ? QStringLiteral("modelData") : d->textRole);
- d->delegateModel->release(object);
- }
- return text;
+ const QString effectiveTextRole = d->textRole.isEmpty() ? QStringLiteral("modelData") : d->textRole;
+ return d->delegateModel->stringValue(index, effectiveTextRole);
}
/*!
- \qmlmethod int QtQuick.Controls::ComboBox::find(string text, flags = Qt.MatchExactly)
+ \qmlmethod int QtQuick.Controls::ComboBox::find(string text, enumeration flags)
Returns the index of the specified \a text, or \c -1 if no match is found.
@@ -1658,6 +1705,12 @@ bool QQuickComboBox::eventFilter(QObject *object, QEvent *event)
// the user clicked on the popup button to open it, not close it).
d->hidePopup(false);
setPressed(false);
+
+ // The focus left the text field, so if the edit text matches an item in the model,
+ // change our currentIndex to that. This matches widgets' behavior.
+ const int indexForEditText = find(d->extra.value().editText, Qt::MatchFixedString);
+ if (indexForEditText > -1)
+ setCurrentIndex(indexForEditText);
}
break;
#if QT_CONFIG(im)
@@ -1812,6 +1865,14 @@ void QQuickComboBox::wheelEvent(QWheelEvent *event)
}
#endif
+bool QQuickComboBox::event(QEvent *e)
+{
+ Q_D(QQuickComboBox);
+ if (e->type() == QEvent::LanguageChange)
+ d->updateCurrentText();
+ return QQuickControl::event(e);
+}
+
void QQuickComboBox::componentComplete()
{
Q_D(QQuickComboBox);
@@ -1824,12 +1885,10 @@ 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 {
- d->updateCurrentText();
- d->updateCurrentValue();
- }
+ else
+ d->updateCurrentTextAndValue();
}
}
@@ -1900,10 +1959,12 @@ void QQuickComboBox::accessibilityActiveChanged(bool active)
QQuickControl::accessibilityActiveChanged(active);
if (active) {
- setAccessibleName(d->hasDisplayText ? d->displayText : d->currentText);
+ maybeSetAccessibleName(d->hasDisplayText ? d->displayText : d->currentText);
setAccessibleProperty("editable", isEditable());
}
}
#endif //
QT_END_NAMESPACE
+
+#include "moc_qquickcombobox_p.cpp"