I'm trying to reorder Repeater items using drag & drop. I'm using a Repeater due to dynamically loaded data. Below is what I have so far using a simple sqlite database with sample data added. What I'm hoping for is to get the re-order set up so when the user releases the dragged object, the database is updated to reflect the new order. The "Change" button reorders the elements as I want, I just can't get it to work property with drag and drop.
The loadData() function simply creates the table and inserts sample data. The "Change" button won't be in the final code, I just wanted to use it to get the re-ordering code to work.
import QtQuick.Controls 2.2 as QC2
import QtQuick 2.10
import QtQuick.Window 2.10
import QtQuick.LocalStorage 2.0
Window {
id: root
width: 320
height: 480
property var db
property int rowHeight: 90
Component.onCompleted: {
db = LocalStorage.openDatabaseSync("test_db", "1.0", "Database", 100000)
loadData()
}
property var sql_data: []
function loadData() {
var sql, rs, len, i
db.transaction(function(tx) {
tx.executeSql("DROP TABLE IF EXISTS tbl")
tx.executeSql("CREATE TABLE IF NOT EXISTS tbl (
rowId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, listOrder INTEGER NOT NULL)")
len = 5
for (i = 0; i < len; i++)
tx.executeSql("INSERT INTO tbl (name, listOrder) VALUES ('Item " + i + "', " + i + ")")
rs = tx.executeSql("SELECT name, listOrder FROM tbl ORDER BY listOrder ASC")
})
len = rs.rows.length
sql_data = new Array(len)
for (i = 0; i < len; i++) {
sql_data[i] = {"name": "", "order": ""}
sql_data[i]["name"] = rs.rows.item(i).name
sql_data[i]["order"] = rs.rows.item(i).listOrder
}
repeater.model = sql_data
}
Flickable {
id: mainFlick
anchors.fill: parent
contentHeight: mainColumn.height
Column {
id: mainColumn
width: parent.width
QC2.Button {
text: "Change"
onClicked: {
var p1, p2
var d1 = new Date().getTime()
var movedEle = 3
var newPos = 1
var ele1 = repeater.itemAt(movedEle)
var ele2 = repeater.itemAt(newPos)
ele1.y = ele2.y
root.sql_data[movedEle]["order"] = root.sql_data[newPos]["order"]
if (newPos < movedEle) {
p1 = newPos
p2 = movedEle
} else {
p1 = movedEle
p2 = newPos
}
root.db.transaction(function(tx) {
var sql = "UPDATE tbl SET listOrder = " + root.sql_data[p1]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[p2]["order"])
for (var i = p1; i < p2; i++) {
if (i !== movedEle) {
repeater.itemAt(i).y = repeater.itemAt(i).y + rowHeight
root.sql_data[i]["order"] += 1
sql = "UPDATE tbl SET listOrder = " + root.sql_data[i]["order"] + " "
sql += "WHERE listOrder = " + (root.sql_data[i]["order"] - 1)
tx.executeSql(sql)
}
}
})
sql_data = sql_data.sort(function(a,b) {
return a["order"] - b["order"]
})
repeater.model = sql_data
var d2 = new Date().getTime()
console.debug("Seconds: " + (d2 - d1) / 1000)
}
}
Repeater {
id: repeater
width: parent.width
model: []
delegate: Column {
id: repeaterItem
width: parent.width - 20
height: rowHeight
anchors.horizontalCenter: parent.horizontalCenter
property int pos: index
DropArea {
id: dragTarget
width: parent.width
height: 20
Rectangle {
id: dropRect
anchors.fill: parent
color: "green"
states: [
State {
when: dragTarget.containsDrag
PropertyChanges {
target: dropRect
color: "red"
}
}
]
}
}
Row {
id: contentRow
width: parent.width
height: parent.height
z: 1
Column {
id: itemColumn
width: parent.width - 70
Text {
text: "Name: " + modelData["name"]
}
Text {
text: "Order: " + modelData["order"]
}
Text {
text: "Repeater Index: " + index
}
} // itemColumn
MouseArea {
id: dragArea
width: 40
height: itemColumn.height
drag.axis: Drag.YAxis
drag.target: dragRect
onReleased: parent = ((dragRect.Drag.target !== null) ? dragRect.Drag.target : root)
Rectangle {
Component.onCompleted: console.debug(dragRect.Drag.target)
id: dragRect
width: 40
height: itemColumn.height
color: "grey"
Drag.active: dragArea.drag.active
Drag.source: dragArea
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
states : State {
when: dragArea.drag.active
ParentChange { target: dragRect; parent: root }
AnchorChanges {
target: dragRect
anchors.verticalCenter: undefined
anchors.horizontalCenter: undefined
}
}
}
}
} // contentRow
Behavior on y {
PropertyAnimation {
duration: 200
easing.type: Easing.Linear
}
}
} // repeaterItem
} // repeater
} // mainColumn
} // mainFlick
} // id
Much of the drag and drop code came from the Qt Examples site.
Here's what my problems are:
- The MouseArea used for dragging the rectangles doesn't seem to change locations. Once a rectangle is actually moved, it stays in its new location but if I want to move it again, I have to click and drag where it was originally when the app loads.
- If I switch the target of the Drag Area from the child rectangle to the entire row (repeaterItem), everything moves properly but will no longer interact with the Drop areas.
- I think I can get the index of the new location after a row has been dragged simply by getting the y-value of the Drop Area. Would there be a better way to do this?
Since the "Change" button already re-orders the row elements, I only need help getting the rows to properly interact with the Drop Areas and then get the y-position of the Drop Area when the dragged item is released.