December 14, 2010

kyleplattner kyleplattner
Lab Rat
242 posts

[Solved] QML and Drag and Drop

Page  
1

So i am trying to find a way to rearrange dashboard widgets for my application. I would to give the user the ability to do this by just pressing and holding on a widget and then dragging it to a new location. Here is an implementation that allows the user to tap the widget he or she wants to change and then move it by tapping a new location. It works but I would love to add drag and drop to this. Please let me know if you have any ideas:

  1. import Qt 4.7
  2.  
  3. Rectangle {
  4.     width: 640
  5.     height: 480
  6.     color: "#111111"
  7.  
  8.  
  9.     Component {
  10.  
  11.  
  12.         id: widgetdelegate
  13.         Item {
  14.             width: grid.cellWidth; height: grid.cellHeight
  15.             Image {
  16.                 source: portrait;
  17.                 anchors.horizontalCenter: parent.horizontalCenter
  18.                 width: grid.cellWidth; height: grid.cellHeight
  19.                 fillMode: Image.PreserveAspectFit
  20.             }
  21.  
  22.  
  23.             MouseArea {
  24.                 id: mouse_area1
  25.                 anchors.fill: parent
  26.                 onClicked: {
  27.                     if (grid.firstIndexDrag== 0) {
  28.                         grid.firstIndexDrag=index
  29.                     }else {
  30.                         widgetmodel.move(grid.firstIndexDrag,index,1)
  31.                         grid.firstIndexDrag= 0
  32.                     }
  33.  
  34.                 }
  35.             }
  36.         }
  37.     }
  38.  
  39.  
  40.     ListModel {
  41.         id: widgetmodel
  42.         ListElement {
  43.             portrait: "Images/widget1.png"
  44.         }
  45.         ListElement {
  46.             portrait: "Images/widget2.png"
  47.         }
  48.         ListElement {
  49.             portrait: "Images/widget3.png"
  50.         }
  51.         ListElement {
  52.              portrait: "Images/widget4.png"
  53.         }
  54.         ListElement {
  55.              portrait: "Images/widget5.png"
  56.         }
  57.         ListElement {
  58.              portrait: "Images/widget6.png"
  59.         }
  60.         ListElement {
  61.              portrait: "Images/widget7.png"
  62.         }
  63.         ListElement {
  64.              portrait: "Images/widget8.png"
  65.         }
  66.         ListElement {
  67.              portrait: "Images/widget9.png"
  68.         }
  69.  
  70.  
  71.  
  72.     }
  73.  
  74.     GridView {
  75.  
  76.         property int firstIndexDrag: 0
  77.  
  78.         id: grid
  79.         x: 0
  80.         y: 0
  81.         anchors.rightMargin: 200
  82.         anchors.bottomMargin: 100
  83.         anchors.leftMargin: 200
  84.         anchors.topMargin: 100
  85.         width: 640
  86.         height: 480
  87.         anchors.fill: parent
  88.         cellWidth: 80; cellHeight: 80
  89.         flow: GridView.LeftToRight
  90.  
  91.  
  92.         model: widgetmodel
  93.         delegate: widgetdelegate
  94.         //highlight: Rectangle { color: "white"; radius: 5 ; z: 1 }
  95.         focus: true
  96.  
  97.  
  98.     }
  99.  
  100.  
  101. }

41 replies

December 14, 2010

anselmolsm anselmolsm
Ant Farmer
417 posts

kyleplattner, is it related with this other thread [developer.qt.nokia.com]?

 Signature 

Anselmo L. S. Melo (anselmolsm)
www.anselmolsm.org

December 14, 2010

kyleplattner kyleplattner
Lab Rat
242 posts

Same in concept, different in approach to implementation.

December 15, 2010

xsacha xsacha
Lab Rat
517 posts

Hey Kyle. Here is drag and drop for you:

For the sake of brevity, I have separated your widgetmodel in to another QML file which I won’t repeat in future.

WidgetModel.qml

  1. import QtQuick 1.0
  2.  
  3. ListModel {
  4.     ListElement {
  5.         portrait: "Images/widget1.png"
  6.     }
  7.     ListElement {
  8.         portrait: "Images/widget2.png"
  9.     }
  10.     ListElement {
  11.         portrait: "Images/widget3.png"
  12.     }
  13.     ListElement {
  14.         portrait: "Images/widget4.png"
  15.     }
  16.     ListElement {
  17.         portrait: "Images/widget5.png"
  18.     }
  19.     ListElement {
  20.         portrait: "Images/widget6.png"
  21.     }
  22.     ListElement {
  23.         portrait: "Images/widget7.png"
  24.     }
  25.     ListElement {
  26.         portrait: "Images/widget8.png"
  27.     }
  28.     ListElement {
  29.         portrait: "Images/widget9.png"
  30.     }
  31. }

