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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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