Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickcombobox.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
12
13#include <QtCore/qregularexpression.h>
14#include <QtCore/qabstractitemmodel.h>
15#include <QtCore/qglobal.h>
16#include <QtGui/qinputmethod.h>
17#include <QtGui/qguiapplication.h>
18#include <QtGui/private/qguiapplication_p.h>
19#include <QtGui/qpa/qplatformtheme.h>
20#include <QtQml/qjsvalue.h>
21#include <QtQml/qqmlcontext.h>
22#include <QtQml/qqmlcomponent.h>
23#include <QtQml/private/qlazilyallocated_p.h>
24#include <private/qqmldelegatemodel_p.h>
25#include <QtQuick/private/qquickaccessibleattached_p.h>
26#include <QtQuick/private/qquickevents_p_p.h>
27#include <QtQuick/private/qquicktextinput_p.h>
28#include <QtQuick/private/qquicktextinput_p_p.h>
29#if QT_CONFIG(quick_itemview)
30#include <QtQuick/private/qquickitemview_p.h>
31#endif
32
34
35Q_STATIC_LOGGING_CATEGORY(lcCalculateWidestTextWidth, "qt.quick.controls.combobox.calculatewidesttextwidth")
36
37/*!
38 \qmltype ComboBox
39 \inherits Control
40//! \nativetype QQuickComboBox
41 \inqmlmodule QtQuick.Controls
42 \since 5.7
43 \ingroup qtquickcontrols-input
44 \ingroup qtquickcontrols-focusscopes
45 \brief Combined button and popup list for selecting options.
46
47 \image qtquickcontrols-combobox.gif
48 {Combo box expanding to show dropdown list}
49
50 ComboBox is a combined button and popup list. It provides a means of
51 presenting a list of options to the user in a way that takes up the
52 minimum amount of screen space.
53
54 ComboBox is populated with a data model. The data model is commonly
55 a JavaScript array, a \l ListModel or an integer, but other types
56 of \l {qml-data-models}{data models} are also supported.
57
58 \code
59 ComboBox {
60 model: ["First", "Second", "Third"]
61 }
62 \endcode
63
64 \section1 Editable ComboBox
65
66 ComboBox can be made \l editable. An editable combo box auto-completes
67 its text based on what is available in the model.
68
69 The following example demonstrates appending content to an editable
70 combo box by reacting to the \l accepted signal.
71
72 \snippet qtquickcontrols-combobox-accepted.qml combobox
73
74 \section1 ComboBox's Popup
75
76 By default, clicking outside of ComboBox's popup will close it, and the
77 event is propagated to items lower in the stacking order. To prevent the
78 popup from closing, set its \l {Popup::}{closePolicy}:
79
80 \snippet qtquickcontrols-combobox-popup.qml closePolicy
81
82 To prevent event propagation, set its \l {Popup::}{modal} property to
83 \c true:
84
85 \snippet qtquickcontrols-combobox-popup.qml modal
86
87 \section1 ComboBox Model Roles
88
89 ComboBox is able to visualize standard \l {qml-data-models}{data models}
90 that provide the \c modelData role:
91 \list
92 \li models that have only one role
93 \li models that do not have named roles (JavaScript array, integer)
94 \endlist
95
96 When using models that have multiple named roles, ComboBox must be configured
97 to use a specific \l {textRole}{text role} for its \l {displayText}{display text}
98 and \l delegate instances. If you want to use a role of the model item
99 that corresponds to the text role, set \l valueRole. The \l currentValue
100 property and \l indexOfValue() method can then be used to get information
101 about those values.
102
103 For example:
104
105 \snippet qtquickcontrols-combobox-valuerole.qml file
106
107 \note If ComboBox is assigned a data model that has multiple named roles, but
108 \l textRole is not defined, ComboBox is unable to visualize it and throws a
109 \c {ReferenceError: modelData is not defined}.
110
111 \sa {Customizing ComboBox}, {Input Controls}, {Focus Management in Qt Quick Controls}
112*/
113
114/*!
115 \qmlsignal void QtQuick.Controls::ComboBox::activated(int index)
116
117 This signal is emitted when the item at \a index is activated by the user.
118
119 An item is activated when it is selected while the popup is open,
120 causing the popup to close (and \l currentIndex to change),
121 or while the popup is closed and the combo box is navigated via
122 keyboard, causing the \l currentIndex to change.
123 The \l currentIndex property is set to \a index.
124
125 \sa currentIndex
126*/
127
128/*!
129 \qmlsignal void QtQuick.Controls::ComboBox::highlighted(int index)
130
131 This signal is emitted when the item at \a index in the popup list is highlighted by the user.
132
133 The highlighted signal is only emitted when the popup is open and an item
134 is highlighted, but not necessarily \l activated.
135
136 \sa highlightedIndex
137*/
138
139/*!
140 \since QtQuick.Controls 2.2 (Qt 5.9)
141 \qmlsignal void QtQuick.Controls::ComboBox::accepted()
142
143 This signal is emitted when the \uicontrol Return or \uicontrol Enter key is pressed
144 on an \l editable combo box.
145
146 You can handle this signal in order to add the newly entered
147 item to the model, for example:
148
149 \snippet qtquickcontrols-combobox-accepted.qml combobox
150
151 Before the signal is emitted, a check is done to see if the string
152 exists in the model. If it does, \l currentIndex will be set to its index,
153 and \l currentText to the string itself.
154
155 After the signal has been emitted, and if the first check failed (that is,
156 the item did not exist), another check will be done to see if the item was
157 added by the signal handler. If it was, the \l currentIndex and
158 \l currentText are updated accordingly. Otherwise, they will be set to
159 \c -1 and \c "", respectively.
160
161 \note If there is a \l validator set on the combo box, the signal will only be
162 emitted if the input is in an acceptable state.
163*/
164
165namespace {
168}
169
170// ### Qt7: Remove this class. Use QQmlDelegateModel instead.
172{
173public:
174 explicit QQuickComboBoxDelegateModel(QQuickComboBox *combo);
175 QVariant variantValue(int index, const QString &role) override;
176
177private:
178 QQuickComboBox *combo = nullptr;
179};
180
186
187QVariant QQuickComboBoxDelegateModel::variantValue(int index, const QString &role)
188{
189 // ### Qt7: Get rid of this. Why do we special case lists of variant maps with
190 // exactly one entry? There are many other ways of producing a list of
191 // map-like things with exactly one entry. And what if some of the maps
192 // in the list have more than one entry? You get inconsistent results.
193 if (role == QLatin1String("modelData")) {
194 const QVariant model = combo->model();
195 if (model.metaType() == QMetaType::fromType<QVariantList>()) {
196 const QVariant object = model.toList().value(index);
197 if (object.metaType() == QMetaType::fromType<QVariantMap>()) {
198 const QVariantMap data = object.toMap();
199 if (data.size() == 1)
200 return data.first();
201 }
202 }
203 }
204
205 return QQmlDelegateModel::variantValue(index, role);
206}
207
209{
210public:
211 Q_DECLARE_PUBLIC(QQuickComboBox)
212
213 /*
214 This defines which criteria is used to determine the current model element.
215 The criteria is set by the currentIndex and currentValue setters.
216 With CurrentValue the currentIndex will be set to the index of the element
217 having the same value and currentText will be set to the text of that element.
218 With CurrentIndex or None (when none is explicitly set)
219 currentText and currentValue are set to the ones of the element at currentIndex.
220 Upon model changes the two other properties (those not set) are updated accordingly.
221 User interaction (choosing an item) doesn't change this criteria.
222 This means that if we start with a model containing the values [foo, bar, baz],
223 the user selecting "bar" (at index 1) and the model then being updated by inserting
224 a new element at the beginning, the end result will depend on currentElementCriteria:
225 - CurrentIndex or None:
226 currentIndex will still be 1 but currentValue will be "foo"
227 - CurrentValue:
228 currentValue will still be "bar" but currentIndex will be 2
229
230 The different criteria also have slight behavior changes upon initialization and
231 model changes. The following table depicts how the currentIndex is set in different
232 cases depending on the model count:
233
234 | CurrentValue | CurrentIndex | None
235 ---------------------------------------------------------------------
236 componentComplete | | | 0 if not empty
237 ---------------------------------------------------------------------
238 countChanged | | -1 if empty
239 ---------------------------------------------------------------------
240 modelChanged | | -1 if empty, 0 if not empty
241
242 Note that with CurrentValue the currentValue will only be changed by user interactions,
243 not by model changes.
244 */
250
251 bool isPopupVisible() const;
252 void showPopup();
253 void hidePopup(bool accept);
254 void togglePopup(bool accept);
257
260
261 void createdItem(int index, QObject *object);
264
265 QString effectiveTextRole() const;
272
273 void onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &roles);
274
275 bool isValidIndex(int index) const;
276
278 QString tryComplete(const QString &inputText);
279
283 void setCurrentIndex(int index);
285 void setHighlightedIndex(int index, Highlighting highlight);
286
287 void keySearch(const QString &text);
288 int match(int start, const QString &text, Qt::MatchFlags flags) const;
289
291
292 bool handlePress(const QPointF &point, ulong timestamp) override;
293 bool handleMove(const QPointF &point, ulong timestamp) override;
294 bool handleRelease(const QPointF &point, ulong timestamp) override;
296
298 void executeIndicator(bool complete = false);
299
301 void executePopup(bool complete = false);
302
303 void itemImplicitWidthChanged(QQuickItem *item) override;
304 void itemImplicitHeightChanged(QQuickItem *item) override;
305 void itemDestroyed(QQuickItem *item) override;
306
307 void setInputMethodHints(Qt::InputMethodHints hints, bool force = false);
308
309 virtual qreal getContentWidth() const override;
312
313 static void hideOldPopup(QQuickPopup *popup);
314
315 QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::ComboBox); }
316
317 bool flat = false;
318 bool down = false;
319 bool hasDown = false;
320 bool pressed = false;
321 bool ownModel = false;
322 bool keyNavigating = false;
323 bool hasDisplayText = false;
327 int currentIndex = -1;
331 QString textRole;
332 QString currentText;
333 QString displayText;
334 QString valueRole;
337 QQmlInstanceModel *delegateModel = nullptr;
338 QQmlComponent *delegate = nullptr;
341 bool m_acceptableInput = true;
344 bool highlightOnHover = true;
345
346 struct ExtraData {
347 bool editable = false;
348 bool accepting = false;
349 bool allowComplete = false;
350 bool selectTextByMouse = false;
352 QString editText;
353#if QT_CONFIG(validator)
354 QValidator *validator = nullptr;
355#endif
356 };
358};
359
361{
362 return popup && popup->isVisible();
363}
364
366{
367 if (!popup)
368 executePopup(true);
369
370 if (popup && !popup->isVisible())
371 popup->open();
372}
373
375{
376 Q_Q(QQuickComboBox);
377 if (accept) {
379 // hiding the popup on user interaction should always emit activated,
380 // even if the current index didn't change
381 emit q->activated(highlightedIndex);
382 }
383 if (popup && popup->isVisible())
384 popup->close();
385}
386
388{
389 if (!popup || !popup->isVisible())
391 else
392 hidePopup(accept);
393}
394
396{
397 Q_Q(QQuickComboBox);
398 if (isPopupVisible())
399 QGuiApplication::inputMethod()->reset();
400
401#if QT_CONFIG(quick_itemview)
402 QQuickItemView *itemView = popup->findChild<QQuickItemView *>();
403 if (itemView)
404 itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
405#endif
406
408
409#if QT_CONFIG(quick_itemview)
410 if (itemView)
411 itemView->positionViewAtIndex(highlightedIndex, QQuickItemView::Beginning);
412#endif
413
414 if (!hasDown) {
415 q->setDown(pressed || isPopupVisible());
416 hasDown = false;
417 }
418}
419
421{
422 Q_Q(QQuickComboBox);
423 popup = nullptr;
424 emit q->popupChanged();
425}
426
428{
429 Q_Q(QQuickComboBox);
430 int index = delegateModel->indexOf(q->sender(), nullptr);
431 if (index != -1) {
433 hidePopup(true);
434 }
435}
436
438{
439 Q_Q(QQuickComboBox);
440 if (keyNavigating)
441 return;
442
443 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(q->sender());
444 if (!button || !button->isHovered() || !button->isEnabled() || QQuickAbstractButtonPrivate::get(button)->touchId != -1)
445 return;
446
447 int index = delegateModel->indexOf(button, nullptr);
448 if (index != -1) {
451
452#if QT_CONFIG(quick_itemview)
453 if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
454 itemView->positionViewAtIndex(index, QQuickItemView::Contain);
455#endif
456 }
457}
458
459void QQuickComboBoxPrivate::createdItem(int index, QObject *object)
460{
461 Q_Q(QQuickComboBox);
462 QQuickItem *item = qobject_cast<QQuickItem *>(object);
463 if (item && !item->parentItem()) {
464 if (popup)
465 item->setParentItem(popup->contentItem());
466 else
467 item->setParentItem(q);
468 QQuickItemPrivate::get(item)->setCulled(true);
469 }
470
471 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object);
472 if (button) {
473 button->setFocusPolicy(Qt::NoFocus);
474 connect(button, &QQuickAbstractButton::clicked, this, &QQuickComboBoxPrivate::itemClicked);
475 connect(button, &QQuickAbstractButton::hoveredChanged, this, &QQuickComboBoxPrivate::itemHovered);
476 }
477
478 if (index == currentIndex && !q->isEditable())
480}
481
483{
484 if (componentComplete && (!extra.isAllocated() || !extra->accepting)) {
486
487 if (implicitContentWidthPolicy == QQuickComboBox::WidestText)
488 updateImplicitContentSize();
489 }
490}
491
493{
494 Q_Q(QQuickComboBox);
495 if (q->count() == 0) {
496 if (currentElementCriteria == CurrentElementCriteria::CurrentValue)
498 else
500 }
501 emit q->countChanged();
502}
503
505{
506 return textRole.isEmpty() ? QStringLiteral("modelData") : textRole;
507}
508
510{
511 Q_Q(QQuickComboBox);
512 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(contentItem);
513 if (!input)
514 return;
515
516 const QString text = input->text();
517
518 if (extra.isAllocated() && extra->allowComplete && !text.isEmpty()) {
519 const QString completed = tryComplete(text);
520 if (completed.size() > text.size()) {
521 input->setText(completed);
522 // This will select the text backwards.
523 input->select(completed.size(), text.size());
524 return;
525 }
526 }
527 q->setEditText(text);
528}
529
531{
532 Q_Q(QQuickComboBox);
533 const int index = q->indexOfValue(currentValue);
534 if (currentIndex == index)
535 return;
536
537 currentIndex = index;
538 emit q->currentIndexChanged();
539}
540
542{
543 Q_Q(QQuickComboBox);
544 const QString text = q->textAt(currentIndex);
545 if (currentText != text) {
546 currentText = text;
547 if (!hasDisplayText)
548 q->maybeSetAccessibleName(text);
549 emit q->currentTextChanged();
550 }
551 if (!hasDisplayText && displayText != text) {
552 displayText = text;
553 emit q->displayTextChanged();
554 }
555 if (!extra.isAllocated() || !extra->accepting)
556 q->setEditText(currentText);
557}
558
560{
561 Q_Q(QQuickComboBox);
562 const QVariant value = q->valueAt(currentIndex);
563 if (currentValue == value)
564 return;
565
566 currentValue = value;
567 emit q->currentValueChanged();
568}
569
571{
572 switch (currentElementCriteria) {
573 case CurrentElementCriteria::None:
574 Q_FALLTHROUGH();
575 case CurrentElementCriteria::CurrentIndex:
578 break;
579 case CurrentElementCriteria::CurrentValue:
582 break;
583 }
584}
585
587{
588 Q_Q(QQuickComboBox);
589
590 if (!contentItem)
591 return;
592
593 const QQuickTextInput *textInputContentItem = qobject_cast<QQuickTextInput *>(contentItem);
594
595 if (!textInputContentItem)
596 return;
597
598 const bool newValue = textInputContentItem->hasAcceptableInput();
599
600 if (m_acceptableInput != newValue) {
601 m_acceptableInput = newValue;
602 emit q->acceptableInputChanged();
603 }
604}
605
606void QQuickComboBoxPrivate::onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList<int> &)
607{
608 const bool currentIndexIsInChangedRange = currentIndex >= topLeft.row() && currentIndex <= bottomRight.row();
609 if (currentIndex == -1 || currentIndexIsInChangedRange)
611}
612
614{
615 return delegateModel && index >= 0 && index < delegateModel->count();
616}
617
619{
620 Q_Q(QQuickComboBox);
621 int idx = q->find(extra.value().editText, Qt::MatchFixedString);
622 if (idx > -1) {
623 // The item that was accepted already exists, so make it the current item.
625 // After accepting text that matches an existing entry, the selection should be cleared.
626 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(contentItem);
627 if (input) {
628 const auto text = input->text();
629 input->select(text.size(), text.size());
630 }
631 }
632
633 extra.value().accepting = true;
634 emit q->accepted();
635
636 // The user might have added the item since it didn't exist, so check again
637 // to see if we can select that new item.
638 if (idx == -1) {
639 idx = q->find(extra.value().editText, Qt::MatchFixedString);
641 }
642 extra.value().accepting = false;
643}
644
645QString QQuickComboBoxPrivate::tryComplete(const QString &input)
646{
647 Q_Q(QQuickComboBox);
648 QString match;
649
650 const int itemCount = q->count();
651 for (int idx = 0; idx < itemCount; ++idx) {
652 const QString text = q->textAt(idx);
653 if (!text.startsWith(input, Qt::CaseInsensitive))
654 continue;
655
656 // either the first or the shortest match
657 if (match.isEmpty() || text.size() < match.size())
658 match = text;
659 }
660
661 if (match.isEmpty())
662 return input;
663
664 return input + match.mid(input.size());
665}
666
668{
669 Q_Q(QQuickComboBox);
670 if (currentIndex == index)
671 return;
672
673 currentIndex = index;
674 emit q->currentIndexChanged();
675
676 if (componentComplete)
678}
679
681{
682 Q_Q(QQuickComboBox);
683 if (currentIndex == index)
684 return;
685
686 currentIndex = index;
687 emit q->currentIndexChanged();
688
691
692 if (activate)
693 emit q->activated(index);
694}
695
697{
698 Q_Q(QQuickComboBox);
699 if (extra.isAllocated())
700 extra->allowComplete = false;
701 if (isPopupVisible()) {
702 if (highlightedIndex < q->count() - 1)
704 } else {
705 if (currentIndex < q->count() - 1)
707 }
708 if (extra.isAllocated())
709 extra->allowComplete = true;
710}
711
713{
714 if (extra.isAllocated())
715 extra->allowComplete = false;
716 if (isPopupVisible()) {
717 if (highlightedIndex > 0)
719 } else {
720 if (currentIndex > 0)
722 }
723 if (extra.isAllocated())
724 extra->allowComplete = true;
725}
726
728{
729 setHighlightedIndex(popup->isVisible() ? currentIndex : -1, NoHighlight);
730}
731
733{
734 Q_Q(QQuickComboBox);
735 if (highlightedIndex == index)
736 return;
737
738 highlightedIndex = index;
739 emit q->highlightedIndexChanged();
740
741 if (highlight)
742 emit q->highlighted(index);
743}
744
745void QQuickComboBoxPrivate::keySearch(const QString &text)
746{
747 const int startIndex = isPopupVisible() ? highlightedIndex : currentIndex;
748 const int index = match(startIndex + 1, text, Qt::MatchStartsWith | Qt::MatchWrap);
749 if (index != -1) {
752 else
754 }
755}
756
757int QQuickComboBoxPrivate::match(int start, const QString &text, Qt::MatchFlags flags) const
758{
759 Q_Q(const QQuickComboBox);
760 uint matchType = flags & 0x0F;
761 bool wrap = flags & Qt::MatchWrap;
762 Qt::CaseSensitivity cs = flags & Qt::MatchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
763 QRegularExpression::PatternOptions options = flags & Qt::MatchCaseSensitive ? QRegularExpression::NoPatternOption
764 : QRegularExpression::CaseInsensitiveOption;
765 int from = start;
766 int to = q->count();
767
768 // iterates twice if wrapping
769 for (int i = 0; (wrap && i < 2) || (!wrap && i < 1); ++i) {
770 for (int idx = from; idx < to; ++idx) {
771 QString t = q->textAt(idx);
772 switch (matchType) {
773 case Qt::MatchExactly:
774 if (t == text)
775 return idx;
776 break;
777 case Qt::MatchRegularExpression: {
778 QRegularExpression rx(QRegularExpression::anchoredPattern(text), options);
779 if (rx.match(t).hasMatch())
780 return idx;
781 break;
782 }
783 case Qt::MatchWildcard: {
784 QRegularExpression rx(QRegularExpression::wildcardToRegularExpression(text),
785 options);
786 if (rx.match(t).hasMatch())
787 return idx;
788 break;
789 }
790 case Qt::MatchStartsWith:
791 if (t.startsWith(text, cs))
792 return idx;
793 break;
794 case Qt::MatchEndsWith:
795 if (t.endsWith(text, cs))
796 return idx;
797 break;
798 case Qt::MatchFixedString:
799 if (t.compare(text, cs) == 0)
800 return idx;
801 break;
802 case Qt::MatchContains:
803 default:
804 if (t.contains(text, cs))
805 return idx;
806 break;
807 }
808 }
809 // prepare for the next iteration
810 from = 0;
811 to = start;
812 }
813 return -1;
814}
815
817{
818 Q_Q(QQuickComboBox);
819 bool ownedOldModel = ownModel;
820 QQmlInstanceModel* oldModel = delegateModel;
821 if (oldModel) {
822 disconnect(delegateModel, &QQmlInstanceModel::countChanged, this, &QQuickComboBoxPrivate::countChanged);
823 disconnect(delegateModel, &QQmlInstanceModel::modelUpdated, this, &QQuickComboBoxPrivate::modelUpdated);
824 disconnect(delegateModel, &QQmlInstanceModel::createdItem, this, &QQuickComboBoxPrivate::createdItem);
825 }
826
827 ownModel = false;
828 delegateModel = model.value<QQmlInstanceModel *>();
829
830 if (!delegateModel && model.isValid()) {
831 QQmlDelegateModel *dataModel = new QQuickComboBoxDelegateModel(q);
832 dataModel->setModel(model);
833 dataModel->setDelegate(delegate);
834 if (q->isComponentComplete())
835 dataModel->componentComplete();
836
837 ownModel = true;
838 delegateModel = dataModel;
839 }
840
841 if (delegateModel) {
842 connect(delegateModel, &QQmlInstanceModel::countChanged, this, &QQuickComboBoxPrivate::countChanged);
843 connect(delegateModel, &QQmlInstanceModel::modelUpdated, this, &QQuickComboBoxPrivate::modelUpdated);
844 connect(delegateModel, &QQmlInstanceModel::createdItem, this, &QQuickComboBoxPrivate::createdItem);
845 }
846
847 emit q->delegateModelChanged();
848
849 if (ownedOldModel)
850 delete oldModel;
851}
852
853bool QQuickComboBoxPrivate::handlePress(const QPointF &point, ulong timestamp)
854{
855 Q_Q(QQuickComboBox);
856 QQuickControlPrivate::handlePress(point, timestamp);
857 q->setPressed(true);
858 return true;
859}
860
861bool QQuickComboBoxPrivate::handleMove(const QPointF &point, ulong timestamp)
862{
863 Q_Q(QQuickComboBox);
864 QQuickControlPrivate::handleMove(point, timestamp);
865 q->setPressed(q->contains(point));
866 return true;
867}
868
869bool QQuickComboBoxPrivate::handleRelease(const QPointF &point, ulong timestamp)
870{
871 Q_Q(QQuickComboBox);
872 QQuickControlPrivate::handleRelease(point, timestamp);
873 if (pressed) {
874 q->setPressed(false);
875 togglePopup(false);
876 }
877 return true;
878}
879
881{
882 Q_Q(QQuickComboBox);
883 QQuickControlPrivate::handleUngrab();
884 q->setPressed(false);
885}
886
888{
889 Q_Q(QQuickComboBox);
890 quickCancelDeferred(q, indicatorName());
891}
892
894{
895 Q_Q(QQuickComboBox);
896 if (indicator.wasExecuted())
897 return;
898
899 if (!indicator || complete)
900 quickBeginDeferred(q, indicatorName(), indicator);
901 if (complete)
902 quickCompleteDeferred(q, indicatorName(), indicator);
903}
904
905static inline QString popupName() { return QStringLiteral("popup"); }
906
908{
909 Q_Q(QQuickComboBox);
910 quickCancelDeferred(q, popupName());
911}
912
914{
915 Q_Q(QQuickComboBox);
916 if (popup.wasExecuted())
917 return;
918
919 if (!popup || complete)
920 quickBeginDeferred(q, popupName(), popup);
921 if (complete)
922 quickCompleteDeferred(q, popupName(), popup);
923}
924
926{
927 Q_Q(QQuickComboBox);
928 QQuickControlPrivate::itemImplicitWidthChanged(item);
929 if (item == indicator)
930 emit q->implicitIndicatorWidthChanged();
931}
932
933void QQuickComboBoxPrivate::setInputMethodHints(Qt::InputMethodHints hints, bool force)
934{
935 Q_Q(QQuickComboBox);
936 if (!force && hints == q->inputMethodHints())
937 return;
938
939 extra.value().inputMethodHints = hints;
940 emit q->inputMethodHintsChanged();
941}
942
944{
945 Q_Q(QQuickComboBox);
946 QQuickControlPrivate::itemImplicitHeightChanged(item);
947 if (item == indicator)
948 emit q->implicitIndicatorHeightChanged();
949}
950
952{
953 Q_Q(QQuickComboBox);
954 QQuickControlPrivate::itemDestroyed(item);
955 if (item == indicator) {
956 indicator = nullptr;
957 emit q->indicatorChanged();
958 }
959}
960
962{
963 if (componentComplete) {
964 switch (implicitContentWidthPolicy) {
965 case QQuickComboBox::WidestText:
966 return calculateWidestTextWidth();
967 case QQuickComboBox::WidestTextWhenCompleted:
968 if (!hasCalculatedWidestText)
969 return calculateWidestTextWidth();
970 break;
971 default:
972 break;
973 }
974 }
975
976 return QQuickControlPrivate::getContentWidth();
977}
978
980{
981 Q_Q(const QQuickComboBox);
982 if (!componentComplete)
983 return 0;
984
985 const int count = q->count();
986 if (count == 0)
987 return 0;
988
989 auto textInput = qobject_cast<QQuickTextInput*>(contentItem);
990 if (!textInput)
991 return 0;
992
993 qCDebug(lcCalculateWidestTextWidth) << "calculating widest text from" << count << "items...";
994
995 // Avoid the index check and repeated calls to effectiveTextRole()
996 // that would result from calling textAt() in a loop.
997 const QString textRole = effectiveTextRole();
998 auto textInputPrivate = QQuickTextInputPrivate::get(textInput);
999 qreal widest = 0;
1000 for (int i = 0; i < count; ++i) {
1001 const QString text = delegateModel->stringValue(i, textRole);
1002 const qreal textImplicitWidth = textInputPrivate->calculateImplicitWidthForText(text);
1003 widest = qMax(widest, textImplicitWidth);
1004 }
1005
1006 qCDebug(lcCalculateWidestTextWidth) << "... widest text is" << widest;
1007 return widest;
1008}
1009
1010/*!
1011 \internal
1012
1013 If the user requested it (and we haven't already done it, depending on the policy),
1014 update the implicit content width to the largest text in the model.
1015*/
1017{
1018 if (!componentComplete)
1019 return;
1020
1021 if (implicitContentWidthPolicy == QQuickComboBox::ContentItemImplicitWidth
1022 || (implicitContentWidthPolicy == QQuickComboBox::WidestTextWhenCompleted && hasCalculatedWidestText))
1023 return;
1024
1025 updateImplicitContentWidth();
1027}
1028
1029void QQuickComboBoxPrivate::hideOldPopup(QQuickPopup *popup)
1030{
1031 if (!popup)
1032 return;
1033
1034 qCDebug(lcItemManagement) << "hiding old popup" << popup;
1035
1036 popup->setVisible(false);
1037 popup->setParentItem(nullptr);
1038#if QT_CONFIG(accessibility)
1039 // Remove the item from the accessibility tree.
1040 QQuickAccessibleAttached *accessible = accessibleAttached(popup);
1041 if (accessible)
1042 accessible->setIgnored(true);
1043#endif
1044}
1045
1046QQuickComboBox::QQuickComboBox(QQuickItem *parent)
1047 : QQuickControl(*(new QQuickComboBoxPrivate), parent)
1048{
1049 setFocusPolicy(Qt::StrongFocus);
1050 setFlag(QQuickItem::ItemIsFocusScope);
1051 setAcceptedMouseButtons(Qt::LeftButton);
1052#if QT_CONFIG(cursor)
1053 setCursor(Qt::ArrowCursor);
1054#endif
1055 Q_D(QQuickComboBox);
1056 d->setInputMethodHints(Qt::ImhNoPredictiveText, true);
1057 d->setSizePolicy(QLayoutPolicy::Preferred, QLayoutPolicy::Fixed);
1058}
1059
1060QQuickComboBox::~QQuickComboBox()
1061{
1062 Q_D(QQuickComboBox);
1063 d->removeImplicitSizeListener(d->indicator);
1064 if (d->popup) {
1065 // Disconnect visibleChanged() to avoid a spurious highlightedIndexChanged() signal
1066 // emission during the destruction of the (visible) popup. (QTBUG-57650)
1067 QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::popupVisibleChanged);
1068 QQuickComboBoxPrivate::hideOldPopup(d->popup);
1069 d->popup = nullptr;
1070 }
1071}
1072
1073/*!
1074 \readonly
1075 \qmlproperty int QtQuick.Controls::ComboBox::count
1076
1077 This property holds the number of items in the combo box.
1078*/
1079int QQuickComboBox::count() const
1080{
1081 Q_D(const QQuickComboBox);
1082 return d->delegateModel ? d->delegateModel->count() : 0;
1083}
1084
1085/*!
1086 \qmlproperty model QtQuick.Controls::ComboBox::model
1087
1088 This property holds the model providing data for the combo box.
1089
1090 \code
1091 ComboBox {
1092 textRole: "key"
1093 model: ListModel {
1094 ListElement { key: "First"; value: 123 }
1095 ListElement { key: "Second"; value: 456 }
1096 ListElement { key: "Third"; value: 789 }
1097 }
1098 }
1099 \endcode
1100
1101 \sa textRole, {qml-data-models}{Data Models}
1102*/
1103QVariant QQuickComboBox::model() const
1104{
1105 Q_D(const QQuickComboBox);
1106 if (!d->qobjectModelGuard.has_value()) {
1107 // It's not a QObject-derived model; return it as-is.
1108 return d->model;
1109 }
1110
1111 // It is a QObject-derived model; only return it if it's still alive.
1112 return !d->qobjectModelGuard->isNull() ? d->model : QVariant();
1113}
1114
1115void QQuickComboBox::setModel(const QVariant& m)
1116{
1117 Q_D(QQuickComboBox);
1118 QVariant newModel = m;
1119 if (newModel.userType() == qMetaTypeId<QJSValue>())
1120 newModel = newModel.value<QJSValue>().toVariant();
1121
1122 if (d->model == newModel)
1123 return;
1124
1125 if (d->qobjectModelGuard.has_value() && !d->qobjectModelGuard->isNull()) {
1126 if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(d->model)) {
1127 QObjectPrivate::disconnect(aim, &QAbstractItemModel::dataChanged,
1128 d, &QQuickComboBoxPrivate::onDataChanged);
1129 }
1130 }
1131 if (QObject* newModelAsQObject = qvariant_cast<QObject *>(newModel)) {
1132 // Ensure that if the model is destroyed, we don't try to access it.
1133 // We store it in std::optional because model() needs to return the model
1134 // as-is if a non-QObject model is contained within it.
1135 d->qobjectModelGuard = QPointer<QObject>(newModelAsQObject);
1136
1137 if (QAbstractItemModel* aim = qvariant_cast<QAbstractItemModel *>(newModel)) {
1138 QObjectPrivate::connect(aim, &QAbstractItemModel::dataChanged,
1139 d, &QQuickComboBoxPrivate::onDataChanged);
1140 }
1141 } else {
1142 // It's not a QObject, so we don't need to worry about tracking its lifetime.
1143 d->qobjectModelGuard.reset();
1144 }
1145
1146 d->model = newModel;
1147 d->createDelegateModel();
1148 emit countChanged();
1149 if (isComponentComplete()) {
1150 if (d->currentElementCriteria != QQuickComboBoxPrivate::CurrentElementCriteria::CurrentValue)
1151 d->setCurrentIndex(count() > 0 ? 0 : -1);
1152 d->updateCurrentElements();
1153 }
1154 emit modelChanged();
1155
1156 d->maybeUpdateImplicitContentWidth();
1157}
1158
1159/*!
1160 \readonly
1161 \qmlproperty model QtQuick.Controls::ComboBox::delegateModel
1162
1163 This property holds the model that provides delegate instances for the combo box.
1164
1165 It is typically assigned to a \l ListView in the \l {Popup::}{contentItem}
1166 of the \l popup.
1167
1168 \sa {Customizing ComboBox}
1169*/
1170QQmlInstanceModel *QQuickComboBox::delegateModel() const
1171{
1172 Q_D(const QQuickComboBox);
1173 return d->delegateModel;
1174}
1175
1176
1177/*!
1178 \readonly
1179 \qmlproperty bool QtQuick.Controls::ComboBox::pressed
1180
1181 This property holds whether the combo box button is physically pressed.
1182 A button can be pressed by either touch or key events.
1183
1184 \sa down
1185*/
1186bool QQuickComboBox::isPressed() const
1187{
1188 Q_D(const QQuickComboBox);
1189 return d->pressed;
1190}
1191
1192void QQuickComboBox::setPressed(bool pressed)
1193{
1194 Q_D(QQuickComboBox);
1195 if (d->pressed == pressed)
1196 return;
1197
1198 d->pressed = pressed;
1199 emit pressedChanged();
1200
1201 if (!d->hasDown) {
1202 setDown(d->pressed || d->isPopupVisible());
1203 d->hasDown = false;
1204 }
1205}
1206
1207/*!
1208 \readonly
1209 \qmlproperty int QtQuick.Controls::ComboBox::highlightedIndex
1210
1211 This property holds the index of the highlighted item in the combo box popup list.
1212
1213 When a highlighted item is activated, the popup is closed, \l currentIndex
1214 is set to \c highlightedIndex, and the value of this property is reset to
1215 \c -1, as there is no longer a highlighted item.
1216
1217 \sa highlighted(), currentIndex
1218*/
1219int QQuickComboBox::highlightedIndex() const
1220{
1221 Q_D(const QQuickComboBox);
1222 return d->highlightedIndex;
1223}
1224
1225/*!
1226 \qmlproperty int QtQuick.Controls::ComboBox::currentIndex
1227
1228 This property holds the index of the current item in the combo box.
1229
1230 The default value is \c -1 when \l count is \c 0, and \c 0 otherwise.
1231
1232 \sa activated(), currentText, highlightedIndex
1233*/
1234int QQuickComboBox::currentIndex() const
1235{
1236 Q_D(const QQuickComboBox);
1237 return d->currentIndex;
1238}
1239
1240void QQuickComboBox::setCurrentIndex(int index)
1241{
1242 Q_D(QQuickComboBox);
1243 d->currentElementCriteria = QQuickComboBoxPrivate::CurrentElementCriteria::CurrentIndex;
1244 d->setCurrentIndex(index);
1245}
1246
1247/*!
1248 \readonly
1249 \qmlproperty string QtQuick.Controls::ComboBox::currentText
1250
1251 This property holds the text of the current item in the combo box.
1252
1253 \sa currentIndex, displayText, textRole, editText
1254*/
1255QString QQuickComboBox::currentText() const
1256{
1257 Q_D(const QQuickComboBox);
1258 return d->currentText;
1259}
1260
1261/*!
1262 \qmlproperty string QtQuick.Controls::ComboBox::displayText
1263
1264 This property holds the text that is displayed on the combo box button.
1265
1266 By default, the display text presents the current selection. That is,
1267 it follows the text of the current item. However, the default display
1268 text can be overridden with a custom value.
1269
1270 \code
1271 ComboBox {
1272 currentIndex: 1
1273 displayText: "Size: " + currentText
1274 model: ["S", "M", "L"]
1275 }
1276 \endcode
1277
1278 \sa currentText, textRole
1279*/
1280QString QQuickComboBox::displayText() const
1281{
1282 Q_D(const QQuickComboBox);
1283 return d->displayText;
1284}
1285
1286void QQuickComboBox::setDisplayText(const QString &text)
1287{
1288 Q_D(QQuickComboBox);
1289 d->hasDisplayText = true;
1290 if (d->displayText == text)
1291 return;
1292
1293 d->displayText = text;
1294 maybeSetAccessibleName(text);
1295 emit displayTextChanged();
1296}
1297
1298void QQuickComboBox::resetDisplayText()
1299{
1300 Q_D(QQuickComboBox);
1301 if (!d->hasDisplayText)
1302 return;
1303
1304 d->hasDisplayText = false;
1305 d->updateCurrentText();
1306}
1307
1308
1309/*!
1310 \qmlproperty string QtQuick.Controls::ComboBox::textRole
1311
1312 This property holds the model role used for populating the combo box.
1313
1314 When the model has multiple roles, \c textRole can be set to determine
1315 which role should be displayed.
1316
1317 \sa model, currentText, displayText, {ComboBox Model Roles}
1318*/
1319QString QQuickComboBox::textRole() const
1320{
1321 Q_D(const QQuickComboBox);
1322 return d->textRole;
1323}
1324
1325void QQuickComboBox::setTextRole(const QString &role)
1326{
1327 Q_D(QQuickComboBox);
1328 if (d->textRole == role)
1329 return;
1330
1331 d->textRole = role;
1332 if (isComponentComplete())
1333 d->updateCurrentText();
1334 emit textRoleChanged();
1335}
1336
1337/*!
1338 \since QtQuick.Controls 2.14 (Qt 5.14)
1339 \qmlproperty string QtQuick.Controls::ComboBox::valueRole
1340
1341 This property holds the model role used for storing the value associated
1342 with each item in the model.
1343
1344 For an example of how to use this property, see \l {ComboBox Model Roles}.
1345
1346 \sa model, currentValue
1347*/
1348QString QQuickComboBox::valueRole() const
1349{
1350 Q_D(const QQuickComboBox);
1351 return d->valueRole;
1352}
1353
1354void QQuickComboBox::setValueRole(const QString &role)
1355{
1356 Q_D(QQuickComboBox);
1357 if (d->valueRole == role)
1358 return;
1359
1360 d->valueRole = role;
1361 if (isComponentComplete())
1362 d->updateCurrentElements();
1363 emit valueRoleChanged();
1364}
1365
1366/*!
1367 \qmlproperty Component QtQuick.Controls::ComboBox::delegate
1368
1369 This property holds a delegate that presents an item in the combo box popup.
1370
1371 It is recommended to use \l ItemDelegate (or any other \l AbstractButton
1372 derivatives) as the delegate. This ensures that the interaction works as
1373 expected, and the popup will automatically close when appropriate. When
1374 other types are used as the delegate, the popup must be closed manually.
1375 For example, if \l MouseArea is used:
1376
1377 \code
1378 delegate: Rectangle {
1379 // ...
1380 MouseArea {
1381 // ...
1382 onClicked: comboBox.popup.close()
1383 }
1384 }
1385 \endcode
1386
1387 \include delegate-ownership.qdocinc {no-ownership-since-6.11} {ComboBox}
1388
1389 \sa ItemDelegate, {Customizing ComboBox}
1390*/
1391QQmlComponent *QQuickComboBox::delegate() const
1392{
1393 Q_D(const QQuickComboBox);
1394 return d->delegate;
1395}
1396
1397void QQuickComboBox::setDelegate(QQmlComponent* delegate)
1398{
1399 Q_D(QQuickComboBox);
1400 if (d->delegate == delegate)
1401 return;
1402
1403 d->delegate = delegate;
1404 QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel*>(d->delegateModel);
1405 if (delegateModel)
1406 delegateModel->setDelegate(d->delegate);
1407 emit delegateChanged();
1408}
1409
1410/*!
1411 \qmlproperty Item QtQuick.Controls::ComboBox::indicator
1412
1413 This property holds the drop indicator item.
1414
1415 \sa {Customizing ComboBox}
1416*/
1417QQuickItem *QQuickComboBox::indicator() const
1418{
1419 QQuickComboBoxPrivate *d = const_cast<QQuickComboBoxPrivate *>(d_func());
1420 if (!d->indicator)
1421 d->executeIndicator();
1422 return d->indicator;
1423}
1424
1425void QQuickComboBox::setIndicator(QQuickItem *indicator)
1426{
1427 Q_D(QQuickComboBox);
1428 if (d->indicator == indicator)
1429 return;
1430
1431 QQuickControlPrivate::warnIfCustomizationNotSupported(this, indicator, QStringLiteral("indicator"));
1432
1433 if (!d->indicator.isExecuting())
1434 d->cancelIndicator();
1435
1436 const qreal oldImplicitIndicatorWidth = implicitIndicatorWidth();
1437 const qreal oldImplicitIndicatorHeight = implicitIndicatorHeight();
1438
1439 d->removeImplicitSizeListener(d->indicator);
1440 QQuickControlPrivate::hideOldItem(d->indicator);
1441 d->indicator = indicator;
1442 if (indicator) {
1443 if (!indicator->parentItem())
1444 indicator->setParentItem(this);
1445 d->addImplicitSizeListener(indicator);
1446 }
1447
1448 if (!qFuzzyCompare(oldImplicitIndicatorWidth, implicitIndicatorWidth()))
1449 emit implicitIndicatorWidthChanged();
1450 if (!qFuzzyCompare(oldImplicitIndicatorHeight, implicitIndicatorHeight()))
1451 emit implicitIndicatorHeightChanged();
1452 if (!d->indicator.isExecuting())
1453 emit indicatorChanged();
1454}
1455
1456/*!
1457 \qmlproperty Popup QtQuick.Controls::ComboBox::popup
1458
1459 This property holds the popup.
1460
1461 The popup can be opened or closed manually, if necessary:
1462
1463 \code
1464 onSpecialEvent: comboBox.popup.close()
1465 \endcode
1466
1467 \sa {Customizing ComboBox}
1468*/
1469QQuickPopup *QQuickComboBox::popup() const
1470{
1471 QQuickComboBoxPrivate *d = const_cast<QQuickComboBoxPrivate *>(d_func());
1472 if (!d->popup)
1473 d->executePopup(isComponentComplete());
1474 return d->popup;
1475}
1476
1477void QQuickComboBox::setPopup(QQuickPopup *popup)
1478{
1479 Q_D(QQuickComboBox);
1480 if (d->popup == popup)
1481 return;
1482
1483 if (!d->popup.isExecuting())
1484 d->cancelPopup();
1485
1486 if (d->popup) {
1487 QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::destroyed, d, &QQuickComboBoxPrivate::popupDestroyed);
1488 QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::popupVisibleChanged);
1489 QQuickComboBoxPrivate::hideOldPopup(d->popup);
1490 }
1491 if (popup) {
1492#if QT_CONFIG(wayland)
1493 QQuickPopupPrivate::get(popup)->extendedWindowType = QNativeInterface::Private::QWaylandWindow::ComboBox;
1494#endif
1495#if QT_CONFIG(xcb)
1496 QQuickPopupPrivate::get(popup)->wmWindowType = QNativeInterface::Private::QXcbWindow::Combo;
1497#endif
1498 QQuickPopupPrivate::get(popup)->allowVerticalFlip = true;
1499 popup->setClosePolicy(QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutsideParent);
1500 QObjectPrivate::connect(popup, &QQuickPopup::visibleChanged, d, &QQuickComboBoxPrivate::popupVisibleChanged);
1501 // QQuickPopup does not derive from QQuickItemChangeListener, so we cannot use
1502 // QQuickItemChangeListener::itemDestroyed so we have to use QObject::destroyed
1503 QObjectPrivate::connect(popup, &QQuickPopup::destroyed, d, &QQuickComboBoxPrivate::popupDestroyed);
1504
1505#if QT_CONFIG(quick_itemview)
1506 if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
1507 itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
1508#endif
1509 }
1510 d->popup = popup;
1511 if (!d->popup.isExecuting())
1512 emit popupChanged();
1513}
1514
1515/*!
1516 \since QtQuick.Controls 2.1 (Qt 5.8)
1517 \qmlproperty bool QtQuick.Controls::ComboBox::flat
1518
1519 This property holds whether the combo box button is flat.
1520
1521 A flat combo box button does not draw a background unless it is interacted
1522 with. In comparison to normal combo boxes, flat combo boxes provide looks
1523 that make them stand out less from the rest of the UI. For instance, when
1524 placing a combo box into a tool bar, it may be desirable to make the combo
1525 box flat so it matches better with the flat looks of tool buttons.
1526
1527 The default value is \c false.
1528*/
1529bool QQuickComboBox::isFlat() const
1530{
1531 Q_D(const QQuickComboBox);
1532 return d->flat;
1533}
1534
1535void QQuickComboBox::setFlat(bool flat)
1536{
1537 Q_D(QQuickComboBox);
1538 if (d->flat == flat)
1539 return;
1540
1541 d->flat = flat;
1542 emit flatChanged();
1543}
1544
1545/*!
1546 \since QtQuick.Controls 2.2 (Qt 5.9)
1547 \qmlproperty bool QtQuick.Controls::ComboBox::down
1548
1549 This property holds whether the combo box button is visually down.
1550
1551 Unless explicitly set, this property is \c true when either \c pressed
1552 or \c popup.visible is \c true. To return to the default value, set this
1553 property to \c undefined.
1554
1555 \sa pressed, popup
1556*/
1557bool QQuickComboBox::isDown() const
1558{
1559 Q_D(const QQuickComboBox);
1560 return d->down;
1561}
1562
1563void QQuickComboBox::setDown(bool down)
1564{
1565 Q_D(QQuickComboBox);
1566 d->hasDown = true;
1567
1568 if (d->down == down)
1569 return;
1570
1571 d->down = down;
1572 emit downChanged();
1573}
1574
1575void QQuickComboBox::resetDown()
1576{
1577 Q_D(QQuickComboBox);
1578 if (!d->hasDown)
1579 return;
1580
1581 setDown(d->pressed || d->isPopupVisible());
1582 d->hasDown = false;
1583}
1584
1585/*!
1586 \since QtQuick.Controls 2.2 (Qt 5.9)
1587 \qmlproperty bool QtQuick.Controls::ComboBox::editable
1588
1589 This property holds whether the combo box is editable.
1590
1591 The default value is \c false.
1592
1593 \sa validator
1594*/
1595bool QQuickComboBox::isEditable() const
1596{
1597 Q_D(const QQuickComboBox);
1598 return d->extra.isAllocated() && d->extra->editable;
1599}
1600
1601void QQuickComboBox::setEditable(bool editable)
1602{
1603 Q_D(QQuickComboBox);
1604 if (editable == isEditable())
1605 return;
1606
1607 if (d->contentItem) {
1608 if (editable) {
1609 d->contentItem->installEventFilter(this);
1610 if (QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem)) {
1611 QObjectPrivate::connect(input, &QQuickTextInput::textChanged, d, &QQuickComboBoxPrivate::updateEditText);
1612 QObjectPrivate::connect(input, &QQuickTextInput::accepted, d, &QQuickComboBoxPrivate::acceptInput);
1613 }
1614#if QT_CONFIG(cursor)
1615 d->contentItem->setCursor(Qt::IBeamCursor);
1616#endif
1617 } else {
1618 d->contentItem->removeEventFilter(this);
1619 if (QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem)) {
1620 QObjectPrivate::disconnect(input, &QQuickTextInput::textChanged, d, &QQuickComboBoxPrivate::updateEditText);
1621 QObjectPrivate::disconnect(input, &QQuickTextInput::accepted, d, &QQuickComboBoxPrivate::acceptInput);
1622 }
1623#if QT_CONFIG(cursor)
1624 d->contentItem->unsetCursor();
1625#endif
1626 }
1627 }
1628
1629 d->extra.value().editable = editable;
1630 setAccessibleProperty("editable", editable);
1631 emit editableChanged();
1632}
1633
1634/*!
1635 \since QtQuick.Controls 2.2 (Qt 5.9)
1636 \qmlproperty string QtQuick.Controls::ComboBox::editText
1637
1638 This property holds the text in the text field of an editable combo box.
1639
1640 \sa editable, currentText, displayText
1641*/
1642QString QQuickComboBox::editText() const
1643{
1644 Q_D(const QQuickComboBox);
1645 return d->extra.isAllocated() ? d->extra->editText : QString();
1646}
1647
1648void QQuickComboBox::setEditText(const QString &text)
1649{
1650 Q_D(QQuickComboBox);
1651 if (text == editText())
1652 return;
1653
1654 d->extra.value().editText = text;
1655 emit editTextChanged();
1656}
1657
1658void QQuickComboBox::resetEditText()
1659{
1660 setEditText(QString());
1661}
1662
1663#if QT_CONFIG(validator)
1664/*!
1665 \since QtQuick.Controls 2.2 (Qt 5.9)
1666 \qmlproperty Validator QtQuick.Controls::ComboBox::validator
1667
1668 This property holds an input text validator for an editable combo box.
1669
1670 When a validator is set, the text field will only accept input which
1671 leaves the text property in an intermediate state. The \l accepted signal
1672 will only be emitted if the text is in an acceptable state when the
1673 \uicontrol Return or \uicontrol Enter key is pressed.
1674
1675 The currently supported validators are \l[QtQuick]{IntValidator},
1676 \l[QtQuick]{DoubleValidator}, and \l[QtQuick]{RegularExpressionValidator}. An
1677 example of using validators is shown below, which allows input of
1678 integers between \c 0 and \c 10 into the text field:
1679
1680 \code
1681 ComboBox {
1682 model: 10
1683 editable: true
1684 validator: IntValidator {
1685 top: 9
1686 bottom: 0
1687 }
1688 }
1689 \endcode
1690
1691 \sa acceptableInput, accepted, editable
1692*/
1693QValidator *QQuickComboBox::validator() const
1694{
1695 Q_D(const QQuickComboBox);
1696 return d->extra.isAllocated() ? d->extra->validator : nullptr;
1697}
1698
1699void QQuickComboBox::setValidator(QValidator *validator)
1700{
1701 Q_D(QQuickComboBox);
1702 if (validator == QQuickComboBox::validator())
1703 return;
1704
1705 d->extra.value().validator = validator;
1706#if QT_CONFIG(validator)
1707 if (validator)
1708 validator->setLocale(d->locale);
1709#endif
1710 emit validatorChanged();
1711}
1712#endif
1713
1714/*!
1715 \since QtQuick.Controls 2.2 (Qt 5.9)
1716 \qmlproperty flags QtQuick.Controls::ComboBox::inputMethodHints
1717
1718 Provides hints to the input method about the expected content of the combo box and how it
1719 should operate.
1720
1721 The default value is \c Qt.ImhNoPredictiveText.
1722
1723 \include inputmethodhints.qdocinc
1724*/
1725Qt::InputMethodHints QQuickComboBox::inputMethodHints() const
1726{
1727 Q_D(const QQuickComboBox);
1728 return d->extra.isAllocated() ? d->extra->inputMethodHints : Qt::ImhNoPredictiveText;
1729}
1730
1731void QQuickComboBox::setInputMethodHints(Qt::InputMethodHints hints)
1732{
1733 Q_D(QQuickComboBox);
1734 d->setInputMethodHints(hints);
1735}
1736
1737/*!
1738 \since QtQuick.Controls 2.2 (Qt 5.9)
1739 \qmlproperty bool QtQuick.Controls::ComboBox::inputMethodComposing
1740 \readonly
1741
1742 This property holds whether an editable combo box has partial text input from an input method.
1743
1744 While it is composing, an input method may rely on mouse or key events from the combo box to
1745 edit or commit the partial text. This property can be used to determine when to disable event
1746 handlers that may interfere with the correct operation of an input method.
1747*/
1748bool QQuickComboBox::isInputMethodComposing() const
1749{
1750 Q_D(const QQuickComboBox);
1751 return d->contentItem && d->contentItem->property("inputMethodComposing").toBool();
1752}
1753
1754/*!
1755 \since QtQuick.Controls 2.2 (Qt 5.9)
1756 \qmlproperty bool QtQuick.Controls::ComboBox::acceptableInput
1757 \readonly
1758
1759 This property holds whether the combo box contains acceptable text in the editable text field.
1760
1761 If a validator has been set, the value is \c true only if the current text is acceptable
1762 to the validator as a final string (not as an intermediate string).
1763
1764 \sa validator, accepted
1765*/
1766bool QQuickComboBox::hasAcceptableInput() const
1767{
1768 Q_D(const QQuickComboBox);
1769 return d->m_acceptableInput;
1770}
1771
1772/*!
1773 \since QtQuick.Controls 2.5 (Qt 5.12)
1774 \qmlproperty real QtQuick.Controls::ComboBox::implicitIndicatorWidth
1775 \readonly
1776
1777 This property holds the implicit indicator width.
1778
1779 The value is equal to \c {indicator ? indicator.implicitWidth : 0}.
1780
1781 This is typically used, together with \l {Control::}{implicitContentWidth} and
1782 \l {Control::}{implicitBackgroundWidth}, to calculate the \l {Item::}{implicitWidth}.
1783
1784 \sa implicitIndicatorHeight
1785*/
1786qreal QQuickComboBox::implicitIndicatorWidth() const
1787{
1788 Q_D(const QQuickComboBox);
1789 if (!d->indicator)
1790 return 0;
1791 return d->indicator->implicitWidth();
1792}
1793
1794/*!
1795 \since QtQuick.Controls 2.5 (Qt 5.12)
1796 \qmlproperty real QtQuick.Controls::ComboBox::implicitIndicatorHeight
1797 \readonly
1798
1799 This property holds the implicit indicator height.
1800
1801 The value is equal to \c {indicator ? indicator.implicitHeight : 0}.
1802
1803 This is typically used, together with \l {Control::}{implicitContentHeight} and
1804 \l {Control::}{implicitBackgroundHeight}, to calculate the \l {Item::}{implicitHeight}.
1805
1806 \sa implicitIndicatorWidth
1807*/
1808qreal QQuickComboBox::implicitIndicatorHeight() const
1809{
1810 Q_D(const QQuickComboBox);
1811 if (!d->indicator)
1812 return 0;
1813 return d->indicator->implicitHeight();
1814}
1815
1816/*!
1817 \since QtQuick.Controls 2.14 (Qt 5.14)
1818 \qmlproperty var QtQuick.Controls::ComboBox::currentValue
1819
1820 This property holds the value of the current item in the combo box.
1821 Setting this property will set \l currentIndex to the item with the
1822 corresponding value or \c -1 if it is not found.
1823 Setting both \l currentIndex and \a currentValue declaratively will
1824 result in undefined behavior.
1825 Setting this property with a value that is not unique is not supported.
1826
1827 For an example of how to use this property, see \l {ComboBox Model Roles}.
1828
1829 \sa currentIndex, currentText, valueRole
1830*/
1831QVariant QQuickComboBox::currentValue() const
1832{
1833 Q_D(const QQuickComboBox);
1834 return d->currentValue;
1835}
1836
1837void QQuickComboBox::setCurrentValue(const QVariant &value)
1838{
1839 Q_D(QQuickComboBox);
1840 d->currentElementCriteria = QQuickComboBoxPrivate::CurrentElementCriteria::CurrentValue;
1841 if (value == d->currentValue)
1842 return;
1843
1844 d->currentValue = value;
1845 emit currentValueChanged();
1846
1847 if (d->componentComplete)
1848 d->updateCurrentElements();
1849}
1850
1851/*!
1852 \readonly
1853 \since QtQuick.Controls 2.14 (Qt 5.14)
1854 \qmlmethod var QtQuick.Controls::ComboBox::valueAt(int index)
1855
1856 Returns the value at position \a index in the combo box.
1857
1858 \sa indexOfValue
1859*/
1860QVariant QQuickComboBox::valueAt(int index) const
1861{
1862 Q_D(const QQuickComboBox);
1863 if (!d->isValidIndex(index))
1864 return QVariant();
1865
1866 const QString effectiveValueRole = d->valueRole.isEmpty() ? QStringLiteral("modelData") : d->valueRole;
1867 return d->delegateModel->variantValue(index, effectiveValueRole);
1868}
1869
1870/*!
1871 \since QtQuick.Controls 2.14 (Qt 5.14)
1872 \qmlmethod int QtQuick.Controls::ComboBox::indexOfValue(object value)
1873
1874 Returns the index of the specified \a value, or \c -1 if no match is found.
1875
1876 For an example of how to use this method, see \l {ComboBox Model Roles}.
1877
1878 \include qquickcombobox.qdocinc functions-after-component-completion
1879
1880 \sa find(), currentValue, currentIndex, valueRole, valueAt
1881*/
1882int QQuickComboBox::indexOfValue(const QVariant &value) const
1883{
1884 for (int i = 0; i < count(); ++i) {
1885 const QVariant ourValue = valueAt(i);
1886 if (value == ourValue)
1887 return i;
1888 }
1889 return -1;
1890}
1891
1892/*!
1893 \since QtQuick.Controls 2.15 (Qt 5.15)
1894 \qmlproperty bool QtQuick.Controls::ComboBox::selectTextByMouse
1895
1896 This property holds whether the text field for an editable ComboBox
1897 can be selected with the mouse.
1898
1899 The default value is \c false.
1900*/
1901bool QQuickComboBox::selectTextByMouse() const
1902{
1903 Q_D(const QQuickComboBox);
1904 return d->extra.isAllocated() ? d->extra->selectTextByMouse : false;
1905}
1906
1907void QQuickComboBox::setSelectTextByMouse(bool canSelect)
1908{
1909 Q_D(QQuickComboBox);
1910 if (canSelect == selectTextByMouse())
1911 return;
1912
1913 d->extra.value().selectTextByMouse = canSelect;
1914 emit selectTextByMouseChanged();
1915}
1916
1917/*!
1918 \since QtQuick.Controls 6.0 (Qt 6.0)
1919 \qmlproperty enumeration QtQuick.Controls::ComboBox::implicitContentWidthPolicy
1920
1921 This property controls how the \l{Control::}{implicitContentWidth} of the ComboBox is
1922 calculated.
1923
1924 When the width of a ComboBox is not large enough to display text, that text
1925 is elided. Depending on which parts of the text are elided, this can make
1926 selecting an item difficult for the end user. An efficient way of ensuring
1927 that a ComboBox is wide enough to avoid text being elided is to set a width
1928 that is known to be large enough:
1929
1930 \code
1931 width: 300
1932 implicitContentWidthPolicy: ComboBox.ContentItemImplicitWidth
1933 \endcode
1934
1935 However, it is often not possible to know whether or not a hard-coded value
1936 will be large enough, as the size of text depends on many factors, such as
1937 font family, font size, translations, and so on.
1938
1939 implicitContentWidthPolicy provides an easy way to control how the
1940 implicitContentWidth is calculated, which in turn affects the
1941 \l{Item::}{implicitWidth} of the ComboBox and ensures that text will not be elided.
1942
1943 The available values are:
1944
1945 \value ContentItemImplicitWidth
1946 The implicitContentWidth will default to that of the \l{Control::}{contentItem}.
1947 This is the most efficient option, as no extra text layout is done.
1948 \value WidestText
1949 The implicitContentWidth will be set to the implicit width of the
1950 the largest text for the given \l textRole every time the model
1951 changes.
1952 This option should be used with smaller models, as it can be expensive.
1953 \value WidestTextWhenCompleted
1954 The implicitContentWidth will be set to the implicit width of the
1955 the largest text for the given \l textRole once after
1956 \l {QQmlParserStatus::componentComplete()}{component completion}.
1957 This option should be used with smaller models, as it can be expensive.
1958
1959 The default value is \c ContentItemImplicitWidth.
1960
1961 As this property only affects the \c implicitWidth of the ComboBox, setting
1962 an explicit \l{Item::}{width} can still result in eliding.
1963
1964 \note This feature requires the contentItem to be a type derived from
1965 \l TextInput.
1966
1967 \note This feature requires text to be laid out, and can therefore be
1968 expensive for large models or models whose contents are updated
1969 frequently.
1970*/
1971QQuickComboBox::ImplicitContentWidthPolicy QQuickComboBox::implicitContentWidthPolicy() const
1972{
1973 Q_D(const QQuickComboBox);
1974 return d->implicitContentWidthPolicy;
1975}
1976
1977void QQuickComboBox::setImplicitContentWidthPolicy(QQuickComboBox::ImplicitContentWidthPolicy policy)
1978{
1979 Q_D(QQuickComboBox);
1980 if (policy == d->implicitContentWidthPolicy)
1981 return;
1982
1983 d->implicitContentWidthPolicy = policy;
1984 d->maybeUpdateImplicitContentWidth();
1985 emit implicitContentWidthPolicyChanged();
1986}
1987
1988/*!
1989 \since QtQuick.Controls 6.12 (Qt 6.12)
1990 \qmlproperty bool QtQuick.Controls::ComboBox::highlightOnHover
1991
1992 This property holds whether hovering over items should highlight
1993 them.
1994
1995 The default value is \c true.
1996*/
1997bool QQuickComboBox::highlightOnHover() const
1998{
1999 Q_D(const QQuickComboBox);
2000 return d->highlightOnHover;
2001}
2002
2003void QQuickComboBox::setHighlightOnHover(bool value)
2004{
2005 Q_D(QQuickComboBox);
2006 if (value == highlightOnHover())
2007 return;
2008
2009 d->highlightOnHover = value;
2010 emit highlightOnHoverChanged();
2011}
2012/*!
2013 \qmlmethod string QtQuick.Controls::ComboBox::textAt(int index)
2014
2015 Returns the text for the specified \a index, or an empty string
2016 if the index is out of bounds.
2017
2018 \include qquickcombobox.qdocinc functions-after-component-completion
2019 For example:
2020 \snippet qtquickcontrols-combobox-textat.qml textat
2021
2022 \sa textRole
2023*/
2024QString QQuickComboBox::textAt(int index) const
2025{
2026 Q_D(const QQuickComboBox);
2027 if (!d->isValidIndex(index))
2028 return QString();
2029
2030 return d->delegateModel->stringValue(index, d->effectiveTextRole());
2031}
2032
2033/*!
2034 \qmlmethod int QtQuick.Controls::ComboBox::find(string text, enumeration flags)
2035
2036 Returns the index of the specified \a text, or \c -1 if no match is found.
2037
2038 The way the search is performed is defined by the specified match \a flags. By default,
2039 combo box performs case sensitive exact matching (\c Qt.MatchExactly). All other match
2040 types are case-insensitive unless the \c Qt.MatchCaseSensitive flag is also specified.
2041
2042 \value Qt.MatchExactly The search term matches exactly (default).
2043 \value Qt.MatchRegularExpression The search term matches as a regular expression.
2044 \value Qt.MatchWildcard The search term matches using wildcards.
2045 \value Qt.MatchFixedString The search term matches as a fixed string.
2046 \value Qt.MatchStartsWith The search term matches the start of the item.
2047 \value Qt.MatchEndsWith The search term matches the end of the item.
2048 \value Qt.MatchContains The search term is contained in the item.
2049 \value Qt.MatchCaseSensitive The search is case sensitive.
2050
2051 \include qquickcombobox.qdocinc functions-after-component-completion
2052 For example:
2053 \snippet qtquickcontrols-combobox-find.qml find
2054
2055 \sa textRole
2056*/
2057int QQuickComboBox::find(const QString &text, Qt::MatchFlags flags) const
2058{
2059 Q_D(const QQuickComboBox);
2060 return d->match(0, text, flags);
2061}
2062
2063/*!
2064 \qmlmethod void QtQuick.Controls::ComboBox::incrementCurrentIndex()
2065
2066 Increments the current index of the combo box, or the highlighted
2067 index if the popup list is visible.
2068
2069 \sa currentIndex, highlightedIndex
2070*/
2071void QQuickComboBox::incrementCurrentIndex()
2072{
2073 Q_D(QQuickComboBox);
2074 d->incrementCurrentIndex();
2075}
2076
2077/*!
2078 \qmlmethod void QtQuick.Controls::ComboBox::decrementCurrentIndex()
2079
2080 Decrements the current index of the combo box, or the highlighted
2081 index if the popup list is visible.
2082
2083 \sa currentIndex, highlightedIndex
2084*/
2085void QQuickComboBox::decrementCurrentIndex()
2086{
2087 Q_D(QQuickComboBox);
2088 d->decrementCurrentIndex();
2089}
2090
2091/*!
2092 \since QtQuick.Controls 2.2 (Qt 5.9)
2093 \qmlmethod void QtQuick.Controls::ComboBox::selectAll()
2094
2095 Selects all the text in the editable text field of the combo box.
2096
2097 \sa editText
2098*/
2099void QQuickComboBox::selectAll()
2100{
2101 Q_D(QQuickComboBox);
2102 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
2103 if (!input)
2104 return;
2105 input->selectAll();
2106}
2107
2108bool QQuickComboBox::eventFilter(QObject *object, QEvent *event)
2109{
2110 Q_D(QQuickComboBox);
2111 switch (event->type()) {
2112 case QEvent::MouseButtonRelease:
2113 if (d->isPopupVisible())
2114 d->hidePopup(false);
2115 break;
2116 case QEvent::KeyPress: {
2117 QKeyEvent *ke = static_cast<QKeyEvent *>(event);
2118 if (d->filterKeyEvent(ke, false))
2119 return true;
2120 event->accept();
2121 if (d->extra.isAllocated())
2122 d->extra->allowComplete = ke->key() != Qt::Key_Backspace && ke->key() != Qt::Key_Delete;
2123 break;
2124 }
2125 case QEvent::FocusOut: {
2126 const bool hasActiveFocus = d->popup && d->popup->hasActiveFocus();
2127 const bool usingPopupWindows =
2128 d->popup ? QQuickPopupPrivate::get(d->popup)->usePopupWindow() : false;
2129 if (qGuiApp->focusObject() != this && !(hasActiveFocus && !usingPopupWindows)) {
2130 // Only close the popup if focus was transferred somewhere else
2131 // than to the popup or the popup button (which normally means that
2132 // the user clicked on the popup button to open it, not close it).
2133 d->hidePopup(false);
2134 setPressed(false);
2135
2136 // The focus left the text field, so if the edit text matches an item in the model,
2137 // change our currentIndex to that. This matches widgets' behavior.
2138 const int indexForEditText = find(d->extra.value().editText, Qt::MatchFixedString);
2139 if (indexForEditText > -1)
2140 d->setCurrentItemAtIndex(indexForEditText, Activate);
2141 }
2142 break;
2143 }
2144#if QT_CONFIG(im)
2145 case QEvent::InputMethod:
2146 if (d->extra.isAllocated())
2147 d->extra->allowComplete = !static_cast<QInputMethodEvent*>(event)->commitString().isEmpty();
2148 break;
2149#endif
2150 default:
2151 break;
2152 }
2153 return QQuickControl::eventFilter(object, event);
2154}
2155
2156void QQuickComboBox::focusInEvent(QFocusEvent *event)
2157{
2158 Q_D(QQuickComboBox);
2159 QQuickControl::focusInEvent(event);
2160 // Setting focus on TextField should not be done when drop down indicator was clicked
2161 // That is why, if focus is not set with key reason, it should not be passed to textEdit by default.
2162 // Focus on Edit Text should be set only intentionally by user.
2163 if ((event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason ||
2164 event->reason() == Qt::ShortcutFocusReason) && d->contentItem && isEditable())
2165 d->contentItem->forceActiveFocus(event->reason());
2166}
2167
2168void QQuickComboBox::focusOutEvent(QFocusEvent *event)
2169{
2170 Q_D(QQuickComboBox);
2171 QQuickControl::focusOutEvent(event);
2172
2173 const bool hasActiveFocus = d->popup && d->popup->hasActiveFocus();
2174 const bool usingPopupWindows = d->popup && QQuickPopupPrivate::get(d->popup)->usePopupWindow();
2175 if (qGuiApp->focusObject() != d->contentItem && !(hasActiveFocus && !usingPopupWindows)) {
2176 // Only close the popup if focus was transferred
2177 // somewhere else than to the popup or the inner line edit (which is
2178 // normally done from QQuickComboBox::focusInEvent).
2179 d->hidePopup(false);
2180 setPressed(false);
2181 }
2182}
2183
2184#if QT_CONFIG(im)
2185void QQuickComboBox::inputMethodEvent(QInputMethodEvent *event)
2186{
2187 Q_D(QQuickComboBox);
2188 QQuickControl::inputMethodEvent(event);
2189 if (!isEditable() && !event->commitString().isEmpty())
2190 d->keySearch(event->commitString());
2191 else
2192 event->ignore();
2193}
2194#endif
2195
2196void QQuickComboBox::keyPressEvent(QKeyEvent *event)
2197{
2198 Q_D(QQuickComboBox);
2199 QQuickControl::keyPressEvent(event);
2200
2201 const auto key = event->key();
2202 if (!isEditable()) {
2203 const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
2204 if (buttonPressKeys.contains(key)) {
2205 if (!event->isAutoRepeat())
2206 setPressed(true);
2207 event->accept();
2208 return;
2209 }
2210 }
2211
2212 switch (key) {
2213 case Qt::Key_Escape:
2214 case Qt::Key_Back:
2215 d->acceptedEscKeyPress = d->isPopupVisible();
2216 d->receivedEscKeyPress = true;
2217 if (d->acceptedEscKeyPress) {
2218 d->hidePopup(false);
2219 setPressed(false);
2220 event->accept();
2221 }
2222 break;
2223 case Qt::Key_Enter:
2224 case Qt::Key_Return:
2225 if (d->isPopupVisible())
2226 setPressed(true);
2227 event->accept();
2228 break;
2229 case Qt::Key_Up:
2230 d->keyNavigating = true;
2231 d->decrementCurrentIndex();
2232 event->accept();
2233 break;
2234 case Qt::Key_Down:
2235 d->keyNavigating = true;
2236 d->incrementCurrentIndex();
2237 event->accept();
2238 break;
2239 case Qt::Key_Home:
2240 d->keyNavigating = true;
2241 if (d->isPopupVisible())
2242 d->setHighlightedIndex(0, Highlight);
2243 else
2244 d->setCurrentItemAtIndex(0, Activate);
2245 event->accept();
2246 break;
2247 case Qt::Key_End:
2248 d->keyNavigating = true;
2249 if (d->isPopupVisible())
2250 d->setHighlightedIndex(count() - 1, Highlight);
2251 else
2252 d->setCurrentItemAtIndex(count() - 1, Activate);
2253 event->accept();
2254 break;
2255 default:
2256 if (!isEditable() && !event->text().isEmpty())
2257 d->keySearch(event->text());
2258 else
2259 event->ignore();
2260 break;
2261 }
2262}
2263
2264void QQuickComboBox::keyReleaseEvent(QKeyEvent *event)
2265{
2266 Q_D(QQuickComboBox);
2267 QQuickControl::keyReleaseEvent(event);
2268 d->keyNavigating = false;
2269 if (event->isAutoRepeat())
2270 return;
2271
2272 const auto key = event->key();
2273 if (!isEditable()) {
2274 const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ButtonPressKeys).value<QList<Qt::Key>>();
2275 if (buttonPressKeys.contains(key)) {
2276 if (!isEditable() && isPressed())
2277 d->togglePopup(true);
2278 setPressed(false);
2279 event->accept();
2280 return;
2281 }
2282 }
2283
2284 switch (key) {
2285 case Qt::Key_Enter:
2286 case Qt::Key_Return:
2287 if (!isEditable() || d->isPopupVisible())
2288 d->hidePopup(d->isPopupVisible());
2289 setPressed(false);
2290 event->accept();
2291 break;
2292 case Qt::Key_Escape:
2293 case Qt::Key_Back:
2294 // If QQuickComboBox accepts the key press event, accept the key release event.
2295 // If QQuickComboBox doesn't receive the key press event, but does receive the
2296 // key release event, most likely the popup has popupType == Window and the window was
2297 // closed on key press, resulting in QQuickComboBox only receiving the release event.
2298 if (d->acceptedEscKeyPress || !d->receivedEscKeyPress)
2299 event->accept();
2300 d->acceptedEscKeyPress = false;
2301 d->receivedEscKeyPress = false;
2302 break;
2303 default:
2304 break;
2305 }
2306}
2307
2308#if QT_CONFIG(wheelevent)
2309void QQuickComboBox::wheelEvent(QWheelEvent *event)
2310{
2311 Q_D(QQuickComboBox);
2312 QQuickControl::wheelEvent(event);
2313 if (d->wheelEnabled && !d->isPopupVisible()) {
2314 if (event->angleDelta().y() > 0)
2315 d->decrementCurrentIndex();
2316 else
2317 d->incrementCurrentIndex();
2318 }
2319}
2320#endif
2321
2322bool QQuickComboBox::event(QEvent *e)
2323{
2324 Q_D(QQuickComboBox);
2325 if (e->type() == QEvent::LanguageChange)
2326 d->updateCurrentElements();
2327 return QQuickControl::event(e);
2328}
2329
2330void QQuickComboBox::componentComplete()
2331{
2332 Q_D(QQuickComboBox);
2333 d->executeIndicator(true);
2334 QQuickControl::componentComplete();
2335 if (d->popup)
2336 d->executePopup(true);
2337
2338 if (d->delegateModel && d->ownModel)
2339 static_cast<QQmlDelegateModel *>(d->delegateModel)->componentComplete();
2340
2341 if (count() > 0) {
2342 if (d->currentElementCriteria == QQuickComboBoxPrivate::CurrentElementCriteria::None && d->currentIndex == -1)
2343 d->setCurrentIndex(0);
2344 else
2345 d->updateCurrentElements();
2346
2347 // If the widest text was already calculated in the call to
2348 // QQmlDelegateModel::componentComplete() above, then we shouldn't do it here too.
2349 if (!d->hasCalculatedWidestText)
2350 d->maybeUpdateImplicitContentWidth();
2351 }
2352}
2353
2354void QQuickComboBox::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
2355{
2356 Q_D(QQuickComboBox);
2357 QQuickControl::itemChange(change, value);
2358 if (change == ItemVisibleHasChanged && !value.boolValue) {
2359 d->hidePopup(false);
2360 setPressed(false);
2361 }
2362}
2363
2364void QQuickComboBox::fontChange(const QFont &newFont, const QFont &oldFont)
2365{
2366 Q_D(QQuickComboBox);
2367 QQuickControl::fontChange(newFont, oldFont);
2368 d->maybeUpdateImplicitContentWidth();
2369}
2370
2371void QQuickComboBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
2372{
2373 Q_D(QQuickComboBox);
2374 if (oldItem) {
2375 oldItem->removeEventFilter(this);
2376 if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem)) {
2377 QObjectPrivate::disconnect(oldInput, &QQuickTextInput::accepted, d, &QQuickComboBoxPrivate::acceptInput);
2378 QObjectPrivate::disconnect(oldInput, &QQuickTextInput::textChanged, d, &QQuickComboBoxPrivate::updateEditText);
2379 disconnect(oldInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickComboBox::inputMethodComposingChanged);
2380 QObjectPrivate::disconnect(oldInput, &QQuickTextInput::acceptableInputChanged, d, &QQuickComboBoxPrivate::updateAcceptableInput);
2381 }
2382 }
2383 if (newItem && isEditable()) {
2384 newItem->installEventFilter(this);
2385 if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem)) {
2386 QObjectPrivate::connect(newInput, &QQuickTextInput::accepted, d, &QQuickComboBoxPrivate::acceptInput);
2387 QObjectPrivate::connect(newInput, &QQuickTextInput::textChanged, d, &QQuickComboBoxPrivate::updateEditText);
2388 connect(newInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickComboBox::inputMethodComposingChanged);
2389 QObjectPrivate::connect(newInput, &QQuickTextInput::acceptableInputChanged, d, &QQuickComboBoxPrivate::updateAcceptableInput);
2390 }
2391#if QT_CONFIG(cursor)
2392 newItem->setCursor(Qt::IBeamCursor);
2393#endif
2394 }
2395
2396 d->updateAcceptableInput();
2397}
2398
2399void QQuickComboBox::localeChange(const QLocale &newLocale, const QLocale &oldLocale)
2400{
2401 QQuickControl::localeChange(newLocale, oldLocale);
2402#if QT_CONFIG(validator)
2403 if (QValidator *v = validator())
2404 v->setLocale(newLocale);
2405#endif
2406}
2407
2408QFont QQuickComboBox::defaultFont() const
2409{
2410 return QQuickTheme::font(QQuickTheme::ComboBox);
2411}
2412
2413#if QT_CONFIG(accessibility)
2414QAccessible::Role QQuickComboBox::accessibleRole() const
2415{
2416 return QAccessible::ComboBox;
2417}
2418
2419void QQuickComboBox::accessibilityActiveChanged(bool active)
2420{
2421 Q_D(QQuickComboBox);
2422 QQuickControl::accessibilityActiveChanged(active);
2423
2424 if (active) {
2425 maybeSetAccessibleName(d->hasDisplayText ? d->displayText : d->currentText);
2426 setAccessibleProperty("editable", isEditable());
2427 }
2428}
2429#endif //
2430
2431QT_END_NAMESPACE
2432
2433#include "moc_qquickcombobox_p.cpp"
QQuickComboBoxDelegateModel(QQuickComboBox *combo)
QVariant variantValue(int index, const QString &role) override
void itemDestroyed(QQuickItem *item) override
bool handleRelease(const QPointF &point, ulong timestamp) override
CurrentElementCriteria currentElementCriteria
QQuickDeferredPointer< QQuickPopup > popup
qreal calculateWidestTextWidth() const
static void hideOldPopup(QQuickPopup *popup)
void setInputMethodHints(Qt::InputMethodHints hints, bool force=false)
void setHighlightedIndex(int index, Highlighting highlight)
void createdItem(int index, QObject *object)
QString effectiveTextRole() const
virtual qreal getContentWidth() const override
void keySearch(const QString &text)
void handleUngrab() override
int match(int start, const QString &text, Qt::MatchFlags flags) const
QPalette defaultPalette() const override
bool handleMove(const QPointF &point, ulong timestamp) override
QQmlInstanceModel * delegateModel
void itemImplicitHeightChanged(QQuickItem *item) override
void togglePopup(bool accept)
void executePopup(bool complete=false)
void itemImplicitWidthChanged(QQuickItem *item) override
void onDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
void executeIndicator(bool complete=false)
QQuickDeferredPointer< QQuickItem > indicator
void hidePopup(bool accept)
void setCurrentIndex(int index)
QLazilyAllocated< ExtraData > extra
std::optional< QPointer< QObject > > qobjectModelGuard
bool handlePress(const QPointF &point, ulong timestamp) override
void setCurrentItemAtIndex(int, Activation activate)
bool isValidIndex(int index) const
QString tryComplete(const QString &inputText)
Combined button and popup list for selecting options.
static QString popupName()
Qt::InputMethodHints inputMethodHints