Introduce SearchField for Quick Controls
Add a new QQuickSearchField as part of the Qt Quick Controls to simplify implementing search functionality for lists of items. Task-number: QTBUG-126188 Change-Id: I634131161447616a2d66e7f301bd8a24adac2d7f Reviewed-by: Jan Arve Sæther <jan-arve.saether@qt.io>
This commit is contained in:
parent
5f9eafcf39
commit
8576166145
|
@ -10,6 +10,7 @@ qt_standard_project_setup(REQUIRES 6.8)
|
|||
|
||||
qt_add_executable(galleryexample WIN32 MACOSX_BUNDLE
|
||||
gallery.cpp
|
||||
qmlsortfilterproxymodel.h
|
||||
)
|
||||
|
||||
qt_add_qml_module(galleryexample
|
||||
|
@ -36,6 +37,7 @@ qt_add_qml_module(galleryexample
|
|||
"pages/ScrollBarPage.qml"
|
||||
"pages/ScrollIndicatorPage.qml"
|
||||
"pages/ScrollablePage.qml"
|
||||
"pages/SearchFieldPage.qml"
|
||||
"pages/SliderPage.qml"
|
||||
"pages/SpinBoxPage.qml"
|
||||
"pages/StackViewPage.qml"
|
||||
|
|
|
@ -24,6 +24,7 @@ RESOURCES += \
|
|||
pages/ScrollablePage.qml \
|
||||
pages/ScrollBarPage.qml \
|
||||
pages/ScrollIndicatorPage.qml \
|
||||
pages/SearchFieldPage.qml \
|
||||
pages/SliderPage.qml \
|
||||
pages/SpinBoxPage.qml \
|
||||
pages/StackViewPage.qml \
|
||||
|
|
|
@ -149,6 +149,7 @@ ApplicationWindow {
|
|||
ListElement { title: qsTr("RangeSlider"); source: "qrc:/pages/RangeSliderPage.qml" }
|
||||
ListElement { title: qsTr("ScrollBar"); source: "qrc:/pages/ScrollBarPage.qml" }
|
||||
ListElement { title: qsTr("ScrollIndicator"); source: "qrc:/pages/ScrollIndicatorPage.qml" }
|
||||
ListElement { title: qsTr("SearchField"); source: "qrc:/pages/SearchFieldPage.qml" }
|
||||
ListElement { title: qsTr("Slider"); source: "qrc:/pages/SliderPage.qml" }
|
||||
ListElement { title: qsTr("SpinBox"); source: "qrc:/pages/SpinBoxPage.qml" }
|
||||
ListElement { title: qsTr("StackView"); source: "qrc:/pages/StackViewPage.qml" }
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
ScrollablePage {
|
||||
id: page
|
||||
|
||||
Column {
|
||||
spacing: 40
|
||||
width: parent.width
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
wrapMode: Label.Wrap
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
text: qsTr("SearchField is a styled text input for searching, typically "
|
||||
+ "with a magnifier and clear icon.")
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: colorModel
|
||||
ListElement { color: "blue" }
|
||||
ListElement { color: "green" }
|
||||
ListElement { color: "red" }
|
||||
ListElement { color: "yellow" }
|
||||
ListElement { color: "orange" }
|
||||
ListElement { color: "purple" }
|
||||
}
|
||||
|
||||
SortFilterProxyModel {
|
||||
id: colorFilter
|
||||
sourceModel: colorModel
|
||||
filterRegularExpression: RegExp(colorSearch.text, "i")
|
||||
}
|
||||
|
||||
SearchField {
|
||||
id: colorSearch
|
||||
suggestionModel: colorFilter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef QMLSORTFILTERPROXYMODEL_H
|
||||
#define QMLSORTFILTERPROXYMODEL_H
|
||||
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
class QmlSortFilterProxyModel {
|
||||
Q_GADGET
|
||||
QML_FOREIGN(QSortFilterProxyModel)
|
||||
QML_NAMED_ELEMENT(SortFilterProxyModel)
|
||||
};
|
||||
|
||||
#endif // QMLSORTFILTERPROXYMODEL_H
|
|
@ -46,6 +46,7 @@ set(qml_files
|
|||
"ScrollBar.qml"
|
||||
"ScrollIndicator.qml"
|
||||
"ScrollView.qml"
|
||||
"SearchField.qml"
|
||||
"SelectionRectangle.qml"
|
||||
"Slider.qml"
|
||||
"SpinBox.qml"
|
||||
|
@ -210,6 +211,7 @@ set(qtquickcontrols2basicstyle_resource_files
|
|||
"images/check@2x.png"
|
||||
"images/check@3x.png"
|
||||
"images/check@4x.png"
|
||||
"images/close_circle.png"
|
||||
"images/dial-indicator.png"
|
||||
"images/dial-indicator@2x.png"
|
||||
"images/dial-indicator@3x.png"
|
||||
|
@ -222,6 +224,7 @@ set(qtquickcontrols2basicstyle_resource_files
|
|||
"images/drop-indicator@2x.png"
|
||||
"images/drop-indicator@3x.png"
|
||||
"images/drop-indicator@4x.png"
|
||||
"images/search-magnifier.png"
|
||||
)
|
||||
|
||||
qt_internal_add_resource(QuickControls2Basic "qtquickcontrols2basicstyle"
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls.impl
|
||||
import QtQuick.Templates as T
|
||||
|
||||
T.SearchField {
|
||||
id: control
|
||||
|
||||
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
|
||||
implicitContentWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
implicitContentHeight + topPadding + bottomPadding,
|
||||
searchIndicator.implicitIndicatorHeight + topPadding + bottomPadding,
|
||||
clearIndicator.implicitIndicatorHeight + topPadding + bottomPadding)
|
||||
|
||||
leftPadding: padding + (control.mirrored || !searchIndicator.indicator || !searchIndicator.indicator.visible ? 0 : searchIndicator.indicator.width + spacing)
|
||||
rightPadding: padding + (control.mirrored || !clearIndicator.indicator || !clearIndicator.indicator.visible ? 0 : clearIndicator.indicator.width + spacing)
|
||||
|
||||
delegate: ItemDelegate {
|
||||
width: ListView.view.width
|
||||
text: model[control.textRole]
|
||||
palette.text: control.palette.text
|
||||
palette.highlightedText: control.palette.highlightedText
|
||||
font.weight: control.currentIndex === index ? Font.DemiBold : Font.Normal
|
||||
highlighted: control.currentIndex === index
|
||||
hoverEnabled: control.hoverEnabled
|
||||
|
||||
required property var model
|
||||
required property int index
|
||||
}
|
||||
|
||||
searchIndicator.indicator: Rectangle {
|
||||
implicitWidth: 28
|
||||
implicitHeight: 28
|
||||
|
||||
x: !control.mirrored ? 3 : control.width - width - 3
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
color: control.palette.button
|
||||
|
||||
ColorImage {
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
color: control.palette.dark
|
||||
defaultColor: "#353637"
|
||||
source: "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/search-magnifier.png"
|
||||
opacity: enabled ? 1 : 0.3
|
||||
}
|
||||
}
|
||||
|
||||
clearIndicator.indicator: Rectangle {
|
||||
implicitWidth: 28
|
||||
implicitHeight: 28
|
||||
|
||||
x: control.mirrored ? 3 : control.width - width - 3
|
||||
y: control.topPadding + (control.availableHeight - height) / 2
|
||||
visible: control.text.length > 0
|
||||
color: control.palette.button
|
||||
|
||||
ColorImage {
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
color: control.palette.dark
|
||||
defaultColor: "#353637"
|
||||
source: "qrc:/qt-project.org/imports/QtQuick/Controls/Basic/images/close_circle.png"
|
||||
opacity: enabled ? 1 : 0.3
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: T.TextField {
|
||||
leftPadding: control.searchIndicator.indicator && !control.mirrored ? 6 : 0
|
||||
rightPadding: control.clearIndicator.indicator && !control.mirrored ? 6 : 0
|
||||
topPadding: 6 - control.padding
|
||||
bottomPadding: 6 - control.padding
|
||||
|
||||
text: control.text
|
||||
|
||||
color: control.palette.text
|
||||
selectionColor: control.palette.highlight
|
||||
selectedTextColor: control.palette.highlightedText
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 40
|
||||
|
||||
color: control.palette.button
|
||||
border.width: (control.activeFocus || control.contentItem.activeFocus) ? 2 : 1
|
||||
border.color: (control.activeFocus || control.contentItem.activeFocus) ? control.palette.highlight : control.palette.mid
|
||||
}
|
||||
|
||||
popup: T.Popup {
|
||||
y: control.height
|
||||
width: control.width
|
||||
height: Math.min(contentItem.implicitHeight, control.Window.height - control.y - control.height - control.padding)
|
||||
topMargin: 6
|
||||
bottomMargin: 6
|
||||
palette: control.palette
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: control.delegateModel
|
||||
currentIndex: control.currentIndex
|
||||
highlightMoveDuration: 0
|
||||
|
||||
Rectangle {
|
||||
z: 10
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: "transparent"
|
||||
border.color: control.palette.mid
|
||||
}
|
||||
|
||||
T.ScrollIndicator.vertical: ScrollIndicator { }
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: control.palette.window
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 607 B |
Binary file not shown.
After Width: | Height: | Size: 489 B |
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
|
@ -83,6 +83,7 @@ qt_internal_add_qml_module(QuickTemplates2
|
|||
qquickscrollbar_p_p.h
|
||||
qquickscrollindicator.cpp qquickscrollindicator_p.h
|
||||
qquickscrollview.cpp qquickscrollview_p.h
|
||||
qquicksearchfield.cpp qquicksearchfield_p.h
|
||||
qquickshortcutcontext.cpp
|
||||
qquickshortcutcontext_p_p.h
|
||||
qquickslider.cpp qquickslider_p.h
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,115 @@
|
|||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef QQUICKSEARCHFIELD_P_H
|
||||
#define QQUICKSEARCHFIELD_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtQuickTemplates2/private/qquickcontrol_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QQuickSearchFieldPrivate;
|
||||
class QQuickTextField;
|
||||
class QQuickPopup;
|
||||
class QQmlInstanceModel;
|
||||
class QQuickIndicatorButton;
|
||||
|
||||
class Q_QUICKTEMPLATES2_EXPORT QQuickSearchField : public QQuickControl
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QVariant suggestionModel READ suggestionModel WRITE setSuggestionModel
|
||||
NOTIFY suggestionModelChanged FINAL)
|
||||
Q_PROPERTY(QQmlInstanceModel *delegateModel READ delegateModel NOTIFY delegateModelChanged FINAL)
|
||||
Q_PROPERTY(int suggestionCount READ suggestionCount NOTIFY suggestionCountChanged FINAL)
|
||||
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged
|
||||
FINAL)
|
||||
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged FINAL)
|
||||
Q_PROPERTY(QString textRole READ textRole WRITE setTextRole NOTIFY textRoleChanged FINAL)
|
||||
Q_PROPERTY(bool live READ isLive WRITE setLive NOTIFY liveChanged)
|
||||
Q_PROPERTY(QQuickIndicatorButton *searchIndicator READ searchIndicator CONSTANT FINAL)
|
||||
Q_PROPERTY(QQuickIndicatorButton *clearIndicator READ clearIndicator CONSTANT FINAL)
|
||||
Q_PROPERTY(QQuickPopup *popup READ popup WRITE setPopup NOTIFY popupChanged FINAL)
|
||||
Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged FINAL)
|
||||
|
||||
QML_NAMED_ELEMENT(SearchField)
|
||||
QML_ADDED_IN_VERSION(6, 10)
|
||||
|
||||
public:
|
||||
explicit QQuickSearchField(QQuickItem *parent = nullptr);
|
||||
~QQuickSearchField();
|
||||
|
||||
QVariant suggestionModel() const;
|
||||
void setSuggestionModel(const QVariant &model);
|
||||
|
||||
QQmlInstanceModel *delegateModel() const;
|
||||
|
||||
int suggestionCount() const;
|
||||
|
||||
int currentIndex() const;
|
||||
void setCurrentIndex(int index);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &text);
|
||||
|
||||
QString textRole() const;
|
||||
void setTextRole(const QString &textRole);
|
||||
|
||||
bool isLive() const;
|
||||
void setLive(const bool live);
|
||||
|
||||
QQuickIndicatorButton *searchIndicator() const;
|
||||
QQuickIndicatorButton *clearIndicator() const;
|
||||
|
||||
QQuickPopup *popup() const;
|
||||
void setPopup(QQuickPopup *popup);
|
||||
|
||||
QQmlComponent *delegate() const;
|
||||
void setDelegate(QQmlComponent *delegate);
|
||||
|
||||
Q_SIGNALS:
|
||||
void activated(int index);
|
||||
void accepted();
|
||||
void searchTriggered();
|
||||
void textEdited();
|
||||
void suggestionModelChanged();
|
||||
void delegateModelChanged();
|
||||
void suggestionCountChanged();
|
||||
void currentIndexChanged();
|
||||
void textChanged();
|
||||
void textRoleChanged();
|
||||
void liveChanged();
|
||||
void popupChanged();
|
||||
void delegateChanged();
|
||||
|
||||
void searchButtonPressed();
|
||||
void clearButtonPressed();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *object, QEvent *event) override;
|
||||
void focusInEvent(QFocusEvent *event) override;
|
||||
void focusOutEvent(QFocusEvent *event) override;
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
void classBegin() override;
|
||||
void componentComplete() override;
|
||||
void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) override;
|
||||
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data) override;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QQuickSearchField)
|
||||
Q_DECLARE_PRIVATE(QQuickSearchField)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QQUICKSEARCHFIELD_P_H
|
|
@ -0,0 +1,230 @@
|
|||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtTest
|
||||
import QtQuick.Controls
|
||||
import Qt.test.controls
|
||||
|
||||
TestCase {
|
||||
id: testCase
|
||||
width: 400
|
||||
height: 400
|
||||
visible: true
|
||||
when: windowShown
|
||||
name: "SearchField"
|
||||
|
||||
Component {
|
||||
id: signalSpy
|
||||
SignalSpy { }
|
||||
}
|
||||
|
||||
Component {
|
||||
id: searchField
|
||||
SearchField { }
|
||||
}
|
||||
|
||||
Component {
|
||||
id: searchText
|
||||
SearchField {
|
||||
TextField{ }
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
failOnWarning(/.?/)
|
||||
}
|
||||
|
||||
function test_defaults() {
|
||||
let control = createTemporaryObject(searchField, testCase)
|
||||
verify(control)
|
||||
|
||||
compare(control.suggestionModel, undefined)
|
||||
compare(control.suggestionCount, 0)
|
||||
compare(control.currentIndex, -1)
|
||||
compare(control.text, "")
|
||||
compare(control.textRole, "")
|
||||
compare(control.live, true)
|
||||
verify(control.delegate)
|
||||
verify(control.popup)
|
||||
}
|
||||
|
||||
// TO-DO: Implement SFPM logic after 6.10
|
||||
// ListModel {
|
||||
// id: specialCharModels
|
||||
// ListElement { text: "こんにちは" }
|
||||
// ListElement { text: "Pi: π (3.14)"; }
|
||||
// ListElement { text: "Math: ∑ ∞ ≈"; }
|
||||
// ListElement { text: "Emoji: 😃🎉🔥"; }
|
||||
// ListElement { text: "Currency: € ¥ ₹ $"; }
|
||||
// ListElement { text: "α β γ"; }
|
||||
// ListElement { text: "Привет"; }
|
||||
// ListElement { text: "مرحبًا"; }
|
||||
// ListElement { text: "你好"; }
|
||||
// ListElement { text: "שלום"; }
|
||||
// ListElement { text: "Brackets: { [ ( < > ) ] }"; }
|
||||
// }
|
||||
|
||||
// function test_specialCharacters() {
|
||||
// let control = createTemporaryObject(searchField, testCase)
|
||||
// verify(control)
|
||||
|
||||
// control.suggestionModel = specialCharModels
|
||||
// let textItem = control.contentItem
|
||||
// textItem.text = "e"
|
||||
|
||||
// compare(control.text, "e")
|
||||
// compare(control.suggestionCount, 3)
|
||||
// compare(control.currentIndex, 0)
|
||||
// compare(control.popup.visible, true)
|
||||
|
||||
// textItem.text = "П"
|
||||
|
||||
// compare(control.text, "П")
|
||||
// compare(control.suggestionCount, 1)
|
||||
// compare(control.currentIndex, 0)
|
||||
// compare(control.popup.visible, true)
|
||||
|
||||
// textItem.text = "🎉"
|
||||
|
||||
// compare(control.text, "🎉")
|
||||
// compare(control.suggestionCount, 1)
|
||||
// compare(control.currentIndex, 0)
|
||||
// compare(control.popup.visible, true)
|
||||
// }
|
||||
|
||||
ListModel {
|
||||
id : fruitModel
|
||||
ListElement { name: "Apple"; color: "green" }
|
||||
ListElement { name: "Cherry"; color: "red" }
|
||||
ListElement { name: "Banana"; color: "yellow" }
|
||||
ListElement { name: "Orange"; color: "orange" }
|
||||
ListElement { name: "WaterMelon"; color: "pink" }
|
||||
}
|
||||
|
||||
function test_textRole() {
|
||||
ignoreWarning(/Unable to assign QQmlDMAbstractItemModelData to QString/)
|
||||
let control = createTemporaryObject(searchField, testCase)
|
||||
verify(control)
|
||||
|
||||
control.suggestionModel = fruitModel
|
||||
control.textRole = "name"
|
||||
|
||||
let textItem = control.contentItem
|
||||
textItem.text = "a"
|
||||
|
||||
compare(control.text, "a")
|
||||
compare(control.suggestionCount, 5)
|
||||
compare(control.popup.visible,true)
|
||||
|
||||
control.textRole = "color"
|
||||
|
||||
textItem.text = "r"
|
||||
|
||||
compare(control.text, "r")
|
||||
compare(control.suggestionCount, 5)
|
||||
compare(control.popup.visible,true)
|
||||
}
|
||||
|
||||
Component {
|
||||
id: suggestion
|
||||
SearchField {
|
||||
onTextEdited: {
|
||||
if (text === "a") {
|
||||
suggestionModel = ["Apple", "Apricot"]
|
||||
} else if (text === "c") {
|
||||
suggestionModel = ["Cherry", "Coconut", "Cranberry"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function test_suggestionPopup() {
|
||||
let control = createTemporaryObject(suggestion, testCase)
|
||||
verify(control)
|
||||
|
||||
compare(control.popup.visible, false)
|
||||
|
||||
let textItem = control.contentItem
|
||||
|
||||
textItem.text = "a"
|
||||
compare(control.suggestionCount, 2)
|
||||
compare(control.currentIndex, 0)
|
||||
compare(control.popup.visible, true)
|
||||
|
||||
textItem.text = "c"
|
||||
compare(control.suggestionCount, 3)
|
||||
compare(control.currentIndex, 0)
|
||||
compare(control.popup.visible, true)
|
||||
}
|
||||
|
||||
function test_textEdited() {
|
||||
let control = createTemporaryObject(searchField, testCase)
|
||||
verify(control)
|
||||
|
||||
let textEditedSpy = signalSpy.createObject(control, {target: control, signalName: "textEdited"})
|
||||
verify(textEditedSpy.valid)
|
||||
|
||||
let searchTriggeredSpy = signalSpy.createObject(control, {target: control, signalName: "searchTriggered"})
|
||||
verify(searchTriggeredSpy.valid)
|
||||
|
||||
control.live = true
|
||||
let textItem = control.contentItem
|
||||
textItem.text = "a"
|
||||
|
||||
compare(control.text, "a")
|
||||
compare(textEditedSpy.count, 1)
|
||||
|
||||
compare(searchTriggeredSpy.count, 1)
|
||||
}
|
||||
|
||||
function test_arrowKeys() {
|
||||
ignoreWarning(/Unable to assign QQmlDMAbstractItemModelData to QString/)
|
||||
let control = createTemporaryObject(searchField, testCase)
|
||||
verify(control)
|
||||
|
||||
let openedSpy = signalSpy.createObject(control, {target: control.popup, signalName: "opened"})
|
||||
verify(openedSpy.valid)
|
||||
|
||||
let closedSpy = signalSpy.createObject(control, {target: control.popup, signalName: "closed"})
|
||||
verify(closedSpy.valid)
|
||||
|
||||
let acceptedSpy = signalSpy.createObject(control, {target: control, signalName: "accepted"})
|
||||
verify(closedSpy.valid)
|
||||
|
||||
let searchTriggeredSpy = signalSpy.createObject(control, {target: control, signalName: "searchTriggered"})
|
||||
verify(searchTriggeredSpy.valid)
|
||||
|
||||
control.forceActiveFocus()
|
||||
verify(control.activeFocus)
|
||||
|
||||
control.suggestionModel = fruitModel
|
||||
control.textRole = "name"
|
||||
|
||||
let textItem = control.contentItem
|
||||
textItem.text = "a"
|
||||
|
||||
compare(control.popup.visible, true)
|
||||
|
||||
keyClick(Qt.Key_Down)
|
||||
compare(control.currentIndex, 1)
|
||||
|
||||
keyClick(Qt.Key_Down)
|
||||
compare(control.currentIndex, 2)
|
||||
|
||||
keyClick(Qt.Key_Up)
|
||||
compare(control.currentIndex, 1)
|
||||
|
||||
keyClick(Qt.Key_Enter)
|
||||
compare(control.text, "Cherry")
|
||||
compare(acceptedSpy.count, 1)
|
||||
compare(searchTriggeredSpy.count, 2)
|
||||
|
||||
keyClick(Qt.Key_Back)
|
||||
compare(control.popup.visible, false)
|
||||
|
||||
keyClick(Qt.Key_Escape)
|
||||
compare(control.text, "")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Proxy 1.0
|
||||
|
||||
GridLayout{
|
||||
width: 500
|
||||
height: 300
|
||||
anchors.fill: parent
|
||||
rows: 4
|
||||
flow: GridLayout.TopToBottom
|
||||
|
||||
SearchField {
|
||||
live: false
|
||||
}
|
||||
|
||||
// TO-DO: Add a test case for autoSuggest property
|
||||
// SearchField {
|
||||
// autoSuggest: true
|
||||
// }
|
||||
|
||||
SearchField {
|
||||
suggestionModel: ListModel {
|
||||
ListElement { color: "blue" }
|
||||
ListElement { color: "green" }
|
||||
ListElement { color: "red" }
|
||||
ListElement { color: "yellow" }
|
||||
ListElement { color: "orange" }
|
||||
ListElement { color: "purple" }
|
||||
ListElement { color: "cyan" }
|
||||
ListElement { color: "magenta" }
|
||||
ListElement { color: "chartreuse" }
|
||||
ListElement { color: "aquamarine" }
|
||||
ListElement { color: "indigo" }
|
||||
ListElement { color: "black" }
|
||||
ListElement { color: "lightsteelblue" }
|
||||
ListElement { color: "violet" }
|
||||
ListElement { color: "grey" }
|
||||
ListElement { color: "springgreen" }
|
||||
ListElement { color: "salmon" }
|
||||
ListElement { color: "blanchedalmond" }
|
||||
ListElement { color: "forestgreen" }
|
||||
ListElement { color: "pink" }
|
||||
ListElement { color: "navy" }
|
||||
ListElement { color: "goldenrod" }
|
||||
ListElement { color: "crimson" }
|
||||
ListElement { color: "turquoise" }
|
||||
}
|
||||
}
|
||||
|
||||
SearchField {
|
||||
suggestionModel: ["January", "February", "March", "April", "May", "June", "July", "August",
|
||||
"September", "October", "November", "December"]
|
||||
}
|
||||
|
||||
SearchField {
|
||||
suggestionModel: ListModel {
|
||||
ListElement { name: "Apple"; color: "green" }
|
||||
ListElement { name: "Cherry"; color: "red" }
|
||||
ListElement { name: "Banana"; color: "yellow" }
|
||||
ListElement { name: "Orange"; color: "orange" }
|
||||
ListElement { name: "WaterMelon"; color: "pink" }
|
||||
}
|
||||
textRole: "color"
|
||||
}
|
||||
|
||||
SearchField {
|
||||
id: searchField
|
||||
suggestionModel: QSortFilterProxyModel {
|
||||
id: colorModel
|
||||
sourceModel: ListModel {
|
||||
ListElement { color: "blue" }
|
||||
ListElement { color: "green" }
|
||||
ListElement { color: "red" }
|
||||
ListElement { color: "yellow" }
|
||||
ListElement { color: "orange" }
|
||||
ListElement { color: "purple" }
|
||||
}
|
||||
}
|
||||
onTextChanged: {
|
||||
colorModel.filterRegularExpression = new RegExp(searchField.text, "i")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
#include <QtGui/QPalette>
|
||||
#include <QtGui/QFont>
|
||||
#include <QtQuickControls2/QQuickStyle>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -153,6 +155,9 @@ void tst_Baseline_Controls::initTestCase()
|
|||
qInfo("PlatformName computed to be : %s", qPrintable(platformName));
|
||||
qInfo("Color Scheme computed as : %s", qPrintable(colorSchemeIdStr));
|
||||
qInfo("Native style name is : %s", qPrintable(QQuickStyle::name()));
|
||||
|
||||
qmlRegisterAnonymousType<QAbstractItemModel>("Proxy", 1);
|
||||
qmlRegisterType<QSortFilterProxyModel>("Proxy", 1, 0, "QSortFilterProxyModel");
|
||||
}
|
||||
|
||||
void tst_Baseline_Controls::init()
|
||||
|
|
Loading…
Reference in New Issue