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

source code of qtdeclarative/src/quicktemplates/qquickcombobox.cpp