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

source code of qtdeclarative/src/quicktemplates/qquickdrawer.cpp