Warning, /frameworks/kirigami/src/controls/ListItemDragHandle.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  *  SPDX-FileCopyrightText: 2018 by Marco Martin <mart@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 import QtQuick 2.6
0008 import org.kde.kirigami 2.4 as Kirigami
0009 
0010 /**
0011  * Implements a drag handle supposed to be in items in ListViews to reorder items.
0012  * 
0013  * The QtQuick.ListView must use a model that supports item reordering, such as
0014  * QtQml.Models.ListModel.move or QAbstractItemModel instances
0015  * with QAbstractItemModel::moveRows correctly implemented.
0016  * In order for ListItemDragHandle to work correctly, the list item that is being dragged
0017  * should not directly be the delegate of the ListView, but a child of it.
0018  *
0019  * Example usage:
0020  * @include listitemdraghandle.qml
0021  *
0022  * As seen from the example, we wrapped the AbstractListItem with an
0023  * Item component. This is because when dragging the list item around, only the item that
0024  * the drag handle is assigned to is moved, and the wrapper Item stays there for
0025  * it to take up space so that other list items don't take it.
0026  *
0027  * @since org.kde.kirigami 2.5
0028  * @inherit QtQuick.Item
0029  */
0030 Item {
0031     id: root
0032 
0033     /**
0034      * @brief This property holds the delegate that will be dragged around.
0035      *
0036      * This item *must* be a child of the actual ListView's delegate.
0037      */
0038     property Item listItem
0039 
0040     /**
0041      * @brief This property holds the ListView that the delegate belong to.
0042      */
0043     property ListView listView
0044 
0045     /**
0046      * @brief This signal is emitted when the drag handle wants to move the item in the model.
0047      *
0048      * The following example does the move in the case a ListModel is used:
0049      * @code{.qml}
0050      *  onMoveRequested: listModel.move(oldIndex, newIndex, 1)
0051      * @endcode
0052      * @param oldIndex the index the item is currently at
0053      * @param newIndex the index we want to move the item to
0054      */
0055     signal moveRequested(int oldIndex, int newIndex)
0056 
0057     /**
0058      * @brief This signal is emitted when the drag operation is complete and the item has been
0059      * dropped in the new final position.
0060      */
0061     signal dropped()
0062 
0063     implicitWidth: Kirigami.Units.iconSizes.smallMedium
0064     implicitHeight: implicitWidth
0065 
0066     MouseArea {
0067         id: mouseArea
0068         anchors.fill: parent
0069         drag {
0070             target: listItem
0071             axis: Drag.YAxis
0072             minimumY: 0
0073         }
0074         cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor
0075 
0076         Kirigami.Icon {
0077             id: internal
0078             source: "handle-sort"
0079             property int startY
0080             property int mouseDownY
0081             property Item originalParent
0082             opacity: mouseArea.pressed || (!Kirigami.Settings.tabletMode && listItem.hovered) ? 1 : 0.6
0083             property int listItemLastY
0084             property bool draggingUp
0085 
0086             function arrangeItem() {
0087                 const newIndex = listView.indexAt(1, listView.contentItem.mapFromItem(mouseArea, 0, internal.mouseDownY).y);
0088 
0089                 if (newIndex > -1 && ((internal.draggingUp && newIndex < index) || (!internal.draggingUp && newIndex > index))) {
0090                     root.moveRequested(index, newIndex);
0091                 }
0092             }
0093 
0094             anchors.fill: parent
0095         }
0096         preventStealing: true
0097 
0098 
0099         onPressed: mouse => {
0100             internal.originalParent = listItem.parent;
0101             listItem.parent = listView;
0102             listItem.y = internal.originalParent.mapToItem(listItem.parent, listItem.x, listItem.y).y;
0103             internal.originalParent.z = 99;
0104             internal.startY = listItem.y;
0105             internal.listItemLastY = listItem.y;
0106             internal.mouseDownY = mouse.y;
0107             // while dragging listItem's height could change
0108             // we want a const maximumY during the dragging time
0109             mouseArea.drag.maximumY = listView.height - listItem.height;
0110         }
0111 
0112         onPositionChanged: mouse => {
0113             if (!pressed || listItem.y === internal.listItemLastY) {
0114                 return;
0115             }
0116 
0117             internal.draggingUp = listItem.y < internal.listItemLastY
0118             internal.listItemLastY = listItem.y;
0119 
0120             internal.arrangeItem();
0121 
0122              // autoscroll when the dragging item reaches the listView's top/bottom boundary
0123             scrollTimer.running = (listView.contentHeight > listView.height)
0124                                && ( (listItem.y === 0 && !listView.atYBeginning) ||
0125                                     (listItem.y === mouseArea.drag.maximumY && !listView.atYEnd) );
0126         }
0127         onReleased: mouse => {
0128             listItem.y = internal.originalParent.mapFromItem(listItem, 0, 0).y;
0129             listItem.parent = internal.originalParent;
0130             dropAnimation.running = true;
0131             scrollTimer.running = false;
0132             root.dropped();
0133         }
0134         onCanceled: released()
0135         SequentialAnimation {
0136             id: dropAnimation
0137             YAnimator {
0138                 target: listItem
0139                 from: listItem.y
0140                 to: 0
0141                 duration: Kirigami.Units.longDuration
0142                 easing.type: Easing.InOutQuad
0143             }
0144             PropertyAction {
0145                 target: listItem.parent
0146                 property: "z"
0147                 value: 0
0148             }
0149         }
0150         Timer {
0151             id: scrollTimer
0152             interval: 50
0153             repeat: true
0154             onTriggered: {
0155                 if (internal.draggingUp) {
0156                     listView.contentY -= Kirigami.Units.gridUnit;
0157                     if (listView.atYBeginning) {
0158                         listView.positionViewAtBeginning();
0159                         stop();
0160                     }
0161                 } else {
0162                     listView.contentY += Kirigami.Units.gridUnit;
0163                     if (listView.atYEnd) {
0164                         listView.positionViewAtEnd();
0165                         stop();
0166                     }
0167                 }
0168                 internal.arrangeItem();
0169             }
0170         }
0171     }
0172 }
0173