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