skip to Main Content

How do I perform a multiple search in the QML Listview.

I have the following piece of code and it works fine for single Listview element:

Page 
{

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 10
        Label { text: qsTr("Search") }

        Frame {
            Layout.fillWidth: true
            TextEdit {
                id: searchTextEdit
                width: parent.width
            }
        }

        Label { text: qsTr("States (count:%1)").arg(filterDelegateModel.count) }

        ListView {
            Layout.fillWidth: true
            Layout.fillHeight: true
            clip: true
            model: FilterDelegateModel {
                id: filterDelegateModel
                model: states
                filter: search ? model =>  model.state.toLowerCase().indexOf(search) !== -1 : null
                property string search: searchTextEdit.text.toLowerCase()
                onSearchChanged: Qt.callLater(update)
                delegate: Frame {
                    id: frame
                    property int visibleIndex: DelegateModel.visibleIndex
                    width: ListView.view.width
                    background: Rectangle {
                        color: visibleIndex & 1 ? "#e0e0e0" : "#d0d0d0"
                        border.color: "#c0c0c0"
                    }
                    RowLayout {
                        width: parent.width
                        Text {
                            text: (visibleIndex + 1)
                            color: "grey"
                        }
                        Text {
                            Layout.fillWidth: true
                            text: model.state
                        }
                    }
                }
            }
        }
    }

    ListModel {
        id: states
        ListElement { state:"Durban" }
        ListElement { state:"New York" }
        ListElement { state:"Toronto" }
        ListElement { state:"Mbabane" }
        ListElement { state:"Harare" }
    }
}

Now I’ve added a second property "Country" as follows and the search filter isn’t working anymore:

    ListModel {
        id: states
        ListElement { state:"Durban"; country: "RSA" }
        ListElement { state:"New York"; country: "USA" }
        ListElement { state:"Toronto"; country: "Canada" }
        ListElement { state:"Mbabane"; country: "Swaziland" }
        ListElement { state:"Harare"; country: "Zimbabwe" }
    }

I need to be able to filter the list by either the state or country.

2

