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
|
qt_add_executable(galleryexample WIN32 MACOSX_BUNDLE
|
||||||
gallery.cpp
|
gallery.cpp
|
||||||
|
qmlsortfilterproxymodel.h
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_qml_module(galleryexample
|
qt_add_qml_module(galleryexample
|
||||||
|
@ -36,6 +37,7 @@ qt_add_qml_module(galleryexample
|
||||||
"pages/ScrollBarPage.qml"
|
"pages/ScrollBarPage.qml"
|
||||||
"pages/ScrollIndicatorPage.qml"
|
"pages/ScrollIndicatorPage.qml"
|
||||||
"pages/ScrollablePage.qml"
|
"pages/ScrollablePage.qml"
|
||||||
|
"pages/SearchFieldPage.qml"
|
||||||
"pages/SliderPage.qml"
|
"pages/SliderPage.qml"
|
||||||
"pages/SpinBoxPage.qml"
|
"pages/SpinBoxPage.qml"
|
||||||
"pages/StackViewPage.qml"
|
"pages/StackViewPage.qml"
|
||||||
|
|
|
@ -24,6 +24,7 @@ RESOURCES += \
|
||||||
pages/ScrollablePage.qml \
|
pages/ScrollablePage.qml \
|
||||||
pages/ScrollBarPage.qml \
|
pages/ScrollBarPage.qml \
|
||||||
pages/ScrollIndicatorPage.qml \
|
pages/ScrollIndicatorPage.qml \
|
||||||
|
pages/SearchFieldPage.qml \
|
||||||
pages/SliderPage.qml \
|
pages/SliderPage.qml \
|
||||||
pages/SpinBoxPage.qml \
|
pages/SpinBoxPage.qml \
|
||||||
pages/StackViewPage.qml \
|
pages/StackViewPage.qml \
|
||||||
|
|
|
@ -149,6 +149,7 @@ ApplicationWindow {
|
||||||
ListElement { title: qsTr("RangeSlider"); source: "qrc:/pages/RangeSliderPage.qml" }
|
ListElement { title: qsTr("RangeSlider"); source: "qrc:/pages/RangeSliderPage.qml" }
|
||||||
ListElement { title: qsTr("ScrollBar"); source: "qrc:/pages/ScrollBarPage.qml" }
|
ListElement { title: qsTr("ScrollBar"); source: "qrc:/pages/ScrollBarPage.qml" }
|
||||||
ListElement { title: qsTr("ScrollIndicator"); source: "qrc:/pages/ScrollIndicatorPage.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("Slider"); source: "qrc:/pages/SliderPage.qml" }
|
||||||
ListElement { title: qsTr("SpinBox"); source: "qrc:/pages/SpinBoxPage.qml" }
|
ListElement { title: qsTr("SpinBox"); source: "qrc:/pages/SpinBoxPage.qml" }
|
||||||
ListElement { title: qsTr("StackView"); source: "qrc:/pages/StackViewPage.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"
|
"ScrollBar.qml"
|
||||||
"ScrollIndicator.qml"
|
"ScrollIndicator.qml"
|
||||||
"ScrollView.qml"
|
"ScrollView.qml"
|
||||||
|
"SearchField.qml"
|
||||||
"SelectionRectangle.qml"
|
"SelectionRectangle.qml"
|
||||||
"Slider.qml"
|
"Slider.qml"
|
||||||
"SpinBox.qml"
|
"SpinBox.qml"
|
||||||
|
@ -210,6 +211,7 @@ set(qtquickcontrols2basicstyle_resource_files
|
||||||
"images/check@2x.png"
|
"images/check@2x.png"
|
||||||
"images/check@3x.png"
|
"images/check@3x.png"
|
||||||
"images/check@4x.png"
|
"images/check@4x.png"
|
||||||
|
"images/close_circle.png"
|
||||||
"images/dial-indicator.png"
|
"images/dial-indicator.png"
|
||||||
"images/dial-indicator@2x.png"
|
"images/dial-indicator@2x.png"
|
||||||
"images/dial-indicator@3x.png"
|
"images/dial-indicator@3x.png"
|
||||||
|
@ -222,6 +224,7 @@ set(qtquickcontrols2basicstyle_resource_files
|
||||||
"images/drop-indicator@2x.png"
|
"images/drop-indicator@2x.png"
|
||||||
"images/drop-indicator@3x.png"
|
"images/drop-indicator@3x.png"
|
||||||
"images/drop-indicator@4x.png"
|
"images/drop-indicator@4x.png"
|
||||||
|
"images/search-magnifier.png"
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_internal_add_resource(QuickControls2Basic "qtquickcontrols2basicstyle"
|
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
|
qquickscrollbar_p_p.h
|
||||||
qquickscrollindicator.cpp qquickscrollindicator_p.h
|
qquickscrollindicator.cpp qquickscrollindicator_p.h
|
||||||
qquickscrollview.cpp qquickscrollview_p.h
|
qquickscrollview.cpp qquickscrollview_p.h
|
||||||
|
qquicksearchfield.cpp qquicksearchfield_p.h
|
||||||
qquickshortcutcontext.cpp
|
qquickshortcutcontext.cpp
|
||||||
qquickshortcutcontext_p_p.h
|
qquickshortcutcontext_p_p.h
|
||||||
qquickslider.cpp qquickslider_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/QPalette>
|
||||||
#include <QtGui/QFont>
|
#include <QtGui/QFont>
|
||||||
#include <QtQuickControls2/QQuickStyle>
|
#include <QtQuickControls2/QQuickStyle>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@ -153,6 +155,9 @@ void tst_Baseline_Controls::initTestCase()
|
||||||
qInfo("PlatformName computed to be : %s", qPrintable(platformName));
|
qInfo("PlatformName computed to be : %s", qPrintable(platformName));
|
||||||
qInfo("Color Scheme computed as : %s", qPrintable(colorSchemeIdStr));
|
qInfo("Color Scheme computed as : %s", qPrintable(colorSchemeIdStr));
|
||||||
qInfo("Native style name is : %s", qPrintable(QQuickStyle::name()));
|
qInfo("Native style name is : %s", qPrintable(QQuickStyle::name()));
|
||||||
|
|
||||||
|
qmlRegisterAnonymousType<QAbstractItemModel>("Proxy", 1);
|
||||||
|
qmlRegisterType<QSortFilterProxyModel>("Proxy", 1, 0, "QSortFilterProxyModel");
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_Baseline_Controls::init()
|
void tst_Baseline_Controls::init()
|
||||||
|
|
Loading…
Reference in New Issue