aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/quickcontrols2/gallery/pages/ComboBoxPage.qml25
-rw-r--r--src/imports/controls/ComboBox.qml40
-rw-r--r--src/imports/controls/doc/src/includes/inputmethodhints.qdocinc38
-rw-r--r--src/imports/controls/material/ComboBox.qml39
-rw-r--r--src/imports/controls/universal/ComboBox.qml44
-rw-r--r--src/quicktemplates2/qquickcombobox.cpp430
-rw-r--r--src/quicktemplates2/qquickcombobox_p.h36
-rw-r--r--tests/auto/controls/data/tst_combobox.qml298
8 files changed, 910 insertions, 40 deletions
diff --git a/examples/quickcontrols2/gallery/pages/ComboBoxPage.qml b/examples/quickcontrols2/gallery/pages/ComboBoxPage.qml
index a52fcb67..dd06cbcb 100644
--- a/examples/quickcontrols2/gallery/pages/ComboBoxPage.qml
+++ b/examples/quickcontrols2/gallery/pages/ComboBoxPage.qml
@@ -39,7 +39,7 @@
****************************************************************************/
import QtQuick 2.6
-import QtQuick.Controls 2.1
+import QtQuick.Controls 2.2
ScrollablePage {
id: page
@@ -60,5 +60,28 @@ ScrollablePage {
model: ["First", "Second", "Third"]
anchors.horizontalCenter: parent.horizontalCenter
}
+
+ Label {
+ width: parent.width
+ wrapMode: Label.Wrap
+ horizontalAlignment: Qt.AlignHCenter
+ text: "ComboBox can be made \l editable. An editable combo box auto-"
+ + "completes its text based on what is available in the model."
+ }
+
+ 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})
+ }
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
}
}
diff --git a/src/imports/controls/ComboBox.qml b/src/imports/controls/ComboBox.qml
index 4944906a..b1497a12 100644
--- a/src/imports/controls/ComboBox.qml
+++ b/src/imports/controls/ComboBox.qml
@@ -64,40 +64,54 @@ T.ComboBox {
indicator: Image {
x: control.mirrored ? control.padding : control.width - width - control.padding
y: control.topPadding + (control.availableHeight - height) / 2
- source: "image://default/double-arrow/" + (control.visualFocus ? Default.focusColor : Default.textColor)
+ source: "image://default/double-arrow/" + (!control.editable && control.visualFocus ? Default.focusColor : Default.textColor)
sourceSize.width: width
sourceSize.height: height
opacity: enabled ? 1 : 0.3
}
- contentItem: Text {
- leftPadding: control.mirrored ? 0 : 12
- rightPadding: control.mirrored ? 12 : 0
+ contentItem: T.TextField {
+ leftPadding: !control.mirrored ? 12 : control.editable && activeFocus ? 3 : 1
+ rightPadding: control.mirrored ? 12 : control.editable && activeFocus ? 3 : 1
topPadding: 6 - control.padding
bottomPadding: 6 - control.padding
- text: control.displayText
+ text: control.editable ? control.editText : control.displayText
+
+ enabled: control.editable
+ autoScroll: control.editable
+ readOnly: control.popup.visible
+ inputMethodHints: control.inputMethodHints
+ validator: control.validator
+
font: control.font
- color: control.visualFocus ? Default.focusColor : Default.textColor
+ color: !control.editable && control.visualFocus ? Default.focusColor : Default.textColor
+ selectionColor: Default.focusColor
+ selectedTextColor: Default.textLightColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
- elide: Text.ElideRight
- opacity: enabled ? 1 : 0.3
+ opacity: control.enabled ? 1 : 0.3
+
+ background: Rectangle {
+ visible: control.editable && !control.flat
+ border.width: parent && parent.activeFocus ? 2 : 1
+ border.color: parent && parent.activeFocus ? Default.focusColor : Default.buttonColor
+ }
}
background: Rectangle {
implicitWidth: 120
implicitHeight: 40
- color: control.visualFocus ? (control.pressed ? Default.focusPressedColor : Default.focusLightColor) :
- (control.down ? Default.buttonPressedColor : Default.buttonColor)
+ color: !control.editable && control.visualFocus ? (control.pressed ? Default.focusPressedColor : Default.focusLightColor) :
+ (control.down || popup.visible ? Default.buttonPressedColor : Default.buttonColor)
border.color: Default.focusColor
- border.width: control.visualFocus ? 2 : 0
- visible: !control.flat || control.pressed
+ border.width: !control.editable && control.visualFocus ? 2 : 0
+ visible: !control.flat || control.down
}
popup: T.Popup {
- y: control.height - (control.visualFocus ? 0 : 1)
+ y: control.height
width: control.width
implicitHeight: contentItem.implicitHeight
topMargin: 6
diff --git a/src/imports/controls/doc/src/includes/inputmethodhints.qdocinc b/src/imports/controls/doc/src/includes/inputmethodhints.qdocinc
new file mode 100644
index 00000000..73710e1e
--- /dev/null
+++ b/src/imports/controls/doc/src/includes/inputmethodhints.qdocinc
@@ -0,0 +1,38 @@
+//! [flags]
+The value is a bit-wise combination of flags or \c Qt.ImhNone if no hints are set.
+
+Flags that alter behavior are:
+
+\list
+\li Qt.ImhHiddenText - Characters should be hidden, as is typically used when entering passwords.
+\li Qt.ImhSensitiveData - Typed text should not be stored by the active input method
+ in any persistent storage like predictive user dictionary.
+\li Qt.ImhNoAutoUppercase - The input method should not try to automatically switch to upper case
+ when a sentence ends.
+\li Qt.ImhPreferNumbers - Numbers are preferred (but not required).
+\li Qt.ImhPreferUppercase - Upper case letters are preferred (but not required).
+\li Qt.ImhPreferLowercase - Lower case letters are preferred (but not required).
+\li Qt.ImhNoPredictiveText - Do not use predictive text (i.e. dictionary lookup) while typing.
+
+\li Qt.ImhDate - The text editor functions as a date field.
+\li Qt.ImhTime - The text editor functions as a time field.
+\endlist
+
+Flags that restrict input (exclusive flags) are:
+
+\list
+\li Qt.ImhDigitsOnly - Only digits are allowed.
+\li Qt.ImhFormattedNumbersOnly - Only number input is allowed. This includes decimal point and minus sign.
+\li Qt.ImhUppercaseOnly - Only upper case letter input is allowed.
+\li Qt.ImhLowercaseOnly - Only lower case letter input is allowed.
+\li Qt.ImhDialableCharactersOnly - Only characters suitable for phone dialing are allowed.
+\li Qt.ImhEmailCharactersOnly - Only characters suitable for email addresses are allowed.
+\li Qt.ImhUrlCharactersOnly - Only characters suitable for URLs are allowed.
+\endlist
+
+Masks:
+
+\list
+\li Qt.ImhExclusiveInputMask - This mask yields nonzero if any of the exclusive flags are used.
+\endlist
+//! [flags]
diff --git a/src/imports/controls/material/ComboBox.qml b/src/imports/controls/material/ComboBox.qml
index 50175cbe..8f41a481 100644
--- a/src/imports/controls/material/ComboBox.qml
+++ b/src/imports/controls/material/ComboBox.qml
@@ -73,17 +73,27 @@ T.ComboBox {
source: "image://material/drop-indicator/" + (control.enabled ? control.Material.foreground : control.Material.hintTextColor)
}
- contentItem: Text {
+ contentItem: T.TextField {
padding: 6
- leftPadding: control.mirrored ? 0 : 12
- rightPadding: control.mirrored ? 12 : 0
+ leftPadding: control.editable ? 2 : control.mirrored ? 0 : 12
+ rightPadding: control.editable ? 2 : control.mirrored ? 12 : 0
+
+ text: control.editable ? control.editText : control.displayText
+
+ enabled: control.editable
+ autoScroll: control.editable
+ readOnly: control.popup.visible
+ inputMethodHints: control.inputMethodHints
+ validator: control.validator
- text: control.displayText
font: control.font
color: control.enabled ? control.Material.foreground : control.Material.hintTextColor
+ selectionColor: control.Material.accentColor
+ selectedTextColor: control.Material.primaryHighlightedTextColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
- elide: Text.ElideRight
+
+ cursorDelegate: CursorDelegate { }
}
background: Rectangle {
@@ -94,32 +104,43 @@ T.ComboBox {
y: 6
height: parent.height - 12
radius: control.flat ? 0 : 2
- color: control.Material.dialogColor
+ color: !control.editable ? control.Material.dialogColor : "transparent"
Behavior on color {
+ enabled: !control.editable
ColorAnimation {
duration: 400
}
}
- layer.enabled: control.enabled && control.Material.background.a > 0
+ layer.enabled: control.enabled && !control.editable && control.Material.background.a > 0
layer.effect: ElevationEffect {
elevation: control.Material.elevation
}
+ Rectangle {
+ visible: control.editable
+ y: parent.y + control.baselineOffset
+ width: parent.width
+ height: control.activeFocus ? 2 : 1
+ color: control.editable && control.activeFocus ? control.Material.accentColor : control.Material.hintTextColor
+ }
+
Ripple {
clip: control.flat
clipRadius: control.flat ? 0 : 2
- width: parent.width
+ x: control.editable && control.indicator ? control.indicator.x : 0
+ width: control.editable && control.indicator ? control.indicator.width : parent.width
height: parent.height
pressed: control.pressed
- anchor: control
+ anchor: control.editable && control.indicator ? control.indicator : control
active: control.pressed || control.visualFocus || control.hovered
color: control.Material.rippleColor
}
}
popup: T.Popup {
+ y: control.editable ? control.height - 5 : 0
width: control.width
implicitHeight: contentItem.implicitHeight
transformOrigin: Item.Top
diff --git a/src/imports/controls/universal/ComboBox.qml b/src/imports/controls/universal/ComboBox.qml
index 7552c164..4c5ecf74 100644
--- a/src/imports/controls/universal/ComboBox.qml
+++ b/src/imports/controls/universal/ComboBox.qml
@@ -53,6 +53,8 @@ T.ComboBox {
leftPadding: padding + (!control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
rightPadding: padding + (control.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
+ Universal.theme: editable && activeFocus ? Universal.Light : undefined
+
delegate: ItemDelegate {
width: parent.width
text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData
@@ -66,22 +68,40 @@ T.ComboBox {
source: "image://universal/downarrow/" + (!control.enabled ? control.Universal.baseLowColor : control.Universal.baseMediumHighColor)
sourceSize.width: width
sourceSize.height: height
+
+ Rectangle {
+ z: -1
+ width: parent.width
+ height: parent.height
+ color: control.activeFocus ? control.Universal.accent :
+ control.pressed ? control.Universal.baseMediumLowColor :
+ control.hovered ? control.Universal.baseLowColor : "transparent"
+ visible: control.editable && !contentItem.hovered && (control.pressed || control.hovered)
+ opacity: control.activeFocus && !control.pressed ? 0.4 : 1.0
+ }
}
- contentItem: Text {
- leftPadding: control.mirrored ? 0 : 12
- rightPadding: control.mirrored ? 10 : 0
+ contentItem: T.TextField {
+ leftPadding: control.mirrored ? 1 : 12
+ rightPadding: control.mirrored ? 10 : 1
topPadding: 5 - control.topPadding
bottomPadding: 7 - control.bottomPadding
- text: control.displayText
+ text: control.editable ? control.editText : control.displayText
+
+ enabled: control.editable
+ autoScroll: control.editable
+ readOnly: control.popup.visible
+ inputMethodHints: control.inputMethodHints
+ validator: control.validator
+
font: control.font
+ color: !control.enabled ? control.Universal.chromeDisabledLowColor :
+ control.editable && control.activeFocus ? control.Universal.chromeBlackHighColor : control.Universal.foreground
+ selectionColor: control.Universal.accent
+ selectedTextColor: control.Universal.chromeWhiteColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
- elide: Text.ElideRight
-
- opacity: enabled ? 1.0 : 0.2
- color: control.Universal.foreground
}
background: Rectangle {
@@ -90,11 +110,13 @@ T.ComboBox {
border.width: control.flat ? 0 : 2 // ComboBoxBorderThemeThickness
border.color: !control.enabled ? control.Universal.baseLowColor :
- control.down ? control.Universal.baseMediumLowColor :
+ control.editable && control.activeFocus ? control.Universal.accent :
+ control.down || popup.visible ? control.Universal.baseMediumLowColor :
control.hovered ? control.Universal.baseMediumColor : control.Universal.baseMediumLowColor
color: !control.enabled ? control.Universal.baseLowColor :
control.down ? control.Universal.listMediumColor :
- control.flat && control.hovered ? control.Universal.listLowColor : control.Universal.altMediumLowColor
+ control.flat && control.hovered ? control.Universal.listLowColor :
+ control.editable && control.activeFocus ? control.Universal.background : control.Universal.altMediumLowColor
visible: !control.flat || control.pressed || control.hovered || control.visualFocus
Rectangle {
@@ -103,7 +125,7 @@ T.ComboBox {
width: parent.width - 4
height: parent.height - 4
- visible: control.visualFocus
+ visible: control.visualFocus && !control.editable
color: control.Universal.accent
opacity: control.Universal.theme === Universal.Light ? 0.4 : 0.6
}
diff --git a/src/quicktemplates2/qquickcombobox.cpp b/src/quicktemplates2/qquickcombobox.cpp
index 5f18fb40..0ced47d1 100644
--- a/src/quicktemplates2/qquickcombobox.cpp
+++ b/src/quicktemplates2/qquickcombobox.cpp
@@ -41,11 +41,15 @@
#include <QtCore/qregexp.h>
#include <QtCore/qabstractitemmodel.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 <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquicktextinput_p.h>
QT_BEGIN_NAMESPACE
@@ -74,6 +78,30 @@ QT_BEGIN_NAMESPACE
}
\endcode
+ \section1 Editable ComboBox
+
+ ComboBox can be made \l editable. An editable combo box auto-completes
+ its text based on what is available in the model.
+
+ 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
+
\section1 ComboBox Model Roles
ComboBox is able to visualize standard \l {qml-data-models}{data models}
@@ -130,6 +158,19 @@ QT_BEGIN_NAMESPACE
\sa highlightedIndex
*/
+/*!
+ \since QtQuick.Controls 2.2
+ \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 \c currentText will be updated
+ accordingly.
+
+ \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.
+*/
+
namespace {
enum Activation { NoActivate, Activate };
enum Highlighting { NoHighlight, Highlight };
@@ -181,8 +222,15 @@ public:
void itemClicked();
void createdItem(int index, QObject *object);
+ void modelUpdated();
void countChanged();
+
+ void updateEditText();
void updateCurrentText();
+
+ void acceptInput();
+ QString tryComplete(const QString &inputText);
+
void incrementCurrentIndex();
void decrementCurrentIndex();
void setCurrentIndex(int index, Activation activate);
@@ -212,6 +260,23 @@ public:
QQmlComponent *delegate;
QQuickItem *indicator;
QQuickPopup *popup;
+
+ struct ExtraData {
+ ExtraData()
+ : editable(false),
+ accepting(false),
+ allowComplete(false),
+ inputMethodHints(Qt::ImhNone),
+ validator(nullptr) { }
+
+ bool editable;
+ bool accepting;
+ bool allowComplete;
+ Qt::InputMethodHints inputMethodHints;
+ QString editText;
+ QValidator *validator;
+ };
+ QLazilyAllocated<ExtraData> extra;
};
QQuickComboBoxPrivate::QQuickComboBoxPrivate()
@@ -267,6 +332,9 @@ void QQuickComboBoxPrivate::togglePopup(bool accept)
void QQuickComboBoxPrivate::popupVisibleChanged()
{
Q_Q(QQuickComboBox);
+ if (isPopupVisible())
+ QGuiApplication::inputMethod()->reset();
+
updateHighlightedIndex();
if (!hasDown) {
q->setDown(pressed || isPopupVisible());
@@ -286,6 +354,7 @@ void QQuickComboBoxPrivate::itemClicked()
void QQuickComboBoxPrivate::createdItem(int index, QObject *object)
{
+ Q_Q(QQuickComboBox);
QQuickItem *item = qobject_cast<QQuickItem *>(object);
if (popup && item && !item->parentItem()) {
item->setParentItem(popup->contentItem());
@@ -298,7 +367,13 @@ void QQuickComboBoxPrivate::createdItem(int index, QObject *object)
connect(button, &QQuickAbstractButton::clicked, this, &QQuickComboBoxPrivate::itemClicked);
}
- if (index == currentIndex)
+ if (index == currentIndex && !q->isEditable())
+ updateCurrentText();
+}
+
+void QQuickComboBoxPrivate::modelUpdated()
+{
+ if (!extra.isAllocated() || !extra->accepting)
updateCurrentText();
}
@@ -310,6 +385,26 @@ void QQuickComboBoxPrivate::countChanged()
emit q->countChanged();
}
+void QQuickComboBoxPrivate::updateEditText()
+{
+ Q_Q(QQuickComboBox);
+ QQuickTextInput *input = qobject_cast<QQuickTextInput *>(contentItem);
+ if (!input)
+ return;
+
+ const QString text = input->text();
+
+ if (extra.isAllocated() && extra->allowComplete && !text.isEmpty()) {
+ const QString completed = tryComplete(text);
+ if (completed.length() > text.length()) {
+ input->setText(completed);
+ input->select(completed.length(), text.length());
+ return;
+ }
+ }
+ q->setEditText(text);
+}
+
void QQuickComboBoxPrivate::updateCurrentText()
{
Q_Q(QQuickComboBox);
@@ -324,6 +419,45 @@ void QQuickComboBoxPrivate::updateCurrentText()
displayText = text;
emit q->displayTextChanged();
}
+ if (!extra.isAllocated() || !extra->accepting)
+ q->setEditText(currentText);
+}
+
+void QQuickComboBoxPrivate::acceptInput()
+{
+ Q_Q(QQuickComboBox);
+ int idx = q->find(extra.value().editText, Qt::MatchFixedString);
+ if (idx > -1)
+ q->setCurrentIndex(idx);
+
+ extra.value().accepting = true;
+ emit q->accepted();
+
+ if (idx == -1)
+ q->setCurrentIndex(q->find(extra.value().editText, Qt::MatchFixedString));
+ extra.value().accepting = false;
+}
+
+QString QQuickComboBoxPrivate::tryComplete(const QString &input)
+{
+ Q_Q(QQuickComboBox);
+ QString match;
+
+ const int itemCount = q->count();
+ for (int idx = 0; idx < itemCount; ++idx) {
+ const QString text = q->textAt(idx);
+ if (!text.startsWith(input, Qt::CaseInsensitive))
+ continue;
+
+ // either the first or the shortest match
+ if (match.isEmpty() || text.length() < match.length())
+ match = text;
+ }
+
+ if (match.isEmpty())
+ return input;
+
+ return input + match.mid(input.length());
}
void QQuickComboBoxPrivate::setCurrentIndex(int index, Activation activate)
@@ -345,6 +479,8 @@ void QQuickComboBoxPrivate::setCurrentIndex(int index, Activation activate)
void QQuickComboBoxPrivate::incrementCurrentIndex()
{
Q_Q(QQuickComboBox);
+ if (extra.isAllocated())
+ extra->allowComplete = false;
if (isPopupVisible()) {
if (highlightedIndex < q->count() - 1)
setHighlightedIndex(highlightedIndex + 1, Highlight);
@@ -352,10 +488,14 @@ void QQuickComboBoxPrivate::incrementCurrentIndex()
if (currentIndex < q->count() - 1)
setCurrentIndex(currentIndex + 1, Activate);
}
+ if (extra.isAllocated())
+ extra->allowComplete = true;
}
void QQuickComboBoxPrivate::decrementCurrentIndex()
{
+ if (extra.isAllocated())
+ extra->allowComplete = false;
if (isPopupVisible()) {
if (highlightedIndex > 0)
setHighlightedIndex(highlightedIndex - 1, Highlight);
@@ -363,6 +503,8 @@ void QQuickComboBoxPrivate::decrementCurrentIndex()
if (currentIndex > 0)
setCurrentIndex(currentIndex - 1, Activate);
}
+ if (extra.isAllocated())
+ extra->allowComplete = true;
}
void QQuickComboBoxPrivate::updateHighlightedIndex()
@@ -449,7 +591,7 @@ void QQuickComboBoxPrivate::createDelegateModel()
QQmlInstanceModel* oldModel = delegateModel;
if (oldModel) {
disconnect(delegateModel, &QQmlInstanceModel::countChanged, this, &QQuickComboBoxPrivate::countChanged);
- disconnect(delegateModel, &QQmlInstanceModel::modelUpdated, this, &QQuickComboBoxPrivate::updateCurrentText);
+ disconnect(delegateModel, &QQmlInstanceModel::modelUpdated, this, &QQuickComboBoxPrivate::modelUpdated);
disconnect(delegateModel, &QQmlInstanceModel::createdItem, this, &QQuickComboBoxPrivate::createdItem);
}
@@ -469,7 +611,7 @@ void QQuickComboBoxPrivate::createDelegateModel()
if (delegateModel) {
connect(delegateModel, &QQmlInstanceModel::countChanged, this, &QQuickComboBoxPrivate::countChanged);
- connect(delegateModel, &QQmlInstanceModel::modelUpdated, this, &QQuickComboBoxPrivate::updateCurrentText);
+ connect(delegateModel, &QQmlInstanceModel::modelUpdated, this, &QQuickComboBoxPrivate::modelUpdated);
connect(delegateModel, &QQmlInstanceModel::createdItem, this, &QQuickComboBoxPrivate::createdItem);
}
@@ -485,6 +627,7 @@ QQuickComboBox::QQuickComboBox(QQuickItem *parent) :
setFocusPolicy(Qt::StrongFocus);
setFlag(QQuickItem::ItemIsFocusScope);
setAcceptedMouseButtons(Qt::LeftButton);
+ setInputMethodHints(Qt::ImhNoPredictiveText);
}
QQuickComboBox::~QQuickComboBox()
@@ -567,6 +710,48 @@ QQmlInstanceModel *QQuickComboBox::delegateModel() const
}
/*!
+ \since QtQuick.Controls 2.2
+ \qmlproperty bool QtQuick.Controls::ComboBox::editable
+
+ This property holds whether the combo box is editable.
+
+ The default value is \c false.
+
+ \sa validator
+*/
+bool QQuickComboBox::isEditable() const
+{
+ Q_D(const QQuickComboBox);
+ return d->extra.isAllocated() && d->extra->editable;
+}
+
+void QQuickComboBox::setEditable(bool editable)
+{
+ Q_D(QQuickComboBox);
+ if (editable == isEditable())
+ return;
+
+ if (d->contentItem) {
+ if (editable) {
+ d->contentItem->installEventFilter(this);
+ if (QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem)) {
+ QObjectPrivate::connect(input, &QQuickTextInput::textChanged, d, &QQuickComboBoxPrivate::updateEditText);
+ QObjectPrivate::connect(input, &QQuickTextInput::accepted, d, &QQuickComboBoxPrivate::acceptInput);
+ }
+ } else {
+ d->contentItem->removeEventFilter(this);
+ if (QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem)) {
+ QObjectPrivate::disconnect(input, &QQuickTextInput::textChanged, d, &QQuickComboBoxPrivate::updateEditText);
+ QObjectPrivate::disconnect(input, &QQuickTextInput::accepted, d, &QQuickComboBoxPrivate::acceptInput);
+ }
+ }
+ }
+
+ d->extra.value().editable = editable;
+ emit editableChanged();
+}
+
+/*!
\since QtQuick.Controls 2.1
\qmlproperty bool QtQuick.Controls::ComboBox::flat
@@ -765,6 +950,35 @@ void QQuickComboBox::resetDisplayText()
}
/*!
+ \since QtQuick.Controls 2.2
+ \qmlproperty string QtQuick.Controls::ComboBox::editText
+
+ This property holds the text in the text field of an editable combo box.
+
+ \sa editable
+*/
+QString QQuickComboBox::editText() const
+{
+ Q_D(const QQuickComboBox);
+ return d->extra.isAllocated() ? d->extra->editText : QString();
+}
+
+void QQuickComboBox::setEditText(const QString &text)
+{
+ Q_D(QQuickComboBox);
+ if (text == editText())
+ return;
+
+ d->extra.value().editText = text;
+ emit editTextChanged();
+}
+
+void QQuickComboBox::resetEditText()
+{
+ setEditText(QString());
+}
+
+/*!
\qmlproperty string QtQuick.Controls::ComboBox::textRole
This property holds the model role used for populating the combo box.
@@ -901,6 +1115,115 @@ void QQuickComboBox::setPopup(QQuickPopup *popup)
}
/*!
+ \since QtQuick.Controls 2.2
+ \qmlproperty Validator QtQuick.Controls::ComboBox::validator
+
+ This property holds an input text validator for an editable combo box.
+
+ When a validator is set, the text field will only accept input which
+ leaves the text property in an intermediate state. The \l accepted signal
+ will only be emitted if the text is in an acceptable state when the
+ \uicontrol Return or \uicontrol Enter key is pressed.
+
+ The currently supported validators are \l[QtQuick]{IntValidator},
+ \l[QtQuick]{DoubleValidator}, and \l[QtQuick]{RegExpValidator}. An
+ example of using validators is shown below, which allows input of
+ integers between \c 0 and \c 10 into the text field:
+
+ \code
+ ComboBox {
+ model: 10
+ editable: true
+ validator: IntValidator {
+ top: 9
+ bottom: 0
+ }
+ }
+ \endcode
+
+ \sa acceptableInput, accepted, editable
+*/
+QValidator *QQuickComboBox::validator() const
+{
+ Q_D(const QQuickComboBox);
+ return d->extra.isAllocated() ? d->extra->validator : nullptr;
+}
+
+void QQuickComboBox::setValidator(QValidator *validator)
+{
+ Q_D(QQuickComboBox);
+ if (validator == QQuickComboBox::validator())
+ return;
+
+ d->extra.value().validator = validator;
+ if (validator)
+ validator->setLocale(d->locale);
+ emit validatorChanged();
+}
+
+/*!
+ \since QtQuick.Controls 2.2
+ \qmlproperty flags QtQuick.Controls::ComboBox::inputMethodHints
+
+ Provides hints to the input method about the expected content of the combo box and how it
+ should operate.
+
+ The default value is \c Qt.ImhNoPredictiveText.
+
+ \include inputmethodhints.qdocinc
+*/
+Qt::InputMethodHints QQuickComboBox::inputMethodHints() const
+{
+ Q_D(const QQuickComboBox);
+ return d->extra.isAllocated() ? d->extra->inputMethodHints : Qt::ImhNoPredictiveText;
+}
+
+void QQuickComboBox::setInputMethodHints(Qt::InputMethodHints hints)
+{
+ Q_D(QQuickComboBox);
+ if (hints == inputMethodHints())
+ return;
+
+ d->extra.value().inputMethodHints = hints;
+ emit inputMethodHintsChanged();
+}
+
+/*!
+ \since QtQuick.Controls 2.2
+ \qmlproperty bool QtQuick.Controls::ComboBox::inputMethodComposing
+ \readonly
+
+ This property holds whether an editable combo box has partial text input from an input method.
+
+ While it is composing, an input method may rely on mouse or key events from the combo box to
+ edit or commit the partial text. This property can be used to determine when to disable event
+ handlers that may interfere with the correct operation of an input method.
+*/
+bool QQuickComboBox::isInputMethodComposing() const
+{
+ Q_D(const QQuickComboBox);
+ return d->contentItem && d->contentItem->property("inputMethodComposing").toBool();
+}
+
+/*!
+ \since QtQuick.Controls 2.2
+ \qmlproperty bool QtQuick.Controls::ComboBox::acceptableInput
+ \readonly
+
+ This property holds whether the combo box contains acceptable text in the editable text field.
+
+ If a validator has been set, the value is \c true only if the current text is acceptable
+ to the validator as a final string (not as an intermediate string).
+
+ \sa validator, accepted
+*/
+bool QQuickComboBox::hasAcceptableInput() const
+{
+ Q_D(const QQuickComboBox);
+ return d->contentItem && d->contentItem->property("acceptableInput").toBool();
+}
+
+/*!
\qmlmethod string QtQuick.Controls::ComboBox::textAt(int index)
Returns the text for the specified \a index, or an empty string
@@ -977,6 +1300,59 @@ void QQuickComboBox::decrementCurrentIndex()
d->decrementCurrentIndex();
}
+/*!
+ \since QtQuick.Controls 2.2
+ \qmlmethod void QtQuick.Controls::ComboBox::selectAll()
+
+ Selects all the text in the editable text field of the combo box.
+
+ \sa editText
+*/
+void QQuickComboBox::selectAll()
+{
+ Q_D(QQuickComboBox);
+ QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
+ if (!input)
+ return;
+ input->selectAll();
+}
+
+bool QQuickComboBox::eventFilter(QObject *object, QEvent *event)
+{
+ Q_D(QQuickComboBox);
+ switch (event->type()) {
+ case QEvent::MouseButtonRelease:
+ if (d->isPopupVisible())
+ d->hidePopup(false);
+ break;
+ case QEvent::KeyPress: {
+ const int key = static_cast<QKeyEvent *>(event)->key();
+ if (d->extra.isAllocated())
+ d->extra->allowComplete = key != Qt::Key_Backspace && key != Qt::Key_Delete;
+ break;
+ }
+ case QEvent::FocusOut:
+ d->hidePopup(false);
+ setPressed(false);
+ break;
+ case QEvent::InputMethod:
+ if (d->extra.isAllocated())
+ d->extra->allowComplete = !static_cast<QInputMethodEvent*>(event)->commitString().isEmpty();
+ break;
+ default:
+ break;
+ }
+ return QQuickControl::eventFilter(object, event);
+}
+
+void QQuickComboBox::focusInEvent(QFocusEvent *event)
+{
+ Q_D(QQuickComboBox);
+ QQuickControl::focusInEvent(event);
+ if (d->contentItem && isEditable())
+ d->contentItem->forceActiveFocus(event->reason());
+}
+
void QQuickComboBox::focusOutEvent(QFocusEvent *event)
{
Q_D(QQuickComboBox);
@@ -985,6 +1361,16 @@ void QQuickComboBox::focusOutEvent(QFocusEvent *event)
setPressed(false);
}
+void QQuickComboBox::inputMethodEvent(QInputMethodEvent *event)
+{
+ Q_D(QQuickComboBox);
+ QQuickControl::inputMethodEvent(event);
+ if (!isEditable() && !event->commitString().isEmpty())
+ d->keySearch(event->commitString());
+ else
+ event->ignore();
+}
+
void QQuickComboBox::keyPressEvent(QKeyEvent *event)
{
Q_D(QQuickComboBox);
@@ -1030,7 +1416,7 @@ void QQuickComboBox::keyPressEvent(QKeyEvent *event)
event->accept();
break;
default:
- if (!event->text().isEmpty())
+ if (!isEditable() && !event->text().isEmpty())
d->keySearch(event->text());
else
event->ignore();
@@ -1047,13 +1433,15 @@ void QQuickComboBox::keyReleaseEvent(QKeyEvent *event)
switch (event->key()) {
case Qt::Key_Space:
- d->togglePopup(true);
+ if (!isEditable())
+ d->togglePopup(true);
setPressed(false);
event->accept();
break;
case Qt::Key_Enter:
case Qt::Key_Return:
- d->hidePopup(d->isPopupVisible());
+ if (!isEditable() || d->isPopupVisible())
+ d->hidePopup(d->isPopupVisible());
setPressed(false);
event->accept();
break;
@@ -1126,6 +1514,36 @@ void QQuickComboBox::componentComplete()
}
}
+void QQuickComboBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
+{
+ Q_D(QQuickComboBox);
+ if (oldItem) {
+ oldItem->removeEventFilter(this);
+ if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem)) {
+ QObjectPrivate::disconnect(oldInput, &QQuickTextInput::accepted, d, &QQuickComboBoxPrivate::acceptInput);
+ QObjectPrivate::disconnect(oldInput, &QQuickTextInput::textChanged, d, &QQuickComboBoxPrivate::updateEditText);
+ disconnect(oldInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickComboBox::inputMethodComposingChanged);
+ disconnect(oldInput, &QQuickTextInput::acceptableInputChanged, this, &QQuickComboBox::acceptableInputChanged);
+ }
+ }
+ if (newItem && isEditable()) {
+ newItem->installEventFilter(this);
+ if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem)) {
+ QObjectPrivate::connect(newInput, &QQuickTextInput::accepted, d, &QQuickComboBoxPrivate::acceptInput);
+ QObjectPrivate::connect(newInput, &QQuickTextInput::textChanged, d, &QQuickComboBoxPrivate::updateEditText);
+ connect(newInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickComboBox::inputMethodComposingChanged);
+ connect(newInput, &QQuickTextInput::acceptableInputChanged, this, &QQuickComboBox::acceptableInputChanged);
+ }
+ }
+}
+
+void QQuickComboBox::localeChange(const QLocale &newLocale, const QLocale &oldLocale)
+{
+ QQuickControl::localeChange(newLocale, oldLocale);
+ if (QValidator *v = validator())
+ v->setLocale(newLocale);
+}
+
QFont QQuickComboBox::defaultFont() const
{
return QQuickControlPrivate::themeFont(QPlatformTheme::ComboMenuItemFont);
diff --git a/src/quicktemplates2/qquickcombobox_p.h b/src/quicktemplates2/qquickcombobox_p.h
index b1491ac5..6bbbe3b7 100644
--- a/src/quicktemplates2/qquickcombobox_p.h
+++ b/src/quicktemplates2/qquickcombobox_p.h
@@ -52,6 +52,7 @@
QT_BEGIN_NAMESPACE
+class QValidator;
class QQuickPopup;
class QQmlInstanceModel;
class QQuickComboBoxPrivate;
@@ -62,6 +63,7 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickComboBox : public QQuickControl
Q_PROPERTY(int count READ count NOTIFY countChanged FINAL)
Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged FINAL)
Q_PROPERTY(QQmlInstanceModel *delegateModel READ delegateModel NOTIFY delegateModelChanged FINAL)
+ Q_PROPERTY(bool editable READ isEditable WRITE setEditable NOTIFY editableChanged FINAL REVISION 2)
Q_PROPERTY(bool flat READ isFlat WRITE setFlat NOTIFY flatChanged FINAL REVISION 1)
Q_PROPERTY(bool down READ isDown WRITE setDown RESET resetDown NOTIFY downChanged FINAL REVISION 2)
Q_PROPERTY(bool pressed READ isPressed WRITE setPressed NOTIFY pressedChanged FINAL) // ### Qt 6: should not be writable
@@ -69,10 +71,15 @@ class Q_QUICKTEMPLATES2_PRIVATE_EXPORT QQuickComboBox : public QQuickControl
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged FINAL)
Q_PROPERTY(QString currentText READ currentText NOTIFY currentTextChanged FINAL)
Q_PROPERTY(QString displayText READ displayText WRITE setDisplayText RESET resetDisplayText NOTIFY displayTextChanged FINAL)
+ Q_PROPERTY(QString editText READ editText WRITE setEditText RESET resetEditText NOTIFY editTextChanged FINAL REVISION 2)
Q_PROPERTY(QString textRole READ textRole WRITE setTextRole NOTIFY textRoleChanged FINAL)
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL)
Q_PROPERTY(QQuickItem *indicator READ indicator WRITE setIndicator NOTIFY indicatorChanged FINAL)
Q_PROPERTY(QQuickPopup *popup READ popup WRITE setPopup NOTIFY popupChanged FINAL)
+ Q_PROPERTY(QValidator *validator READ validator WRITE setValidator NOTIFY validatorChanged FINAL REVISION 2)
+ Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ inputMethodHints WRITE setInputMethodHints NOTIFY inputMethodHintsChanged FINAL REVISION 2)
+ Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged FINAL REVISION 2)
+ Q_PROPERTY(bool acceptableInput READ hasAcceptableInput NOTIFY acceptableInputChanged FINAL REVISION 2)
public:
explicit QQuickComboBox(QQuickItem *parent = nullptr);
@@ -84,6 +91,9 @@ public:
void setModel(const QVariant &model);
QQmlInstanceModel *delegateModel() const;
+ bool isEditable() const;
+ void setEditable(bool editable);
+
bool isFlat() const;
void setFlat(bool flat);
@@ -105,6 +115,10 @@ public:
void setDisplayText(const QString &text);
void resetDisplayText();
+ QString editText() const;
+ void setEditText(const QString &text);
+ void resetEditText();
+
QString textRole() const;
void setTextRole(const QString &role);
@@ -117,17 +131,28 @@ public:
QQuickPopup *popup() const;
void setPopup(QQuickPopup *popup);
+ QValidator *validator() const;
+ void setValidator(QValidator *validator);
+
+ Qt::InputMethodHints inputMethodHints() const;
+ void setInputMethodHints(Qt::InputMethodHints hints);
+
+ bool isInputMethodComposing() const;
+ bool hasAcceptableInput() const;
+
Q_INVOKABLE QString textAt(int index) const;
Q_INVOKABLE int find(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly) const;
public Q_SLOTS:
void incrementCurrentIndex();
void decrementCurrentIndex();
+ Q_REVISION(2) void selectAll();
Q_SIGNALS:
void countChanged();
void modelChanged();
void delegateModelChanged();
+ Q_REVISION(2) void editableChanged();
Q_REVISION(1) void flatChanged();
Q_REVISION(2) void downChanged();
void pressedChanged();
@@ -135,16 +160,25 @@ Q_SIGNALS:
void currentIndexChanged();
void currentTextChanged();
void displayTextChanged();
+ Q_REVISION(2) void editTextChanged();
void textRoleChanged();
void delegateChanged();
void indicatorChanged();
void popupChanged();
+ Q_REVISION(2) void validatorChanged();
+ Q_REVISION(2) void inputMethodHintsChanged();
+ Q_REVISION(2) void inputMethodComposingChanged();
+ Q_REVISION(2) void acceptableInputChanged();
void activated(int index);
void highlighted(int index);
+ Q_REVISION(2) void accepted();
protected:
+ bool eventFilter(QObject *object, QEvent *event) override;
+ void focusInEvent(QFocusEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
+ void inputMethodEvent(QInputMethodEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
@@ -154,6 +188,8 @@ protected:
void wheelEvent(QWheelEvent *event) override;
void componentComplete() override;
+ void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override;
+ void localeChange(const QLocale &newLocale, const QLocale &oldLocale) override;
QFont defaultFont() const override;
diff --git a/tests/auto/controls/data/tst_combobox.qml b/tests/auto/controls/data/tst_combobox.qml
index d0cdfb17..68fe4c76 100644
--- a/tests/auto/controls/data/tst_combobox.qml
+++ b/tests/auto/controls/data/tst_combobox.qml
@@ -1115,4 +1115,302 @@ TestCase {
closedSpy.target = null
control.destroy()
}
+
+ RegExpValidator {
+ id: regExpValidator
+ regExp: /(red|blue|green)?/
+ }
+
+ function test_validator() {
+ var control = comboBox.createObject(testCase, {editable: true, validator: regExpValidator})
+
+ control.editText = "blu"
+ compare(control.acceptableInput, false)
+ control.editText = "blue"
+ compare(control.acceptableInput, true)
+ control.editText = "bluee"
+ compare(control.acceptableInput, false)
+ control.editText = ""
+ compare(control.acceptableInput, true)
+ control.editText = ""
+ control.forceActiveFocus()
+ keyPress(Qt.Key_A)
+ compare(control.editText, "")
+ keyPress(Qt.Key_A)
+ compare(control.editText, "")
+ keyPress(Qt.Key_R)
+ compare(control.editText, "r")
+ keyPress(Qt.Key_A)
+ compare(control.editText, "r")
+ compare(control.acceptableInput, false)
+ keyPress(Qt.Key_E)
+ compare(control.editText, "re")
+ compare(control.acceptableInput, false)
+ keyPress(Qt.Key_D)
+ compare(control.editText, "red")
+ compare(control.acceptableInput, true)
+
+ control.destroy()
+ }
+
+ Component {
+ id: appendFindBox
+ ComboBox {
+ editable: true
+ model: ListModel {
+ ListElement { text:"first" }
+ }
+ onAccepted: {
+ if (find(editText) === -1)
+ model.append({text: editText})
+ }
+ }
+ }
+
+ function test_append_find() {
+ var control = appendFindBox.createObject(testCase)
+
+ compare(control.currentIndex, 0)
+ compare(control.currentText, "first")
+ control.forceActiveFocus()
+ compare(control.activeFocus, true)
+
+ control.selectAll()
+ keyPress(Qt.Key_T)
+ keyPress(Qt.Key_H)
+ keyPress(Qt.Key_I)
+ keyPress(Qt.Key_R)
+ keyPress(Qt.Key_D)
+ compare(control.count, 1)
+ compare(control.currentText, "first")
+ compare(control.editText, "third")
+
+ keyPress(Qt.Key_Enter)
+ compare(control.count, 2)
+ compare(control.currentIndex, 1)
+ compare(control.currentText, "third")
+
+ control.destroy()
+ }
+
+ function test_editable() {
+ var control = comboBox.createObject(testCase, {editable: true, model: ["Banana", "Coco", "Coconut", "Apple", "Cocomuffin"]})
+ verify(control)
+
+ control.forceActiveFocus()
+ verify(control.activeFocus)
+
+ var acceptCount = 0
+
+ var acceptSpy = signalSpy.createObject(control, {target: control, signalName: "accepted"})
+ verify(acceptSpy.valid)
+
+ compare(control.editText, "Banana")
+ compare(control.currentText, "Banana")
+ compare(control.currentIndex, 0)
+ compare(acceptSpy.count, 0)
+ control.editText = ""
+
+ keyPress(Qt.Key_C)
+ compare(control.editText, "coco")
+ compare(control.currentText, "Banana")
+ compare(control.currentIndex, 0)
+
+ keyPress(Qt.Key_Right)
+ keyPress(Qt.Key_N)
+ compare(control.editText, "coconut")
+ compare(control.currentText, "Banana")
+ compare(control.currentIndex, 0)
+
+ keyPress(Qt.Key_Enter) // Accept
+ compare(control.editText, "Coconut")
+ compare(control.currentText, "Coconut")
+ compare(control.currentIndex, 2)
+ compare(acceptSpy.count, ++acceptCount)
+
+ keyPress(Qt.Key_Backspace)
+ keyPress(Qt.Key_Backspace)
+ keyPress(Qt.Key_Backspace)
+ keyPress(Qt.Key_M)
+ compare(control.editText, "Cocomuffin")
+ compare(control.currentText, "Coconut")
+ compare(control.currentIndex, 2)
+
+ keyPress(Qt.Key_Enter) // Accept
+ compare(control.editText, "Cocomuffin")
+ compare(control.currentText, "Cocomuffin")
+ compare(control.currentIndex, 4)
+ compare(acceptSpy.count, ++acceptCount)
+
+ keyPress(Qt.Key_Return) // Accept
+ compare(control.editText, "Cocomuffin")
+ compare(control.currentText, "Cocomuffin")
+ compare(control.currentIndex, 4)
+ compare(acceptSpy.count, ++acceptCount)
+
+ control.editText = ""
+ compare(control.editText, "")
+ compare(control.currentText, "Cocomuffin")
+ compare(control.currentIndex, 4)
+
+ keyPress(Qt.Key_A)
+ compare(control.editText, "apple")
+ compare(control.currentText, "Cocomuffin")
+ compare(control.currentIndex, 4)
+
+ keyPress(Qt.Key_Return) // Accept
+ compare(control.editText, "Apple")
+ compare(control.currentText, "Apple")
+ compare(control.currentIndex, 3)
+ compare(acceptSpy.count, ++acceptCount)
+
+ control.editText = ""
+ keyPress(Qt.Key_A)
+ keyPress(Qt.Key_B)
+ compare(control.editText, "ab")
+ compare(control.currentText, "Apple")
+ compare(control.currentIndex, 3)
+
+ keyPress(Qt.Key_Return) // Accept
+ compare(control.editText, "ab")
+ compare(control.currentText, "")
+ compare(control.currentIndex, -1)
+ compare(acceptSpy.count, ++acceptCount)
+
+ control.editText = ""
+ compare(control.editText, "")
+ compare(control.currentText, "")
+ compare(control.currentIndex, -1)
+
+ keyPress(Qt.Key_C)
+ keyPress(Qt.Key_Return) // Accept
+ compare(control.editText, "Coco")
+ compare(control.currentText, "Coco")
+ compare(control.currentIndex, 1)
+ compare(acceptSpy.count, ++acceptCount)
+
+ keyPress(Qt.Key_Down)
+ compare(control.editText, "Coconut")
+ compare(control.currentText, "Coconut")
+ compare(control.currentIndex, 2)
+
+ keyPress(Qt.Key_Up)
+ compare(control.editText, "Coco")
+ compare(control.currentText, "Coco")
+ compare(control.currentIndex, 1)
+
+ control.editText = ""
+ compare(control.editText, "")
+ compare(control.currentText, "Coco")
+ compare(control.currentIndex, 1)
+
+ keyPress(Qt.Key_C)
+ keyPress(Qt.Key_O)
+ keyPress(Qt.Key_C) // autocompletes "coco"
+ keyPress(Qt.Key_Backspace)
+ keyPress(Qt.Key_Return) // Accept "coc"
+ compare(control.editText, "coc")
+ compare(control.currentText, "")
+ compare(control.currentIndex, -1)
+ compare(acceptSpy.count, ++acceptCount)
+
+ control.editText = ""
+ compare(control.editText, "")
+ compare(control.currentText, "")
+ compare(control.currentIndex, -1)
+
+ keyPress(Qt.Key_C)
+ keyPress(Qt.Key_O)
+ keyPress(Qt.Key_C) // autocompletes "coc"
+ keyPress(Qt.Key_Space)
+ keyPress(Qt.Key_Return) // Accept "coc "
+ compare(control.editText, "coc ")
+ compare(control.currentText, "")
+ compare(control.currentIndex, -1)
+ compare(acceptSpy.count, ++acceptCount)
+
+ control.destroy()
+ }
+
+ Component {
+ id: keysAttachedBox
+ ComboBox {
+ editable: true
+ property bool gotit: false
+ Keys.onPressed: {
+ if (!gotit && event.key === Qt.Key_B) {
+ gotit = true
+ event.accepted = true
+ }
+ }
+ }
+ }
+
+ function test_keys_attached() {
+ var control = keysAttachedBox.createObject(testCase)
+ verify(control)
+
+ control.forceActiveFocus()
+ verify(control.activeFocus)
+
+ verify(!control.gotit)
+ compare(control.editText, "")
+
+ keyPress(Qt.Key_A)
+ verify(control.activeFocus)
+ verify(!control.gotit)
+ compare(control.editText, "a")
+
+ keyPress(Qt.Key_B)
+ verify(control.activeFocus)
+ expectFail("", "An editable ComboBox does not yet support the Keys attached property.")
+ verify(control.gotit)
+ compare(control.editText, "a")
+
+ keyPress(Qt.Key_B)
+ verify(control.activeFocus)
+ verify(control.gotit)
+ compare(control.editText, "ab")
+
+ control.destroy()
+ }
+
+ function test_minusOneIndexResetsSelection_QTBUG_35794_data() {
+ return [
+ { tag: "editable", editable: true },
+ { tag: "non-editable", editable: false }
+ ]
+ }
+
+ function test_minusOneIndexResetsSelection_QTBUG_35794(data) {
+ var control = comboBox.createObject(testCase, {editable: data.editable, model: ["A", "B", "C"]})
+ verify(control)
+
+ compare(control.currentIndex, 0)
+ compare(control.currentText, "A")
+ control.currentIndex = -1
+ compare(control.currentIndex, -1)
+ compare(control.currentText, "")
+ control.currentIndex = 1
+ compare(control.currentIndex, 1)
+ compare(control.currentText, "B")
+
+ control.destroy()
+ }
+
+ function test_minusOneToZeroSelection_QTBUG_38036() {
+ var control = comboBox.createObject(testCase, {model: ["A", "B", "C"]})
+ verify(control)
+
+ compare(control.currentIndex, 0)
+ compare(control.currentText, "A")
+ control.currentIndex = -1
+ compare(control.currentIndex, -1)
+ compare(control.currentText, "")
+ control.currentIndex = 0
+ compare(control.currentIndex, 0)
+ compare(control.currentText, "A")
+
+ control.destroy()
+ }
}