Kirigami2

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

KDE's Doxygen guidelines are available online.