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