There's a simple QStandardItemModel
defined in c which I am displaying in a QML
ListView
via custom Delegate
s 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:
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!