Main.qml

  1. import QtQuick 1.0
  2.  
  3. Rectangle {
  4.     width: 640
  5.     height: 480
  6.     color: "#111111"
  7.     Component {
  8.         id: widgetdelegate
  9.         Item {
  10.             width: grid.cellWidth; height: grid.cellHeight
  11.             Image {
  12.                 source: portrait;
  13.                 anchors.horizontalCenter: parent.horizontalCenter
  14.                 width: grid.cellWidth; height: grid.cellHeight
  15.                 fillMode: Image.PreserveAspectFit
  16.             }
  17.             Rectangle {
  18.                 width: parent.width; height: parent.height; radius: 5
  19.                 border.color: "white"; color: "transparent"; border.width: 6;
  20.                 visible: index == grid.firstIndexDrag
  21.             }
  22.         }
  23.     }
  24.  
  25.     GridView {
  26.         property int firstIndexDrag: -1
  27.  
  28.         id: grid
  29.         x: 0; y: 0
  30.         interactive: false
  31.  
  32.         anchors.rightMargin: 200
  33.         anchors.bottomMargin: 100
  34.         anchors.leftMargin: 200
  35.         anchors.topMargin: 100
  36.         width: 640
  37.         height: 480
  38.         anchors.fill: parent
  39.         cellWidth: 80; cellHeight: 80
  40.  
  41.         model: WidgetModel { id: widgetmodel }
  42.         delegate: widgetdelegate
  43.         MouseArea {
  44.             anchors.fill: parent
  45.             onReleased: {
  46.                 if (grid.firstIndexDrag != -1)
  47.                     widgetmodel.move(grid.firstIndexDrag,grid.indexAt(mouseX, mouseY),1)
  48.                 grid.firstIndexDrag = -1
  49.             }
  50.             onPressed: grid.firstIndexDrag=grid.indexAt(mouseX, mouseY)
  51.         }
  52.     }
  53. }

You probably want to animate the drag and drop too. But didn’t we cover this in the other topic? Please tell me if you desire code for this too.

 Signature 

- Sacha

December 15, 2010

kyleplattner kyleplattner
Lab Rat
242 posts

I am interested in the code for animating it, thanks. I will try this tonight.

I really appreciate your help.

Kyle

December 15, 2010

xsacha xsacha
Lab Rat
517 posts

Ok, I used re-parenting to animate the drag:

  1. import QtQuick 1.0
  2.  
  3. Rectangle {
  4.     width: 640
  5.     height: 480
  6.     color: "#222222"
  7.     Component {
  8.         id: widgetdelegate
  9.         Item {
  10.             width: grid.cellWidth; height: grid.cellHeight
  11.             Image {
  12.                 id: im
  13.                 source: portrait;
  14.                 anchors.centerIn: parent
  15.                 width: grid.cellWidth - 10; height: grid.cellHeight - 10
  16.                 smooth: true
  17.                 fillMode: Image.PreserveAspectFit
  18.                 Rectangle {
  19.                     id: imRect
  20.                     anchors.fill: parent; radius: 5
  21.                     anchors.centerIn: parent
  22.                     border.color: "#326487"; color: "transparent"; border.width: 6;
  23.                     opacity: 0
  24.                 }
  25.             }
  26.             Rectangle {
  27.                 id: iWasHere
  28.                 width: 20; height: 20; radius: 20
  29.                 smooth: true
  30.                 anchors.centerIn: parent
  31.                 color: "white";
  32.                 opacity: 0
  33.             }
  34.             states: [
  35.                 State {
  36.                     name: "inDrag"
  37.                     when: index == grid.firstIndexDrag
  38.                     PropertyChanges { target: iWasHere; opacity: 1 }
  39.                     PropertyChanges { target: imRect; opacity: 1 }
  40.                     PropertyChanges { target: im; parent: container }
  41.                     PropertyChanges { target: im; width: (grid.cellWidth - 10) / 2 }
  42.                     PropertyChanges { target: im; height: (grid.cellHeight - 10) / 2 }
  43.                     PropertyChanges { target: im; anchors.centerIn: undefined }
  44.                     PropertyChanges { target: im; x: coords.mouseX - im.width/2 }
  45.                     PropertyChanges { target: im; y: coords.mouseY - im.height/2 }
  46.                 }
  47.             ]
  48.             transitions: [
  49.                 Transition { NumberAnimation { properties: "width, height, opacity"; duration: 300; easing.type: Easing.InOutQuad } }
  50.             ]
  51.         }
  52.     }
  53.  
  54.     GridView {
  55.         property int firstIndexDrag: -1
  56.  
  57.         id: grid
  58.         x: 0; y: 0
  59.         interactive: false
  60.  
  61.         anchors.rightMargin: 200
  62.         anchors.bottomMargin: 100
  63.         anchors.leftMargin: 200
  64.         anchors.topMargin: 100
  65.         anchors.fill: parent
  66.         cellWidth: 80; cellHeight: 80;
  67.  
  68.         model: WidgetModel { id: widgetmodel }
  69.         delegate: widgetdelegate
  70.         Item {
  71.             id: container
  72.             anchors.fill: parent
  73.         }
  74.         MouseArea {
  75.             id: coords
  76.             anchors.fill: parent
  77.  
  78.             onReleased: {
  79.                 if (grid.firstIndexDrag != -1)
  80.                     widgetmodel.move(grid.firstIndexDrag,grid.indexAt(mouseX, mouseY),1)
  81.                 grid.firstIndexDrag = -1
  82.             }
  83.             onPressed: {
  84.                 grid.firstIndexDrag=grid.indexAt(mouseX, mouseY)
  85.             }
  86.         }
  87.     }
  88. }

