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  *
13  * The QtQuick.ListView must use a model that supports item reordering, such as
14  * QtQml.Models.ListModel.move or QAbstractItemModel instances
15  * with QAbstractItemModel::moveRows correctly implemented.
16  * In order for ListItemDragHandle to work correctly, the list item that is being dragged
17  * should not directly be the delegate of the ListView, but a child of it.
18  *
19  * Example usage:
20  * @include listitemdraghandle.qml
21  *
22  * As seen from the example, we wrapped the AbstractListItem with an
23  * Item component. This is because when dragging the list item around, only the item that
24  * the drag handle is assigned to is moved, and the wrapper Item stays there for
25  * it to take up space so that other list items don't take it.
26  *
27  * @since org.kde.kirigami 2.5
28  * @inherit QtQuick.Item
29  */
30 Item {
31  id: root
32 
33  /**
34  * @brief This property holds the delegate that will be dragged around.
35  *
36  * This item *must* be a child of the actual ListView's delegate.
37  */
38  property Item listItem
39 
40  /**
41  * @brief This property holds the ListView that the delegate belong to.
42  */
43  property ListView listView
44 
45  /**
46  * @brief This signal is emitted when the drag handle wants to move the item in the model.
47  *
48  * The following example does the move in the case a ListModel is used:
49  * @code{.qml}
50  * onMoveRequested: listModel.move(oldIndex, newIndex, 1)
51  * @endcode
52  * @param oldIndex the index the item is currently at
53  * @param newIndex the index we want to move the item to
54  */
55  signal moveRequested(int oldIndex, int newIndex)
56 
57  /**
58  * @brief This signal is emitted when the drag operation is complete and the item has been
59  * dropped in the new final position.
60  */
61  signal dropped()
62 
63  implicitWidth: Kirigami.Units.iconSizes.smallMedium
64  implicitHeight: implicitWidth
65 
66  MouseArea {
67  id: mouseArea
68  anchors.fill: parent
69  drag {
70  target: listItem
71  axis: Drag.YAxis
72  minimumY: 0
73  }
74  cursorShape: pressed ? Qt.ClosedHandCursor : Qt.OpenHandCursor
75 
76  Kirigami.Icon {
77  id: internal
78  source: "handle-sort"
79  property int startY
80  property int mouseDownY
81  property Item originalParent
82  opacity: mouseArea.pressed || (!Kirigami.Settings.tabletMode && listItem.hovered) ? 1 : 0.6
83  property int listItemLastY
84  property bool draggingUp
85 
86  function arrangeItem() {
87  const newIndex = listView.indexAt(1, listView.contentItem.mapFromItem(mouseArea, 0, internal.mouseDownY).y);
88 
89  if (newIndex > -1 && ((internal.draggingUp && newIndex < index) || (!internal.draggingUp && newIndex > index))) {
90  root.moveRequested(index, newIndex);
91  }
92  }
93 
94  anchors.fill: parent
95  }
96  preventStealing: true
97 
98 
99  onPressed: mouse => {
100  internal.originalParent = listItem.parent;
101  listItem.parent = listView;
102  listItem.y = internal.originalParent.mapToItem(listItem.parent, listItem.x, listItem.y).y;
103  internal.originalParent.z = 99;
104  internal.startY = listItem.y;
105  internal.listItemLastY = listItem.y;
106  internal.mouseDownY = mouse.y;
107  // while dragging listItem's height could change
108  // we want a const maximumY during the dragging time
109  mouseArea.drag.maximumY = listView.height - listItem.height;
110  }
111 
112  onPositionChanged: mouse => {
113  if (!pressed || listItem.y === internal.listItemLastY) {
114  return;
115  }
116 
117  internal.draggingUp = listItem.y < internal.listItemLastY
118  internal.listItemLastY = listItem.y;
119 
120  internal.arrangeItem();
121 
122  // autoscroll when the dragging item reaches the listView's top/bottom boundary
123  scrollTimer.running = (listView.contentHeight > listView.height)
124  && ( (listItem.y === 0 && !listView.atYBeginning) ||
125  (listItem.y === mouseArea.drag.maximumY && !listView.atYEnd) );
126  }
127  onReleased: mouse => {
128  listItem.y = internal.originalParent.mapFromItem(listItem, 0, 0).y;
129  listItem.parent = internal.originalParent;
130  dropAnimation.running = true;
131  scrollTimer.running = false;
132  root.dropped();
133  }
134  onCanceled: released()
135  SequentialAnimation {
136  id: dropAnimation
137  YAnimator {
138  target: listItem
139  from: listItem.y
140  to: 0
141  duration: Kirigami.Units.longDuration
142  easing.type: Easing.InOutQuad
143  }
144  PropertyAction {
145  target: listItem.parent
146  property: "z"
147  value: 0
148  }
149  }
150  Timer {
151  id: scrollTimer
152  interval: 50
153  repeat: true
154  onTriggered: {
155  if (internal.draggingUp) {
156  listView.contentY -= Kirigami.Units.gridUnit;
157  if (listView.atYBeginning) {
158  listView.positionViewAtBeginning();
159  stop();
160  }
161  } else {
162  listView.contentY += Kirigami.Units.gridUnit;
163  if (listView.atYEnd) {
164  listView.positionViewAtEnd();
165  stop();
166  }
167  }
168  internal.arrangeItem();
169  }
170  }
171  }
172 }
173 
void stop(Ekos::AlignState mode)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Oct 3 2023 03:58:35 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.