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

source code of qtquickcontrols2/src/quicktemplates2/qquickcombobox.cpp