move qml treeview items
-
Hi all,
I have QML TreeView with model derived from QAbstractItemModel, there is single column and I need to move items, up and down.
My code works well when all parent items are collapsed, then I can drag up/down items no problem.
Problem is when some view is expanded, then when I select item to move, only root items are moving and app crashes when moving item over opened branch
The question is: how do I check in QML that the item I'm moving is some child item and on cpp side moveRow should move child items of this branch?
Code I'm using is below.TreeView { id:treeView anchors.fill:parent TableViewColumn { title: "Name" role: "name_role" width: treeView.width } model:TreeModel rowDelegate: Rectangle { id:rowDelegate height:50 } itemDelegate: dragDelegate } Component { id: dragDelegate MouseArea { id: dragArea property alias item_idx:content.index property bool held: false height: content.height drag.target: held ? content : undefined drag.axis: Drag.YAxis onPressAndHold: held = true onReleased: held = false Rectangle { id: content property int index:model.index anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } width: dragArea.width height: 20 Text{ anchors.fill:parent text: model.name_role verticalAlignment: Text.AlignVCenter } border.width: 1 border.color: "lightsteelblue" color: dragArea.held ? "lightsteelblue" : "white" Behavior on color { ColorAnimation { duration: 100 } } radius: 2 Drag.active: dragArea.held Drag.source: dragArea Drag.hotSpot.x: width / 2 Drag.hotSpot.y: height / 2 states: State { when: dragArea.held ParentChange { target: content; parent: treeView } AnchorChanges { target: content anchors { horizontalCenter: undefined; verticalCenter: undefined } } } } DropArea { id:objectDropArea anchors { fill: parent; margins: 10 } onEntered: { TreeModel.move(drag.source.item_idx,dragArea.item_idx) } } } } cpp side: void TreeModel::move(int from, int to) { qDebug()<<"TreeModel::move from:"<<from<<" to:"<<to; int toSignal = to; if(from < to){ toSignal += 1; } if(!beginMoveRows(QModelIndex(), from, from, QModelIndex(), toSignal)){ return; } this->moveRow(QModelIndex(),from,QModelIndex(),toSignal); endMoveRows(); } void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent) { qDebug()<<"TreeModel::setupModelData parent row:"<<parent->row(); QList<TreeItem*> parents; QList<int> indentations; parents << parent; indentations << 0; int number = 0; while (number < lines.count()) { int position = 0; while (position < lines[number].length()) { if (lines[number].at(position) != ' ') break; position++; } QString lineData = lines[number].mid(position).trimmed(); if (!lineData.isEmpty()) { // Read the column data from the rest of the line. QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts); QList<QVariant> columnData; for (int column = 0; column < columnStrings.count(); ++column) columnData << columnStrings[column]; if (position > indentations.last()) { // The last child of the current parent is now the new parent // unless the current parent has no children. if (parents.last()->childCount() > 0) { parents << parents.last()->child(parents.last()->childCount()-1); indentations << position; } } else { while (position < indentations.last() && parents.count() > 0) { parents.pop_back(); indentations.pop_back(); } } // Append a new item to the current parent's list of children. parents.last()->appendChild(new TreeItem(columnData, parents.last())); } ++number; } }
Best,
Marek -
Hi all,
I have QML TreeView with model derived from QAbstractItemModel, there is single column and I need to move items, up and down.
My code works well when all parent items are collapsed, then I can drag up/down items no problem.
Problem is when some view is expanded, then when I select item to move, only root items are moving and app crashes when moving item over opened branch
The question is: how do I check in QML that the item I'm moving is some child item and on cpp side moveRow should move child items of this branch?
Code I'm using is below.TreeView { id:treeView anchors.fill:parent TableViewColumn { title: "Name" role: "name_role" width: treeView.width } model:TreeModel rowDelegate: Rectangle { id:rowDelegate height:50 } itemDelegate: dragDelegate } Component { id: dragDelegate MouseArea { id: dragArea property alias item_idx:content.index property bool held: false height: content.height drag.target: held ? content : undefined drag.axis: Drag.YAxis onPressAndHold: held = true onReleased: held = false Rectangle { id: content property int index:model.index anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } width: dragArea.width height: 20 Text{ anchors.fill:parent text: model.name_role verticalAlignment: Text.AlignVCenter } border.width: 1 border.color: "lightsteelblue" color: dragArea.held ? "lightsteelblue" : "white" Behavior on color { ColorAnimation { duration: 100 } } radius: 2 Drag.active: dragArea.held Drag.source: dragArea Drag.hotSpot.x: width / 2 Drag.hotSpot.y: height / 2 states: State { when: dragArea.held ParentChange { target: content; parent: treeView } AnchorChanges { target: content anchors { horizontalCenter: undefined; verticalCenter: undefined } } } } DropArea { id:objectDropArea anchors { fill: parent; margins: 10 } onEntered: { TreeModel.move(drag.source.item_idx,dragArea.item_idx) } } } } cpp side: void TreeModel::move(int from, int to) { qDebug()<<"TreeModel::move from:"<<from<<" to:"<<to; int toSignal = to; if(from < to){ toSignal += 1; } if(!beginMoveRows(QModelIndex(), from, from, QModelIndex(), toSignal)){ return; } this->moveRow(QModelIndex(),from,QModelIndex(),toSignal); endMoveRows(); } void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent) { qDebug()<<"TreeModel::setupModelData parent row:"<<parent->row(); QList<TreeItem*> parents; QList<int> indentations; parents << parent; indentations << 0; int number = 0; while (number < lines.count()) { int position = 0; while (position < lines[number].length()) { if (lines[number].at(position) != ' ') break; position++; } QString lineData = lines[number].mid(position).trimmed(); if (!lineData.isEmpty()) { // Read the column data from the rest of the line. QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts); QList<QVariant> columnData; for (int column = 0; column < columnStrings.count(); ++column) columnData << columnStrings[column]; if (position > indentations.last()) { // The last child of the current parent is now the new parent // unless the current parent has no children. if (parents.last()->childCount() > 0) { parents << parents.last()->child(parents.last()->childCount()-1); indentations << position; } } else { while (position < indentations.last() && parents.count() > 0) { parents.pop_back(); indentations.pop_back(); } } // Append a new item to the current parent's list of children. parents.last()->appendChild(new TreeItem(columnData, parents.last())); } ++number; } }
Best,
MarekThere has been some progress, If I pass from QML to cpp QModelIndex then I can change order of the child items
void TreeModel::move(QModelIndex from, QModelIndex to) { qDebug()<<"TreeModel::move from:"<<from.row()<<" to:"<<to.row(); if(to.row()<0) return; if(from.row()<0) return; int toSignal = to.row(); if(from.row() < to.row()){ toSignal += 1; } if(!beginMoveRows(from.parent(), from.row(), from.row(), to.parent(), toSignal)){ return; } endMoveRows(); }
Now the problem is that change is not persisten, when I open branch, change order of the items, close branch, and open again - order is reversed, so I need to change cpp model.
Second problem: if I move item from one branch to another, bad thing happens, crash
Best,
Marek -
There has been some progress, If I pass from QML to cpp QModelIndex then I can change order of the child items
void TreeModel::move(QModelIndex from, QModelIndex to) { qDebug()<<"TreeModel::move from:"<<from.row()<<" to:"<<to.row(); if(to.row()<0) return; if(from.row()<0) return; int toSignal = to.row(); if(from.row() < to.row()){ toSignal += 1; } if(!beginMoveRows(from.parent(), from.row(), from.row(), to.parent(), toSignal)){ return; } endMoveRows(); }
Now the problem is that change is not persisten, when I open branch, change order of the items, close branch, and open again - order is reversed, so I need to change cpp model.
Second problem: if I move item from one branch to another, bad thing happens, crash
Best,
MarekAnybody? Some help would be appreciated ;)
This is based on this example:
https://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html
I have added some simple functions to TreeItemvoid TreeItem::move(int from, int to) { qDebug()<<"TreeItem::move from:"<<from<<" to:"<<to; m_childItems.move(from,to); } void TreeItem::remove(int pos) { qDebug()<<"TreeItem::remove pos:"<<pos; m_childItems.removeAt(pos); } void TreeItem::insert(int pos,TreeItem *child) { qDebug()<<"TreeItem::insert pos:"<<pos<<" child:"<<child<<" count:"<<m_childItems.count(); m_childItems.insert(pos,child); }
So now when I'm moving items with the same parent, all is well.
When I'm moving item to change parent, something is wrong, like in model rowCount is not refreshed, or maybe I need to rebuild some persisten index ?
This is my code now:void TreeModel::move(QModelIndex from, QModelIndex to) { qDebug()<<"TreeModel::move from:"<<from.row()<<" to:"<<to.row(); if(to.row()<0) return; if(from.row()<0) return; int toSignal = to.row(); if(from.row() < to.row()){ toSignal += 1; } if(!beginMoveRows(from.parent(), from.row(), from.row(), to.parent(), toSignal)){ return; } if(from.parent().isValid()) { if(from.parent()==to.parent()) { //move inside the same parent TreeItem *parent=static_cast<TreeItem*>(from.parent().internalPointer()); parent->move(from.row(),to.row()); } else { //change parent TreeItem *old_parent=static_cast<TreeItem*>(from.parent().internalPointer()); TreeItem *new_parent=static_cast<TreeItem*>(to.parent().internalPointer()); TreeItem *child=static_cast<TreeItem*>(from.internalPointer()); if(!old_parent || !new_parent || !child) return; qDebug()<<"old_parent:"<<old_parent<<" new_parent:"<<new_parent<<" child:"<<child; old_parent->remove(from.row()); new_parent->insert(to.row(),child); } } qDebug()<<"item moved"; endMoveRows(); }
result of moving first item (Creating a Dialog) from second branch to first branch, indenatation is wrong, and later on app crashes when I try to move again this item.
Best,
Marek -
Anybody? Some help would be appreciated ;)
This is based on this example:
https://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html
I have added some simple functions to TreeItemvoid TreeItem::move(int from, int to) { qDebug()<<"TreeItem::move from:"<<from<<" to:"<<to; m_childItems.move(from,to); } void TreeItem::remove(int pos) { qDebug()<<"TreeItem::remove pos:"<<pos; m_childItems.removeAt(pos); } void TreeItem::insert(int pos,TreeItem *child) { qDebug()<<"TreeItem::insert pos:"<<pos<<" child:"<<child<<" count:"<<m_childItems.count(); m_childItems.insert(pos,child); }
So now when I'm moving items with the same parent, all is well.
When I'm moving item to change parent, something is wrong, like in model rowCount is not refreshed, or maybe I need to rebuild some persisten index ?
This is my code now:void TreeModel::move(QModelIndex from, QModelIndex to) { qDebug()<<"TreeModel::move from:"<<from.row()<<" to:"<<to.row(); if(to.row()<0) return; if(from.row()<0) return; int toSignal = to.row(); if(from.row() < to.row()){ toSignal += 1; } if(!beginMoveRows(from.parent(), from.row(), from.row(), to.parent(), toSignal)){ return; } if(from.parent().isValid()) { if(from.parent()==to.parent()) { //move inside the same parent TreeItem *parent=static_cast<TreeItem*>(from.parent().internalPointer()); parent->move(from.row(),to.row()); } else { //change parent TreeItem *old_parent=static_cast<TreeItem*>(from.parent().internalPointer()); TreeItem *new_parent=static_cast<TreeItem*>(to.parent().internalPointer()); TreeItem *child=static_cast<TreeItem*>(from.internalPointer()); if(!old_parent || !new_parent || !child) return; qDebug()<<"old_parent:"<<old_parent<<" new_parent:"<<new_parent<<" child:"<<child; old_parent->remove(from.row()); new_parent->insert(to.row(),child); } } qDebug()<<"item moved"; endMoveRows(); }
result of moving first item (Creating a Dialog) from second branch to first branch, indenatation is wrong, and later on app crashes when I try to move again this item.
Best,
MarekI have corrected move function a bit, it almost work ;) the problem is that some strange "artifact" is left afte move with change parent operation
Here is the code:
void TreeModel::move(QModelIndex from, QModelIndex to) { qDebug()<<"TreeModel::move from:"<<from.row()<<" to:"<<to.row(); int toSignal = to.row(); if(from.row() < to.row()){ toSignal += 1; } if(from.parent().isValid()) { if(from.parent()==to.parent()) { //move inside the same parent qDebug()<<"TreeModel::move move inside parent"; if(!beginMoveRows(from.parent(), from.row(), from.row(), to.parent(), toSignal)){ return; } TreeItem *parent=static_cast<TreeItem*>(from.parent().internalPointer()); parent->move(from.row(),to.row()); endMoveRows(); } else { //change parent TreeItem *old_parent=static_cast<TreeItem*>(from.parent().internalPointer()); TreeItem *new_parent=static_cast<TreeItem*>(to.parent().internalPointer()); TreeItem *child=static_cast<TreeItem*>(from.internalPointer()); if(!old_parent || !new_parent || !child) return; qDebug()<<"old_parent:"<<old_parent<<" new_parent:"<<new_parent<<" child:"<<child; emit beginRemoveRows(from.parent(),from.row(),from.row()); old_parent->remove(from.row()); emit endRemoveRows(); emit beginInsertRows(to.parent(),to.row(),to.row()); child->setParent(new_parent); new_parent->insert(to.row(),child); emit endInsertRows(); } } else { qDebug()<<"TreeModel::move parent invalid"; } }
Here is the result:
And some debug from this operation:TreeModel::move from: 0 to: 1 old_parent: 0x55ef91136820 new_parent: 0x55ef911363d0 child: 0x55ef91136930 TreeItem::remove pos: 0 TreeItem::insert pos: 1 child: 0x55ef91136930 count: 3 TreeModel::move from: -1 to: 1 TreeModel::move parent invalid
I was moving "Creating a Dialog" from first position in second branch to first branch, at the end there is some move operation with invalid parent index, I don't know where this comes from...
Best,
Marek