Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquicksearchfield.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7#include <private/qquickindicatorbutton_p.h>
8#include <QtQuickTemplates2/private/qquicktextfield_p.h>
11#include <private/qqmldelegatemodel_p.h>
14#include <QtQml/qqmlcomponent.h>
15#include <QtQuick/private/qquickaccessibleattached_p.h>
16#if QT_CONFIG(quick_itemview)
17# include <QtQuick/private/qquickitemview_p.h>
18#endif
19
21
22/*!
23 \qmltype SearchField
24 \inherits Control
25 //! \nativetype QQuickSearchField
26 \inqmlmodule QtQuick.Controls
27 \since 6.10
28 \ingroup qtquickcontrols-input
29 \ingroup qtquickcontrols-focusscopes
30 \brief A specialized input field designed to use for search functionality.
31
32 SearchField is a specialized input field designed to use for search functionality.
33 The control includes a text field, search and clear icons, and a popup that
34 displays suggestions or search results.
35
36 \note The iOS style does not provide a built-in popup for SearchField in
37 order to preserve the native look and feel. If a popup is still wanted,
38 it has to be defined by the user.
39
40 \image qtquickcontrols-searchfield.gif
41 {Search field with search icon and clear button}
42
43 \section1 SearchField's Indicators
44
45 SearchField provides two optional embedded indicator buttons: \l searchIndicator and
46 \l clearIndicator.
47
48 These are not indicators in the sense of \l BusyIndicator or \l ProgressBar. Instead,
49 they are interactive controls embedded into the field (similar to the up/down buttons
50 in \l SpinBox). Pressing \l searchIndicator triggers \l searchButtonPressed(), and pressing
51 \l clearIndicator triggers \l clearButtonPressed().
52
53 In addition to exposing the actions, the indicator buttons provide interaction state
54 (pressed/hovered/focused, etc.) that can be used by styles.
55
56 \section2 Customizing indicator content
57
58 The \l searchIndicator and \l clearIndicator properties are read-only. Customization is
59 supported through their internal properties.
60
61 In particular, the button's visual content is provided by its \c indicator item, which is
62 writable. This allows the default content to be replaced or removed entirely.
63
64 For example, to remove both indicator icons:
65
66 \code
67 SearchField {
68 searchIndicator.indicator: null
69 clearIndicator.indicator: null
70 }
71 \endcode
72
73 This is a supported customization scenario. Different SearchField variants may omit one
74 of the buttons (for example, providing only a search button) or replace the indicator
75 content with an alternative item (for example, a microphone icon to trigger speech input).
76
77 \sa searchIndicator, clearIndicator, searchButtonPressed(), clearButtonPressed()
78
79 \section1 SearchField Model Roles
80
81 SearchField is able to visualize standard \l {qml-data-models}{data models}
82 that provide the \c modelData role:
83 \list
84 \li models that have only one role
85 \li models that do not have named roles (JavaScript array, integer)
86 \endlist
87
88 When using models that have multiple named roles, SearchField must be configured
89 to use a specific \l {textRole}{text role} for its \l {text}{text}
90 and \l delegate instances.
91
92 \code
93 ListModel {
94 id : fruitModel
95 ListElement { name: "Apple"; color: "green" }
96 ListElement { name: "Cherry"; color: "red" }
97 ListElement { name: "Banana"; color: "yellow" }
98 ListElement { name: "Orange"; color: "orange" }
99 ListElement { name: "WaterMelon"; color: "pink" }
100 }
101
102 SortFilterProxyModel {
103 id: fruitFilter
104 model: fruitModel
105 sorters: [
106 RoleSorter {
107 roleName: "name"
108 }
109 ]
110 filters: [
111 FunctionFilter {
112 component CustomData: QtObject { property string name }
113 property var regExp: new RegExp(fruitSearch.text, "i")
114 onRegExpChanged: invalidate()
115 function filter(data: CustomData): bool {
116 return regExp.test(data.name);
117 }
118 }
119 ]
120 }
121
122 SearchField {
123 id: fruitSearch
124 suggestionModel: fruitFilter
125 textRole: "name"
126 anchors.horizontalCenter: parent.horizontalCenter
127 }
128 \endcode
129 */
130
131/*!
132 \qmlsignal void QtQuick.Controls::SearchField::activated(int index)
133
134 This signal is emitted when the item at \a index is activated by the user.
135
136 An item is activated when it is selected while the popup is open,
137 causing the popup to close (and \l currentIndex to change).
138 The \l currentIndex property is set to \a index.
139
140 \sa currentIndex
141*/
142
143
144/*!
145 \qmlsignal void QtQuick.Controls::SearchField::highlighted(int index)
146
147 This signal is emitted when the item at \a index in the popup list is highlighted by the user.
148
149 The highlighted signal is only emitted when the popup is open and an item
150 is highlighted, but not necessarily \l activated.
151
152 \sa highlightedIndex
153*/
154
155/*!
156 \qmlsignal void QtQuick.Controls::SearchField::accepted()
157
158 This signal is emitted when the user confirms their input by pressing
159 the Enter or Return key.
160
161 This signal is typically used to trigger a search or action based on
162 the final text input, and it indicates the user's intention to complete
163 or submit the query.
164
165 \sa searchTriggered()
166 */
167
168/*!
169 \qmlsignal void QtQuick.Controls::SearchField::searchTriggered()
170
171 This signal is emitted when a search action is initiated.
172
173 It occurs in two cases:
174 1. When the Enter or Return key is pressed, it will be emitted together
175 with accepted() signal
176 2. When the text is edited and if the \l live property is set to \c true,
177 this signal will be emitted.
178
179 This signal is ideal for initiating searches both on-demand and in real-time as
180 the user types, depending on the desired interaction model.
181
182 \sa accepted(), textEdited()
183 */
184
185/*!
186 \qmlsignal void QtQuick.Controls::SearchField::textEdited()
187
188 This signal is emitted every time the user modifies the text in the
189 search field, typically with each keystroke.
190
191 \sa searchTriggered()
192 */
193
194namespace {
197}
198
200{
201public:
202 Q_DECLARE_PUBLIC(QQuickSearchField)
203
204 bool isPopupVisible() const;
205 void showPopup();
206 void hidePopup(bool accept);
207 static void hideOldPopup(QQuickPopup *popup);
210
213
214 void createdItem(int index, QObject *object);
216
219 void setCurrentIndex(int index);
220 void setCurrentItemAtIndex(int index, Activation activate);
222 void setHighlightedIndex(int index, Highlighting highlight);
223
225
226 QString currentTextRole() const;
227 void selectAll();
230 QString textAt(int index) const;
231 bool isValidIndex(int index) const;
232
234 void executePopup(bool complete = false);
235
236 bool handlePress(const QPointF &point, ulong timestamp) override;
237 bool handleRelease(const QPointF &point, ulong timestamp) override;
238
241
242 void itemImplicitWidthChanged(QQuickItem *item) override;
243 void itemImplicitHeightChanged(QQuickItem *item) override;
244 void itemDestroyed(QQuickItem *item) override;
245
246 static inline QString popupName() { return QStringLiteral("popup"); }
247
249 bool hasCurrentIndex = false;
251 int currentIndex = -1;
252 QString text;
253 QString textRole;
254 bool live = true;
255 bool searchPressed = false;
256 bool clearPressed = false;
257 bool searchFlat = false;
258 bool clearFlat = false;
259 bool searchDown = false;
260 bool clearDown = false;
261 bool hasSearchDown = false;
262 bool hasClearDown = false;
263 bool ownModel = false;
264 QQmlInstanceModel *delegateModel = nullptr;
265 QQmlComponent *delegate = nullptr;
266 QQuickIndicatorButton *searchIndicator = nullptr;
267 QQuickIndicatorButton *clearIndicator = nullptr;
269};
270
271bool QQuickSearchFieldPrivate::isPopupVisible() const
272{
273 return popup && popup->isVisible();
274}
275
277{
278 if (!popup)
279 executePopup(true);
280
281 if (popup && !popup->isVisible())
282 popup->open();
283}
284
286{
287 Q_Q(QQuickSearchField);
288 if (accept) {
290 // hiding the popup on user interaction should always emit activated,
291 // even if the current index didn't change
292 emit q->activated(highlightedIndex);
293 }
294 if (popup && popup->isVisible())
295 popup->close();
296}
297
298void QQuickSearchFieldPrivate::hideOldPopup(QQuickPopup *popup)
299{
300 if (!popup)
301 return;
302
303 qCDebug(lcItemManagement) << "hiding old popup" << popup;
304
305 popup->setVisible(false);
306 popup->setParentItem(nullptr);
307#if QT_CONFIG(accessibility)
308 // Remove the item from the accessibility tree.
309 QQuickAccessibleAttached *accessible = accessibleAttached(popup);
310 if (accessible)
311 accessible->setIgnored(true);
312#endif
313}
314
316{
317 if (isPopupVisible())
318 QGuiApplication::inputMethod()->reset();
319
320#if QT_CONFIG(quick_itemview)
321 QQuickItemView *itemView = popup->findChild<QQuickItemView *>();
322 if (itemView)
323 itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
324#endif
325
327
328#if QT_CONFIG(quick_itemview)
329 if (itemView)
330 itemView->positionViewAtIndex(highlightedIndex, QQuickItemView::Beginning);
331#endif
332}
333
335{
336 Q_Q(QQuickSearchField);
337 popup = nullptr;
338 emit q->popupChanged();
339}
340
342{
343 Q_Q(QQuickSearchField);
344 int index = delegateModel->indexOf(q->sender(), nullptr);
345 if (index != -1) {
347 hidePopup(true);
348 }
349}
350
352{
353 Q_Q(QQuickSearchField);
354
355 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(q->sender());
356 if (!button || !button->isHovered() || !button->isEnabled()
357 || QQuickAbstractButtonPrivate::get(button)->touchId != -1)
358 return;
359
360 int index = delegateModel->indexOf(button, nullptr);
361 if (index != -1) {
363
364#if QT_CONFIG(quick_itemview)
365 if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
366 itemView->positionViewAtIndex(index, QQuickItemView::Contain);
367#endif
368 }
369}
370
371void QQuickSearchFieldPrivate::createdItem(int index, QObject *object)
372{
373 Q_UNUSED(index);
374 Q_Q(QQuickSearchField);
375 QQuickItem *item = qobject_cast<QQuickItem *>(object);
376 if (item && !item->parentItem()) {
377 if (popup)
378 item->setParentItem(popup->contentItem());
379 else
380 item->setParentItem(q);
381 QQuickItemPrivate::get(item)->setCulled(true);
382 }
383
384 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(object);
385 if (button) {
386 button->setFocusPolicy(Qt::NoFocus);
387 connect(button, &QQuickAbstractButton::clicked, this,
389 connect(button, &QQuickAbstractButton::hoveredChanged, this,
391 }
392}
393
395{
396 Q_Q(QQuickSearchField);
397 if (q->suggestionCount() == 0)
399 // If the suggestionModel has been updated and the current text matches an item in
400 // the model, update currentIndex and highlightedIndex to the index of that item.
401 if (!text.isEmpty()) {
402 for (int idx = 0; idx < q->suggestionCount(); ++idx) {
403 QString t = textAt(idx);
404 if (t == text) {
407 break;
408 }
409 }
410 }
411
412 emit q->suggestionCountChanged();
413}
414
416{
417 Q_Q(QQuickSearchField);
418 if (isPopupVisible()) {
419 if (highlightedIndex < q->suggestionCount() - 1)
421 }
422}
423
425{
426 if (isPopupVisible()) {
427 if (highlightedIndex > 0)
429 }
430}
431
433{
434 Q_Q(QQuickSearchField);
435 if (currentIndex == index)
436 return;
437
438 currentIndex = index;
439 emit q->currentIndexChanged();
440}
441
443{
444 Q_Q(QQuickSearchField);
445 if (currentIndex == index)
446 return;
447
448 currentIndex = index;
449 emit q->currentIndexChanged();
450
452
453 if (activate)
454 emit q->activated(index);
455}
456
458{
459 Q_Q(QQuickSearchField);
460 int index = -1;
461
462 if (isPopupVisible()) {
463 if (currentIndex >= 0)
464 index = currentIndex;
465 else if (q->suggestionCount() > 0)
466 index = 0; // auto-highlight first suggestion
467 }
468
470}
471
473{
474 Q_Q(QQuickSearchField);
475 if (highlightedIndex == index)
476 return;
477
478 highlightedIndex = index;
479 emit q->highlightedIndexChanged();
480
481 if (highlight)
482 emit q->highlighted(index);
483}
484
486{
487 Q_Q(QQuickSearchField);
488 bool ownedOldModel = ownModel;
489 QQmlInstanceModel *oldModel = delegateModel;
490
491 if (oldModel) {
492 disconnect(delegateModel, &QQmlInstanceModel::countChanged, this,
494 disconnect(delegateModel, &QQmlInstanceModel::createdItem, this,
496 }
497
498 ownModel = false;
499 delegateModel = suggestionModel.value<QQmlInstanceModel *>();
500
501 if (!delegateModel && suggestionModel.isValid()) {
502 QQmlDelegateModel *dataModel = new QQmlDelegateModel(qmlContext(q), q);
503 dataModel->setModel(suggestionModel);
504 dataModel->setDelegate(delegate);
505 if (q->isComponentComplete())
506 dataModel->componentComplete();
507
508 ownModel = true;
509 delegateModel = dataModel;
510 }
511
512 if (delegateModel) {
513 connect(delegateModel, &QQmlInstanceModel::countChanged, this,
515 connect(delegateModel, &QQmlInstanceModel::createdItem, this,
517 }
518
519 emit q->delegateModelChanged();
520
521 if (ownedOldModel)
522 delete oldModel;
523}
524
526{
527 return textRole.isEmpty() ? QStringLiteral("modelData") : textRole;
528}
529
531{
532 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(contentItem);
533 if (!input)
534 return;
535 input->selectAll();
536}
537
539{
540 Q_Q(QQuickSearchField);
541 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(contentItem);
542 if (!input)
543 return;
544
545 const QString textInput = input->text();
546
547 if (text != textInput) {
548 q->setText(textInput);
549 emit q->textEdited();
550
553
554 if (live)
555 emit q->searchTriggered();
556 }
557
558 if (text.isEmpty()) {
559 if (isPopupVisible())
560 hidePopup(false);
561 } else {
562 if (delegateModel && delegateModel->count() > 0) {
563 if (!isPopupVisible())
565 } else {
566 if (isPopupVisible())
567 hidePopup(false);
568 }
569 }
570}
571
573{
574 Q_Q(QQuickSearchField);
575 const QString currentText = textAt(currentIndex);
576
577 if (text != currentText)
578 q->setText(currentText);
579}
580
581QString QQuickSearchFieldPrivate::textAt(int index) const
582{
583 if (!isValidIndex(index))
584 return QString();
585
586 return delegateModel->stringValue(index, currentTextRole());
587}
588
590{
591 return delegateModel && index >= 0 && index < delegateModel->count();
592}
593
595{
596 Q_Q(QQuickSearchField);
597 quickCancelDeferred(q, popupName());
598}
599
601{
602 Q_Q(QQuickSearchField);
603 if (popup.wasExecuted())
604 return;
605
606 if (!popup || complete)
607 quickBeginDeferred(q, popupName(), popup);
608 if (complete)
609 quickCompleteDeferred(q, popupName(), popup);
610}
611
612bool QQuickSearchFieldPrivate::handlePress(const QPointF &point, ulong timestamp)
613{
614 Q_Q(QQuickSearchField);
615 QQuickControlPrivate::handlePress(point, timestamp);
616
617 QQuickItem *si = searchIndicator->indicator();
618 QQuickItem *ci = clearIndicator->indicator();
619 const bool isSearch = si && si->isEnabled() && si->contains(q->mapToItem(si, point));
620 const bool isClear = ci && ci->isEnabled() && ci->contains(q->mapToItem(ci, point));
621
622 if (isSearch) {
623 searchIndicator->setPressed(true);
625 } else if (isClear) {
626 clearIndicator->setPressed(true);
628 }
629
630 return true;
631}
632
633bool QQuickSearchFieldPrivate::handleRelease(const QPointF &point, ulong timestamp)
634{
635 QQuickControlPrivate::handleRelease(point, timestamp);
636 if (searchIndicator->isPressed())
637 searchIndicator->setPressed(false);
638 else if (clearIndicator->isPressed())
639 clearIndicator->setPressed(false);
640 return true;
641}
642
644{
645 Q_Q(QQuickSearchField);
646
647 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(contentItem);
648 if (!input)
649 return;
650
651 input->forceActiveFocus();
652 emit q->searchButtonPressed();
653}
654
656{
657 Q_Q(QQuickSearchField);
658
659 if (text.isEmpty())
660 return;
661
662 // if text is not null then clear text, also reset highlightedIndex and currentIndex
663 if (!text.isEmpty()) {
666 q->setText(QString());
667
668 if (isPopupVisible())
669 hidePopup(false);
670
671 emit q->clearButtonPressed();
672 }
673}
674
676{
677 QQuickControlPrivate::itemImplicitWidthChanged(item);
678 if (item == searchIndicator->indicator())
679 emit searchIndicator->implicitIndicatorWidthChanged();
680 if (item == clearIndicator->indicator())
681 emit clearIndicator->implicitIndicatorWidthChanged();
682}
683
685{
686 QQuickControlPrivate::itemImplicitHeightChanged(item);
687 if (item == searchIndicator->indicator())
688 emit searchIndicator->implicitIndicatorHeightChanged();
689 if (item == clearIndicator->indicator())
690 emit clearIndicator->implicitIndicatorHeightChanged();
691}
692
694{
695 QQuickControlPrivate::itemDestroyed(item);
696 if (item == searchIndicator->indicator())
697 searchIndicator->setIndicator(nullptr);
698 if (item == clearIndicator->indicator())
699 clearIndicator->setIndicator(nullptr);
700}
701
702QQuickSearchField::QQuickSearchField(QQuickItem *parent)
703 : QQuickControl(*(new QQuickSearchFieldPrivate), parent)
704{
705 Q_D(QQuickSearchField);
706 d->searchIndicator = new QQuickIndicatorButton(this);
707 d->clearIndicator = new QQuickIndicatorButton(this);
708
709 setFocusPolicy(Qt::StrongFocus);
710 setFlag(QQuickItem::ItemIsFocusScope);
711 setAcceptedMouseButtons(Qt::LeftButton);
712#if QT_CONFIG(cursor)
713 setCursor(Qt::ArrowCursor);
714#endif
715
716 d->init();
717}
718
719QQuickSearchField::~QQuickSearchField()
720{
721 Q_D(QQuickSearchField);
722 d->removeImplicitSizeListener(d->searchIndicator->indicator());
723 d->removeImplicitSizeListener(d->clearIndicator->indicator());
724
725 if (d->popup) {
726 QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::visibleChanged, d,
727 &QQuickSearchFieldPrivate::popupVisibleChanged);
728 d->hideOldPopup(d->popup);
729 d->popup = nullptr;
730 }
731}
732
733/*!
734 \qmlproperty model QtQuick.Controls::SearchField::suggestionModel
735
736 This property holds the data model used to display search suggestions in the popup menu.
737
738 \code
739 SearchField {
740 textRole: "age"
741 suggestionModel: ListModel {
742 ListElement { name: "Karen"; age: "66" }
743 ListElement { name: "Jim"; age: "32" }
744 ListElement { name: "Pamela"; age: "28" }
745 }
746 }
747 \endcode
748
749 \sa textRole
750 */
751
752QVariant QQuickSearchField::suggestionModel() const
753{
754 Q_D(const QQuickSearchField);
755 return d->suggestionModel;
756}
757
758void QQuickSearchField::setSuggestionModel(const QVariant &model)
759{
760 Q_D(QQuickSearchField);
761
762 QVariant suggestionModel = model;
763 if (suggestionModel.userType() == qMetaTypeId<QJSValue>())
764 suggestionModel = get<QJSValue>(std::move(suggestionModel)).toVariant();
765
766 if (d->suggestionModel == suggestionModel)
767 return;
768
769 d->suggestionModel = suggestionModel;
770 d->createDelegateModel();
771 emit suggestionCountChanged();
772 emit suggestionModelChanged();
773}
774
775/*!
776 \readonly
777 \qmlproperty model QtQuick.Controls::SearchField::delegateModel
778
779 This property holds the model that provides delegate instances for the search field.
780
781 It is typically assigned to a \l ListView in the \l {Popup::}{contentItem}
782 of the \l popup.
783
784 */
785QQmlInstanceModel *QQuickSearchField::delegateModel() const
786{
787 Q_D(const QQuickSearchField);
788 return d->delegateModel;
789}
790
791/*!
792 \readonly
793 \qmlproperty int QtQuick.Controls::SearchField::suggestionCount
794
795 This property holds the number of suggestions to display from the suggestion model.
796 */
797int QQuickSearchField::suggestionCount() const
798{
799 Q_D(const QQuickSearchField);
800 return d->delegateModel ? d->delegateModel->count() : 0;
801}
802
803/*!
804 \qmlproperty int QtQuick.Controls::SearchField::currentIndex
805
806 This property holds the index of the currently selected suggestion in the popup list.
807
808 Its value is \c -1 when no suggestion is selected.
809
810 currentIndex is not modified automatically when the model changes or when the user types
811 or edits text. It is updated only when the user explicitly selects a suggestion, either
812 by clicking an item in the popup, or by pressing Enter on a highlighted item.
813
814 currentIndex can be set; for example, to display the first item in the model at startup.
815 Before doing so, ensure that the model is not empty:
816
817 \snippet qtquickcontrols-searchfield-currentIndex.qml currentIndex
818
819 \sa activated(), text, highlightedIndex
820 */
821int QQuickSearchField::currentIndex() const
822{
823 Q_D(const QQuickSearchField);
824 return d->currentIndex;
825}
826
827void QQuickSearchField::setCurrentIndex(int index)
828{
829 Q_D(QQuickSearchField);
830 d->hasCurrentIndex = true;
831 d->setCurrentIndex(index);
832}
833
834/*!
835 \readonly
836 \qmlproperty int QtQuick.Controls::SearchField::highlightedIndex
837
838 This property holds the index of the currently highlighted item in
839 the popup list.
840
841 When the highlighted item is activated, the popup closes, \l currentIndex
842 is updated to match \c highlightedIndex, and this property is reset to
843 \c -1, indicating that no item is currently highlighted.
844
845 \sa highlighted(), currentIndex
846*/
847int QQuickSearchField::highlightedIndex() const
848{
849 Q_D(const QQuickSearchField);
850 return d->highlightedIndex;
851}
852
853/*!
854 \qmlproperty string QtQuick.Controls::SearchField::text
855
856 This property holds the current input text in the search field.
857
858 Text is bound to the user input, triggering suggestion updates or search logic.
859
860 \sa searchTriggered(), textEdited()
861 */
862QString QQuickSearchField::text() const
863{
864 Q_D(const QQuickSearchField);
865 return d->text;
866}
867
868void QQuickSearchField::setText(const QString &text)
869{
870 Q_D(QQuickSearchField);
871 if (d->text == text)
872 return;
873
874 d->text = text;
875 emit textChanged();
876}
877
878/*!
879 \qmlproperty string QtQuick.Controls::SearchField::textRole
880
881 This property holds the model role used to display items in the suggestion model
882 shown in the popup list.
883
884 When the model has multiple roles, \c textRole can be set to determine
885 which role should be displayed.
886 */
887QString QQuickSearchField::textRole() const
888{
889 Q_D(const QQuickSearchField);
890 return d->textRole;
891}
892
893void QQuickSearchField::setTextRole(const QString &textRole)
894{
895 Q_D(QQuickSearchField);
896 if (d->textRole == textRole)
897 return;
898
899 d->textRole = textRole;
900}
901
902/*!
903 \qmlproperty bool QtQuick.Controls::SearchField::live
904
905 This property holds a boolean value that determines whether the search is triggered
906 on every text edit.
907
908 When set to \c true, the \l searchTriggered() signal is emitted on each text change,
909 allowing you to respond to every keystroke.
910 When set to \c false, the \l searchTriggered() is only emitted when the user presses
911 the Enter or Return key.
912
913 \sa searchTriggered()
914 */
915
916bool QQuickSearchField::isLive() const
917{
918 Q_D(const QQuickSearchField);
919 return d->live;
920}
921
922void QQuickSearchField::setLive(const bool live)
923{
924 Q_D(QQuickSearchField);
925
926 if (d->live == live)
927 return;
928
929 d->live = live;
930}
931
932/*!
933 \include qquickindicatorbutton.qdocinc {properties} {SearchField} {searchIndicator}
934
935 This property holds the search indicator. Pressing it triggers
936 \l searchButtonPressed().
937
938 It is exposed so that styles and applications can customize it through its
939 internal properties (for example, replacing or removing the \c searchIndicator
940 via \c searchIndicator.indicator, or reacting to interaction state such as
941 pressed and hovered).
942
943 \sa {SearchField's Indicators}
944 */
945QQuickIndicatorButton *QQuickSearchField::searchIndicator() const
946{
947 Q_D(const QQuickSearchField);
948 return d->searchIndicator;
949}
950
951/*!
952 \include qquickindicatorbutton.qdocinc {properties} {SearchField} {clearIndicator}
953
954 This property holds the clear indicator. Pressing it triggers
955 \l clearButtonPressed().
956
957 It is exposed so that styles and applications can customize it through its
958 internal properties (for example, replacing or removing the \c clearIndicator
959 via \c clearIndicator.indicator, or reacting to interaction state such as
960 pressed and hovered).
961
962 \sa {SearchField's Indicators}
963*/
964QQuickIndicatorButton *QQuickSearchField::clearIndicator() const
965{
966 Q_D(const QQuickSearchField);
967 return d->clearIndicator;
968}
969
970
971/*!
972 \qmlproperty Popup QtQuick.Controls::SearchField::popup
973
974 This property holds the popup.
975
976 The popup can be opened or closed manually, if necessary:
977
978 \code
979 onSpecialEvent: searchField.popup.close()
980 \endcode
981 */
982QQuickPopup *QQuickSearchField::popup() const
983{
984 QQuickSearchFieldPrivate *d = const_cast<QQuickSearchFieldPrivate *>(d_func());
985 if (!d->popup)
986 d->executePopup(isComponentComplete());
987 return d->popup;
988}
989
990void QQuickSearchField::setPopup(QQuickPopup *popup)
991{
992 Q_D(QQuickSearchField);
993 if (d->popup == popup)
994 return;
995
996 if (!d->popup.isExecuting())
997 d->cancelPopup();
998
999 if (d->popup) {
1000 QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::destroyed, d,
1001 &QQuickSearchFieldPrivate::popupDestroyed);
1002 QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::visibleChanged, d,
1003 &QQuickSearchFieldPrivate::popupVisibleChanged);
1004 QQuickSearchFieldPrivate::hideOldPopup(d->popup);
1005 }
1006
1007 if (popup) {
1008 QQuickPopupPrivate::get(popup)->allowVerticalFlip = true;
1009 popup->setClosePolicy(QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutsideParent);
1010 QObjectPrivate::connect(popup, &QQuickPopup::visibleChanged, d,
1011 &QQuickSearchFieldPrivate::popupVisibleChanged);
1012 // QQuickPopup does not derive from QQuickItemChangeListener, so we cannot use
1013 // QQuickItemChangeListener::itemDestroyed so we have to use QObject::destroyed
1014 QObjectPrivate::connect(popup, &QQuickPopup::destroyed, d,
1015 &QQuickSearchFieldPrivate::popupDestroyed);
1016
1017#if QT_CONFIG(quick_itemview)
1018 if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
1019 itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
1020#endif
1021 }
1022
1023 d->popup = popup;
1024 if (!d->popup.isExecuting())
1025 emit popupChanged();
1026}
1027
1028/*!
1029 \qmlproperty Component QtQuick.Controls::SearchField::delegate
1030
1031 This property holds a delegate that presents an item in the search field popup.
1032
1033 It is recommended to use \l ItemDelegate (or any other \l AbstractButton
1034 derivatives) as the delegate. This ensures that the interaction works as
1035 expected, and the popup will automatically close when appropriate. When
1036 other types are used as the delegate, the popup must be closed manually.
1037 For example, if \l MouseArea is used:
1038
1039 \code
1040 delegate: Rectangle {
1041 // ...
1042 MouseArea {
1043 // ...
1044 onClicked: searchField.popup.close()
1045 }
1046 }
1047 \endcode
1048
1049 \include delegate-ownership.qdocinc {no-ownership-since-6.11} {SearchField}
1050*/
1051QQmlComponent *QQuickSearchField::delegate() const
1052{
1053 Q_D(const QQuickSearchField);
1054 return d->delegate;
1055}
1056
1057void QQuickSearchField::setDelegate(QQmlComponent *delegate)
1058{
1059 Q_D(QQuickSearchField);
1060 if (d->delegate == delegate)
1061 return;
1062
1063 d->delegate = delegate;
1064 QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(d->delegateModel);
1065 if (delegateModel)
1066 delegateModel->setDelegate(d->delegate);
1067 emit delegateChanged();
1068}
1069
1070bool QQuickSearchField::eventFilter(QObject *object, QEvent *event)
1071{
1072 Q_D(QQuickSearchField);
1073
1074 switch (event->type()) {
1075 case QEvent::MouseButtonRelease: {
1076 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1077 if (input->hasFocus()) {
1078 if (!d->text.isEmpty() && !d->isPopupVisible() && (d->delegateModel && d->delegateModel->count() > 0))
1079 d->showPopup();
1080 }
1081 break;
1082 }
1083 case QEvent::FocusOut: {
1084 const bool hasActiveFocus = d->popup && d->popup->hasActiveFocus();
1085 const bool usingPopupWindows =
1086 d->popup ? QQuickPopupPrivate::get(d->popup)->usePopupWindow() : false;
1087 if (qGuiApp->focusObject() != this && !(hasActiveFocus && !usingPopupWindows))
1088 d->hidePopup(false);
1089 break;
1090 }
1091 default:
1092 break;
1093 }
1094 return QQuickControl::eventFilter(object, event);
1095}
1096
1097void QQuickSearchField::focusInEvent(QFocusEvent *event)
1098{
1099 Q_D(QQuickSearchField);
1100 QQuickControl::focusInEvent(event);
1101
1102 if ((event->reason() == Qt::TabFocusReason || event->reason() == Qt::BacktabFocusReason
1103 || event->reason() == Qt::ShortcutFocusReason)
1104 && d->contentItem)
1105 d->contentItem->forceActiveFocus(event->reason());
1106}
1107
1108void QQuickSearchField::focusOutEvent(QFocusEvent *event)
1109{
1110 Q_D(QQuickSearchField);
1111 QQuickControl::focusOutEvent(event);
1112
1113 const bool hasActiveFocus = d->popup && d->popup->hasActiveFocus();
1114 const bool usingPopupWindows = d->popup && QQuickPopupPrivate::get(d->popup)->usePopupWindow();
1115
1116 if (qGuiApp->focusObject() != d->contentItem && !(hasActiveFocus && !usingPopupWindows))
1117 d->hidePopup(false);
1118}
1119
1120void QQuickSearchField::hoverEnterEvent(QHoverEvent *event)
1121{
1122 Q_D(QQuickSearchField);
1123 QQuickControl::hoverEnterEvent(event);
1124 QQuickItem *si = d->searchIndicator->indicator();
1125 QQuickItem *ci = d->clearIndicator->indicator();
1126 d->searchIndicator->setHovered(si && si->isEnabled() && si->contains(mapToItem(si, event->position())));
1127 d->clearIndicator->setHovered(ci && ci->isEnabled() && ci->contains(mapToItem(ci, event->position())));
1128 event->ignore();
1129}
1130
1131void QQuickSearchField::hoverMoveEvent(QHoverEvent *event)
1132{
1133 Q_D(QQuickSearchField);
1134 QQuickControl::hoverMoveEvent(event);
1135 QQuickItem *si = d->searchIndicator->indicator();
1136 QQuickItem *ci = d->clearIndicator->indicator();
1137 d->searchIndicator->setHovered(si && si->isEnabled() && si->contains(mapToItem(si, event->position())));
1138 d->clearIndicator->setHovered(ci && ci->isEnabled() && ci->contains(mapToItem(ci, event->position())));
1139 event->ignore();
1140}
1141
1142void QQuickSearchField::hoverLeaveEvent(QHoverEvent *event)
1143{
1144 Q_D(QQuickSearchField);
1145 QQuickControl::hoverLeaveEvent(event);
1146 d->searchIndicator->setHovered(false);
1147 d->clearIndicator->setHovered(false);
1148 event->ignore();
1149}
1150
1151void QQuickSearchField::keyPressEvent(QKeyEvent *event)
1152{
1153 Q_D(QQuickSearchField);
1154
1155 const auto key = event->key();
1156
1157 if (!d->suggestionModel.isNull() && !d->text.isEmpty()) {
1158 switch (key) {
1159 case Qt::Key_Escape:
1160 case Qt::Key_Back:
1161 if (d->isPopupVisible()) {
1162 d->hidePopup(false);
1163 event->accept();
1164 } else {
1165 setText(QString());
1166 }
1167 break;
1168 case Qt::Key_Return:
1169 case Qt::Key_Enter:
1170 if (d->isPopupVisible())
1171 d->hidePopup(true);
1172 emit accepted();
1173 emit searchTriggered();
1174 event->accept();
1175 break;
1176 case Qt::Key_Up:
1177 d->decreaseCurrentIndex();
1178 event->accept();
1179 break;
1180 case Qt::Key_Down:
1181 d->increaseCurrentIndex();
1182 event->accept();
1183 break;
1184 case Qt::Key_Home:
1185 if (d->isPopupVisible())
1186 d->setHighlightedIndex(0, Highlight);
1187 event->accept();
1188 break;
1189 case Qt::Key_End:
1190 if (d->isPopupVisible())
1191 d->setHighlightedIndex(suggestionCount() - 1, Highlight);
1192 event->accept();
1193 break;
1194 default:
1195 QQuickControl::keyPressEvent(event);
1196 break;
1197 }
1198 }
1199}
1200
1201void QQuickSearchField::classBegin()
1202{
1203 Q_D(QQuickSearchField);
1204 QQuickControl::classBegin();
1205
1206 QQmlContext *context = qmlContext(this);
1207 if (context) {
1208 QQmlEngine::setContextForObject(d->searchIndicator, context);
1209 QQmlEngine::setContextForObject(d->clearIndicator, context);
1210 }
1211}
1212
1213void QQuickSearchField::componentComplete()
1214{
1215 Q_D(QQuickSearchField);
1216 QQuickIndicatorButtonPrivate::get(d->searchIndicator)->executeIndicator(true);
1217 QQuickIndicatorButtonPrivate::get(d->clearIndicator)->executeIndicator(true);
1218 QQuickControl::componentComplete();
1219
1220 if (d->popup)
1221 d->executePopup(true);
1222
1223 if (d->delegateModel && d->ownModel)
1224 static_cast<QQmlDelegateModel *>(d->delegateModel)->componentComplete();
1225}
1226
1227void QQuickSearchField::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
1228{
1229 Q_D(QQuickSearchField);
1230 if (oldItem) {
1231 oldItem->removeEventFilter(this);
1232 if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem)) {
1233 QObjectPrivate::disconnect(oldInput, &QQuickTextInput::textChanged, d,
1234 &QQuickSearchFieldPrivate::updateText);
1235 }
1236 }
1237
1238 if (newItem) {
1239 newItem->installEventFilter(this);
1240 if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem)) {
1241 QObjectPrivate::connect(newInput, &QQuickTextInput::textChanged, d,
1242 &QQuickSearchFieldPrivate::updateText);
1243 }
1244 #if QT_CONFIG(cursor)
1245 newItem->setCursor(Qt::IBeamCursor);
1246 #endif
1247 }
1248}
1249
1250void QQuickSearchField::itemChange(ItemChange change, const ItemChangeData &data)
1251{
1252 Q_D(QQuickSearchField);
1253 QQuickControl::itemChange(change, data);
1254 if (change == ItemVisibleHasChanged && !data.boolValue) {
1255 d->hidePopup(false);
1256 d->setCurrentItemAtIndex(-1, NoActivate);
1257 }
1258}
1259
1260QT_END_NAMESPACE
1261
1262#include "moc_qquicksearchfield_p.cpp"
void createdItem(int index, QObject *object)
QQuickIndicatorButton * clearIndicator
bool handleRelease(const QPointF &point, ulong timestamp) override
void itemDestroyed(QQuickItem *item) override
bool isValidIndex(int index) const
static void hideOldPopup(QQuickPopup *popup)
QQuickDeferredPointer< QQuickPopup > popup
void itemImplicitWidthChanged(QQuickItem *item) override
void itemImplicitHeightChanged(QQuickItem *item) override
void setCurrentItemAtIndex(int index, Activation activate)
bool handlePress(const QPointF &point, ulong timestamp) override
void setHighlightedIndex(int index, Highlighting highlight)
QString textAt(int index) const
void executePopup(bool complete=false)
QQmlInstanceModel * delegateModel
QQuickIndicatorButton * searchIndicator
Combined button and popup list for selecting options.