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