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 velocityCalculator.startMeasuring(point, timestamp);
474
475 return QQuickPopupPrivate::handlePress(item, point, timestamp)
476 || (interactive && popupItem == item);
477}
478
479bool QQuickDrawerPrivate::handleMove(QQuickItem *item, const QPointF &point, ulong timestamp)
480{
481 Q_Q(QQuickDrawer);
482 if (!QQuickPopupPrivate::handleMove(item, point, timestamp))
483 return false;
484
485 // limit/reset the offset to the edge of the drawer when pushed from the outside
486 if (qFuzzyCompare(position, qreal(1.0)) && !contains(point))
487 offset = 0;
488
489 bool isGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab();
490 if (isGrabbed)
491 q->setPosition(positionAt(point) - offset);
492
493 return isGrabbed;
494}
495
496bool QQuickDrawerPrivate::handleRelease(QQuickItem *item, const QPointF &point, ulong timestamp)
497{
498 auto cleanup = qScopeGuard([this] {
499 popupItem->setKeepMouseGrab(false);
500 popupItem->setKeepTouchGrab(false);
501 pressPoint = QPointF();
502 touchId = -1;
503 });
504 if (pressPoint.isNull())
505 return false;
506 if (!popupItem->keepMouseGrab() && !popupItem->keepTouchGrab()) {
507 velocityCalculator.reset();
508 return QQuickPopupPrivate::handleRelease(item, point, timestamp);
509 }
510
511 velocityCalculator.stopMeasuring(point, timestamp);
512 Qt::Edge effEdge = effectiveEdge();
513 qreal velocity = 0;
514 if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
515 velocity = velocityCalculator.velocity().x();
516 else
517 velocity = velocityCalculator.velocity().y();
518
519 // the velocity is calculated so that swipes from left to right
520 // and top to bottom have positive velocity, and swipes from right
521 // to left and bottom to top have negative velocity.
522 //
523 // - top/left edge: positive velocity opens, negative velocity closes
524 // - bottom/right edge: negative velocity opens, positive velocity closes
525 //
526 // => invert the velocity for bottom and right edges, for the threshold comparison below
527 if (effEdge == Qt::RightEdge || effEdge == Qt::BottomEdge)
528 velocity = -velocity;
529
530 if (position > 0.7 || velocity > openCloseVelocityThreshold) {
531 transitionManager.transitionEnter();
532 } else if (position < 0.3 || velocity < -openCloseVelocityThreshold) {
533 transitionManager.transitionExit();
534 } else {
535 switch (effEdge) {
536 case Qt::LeftEdge:
537 if (point.x() - pressPoint.x() > 0)
538 transitionManager.transitionEnter();
539 else
540 transitionManager.transitionExit();
541 break;
542 case Qt::RightEdge:
543 if (point.x() - pressPoint.x() < 0)
544 transitionManager.transitionEnter();
545 else
546 transitionManager.transitionExit();
547 break;
548 case Qt::TopEdge:
549 if (point.y() - pressPoint.y() > 0)
550 transitionManager.transitionEnter();
551 else
552 transitionManager.transitionExit();
553 break;
554 case Qt::BottomEdge:
555 if (point.y() - pressPoint.y() < 0)
556 transitionManager.transitionEnter();
557 else
558 transitionManager.transitionExit();
559 break;
560 }
561 }
562
563 // the cleanup() lambda will run before return
564 return popupItem->keepMouseGrab() || popupItem->keepTouchGrab();
565}
566
567void QQuickDrawerPrivate::handleUngrab()
568{
569 QQuickPopupPrivate::handleUngrab();
570
571 velocityCalculator.reset();
572}
573
574static QList<QQuickStateAction> prepareTransition(QQuickDrawer *drawer, QQuickTransition *transition, qreal to)
575{
576 QList<QQuickStateAction> actions;
577 if (!transition || !QQuickPopupPrivate::get(drawer)->window || !transition->enabled())
578 return actions;
579
580 qmlExecuteDeferred(transition);
581
582 QQmlProperty defaultTarget(drawer, QLatin1String("position"));
583 QQmlListProperty<QQuickAbstractAnimation> animations = transition->animations();
584 int count = animations.count(&animations);
585 for (int i = 0; i < count; ++i) {
586 QQuickAbstractAnimation *anim = animations.at(&animations, i);
587 anim->setDefaultTarget(defaultTarget);
588 }
589
590 actions << QQuickStateAction(drawer, QLatin1String("position"), to);
591 return actions;
592}
593
594bool QQuickDrawerPrivate::prepareEnterTransition()
595{
596 Q_Q(QQuickDrawer);
597 enterActions = prepareTransition(q, enter, 1.0);
598 return QQuickPopupPrivate::prepareEnterTransition();
599}
600
601bool QQuickDrawerPrivate::prepareExitTransition()
602{
603 Q_Q(QQuickDrawer);
604 exitActions = prepareTransition(q, exit, 0.0);
605 return QQuickPopupPrivate::prepareExitTransition();
606}
607
608QQuickPopup::PopupType QQuickDrawerPrivate::resolvedPopupType() const
609{
610 // For now, a drawer will always be shown in-scene
611 return QQuickPopup::Item;
612}
613
614bool QQuickDrawerPrivate::setEdge(Qt::Edge e)
615{
616 Q_Q(QQuickDrawer);
617 switch (e) {
618 case Qt::LeftEdge:
619 case Qt::RightEdge:
620 allowVerticalMove = true;
621 allowVerticalResize = true;
622 allowHorizontalMove = false;
623 allowHorizontalResize = false;
624 break;
625 case Qt::TopEdge:
626 case Qt::BottomEdge:
627 allowVerticalMove = false;
628 allowVerticalResize = false;
629 allowHorizontalMove = true;
630 allowHorizontalResize = true;
631 break;
632 default:
633 qmlWarning(q) << "invalid edge value - valid values are: "
634 << "Qt.TopEdge, Qt.LeftEdge, Qt.RightEdge, Qt.BottomEdge";
635 return false;
636 }
637
638 edge = e;
639 return true;
640}
641
642QQuickDrawer::QQuickDrawer(QObject *parent)
643 : QQuickPopup(*(new QQuickDrawerPrivate), parent)
644{
645 Q_D(QQuickDrawer);
646 d->dragMargin = QGuiApplication::styleHints()->startDragDistance();
647 d->setEdge(Qt::LeftEdge);
648
649 setFocus(true);
650 setModal(true);
651
652 QQuickItemPrivate::get(d->popupItem)->isTabFence = isModal();
653 connect(this, &QQuickPopup::modalChanged, this, [this] {
654 QQuickItemPrivate::get(d_func()->popupItem)->isTabFence = isModal();
655 });
656
657 setFiltersChildMouseEvents(true);
658 setClosePolicy(CloseOnEscape | CloseOnReleaseOutside);
659}
660
661/*!
662 \qmlproperty enumeration QtQuick.Controls::Drawer::edge
663
664 This property holds the edge of the window at which the drawer will
665 open from. The acceptable values are:
666
667 \value Qt.TopEdge The top edge of the window.
668 \value Qt.LeftEdge The left edge of the window (default).
669 \value Qt.RightEdge The right edge of the window.
670 \value Qt.BottomEdge The bottom edge of the window.
671*/
672Qt::Edge QQuickDrawer::edge() const
673{
674 Q_D(const QQuickDrawer);
675 return d->edge;
676}
677
678Qt::Edge QQuickDrawerPrivate::effectiveEdge() const
679{
680 auto realEdge = edge;
681 qreal rotation = window->contentItem()->rotation();
682 const bool clockwise = rotation > 0;
683 while (qAbs(rotation) >= 90) {
684 rotation -= clockwise ? 90 : -90;
685 switch (realEdge) {
686 case Qt::LeftEdge:
687 realEdge = clockwise ? Qt::TopEdge : Qt::BottomEdge;
688 break;
689 case Qt::TopEdge:
690 realEdge = clockwise ? Qt::RightEdge : Qt::LeftEdge;
691 break;
692 case Qt::RightEdge:
693 realEdge = clockwise ? Qt::BottomEdge : Qt::TopEdge;
694 break;
695 case Qt::BottomEdge:
696 realEdge = clockwise ? Qt::LeftEdge : Qt::RightEdge;
697 break;
698 }
699 }
700 return realEdge;
701}
702
703void QQuickDrawer::setEdge(Qt::Edge edge)
704{
705 Q_D(QQuickDrawer);
706 if (d->edge == edge)
707 return;
708
709 if (!d->setEdge(edge))
710 return;
711
712 if (isComponentComplete())
713 d->reposition();
714 emit edgeChanged();
715}
716
717/*!
718 \qmlproperty real QtQuick.Controls::Drawer::position
719
720 This property holds the position of the drawer relative to its final
721 destination. That is, the position will be \c 0.0 when the drawer
722 is fully closed, and \c 1.0 when fully open.
723*/
724qreal QQuickDrawer::position() const
725{
726 Q_D(const QQuickDrawer);
727 return d->position;
728}
729
730void QQuickDrawer::setPosition(qreal position)
731{
732 Q_D(QQuickDrawer);
733 position = std::clamp(position, qreal(0.0), qreal(1.0));
734 if (qFuzzyCompare(d->position, position))
735 return;
736
737 d->position = position;
738 if (isComponentComplete())
739 d->reposition();
740 if (d->dimmer)
741 d->dimmer->setOpacity(position);
742 emit positionChanged();
743}
744
745/*!
746 \qmlproperty real QtQuick.Controls::Drawer::dragMargin
747
748 This property holds the distance from the screen edge within which
749 drag actions will open the drawer. Setting the value to \c 0 or less
750 prevents opening the drawer by dragging.
751
752 The default value is \c Application.styleHints.startDragDistance.
753
754 \sa interactive
755*/
756qreal QQuickDrawer::dragMargin() const
757{
758 Q_D(const QQuickDrawer);
759 return d->dragMargin;
760}
761
762void QQuickDrawer::setDragMargin(qreal margin)
763{
764 Q_D(QQuickDrawer);
765 if (qFuzzyCompare(d->dragMargin, margin))
766 return;
767
768 d->dragMargin = margin;
769 emit dragMarginChanged();
770}
771
772void QQuickDrawer::resetDragMargin()
773{
774 setDragMargin(QGuiApplication::styleHints()->startDragDistance());
775}
776
777/*!
778 \since QtQuick.Controls 2.2 (Qt 5.9)
779 \qmlproperty bool QtQuick.Controls::Drawer::interactive
780
781 This property holds whether the drawer is interactive. A non-interactive
782 drawer does not react to swipes.
783
784 The default value is \c true.
785
786 \sa dragMargin
787*/
788bool QQuickDrawer::isInteractive() const
789{
790 Q_D(const QQuickDrawer);
791 return d->interactive;
792}
793
794void QQuickDrawer::setInteractive(bool interactive)
795{
796 Q_D(QQuickDrawer);
797 if (d->interactive == interactive)
798 return;
799
800 setFiltersChildMouseEvents(interactive);
801 d->interactive = interactive;
802 emit interactiveChanged();
803}
804
805bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event)
806{
807 Q_D(QQuickDrawer);
808 switch (event->type()) {
809#if QT_CONFIG(quicktemplates2_multitouch)
810 case QEvent::TouchUpdate:
811 return d->grabTouch(child, static_cast<QTouchEvent *>(event));
812 case QEvent::TouchBegin:
813 case QEvent::TouchEnd:
814 return d->handleTouchEvent(child, static_cast<QTouchEvent *>(event));
815#endif
816 case QEvent::MouseMove:
817 return d->grabMouse(child, static_cast<QMouseEvent *>(event));
818 case QEvent::MouseButtonPress:
819 case QEvent::MouseButtonRelease:
820 return d->handleMouseEvent(child, static_cast<QMouseEvent *>(event));
821 default:
822 break;
823 }
824 return false;
825}
826
827void QQuickDrawer::mouseMoveEvent(QMouseEvent *event)
828{
829 Q_D(QQuickDrawer);
830 d->grabMouse(d->popupItem, event);
831}
832
833bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event)
834{
835 Q_D(QQuickDrawer);
836 switch (event->type()) {
837#if QT_CONFIG(quicktemplates2_multitouch)
838 case QEvent::TouchUpdate:
839 return d->grabTouch(item, static_cast<QTouchEvent *>(event));
840#endif
841 case QEvent::MouseMove:
842 return d->grabMouse(item, static_cast<QMouseEvent *>(event));
843 default:
844 break;
845 }
846 return QQuickPopup::overlayEvent(item, event);
847}
848
849#if QT_CONFIG(quicktemplates2_multitouch)
850void QQuickDrawer::touchEvent(QTouchEvent *event)
851{
852 Q_D(QQuickDrawer);
853 d->grabTouch(d->popupItem, event);
854}
855#endif
856
857void QQuickDrawer::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
858{
859 Q_D(QQuickDrawer);
860 QQuickPopup::geometryChange(newGeometry, oldGeometry);
861 d->resizeDimmer();
862}
863
864QT_END_NAMESPACE
865
866#include "moc_qquickdrawer_p.cpp"
static bool keepGrab(QQuickItem *item)
static QList< QQuickStateAction > prepareTransition(QQuickDrawer *drawer, QQuickTransition *transition, qreal to)
static const qreal openCloseVelocityThreshold