Home > Back-end >  Update c model on qml delegate visual model change
Update c model on qml delegate visual model change

Time:11-30

There's a simple QStandardItemModel defined in c which I am displaying in a QML ListView via custom Delegates and a DelegateModel. The ListView can be reordered via Drag'n Drop:

// The DropArea is part of the delegate `comp_container`
DropArea{
    anchors{fill: parent}
    keys: ["pageitem"]
    onEntered: {
        let from = drag.source.DelegateModel.itemsIndex
        let to = dragAreaPage.DelegateModel.itemsIndex
        if ( pageItemDragOperationStartIndex === -1 ){
            pageItemDragOperationStartIndex = from
        }
        pageItemDragOperationFinalIndex = to
        console.log(from   "->"   to)
        visualModel.items.move(from,to)
    }
}

Here is the delegate model and pageproxymodel is the c model.

DelegateModel {
    id: visualModel
    model: pageproxymodel
    delegate: comp_container
}

How do I want to update the c model? The delegate's top level item is a MouseArea and I handle the reordering in the release handler:

onReleased: {
    if ( pageItemDragOperationStartIndex !== -1 && pageItemDragOperationFinalIndex !== -1 ){
        console.log("Page item final drag operation: "   pageItemDragOperationStartIndex   "->"   pageItemDragOperationFinalIndex)
        pageproxymodel.move(pageItemDragOperationStartIndex, pageItemDragOperationFinalIndex)
        pageItemDragOperationStartIndex = -1
        pageItemDragOperationFinalIndex = -1
    }
}

The c model's move function forwards the call to this handler:

bool PageModel::moveRow(const QModelIndex &sourceParent,
                        int sourceRow,
                        const QModelIndex &destinationParent,
                        int destinationChild)
{
    if ( sourceRow < 0 || sourceRow > rowCount()-1 ||
         destinationChild < 0 || destinationChild > rowCount() )
    {
        return false;
    }

    beginMoveRows(sourceParent, sourceRow, sourceRow, destinationParent, destinationChild);
 
    QList<QStandardItem*> rowItems = takeRow(sourceRow);
    insertRow(destinationChild, rowItems);

    endMoveRows();

    return true;
}

With the above c model code, it crashes at the release handler in QML:

enter image description here

I've tried other things to see the effect, no crashes, but also not the expected behaviour.

  • deleting a single row (which deletes 2 (!) rows in the QML ListView)
  • deleting a single row without begin/end calls (deletes 1 rows in the QML ListView, but can't be right)
  • remove and insert a single row without begin/end calls (QML ListView looks fine for a while but comes out of sync after a few moves)

Basically all I want to do is to save the ListView state via the c model, after all that is a standard use case and something simple must be wrong on my side, yet I can't see it.

CodePudding user response:

Found the mistake: The pageItemDragOperationStartIndex and pageItemDragOperationFinalIndex variables where part of each delegate, but not of the page. Also, as was pointed out in the comments, using a QStandardItemModel it is not necessary to call the begin/end functions. Now it works like a charm.

CodePudding user response:

One thing I like to do with DelegateModel makes use of DelegateModelGroup. By declaring a group named "all", it introduces an attached property allIndex which is useful for tracking an item after it has been reordered. The following example implements a DelegateModel with both MouseArea and DropArea. When in dragging mode, I disable all MouseArea so that the DropArea can have a chance at responding.

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Page {
    property int activeMouseArea: -1
    ListView {
        id: listView
        width: 420
        height: parent.height
        model: SampleDelegateModel { }
        ScrollBar.vertical: ScrollBar {
            width: 20
            policy: ScrollBar.AlwaysOn
        }
    }
    footer: Text { id: dbg }
}

// SampleData.qml
import QtQuick
import QtQuick.Controls

ListModel {
    ListElement { name: "Steve Jobs" }
    ListElement { name: "Jeff Bezos" }
    ListElement { name: "Bill Gates" }
    ListElement { name: "Elon Musk" }
}

// SampleDelegateModel.qml
import QtQuick
import QtQuick.Controls
import QtQml.Models

DelegateModel {
    id: delegateModel
    model: SampleData { }
    delegate: SampleDelegate { }
    groups: [
        DelegateModelGroup {
            id: allItems
            name: "all"
            includeByDefault: true
            onCountChanged: {
                if (updateIndex > allItems.count) updateIndex = allItems.count;
                if (updateIndex < allItems.count) Qt.callLater(update, updateIndex);
            }
        }
    ]
    filterOnGroup: "all"
    function moveItem(from, to) {
        dbg.text = `Debugging: moveItem(${from},${to})`;
        allItems.move(from, to);
    }
}

// SampleDelegate.qml
import QtQuick
import QtQuick.Controls
import QtQml.Models

Rectangle {
    property int allIndex: DelegateModel.allIndex
    width: 400
    height: labelText.height   20
    border.color: "grey"
    z: mouseArea.drag.active || mouseArea.pressed ? 2 : 1
    property int dragTo: -1
    Drag.active: mouseArea.drag.active

    Text {
        id: labelText
        anchors.centerIn: parent
        text: allIndex   ": ["   index   "] "   name
    }

    DropArea {
        anchors.fill: parent
        onEntered: drag.source.dragTo = allIndex
    }

    MouseArea {
        id: mouseArea
        anchors.fill: parent
        drag.target: parent
        property point startPoint
        enabled: activeMouseArea === -1
        onPressed: {
            activeMouseArea = allIndex;
            dragTo = -1;
            startPoint = Qt.point(parent.x, parent.y);
        }
        onReleased: {
            activeMouseArea = -1;
            [parent.x,parent.y] = [startPoint.x, startPoint.y];
            Qt.callLater(delegateModel.moveItem, allIndex, dragTo);
        }
    }
}

You can Try it Online!

  • Related