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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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