Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Drag&Drop in QML/C++ mixed model/view
Forum Updated to NodeBB v4.3 + New Features

Drag&Drop in QML/C++ mixed model/view

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
6 Posts 2 Posters 1.4k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • G Offline
    G Offline
    Gourmet
    wrote on last edited by Gourmet
    #1

    I have got working Qt/C++ model with QML implemented view using ListView. It works fine but now I want make available D&D operating over list items. It will not be just a visual reordering - list model will change after view item moved to another list position. Therefore I must implement some communication with C++ model. I read QML Dynamic View Ordering Tutorial chapter and now I see how change the QML code to allow D&D. But this tutorial uses QML implemented model therefore I do not see clearly how I should inform my model about D&D view changes. I must send to model the signal with information about two indexes - the index of item when drag was started and the index of item when it was dropped.

    As I can imagine first I must declare signal in root (following uses code from tutorial):

    Rectangle {
        id: root
        property int startIndex
        signal sendIndexes( int old, int new )
        width: 300; height: 400
    .....
    

    In C++ code I attach slot to the signal. I already have got similar code working in other case.
    Then in MouseArea I can store starting index and send signal when needed:

     MouseArea {
                id: dragArea
    ...
                onPressAndHold: {
                            held = true
                            startIndex = index
                }
                onReleased: {
                            held = false
                            root.sendIndexes( startIndex, index )
                }
    

    In receiving slot I can define if indexes are equal then no moving happen.

    Is this enough or I must make something else? I am not sure if the index changes when onReleased handler is called. May be signal better emit in onDrop handler inside DropArea. Sorry, I am not a keen in QML, just studying it.

    1 Reply Last reply
    0
    • Pradeep P NP Offline
      Pradeep P NP Offline
      Pradeep P N
      wrote on last edited by Pradeep P N
      #2

      Hi @Gourmet

      • Create a C++ class inheriting from QAbstractListModel
        Example : class MyModel : public QAbstractListModel
      • Write a Q_INVOKABLE void swapData(int _from, int _to = 0);
      • Use beginMoveRows() & endMoveRows() ( you can even use beginMoveColumns() & endMoveColumns() )
      void MyModel::swapData(int _from, int _to)
      {
          qDebug() << _from << _to << endl;
          if(_from != _to){
              beginMoveRows(QModelIndex(), _from, _from, QModelIndex(), _to);
              m_List.move(_from, _to);
              endMoveRows();
          }
      }
      
      • Expose it to QML
        MyModel *_model = new MyModel;
        context->setContextProperty("myModel", _model);
      • Use DropArea inside MouseArea {}
                      DropArea {
                          anchors { 
                              fill: parent; 
                              margins: 10 
                          }
      
                          onEntered: {
                              dragArea.fromVal = dragArea.DelegateModel.itemsIndex
                              dragArea.toVal = drag.source.DelegateModel.itemsIndex
      
                              visualModel.items.move(dragArea.fromVal, dragArea.toVal, 1)
                          }
      
      
      • MouseArea{} onRelease:
                                  truckModel.swapData(index, dragArea.toVal)
      

      Is this what you expect the Drag & Drop ?

      0_1560825097496_D_D.gif

      All the best

      Pradeep Nimbalkar.
      Upvote the answer(s) that helped you to solve the issue...
      Keep code clean.

      G 1 Reply Last reply
      3
      • Pradeep P NP Pradeep P N

        Hi @Gourmet

        • Create a C++ class inheriting from QAbstractListModel
          Example : class MyModel : public QAbstractListModel
        • Write a Q_INVOKABLE void swapData(int _from, int _to = 0);
        • Use beginMoveRows() & endMoveRows() ( you can even use beginMoveColumns() & endMoveColumns() )
        void MyModel::swapData(int _from, int _to)
        {
            qDebug() << _from << _to << endl;
            if(_from != _to){
                beginMoveRows(QModelIndex(), _from, _from, QModelIndex(), _to);
                m_List.move(_from, _to);
                endMoveRows();
            }
        }
        
        • Expose it to QML
          MyModel *_model = new MyModel;
          context->setContextProperty("myModel", _model);
        • Use DropArea inside MouseArea {}
                        DropArea {
                            anchors { 
                                fill: parent; 
                                margins: 10 
                            }
        
                            onEntered: {
                                dragArea.fromVal = dragArea.DelegateModel.itemsIndex
                                dragArea.toVal = drag.source.DelegateModel.itemsIndex
        
                                visualModel.items.move(dragArea.fromVal, dragArea.toVal, 1)
                            }
        
        
        • MouseArea{} onRelease:
                                    truckModel.swapData(index, dragArea.toVal)
        

        Is this what you expect the Drag & Drop ?

        0_1560825097496_D_D.gif

        All the best

        G Offline
        G Offline
        Gourmet
        wrote on last edited by
        #3

        @Pradeep-P-N thanx but this is not fully applicable... I have already working model/view based on QList<myclass*> and QVariant. I just have not time to redesign it for QAbstractListModel, implement roles and so on. I can send signal from inside view to model with indexes of elements to be swapped. After swap I will just reset setContextProperty(). The only thing what I need is - proper place for this signal. Your example gives me idea for this.

        1 Reply Last reply
        0
        • G Offline
          G Offline
          Gourmet
          wrote on last edited by Gourmet
          #4

          I am weird... What is DelegateModel in case when model is written on C++? Here:

          onEntered: {
                  dragArea.fromVal = dragArea.DelegateModel.itemsIndex
                  dragArea.toVal = drag.source.DelegateModel.itemsIndex
          
                  visualModel.items.move(dragArea.fromVal, dragArea.toVal, 1)
          }
          

          I know only model name in ListView but I do not have DelegateModel defined in QML.

          1 Reply Last reply
          0
          • G Offline
            G Offline
            Gourmet
            wrote on last edited by Gourmet
            #5

            First I want add scrolling while drag. This is not shown in any example. I have following code.

            Item {
                id: root
                visible: true
            
                ListView {
                    id: lView
                    orientation: ListView.Vertical
                    model: visualModel
                    anchors.fill: parent
            
                    DelegateModel {
                        id: visualModel
                        model: itemsList
            
                        delegate: Rectangle {
                            id: listItem
                            color: tapArea.held ? "lightsteelblue" : modelData.Bought ? "darkgrey" : "yellow"
                            Behavior on color { ColorAnimation { duration: 100 } }
            
                            width: lView.width
                            height: 200
                            border.width: 1
                            border.color: "blue"
            
                            states: State {
                                when: tapArea.held
            
                                ParentChange { target: listItem; parent: root }
                                AnchorChanges {
                                    target: listItem
                                    anchors { horizontalCenter: undefined; verticalCenter: undefined }
                                }
                            }
            
                            Drag.active: tapArea.held
                            Drag.source: tapArea
                            Drag.hotSpot.x: width / 2
                            Drag.hotSpot.y: height / 2
                            
                            MouseArea {
                                id: tapArea
                                z: -1
                                anchors.fill: parent
                                onDoubleClicked: { modelData.EditedIndex = index }
            
                                property bool held: false
                                property int lasty: 0
                                property bool moveUp: false
                                onYChanged: { moveUp = lasty > y; lasty = y }
            
                                drag.target: held ? listItem : undefined
                                drag.axis: Drag.YAxis
            
                                onPressAndHold: held = true
                                onReleased: held = false
                                DropArea {
                                    anchors { fill: parent; margins: 10 }
            
                                    onEntered: {
                                        console.log("entered drop area")
                                        visualModel.items.move(
                                                   drag.source.DelegateModel.itemsIndex,
                                                   tapArea.DelegateModel.itemsIndex )
                                        lView.currentIndex = tapArea.DelegateModel.itemsIndex + (tapArea.moveUp ? -1 : 1)
                                    }
                                }
                            }
                        }
                    }
                    ScrollBar.vertical: ScrollBar{
                        active: true
                        width: 20
                    }
                }
            }
            

            It almost woks - item is long selected by long tap and can be dragged but only inside window. Drag does not scroll when list is long. But I need it scrolling therefore tried implement this. I get console error

            :-1 ((null)): <Unknown File>: QML DelegateModelGroup: move: invalid from index
            

            each time when select item by long tap. Message "entered drop area" does not ever appear.

            1 Reply Last reply
            0
            • G Offline
              G Offline
              Gourmet
              wrote on last edited by Gourmet
              #6

              Things become more strange... I have added

                              DropArea {
                                  id: dropper
              

              and made this :

                          delegate: Rectangle {
                              border.color: dropper.containsDrag ? "red" : "blue"
              

              As it's written in docs: "A DropArea is an invisible item which receives events when other items are dragged over it.". Over - means the containsDrag must become true when there is drug over this item. That means border of item under drugged must become red. But... border color becomes red around the dragged item - not under drugged. What the heck is going on? :-\

              The same happiness when I change bool toggle inside onEntered handler and change border color depending from this toggle. It changes color of dragged item but not under drugged.

                              DropArea {
                                          console.warn(itemName.text)
              

              Shows text of the dragged item - instead of item text under dragged.
              I am finally weird. HELP!!!

              ...
              No anything method works in Android.

              1 Reply Last reply
              0

              • Login

              • Login or register to search.
              • First post
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved