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)



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.