The stock QML ListView is pretty handy for displaying simple data structures. While porting To-Do-O to Qt/QML (MeeGo/Harmattan to be more precise) I stumbled across a use case where I want to move / reorder items in a list. In my opinion the most natural way to do this on a touch UI device is to use some drag-and-drop gesture like handling for moving items. In this post I’ll describe how I implemented a simple solution for moving items in a QML ListView via touch interaction.
The idea behind the implementation is that the user selects the item to be moved, keeps the item pressed and uses a second finger to move the item in the list. Fortunately, QML provides the PinchArea which is designed to handle this kind of two finger gesture. Using a ListView along with a PinchArea the implementation part is pretty straight forward. The following listing shows the code:
import QtQuick 1.1 import com.meego 1.0 PageStackWindow { id: appWindow initialPage: mainPage ListModel{ id: myModel ListElement{ number: "1" } ListElement{ number: "2" } ListElement{ number: "3" } ListElement{ number: "4" } ListElement{ number: "5" } ListElement{ number: "6" } ListElement{ number: "7" } ListElement{ number: "8" } } Page{ id: mainPage Rectangle{ id: rect anchors.fill: parent PinchArea{ id: dndArea anchors.fill: parent property real lastPosition: 0 property real moveDelta: 40 onPinchStarted: lastPosition = pinch.startPoint2.y onPinchUpdated: { var currentPosition = pinch.point2.y if(currentPosition === pinch.point1.y) return if(lastPosition - currentPosition > moveDelta){ lastPosition = currentPosition moveItem(myList.currentIndex - 1) }else if (lastPosition - currentPosition < -moveDelta){ lastPosition = currentPosition moveItem(myList.currentIndex + 1) } } function moveItem(targetIndex){ if(targetIndex >= 0 && targetIndex < myModel.count){ myModel.move(myList.currentIndex, targetIndex, 1) } } } ListView{ id: myList anchors.fill: parent model: myModel delegate: Text{ width: parent.width text: number font.pointSize: 40 horizontalAlignment: Text.AlignHCenter MouseArea{ anchors.fill: parent onClicked: myList.currentIndex = index } } highlight: Rectangle{ color: "lightblue" } } } } }
If you want to use this code with a custom C++/Qt model based e.g. on QAbstractItemModel you need to add an according “move” method to your model which takes care of moving the item and updating the ListView. Note that this method must be callable from QML. Hence it has to be either a slot or use the Q_INVOKABLE macro.
Additionally, you need to add a “count” property to your model. This can be done by adding “Q_PROPERTY(int count READ rowCount)” to the class definition of your model (before the “public:” part). You should have implemented a “rowCount” method anyhow when inheriting from QAbstractItemModel.
You can download a runnable version of this example for MeeGo/Harmattan via this link.
This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.