1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include "qquickdrawer_p.h" |
38 | #include "qquickdrawer_p_p.h" |
39 | #include "qquickpopupitem_p_p.h" |
40 | #include "qquickpopuppositioner_p_p.h" |
41 | |
42 | #include <QtGui/qstylehints.h> |
43 | #include <QtGui/private/qguiapplication_p.h> |
44 | #include <QtQml/qqmlinfo.h> |
45 | #include <QtQuick/private/qquickwindow_p.h> |
46 | #include <QtQuick/private/qquickanimation_p.h> |
47 | #include <QtQuick/private/qquicktransition_p.h> |
48 | |
49 | QT_BEGIN_NAMESPACE |
50 | |
51 | /*! |
52 | \qmltype Drawer |
53 | \inherits Popup |
54 | //! \instantiates QQuickDrawer |
55 | \inqmlmodule QtQuick.Controls |
56 | \since 5.7 |
57 | \ingroup qtquickcontrols2-navigation |
58 | \ingroup qtquickcontrols2-popups |
59 | \brief Side panel that can be opened and closed using a swipe gesture. |
60 | |
61 | Drawer provides a swipe-based side panel, similar to those often used in |
62 | touch interfaces to provide a central location for navigation. |
63 | |
64 | \image qtquickcontrols2-drawer.gif |
65 | |
66 | Drawer can be positioned at any of the four edges of the content item. |
67 | The drawer above is positioned against the left edge of the window. The |
68 | drawer is then opened by \e "dragging" it out from the left edge of the |
69 | window. |
70 | |
71 | \code \QtMinorVersion |
72 | import QtQuick 2.\1 |
73 | import QtQuick.Controls 2.\1 |
74 | |
75 | ApplicationWindow { |
76 | id: window |
77 | visible: true |
78 | |
79 | Drawer { |
80 | id: drawer |
81 | width: 0.66 * window.width |
82 | height: window.height |
83 | |
84 | Label { |
85 | text: "Content goes here!" |
86 | anchors.centerIn: parent |
87 | } |
88 | } |
89 | } |
90 | \endcode |
91 | |
92 | Drawer is a special type of popup that resides at one of the window \l {edge}{edges}. |
93 | By default, Drawer re-parents itself to the window \l {ApplicationWindow::}{overlay}, |
94 | and therefore operates on window coordinates. It is also possible to manually set the |
95 | \l {Popup::}{parent} to something else to make the drawer operate in a specific |
96 | coordinate space. |
97 | |
98 | Drawer can be configured to cover only part of its window edge. The following example |
99 | illustrates how Drawer can be positioned to appear below a window header: |
100 | |
101 | \code \QtMinorVersion |
102 | import QtQuick 2.\1 |
103 | import QtQuick.Controls 2.\1 |
104 | |
105 | ApplicationWindow { |
106 | id: window |
107 | visible: true |
108 | |
109 | header: ToolBar { } |
110 | |
111 | Drawer { |
112 | y: header.height |
113 | width: window.width * 0.6 |
114 | height: window.height - header.height |
115 | } |
116 | } |
117 | \endcode |
118 | |
119 | The \l position property determines how much of the drawer is visible, as |
120 | a value between \c 0.0 and \c 1.0. It is not possible to set the x-coordinate |
121 | (or horizontal margins) of a drawer at the left or right window edge, or the |
122 | y-coordinate (or vertical margins) of a drawer at the top or bottom window edge. |
123 | |
124 | In the image above, the application's contents are \e "pushed" across the |
125 | screen. This is achieved by applying a translation to the contents: |
126 | |
127 | \code \QtMinorVersion |
128 | import QtQuick 2.\1 |
129 | import QtQuick.Controls 2.\1 |
130 | |
131 | ApplicationWindow { |
132 | id: window |
133 | width: 200 |
134 | height: 228 |
135 | visible: true |
136 | |
137 | Drawer { |
138 | id: drawer |
139 | width: 0.66 * window.width |
140 | height: window.height |
141 | } |
142 | |
143 | Label { |
144 | id: content |
145 | |
146 | text: "Aa" |
147 | font.pixelSize: 96 |
148 | anchors.fill: parent |
149 | verticalAlignment: Label.AlignVCenter |
150 | horizontalAlignment: Label.AlignHCenter |
151 | |
152 | transform: Translate { |
153 | x: drawer.position * content.width * 0.33 |
154 | } |
155 | } |
156 | } |
157 | \endcode |
158 | |
159 | If you would like the application's contents to stay where they are when |
160 | the drawer is opened, don't apply a translation. |
161 | |
162 | Drawer can be configured as a non-closable persistent side panel by |
163 | making the Drawer \l {Popup::modal}{non-modal} and \l {interactive} |
164 | {non-interactive}. See the \l {Qt Quick Controls 2 - Side Panel}{Side Panel} |
165 | example for more details. |
166 | |
167 | \note On some platforms, certain edges may be reserved for system |
168 | gestures and therefore cannot be used with Drawer. For example, the |
169 | top and bottom edges may be reserved for system notifications and |
170 | control centers on Android and iOS. |
171 | |
172 | \sa SwipeView, {Customizing Drawer}, {Navigation Controls}, {Popup Controls} |
173 | */ |
174 | |
175 | class QQuickDrawerPositioner : public QQuickPopupPositioner |
176 | { |
177 | public: |
178 | QQuickDrawerPositioner(QQuickDrawer *drawer) : QQuickPopupPositioner(drawer) { } |
179 | |
180 | void reposition() override; |
181 | }; |
182 | |
183 | qreal QQuickDrawerPrivate::offsetAt(const QPointF &point) const |
184 | { |
185 | qreal offset = positionAt(point) - position; |
186 | |
187 | // don't jump when dragged open |
188 | if (offset > 0 && position > 0 && !contains(scenePos: point)) |
189 | offset = 0; |
190 | |
191 | return offset; |
192 | } |
193 | |
194 | qreal QQuickDrawerPrivate::positionAt(const QPointF &point) const |
195 | { |
196 | Q_Q(const QQuickDrawer); |
197 | QQuickWindow *window = q->window(); |
198 | if (!window) |
199 | return 0; |
200 | |
201 | switch (edge) { |
202 | case Qt::TopEdge: |
203 | return point.y() / q->height(); |
204 | case Qt::LeftEdge: |
205 | return point.x() / q->width(); |
206 | case Qt::RightEdge: |
207 | return (window->width() - point.x()) / q->width(); |
208 | case Qt::BottomEdge: |
209 | return (window->height() - point.y()) / q->height(); |
210 | default: |
211 | return 0; |
212 | } |
213 | } |
214 | |
215 | QQuickPopupPositioner *QQuickDrawerPrivate::getPositioner() |
216 | { |
217 | Q_Q(QQuickDrawer); |
218 | if (!positioner) |
219 | positioner = new QQuickDrawerPositioner(q); |
220 | return positioner; |
221 | } |
222 | |
223 | void QQuickDrawerPositioner::reposition() |
224 | { |
225 | if (m_positioning) |
226 | return; |
227 | |
228 | QQuickDrawer *drawer = static_cast<QQuickDrawer*>(popup()); |
229 | QQuickWindow *window = drawer->window(); |
230 | if (!window) |
231 | return; |
232 | |
233 | const qreal position = drawer->position(); |
234 | QQuickItem * = drawer->popupItem(); |
235 | switch (drawer->edge()) { |
236 | case Qt::LeftEdge: |
237 | popupItem->setX((position - 1.0) * popupItem->width()); |
238 | break; |
239 | case Qt::RightEdge: |
240 | popupItem->setX(window->width() - position * popupItem->width()); |
241 | break; |
242 | case Qt::TopEdge: |
243 | popupItem->setY((position - 1.0) * popupItem->height()); |
244 | break; |
245 | case Qt::BottomEdge: |
246 | popupItem->setY(window->height() - position * popupItem->height()); |
247 | break; |
248 | } |
249 | |
250 | QQuickPopupPositioner::reposition(); |
251 | } |
252 | |
253 | void QQuickDrawerPrivate::showOverlay() |
254 | { |
255 | // managed in setPosition() |
256 | } |
257 | |
258 | void QQuickDrawerPrivate::hideOverlay() |
259 | { |
260 | // managed in setPosition() |
261 | } |
262 | |
263 | void QQuickDrawerPrivate::resizeOverlay() |
264 | { |
265 | if (!dimmer || !window) |
266 | return; |
267 | |
268 | QRectF geometry(0, 0, window->width(), window->height()); |
269 | |
270 | if (edge == Qt::LeftEdge || edge == Qt::RightEdge) { |
271 | geometry.setY(popupItem->y()); |
272 | geometry.setHeight(popupItem->height()); |
273 | } else { |
274 | geometry.setX(popupItem->x()); |
275 | geometry.setWidth(popupItem->width()); |
276 | } |
277 | |
278 | dimmer->setPosition(geometry.topLeft()); |
279 | dimmer->setSize(geometry.size()); |
280 | } |
281 | |
282 | static bool isWithinDragMargin(const QQuickDrawer *drawer, const QPointF &pos) |
283 | { |
284 | switch (drawer->edge()) { |
285 | case Qt::LeftEdge: |
286 | return pos.x() <= drawer->dragMargin(); |
287 | case Qt::RightEdge: |
288 | return pos.x() >= drawer->window()->width() - drawer->dragMargin(); |
289 | case Qt::TopEdge: |
290 | return pos.y() <= drawer->dragMargin(); |
291 | case Qt::BottomEdge: |
292 | return pos.y() >= drawer->window()->height() - drawer->dragMargin(); |
293 | default: |
294 | Q_UNREACHABLE(); |
295 | break; |
296 | } |
297 | return false; |
298 | } |
299 | |
300 | bool QQuickDrawerPrivate::startDrag(QEvent *event) |
301 | { |
302 | Q_Q(QQuickDrawer); |
303 | if (!window || !interactive || dragMargin < 0.0 || qFuzzyIsNull(d: dragMargin)) |
304 | return false; |
305 | |
306 | switch (event->type()) { |
307 | case QEvent::MouseButtonPress: |
308 | if (isWithinDragMargin(drawer: q, pos: static_cast<QMouseEvent *>(event)->windowPos())) { |
309 | prepareEnterTransition(); |
310 | reposition(); |
311 | return handleMouseEvent(item: window->contentItem(), event: static_cast<QMouseEvent *>(event)); |
312 | } |
313 | break; |
314 | |
315 | #if QT_CONFIG(quicktemplates2_multitouch) |
316 | case QEvent::TouchBegin: |
317 | case QEvent::TouchUpdate: |
318 | for (const QTouchEvent::TouchPoint &point : static_cast<QTouchEvent *>(event)->touchPoints()) { |
319 | if (point.state() == Qt::TouchPointPressed && isWithinDragMargin(drawer: q, pos: point.scenePos())) { |
320 | prepareEnterTransition(); |
321 | reposition(); |
322 | return handleTouchEvent(item: window->contentItem(), event: static_cast<QTouchEvent *>(event)); |
323 | } |
324 | } |
325 | break; |
326 | #endif |
327 | |
328 | default: |
329 | break; |
330 | } |
331 | |
332 | return false; |
333 | } |
334 | |
335 | static inline bool keepGrab(QQuickItem *item) |
336 | { |
337 | return item->keepMouseGrab() || item->keepTouchGrab(); |
338 | } |
339 | |
340 | bool QQuickDrawerPrivate::grabMouse(QQuickItem *item, QMouseEvent *event) |
341 | { |
342 | Q_Q(QQuickDrawer); |
343 | handleMouseEvent(item, event); |
344 | |
345 | if (!window || !interactive || keepGrab(item: popupItem) || keepGrab(item)) |
346 | return false; |
347 | |
348 | const QPointF movePoint = event->windowPos(); |
349 | |
350 | // Flickable uses a hard-coded threshold of 15 for flicking, and |
351 | // QStyleHints::startDragDistance for dragging. Drawer uses a bit |
352 | // larger threshold to avoid being too eager to steal touch (QTBUG-50045) |
353 | const int threshold = qMax(a: 20, b: QGuiApplication::styleHints()->startDragDistance() + 5); |
354 | bool overThreshold = false; |
355 | if (position > 0 || dragMargin > 0) { |
356 | const bool xOverThreshold = QQuickWindowPrivate::dragOverThreshold(d: movePoint.x() - pressPoint.x(), axis: Qt::XAxis, event, startDragThreshold: threshold); |
357 | const bool yOverThreshold = QQuickWindowPrivate::dragOverThreshold(d: movePoint.y() - pressPoint.y(), axis: Qt::YAxis, event, startDragThreshold: threshold); |
358 | if (edge == Qt::LeftEdge || edge == Qt::RightEdge) |
359 | overThreshold = xOverThreshold && !yOverThreshold; |
360 | else |
361 | overThreshold = yOverThreshold && !xOverThreshold; |
362 | } |
363 | |
364 | // Don't be too eager to steal presses outside the drawer (QTBUG-53929) |
365 | if (overThreshold && qFuzzyCompare(p1: position, p2: qreal(1.0)) && !contains(scenePos: movePoint)) { |
366 | if (edge == Qt::LeftEdge || edge == Qt::RightEdge) |
367 | overThreshold = qAbs(t: movePoint.x() - q->width()) < dragMargin; |
368 | else |
369 | overThreshold = qAbs(t: movePoint.y() - q->height()) < dragMargin; |
370 | } |
371 | |
372 | if (overThreshold) { |
373 | popupItem->grabMouse(); |
374 | popupItem->setKeepMouseGrab(true); |
375 | offset = offsetAt(point: movePoint); |
376 | } |
377 | |
378 | return overThreshold; |
379 | } |
380 | |
381 | #if QT_CONFIG(quicktemplates2_multitouch) |
382 | bool QQuickDrawerPrivate::grabTouch(QQuickItem *item, QTouchEvent *event) |
383 | { |
384 | Q_Q(QQuickDrawer); |
385 | bool handled = handleTouchEvent(item, event); |
386 | |
387 | if (!window || !interactive || keepGrab(item: popupItem) || keepGrab(item) || !event->touchPointStates().testFlag(flag: Qt::TouchPointMoved)) |
388 | return handled; |
389 | |
390 | bool overThreshold = false; |
391 | for (const QTouchEvent::TouchPoint &point : event->touchPoints()) { |
392 | if (!acceptTouch(point) || point.state() != Qt::TouchPointMoved) |
393 | continue; |
394 | |
395 | const QPointF movePoint = point.scenePos(); |
396 | |
397 | // Flickable uses a hard-coded threshold of 15 for flicking, and |
398 | // QStyleHints::startDragDistance for dragging. Drawer uses a bit |
399 | // larger threshold to avoid being too eager to steal touch (QTBUG-50045) |
400 | const int threshold = qMax(a: 20, b: QGuiApplication::styleHints()->startDragDistance() + 5); |
401 | if (position > 0 || dragMargin > 0) { |
402 | const bool xOverThreshold = QQuickWindowPrivate::dragOverThreshold(d: movePoint.x() - pressPoint.x(), axis: Qt::XAxis, tp: &point, startDragThreshold: threshold); |
403 | const bool yOverThreshold = QQuickWindowPrivate::dragOverThreshold(d: movePoint.y() - pressPoint.y(), axis: Qt::YAxis, tp: &point, startDragThreshold: threshold); |
404 | if (edge == Qt::LeftEdge || edge == Qt::RightEdge) |
405 | overThreshold = xOverThreshold && !yOverThreshold; |
406 | else |
407 | overThreshold = yOverThreshold && !xOverThreshold; |
408 | } |
409 | |
410 | // Don't be too eager to steal presses outside the drawer (QTBUG-53929) |
411 | if (overThreshold && qFuzzyCompare(p1: position, p2: qreal(1.0)) && !contains(scenePos: movePoint)) { |
412 | if (edge == Qt::LeftEdge || edge == Qt::RightEdge) |
413 | overThreshold = qAbs(t: movePoint.x() - q->width()) < dragMargin; |
414 | else |
415 | overThreshold = qAbs(t: movePoint.y() - q->height()) < dragMargin; |
416 | } |
417 | |
418 | if (overThreshold) { |
419 | popupItem->grabTouchPoints(ids: QVector<int>() << touchId); |
420 | popupItem->setKeepTouchGrab(true); |
421 | offset = offsetAt(point: movePoint); |
422 | } |
423 | } |
424 | |
425 | return overThreshold; |
426 | } |
427 | #endif |
428 | |
429 | static const qreal openCloseVelocityThreshold = 300; |
430 | |
431 | bool QQuickDrawerPrivate::blockInput(QQuickItem *item, const QPointF &point) const |
432 | { |
433 | Q_Q(const QQuickDrawer); |
434 | |
435 | // We want all events, if mouse/touch is already grabbed. |
436 | if (popupItem->keepMouseGrab() || popupItem->keepTouchGrab()) |
437 | return true; |
438 | |
439 | // Don't block input to drawer's children/content. |
440 | if (popupItem->isAncestorOf(child: item)) |
441 | return false; |
442 | |
443 | // Don't block outside a drawer's background dimming |
444 | if (dimmer && !dimmer->contains(point: dimmer->mapFromScene(point))) |
445 | return false; |
446 | |
447 | // Accept all events within drag area. |
448 | if (isWithinDragMargin(drawer: q, pos: point)) |
449 | return true; |
450 | |
451 | // Accept all other events if drawer is modal. |
452 | return modal; |
453 | } |
454 | |
455 | bool QQuickDrawerPrivate::handlePress(QQuickItem *item, const QPointF &point, ulong timestamp) |
456 | { |
457 | offset = 0; |
458 | velocityCalculator.startMeasuring(point1: point, timestamp); |
459 | |
460 | if (!QQuickPopupPrivate::handlePress(item, point, timestamp)) |
461 | return false; |
462 | |
463 | return true; |
464 | } |
465 | |
466 | bool QQuickDrawerPrivate::handleMove(QQuickItem *item, const QPointF &point, ulong timestamp) |
467 | { |
468 | Q_Q(QQuickDrawer); |
469 | if (!QQuickPopupPrivate::handleMove(item, point, timestamp)) |
470 | return false; |
471 | |
472 | // limit/reset the offset to the edge of the drawer when pushed from the outside |
473 | if (qFuzzyCompare(p1: position, p2: qreal(1.0)) && !contains(scenePos: point)) |
474 | offset = 0; |
475 | |
476 | bool isGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab(); |
477 | if (isGrabbed) |
478 | q->setPosition(positionAt(point) - offset); |
479 | |
480 | return isGrabbed; |
481 | } |
482 | |
483 | bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point, ulong timestamp) |
484 | { |
485 | if (pressPoint.isNull()) |
486 | return false; |
487 | if (!popupItem->keepMouseGrab() && !popupItem->keepTouchGrab()) { |
488 | velocityCalculator.reset(); |
489 | return QQuickPopupPrivate::handleRelease(item, point, timestamp); |
490 | } |
491 | |
492 | velocityCalculator.stopMeasuring(m_point2: point, timestamp); |
493 | |
494 | qreal velocity = 0; |
495 | if (edge == Qt::LeftEdge || edge == Qt::RightEdge) |
496 | velocity = velocityCalculator.velocity().x(); |
497 | else |
498 | velocity = velocityCalculator.velocity().y(); |
499 | |
500 | // the velocity is calculated so that swipes from left to right |
501 | // and top to bottom have positive velocity, and swipes from right |
502 | // to left and bottom to top have negative velocity. |
503 | // |
504 | // - top/left edge: positive velocity opens, negative velocity closes |
505 | // - bottom/right edge: negative velocity opens, positive velocity closes |
506 | // |
507 | // => invert the velocity for bottom and right edges, for the threshold comparison below |
508 | if (edge == Qt::RightEdge || edge == Qt::BottomEdge) |
509 | velocity = -velocity; |
510 | |
511 | if (position > 0.7 || velocity > openCloseVelocityThreshold) { |
512 | transitionManager.transitionEnter(); |
513 | } else if (position < 0.3 || velocity < -openCloseVelocityThreshold) { |
514 | transitionManager.transitionExit(); |
515 | } else { |
516 | switch (edge) { |
517 | case Qt::LeftEdge: |
518 | if (point.x() - pressPoint.x() > 0) |
519 | transitionManager.transitionEnter(); |
520 | else |
521 | transitionManager.transitionExit(); |
522 | break; |
523 | case Qt::RightEdge: |
524 | if (point.x() - pressPoint.x() < 0) |
525 | transitionManager.transitionEnter(); |
526 | else |
527 | transitionManager.transitionExit(); |
528 | break; |
529 | case Qt::TopEdge: |
530 | if (point.y() - pressPoint.y() > 0) |
531 | transitionManager.transitionEnter(); |
532 | else |
533 | transitionManager.transitionExit(); |
534 | break; |
535 | case Qt::BottomEdge: |
536 | if (point.y() - pressPoint.y() < 0) |
537 | transitionManager.transitionEnter(); |
538 | else |
539 | transitionManager.transitionExit(); |
540 | break; |
541 | } |
542 | } |
543 | |
544 | bool wasGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab(); |
545 | popupItem->setKeepMouseGrab(false); |
546 | popupItem->setKeepTouchGrab(false); |
547 | |
548 | pressPoint = QPointF(); |
549 | touchId = -1; |
550 | |
551 | return wasGrabbed; |
552 | } |
553 | |
554 | void QQuickDrawerPrivate::handleUngrab() |
555 | { |
556 | QQuickPopupPrivate::handleUngrab(); |
557 | |
558 | velocityCalculator.reset(); |
559 | } |
560 | |
561 | static QList<QQuickStateAction> prepareTransition(QQuickDrawer *drawer, QQuickTransition *transition, qreal to) |
562 | { |
563 | QList<QQuickStateAction> actions; |
564 | if (!transition || !QQuickPopupPrivate::get(popup: drawer)->window || !transition->enabled()) |
565 | return actions; |
566 | |
567 | qmlExecuteDeferred(transition); |
568 | |
569 | QQmlProperty defaultTarget(drawer, QLatin1String("position" )); |
570 | QQmlListProperty<QQuickAbstractAnimation> animations = transition->animations(); |
571 | int count = animations.count(&animations); |
572 | for (int i = 0; i < count; ++i) { |
573 | QQuickAbstractAnimation *anim = animations.at(&animations, i); |
574 | anim->setDefaultTarget(defaultTarget); |
575 | } |
576 | |
577 | actions << QQuickStateAction(drawer, QLatin1String("position" ), to); |
578 | return actions; |
579 | } |
580 | |
581 | bool QQuickDrawerPrivate::prepareEnterTransition() |
582 | { |
583 | Q_Q(QQuickDrawer); |
584 | enterActions = prepareTransition(drawer: q, transition: enter, to: 1.0); |
585 | return QQuickPopupPrivate::prepareEnterTransition(); |
586 | } |
587 | |
588 | bool QQuickDrawerPrivate::prepareExitTransition() |
589 | { |
590 | Q_Q(QQuickDrawer); |
591 | exitActions = prepareTransition(drawer: q, transition: exit, to: 0.0); |
592 | return QQuickPopupPrivate::prepareExitTransition(); |
593 | } |
594 | |
595 | bool QQuickDrawerPrivate::setEdge(Qt::Edge e) |
596 | { |
597 | Q_Q(QQuickDrawer); |
598 | switch (e) { |
599 | case Qt::LeftEdge: |
600 | case Qt::RightEdge: |
601 | allowVerticalMove = true; |
602 | allowVerticalResize = true; |
603 | allowHorizontalMove = false; |
604 | allowHorizontalResize = false; |
605 | break; |
606 | case Qt::TopEdge: |
607 | case Qt::BottomEdge: |
608 | allowVerticalMove = false; |
609 | allowVerticalResize = false; |
610 | allowHorizontalMove = true; |
611 | allowHorizontalResize = true; |
612 | break; |
613 | default: |
614 | qmlWarning(me: q) << "invalid edge value - valid values are: " |
615 | << "Qt.TopEdge, Qt.LeftEdge, Qt.RightEdge, Qt.BottomEdge" ; |
616 | return false; |
617 | } |
618 | |
619 | edge = e; |
620 | return true; |
621 | } |
622 | |
623 | QQuickDrawer::QQuickDrawer(QObject *parent) |
624 | : QQuickPopup(*(new QQuickDrawerPrivate), parent) |
625 | { |
626 | Q_D(QQuickDrawer); |
627 | d->dragMargin = QGuiApplication::styleHints()->startDragDistance(); |
628 | d->setEdge(Qt::LeftEdge); |
629 | |
630 | setFocus(true); |
631 | setModal(true); |
632 | setFiltersChildMouseEvents(true); |
633 | setClosePolicy(CloseOnEscape | CloseOnReleaseOutside); |
634 | } |
635 | |
636 | /*! |
637 | \qmlproperty enumeration QtQuick.Controls::Drawer::edge |
638 | |
639 | This property holds the edge of the window at which the drawer will |
640 | open from. The acceptable values are: |
641 | |
642 | \value Qt.TopEdge The top edge of the window. |
643 | \value Qt.LeftEdge The left edge of the window (default). |
644 | \value Qt.RightEdge The right edge of the window. |
645 | \value Qt.BottomEdge The bottom edge of the window. |
646 | */ |
647 | Qt::Edge QQuickDrawer::edge() const |
648 | { |
649 | Q_D(const QQuickDrawer); |
650 | return d->edge; |
651 | } |
652 | |
653 | void QQuickDrawer::setEdge(Qt::Edge edge) |
654 | { |
655 | Q_D(QQuickDrawer); |
656 | if (d->edge == edge) |
657 | return; |
658 | |
659 | if (!d->setEdge(edge)) |
660 | return; |
661 | |
662 | if (isComponentComplete()) |
663 | d->reposition(); |
664 | emit edgeChanged(); |
665 | } |
666 | |
667 | /*! |
668 | \qmlproperty real QtQuick.Controls::Drawer::position |
669 | |
670 | This property holds the position of the drawer relative to its final |
671 | destination. That is, the position will be \c 0.0 when the drawer |
672 | is fully closed, and \c 1.0 when fully open. |
673 | */ |
674 | qreal QQuickDrawer::position() const |
675 | { |
676 | Q_D(const QQuickDrawer); |
677 | return d->position; |
678 | } |
679 | |
680 | void QQuickDrawer::setPosition(qreal position) |
681 | { |
682 | Q_D(QQuickDrawer); |
683 | position = qBound<qreal>(min: 0.0, val: position, max: 1.0); |
684 | if (qFuzzyCompare(p1: d->position, p2: position)) |
685 | return; |
686 | |
687 | d->position = position; |
688 | if (isComponentComplete()) |
689 | d->reposition(); |
690 | if (d->dimmer) |
691 | d->dimmer->setOpacity(position); |
692 | emit positionChanged(); |
693 | } |
694 | |
695 | /*! |
696 | \qmlproperty real QtQuick.Controls::Drawer::dragMargin |
697 | |
698 | This property holds the distance from the screen edge within which |
699 | drag actions will open the drawer. Setting the value to \c 0 or less |
700 | prevents opening the drawer by dragging. |
701 | |
702 | The default value is \c Qt.styleHints.startDragDistance. |
703 | |
704 | \sa interactive |
705 | */ |
706 | qreal QQuickDrawer::dragMargin() const |
707 | { |
708 | Q_D(const QQuickDrawer); |
709 | return d->dragMargin; |
710 | } |
711 | |
712 | void QQuickDrawer::setDragMargin(qreal margin) |
713 | { |
714 | Q_D(QQuickDrawer); |
715 | if (qFuzzyCompare(p1: d->dragMargin, p2: margin)) |
716 | return; |
717 | |
718 | d->dragMargin = margin; |
719 | emit dragMarginChanged(); |
720 | } |
721 | |
722 | void QQuickDrawer::resetDragMargin() |
723 | { |
724 | setDragMargin(QGuiApplication::styleHints()->startDragDistance()); |
725 | } |
726 | |
727 | /*! |
728 | \since QtQuick.Controls 2.2 (Qt 5.9) |
729 | \qmlproperty bool QtQuick.Controls::Drawer::interactive |
730 | |
731 | This property holds whether the drawer is interactive. A non-interactive |
732 | drawer does not react to swipes. |
733 | |
734 | The default value is \c true. |
735 | |
736 | \sa dragMargin |
737 | */ |
738 | bool QQuickDrawer::isInteractive() const |
739 | { |
740 | Q_D(const QQuickDrawer); |
741 | return d->interactive; |
742 | } |
743 | |
744 | void QQuickDrawer::setInteractive(bool interactive) |
745 | { |
746 | Q_D(QQuickDrawer); |
747 | if (d->interactive == interactive) |
748 | return; |
749 | |
750 | setFiltersChildMouseEvents(interactive); |
751 | d->interactive = interactive; |
752 | emit interactiveChanged(); |
753 | } |
754 | |
755 | bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event) |
756 | { |
757 | Q_D(QQuickDrawer); |
758 | switch (event->type()) { |
759 | #if QT_CONFIG(quicktemplates2_multitouch) |
760 | case QEvent::TouchUpdate: |
761 | return d->grabTouch(item: child, event: static_cast<QTouchEvent *>(event)); |
762 | #endif |
763 | case QEvent::MouseMove: |
764 | return d->grabMouse(item: child, event: static_cast<QMouseEvent *>(event)); |
765 | case QEvent::MouseButtonPress: |
766 | case QEvent::MouseButtonRelease: |
767 | return d->handleMouseEvent(item: child, event: static_cast<QMouseEvent *>(event)); |
768 | default: |
769 | break; |
770 | } |
771 | return false; |
772 | } |
773 | |
774 | void QQuickDrawer::mouseMoveEvent(QMouseEvent *event) |
775 | { |
776 | Q_D(QQuickDrawer); |
777 | d->grabMouse(item: d->popupItem, event); |
778 | } |
779 | |
780 | bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event) |
781 | { |
782 | Q_D(QQuickDrawer); |
783 | switch (event->type()) { |
784 | #if QT_CONFIG(quicktemplates2_multitouch) |
785 | case QEvent::TouchUpdate: |
786 | return d->grabTouch(item, event: static_cast<QTouchEvent *>(event)); |
787 | #endif |
788 | case QEvent::MouseMove: |
789 | return d->grabMouse(item, event: static_cast<QMouseEvent *>(event)); |
790 | default: |
791 | break; |
792 | } |
793 | return QQuickPopup::overlayEvent(item, event); |
794 | } |
795 | |
796 | #if QT_CONFIG(quicktemplates2_multitouch) |
797 | void QQuickDrawer::touchEvent(QTouchEvent *event) |
798 | { |
799 | Q_D(QQuickDrawer); |
800 | d->grabTouch(item: d->popupItem, event); |
801 | } |
802 | #endif |
803 | |
804 | void QQuickDrawer::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
805 | { |
806 | Q_D(QQuickDrawer); |
807 | QQuickPopup::geometryChanged(newGeometry, oldGeometry); |
808 | d->resizeOverlay(); |
809 | } |
810 | |
811 | QT_END_NAMESPACE |
812 | |