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