Enjoy!

 Signature 

- Sacha

December 15, 2010

kyleplattner kyleplattner
Lab Rat
242 posts

Amazing, very well done. Thanks so much! I owe you a lot.

December 15, 2010

xsacha xsacha
Lab Rat
517 posts

Oh by the way, I read your previous thread and remembered you wanted to have that ‘squiggle’ thing when the user does an ‘onPressAndHold’ — like iOS does. So, I did this too. I hope you learn from the code:

Main.qml

  1. import QtQuick 1.0
  2.  
  3. Rectangle {
  4.     width: 640
  5.     height: 480
  6.     color: "#222222"
  7.     Component {
  8.         id: widgetdelegate
  9.         Item {
  10.             width: grid.cellWidth; height: grid.cellHeight
  11.             Image {
  12.                 id: im
  13.                 state: "inactive"
  14.                 source: portrait;
  15.                 anchors.centerIn: parent
  16.                 width: grid.cellWidth - 10; height: grid.cellHeight - 10
  17.                 smooth: true
  18.                 fillMode: Image.PreserveAspectFit
  19.                 SequentialAnimation on rotation {
  20.                     NumberAnimation { to:  20; duration: 200 }
  21.                     NumberAnimation { to: -20; duration: 400 }
  22.                     NumberAnimation { to:   0; duration: 200 }
  23.                     running: im.state == "squiggle"
  24.                     loops: Animation.Infinite
  25.                 }
  26.                 Rectangle {
  27.                     id: imRect
  28.                     anchors.fill: parent; radius: 5
  29.                     anchors.centerIn: parent
  30.                     border.color: "#326487"; color: "transparent"; border.width: 6;
  31.                     opacity: 0
  32.                 }
  33.                 states: [
  34.                     State {
  35.                         name: "squiggle";
  36.                         when: (grid.firstIndexDrag != -1) && (grid.firstIndexDrag != index)
  37.                     },
  38.                     State {
  39.                         name: "inactive";
  40.                         when: (grid.firstIndexDrag == -1) || (grid.firstIndexDrag == index)
  41.                         PropertyChanges { target: im; rotation: 0}
  42.                     }
  43.                 ]
  44.             }
  45.             Rectangle {
  46.                 id: iWasHere
  47.                 width: 20; height: 20; radius: 20
  48.                 smooth: true
  49.                 anchors.centerIn: parent
  50.                 color: "white";
  51.                 opacity: 0
  52.             }
  53.             states: [
  54.                 State {
  55.                     name: "inDrag"
  56.                     when: index == grid.firstIndexDrag
  57.                     PropertyChanges { target: iWasHere; opacity: 1 }
  58.                     PropertyChanges { target: imRect; opacity: 1 }
  59.                     PropertyChanges { target: im; parent: container }
  60.                     PropertyChanges { target: im; width: (grid.cellWidth - 10) / 2 }
  61.                     PropertyChanges { target: im; height: (grid.cellHeight - 10) / 2 }
  62.                     PropertyChanges { target: im; anchors.centerIn: undefined }
  63.                     PropertyChanges { target: im; x: coords.mouseX - im.width/2 }
  64.                     PropertyChanges { target: im; y: coords.mouseY - im.height/2 }
  65.                 }
  66.             ]
  67.             transitions: [
  68.                 Transition { NumberAnimation { properties: "width, height, opacity"; duration: 300; easing.type: Easing.InOutQuad } }
  69.             ]
  70.         }
  71.     }
  72.  
  73.     GridView {
  74.         property int firstIndexDrag: -1
  75.  
  76.         id: grid
  77.         x: 0; y: 0
  78.         interactive: false
  79.  
  80.         anchors.rightMargin: 200
  81.         anchors.bottomMargin: 100
  82.         anchors.leftMargin: 200
  83.         anchors.topMargin: 100
  84.         anchors.fill: parent
  85.         cellWidth: 80; cellHeight: 80;
  86.  
  87.         model: WidgetModel { id: widgetmodel }
  88.         delegate: widgetdelegate
  89.         Item {
  90.             id: container
  91.             anchors.fill: parent
  92.         }
  93.         MouseArea {
  94.             id: coords
  95.             anchors.fill: parent
  96.             onReleased: {
  97.                 if (grid.firstIndexDrag != -1)
  98.                     widgetmodel.move(grid.firstIndexDrag,grid.indexAt(mouseX, mouseY),1)
  99.                 grid.firstIndexDrag = -1
  100.             }
  101.             onPressAndHold: {
  102.                 grid.firstIndexDrag=grid.indexAt(mouseX, mouseY)
  103.             }
  104.         }
  105.     }
  106. }

