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