I know this is really not the place to ask this, but I couldn't get an answer to this question on the official Qt forums. I know others have gone this route when building GUIs for their own engines, so I have a better chance here. If this is an issue with this, I have no problem removing this post. That said, here's the contents of my post from the Qt forums:
I've setup my own derivative class of QAbstractItemModel for my engine's node hierarchy. It seems to perform inserts and deletes properly. On top of that, I can get my elements to my via drag and drop. I've overridden moveRows in my QAbstractItemModel subclass, and I call moveRows right before dropMimeData() returns. It appears that dropMimeData() needs to return false upon success after moveRows() is called for the QTreeView to update correctly. If it returns true, then my items will disappear in the view. I'll print out my scene's node hierarchy to the console, and everything looks correct. It just seems that the model doesn't update. Here's the implementation of my SceneModel class (subclassed from QAbstractItemModel):
const QString SceneModelMIMEType = "application/node.bin";
#include "SceneModel.h"
#include "Scene.h"
#include "Node.h"
#include <stdio.h>
#include <stdlib.h>
#include <QKeyEvent>
#include <QMimeData>
#include <QByteArray>
#include <QDebug>
Qt::ItemFlags SceneModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
if(index.isValid())
return defaultFlags | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsEditable;
else
return defaultFlags | Qt::ItemIsDropEnabled;
return QAbstractItemModel::flags(index);
}
Qt::DropActions SceneModel::supportedDropActions() const
{
return QAbstractItemModel::supportedDropActions() | Qt::MoveAction;
}
int SceneModel::rowCount(const QModelIndex &parent) const
{
// make sure the scene's root node is valid and the parent's column is zero
if(!Scene::GetInstance()->GetRoot() || parent.column() > 0)
return 0;
// return the parent's children, if valid
if(parent.isValid())
return static_cast<Node*>(parent.internalPointer())->GetNumChildren();
// otherwise, return the children
return Scene::GetInstance()->GetRoot()->GetNumChildren();
}
int SceneModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
QModelIndex SceneModel::index(int row, int column, const QModelIndex &parent) const
{
// make sure the root, row and column data is valid
if(!Scene::GetInstance()->GetRoot() || !hasIndex(row, column, parent))
return QModelIndex();
// get the parent Node from the index
Node *parentItem = Scene::GetInstance()->GetRoot();
if(parent.isValid())
parentItem = static_cast<Node*>(parent.internalPointer());
Node *childItem = parentItem->GetChild(row);
if(childItem)
return createIndex(row, column, childItem);
return QModelIndex();
}
QModelIndex SceneModel::parent(const QModelIndex &index) const
{
// make sure the index is valid
if(!index.isValid())
return QModelIndex();
// get the child item to get the parent item
Node *childItem = static_cast<Node*>(index.internalPointer());
if(!childItem) return QModelIndex();
Node *parentItem = childItem->GetParent();
// return nothing if the parent item is the root
if(!parentItem || parentItem == Scene::GetInstance()->GetRoot())
return QModelIndex();
return createIndex(parentItem->GetChildIndex(), 0, parentItem);
}
QVariant SceneModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
// handle displaying the data
if(role == Qt::DisplayRole || role == Qt::EditRole)
{
// get the child and parent Node pointers
Node *childItem = static_cast<Node*>(index.internalPointer());
return QString(childItem->GetName().c_str());
}
return QVariant();
}
bool SceneModel::submit()
{
// check if the selected node is valid
Node *selectedNode = Scene::GetInstance()->GetSelectedNode();
if(selectedNode)
{
// get the selected node's index, and update its display data
QModelIndex selectedIndex = createIndex(selectedNode->GetChildIndex(), 0, selectedNode);
setData(selectedIndex, QString(selectedNode->GetName().c_str()), Qt::DisplayRole);
}
return true;
}
bool SceneModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
// check if the index is valid, and either in EditRole or DisplayRole mode
if(index.isValid() && (role == Qt::EditRole || role == Qt::DisplayRole))
{
//
Node *node = static_cast<Node*>(index.internalPointer());
if(!node) return false;
// set the node's new name, and signal that data has changed
node->SetName(std::string(value.toString().toUtf8()));
emit dataChanged(index, index);
return true;
}
return setData(index, value, role);
}
bool SceneModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
// make sure this is aciton shouldn't be ignored
if(!data || action == Qt::IgnoreAction)
return false;
// get rowCount if row is invalid (this might be an old Qt bug)
if(row == -1)
row = rowCount() - 1;
// get the parent node
Node *parentNode = static_cast<Node*>(parent.internalPointer());
if(!parentNode) return false;
// get the encoded data of our Node pointer
QByteArray encodedData = data->data(SceneModelMIMEType);
Node *node = (Node*)encodedData.toULongLong();
if(!node) return false;
Node *sourceParentNode = node->GetParent();
QModelIndex sourceParent = createIndex(sourceParentNode->GetChildIndex(), 0, sourceParentNode);
//qDebug() << "dropMimeData() - movedNode:" << node->GetName().c_str() << "parentNode:" << sourceParentNode->GetName().c_str();
/*qDebug() << "sourceParent:" << sourceParentNode->GetName().c_str()
<< "destinationParent:" << parentNode->GetName().c_str()
<< "movingNode:" << node->GetName().c_str()
<< "sourceRow:" << node->GetChildIndex()
<< "destinationRow:" << row;*/
// move the row, but don't return true as that'll trigger the drop event
moveRow(sourceParent, node->GetChildIndex(), parent, row);
return false; // returning true causes weird behavior
}
QStringList SceneModel::mimeTypes() const
{
QStringList types;
types << SceneModelMIMEType;
return types;
}
QMimeData *SceneModel::mimeData(const QModelIndexList &indexes) const
{
// make sure there's exactly 1 index and it's valid
if(indexes.size() != 1 || !indexes[0].isValid())
return nullptr;
// get the Node pointer from the index's internal pointer, convert it to quintptr, then to a UTF string
quintptr nodeAddress = (quintptr)indexes[0].internalPointer();
QByteArray encodedData(QString::number(nodeAddress).toUtf8());
// allocate mimeData, provide it with encodedData, and return it
QMimeData *mimeData = new QMimeData();
mimeData->setData(SceneModelMIMEType, encodedData);
return mimeData;
}
bool SceneModel::insertRows(int row, int count, const QModelIndex &parent)
{
Node *parentNode = static_cast<Node*>(parent.internalPointer());
if(!parentNode)
parentNode = Scene::GetInstance()->GetRoot();
// make sure a parent node was found before continuing
if(parentNode)
{
// begin inserting rows, and add begin adding uniquely-named fields
beginInsertRows(parent, row, row+count-1);
// insert nodes
for(int i=0;i<count;++i)
parentNode->AddChild("New Node");
endInsertRows();
return true;
}
return QAbstractItemModel::insertRows(row, count, parent);
}
bool SceneModel::removeRows(int row, int count, const QModelIndex &parent)
{
Node *parentItem = static_cast<Node*>(parent.internalPointer());
if(!parentItem)
parentItem = Scene::GetInstance()->GetRoot();
// make sure a parent node was found before continuing
if(parentItem)
{
beginRemoveRows(parent, row, row+count-1);
parentItem->RemoveChildren(row, count);
endRemoveRows();
return true;
}
return QAbstractItemModel::removeRows(row, count, parent);
}
bool SceneModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
{
Node *sourceParentNode = static_cast<Node*>(sourceParent.internalPointer());
Node *destinationParentNode = static_cast<Node*>(destinationParent.internalPointer());
Node *childNode = sourceParentNode->GetChild(sourceRow);
// if source and destination parents are the same, move elements locally
if(sourceParentNode == destinationParentNode)
{
// only process if a local move is possible
if(sourceParentNode->IsMovePossible(sourceRow, count, destinationChild))
{
beginMoveRows(sourceParent, sourceRow, sourceRow+count-1, destinationParent, destinationChild);
sourceParentNode->MoveChildren(sourceRow, count, destinationChild);
endMoveRows();
}
} else {
// otherwise, move the node under the parent
beginMoveRows(sourceParent, sourceRow, sourceRow+count-1, destinationParent, destinationChild);
childNode->SetParent(destinationParentNode);
endMoveRows();
}
Scene::GetInstance()->GetRoot()->PrintChildren();
return true;
}
I didn't provide the interface since it doesn't have any member fields. It just has a constructor and destructor, but the constructor that initializes the base class with the passed-in QObject *parent parameter. I couldn't figure out how to embed the code above into code tags so that it's much easier to read. Sorry if something didn't make sense in my description above. I'm tired today.