Answers


  1. Try updating your FilterDelegateModel filter function to search for the input text within both "state" and "country" properties. Here’s the modified code:

    Page {
        ColumnLayout {
            anchors.fill: parent
            anchors.margins: 10
            Label { text: qsTr("Search") }
    
            Frame {
                Layout.fillWidth: true
                TextEdit {
                    id: searchTextEdit
                    width: parent.width
                }
            }
    
            Label { text: qsTr("States (count:%1)").arg(filterDelegateModel.count) }
    
            ListView {
                Layout.fillWidth: true
                Layout.fillHeight: true
                clip: true
                model: FilterDelegateModel {
                    id: filterDelegateModel
                    model: states
                    filter: search ? model => {
                        return model.state.toLowerCase().indexOf(search) !== -1 ||
                               model.country.toLowerCase().indexOf(search) !== -1;
                    } : null
                    property string search: searchTextEdit.text.toLowerCase()
                    onSearchChanged: Qt.callLater(update)
                    delegate: Frame {
                        id: frame
                        property int visibleIndex: DelegateModel.visibleIndex
                        width: ListView.view.width
                        background: Rectangle {
                            color: visibleIndex & 1 ? "#e0e0e0" : "#d0d0c0"
                            border.color: "#c0c0c0"
                        }
                        RowLayout {
                            width: parent.width
                            Text {
                                text: (visibleIndex + 1)
                                color: "grey"
                            }
                            Text {
                                Layout.fillWidth: true
                                text: model.state
                            }
                            Text {
                                Layout.fillWidth: true
                                text: model.country
                            }
                        }
                    }
                }
            }
        }
    
        ListModel {
            id: states
            ListElement { state: "Durban"; country: "RSA" }
            ListElement { state: "New York"; country: "USA" }
            ListElement { state: "Toronto"; country: "Canada" }
            ListElement { state: "Mbabane"; country: "Swaziland" }
            ListElement { state: "Harare"; country: "Zimbabwe" }
        }
    }
    

    This updated code enhances the filter function to consider both the "state" and "country" properties. As a result, your ListView will now filter items based on input text entered into the TextEdit, considering both the state and country properties of each item in the model.

    Login or Signup to reply.
  2. You can join the strings together prior to doing an indexOf, e.g.

    filter: search ? model => (model.state + ", " + model.country).toLowerCase().indexOf(search) !== -1 : null
    

    Here’s a full working example:

    import QtQuick
    import QtQuick.Controls
    import QtQuick.Layouts
    import QtQuick3D
    Page {
        id: page
        ColumnLayout {
            anchors.fill: parent
            anchors.margins: 10
            Label { text: qsTr("Search") }
    
            Frame {
                Layout.fillWidth: true
                TextEdit {
                    id: searchTextEdit
                    width: parent.width
                }
            }
    
            Label { text: qsTr("States (count:%1)").arg(filterDelegateModel.count) }
    
            ListView {
                Layout.fillWidth: true
                Layout.fillHeight: true
                clip: true
                model: FilterDelegateModel {
                    id: filterDelegateModel
                    model: states
                    filter: search ? model => (model.state + ", " + model.country).toLowerCase().indexOf(search) !== -1 : null
                    property string search: searchTextEdit.text.toLowerCase() 
                    onSearchChanged: Qt.callLater(update)
                    delegate: Frame {
                        id: frame
                        property int visibleIndex: DelegateModel.visibleIndex
                        width: ListView.view.width
                        background: Rectangle {
                            color: visibleIndex & 1 ? "#e0e0e0" : "#d0d0d0"
                            border.color: "#c0c0c0"
                        }
                        RowLayout {
                            width: parent.width
                            Text {
                                text: (visibleIndex + 1)
                                color: "grey"
                            }
                            Text {
                                Layout.fillWidth: true
                                text: model.state + ", " + model.country
                            }
                        }
                    }
                }
            }
        }
    
        ListModel {
            id: states
            ListElement { state:"Durban"; country: "RSA" }
            ListElement { state:"New York"; country: "USA" }
            ListElement { state:"Toronto"; country: "Canada" }
            ListElement { state:"Mbabane"; country: "Swaziland" }
            ListElement { state:"Harare"; country: "Zimbabwe" }
        }
    }
    
    // FilterDelegateModel.qml
    import QtQuick 2.15
    import QtQuick.Controls 2.15
    import QtQml.Models 2.15
    
    DelegateModel {
        property var filter: null
        readonly property bool running: !filtered
        readonly property bool filtered: updateIndex >= allItems.count
        readonly property int progress: filtered ? 100 : Math.floor(100 * updateIndex / allItems.count)
        property int updateIndex: 0
        onFilterChanged: Qt.callLater(update)
        groups: [
            DelegateModelGroup {
                id: allItems
                name: "all"
                includeByDefault: true
                onCountChanged: {
                    if (updateIndex > allItems.count) updateIndex = allItems.count;
                    if (updateIndex < allItems.count) Qt.callLater(update, updateIndex);
                }
            },
            DelegateModelGroup {
                id: visibleItems
                name: "visible"
            }
        ]
        filterOnGroup: "visible"
    
        function update(startIndex) {
            startIndex = startIndex ?? 0;
            if (startIndex < 0) startIndex = 0;
            if (startIndex >= allItems.count) {
                updateIndex = allItems.count;
                return;
            }
            updateIndex = startIndex;
            if (updateIndex === 0) {
                allItems.setGroups(0, allItems.count, [ "all" ] );
            }
            for (let ts = Date.now(); updateIndex < allItems.count && Date.now() < ts + 50; updateIndex++) {
                let visible = !filter || filter(allItems.get(updateIndex).model);
                if (!visible) continue;
                allItems.setGroups(updateIndex, 1, [ "all", "visible" ]);
            }
            if (updateIndex < allItems.count) Qt.callLater(update, updateIndex);
        }
    
        Component.onCompleted: Qt.callLater(update)
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search