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 sourceModel: fruitModel
105 sorters: [
106 RoleSorter {
107 roleName: "name"
108 }
109 ]
110 filters: [
111 FunctionFilter {
112 property var regExp: new RegExp(fruitSearch.text, "i")
113 onRegExpChanged: invalidate()
114 function filter(name: string): bool {
115 return regExp.test(name);
116 }
117 }
118 ]
119 }
120
121 SearchField {
122 id: fruitSearch
123 suggestionModel: fruitFilter
124 textRole: "name"
125 anchors.horizontalCenter: parent.horizontalCenter
126 }
127 \endcode
128 */
129
130/*!
131 \qmlsignal void QtQuick.Controls::SearchField::activated(int index)
132
133 This signal is emitted when the item at \a index is activated by the user.
134
135 An item is activated when it is selected while the popup is open,
136 causing the popup to close (and \l currentIndex to change).
137 The \l currentIndex property is set to \a index.
138
139 \sa currentIndex
140*/
141
142
143/*!
144 \qmlsignal void QtQuick.Controls::SearchField::highlighted(int index)
145
146 This signal is emitted when the item at \a index in the popup list is highlighted by the user.
147
148 The highlighted signal is only emitted when the popup is open and an item
149 is highlighted, but not necessarily \l activated.
150
151 \sa highlightedIndex
152*/
153
154/*!
155 \qmlsignal void QtQuick.Controls::SearchField::accepted()
156
157 This signal is emitted when the user confirms their input by pressing
158 the Enter or Return key.
159
160 This signal is typically used to trigger a search or action based on
161 the final text input, and it indicates the user's intention to complete
162 or submit the query.
163
164 \sa searchTriggered()
165 */
166
167/*!
168 \qmlsignal void QtQuick.Controls::SearchField::searchTriggered()
169
170 This signal is emitted when a search action is initiated.
171
172 It occurs in two cases:
173 1. When the Enter or Return key is pressed, it will be emitted together
174 with accepted() signal
175 2. When the text is edited and if the \l live property is set to \c true,
176 this signal will be emitted.
177
178 This signal is ideal for initiating searches both on-demand and in real-time as
179 the user types, depending on the desired interaction model.
180
181 \sa accepted(), textEdited()
182 */
183
184/*!
185 \qmlsignal void QtQuick.Controls::SearchField::textEdited()
186
187 This signal is emitted every time the user modifies the text in the
188 search field, typically with each keystroke.
189
190 \sa searchTriggered()
191 */
192
193/*!
194 \qmlsignal void QtQuick.Controls::SearchField::searchButtonPressed()
195
196 This signal is emitted when the search button is pressed.
197
198 \sa clearButtonPressed()
199*/
200
201/*!
202 \qmlsignal void QtQuick.Controls::SearchField::clearButtonPressed()
203
204 This signal is emitted when the clear button is pressed.
205
206 \sa searchButtonPressed()
207*/
208
209namespace {
212}
213
215{
216public:
217 Q_DECLARE_PUBLIC(QQuickSearchField)
218
219 bool isPopupVisible() const;
220 void showPopup();
221 void hidePopup(bool accept);
222 static void hideOldPopup(QQuickPopup *popup);
225
228
229 void createdItem(int index, QObject *object);
231
234 void setCurrentIndex(int index);
235 void setCurrentItemAtIndex(int index, Activation activate);
237 void setHighlightedIndex(int index, Highlighting highlight);
238
240
242
243 QString currentTextRole() const;
246 QString textAt(int index) const;
247 bool isValidIndex(int index) const;
248
250 void executePopup(bool complete = false);
251
252 bool handlePress(const QPointF &point, ulong timestamp) override;
253 bool handleRelease(const QPointF &point, ulong timestamp) override;
254
257
258 void itemImplicitWidthChanged(QQuickItem *item) override;
259 void itemImplicitHeightChanged(QQuickItem *item) override;
260 void itemDestroyed(QQuickItem *item) override;
261
262 static inline QString popupName() { return QStringLiteral("popup"); }
263
266 int currentIndex = -1;
268 QString text;
269 QString textRole;
271 bool hasCurrentIndex = false;
272 bool live = true;
273 bool searchPressed = false;
274 bool clearPressed = false;
275 bool searchFlat = false;
276 bool clearFlat = false;
277 bool searchDown = false;
278 bool clearDown = false;
279 bool hasSearchDown = false;
280 bool hasClearDown = false;
281 bool ownModel = false;
282 bool selectTextByMouse = true;
283 QQmlInstanceModel *delegateModel = nullptr;
284 QQmlComponent *delegate = nullptr;
285 QQuickIndicatorButton *searchIndicator = nullptr;
286 QQuickIndicatorButton *clearIndicator = nullptr;
288};
289
290bool QQuickSearchFieldPrivate::isPopupVisible() const
291{
292 return popup && popup->isVisible();
293}
294
296{
297 if (!popup)
298 executePopup(true);
299
300 if (popup && !popup->isVisible())
301 popup->open();
302}
303
305{
306 Q_Q(QQuickSearchField);
307 if (accept) {
309 // hiding the popup on user interaction should always emit activated,
310 // even if the current index didn't change
311 emit q->activated(highlightedIndex);
312 }
313 if (popup && popup->isVisible())
314 popup->close();
315}
316
317void QQuickSearchFieldPrivate::hideOldPopup(QQuickPopup *popup)
318{
319 if (!popup)
320 return;
321
322 qCDebug(lcItemManagement) << "hiding old popup" << popup;
323
324 popup->setVisible(false);
325 popup->setParentItem(nullptr);
326#if QT_CONFIG(accessibility)
327 // Remove the item from the accessibility tree.
328 QQuickAccessibleAttached *accessible = accessibleAttached(popup);
329 if (accessible)
330 accessible->setIgnored(true);
331#endif
332}
333
335{
336 if (isPopupVisible())
337 QGuiApplication::inputMethod()->reset();
338
339#if QT_CONFIG(quick_itemview)
340 QQuickItemView *itemView = popup->findChild<QQuickItemView *>();
341 if (itemView)
342 itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
343#endif
344
346
347#if QT_CONFIG(quick_itemview)
348 if (itemView)
349 itemView->positionViewAtIndex(highlightedIndex, QQuickItemView::Beginning);
350#endif
351}
352
354{
355 Q_Q(QQuickSearchField);
356 popup = nullptr;
357 emit q->popupChanged();
358}
359
361{
362 Q_Q(QQuickSearchField);
363 int index = delegateModel->indexOf(q->sender(), nullptr);
364 if (index != -1) {
366 hidePopup(true);
367 }
368}
369
371{
372 Q_Q(QQuickSearchField);
373
374 QQuickAbstractButton *button = qobject_cast<QQuickAbstractButton *>(q->sender());
375 if (!button || !button->isHovered() || !button->isEnabled()
376 || QQuickAbstractButtonPrivate::get(button)->touchId != -1)
377 return;
378
379 int index = delegateModel->indexOf(button, nullptr);
380 if (index != -1) {
382
383#if QT_CONFIG(quick_itemview)
384 if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
385 itemView->positionViewAtIndex(index, QQuickItemView::Contain);
386#endif
387 }
388}
389
390void QQuickSearchFieldPrivate::createdItem(int index, QObject *object)
391{
392 Q_UNUSED(index);
393 Q_Q(QQuickSearchField);
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(button, &QQuickAbstractButton::clicked, this,
408 connect(button, &QQuickAbstractButton::hoveredChanged, this,
410 }
411}
412
414{
415 Q_Q(QQuickSearchField);
416
417 if (q->suggestionCount() == 0) {
420 emit q->suggestionCountChanged();
421 return;
422 }
423
424 // If the suggestionModel has been updated and the current text matches an item in
425 // the model, update currentIndex and highlightedIndex to the index of that item.
426 if (!text.isEmpty()) {
427 for (int idx = 0; idx < q->suggestionCount(); ++idx) {
428 if (textAt(idx) == text) {
431 break;
432 }
433 }
434 }
435
436 emit q->suggestionCountChanged();
437}
438
440{
441 Q_Q(QQuickSearchField);
442 if (isPopupVisible()) {
443 if (highlightedIndex < q->suggestionCount() - 1)
445 }
446}
447
449{
450 if (isPopupVisible()) {
451 if (highlightedIndex > 0)
453 }
454}
455
457{
458 Q_Q(QQuickSearchField);
459 if (currentIndex == index)
460 return;
461
462 currentIndex = index;
463 emit q->currentIndexChanged();
464}
465
467{
468 Q_Q(QQuickSearchField);
469 if (currentIndex == index)
470 return;
471
472 currentIndex = index;
473 emit q->currentIndexChanged();
474
476
477 if (activate)
478 emit q->activated(index);
479}
480
482{
483 Q_Q(QQuickSearchField);
484 int index = -1;
485
486 if (isPopupVisible()) {
487 if (currentIndex >= 0)
488 index = currentIndex;
489 else if (q->suggestionCount() > 0)
490 index = 0; // auto-highlight first suggestion
491 }
492
494}
495
497{
498 Q_Q(QQuickSearchField);
499 if (highlightedIndex == index)
500 return;
501
502 highlightedIndex = index;
503 emit q->highlightedIndexChanged();
504
505 if (highlight)
506 emit q->highlighted(index);
507}
508
510{
511 Q_Q(QQuickSearchField);
512
513 if (!contentItem)
514 return;
515
516 const QQuickTextInput *input = qobject_cast<QQuickTextInput *>(contentItem);
517 if (!input)
518 return;
519
520 const int pos = input->cursorPosition();
521 if (cursorPosition == pos)
522 return;
523
524 cursorPosition = pos;
525 emit q->cursorPositionChanged();
526}
527
529{
530 Q_Q(QQuickSearchField);
531 bool ownedOldModel = ownModel;
532 QQmlInstanceModel *oldModel = delegateModel;
533
534 if (oldModel) {
535 disconnect(delegateModel, &QQmlInstanceModel::countChanged, this,
537 disconnect(delegateModel, &QQmlInstanceModel::createdItem, this,
539 }
540
541 ownModel = false;
542 delegateModel = suggestionModel.value<QQmlInstanceModel *>();
543
544 if (!delegateModel && suggestionModel.isValid()) {
545 QQmlDelegateModel *dataModel = new QQmlDelegateModel(qmlContext(q), q);
546 dataModel->setModel(suggestionModel);
547 dataModel->setDelegate(delegate);
548 if (q->isComponentComplete())
549 dataModel->componentComplete();
550
551 ownModel = true;
552 delegateModel = dataModel;
553 }
554
555 if (delegateModel) {
556 connect(delegateModel, &QQmlInstanceModel::countChanged, this,
558 connect(delegateModel, &QQmlInstanceModel::createdItem, this,
560 }
561
562 emit q->delegateModelChanged();
563
564 if (ownedOldModel)
565 delete oldModel;
566}
567
569{
570 return textRole.isEmpty() ? QStringLiteral("modelData") : textRole;
571}
572
574{
575 Q_Q(QQuickSearchField);
576 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(contentItem);
577 if (!input)
578 return;
579
580 const QString textInput = input->text();
581
582 if (text != textInput) {
583 q->setText(textInput);
584 emit q->textEdited();
585
588
589 if (live)
590 emit q->searchTriggered();
591 }
592
593 if (text.isEmpty()) {
594 if (isPopupVisible())
595 hidePopup(false);
596 } else {
597 if (delegateModel && delegateModel->count() > 0) {
598 if (!isPopupVisible())
600 } else {
601 if (isPopupVisible())
602 hidePopup(false);
603 }
604 }
605}
606
608{
609 Q_Q(QQuickSearchField);
610 if (currentIndex < 0)
611 return;
612
613 const QString currentText = textAt(currentIndex);
614 if (text != currentText)
615 q->setText(currentText);
616}
617
618QString QQuickSearchFieldPrivate::textAt(int index) const
619{
620 if (!isValidIndex(index))
621 return QString();
622
623 return delegateModel->stringValue(index, currentTextRole());
624}
625
627{
628 return delegateModel && index >= 0 && index < delegateModel->count();
629}
630
632{
633 Q_Q(QQuickSearchField);
634 quickCancelDeferred(q, popupName());
635}
636
638{
639 Q_Q(QQuickSearchField);
640 if (popup.wasExecuted())
641 return;
642
643 if (!popup || complete)
644 quickBeginDeferred(q, popupName(), popup);
645 if (complete)
646 quickCompleteDeferred(q, popupName(), popup);
647}
648
649bool QQuickSearchFieldPrivate::handlePress(const QPointF &point, ulong timestamp)
650{
651 Q_Q(QQuickSearchField);
652 QQuickControlPrivate::handlePress(point, timestamp);
653
654 QQuickItem *si = searchIndicator->indicator();
655 QQuickItem *ci = clearIndicator->indicator();
656 const bool isSearch = si && si->isEnabled() && si->contains(q->mapToItem(si, point));
657 const bool isClear = ci && ci->isEnabled() && ci->contains(q->mapToItem(ci, point));
658
659 if (isSearch) {
660 searchIndicator->setPressed(true);
662 } else if (isClear) {
663 clearIndicator->setPressed(true);
665 }
666
667 return true;
668}
669
670bool QQuickSearchFieldPrivate::handleRelease(const QPointF &point, ulong timestamp)
671{
672 QQuickControlPrivate::handleRelease(point, timestamp);
673 if (searchIndicator->isPressed())
674 searchIndicator->setPressed(false);
675 else if (clearIndicator->isPressed())
676 clearIndicator->setPressed(false);
677 return true;
678}
679
681{
682 Q_Q(QQuickSearchField);
683
684 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(contentItem);
685 if (!input)
686 return;
687
688 input->forceActiveFocus();
689 emit q->searchButtonPressed();
690}
691
693{
694 Q_Q(QQuickSearchField);
695
696 if (text.isEmpty())
697 return;
698
699 // if text is not null then clear text, also reset highlightedIndex and currentIndex
700 if (!text.isEmpty()) {
703 q->setText(QString());
704
705 if (isPopupVisible())
706 hidePopup(false);
707
708 emit q->clearButtonPressed();
709 }
710}
711
713{
714 QQuickControlPrivate::itemImplicitWidthChanged(item);
715 if (item == searchIndicator->indicator())
716 emit searchIndicator->implicitIndicatorWidthChanged();
717 if (item == clearIndicator->indicator())
718 emit clearIndicator->implicitIndicatorWidthChanged();
719}
720
722{
723 QQuickControlPrivate::itemImplicitHeightChanged(item);
724 if (item == searchIndicator->indicator())
725 emit searchIndicator->implicitIndicatorHeightChanged();
726 if (item == clearIndicator->indicator())
727 emit clearIndicator->implicitIndicatorHeightChanged();
728}
729
731{
732 QQuickControlPrivate::itemDestroyed(item);
733 if (item == searchIndicator->indicator())
734 searchIndicator->setIndicator(nullptr);
735 if (item == clearIndicator->indicator())
736 clearIndicator->setIndicator(nullptr);
737}
738
739QQuickSearchField::QQuickSearchField(QQuickItem *parent)
740 : QQuickControl(*(new QQuickSearchFieldPrivate), parent)
741{
742 Q_D(QQuickSearchField);
743 d->searchIndicator = new QQuickIndicatorButton(this);
744 d->clearIndicator = new QQuickIndicatorButton(this);
745
746 setFocusPolicy(Qt::StrongFocus);
747 setFlag(QQuickItem::ItemIsFocusScope);
748 setAcceptedMouseButtons(Qt::LeftButton);
749#if QT_CONFIG(cursor)
750 setCursor(Qt::ArrowCursor);
751#endif
752
753 d->init();
754}
755
756QQuickSearchField::~QQuickSearchField()
757{
758 Q_D(QQuickSearchField);
759 d->removeImplicitSizeListener(d->searchIndicator->indicator());
760 d->removeImplicitSizeListener(d->clearIndicator->indicator());
761
762 if (d->popup) {
763 QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::visibleChanged, d,
764 &QQuickSearchFieldPrivate::popupVisibleChanged);
765 d->hideOldPopup(d->popup);
766 d->popup = nullptr;
767 }
768}
769
770/*!
771 \qmlproperty model QtQuick.Controls::SearchField::suggestionModel
772
773 This property holds the data model used to display search suggestions in the popup menu.
774
775 \code
776 SearchField {
777 textRole: "age"
778 suggestionModel: ListModel {
779 ListElement { name: "Karen"; age: "66" }
780 ListElement { name: "Jim"; age: "32" }
781 ListElement { name: "Pamela"; age: "28" }
782 }
783 }
784 \endcode
785
786 \sa textRole
787 */
788
789QVariant QQuickSearchField::suggestionModel() const
790{
791 Q_D(const QQuickSearchField);
792 return d->suggestionModel;
793}
794
795void QQuickSearchField::setSuggestionModel(const QVariant &model)
796{
797 Q_D(QQuickSearchField);
798
799 QVariant suggestionModel;
800 if (QJSValue *value = get_if<QJSValue>(&suggestionModel))
801 suggestionModel = value->toVariant();
802 else
803 suggestionModel = model;
804
805 if (d->suggestionModel == suggestionModel)
806 return;
807
808 d->suggestionModel = suggestionModel;
809 d->createDelegateModel();
810 emit suggestionCountChanged();
811 emit suggestionModelChanged();
812}
813
814/*!
815 \readonly
816 \qmlproperty model QtQuick.Controls::SearchField::delegateModel
817
818 This property holds the model that provides delegate instances for the search field.
819
820 It is typically assigned to a \l ListView in the \l {Popup::}{contentItem}
821 of the \l popup.
822
823 */
824QQmlInstanceModel *QQuickSearchField::delegateModel() const
825{
826 Q_D(const QQuickSearchField);
827 return d->delegateModel;
828}
829
830/*!
831 \readonly
832 \qmlproperty int QtQuick.Controls::SearchField::suggestionCount
833
834 This property holds the number of suggestions to display from the suggestion model.
835 */
836int QQuickSearchField::suggestionCount() const
837{
838 Q_D(const QQuickSearchField);
839 return d->delegateModel ? d->delegateModel->count() : 0;
840}
841
842/*!
843 \qmlproperty int QtQuick.Controls::SearchField::currentIndex
844
845 This property holds the index of the currently selected suggestion in the popup list.
846
847 Its value is \c -1 when no suggestion is selected.
848
849 currentIndex is not modified automatically when the model changes or when the user types
850 or edits text. It is updated only when the user explicitly selects a suggestion, either
851 by clicking an item in the popup, or by pressing Enter on a highlighted item.
852
853 currentIndex can be set; for example, to display the first item in the model at startup.
854 Before doing so, ensure that the model is not empty:
855
856 \snippet qtquickcontrols-searchfield-currentIndex.qml currentIndex
857
858 \sa activated(), text, highlightedIndex
859 */
860int QQuickSearchField::currentIndex() const
861{
862 Q_D(const QQuickSearchField);
863 return d->currentIndex;
864}
865
866void QQuickSearchField::setCurrentIndex(int index)
867{
868 Q_D(QQuickSearchField);
869 d->hasCurrentIndex = true;
870 d->setCurrentIndex(index);
871}
872
873/*!
874 \readonly
875 \qmlproperty int QtQuick.Controls::SearchField::highlightedIndex
876
877 This property holds the index of the currently highlighted item in
878 the popup list.
879
880 When the highlighted item is activated, the popup closes, \l currentIndex
881 is updated to match \c highlightedIndex, and this property is reset to
882 \c -1, indicating that no item is currently highlighted.
883
884 \sa highlighted(), currentIndex
885*/
886int QQuickSearchField::highlightedIndex() const
887{
888 Q_D(const QQuickSearchField);
889 return d->highlightedIndex;
890}
891
892/*!
893 \qmlproperty string QtQuick.Controls::SearchField::text
894
895 This property holds the current input text in the search field.
896
897 Text is bound to the user input, triggering suggestion updates or search logic.
898
899 \sa searchTriggered(), textEdited()
900 */
901QString QQuickSearchField::text() const
902{
903 Q_D(const QQuickSearchField);
904 return d->text;
905}
906
907void QQuickSearchField::setText(const QString &text)
908{
909 Q_D(QQuickSearchField);
910 if (d->text == text)
911 return;
912
913 d->text = text;
914 emit textChanged();
915}
916
917/*!
918 \qmlproperty string QtQuick.Controls::SearchField::textRole
919
920 This property holds the model role used to display items in the suggestion model
921 shown in the popup list.
922
923 When the model has multiple roles, \c textRole can be set to determine
924 which role should be displayed.
925 */
926QString QQuickSearchField::textRole() const
927{
928 Q_D(const QQuickSearchField);
929 return d->textRole;
930}
931
932void QQuickSearchField::setTextRole(const QString &textRole)
933{
934 Q_D(QQuickSearchField);
935 if (d->textRole == textRole)
936 return;
937
938 d->textRole = textRole;
939}
940
941/*!
942 \qmlproperty bool QtQuick.Controls::SearchField::live
943
944 This property holds a boolean value that determines whether the search is triggered
945 on every text edit.
946
947 When set to \c true, the \l searchTriggered() signal is emitted on each text change,
948 allowing you to respond to every keystroke.
949 When set to \c false, the \l searchTriggered() is only emitted when the user presses
950 the Enter or Return key.
951
952 \sa searchTriggered()
953 */
954
955bool QQuickSearchField::isLive() const
956{
957 Q_D(const QQuickSearchField);
958 return d->live;
959}
960
961void QQuickSearchField::setLive(const bool live)
962{
963 Q_D(QQuickSearchField);
964
965 if (d->live == live)
966 return;
967
968 d->live = live;
969}
970
971/*!
972 \include qquickindicatorbutton.qdocinc {properties} {SearchField} {searchIndicator}
973
974 This property holds the search indicator. Pressing it triggers
975 \l searchButtonPressed.
976
977 It is exposed so that styles and applications can customize it through its
978 internal properties (for example, replacing or removing the \c searchIndicator
979 via \c searchIndicator.indicator, or reacting to interaction state such as
980 pressed and hovered).
981
982 \sa {SearchField's Indicators}
983 */
984QQuickIndicatorButton *QQuickSearchField::searchIndicator() const
985{
986 Q_D(const QQuickSearchField);
987 return d->searchIndicator;
988}
989
990/*!
991 \include qquickindicatorbutton.qdocinc {properties} {SearchField} {clearIndicator}
992
993 This property holds the clear indicator. Pressing it triggers
994 \l clearButtonPressed.
995
996 It is exposed so that styles and applications can customize it through its
997 internal properties (for example, replacing or removing the \c clearIndicator
998 via \c clearIndicator.indicator, or reacting to interaction state such as
999 pressed and hovered).
1000
1001 \sa {SearchField's Indicators}
1002*/
1003QQuickIndicatorButton *QQuickSearchField::clearIndicator() const
1004{
1005 Q_D(const QQuickSearchField);
1006 return d->clearIndicator;
1007}
1008
1009
1010/*!
1011 \qmlproperty Popup QtQuick.Controls::SearchField::popup
1012
1013 This property holds the popup.
1014
1015 The popup can be opened or closed manually, if necessary:
1016
1017 \code
1018 onSpecialEvent: searchField.popup.close()
1019 \endcode
1020 */
1021QQuickPopup *QQuickSearchField::popup() const
1022{
1023 QQuickSearchFieldPrivate *d = const_cast<QQuickSearchFieldPrivate *>(d_func());
1024 if (!d->popup)
1025 d->executePopup(isComponentComplete());
1026 return d->popup;
1027}
1028
1029void QQuickSearchField::setPopup(QQuickPopup *popup)
1030{
1031 Q_D(QQuickSearchField);
1032 if (d->popup == popup)
1033 return;
1034
1035 if (!d->popup.isExecuting())
1036 d->cancelPopup();
1037
1038 if (d->popup) {
1039 QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::destroyed, d,
1040 &QQuickSearchFieldPrivate::popupDestroyed);
1041 QObjectPrivate::disconnect(d->popup.data(), &QQuickPopup::visibleChanged, d,
1042 &QQuickSearchFieldPrivate::popupVisibleChanged);
1043 QQuickSearchFieldPrivate::hideOldPopup(d->popup);
1044 }
1045
1046 if (popup) {
1047#if QT_CONFIG(wayland)
1048 QQuickPopupPrivate::get(popup)->extendedWindowType = QNativeInterface::Private::QWaylandWindow::ComboBox;
1049#endif
1050#if QT_CONFIG(xcb)
1051 QQuickPopupPrivate::get(popup)->wmWindowType = QNativeInterface::Private::QXcbWindow::Combo;
1052#endif
1053 QQuickPopupPrivate::get(popup)->allowVerticalFlip = true;
1054 popup->setClosePolicy(QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutsideParent);
1055 QObjectPrivate::connect(popup, &QQuickPopup::visibleChanged, d,
1056 &QQuickSearchFieldPrivate::popupVisibleChanged);
1057 // QQuickPopup does not derive from QQuickItemChangeListener, so we cannot use
1058 // QQuickItemChangeListener::itemDestroyed so we have to use QObject::destroyed
1059 QObjectPrivate::connect(popup, &QQuickPopup::destroyed, d,
1060 &QQuickSearchFieldPrivate::popupDestroyed);
1061
1062#if QT_CONFIG(quick_itemview)
1063 if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
1064 itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
1065#endif
1066 }
1067
1068 d->popup = popup;
1069 if (!d->popup.isExecuting())
1070 emit popupChanged();
1071}
1072
1073/*!
1074 \qmlproperty Component QtQuick.Controls::SearchField::delegate
1075
1076 This property holds a delegate that presents an item in the search field popup.
1077
1078 It is recommended to use \l ItemDelegate (or any other \l AbstractButton
1079 derivatives) as the delegate. This ensures that the interaction works as
1080 expected, and the popup will automatically close when appropriate. When
1081 other types are used as the delegate, the popup must be closed manually.
1082 For example, if \l MouseArea is used:
1083
1084 \code
1085 delegate: Rectangle {
1086 // ...
1087 MouseArea {
1088 // ...
1089 onClicked: searchField.popup.close()
1090 }
1091 }
1092 \endcode
1093
1094 \include delegate-ownership.qdocinc {no-ownership-since-6.11} {SearchField}
1095*/
1096QQmlComponent *QQuickSearchField::delegate() const
1097{
1098 Q_D(const QQuickSearchField);
1099 return d->delegate;
1100}
1101
1102void QQuickSearchField::setDelegate(QQmlComponent *delegate)
1103{
1104 Q_D(QQuickSearchField);
1105 if (d->delegate == delegate)
1106 return;
1107
1108 d->delegate = delegate;
1109 QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(d->delegateModel);
1110 if (delegateModel)
1111 delegateModel->setDelegate(d->delegate);
1112 emit delegateChanged();
1113}
1114
1115/*!
1116 \qmlproperty string QtQuick.Controls::SearchField::placeholderText
1117 \since 6.12
1118
1119 This property holds the hint that is displayed in the SearchField before the user
1120 enters text.
1121*/
1122QString QQuickSearchField::placeholderText() const
1123{
1124 Q_D(const QQuickSearchField);
1125 return d->placeholderText;
1126}
1127
1128void QQuickSearchField::setPlaceholderText(const QString &text)
1129{
1130 Q_D(QQuickSearchField);
1131 if (d->placeholderText == text)
1132 return;
1133
1134 d->placeholderText = text;
1135 emit placeholderTextChanged();
1136}
1137
1138/*!
1139 \qmlproperty bool QtQuick.Controls::SearchField::selectTextByMouse
1140 \since 6.12
1141
1142 This property holds whether the text can be selected with the mouse.
1143
1144 The default value is \c true.
1145*/
1146bool QQuickSearchField::selectTextByMouse() const
1147{
1148 Q_D(const QQuickSearchField);
1149 return d->selectTextByMouse;
1150}
1151
1152void QQuickSearchField::setSelectTextByMouse(const bool selectable)
1153{
1154 Q_D(QQuickSearchField);
1155 if (d->selectTextByMouse == selectable)
1156 return;
1157
1158 d->selectTextByMouse = selectable;
1159 emit selectTextByMouseChanged();
1160}
1161
1162/*!
1163 \readonly
1164 \qmlproperty string QtQuick.Controls::SearchField::selectedText
1165 \since 6.12
1166
1167 This read-only property holds the currently selected text.
1168*/
1169QString QQuickSearchField::selectedText() const
1170{
1171 Q_D(const QQuickSearchField);
1172 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1173 if (!input)
1174 return QString();
1175 return input->selectedText();
1176}
1177
1178/*!
1179 \readonly
1180 \qmlproperty int QtQuick.Controls::SearchField::selectionStart
1181 \since 6.12
1182
1183 The cursor position before the first character in the current selection.
1184
1185 This property is read-only. To change the selection, use select(start, end),
1186 selectAll(), or selectWord().
1187
1188 \sa selectionEnd, cursorPosition, selectedText
1189*/
1190int QQuickSearchField::selectionStart() const
1191{
1192 Q_D(const QQuickSearchField);
1193 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1194 if (!input)
1195 return 0;
1196 return input->selectionStart();
1197}
1198
1199/*!
1200 \readonly
1201 \qmlproperty int QtQuick.Controls::SearchField::selectionEnd
1202 \since 6.12
1203
1204 The cursor position after the last character in the current selection.
1205
1206 This property is read-only. To change the selection, use select(start, end),
1207 selectAll(), or selectWord().
1208
1209 \sa selectionStart, cursorPosition, selectedText
1210*/
1211int QQuickSearchField::selectionEnd() const
1212{
1213 Q_D(const QQuickSearchField);
1214 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1215 if (!input)
1216 return 0;
1217 return input->selectionEnd();
1218}
1219
1220/*!
1221 \qmlproperty int QtQuick.Controls::SearchField::cursorPosition
1222 \since 6.12
1223
1224 The position of the cursor in the text field. The cursor is positioned between
1225 characters.
1226
1227 \note The \e characters in this case refer to the string of \l QChar objects,
1228 therefore 16-bit Unicode characters, and the position is considered an index
1229 into this string. This does not necessarily correspond to individual graphemes
1230 in the writing system, as a single grapheme may be represented by multiple
1231 Unicode characters, such as in the case of surrogate pairs, linguistic
1232 ligatures or diacritics.
1233*/
1234int QQuickSearchField::cursorPosition() const
1235{
1236 Q_D(const QQuickSearchField);
1237 return d->cursorPosition;
1238}
1239
1240void QQuickSearchField::setCursorPosition(int position)
1241{
1242 Q_D(QQuickSearchField);
1243 if (d->cursorPosition == position)
1244 return;
1245
1246 d->cursorPosition = position;
1247
1248 if (QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem)) {
1249 if (input->cursorPosition() != position)
1250 input->setCursorPosition(position);
1251 }
1252
1253 emit cursorPositionChanged();
1254}
1255
1256/*!
1257 \qmlmethod void QtQuick.Controls::SearchField::selectAll()
1258 \since 6.12
1259
1260 Selects all text in the text field of the control.
1261*/
1262void QQuickSearchField::selectAll()
1263{
1264 Q_D(QQuickSearchField);
1265 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1266 if (!input)
1267 return;
1268 input->selectAll();
1269}
1270
1271/*!
1272 \qmlmethod void QtQuick.Controls::SearchField::select(int start, int end)
1273 \since 6.12
1274
1275 Causes the text from \a start to \a end to be selected.
1276
1277 If either \a start or \a end is out of range, the selection is not changed.
1278
1279 After calling this, selectionStart will become the lesser
1280 and selectionEnd will become the greater (regardless of the order passed
1281 to this method).
1282
1283 \sa selectionStart, selectionEnd
1284*/
1285void QQuickSearchField::select(int start, int end)
1286{
1287 Q_D(QQuickSearchField);
1288 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1289 if (!input)
1290 return;
1291 input->select(start, end);
1292}
1293
1294/*!
1295 \qmlmethod void QtQuick.Controls::SearchField::selectWord()
1296 \since 6.12
1297
1298 Causes the word closest to the current cursor position to be selected.
1299
1300 \sa cursorPosition, selectedText
1301*/
1302void QQuickSearchField::selectWord()
1303{
1304 Q_D(QQuickSearchField);
1305 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1306 if (!input)
1307 return;
1308 input->selectWord();
1309}
1310
1311/*!
1312 \qmlmethod void QtQuick.Controls::SearchField::deselect()
1313 \since 6.12
1314
1315 Removes the active text selection.
1316
1317 \sa selectedText, selectionStart, selectionEnd
1318*/
1319void QQuickSearchField::deselect()
1320{
1321 Q_D(QQuickSearchField);
1322 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1323 if (!input)
1324 return;
1325 input->deselect();
1326}
1327
1328bool QQuickSearchField::eventFilter(QObject *object, QEvent *event)
1329{
1330 Q_D(QQuickSearchField);
1331
1332 switch (event->type()) {
1333 case QEvent::MouseButtonRelease: {
1334 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1335 if (input->hasFocus() && !d->text.isEmpty() && !d->isPopupVisible()
1336 && (d->delegateModel && d->delegateModel->count() > 0)
1337 && static_cast<QMouseEvent*>(event)->button() == Qt::LeftButton) {
1338 d->showPopup();
1339 }
1340 break;
1341 }
1342 case QEvent::FocusOut: {
1343 const bool hasActiveFocus = d->popup && d->popup->hasActiveFocus();
1344 const bool usingPopupWindows =
1345 d->popup ? QQuickPopupPrivate::get(d->popup)->usePopupWindow() : false;
1346 if (qGuiApp->focusObject() != this && !(hasActiveFocus && !usingPopupWindows))
1347 d->hidePopup(false);
1348 break;
1349 }
1350 default:
1351 break;
1352 }
1353
1354 return QQuickControl::eventFilter(object, event);
1355}
1356
1357void QQuickSearchField::focusInEvent(QFocusEvent *event)
1358{
1359 Q_D(QQuickSearchField);
1360 QQuickControl::focusInEvent(event);
1361
1362 if (!d->contentItem)
1363 return;
1364
1365 const auto reason = event->reason();
1366 switch (reason) {
1367 case Qt::TabFocusReason:
1368 case Qt::BacktabFocusReason:
1369 case Qt::ShortcutFocusReason:
1370 case Qt::OtherFocusReason:
1371 if (reason != Qt::OtherFocusReason || !d->isPopupVisible())
1372 d->contentItem->forceActiveFocus(reason);
1373 break;
1374 default:
1375 break;
1376 }
1377}
1378
1379void QQuickSearchField::focusOutEvent(QFocusEvent *event)
1380{
1381 Q_D(QQuickSearchField);
1382 QQuickControl::focusOutEvent(event);
1383
1384 const bool hasActiveFocus = d->popup && d->popup->hasActiveFocus();
1385 const bool usingPopupWindows = d->popup && QQuickPopupPrivate::get(d->popup)->usePopupWindow();
1386
1387 if (qGuiApp->focusObject() != d->contentItem && !(hasActiveFocus && !usingPopupWindows))
1388 d->hidePopup(false);
1389}
1390
1391void QQuickSearchField::hoverEnterEvent(QHoverEvent *event)
1392{
1393 Q_D(QQuickSearchField);
1394 QQuickControl::hoverEnterEvent(event);
1395 QQuickItem *si = d->searchIndicator->indicator();
1396 QQuickItem *ci = d->clearIndicator->indicator();
1397 d->searchIndicator->setHovered(si && si->isEnabled() && si->contains(mapToItem(si, event->position())));
1398 d->clearIndicator->setHovered(ci && ci->isEnabled() && ci->contains(mapToItem(ci, event->position())));
1399 event->ignore();
1400}
1401
1402void QQuickSearchField::hoverMoveEvent(QHoverEvent *event)
1403{
1404 Q_D(QQuickSearchField);
1405 QQuickControl::hoverMoveEvent(event);
1406 QQuickItem *si = d->searchIndicator->indicator();
1407 QQuickItem *ci = d->clearIndicator->indicator();
1408 d->searchIndicator->setHovered(si && si->isEnabled() && si->contains(mapToItem(si, event->position())));
1409 d->clearIndicator->setHovered(ci && ci->isEnabled() && ci->contains(mapToItem(ci, event->position())));
1410 event->ignore();
1411}
1412
1413void QQuickSearchField::hoverLeaveEvent(QHoverEvent *event)
1414{
1415 Q_D(QQuickSearchField);
1416 QQuickControl::hoverLeaveEvent(event);
1417 d->searchIndicator->setHovered(false);
1418 d->clearIndicator->setHovered(false);
1419 event->ignore();
1420}
1421
1422void QQuickSearchField::keyPressEvent(QKeyEvent *event)
1423{
1424 Q_D(QQuickSearchField);
1425
1426 const auto key = event->key();
1427 const bool hasModel = !d->suggestionModel.isNull();
1428 const bool hasText = !d->text.isEmpty();
1429
1430 switch (key) {
1431 case Qt::Key_Escape:
1432 case Qt::Key_Back:
1433 if (d->isPopupVisible()) {
1434 d->hidePopup(false);
1435 event->accept();
1436 } else if (hasText) {
1437 setText(QString());
1438 event->accept();
1439 }
1440 break;
1441 case Qt::Key_Return:
1442 case Qt::Key_Enter:
1443 if (d->isPopupVisible())
1444 d->hidePopup(true);
1445 emit accepted();
1446 emit searchTriggered();
1447 event->accept();
1448 break;
1449 case Qt::Key_Up:
1450 if (hasModel) {
1451 d->decreaseCurrentIndex();
1452 event->accept();
1453 }
1454 break;
1455 case Qt::Key_Down:
1456 if (hasModel) {
1457 d->increaseCurrentIndex();
1458 event->accept();
1459 }
1460 break;
1461 case Qt::Key_Home:
1462 if (d->isPopupVisible()) {
1463 d->setHighlightedIndex(0, Highlight);
1464 event->accept();
1465 }
1466 break;
1467 case Qt::Key_End:
1468 if (d->isPopupVisible()) {
1469 d->setHighlightedIndex(suggestionCount() - 1, Highlight);
1470 event->accept();
1471 }
1472 break;
1473 default:
1474 QQuickControl::keyPressEvent(event);
1475 break;
1476 }
1477}
1478
1479void QQuickSearchField::classBegin()
1480{
1481 Q_D(QQuickSearchField);
1482 QQuickControl::classBegin();
1483
1484 QQmlContext *context = qmlContext(this);
1485 if (context) {
1486 QQmlEngine::setContextForObject(d->searchIndicator, context);
1487 QQmlEngine::setContextForObject(d->clearIndicator, context);
1488 }
1489}
1490
1491void QQuickSearchField::componentComplete()
1492{
1493 Q_D(QQuickSearchField);
1494 QQuickIndicatorButtonPrivate::get(d->searchIndicator)->executeIndicator(true);
1495 QQuickIndicatorButtonPrivate::get(d->clearIndicator)->executeIndicator(true);
1496 QQuickControl::componentComplete();
1497
1498 if (d->popup)
1499 d->executePopup(true);
1500
1501 if (d->delegateModel && d->ownModel)
1502 static_cast<QQmlDelegateModel *>(d->delegateModel)->componentComplete();
1503}
1504
1505void QQuickSearchField::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
1506{
1507 Q_D(QQuickSearchField);
1508 if (oldItem) {
1509 oldItem->removeEventFilter(this);
1510 if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem)) {
1511 QObjectPrivate::disconnect(oldInput, &QQuickTextInput::textChanged, d,
1512 &QQuickSearchFieldPrivate::updateText);
1513 QObjectPrivate::disconnect(oldInput, &QQuickTextInput::cursorPositionChanged,
1514 d, &QQuickSearchFieldPrivate::updateCursorPosition);
1515 }
1516 }
1517
1518 if (newItem) {
1519 newItem->installEventFilter(this);
1520 if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem)) {
1521 QObjectPrivate::connect(newInput, &QQuickTextInput::textChanged, d,
1522 &QQuickSearchFieldPrivate::updateText);
1523 QObjectPrivate::connect(newInput, &QQuickTextInput::cursorPositionChanged,
1524 d, &QQuickSearchFieldPrivate::updateCursorPosition);
1525 }
1526 #if QT_CONFIG(cursor)
1527 newItem->setCursor(Qt::IBeamCursor);
1528 #endif
1529 }
1530}
1531
1532void QQuickSearchField::itemChange(ItemChange change, const ItemChangeData &data)
1533{
1534 Q_D(QQuickSearchField);
1535 QQuickControl::itemChange(change, data);
1536 if (change == ItemVisibleHasChanged && !data.boolValue) {
1537 d->hidePopup(false);
1538 d->setCurrentItemAtIndex(-1, NoActivate);
1539 }
1540}
1541
1542QT_END_NAMESPACE
1543
1544#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.