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 QQuickPopupPrivate::get(popup)->allowVerticalFlip = true;
1051 popup->setClosePolicy(QQuickPopup::CloseOnEscape | QQuickPopup::CloseOnPressOutsideParent);
1052 QObjectPrivate::connect(popup, &QQuickPopup::visibleChanged, d,
1053 &QQuickSearchFieldPrivate::popupVisibleChanged);
1054 // QQuickPopup does not derive from QQuickItemChangeListener, so we cannot use
1055 // QQuickItemChangeListener::itemDestroyed so we have to use QObject::destroyed
1056 QObjectPrivate::connect(popup, &QQuickPopup::destroyed, d,
1057 &QQuickSearchFieldPrivate::popupDestroyed);
1058
1059#if QT_CONFIG(quick_itemview)
1060 if (QQuickItemView *itemView = popup->findChild<QQuickItemView *>())
1061 itemView->setHighlightRangeMode(QQuickItemView::NoHighlightRange);
1062#endif
1063 }
1064
1065 d->popup = popup;
1066 if (!d->popup.isExecuting())
1067 emit popupChanged();
1068}
1069
1070/*!
1071 \qmlproperty Component QtQuick.Controls::SearchField::delegate
1072
1073 This property holds a delegate that presents an item in the search field popup.
1074
1075 It is recommended to use \l ItemDelegate (or any other \l AbstractButton
1076 derivatives) as the delegate. This ensures that the interaction works as
1077 expected, and the popup will automatically close when appropriate. When
1078 other types are used as the delegate, the popup must be closed manually.
1079 For example, if \l MouseArea is used:
1080
1081 \code
1082 delegate: Rectangle {
1083 // ...
1084 MouseArea {
1085 // ...
1086 onClicked: searchField.popup.close()
1087 }
1088 }
1089 \endcode
1090
1091 \include delegate-ownership.qdocinc {no-ownership-since-6.11} {SearchField}
1092*/
1093QQmlComponent *QQuickSearchField::delegate() const
1094{
1095 Q_D(const QQuickSearchField);
1096 return d->delegate;
1097}
1098
1099void QQuickSearchField::setDelegate(QQmlComponent *delegate)
1100{
1101 Q_D(QQuickSearchField);
1102 if (d->delegate == delegate)
1103 return;
1104
1105 d->delegate = delegate;
1106 QQmlDelegateModel *delegateModel = qobject_cast<QQmlDelegateModel *>(d->delegateModel);
1107 if (delegateModel)
1108 delegateModel->setDelegate(d->delegate);
1109 emit delegateChanged();
1110}
1111
1112/*!
1113 \qmlproperty string QtQuick.Controls::SearchField::placeholderText
1114 \since 6.12
1115
1116 This property holds the hint that is displayed in the SearchField before the user
1117 enters text.
1118*/
1119QString QQuickSearchField::placeholderText() const
1120{
1121 Q_D(const QQuickSearchField);
1122 return d->placeholderText;
1123}
1124
1125void QQuickSearchField::setPlaceholderText(const QString &text)
1126{
1127 Q_D(QQuickSearchField);
1128 if (d->placeholderText == text)
1129 return;
1130
1131 d->placeholderText = text;
1132 emit placeholderTextChanged();
1133}
1134
1135/*!
1136 \qmlproperty bool QtQuick.Controls::SearchField::selectTextByMouse
1137 \since 6.12
1138
1139 This property holds whether the text can be selected with the mouse.
1140
1141 The default value is \c true.
1142*/
1143bool QQuickSearchField::selectTextByMouse() const
1144{
1145 Q_D(const QQuickSearchField);
1146 return d->selectTextByMouse;
1147}
1148
1149void QQuickSearchField::setSelectTextByMouse(const bool selectable)
1150{
1151 Q_D(QQuickSearchField);
1152 if (d->selectTextByMouse == selectable)
1153 return;
1154
1155 d->selectTextByMouse = selectable;
1156 emit selectTextByMouseChanged();
1157}
1158
1159/*!
1160 \readonly
1161 \qmlproperty string QtQuick.Controls::SearchField::selectedText
1162 \since 6.12
1163
1164 This read-only property holds the currently selected text.
1165*/
1166QString QQuickSearchField::selectedText() const
1167{
1168 Q_D(const QQuickSearchField);
1169 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1170 if (!input)
1171 return QString();
1172 return input->selectedText();
1173}
1174
1175/*!
1176 \readonly
1177 \qmlproperty int QtQuick.Controls::SearchField::selectionStart
1178 \since 6.12
1179
1180 The cursor position before the first character in the current selection.
1181
1182 This property is read-only. To change the selection, use select(start, end),
1183 selectAll(), or selectWord().
1184
1185 \sa selectionEnd, cursorPosition, selectedText
1186*/
1187int QQuickSearchField::selectionStart() const
1188{
1189 Q_D(const QQuickSearchField);
1190 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1191 if (!input)
1192 return 0;
1193 return input->selectionStart();
1194}
1195
1196/*!
1197 \readonly
1198 \qmlproperty int QtQuick.Controls::SearchField::selectionEnd
1199 \since 6.12
1200
1201 The cursor position after the last character in the current selection.
1202
1203 This property is read-only. To change the selection, use select(start, end),
1204 selectAll(), or selectWord().
1205
1206 \sa selectionStart, cursorPosition, selectedText
1207*/
1208int QQuickSearchField::selectionEnd() const
1209{
1210 Q_D(const QQuickSearchField);
1211 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1212 if (!input)
1213 return 0;
1214 return input->selectionEnd();
1215}
1216
1217/*!
1218 \qmlproperty int QtQuick.Controls::SearchField::cursorPosition
1219 \since 6.12
1220
1221 The position of the cursor in the text field. The cursor is positioned between
1222 characters.
1223
1224 \note The \e characters in this case refer to the string of \l QChar objects,
1225 therefore 16-bit Unicode characters, and the position is considered an index
1226 into this string. This does not necessarily correspond to individual graphemes
1227 in the writing system, as a single grapheme may be represented by multiple
1228 Unicode characters, such as in the case of surrogate pairs, linguistic
1229 ligatures or diacritics.
1230*/
1231int QQuickSearchField::cursorPosition() const
1232{
1233 Q_D(const QQuickSearchField);
1234 return d->cursorPosition;
1235}
1236
1237void QQuickSearchField::setCursorPosition(int position)
1238{
1239 Q_D(QQuickSearchField);
1240 if (d->cursorPosition == position)
1241 return;
1242
1243 d->cursorPosition = position;
1244
1245 if (QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem)) {
1246 if (input->cursorPosition() != position)
1247 input->setCursorPosition(position);
1248 }
1249
1250 emit cursorPositionChanged();
1251}
1252
1253/*!
1254 \qmlmethod void QtQuick.Controls::SearchField::selectAll()
1255 \since 6.12
1256
1257 Selects all text in the text field of the control.
1258*/
1259void QQuickSearchField::selectAll()
1260{
1261 Q_D(QQuickSearchField);
1262 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1263 if (!input)
1264 return;
1265 input->selectAll();
1266}
1267
1268/*!
1269 \qmlmethod void QtQuick.Controls::SearchField::select(int start, int end)
1270 \since 6.12
1271
1272 Causes the text from \a start to \a end to be selected.
1273
1274 If either \a start or \a end is out of range, the selection is not changed.
1275
1276 After calling this, selectionStart will become the lesser
1277 and selectionEnd will become the greater (regardless of the order passed
1278 to this method).
1279
1280 \sa selectionStart, selectionEnd
1281*/
1282void QQuickSearchField::select(int start, int end)
1283{
1284 Q_D(QQuickSearchField);
1285 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1286 if (!input)
1287 return;
1288 input->select(start, end);
1289}
1290
1291/*!
1292 \qmlmethod void QtQuick.Controls::SearchField::selectWord()
1293 \since 6.12
1294
1295 Causes the word closest to the current cursor position to be selected.
1296
1297 \sa cursorPosition, selectedText
1298*/
1299void QQuickSearchField::selectWord()
1300{
1301 Q_D(QQuickSearchField);
1302 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1303 if (!input)
1304 return;
1305 input->selectWord();
1306}
1307
1308/*!
1309 \qmlmethod void QtQuick.Controls::SearchField::deselect()
1310 \since 6.12
1311
1312 Removes the active text selection.
1313
1314 \sa selectedText, selectionStart, selectionEnd
1315*/
1316void QQuickSearchField::deselect()
1317{
1318 Q_D(QQuickSearchField);
1319 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1320 if (!input)
1321 return;
1322 input->deselect();
1323}
1324
1325bool QQuickSearchField::eventFilter(QObject *object, QEvent *event)
1326{
1327 Q_D(QQuickSearchField);
1328
1329 switch (event->type()) {
1330 case QEvent::MouseButtonRelease: {
1331 QQuickTextInput *input = qobject_cast<QQuickTextInput *>(d->contentItem);
1332 if (input->hasFocus() && !d->text.isEmpty() && !d->isPopupVisible()
1333 && (d->delegateModel && d->delegateModel->count() > 0)
1334 && static_cast<QMouseEvent*>(event)->button() == Qt::LeftButton) {
1335 d->showPopup();
1336 }
1337 break;
1338 }
1339 case QEvent::FocusOut: {
1340 const bool hasActiveFocus = d->popup && d->popup->hasActiveFocus();
1341 const bool usingPopupWindows =
1342 d->popup ? QQuickPopupPrivate::get(d->popup)->usePopupWindow() : false;
1343 if (qGuiApp->focusObject() != this && !(hasActiveFocus && !usingPopupWindows))
1344 d->hidePopup(false);
1345 break;
1346 }
1347 default:
1348 break;
1349 }
1350
1351 return QQuickControl::eventFilter(object, event);
1352}
1353
1354void QQuickSearchField::focusInEvent(QFocusEvent *event)
1355{
1356 Q_D(QQuickSearchField);
1357 QQuickControl::focusInEvent(event);
1358
1359 if (!d->contentItem)
1360 return;
1361
1362 const auto reason = event->reason();
1363 switch (reason) {
1364 case Qt::TabFocusReason:
1365 case Qt::BacktabFocusReason:
1366 case Qt::ShortcutFocusReason:
1367 case Qt::OtherFocusReason:
1368 if (reason != Qt::OtherFocusReason || !d->isPopupVisible())
1369 d->contentItem->forceActiveFocus(reason);
1370 break;
1371 default:
1372 break;
1373 }
1374}
1375
1376void QQuickSearchField::focusOutEvent(QFocusEvent *event)
1377{
1378 Q_D(QQuickSearchField);
1379 QQuickControl::focusOutEvent(event);
1380
1381 const bool hasActiveFocus = d->popup && d->popup->hasActiveFocus();
1382 const bool usingPopupWindows = d->popup && QQuickPopupPrivate::get(d->popup)->usePopupWindow();
1383
1384 if (qGuiApp->focusObject() != d->contentItem && !(hasActiveFocus && !usingPopupWindows))
1385 d->hidePopup(false);
1386}
1387
1388void QQuickSearchField::hoverEnterEvent(QHoverEvent *event)
1389{
1390 Q_D(QQuickSearchField);
1391 QQuickControl::hoverEnterEvent(event);
1392 QQuickItem *si = d->searchIndicator->indicator();
1393 QQuickItem *ci = d->clearIndicator->indicator();
1394 d->searchIndicator->setHovered(si && si->isEnabled() && si->contains(mapToItem(si, event->position())));
1395 d->clearIndicator->setHovered(ci && ci->isEnabled() && ci->contains(mapToItem(ci, event->position())));
1396 event->ignore();
1397}
1398
1399void QQuickSearchField::hoverMoveEvent(QHoverEvent *event)
1400{
1401 Q_D(QQuickSearchField);
1402 QQuickControl::hoverMoveEvent(event);
1403 QQuickItem *si = d->searchIndicator->indicator();
1404 QQuickItem *ci = d->clearIndicator->indicator();
1405 d->searchIndicator->setHovered(si && si->isEnabled() && si->contains(mapToItem(si, event->position())));
1406 d->clearIndicator->setHovered(ci && ci->isEnabled() && ci->contains(mapToItem(ci, event->position())));
1407 event->ignore();
1408}
1409
1410void QQuickSearchField::hoverLeaveEvent(QHoverEvent *event)
1411{
1412 Q_D(QQuickSearchField);
1413 QQuickControl::hoverLeaveEvent(event);
1414 d->searchIndicator->setHovered(false);
1415 d->clearIndicator->setHovered(false);
1416 event->ignore();
1417}
1418
1419void QQuickSearchField::keyPressEvent(QKeyEvent *event)
1420{
1421 Q_D(QQuickSearchField);
1422
1423 const auto key = event->key();
1424 const bool hasModel = !d->suggestionModel.isNull();
1425 const bool hasText = !d->text.isEmpty();
1426
1427 switch (key) {
1428 case Qt::Key_Escape:
1429 case Qt::Key_Back:
1430 if (d->isPopupVisible()) {
1431 d->hidePopup(false);
1432 event->accept();
1433 } else if (hasText) {
1434 setText(QString());
1435 event->accept();
1436 }
1437 break;
1438 case Qt::Key_Return:
1439 case Qt::Key_Enter:
1440 if (d->isPopupVisible())
1441 d->hidePopup(true);
1442 emit accepted();
1443 emit searchTriggered();
1444 event->accept();
1445 break;
1446 case Qt::Key_Up:
1447 if (hasModel && hasText) {
1448 d->decreaseCurrentIndex();
1449 event->accept();
1450 }
1451 break;
1452 case Qt::Key_Down:
1453 if (hasModel && hasText) {
1454 d->increaseCurrentIndex();
1455 event->accept();
1456 }
1457 break;
1458 case Qt::Key_Home:
1459 if (d->isPopupVisible()) {
1460 d->setHighlightedIndex(0, Highlight);
1461 event->accept();
1462 }
1463 break;
1464 case Qt::Key_End:
1465 if (d->isPopupVisible()) {
1466 d->setHighlightedIndex(suggestionCount() - 1, Highlight);
1467 event->accept();
1468 }
1469 break;
1470 default:
1471 QQuickControl::keyPressEvent(event);
1472 break;
1473 }
1474}
1475
1476void QQuickSearchField::classBegin()
1477{
1478 Q_D(QQuickSearchField);
1479 QQuickControl::classBegin();
1480
1481 QQmlContext *context = qmlContext(this);
1482 if (context) {
1483 QQmlEngine::setContextForObject(d->searchIndicator, context);
1484 QQmlEngine::setContextForObject(d->clearIndicator, context);
1485 }
1486}
1487
1488void QQuickSearchField::componentComplete()
1489{
1490 Q_D(QQuickSearchField);
1491 QQuickIndicatorButtonPrivate::get(d->searchIndicator)->executeIndicator(true);
1492 QQuickIndicatorButtonPrivate::get(d->clearIndicator)->executeIndicator(true);
1493 QQuickControl::componentComplete();
1494
1495 if (d->popup)
1496 d->executePopup(true);
1497
1498 if (d->delegateModel && d->ownModel)
1499 static_cast<QQmlDelegateModel *>(d->delegateModel)->componentComplete();
1500}
1501
1502void QQuickSearchField::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
1503{
1504 Q_D(QQuickSearchField);
1505 if (oldItem) {
1506 oldItem->removeEventFilter(this);
1507 if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem)) {
1508 QObjectPrivate::disconnect(oldInput, &QQuickTextInput::textChanged, d,
1509 &QQuickSearchFieldPrivate::updateText);
1510 QObjectPrivate::disconnect(oldInput, &QQuickTextInput::cursorPositionChanged,
1511 d, &QQuickSearchFieldPrivate::updateCursorPosition);
1512 }
1513 }
1514
1515 if (newItem) {
1516 newItem->installEventFilter(this);
1517 if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem)) {
1518 QObjectPrivate::connect(newInput, &QQuickTextInput::textChanged, d,
1519 &QQuickSearchFieldPrivate::updateText);
1520 QObjectPrivate::connect(newInput, &QQuickTextInput::cursorPositionChanged,
1521 d, &QQuickSearchFieldPrivate::updateCursorPosition);
1522 }
1523 #if QT_CONFIG(cursor)
1524 newItem->setCursor(Qt::IBeamCursor);
1525 #endif
1526 }
1527}
1528
1529void QQuickSearchField::itemChange(ItemChange change, const ItemChangeData &data)
1530{
1531 Q_D(QQuickSearchField);
1532 QQuickControl::itemChange(change, data);
1533 if (change == ItemVisibleHasChanged && !data.boolValue) {
1534 d->hidePopup(false);
1535 d->setCurrentItemAtIndex(-1, NoActivate);
1536 }
1537}
1538
1539QT_END_NAMESPACE
1540
1541#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.