1
votes

I'm using a QML ListView with section, click on item to remove with animation. Here the code:

import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    visible: true
    width: 400
    height: 400

    ListView {
        id: list
        anchors.fill: parent
        clip: true
        spacing: 0

        onContentYChanged: console.log("onContentYChanged: " + contentY)
        onContentHeightChanged: console.log("onContentHeightChanged: " + contentHeight)

        model: ListModel {
            id: myModel
            ListElement {name: "Item 1";type: "A"}
            ListElement {name: "Item 2";type: "A"}
            ListElement {name: "Item 3";type: "B"}
        }

        delegate: Rectangle {
            width: parent.width
            height: 50
            color: (index % 2 == 1) ? "#5678a2" : "#88a345"

            Text {
                anchors.verticalCenter: parent.verticalCenter
                text: name
            }

            MouseArea {
                anchors.fill: parent
                onClicked: {
                    console.log("remove: " + index + ", contentY:" + list.contentY)
                    myModel.remove(index)
                }
            }
        }

        section.property: "type"
        section.delegate: Rectangle {
            height: 30
            Text {
                anchors.verticalCenter: parent.verticalCenter
                text: section
            }
        }

        displaced: Transition {
            NumberAnimation { properties: "x,y"; duration: 500; easing.type: Easing.OutCubic }
        }
        remove: Transition {
            NumberAnimation { property: "opacity"; from: 1.0; to: 0; duration: 500 }
            NumberAnimation { property: "scale"; from: 1.0; to: 0; duration: 500 }
        }
    }
}

When I clicked on the first item(Item 1), it got deleted, but the Item 2 was flying up to outside the window. The ListView displayed the remaining items in wrong positions. ContentY changed to 80 (which was the y position of Item 2 before) instead of remaining at 0.

qml: onContentHeightChanged: 300
qml: onContentHeightChanged: 240
qml: onContentHeightChanged: 210
qml: remove: 0, contentY:0
qml: onContentYChanged: 80
qml: onContentHeightChanged: 160

It will work correctly if:

  • Delete other items except the top one.
  • Disable either the section or animation.
1
Hello. Do you still need help? If so, can you clarify, what exactly do you mean by >Delete other items except the top one should it be undeletable? If so, should I be able to delete 1st element if there are few element in group?Maxim Skvortsov
@MaximSkvortsov When there're many items in the ListView, the bug only happens when deleting the 1st (top) item.Vu Trinh

1 Answers

1
votes

I tried your code with Qt 5.13.1. And currently downloading Qt 5.15. For now it looks like it is a bug with section, because I found a lot of not not closed bug reports on bugtracker. I can suggest 2 ways of solving your problem.

  1. Performing animation while locking removal.
  2. Using model with categories.

1st solution I tested by meself. Here is what you need to change to try it:

Delete ListView's removal animations. Add following code to your delegate

ListView.onRemove: SequentialAnimation {
                        PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: true }
                        ParallelAnimation {
                            NumberAnimation { target: wrapper; property: "opacity"; to: 0; duration: 500 }
                            NumberAnimation { target: wrapper; property: "scale"; to: 0; duration: 500 }
                        }
                        PropertyAction { target: wrapper; property: "ListView.delayRemove"; value: false }
                    }

What is this? ListView has a signal remove() which is called BEFORE removing an item from the view. It is described in documentation It is also noted, that

If a remove transition has been specified, it is applied after this signal is handled, providing that delayRemove is false. So in delegate you simply block removal from view, do you animation and unblock it. I suppose it won't be as clean and beautiful as you want it to be simply because view doesn't andjust it's size in this case.

2nd solution

I didn't try to implement it, but I can imagine having a model like this:

ListModel {
    id: myModel
    ListElement { type: "category"; name: "cat1" }
    ListElement { name: "delegate1"; type: "delegate"; catrgory: "cat1"}
    ListElement { name: "delegate2"; type: "delegate"; catrgory: "cat1"}
    ListElement { name: "delegate3"; type: "delegate"; catrgory: "cat1"}
    ListElement { type: "category"; name: "cat2" }
    ListElement { name: "delegate4"; type: "delegate"; catrgory: "cat2"}

To use this as you want, you will need to castomize your delegate accordingly and removal function accordingly, which will lead to much more complex code in comparison to what it would be if section would work properly.

UPD: Same problem in 5.15