lumina/src/qml/presenter/DragHandle.qml

148 lines
5.1 KiB
QML

import QtQuick 2.13
import QtQuick.Layouts 1.15
import org.kde.kirigami 2.13 as Kirigami
Item {
id: root
/**
* @brief This property holds the delegate that will be dragged around.
*
* This item *must* be a child of the actual ListView's delegate.
*/
property Item listItem
/**
* @brief This property holds the ListView that the delegate belong to.
*/
property ListView listView
/**
* @brief This signal is emitted when the drag handle wants to move the item in the model.
*
* The following example does the move in the case a ListModel is used:
* @code{.qml}
* onMoveRequested: listModel.move(oldIndex, newIndex, 1)
* @endcode
* @param oldIndex the index the item is currently at
* @param newIndex the index we want to move the item to
*/
signal moveRequested(int oldIndex, int newIndex)
/**
* @brief This signal is emitted when the drag operation is complete and the item has been
* dropped in the new final position.
*/
signal dropped()
implicitWidth: Kirigami.Units.iconSizes.smallMedium
implicitHeight: implicitWidth
MouseArea {
id: mouseArea
anchors.fill: parent
drag {
target: listItem
axis: Drag.YAxis
minimumY: 0
}
cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor
Kirigami.Icon {
id: internal
source: "handle-sort"
property int startY
property int mouseDownY
property Item originalParent
opacity: mouseArea.pressed || (!Kirigami.Settings.tabletMode && listItem.hovered) ? 1 : 0.6
property int listItemLastY
property bool draggingUp
color: "white"
function arrangeItem() {
const newIndex = listView.indexAt(1, listView.contentItem.mapFromItem(mouseArea, 0, internal.mouseDownY).y);
if (newIndex > -1 && ((internal.draggingUp && newIndex < index) || (!internal.draggingUp && newIndex > index))) {
root.moveRequested(index, newIndex);
}
}
anchors.fill: parent
}
preventStealing: true
onPressed: mouse => {
internal.originalParent = listItem.parent;
listItem.parent = listView;
listItem.y = internal.originalParent.mapToItem(listItem.parent, listItem.x, listItem.y).y;
internal.originalParent.z = 99;
internal.startY = listItem.y;
internal.listItemLastY = listItem.y;
internal.mouseDownY = mouse.y;
// while dragging listItem's height could change
// we want a const maximumY during the dragging time
mouseArea.drag.maximumY = listView.height - listItem.height;
}
onPositionChanged: mouse => {
if (!pressed || listItem.y === internal.listItemLastY) {
return;
}
internal.draggingUp = listItem.y < internal.listItemLastY
internal.listItemLastY = listItem.y;
internal.arrangeItem();
// autoscroll when the dragging item reaches the listView's top/bottom boundary
scrollTimer.running = (listView.contentHeight > listView.height)
&& ( (listItem.y === 0 && !listView.atYBeginning) ||
(listItem.y === mouseArea.drag.maximumY && !listView.atYEnd) );
}
onReleased: mouse => {
listItem.y = internal.originalParent.mapFromItem(listItem, 0, 0).y;
listItem.parent = internal.originalParent;
dropAnimation.running = true;
scrollTimer.running = false;
root.dropped();
}
onCanceled: released()
SequentialAnimation {
id: dropAnimation
YAnimator {
target: listItem
from: listItem.y
to: 0
duration: Kirigami.Units.longDuration
easing.type: Easing.InOutQuad
}
PropertyAction {
target: listItem.parent
property: "z"
value: 0
}
}
Timer {
id: scrollTimer
interval: 50
repeat: true
onTriggered: {
if (internal.draggingUp) {
listView.contentY -= Kirigami.Units.gridUnit;
if (listView.atYBeginning) {
listView.positionViewAtBeginning();
stop();
}
} else {
listView.contentY += Kirigami.Units.gridUnit;
if (listView.atYEnd) {
listView.positionViewAtEnd();
stop();
}
}
internal.arrangeItem();
}
}
}
}