Kirigami2

ListItemDragHandle.qml
1/*
2 * SPDX-FileCopyrightText: 2018 by Marco Martin <mart@kde.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7import QtQuick
8import org.kde.kirigami 2 as Kirigami
9
10/**
11 * Implements a drag handle supposed to be in items in ListViews to reorder items
12 * The ListView must visualize a model which supports item reordering,
13 * such as ListModel.move() or QAbstractItemModel instances with moveRows() correctly implemented.
14 * In order for ListItemDragHandle to work correctly, the listItem that is being dragged
15 * should not directly be the delegate of the ListView, but a child of it.
16 *
17 * It is recommended to use DelagateRecycler as base delegate like the following code:
18 * @code
19 * import QtQuick 2.15
20 * import QtQuick.Layouts 1.15
21 * import QtQuick.Controls 2.15 as QQC2
22 * import org.kde.kirigami 2.19 as Kirigami
23 * ...
24 * Component {
25 * id: delegateComponent
26 * QQC2.ItemDelegate {
27 * id: listItem
28 * contentItem: RowLayout {
29 * Kirigami.ListItemDragHandle {
30 * listItem: listItem
31 * listView: mainList
32 * onMoveRequested: (oldIndex, newIndex) => {
33 * listModel.move(oldIndex, newIndex, 1);
34 * }
35 * }
36 * QQC2.Label {
37 * text: model.label
38 * }
39 * }
40 * }
41 * }
42 * ListView {
43 * id: mainList
44 *
45 * model: ListModel {
46 * id: listModel
47 * ListElement {
48 * label: "Item 1"
49 * }
50 * ListElement {
51 * label: "Item 2"
52 * }
53 * ListElement {
54 * label: "Item 3"
55 * }
56 * }
57 * //this is optional to make list items animated when reordered
58 * moveDisplaced: Transition {
59 * YAnimator {
60 * duration: Kirigami.Units.longDuration
61 * easing.type: Easing.InOutQuad
62 * }
63 * }
64 * delegate: Loader {
65 * width: mainList.width
66 * sourceComponent: delegateComponent
67 * }
68 * }
69 * ...
70 * @endcode
71 *
72 * @since 2.5
73 * @inherit QtQuick.Item
74 */
75Item {
76 id: root
77
78 /**
79 * @brief This property holds the delegate that will be dragged around.
80 *
81 * This item *must* be a child of the actual ListView's delegate.
82 */
83 property Item listItem
85 /**
86 * @brief This property holds the ListView that the delegate belong to.
87 */
88 property ListView listView
89
90 /**
91 * @brief This signal is emitted when the drag handle wants to move the item in the model.
92 *
93 * The following example does the move in the case a ListModel is used:
94 * @code
95 * onMoveRequested: (oldIndex, newIndex) => {
96 * listModel.move(oldIndex, newIndex, 1);
97 * }
98 * @endcode
99 * @param oldIndex the index the item is currently at
100 * @param newIndex the index we want to move the item to
101 */
102 signal moveRequested(int oldIndex, int newIndex)
103
104 /**
105 * @brief This signal is emitted when the drag operation is complete and the item has been
106 * dropped in the new final position.
107 */
108 signal dropped()
109
110 implicitWidth: Kirigami.Units.iconSizes.smallMedium
111 implicitHeight: Kirigami.Units.iconSizes.smallMedium
112
113 MouseArea {
114 id: mouseArea
115
116 anchors.fill: parent
117
118 drag {
119 target: listItem
120 axis: Drag.YAxis
121 minimumY: 0
122 }
123
124 cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor
125 preventStealing: true
126
127 Kirigami.Icon {
128 id: internal
129
130 anchors.fill: parent
131
132 source: "handle-sort"
133 opacity: mouseArea.pressed || (!Kirigami.Settings.tabletMode && listItem.hovered) ? 1 : 0.6
134
135 property int startY
136 property int mouseDownY
137 property Item originalParent
138 property int listItemLastY
139 property bool draggingUp
140
141 function arrangeItem() {
142 const newIndex = listView.indexAt(1, listView.contentItem.mapFromItem(mouseArea, 0, internal.mouseDownY).y);
143
144 if (newIndex > -1 && ((internal.draggingUp && newIndex < index) || (!internal.draggingUp && newIndex > index))) {
145 root.moveRequested(index, newIndex);
146 }
147 }
148 }
149
150 onPressed: mouse => {
151 internal.originalParent = listItem.parent;
152 listItem.parent = listView;
153 listItem.y = internal.originalParent.mapToItem(listItem.parent, listItem.x, listItem.y).y;
154 internal.originalParent.z = 99;
155 internal.startY = listItem.y;
156 internal.listItemLastY = listItem.y;
157 internal.mouseDownY = mouse.y;
158 // while dragging listItem's height could change
159 // we want a const maximumY during the dragging time
160 mouseArea.drag.maximumY = listView.height - listItem.height;
161 }
162
163 onPositionChanged: mouse => {
164 if (!pressed || listItem.y === internal.listItemLastY) {
165 return;
166 }
167
168 internal.draggingUp = listItem.y < internal.listItemLastY
169 internal.listItemLastY = listItem.y;
170
171 internal.arrangeItem();
172
173 // autoscroll when the dragging item reaches the listView's top/bottom boundary
174 scrollTimer.running = (listView.contentHeight > listView.height)
175 && ((listItem.y === 0 && !listView.atYBeginning)
176 || (listItem.y === mouseArea.drag.maximumY && !listView.atYEnd));
177 }
178
179 onReleased: mouse => dropped()
180 onCanceled: dropped()
181
182 function dropped() {
183 listItem.y = internal.originalParent.mapFromItem(listItem, 0, 0).y;
184 listItem.parent = internal.originalParent;
185 dropAnimation.running = true;
186 scrollTimer.running = false;
187 root.dropped();
188 }
189
190 SequentialAnimation {
191 id: dropAnimation
192 YAnimator {
193 target: listItem
194 from: listItem.y
195 to: 0
196 duration: Kirigami.Units.longDuration
197 easing.type: Easing.InOutQuad
198 }
199 PropertyAction {
200 target: listItem.parent
201 property: "z"
202 value: 0
203 }
204 }
205
206 Timer {
207 id: scrollTimer
208
209 interval: 50
210 repeat: true
211
212 onTriggered: {
213 if (internal.draggingUp) {
214 listView.contentY -= Kirigami.Units.gridUnit;
215 if (listView.atYBeginning) {
216 listView.positionViewAtBeginning();
217 stop();
218 }
219 } else {
220 listView.contentY += Kirigami.Units.gridUnit;
221 if (listView.atYEnd) {
222 listView.positionViewAtEnd();
223 stop();
224 }
225 }
226 internal.arrangeItem();
227 }
228 }
229 }
230}
void stop(Ekos::AlignState mode)
QAction * repeat(const QObject *recvr, const char *slot, QObject *parent)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:46 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.