Changeset View
Changeset View
Standalone View
Standalone View
src/controls/templates/OverlayDrawer.qml
Show All 12 Lines | |||||
13 | * | 13 | * | ||
14 | * You should have received a copy of the GNU Library General Public | 14 | * You should have received a copy of the GNU Library General Public | ||
15 | * License along with this program; if not, write to the | 15 | * License along with this program; if not, write to the | ||
16 | * Free Software Foundation, Inc., | 16 | * Free Software Foundation, Inc., | ||
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
18 | */ | 18 | */ | ||
19 | 19 | | |||
20 | import QtQuick 2.1 | 20 | import QtQuick 2.1 | ||
21 | import org.kde.kirigami 1.0 | 21 | import QtQuick.Templates 2.0 as T2 | ||
22 | import org.kde.kirigami 2.0 | ||||
22 | import "private" | 23 | import "private" | ||
23 | 24 | | |||
24 | /** | 25 | /** | ||
25 | * Overlay Drawers are used to expose additional UI elements needed for | 26 | * Overlay Drawers are used to expose additional UI elements needed for | ||
26 | * small secondary tasks for which the main UI elements are not needed. | 27 | * small secondary tasks for which the main UI elements are not needed. | ||
27 | * For example in Okular Active, an Overlay Drawer is used to display | 28 | * For example in Okular Active, an Overlay Drawer is used to display | ||
28 | * thumbnails of all pages within a document along with a search field. | 29 | * thumbnails of all pages within a document along with a search field. | ||
29 | * This is used for the distinct task of navigating to another page. | 30 | * This is used for the distinct task of navigating to another page. | ||
30 | * | 31 | * | ||
31 | */ | 32 | */ | ||
32 | AbstractDrawer { | 33 | T2.Drawer { | ||
33 | id: root | 34 | id: root | ||
34 | 35 | | |||
35 | z: modal ? (opened ? 100 : 99 ): 98 | 36 | z: modal ? (drawerOpen ? 100 : 99 ): 0 | ||
36 | 37 | | |||
37 | //BEGIN Properties | 38 | //BEGIN Properties | ||
38 | /** | 39 | /** | ||
39 | * page: Item | 40 | * drawerOpen: bool | ||
40 | * It's the default property. it's the main content of the drawer page, | 41 | * true when the drawer is open and visible | ||
41 | * the part that is always shown | | |||
42 | */ | 42 | */ | ||
43 | default property alias page: mainPage.data | 43 | property bool drawerOpen: false | ||
44 | 44 | | |||
45 | /** | 45 | /** | ||
46 | * contentItem: Item | 46 | * enabled: bool | ||
47 | * It's the part that can be pulled in and out, will act as a sidebar. | 47 | * This property holds whether the item receives mouse and keyboard events. By default this is true. | ||
48 | */ | 48 | */ | ||
49 | property Item contentItem | 49 | property bool enabled: true | ||
50 | 50 | | |||
51 | /** | 51 | /** | ||
52 | * background: Item | 52 | * peeking: true | ||
53 | * This property holds the background item. | 53 | * When true the drawer is in a state between open and closed. the drawer is visible but not completely open. | ||
54 | * Note: the background will be automatically sized as the whole control | 54 | * This is usually the case when the user is dragging the drawer from a screen | ||
55 | * edge, so the user is "peeking" what's in the drawer | ||||
55 | */ | 56 | */ | ||
56 | property Item background | 57 | property bool peeking: false | ||
57 | 58 | | |||
58 | /** | 59 | /** | ||
59 | * opened: bool | 60 | * animating: Bool | ||
60 | * If true the drawer is open showing the contents of the "drawer" | 61 | * True during an animation of a drawer either opening or closing | ||
61 | * component. | | |||
62 | */ | 62 | */ | ||
63 | property alias opened: mainFlickable.open | 63 | readonly property bool animating : enterAnimation.animating || exitAnimation.animating || positionResetAnim.running | ||
64 | | ||||
65 | /** | | |||
66 | * edge: enumeration | | |||
67 | * This property holds the edge of the content item at which the drawer | | |||
68 | * will open from. | | |||
69 | * The acceptable values are: | | |||
70 | * Qt.TopEdge: The top edge of the content item. | | |||
71 | * Qt.LeftEdge: The left edge of the content item (default). | | |||
72 | * Qt.RightEdge: The right edge of the content item. | | |||
73 | * Qt.BottomEdge: The bottom edge of the content item. | | |||
74 | */ | | |||
75 | property int edge: Qt.LeftEdge | | |||
76 | | ||||
77 | /** | | |||
78 | * position: real | | |||
79 | * This property holds the position of the drawer relative to its | | |||
80 | * final destination. That is, the position will be 0 when the | | |||
81 | * drawer is fully closed, and 1 when fully open. | | |||
82 | */ | | |||
83 | property real position: 0 | | |||
84 | 64 | | |||
85 | /** | 65 | /** | ||
86 | * handleVisible: bool | 66 | * handleVisible: bool | ||
87 | * If true, a little handle will be visible to make opening the drawer easier | 67 | * If true, a little handle will be visible to make opening the drawer easier | ||
88 | * Currently supported only on left and right drawers | 68 | * Currently supported only on left and right drawers | ||
89 | */ | 69 | */ | ||
90 | property bool handleVisible: typeof(applicationWindow)===typeof(Function) && applicationWindow() ? applicationWindow().controlsVisible : true | 70 | property bool handleVisible: typeof(applicationWindow)===typeof(Function) && applicationWindow() ? applicationWindow().controlsVisible : true | ||
91 | 71 | | |||
92 | /** | 72 | /** | ||
93 | * leftPadding: int | 73 | * handle: Item | ||
94 | * default contents padding at left | 74 | * Readonly property that points to the item that will act as a physical | ||
95 | */ | 75 | * handle for the Drawer | ||
96 | property int leftPadding: Units.smallSpacing | 76 | **/ | ||
97 | 77 | readonly property Item handle: MouseArea { | |||
98 | /** | 78 | id: drawerHandle | ||
99 | * topPadding: int | 79 | z: 2000000 | ||
100 | * default contents padding at top | 80 | preventStealing: true | ||
101 | */ | 81 | parent: applicationWindow().overlay.parent | ||
102 | property int topPadding: Units.smallSpacing | | |||
103 | | ||||
104 | /** | | |||
105 | * rightPadding: int | | |||
106 | * default contents padding at right | | |||
107 | */ | | |||
108 | property int rightPadding: Units.smallSpacing | | |||
109 | | ||||
110 | /** | | |||
111 | * bottomPadding: int | | |||
112 | * default contents padding at bottom | | |||
113 | */ | | |||
114 | property int bottomPadding: Units.smallSpacing | | |||
115 | | ||||
116 | /** | | |||
117 | * modal: bool | | |||
118 | * If true the drawer will be an overlay of the main content, | | |||
119 | * obscuring it and blocking input. | | |||
120 | * If false, the drawer will look like a sidebar, with the main content | | |||
121 | * application still usable. | | |||
122 | * It is recomended to use modal on mobile devices and not modal on desktop | | |||
123 | * devices. | | |||
124 | * Default is true | | |||
125 | */ | | |||
126 | //property bool modal: true | | |||
127 | //END Properties | | |||
128 | | ||||
129 | 82 | | |||
130 | //BEGIN Methods | 83 | property int startX | ||
131 | /** | 84 | property int mappedStartX | ||
132 | * open: function | 85 | onPressed: { | ||
133 | * This method opens the drawer, animating the movement if a | 86 | root.peeking = true; | ||
134 | * valid animation has been set. | 87 | startX = mouse.x; | ||
135 | */ | 88 | mappedStartX = mapToItem(parent, startX, 0).x | ||
136 | function open () { | 89 | } | ||
137 | mainAnim.to = 0; | 90 | onPositionChanged: { | ||
91 | var pos = mapToItem(parent, mouse.x - startX, mouse.y); | ||||
138 | switch (root.edge) { | 92 | switch(root.edge) { | ||
139 | case Qt.RightEdge: | 93 | case Qt.LeftEdge: | ||
140 | mainAnim.to = drawerPage.width; | 94 | root.position = pos.x/root.contentItem.width; | ||
141 | break; | 95 | break; | ||
142 | case Qt.BottomEdge: | 96 | case Qt.RightEdge: | ||
143 | mainAnim.to = drawerPage.height; | 97 | root.position = (root.parent.width - pos.x - width)/root.contentItem.width; | ||
144 | break; | 98 | break; | ||
145 | case Qt.LeftEdge: | | |||
146 | case Qt.TopEdge: | | |||
147 | default: | 99 | default: | ||
148 | mainAnim.to = 0; | | |||
149 | break; | | |||
150 | } | 100 | } | ||
151 | mainAnim.running = true; | | |||
152 | mainFlickable.open = true; | | |||
153 | } | 101 | } | ||
102 | onReleased: { | ||||
103 | root.peeking = false; | ||||
154 | 104 | | |||
155 | /** | 105 | if (Math.abs(mapToItem(parent, mouse.x, 0).x - mappedStartX) < Qt.styleHints.startDragDistance) { | ||
156 | * close: function | 106 | root.drawerOpen = !root.drawerOpen; | ||
157 | * This method closes the drawer, animating the movement if a | | |||
158 | * valid animation has been set. | | |||
159 | */ | | |||
160 | function close () { | | |||
161 | switch (root.edge) { | | |||
162 | case Qt.RightEdge: | | |||
163 | case Qt.BottomEdge: | | |||
164 | mainAnim.to = 0; | | |||
165 | break; | | |||
166 | case Qt.LeftEdge: | | |||
167 | mainAnim.to = drawerPage.width; | | |||
168 | break; | | |||
169 | case Qt.TopEdge: | | |||
170 | mainAnim.to = drawerPage.height; | | |||
171 | break; | | |||
172 | } | 107 | } | ||
173 | mainAnim.running = true; | | |||
174 | mainFlickable.open = false; | | |||
175 | } | 108 | } | ||
176 | 109 | onCanceled: { | |||
177 | /** | 110 | root.peeking = false | ||
178 | * clicked: signal | | |||
179 | * This signal is emitted when the drawer is clicked. | | |||
180 | */ | | |||
181 | //END Methods | | |||
182 | | ||||
183 | //BEGIN Signal handlers | | |||
184 | onBackgroundChanged: { | | |||
185 | background.z = 1; | | |||
186 | background.parent = mainItem; | | |||
187 | background.anchors.fill = drawerPage; | | |||
188 | } | 111 | } | ||
189 | 112 | x: { | |||
190 | onPositionChanged: { | | |||
191 | if (!mainFlickable.loopCheck) { | | |||
192 | mainFlickable.loopCheck = true; | | |||
193 | if (!mainFlickable.flicking && !mainFlickable.dragging && !mainAnim.running) { | | |||
194 | switch (root.edge) { | 113 | switch(root.edge) { | ||
195 | case Qt.RightEdge: | | |||
196 | mainFlickable.contentX = drawerPage.width * position; | | |||
197 | break; | | |||
198 | case Qt.LeftEdge: | 114 | case Qt.LeftEdge: | ||
199 | mainFlickable.contentX = drawerPage.width * (1-position); | 115 | return root.background.width * root.position; | ||
200 | break; | 116 | case Qt.RightEdge: | ||
201 | case Qt.BottomEdge: | 117 | return drawerHandle.parent.width - (root.background.width * root.position) - width; | ||
202 | mainFlickable.contentY = drawerPage.height * position; | 118 | default: | ||
203 | break; | 119 | return 0; | ||
204 | } | 120 | } | ||
205 | } | 121 | } | ||
206 | mainFlickable.loopCheck = false; | 122 | | ||
123 | anchors.bottom: parent.bottom | ||||
124 | visible: root.enabled && (root.edge == Qt.LeftEdge || root.edge == Qt.RightEdge) | ||||
125 | width: Units.iconSizes.medium + Units.smallSpacing * 2 | ||||
126 | height: width | ||||
127 | opacity: root.handleVisible ? 1 : 0 | ||||
128 | Behavior on opacity { | ||||
129 | NumberAnimation { | ||||
130 | duration: Units.longDuration | ||||
131 | easing.type: Easing.InOutQuad | ||||
207 | } | 132 | } | ||
208 | } | 133 | } | ||
209 | onContentItemChanged: { | 134 | transform: Translate { | ||
210 | contentItem.parent = drawerPage | 135 | id: translateTransform | ||
211 | contentItem.anchors.fill = drawerPage | 136 | x: root.handleVisible ? 0 : (root.edge == Qt.LeftEdge ? -drawerHandle.width : drawerHandle.width) | ||
212 | if (contentItem.flickableItem !== undefined) { | 137 | Behavior on x { | ||
213 | contentItem.anchors.topMargin = 0; | 138 | NumberAnimation { | ||
214 | contentItem.anchors.leftMargin = 0; | 139 | duration: Units.longDuration | ||
215 | contentItem.anchors.bottomMargin = 0; | 140 | easing.type: !root.handleVisible ? Easing.OutQuad : Easing.InQuad | ||
216 | contentItem.anchors.rightMargin = 0; | | |||
217 | } else { | | |||
218 | contentItem.anchors.topMargin = root.topPadding; | | |||
219 | contentItem.anchors.leftMargin = root.leftPadding; | | |||
220 | contentItem.anchors.bottomMargin = root.bottomPadding; | | |||
221 | contentItem.anchors.rightMargin = root.rightPadding; | | |||
222 | } | 141 | } | ||
223 | } | 142 | } | ||
224 | //END Signal handlers | | |||
225 | | ||||
226 | Item { | | |||
227 | id: mainPage | | |||
228 | anchors.fill: parent | | |||
229 | onChildrenChanged: mainPage.children[0].anchors.fill = mainPage | | |||
230 | } | 143 | } | ||
231 | | ||||
232 | Rectangle { | | |||
233 | anchors.fill: parent | | |||
234 | color: "black" | | |||
235 | opacity: 0.6 * mainFlickable.internalPosition | | |||
236 | visible: root.modal | | |||
237 | } | 144 | } | ||
238 | 145 | | |||
146 | //END Properties | ||||
239 | 147 | | |||
240 | //NOTE: it's a PropertyAnimation instead of a NumberAnimation because | | |||
241 | //NumberAnimation doesn't have NOTIFY signal on to property | | |||
242 | PropertyAnimation { | | |||
243 | id: mainAnim | | |||
244 | target: mainFlickable | | |||
245 | properties: (root.edge == Qt.RightEdge || root.edge == Qt.LeftEdge) ? "contentX" : "contentY" | | |||
246 | duration: Units.longDuration | | |||
247 | easing.type: mainAnim.to > 0 ? Easing.InQuad : Easing.OutQuad | | |||
248 | } | | |||
249 | 148 | | |||
250 | MouseArea { | 149 | //BEGIN reassign properties | ||
251 | id: edgeMouse | 150 | //default paddings | ||
252 | anchors { | 151 | leftPadding: Units.smallSpacing | ||
253 | right: root.edge == Qt.LeftEdge ? undefined : parent.right | 152 | topPadding: Units.smallSpacing | ||
254 | left: root.edge == Qt.RightEdge ? undefined : parent.left | 153 | rightPadding: Units.smallSpacing | ||
255 | top: root.edge == Qt.BottomEdge ? undefined : parent.top | 154 | bottomPadding: Units.smallSpacing | ||
256 | bottom: root.edge == Qt.TopEdge ? undefined : parent.bottom | | |||
257 | } | | |||
258 | z: 99 | | |||
259 | width: Units.smallSpacing * 3 | | |||
260 | height: Units.smallSpacing * 3 | | |||
261 | property int startMouseX | | |||
262 | property real oldMouseX | | |||
263 | property int startMouseY | | |||
264 | property real oldMouseY | | |||
265 | property bool startDragging: false | | |||
266 | | ||||
267 | function managePress(mouse) { | | |||
268 | if (drawerPage.children.length == 0) { | | |||
269 | mouse.accepted = false; | | |||
270 | return; | | |||
271 | } | | |||
272 | 155 | | |||
273 | speedSampler.restart(); | 156 | parent: modal ? T2.ApplicationWindow.overlay : T2.ApplicationWindow.contentItem | ||
274 | mouse.accepted = true; | 157 | height: edge == Qt.LeftEdge || edge == Qt.RightEdge ? applicationWindow().height : Math.min(contentItem.implicitHeight, Math.round(applicationWindow().height*0.8)) | ||
275 | oldMouseX = startMouseX = mouse.x; | 158 | width: edge == Qt.TopEdge || edge == Qt.BottomEdge ? applicationWindow().width : Math.min(contentItem.implicitWidth, Math.round(applicationWindow().width*0.8)) | ||
276 | oldMouseY = startMouseY = mouse.y; | 159 | edge: Qt.LeftEdge | ||
277 | startDragging = false; | 160 | modal: true | ||
278 | } | | |||
279 | onPressed: { | | |||
280 | managePress(mouse) | | |||
281 | } | | |||
282 | 161 | | |||
283 | onPositionChanged: { | 162 | dragMargin: enabled && (edge == Qt.LeftEdge || edge == Qt.RightEdge) ? Qt.styleHints.startDragDistance : 0 | ||
284 | if (!root.contentItem) { | | |||
285 | mouse.accepted = false; | | |||
286 | return; | | |||
287 | } | | |||
288 | 163 | | |||
289 | if (Math.abs(mouse.x - startMouseX) > root.width / 5 || | 164 | implicitWidth: Math.max(background ? background.implicitWidth : 0, contentWidth + leftPadding + rightPadding) | ||
290 | Math.abs(mouse.y - startMouseY) > root.height / 5) { | 165 | implicitHeight: Math.max(background ? background.implicitHeight : 0, contentHeight + topPadding + bottomPadding) | ||
291 | startDragging = true; | 166 | | ||
292 | } | 167 | contentWidth: contentItem.implicitWidth || (contentChildren.length === 1 ? contentChildren[0].implicitWidth : 0) | ||
293 | if (startDragging) { | 168 | contentHeight: contentItem.implicitHeight || (contentChildren.length === 1 ? contentChildren[0].implicitHeight : 0) | ||
294 | switch (root.edge) { | 169 | | ||
295 | case Qt.RightEdge: | 170 | enter: Transition { | ||
296 | mainFlickable.contentX = Math.min(mainItem.width - root.width, mainFlickable.contentX + oldMouseX - mouse.x); | 171 | SequentialAnimation { | ||
297 | break; | 172 | id: enterAnimation | ||
298 | case Qt.LeftEdge: | 173 | /*NOTE: why this? the running status of the enter transition is not relaible and | ||
299 | mainFlickable.contentX = Math.max(0, mainFlickable.contentX + oldMouseX - mouse.x); | 174 | * the SmoothedAnimation is always marked as non running, | ||
300 | break; | 175 | * so the only way to get to a reliable animating status is with this | ||
301 | case Qt.BottomEdge: | 176 | */ | ||
302 | mainFlickable.contentY = Math.min(mainItem.height - root.height, mainFlickable.contentY + oldMouseY - mouse.y); | 177 | property bool animating | ||
303 | break; | 178 | ScriptAction { | ||
304 | case Qt.TopEdge: | 179 | script: enterAnimation.animating = true | ||
305 | mainFlickable.contentY = Math.max(0, mainFlickable.contentY + oldMouseY - mouse.y); | | |||
306 | break; | | |||
307 | } | | |||
308 | } | | |||
309 | oldMouseX = mouse.x; | | |||
310 | oldMouseY = mouse.y; | | |||
311 | } | | |||
312 | onReleased: { | | |||
313 | if (!startDragging) { | | |||
314 | return; | | |||
315 | } | | |||
316 | speedSampler.running = false; | | |||
317 | if (speedSampler.speed != 0) { | | |||
318 | if (root.edge == Qt.RightEdge || root.edge == Qt.LeftEdge) { | | |||
319 | mainFlickable.flick(speedSampler.speed, 0); | | |||
320 | } else { | | |||
321 | mainFlickable.flick(0, speedSampler.speed); | | |||
322 | } | 180 | } | ||
323 | } else { | 181 | SmoothedAnimation { | ||
324 | if (mainFlickable.internalPosition > 0.5) { | 182 | velocity: 5 | ||
325 | root.open(); | | |||
326 | } else { | | |||
327 | root.close(); | | |||
328 | } | 183 | } | ||
184 | ScriptAction { | ||||
185 | script: enterAnimation.animating = false | ||||
329 | } | 186 | } | ||
330 | } | 187 | } | ||
331 | } | 188 | } | ||
332 | 189 | | |||
333 | MouseArea { | 190 | exit: Transition { | ||
334 | id: handleMouseArea | 191 | SequentialAnimation { | ||
335 | z:999 | 192 | id: exitAnimation | ||
336 | anchors { | 193 | property bool animating | ||
337 | right: root.edge == Qt.LeftEdge ? undefined : parent.right | 194 | ScriptAction { | ||
338 | left: root.edge == Qt.RightEdge ? undefined : parent.left | 195 | script: exitAnimation.animating = true | ||
339 | bottom: parent.bottom | | |||
340 | leftMargin: root.opened ? drawerPage.width : 0 | | |||
341 | rightMargin: root.opened ? drawerPage.width : 0 | | |||
342 | } | | |||
343 | visible: root.handleVisible && (root.edge == Qt.LeftEdge || root.edge == Qt.RightEdge) | | |||
344 | width: Units.iconSizes.medium | | |||
345 | height: width | | |||
346 | onPressed: edgeMouse.managePress(mouse); | | |||
347 | onPositionChanged: edgeMouse.positionChanged(mouse); | | |||
348 | onReleased: edgeMouse.released(mouse); | | |||
349 | onClicked: root.opened ? root.close() : root.open(); | | |||
350 | } | | |||
351 | | ||||
352 | Timer { | | |||
353 | id: speedSampler | | |||
354 | interval: 100 | | |||
355 | repeat: true | | |||
356 | property real speed | | |||
357 | property real oldContentX | | |||
358 | property real oldContentY | | |||
359 | onTriggered: { | | |||
360 | if (root.edge == Qt.RightEdge || root.edge == Qt.LeftEdge) { | | |||
361 | speed = (mainFlickable.contentX - oldContentX) * 10; | | |||
362 | oldContentX = mainFlickable.contentX; | | |||
363 | } else { | | |||
364 | speed = (mainFlickable.contentY - oldContentY) * 10; | | |||
365 | oldContentY = mainFlickable.contentY; | | |||
366 | } | | |||
367 | } | 196 | } | ||
368 | onRunningChanged: { | 197 | SmoothedAnimation { | ||
369 | if (running) { | 198 | velocity: 5 | ||
370 | speed = 0; | | |||
371 | oldContentX = mainFlickable.contentX; | | |||
372 | oldContentY = mainFlickable.contentY; | | |||
373 | } else { | | |||
374 | if (root.edge == Qt.RightEdge || root.edge == Qt.LeftEdge) { | | |||
375 | speed = (oldContentX - mainFlickable.contentX) * 10; | | |||
376 | } else { | | |||
377 | speed = (oldContentY - mainFlickable.contentY) * 10; | | |||
378 | } | 199 | } | ||
200 | ScriptAction { | ||||
201 | script: exitAnimation.animating = false | ||||
379 | } | 202 | } | ||
380 | } | 203 | } | ||
381 | } | 204 | } | ||
205 | //END reassign properties | ||||
382 | 206 | | |||
383 | MouseArea { | 207 | | ||
384 | id: mainMouseArea | 208 | //BEGIN signal handlers | ||
385 | anchors.fill: parent | 209 | onPositionChanged: { | ||
386 | drag.filterChildren: true | 210 | if (peeking) { | ||
387 | onClicked: { | 211 | visible = true | ||
388 | if ((root.edge == Qt.LeftEdge && mouse.x < drawerPage.width) || | | |||
389 | (root.edge == Qt.RightEdge && mouse.x > drawerPage.x - mainFlickable.contentX) || | | |||
390 | (root.edge == Qt.TopEdge && mouse.y < drawerPage.height) || | | |||
391 | (root.edge == Qt.BottomEdge && mouse.y > drawerPage.y - mainFlickable.contentY)) { | | |||
392 | return; | | |||
393 | } | 212 | } | ||
394 | root.clicked(); | | |||
395 | root.close(); | | |||
396 | } | 213 | } | ||
397 | enabled: (root.edge == Qt.LeftEdge && !mainFlickable.atXEnd) || | 214 | onVisibleChanged: { | ||
398 | (root.edge == Qt.RightEdge && !mainFlickable.atXBeginning) || | 215 | if (peeking) { | ||
399 | (root.edge == Qt.TopEdge && !mainFlickable.atYEnd) || | 216 | visible = true | ||
400 | (root.edge == Qt.BottomEdge && !mainFlickable.atYBeginning) || | | |||
401 | mainFlickable.moving | | |||
402 | | ||||
403 | Flickable { | | |||
404 | id: mainFlickable | | |||
405 | property bool open | | |||
406 | anchors.fill: parent | | |||
407 | interactive: root.modal | | |||
408 | onOpenChanged: { | | |||
409 | if (open) { | | |||
410 | root.open(); | | |||
411 | Qt.inputMethod.hide(); | | |||
412 | } else { | 217 | } else { | ||
413 | root.close(); | 218 | drawerOpen = visible; | ||
414 | } | | |||
415 | } | 219 | } | ||
416 | enabled: parent.enabled | | |||
417 | flickableDirection: root.edge == Qt.LeftEdge || root.edge == Qt.RightEdge ? Flickable.HorizontalFlick : Flickable.VerticalFlick | | |||
418 | contentWidth: mainItem.width | | |||
419 | contentHeight: mainItem.height | | |||
420 | boundsBehavior: Flickable.StopAtBounds | | |||
421 | readonly property real internalPosition: { | | |||
422 | switch (root.edge) { | | |||
423 | case Qt.RightEdge: | | |||
424 | return mainFlickable.contentX/drawerPage.width; | | |||
425 | case Qt.LeftEdge: | | |||
426 | return 1 - (mainFlickable.contentX/drawerPage.width); | | |||
427 | case Qt.BottomEdge: | | |||
428 | return mainFlickable.contentY/drawerPage.height; | | |||
429 | case Qt.TopEdge: | | |||
430 | return 1 - (mainFlickable.contentY/drawerPage.height); | | |||
431 | } | | |||
432 | } | | |||
433 | property bool loopCheck: false | | |||
434 | onInternalPositionChanged: { | | |||
435 | if (!loopCheck) { | | |||
436 | loopCheck = true; | | |||
437 | root.position = internalPosition; | | |||
438 | loopCheck = false; | | |||
439 | } | 220 | } | ||
440 | } | 221 | onPeekingChanged: { | ||
441 | 222 | if (peeking) { | |||
442 | onFlickingChanged: { | 223 | root.enter.enabled = false; | ||
443 | if (!flicking) { | 224 | root.exit.enabled = false; | ||
444 | if (internalPosition > 0.5) { | | |||
445 | root.open(); | | |||
446 | } else { | 225 | } else { | ||
447 | root.close(); | 226 | positionResetAnim.to = position > 0.5 ? 1 : 0; | ||
448 | } | 227 | positionResetAnim.running = true | ||
449 | } | 228 | root.enter.enabled = true; | ||
229 | root.exit.enabled = true; | ||||
450 | } | 230 | } | ||
451 | onMovingChanged: { | | |||
452 | if (!moving) { | | |||
453 | flickingChanged(); | | |||
454 | } | 231 | } | ||
232 | onDrawerOpenChanged: { | ||||
233 | //sync this property only when the component is properly loaded | ||||
234 | if (!__internal.completed) { | ||||
235 | return; | ||||
455 | } | 236 | } | ||
456 | 237 | positionResetAnim.running = false; | |||
457 | Item { | 238 | if (drawerOpen) { | ||
458 | id: mainItem | 239 | open(); | ||
459 | width: root.width + ((root.edge == Qt.RightEdge || root.edge == Qt.LeftEdge) ? drawerPage.width : 0) | | |||
460 | height: root.height + ((root.edge == Qt.TopEdge || root.edge == Qt.BottomEdge) ? drawerPage.height : 0) | | |||
461 | onWidthChanged: { | | |||
462 | if (root.edge == Qt.LeftEdge) { | | |||
463 | if (root.opened) { | | |||
464 | mainFlickable.contentX = 0; | | |||
465 | } else { | 240 | } else { | ||
466 | mainFlickable.contentX = drawerPage.width; | 241 | close(); | ||
467 | } | | |||
468 | } | | |||
469 | } | | |||
470 | onHeightChanged: { | | |||
471 | if (root.edge == Qt.TopEdge) { | | |||
472 | mainFlickable.contentY = drawerPage.Height; | | |||
473 | } | 242 | } | ||
474 | } | 243 | } | ||
475 | 244 | | |||
476 | 245 | Component.onCompleted: { | |||
477 | Item { | 246 | //if defined as drawerOpen by default in QML, don't animate | ||
478 | id: drawerPage | 247 | if (root.drawerOpen) { | ||
479 | z: 3 | 248 | root.enter.enabled = false; | ||
480 | anchors { | 249 | root.visible = true; | ||
481 | left: root.edge != Qt.RightEdge ? parent.left : undefined | 250 | root.position = 1; | ||
482 | right: root.edge != Qt.LeftEdge ? parent.right : undefined | 251 | root.enter.enabled = true; | ||
483 | top: root.edge != Qt.BottomEdge ? parent.top : undefined | 252 | } | ||
484 | bottom: root.edge != Qt.TopEdge ? parent.bottom : undefined | 253 | __internal.completed = true; | ||
485 | } | 254 | } | ||
486 | 255 | //END signal handlers | |||
487 | clip: true | 256 | | ||
488 | width: root.contentItem ? Math.min(root.contentItem.implicitWidth, root.width * 0.7) : 0 | 257 | //this is as hidden as it can get here | ||
489 | height: root.contentItem ? Math.min(root.contentItem.implicitHeight, root.height * 0.7) : 0 | 258 | property QtObject __internal: QtObject { | ||
490 | } | 259 | //here in order to not be accessible from outside | ||
491 | } | 260 | property bool completed: false | ||
261 | property NumberAnimation positionResetAnim: NumberAnimation { | ||||
262 | id: positionResetAnim | ||||
263 | target: root | ||||
264 | to: 0 | ||||
265 | property: "position" | ||||
266 | duration: (root.position)*Units.longDuration | ||||
492 | } | 267 | } | ||
493 | } | 268 | } | ||
494 | } | 269 | } |