This has been made in to a Wiki Entry:
http://developer.qt.nokia.com/wiki/Drag_and_Drop_within_a_GridView

 Signature 

- Sacha

December 16, 2010

Deqing Deqing
Lab Rat
29 posts

Hi Sacha,

The demo of drag and drop code looks cool!

I tried the code(Main2.qml) of second version in the wiki but failed with following error: “ReferenceError: Can’t find variable: gridId”

Is there anything I can do to avoid this?

THanks

December 16, 2010

xsacha xsacha
Lab Rat
517 posts

Well, I didn’t write the second version.
I tested my (first) version and provided a video of that exact code running.

I can review the second one for you to discover the issue. It seems a lot better than my version so I hope you can get it working.

 Signature 

- Sacha

December 16, 2010

Bradley Bradley
Lab Rat
314 posts

I’m responsible for the second version. I tried to update this post with a “I’ve updated the above wiki entry with an alternate implementation.” like I did on the other thread [developer.qt.nokia.com], but it failed for some reason.

As it turns out, the second version requires Qt 4.7.1. It gives that error with Qt 4.7.0. Thanks for pointing it out. I wasn’t aware of this difference in the versions. I’ve updated the wiki page with a notation about this.

 Signature 

Nokia Certified Qt Specialist.

December 16, 2010

xsacha xsacha
Lab Rat
517 posts

By the way, I just fixed in a critical issue with the first version in the wiki. It was to do with the moving code.
Problem was discovered and fixed in this thread: http://developer.qt.nokia.com/forums/viewthread/2460/P15/

The issue was that if you dragged items forward, the call to ‘move’ would push the indices backwards (to -1, -2 and so on) and you would no longer be able to drag the ‘-1’ index and other items would become out of whack.

It is most noticeable on a 3-item grid.

I think the issue is only if you don’t have animation onMove. The second example uses such an animation so it should be fine. Just be careful if you disable the animation.

 Signature 

- Sacha

December 17, 2010

Deqing Deqing
Lab Rat
29 posts

Thank you guys. I finally get the second version working.

It turns out that “gridId” should be defined in WidgetModel.qml, and it must starts with 0. For example:

  1.     ListElement { icon: "images/widget1.png"; gridId: 0 }
  2.     ListElement { icon: "images/widget2.png"; gridId: 1 }
  3.     ListElement { icon: "images/widget3.png"; gridId: 2 }

December 17, 2010

kyleplattner kyleplattner
Lab Rat
242 posts

Still returning:

  1. file:///Users/kp/Desktop/Precision Work/Screen Design/FinalScreens/QMLFinal/Rearrange4.qml:22: TypeError: Result of expression 'grid.items' [undefined] is not an object.

December 17, 2010

xsacha xsacha
Lab Rat
517 posts

I have made a cleaner version of the ‘second version’
Halved the lines of code, removed quite a lot of redundancy and doesn’t require Qt4.7.1.
Personally, I think the grids list is a bit silly when it can be done with the model/delegate. So I have completely removed the IconItem and grids.
No loss of functionality.
I replaced the first version with it. You can check it out in the wiki.

 Signature 

- Sacha

December 17, 2010

Bradley Bradley
Lab Rat
314 posts

The grids list was made because when I added behaviors in the delegate, it wasn’t working. I don’t know why it wasn’t working for me, but what you’ve got is working. I’ll remove the second version now that it is obsolete.

 Signature 

Nokia Certified Qt Specialist.

Page  
1

  ‹‹ QML Simulator      Setting focus. Differences between focus and activeFocus properties. Help for the pratcial example ››

You must log in to post a reply. Not a member yet? Register here!