[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:
- import Qt 4.7
- Rectangle {
- width: 640
- height: 480
- color: "#111111"
- Component {
- id: widgetdelegate
- Item {
- width: grid.cellWidth; height: grid.cellHeight
- Image {
- source: portrait;
- anchors.horizontalCenter: parent.horizontalCenter
- width: grid.cellWidth; height: grid.cellHeight
- fillMode: Image.PreserveAspectFit
- }
- MouseArea {
- id: mouse_area1
- anchors.fill: parent
- onClicked: {
- if (grid.firstIndexDrag== 0) {
- grid.firstIndexDrag=index
- }else {
- widgetmodel.move(grid.firstIndexDrag,index,1)
- grid.firstIndexDrag= 0
- }
- }
- }
- }
- }
- ListModel {
- id: widgetmodel
- ListElement {
- portrait: "Images/widget1.png"
- }
- ListElement {
- portrait: "Images/widget2.png"
- }
- ListElement {
- portrait: "Images/widget3.png"
- }
- ListElement {
- portrait: "Images/widget4.png"
- }
- ListElement {
- portrait: "Images/widget5.png"
- }
- ListElement {
- portrait: "Images/widget6.png"
- }
- ListElement {
- portrait: "Images/widget7.png"
- }
- ListElement {
- portrait: "Images/widget8.png"
- }
- ListElement {
- portrait: "Images/widget9.png"
- }
- }
- GridView {
- property int firstIndexDrag: 0
- id: grid
- x: 0
- y: 0
- anchors.rightMargin: 200
- anchors.bottomMargin: 100
- anchors.leftMargin: 200
- anchors.topMargin: 100
- width: 640
- height: 480
- anchors.fill: parent
- cellWidth: 80; cellHeight: 80
- flow: GridView.LeftToRight
- model: widgetmodel
- delegate: widgetdelegate
- //highlight: Rectangle { color: "white"; radius: 5 ; z: 1 }
- focus: true
- }
- }
41 replies
kyleplattner, is it related with this other thread [developer.qt.nokia.com]?
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
- import QtQuick 1.0
- ListModel {
- ListElement {
- portrait: "Images/widget1.png"
- }
- ListElement {
- portrait: "Images/widget2.png"
- }
- ListElement {
- portrait: "Images/widget3.png"
- }
- ListElement {
- portrait: "Images/widget4.png"
- }
- ListElement {
- portrait: "Images/widget5.png"
- }
- ListElement {
- portrait: "Images/widget6.png"
- }
- ListElement {
- portrait: "Images/widget7.png"
- }
- ListElement {
- portrait: "Images/widget8.png"
- }
- ListElement {
- portrait: "Images/widget9.png"
- }
- }
Main.qml
- import QtQuick 1.0
- Rectangle {
- width: 640
- height: 480
- color: "#111111"
- Component {
- id: widgetdelegate
- Item {
- width: grid.cellWidth; height: grid.cellHeight
- Image {
- source: portrait;
- anchors.horizontalCenter: parent.horizontalCenter
- width: grid.cellWidth; height: grid.cellHeight
- fillMode: Image.PreserveAspectFit
- }
- Rectangle {
- width: parent.width; height: parent.height; radius: 5
- border.color: "white"; color: "transparent"; border.width: 6;
- visible: index == grid.firstIndexDrag
- }
- }
- }
- GridView {
- property int firstIndexDrag: -1
- id: grid
- x: 0; y: 0
- interactive: false
- anchors.rightMargin: 200
- anchors.bottomMargin: 100
- anchors.leftMargin: 200
- anchors.topMargin: 100
- width: 640
- height: 480
- anchors.fill: parent
- cellWidth: 80; cellHeight: 80
- model: WidgetModel { id: widgetmodel }
- delegate: widgetdelegate
- MouseArea {
- anchors.fill: parent
- onReleased: {
- if (grid.firstIndexDrag != -1)
- widgetmodel.move(grid.firstIndexDrag,grid.indexAt(mouseX, mouseY),1)
- grid.firstIndexDrag = -1
- }
- onPressed: grid.firstIndexDrag=grid.indexAt(mouseX, mouseY)
- }
- }
- }
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.
Ok, I used re-parenting to animate the drag:
- import QtQuick 1.0
- Rectangle {
- width: 640
- height: 480
- color: "#222222"
- Component {
- id: widgetdelegate
- Item {
- width: grid.cellWidth; height: grid.cellHeight
- Image {
- id: im
- source: portrait;
- anchors.centerIn: parent
- width: grid.cellWidth - 10; height: grid.cellHeight - 10
- smooth: true
- fillMode: Image.PreserveAspectFit
- Rectangle {
- id: imRect
- anchors.fill: parent; radius: 5
- anchors.centerIn: parent
- border.color: "#326487"; color: "transparent"; border.width: 6;
- opacity: 0
- }
- }
- Rectangle {
- id: iWasHere
- width: 20; height: 20; radius: 20
- smooth: true
- anchors.centerIn: parent
- color: "white";
- opacity: 0
- }
- states: [
- State {
- name: "inDrag"
- when: index == grid.firstIndexDrag
- PropertyChanges { target: iWasHere; opacity: 1 }
- PropertyChanges { target: imRect; opacity: 1 }
- PropertyChanges { target: im; parent: container }
- PropertyChanges { target: im; width: (grid.cellWidth - 10) / 2 }
- PropertyChanges { target: im; height: (grid.cellHeight - 10) / 2 }
- PropertyChanges { target: im; anchors.centerIn: undefined }
- PropertyChanges { target: im; x: coords.mouseX - im.width/2 }
- PropertyChanges { target: im; y: coords.mouseY - im.height/2 }
- }
- ]
- transitions: [
- Transition { NumberAnimation { properties: "width, height, opacity"; duration: 300; easing.type: Easing.InOutQuad } }
- ]
- }
- }
- GridView {
- property int firstIndexDrag: -1
- id: grid
- x: 0; y: 0
- interactive: false
- anchors.rightMargin: 200
- anchors.bottomMargin: 100
- anchors.leftMargin: 200
- anchors.topMargin: 100
- anchors.fill: parent
- cellWidth: 80; cellHeight: 80;
- model: WidgetModel { id: widgetmodel }
- delegate: widgetdelegate
- Item {
- id: container
- anchors.fill: parent
- }
- MouseArea {
- id: coords
- anchors.fill: parent
- onReleased: {
- if (grid.firstIndexDrag != -1)
- widgetmodel.move(grid.firstIndexDrag,grid.indexAt(mouseX, mouseY),1)
- grid.firstIndexDrag = -1
- }
- onPressed: {
- grid.firstIndexDrag=grid.indexAt(mouseX, mouseY)
- }
- }
- }
- }
Enjoy!
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
- import QtQuick 1.0
- Rectangle {
- width: 640
- height: 480
- color: "#222222"
- Component {
- id: widgetdelegate
- Item {
- width: grid.cellWidth; height: grid.cellHeight
- Image {
- id: im
- state: "inactive"
- source: portrait;
- anchors.centerIn: parent
- width: grid.cellWidth - 10; height: grid.cellHeight - 10
- smooth: true
- fillMode: Image.PreserveAspectFit
- SequentialAnimation on rotation {
- NumberAnimation { to: 20; duration: 200 }
- NumberAnimation { to: -20; duration: 400 }
- NumberAnimation { to: 0; duration: 200 }
- running: im.state == "squiggle"
- loops: Animation.Infinite
- }
- Rectangle {
- id: imRect
- anchors.fill: parent; radius: 5
- anchors.centerIn: parent
- border.color: "#326487"; color: "transparent"; border.width: 6;
- opacity: 0
- }
- states: [
- State {
- name: "squiggle";
- when: (grid.firstIndexDrag != -1) && (grid.firstIndexDrag != index)
- },
- State {
- name: "inactive";
- when: (grid.firstIndexDrag == -1) || (grid.firstIndexDrag == index)
- PropertyChanges { target: im; rotation: 0}
- }
- ]
- }
- Rectangle {
- id: iWasHere
- width: 20; height: 20; radius: 20
- smooth: true
- anchors.centerIn: parent
- color: "white";
- opacity: 0
- }
- states: [
- State {
- name: "inDrag"
- when: index == grid.firstIndexDrag
- PropertyChanges { target: iWasHere; opacity: 1 }
- PropertyChanges { target: imRect; opacity: 1 }
- PropertyChanges { target: im; parent: container }
- PropertyChanges { target: im; width: (grid.cellWidth - 10) / 2 }
- PropertyChanges { target: im; height: (grid.cellHeight - 10) / 2 }
- PropertyChanges { target: im; anchors.centerIn: undefined }
- PropertyChanges { target: im; x: coords.mouseX - im.width/2 }
- PropertyChanges { target: im; y: coords.mouseY - im.height/2 }
- }
- ]
- transitions: [
- Transition { NumberAnimation { properties: "width, height, opacity"; duration: 300; easing.type: Easing.InOutQuad } }
- ]
- }
- }
- GridView {
- property int firstIndexDrag: -1
- id: grid
- x: 0; y: 0
- interactive: false
- anchors.rightMargin: 200
- anchors.bottomMargin: 100
- anchors.leftMargin: 200
- anchors.topMargin: 100
- anchors.fill: parent
- cellWidth: 80; cellHeight: 80;
- model: WidgetModel { id: widgetmodel }
- delegate: widgetdelegate
- Item {
- id: container
- anchors.fill: parent
- }
- MouseArea {
- id: coords
- anchors.fill: parent
- onReleased: {
- if (grid.firstIndexDrag != -1)
- widgetmodel.move(grid.firstIndexDrag,grid.indexAt(mouseX, mouseY),1)
- grid.firstIndexDrag = -1
- }
- onPressAndHold: {
- grid.firstIndexDrag=grid.indexAt(mouseX, mouseY)
- }
- }
- }
- }
This has been made in to a Wiki Entry:
http://developer.qt.nokia.com/wiki/Drag_and_Drop_within_a_GridView
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.
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.
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:
- ListElement { icon: "images/widget1.png"; gridId: 0 }
- ListElement { icon: "images/widget2.png"; gridId: 1 }
- ListElement { icon: "images/widget3.png"; gridId: 2 }
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.
You must log in to post a reply. Not a member yet? Register here!






