diff options
Diffstat (limited to 'src/quicktemplates2/qquickcombobox.cpp')
-rw-r--r-- | src/quicktemplates2/qquickcombobox.cpp | 179 |
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" |