Reputation: 1732
I am trying to sort a list of items in the QML listview, as per the input provided by user. I have written the logic for sorting but unable to set the sorted model. It seems the sorted model is not assigned tp the actual model used for the original ListView. Please have a look to the code and let me know if I am doing something wrong.
//main.qml
import QtQuick 1.1
Rectangle {
id: page
width: 500; height: 400
color: "#edecec"
ModifiedForSorting {
id: search; focus: true
}
}
//ModifiedForSorting.qml
import QtQuick 1.1
FocusScope {
id: focusScope
width: 250; height: 28
Text {
id: typeSomething
anchors.fill: parent; anchors.leftMargin: 8
verticalAlignment: Text.AlignVCenter
text: "Type here..."
color: "gray"
font.italic: true
}
MouseArea {
anchors.fill: parent
onClicked: { focusScope.focus = true; textInput.openSoftwareInputPanel(); }
}
TextInput {
id: textInput
anchors { left: parent.left; leftMargin: 8;rightMargin: 8; verticalCenter: parent.verticalCenter }
focus: true
selectByMouse: true
onTextChanged: {
//update list as per input from user
container.getSortedItems(textInput.text);
color = "red"
}
}
states: State {
name: "hasText"; when: textInput.text != ''
PropertyChanges { target: typeSomething; opacity: 0 }
}
transitions: [
Transition {
from: ""; to: "hasText"
NumberAnimation { exclude: typeSomething; properties: "opacity" }
},
Transition {
from: "hasText"; to: ""
NumberAnimation { properties: "opacity" }
}
]
Rectangle {
id: container
width: 500; height: 400
color: "#343434"
anchors.top: textInput.bottom
ListModel {
id: namesModel
ListElement {
title: "Mumbai"
}
ListElement {
title: "Pune"
}
ListElement {
title: "Bangalore"
}
ListElement {
title: "Kolkata"
}
ListElement {
title: "Hyderabad"
}
ListElement {
title: "Nagpur"
}
ListElement {
title: "Thane"
}
}
// The delegate for each item in the model:
Component {
id: listDelegate
Item {
id: delegateItem
width: listView.width; height: 55
clip: true
Row {
anchors.verticalCenter: parent.verticalCenter
spacing: 10
Column {
anchors.verticalCenter: parent.verticalCenter
Text {
text: title
font.pixelSize: 15
color: "white"
}
}
}
}
}
function showAll() {
var filteredItems = "";
for (var i = 0; i < namesModel.count; i++) {
filteredItems = filteredItems + namesModel.get(i).title;
}
listView.model = filteredItems;
//namesModel = filteredItems;
}
function getSortedItems(searchTerm) {
var filteredItems = "";
if (searchTerm === "") {
showAll();
return;
}
for (var i = 0; i < namesModel.count; i++) {
if (namesModel.get(i).title.indexOf(searchTerm) === 0) {
filteredItems = filteredItems + namesModel.get(i).title;
}
}
listView.model = filteredItems;
//namesModel = filteredItems;
}
// The ListView:
ListView {
id: listView
anchors.fill: parent; anchors.margins: 20
model: namesModel
delegate: listDelegate
}
}
}
As shown above I guess the sorted model is not reassigned to the ListView. I am not sure. Please help me out. Thanks.
Upvotes: 4
Views: 1625
Reputation: 25981
You can now do this with far less code using Array filter()
. Below is an example written using Qt6.x syntax:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
Page {
background: Rectangle { color: "#edecec" }
property var cities: [
{ title: "Mumbai" },
{ title: "Pune" },
{ title: "Bangalore" },
{ title: "Kolkata" },
{ title: "Hyderabad" },
{ title: "Nagpur" },
{ title: "Thane" }
]
ColumnLayout {
anchors.fill: parent
TextField {
id: textField
Layout.fillWidth: true
placeholderText: qsTr("Type something here")
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: cities.filter( city => !textField.text || city.title.toLowerCase().indexOf(textField.text.toLowerCase()) !== -1 )
delegate: ItemDelegate { text: modelData.title }
}
}
}
You can Try it Online!
Alternatively, you can consider using a ListModel combined with a DelegateGroup. The advantage of this is you do not need to repopulate a ListModel everytime the filter changes. Instead, you update the visibility of the item in the DelegateGroup:
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQml.Models
Page {
background: Rectangle { color: "#edecec" }
ListModel {
id: cities
ListElement { title: "Mumbai" }
ListElement { title: "Pune" }
ListElement { title: "Bangalore" }
ListElement { title: "Kolkata" }
ListElement { title: "Hyderabad" }
ListElement { title: "Nagpur" }
ListElement { title: "Thane" }
}
ColumnLayout {
anchors.fill: parent
TextField {
id: textField
Layout.fillWidth: true
placeholderText: qsTr("Type something here")
onTextChanged: delegateModel.refresh()
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
model: DelegateModel {
id: delegateModel
model: cities
groups: [
DelegateModelGroup {
id: allItems
name: "all"
includeByDefault: true
},
DelegateModelGroup {
id: visibleItems
name: "visible"
}
]
filterOnGroup: "visible"
delegate: ItemDelegate { text: title }
function refresh() {
allItems.setGroups(0, allItems.count, ["all"]);
for (let i = 0; i < allItems.count; i++) {
let v = true;
let item = allItems.get(i);
if (textField.text) {
v = item.model.title.toLowerCase().indexOf(textField.text.toLowerCase()) !== -1;
}
if (v) allItems.setGroups(i, ["all", "visible"] );
}
}
}
}
}
Component.onCompleted: delegateModel.refresh()
}
You can Try it Online!
Upvotes: 0
Reputation: 1039
Taking a look at your code I guess you mean filtering items, and not sorting.
In your functions you try to assign a string value filteredItems
to the listView.model
property. You're probably getting an explicit error for that in your application log.
To solve this error you can use an auxiliary ListModel
and populate it with just the items that fits your filter.
Try to replace all your code below the listDelegate
Component with this:
ListModel{ id: filteredModel }
function getSortedItems(searchTerm) {
// Clear the aux model
filteredModel.clear();
// Add fitting items to the aux model
for (var i = 0; i < namesModel.count; i++) {
if (searchTerm === "" || namesModel.get(i).title.indexOf(searchTerm) === 0) {
filteredModel.append(namesModel.get(i));
}
}
}
// The ListView:
ListView {
id: listView
anchors.fill: parent; anchors.margins: 20
model: filteredModel
delegate: listDelegate
}
Upvotes: 3