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