0
votes

I have created a ListView in QML, and I want to be able to implement something like an active item, using an QAbstractListModel as the model that the QML uses. To be more specific, I am using a generic object model, as described in the answer to this question. However, in my QML delegate I have something like this:

Component
{
    id: itemDlgt

    Rectangle
    {
        id: rec
        width: 50
        height: 50
        color: "#645357"

        property bool itemActive: false

        AbstractItem //AbstractItem is the class that my object model uses. Its only property is a boolean value
        {
            id: s
        }

        MouseArea
        {
            anchors.fill: parent

            onClicked:
            {
                s.status = !s.status
                itemActive= s.status // using this to trigger onItemActiveChanged
                console.log(s.status)
                console.log(index)
            }
        }

        onItemActiveChanged:
        {
            if (itemActive == true)
                rec.color = "#823234"
            else
                rec.color = "#645357"
        }


    }
}

What I want to do, is have only one item in the ListView to hold a true value at a time. As soon as another item is clicked, I want to set the AbstractItem of the previously selected item to false, then set the AbstractItem of the new item to true.

Of course, I could use something like this:

ListView 
{
    id: view
    anchors.fill: parent
    clip: true
    model: myAbstractModel
    delegate: itemDlgt
    spacing: 5 
    focus: true //using focus could allow to highlight currently selected item, 
                //by using ListView.isCurrentItem ? "blue": "red"
}

But this doesn't seem to work with a QAbstractListModel, since neither arrow keys, nor clicking on an item seems to highlight the current item.

In addition, I want that item to be highlighted again, in the event that the ListView is forced to reset itself from the c++ side, when I use beginResetModel() and endResetModel(). If I were using a QAbstractListModel as described in Qt Documentation, I would be able to do that easily, by saving the index of the selected item, and storing it until a new item was selected. In other words, something like this:

//somewhere in QAbstractListModel's subclass .h file
int x; // temporary storage for keeping currently selected item

//QAbstractListModel's subclass .cpp file
changeCurrentItem(int index) // function that gets called when user selects an item
{
    //...
    //Checking if x has a value, If not, I simply set the
    //needed item's value to true, and then set x to the value of index.
    //Else do the following...
    m_ItemList.at(x).setToFalse();
    m_ItemList.at(index).setToTrue();
    x = index;
}

But I was facing several issues when I was using that, which is the reason why I decided to use a generic object model, which seems to be more flexible.

Finally, I want to be able to send a signal to the c++ side of the code whenever the currently selected item changes, which is trivial with a MouseArea, but I know not of a method to do that using the ListView's focus property, should that be an option.

To make long things short, this is my question in a few words:

Am I missing something in regards to QML code, that would allow me to highlight the currently selected item, while also being able to keep it active after reseting ListView, and being able to send a signal to c++ when it changes? If not, is there a way to implement a function inside my generic object model, that keeps track of the currently selected item, so that I can highlight it?

1

1 Answers

0
votes

If using the ListView's currentIndex property is the goto approach.

Simply set the color in the delegate via:

color: index == view.currentIndex ? "blue": "red"

And don't forget that in order for this to work, you must set the active index, it doesn't work by magic, and by default it will be -1, so no item will be highlighted:

//in the delegate mouse area
onClicked: view.currentIndex = index

The same applies to keyboard events too, you will have to tell the view what to do with the events, so you can have the up and down keys increment and decrements the current index.

There is another approach, which is more applicable if you want to detach the active item functionality from any view, and have it at item level:

property ItemType activeItem: null
function setActiveItem(item) {
  if (activeItem) activeItem.active = false
  activeItem = item
  activeItem.active = true
}

The focus property only specifies whether an item has keyboard event focus or not.

If you want to signal on the the index change, use onCurrentIndexChanged