From 10:00 CET Friday 22nd November we will adjust how the server works to deal with some recently reported problems. Therefore there may be a load problem, if you experience more problems than usual trying to access the forum then please PM AndyS or any of the moderators so they can inform me.


Custom QStandardItem drag and drop not working properly



  • Hello,

    I come to you cause I do not manage to make my custom QStandardItem/QStandardItemModel working as I want it to.

    I basically have these 4 classes :

    class Map
    {
    public:
        Map();
        Map(const sf::String& name);
        Map(const Map& map);
    
        //accessor
    
    private:
        sf::String m_name;
    };
    /*****************************************/
    
    class MapItem : public QStandardItem
    {
    public:
        MapItem(Map* map);
    
        Map* map() const;
    
        QVariant data(int role = Qt::UserRole + 1) const;
        void setData(const QVariant &value, int role = Qt::UserRole + 1);
    
        QStandardItem* clone() const;
    
    private:
        Map* m_map;
    };
    
    /*****************************************/
    
    class MapItemModel : public QStandardItemModel
    {
    public:
        MapItemModel();
    };
    
    /*****************************************/
    
    class MapItemView : public QTreeView
    {
    public:
        MapItemView(QWidget* parent = Q_NULLPTR);
    
        QSize sizeHint() const;
    };
    
    

    So there is a custom QStandardItem (MapItem) which contains a pointer on a Map object. My first question is : is it the correct way to have a QStandardItem containing custom data ? Or should I use the setData method with a custom userRole (which I tried but did not seem to be really pertinent in my opinion) ?

    Then, I have a custom model which I aliment with MapItems. My model is set in an MapItemView, which is simply a QTreeView in which I intend to implement custom behaviors. My second and main problem is that I cannot manage to make internal move operations work properly. Here is the relevant source code:

    //***********MapItem
    
    MapItem::MapItem(Map* map)
    {
        m_map = map;
    }
    
    Map* MapItem::map() const
    {
        return m_map;
    }
    
    QVariant MapItem::data(int role) const
    {
        if(role == Qt::DisplayRole || role == Qt::EditRole)
        {
            return QVariant(map()->name().toAnsiString().c_str());
        }
    
        return QStandardItem::data(role);
    }
    
    void MapItem::setData(const QVariant &value, int role)
    {
        if(role == Qt::DisplayRole || role == Qt::EditRole)
        {
            QString mapName = value.toString();
            map()->setName(mapName.toStdString().c_str());
            emitDataChanged();
        }
        else
        {
            QStandardItem::setData(value, role);
        }
    }
    
    QStandardItem* MapItem::clone() const
    {
        Map* map = new Map(*m_map);
        MapItem* mapItem = new MapItem(map);
        return mapItem;
    }
    
    //***********MapItemModel
    
    MapItemModel::MapItemModel() :
        QStandardItemModel()
    {
        setItemPrototype(new MapItem(new Map()));
    
        QStandardItem* parentItem = invisibleRootItem();
        MapItem* mapItem = new MapItem(new Map("test1"));
        parentItem->appendRow(mapItem);
        mapItem->appendRow(new MapItem(new Map("test11")));
        mapItem->appendRow(new MapItem(new Map("test12")));
        parentItem->appendRow(new MapItem(new Map("test2")));
        parentItem->appendRow(new MapItem(new Map("test3")));
    }
    
    //***********MapItemView
    
    MapItemView::MapItemView(QWidget* parent) :
        QTreeView(parent)
    {
        setModel(new MapItemModel);
        setHeaderHidden(true);
        expandAll();
    
        //drag and drop
        setSelectionMode(QAbstractItemView::SingleSelection);
        setDragEnabled(true);
        viewport()->setAcceptDrops(true);
        setDropIndicatorShown(true);
        setDragDropMode(QAbstractItemView::InternalMove);
    }
    

    When I perform a move operation of a MapItem, the MapItem i moved "dissapears" and a new default empty MapItem is provided. According to the documentation, it must be caused by the

    setItemPrototype(new MapItem(new Map()));
    

    call. But if I do not call this method, the moved MapItem "becomes" a QStandardItem (thus with no Map object).

    My second and main question is then : how to implement correct drag and drop operations with custom QStandardItem which needs to own custom data ?

    With thanks


  • Moderators

    @Yohdu said:

    My second and main question is then : how to implement correct drag and drop operations with custom QStandardItem which needs to own custom data ?

    With the standard item widgets/models you can't drag-n-drop custom item roles.
    This makes sense, since the model doesn't know which UserRole+ was set.
    It would need to iterate all possible item-role values when creating the drop-data.

    The only solution i see is to create your own custom model and reimplement the needed drag-n-drop methods. This also give you full control over your data structure storing the data inside the model.
    This is more work, but you will learn a lot and gain some performance (by avoiding the standard-item classes)