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>
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
146class QQuickDrawerPositioner :
public QQuickPopupPositioner
149 QQuickDrawerPositioner(QQuickDrawer *drawer) : QQuickPopupPositioner(drawer) { }
151 void reposition() override;
154qreal QQuickDrawerPrivate::offsetAt(
const QPointF &point)
const
156 qreal offset = positionAt(point) - position;
159 if (offset > 0 && position > 0 && !contains(point))
165qreal QQuickDrawerPrivate::positionAt(
const QPointF &point)
const
167 Q_Q(
const QQuickDrawer);
168 QQuickWindow *window = q->window();
172 auto size = QSizeF(q->width(), q->height());
174 switch (effectiveEdge()) {
176 if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
178 return point.y() / size.height();
180 if (edge == Qt::TopEdge || edge == Qt::BottomEdge)
182 return point.x() / size.width();
184 if (edge == Qt::TopEdge || edge == Qt::BottomEdge)
186 return (window->width() - point.x()) / size.width();
188 if (edge == Qt::LeftEdge || edge == Qt::RightEdge)
190 return (window->height() - point.y()) / size.height();
196QQuickPopupPositioner *QQuickDrawerPrivate::getPositioner()
200 positioner =
new QQuickDrawerPositioner(q);
204void QQuickDrawerPositioner::reposition()
209 QQuickDrawer *drawer =
static_cast<QQuickDrawer*>(popup());
213 QQuickOverlay *overlay = QQuickOverlay::overlay(drawer->window(), drawer->parentItem());
217 const qreal position = drawer->position();
218 QQuickItem *popupItem = drawer->popupItem();
219 switch (drawer->edge()) {
221 popupItem->setX((position - 1.0) * popupItem->width());
224 popupItem->setX(overlay->width() - position * popupItem->width());
227 popupItem->setY((position - 1.0) * popupItem->height());
230 popupItem->setY(overlay->height() - position * popupItem->height());
234 QQuickPopupPositioner::reposition();
237void QQuickDrawerPrivate::showDimmer()
242void QQuickDrawerPrivate::hideDimmer()
247void QQuickDrawerPrivate::resizeDimmer()
249 if (!dimmer || !window)
252 const QQuickOverlay *overlay = QQuickOverlay::overlay(window, parentItem);
254 QRectF geometry(0, 0, overlay ? overlay->width() : 0, overlay ? overlay->height() : 0);
256 if (edge == Qt::LeftEdge || edge == Qt::RightEdge) {
257 geometry.setY(popupItem->y());
258 geometry.setHeight(popupItem->height());
260 geometry.setX(popupItem->x());
261 geometry.setWidth(popupItem->width());
264 dimmer->setPosition(geometry.topLeft());
265 dimmer->setSize(geometry.size());
268bool QQuickDrawerPrivate::isWithinDragMargin(
const QPointF &pos)
const
270 Q_Q(
const QQuickDrawer);
271 switch (effectiveEdge()) {
273 return pos.x() <= q->dragMargin();
275 return pos.x() >= q->window()->width() - q->dragMargin();
277 return pos.y() <= q->dragMargin();
279 return pos.y() >= q->window()->height() - q->dragMargin();
287bool QQuickDrawerPrivate::startDrag(QEvent *event)
289 delayedEnterTransition =
false;
290 if (!window || !interactive || dragMargin < 0.0 || qFuzzyIsNull(dragMargin))
293 switch (event->type()) {
294 case QEvent::MouseButtonPress:
295 if (QMouseEvent *mouseEvent =
static_cast<QMouseEvent *>(event); isWithinDragMargin(mouseEvent->scenePosition())) {
298 delayedEnterTransition =
true;
299 mouseEvent->addPassiveGrabber(mouseEvent->point(0), popupItem);
300 handleMouseEvent(window->contentItem(), mouseEvent);
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);
330 return item->keepMouseGrab() || item->keepTouchGrab();
333bool QQuickDrawerPrivate::grabMouse(QQuickItem *item, QMouseEvent *event)
336 handleMouseEvent(item, event);
338 if (!window || !interactive || keepGrab(popupItem) || keepGrab(item))
341 const QPointF movePoint = event->scenePosition();
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;
357 overThreshold = yOverThreshold && !xOverThreshold;
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;
365 overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin;
369 if (delayedEnterTransition) {
370 prepareEnterTransition();
372 delayedEnterTransition =
false;
375 popupItem->grabMouse();
376 popupItem->setKeepMouseGrab(
true);
377 offset = offsetAt(movePoint);
380 return overThreshold;
383#if QT_CONFIG(quicktemplates2_multitouch)
384bool QQuickDrawerPrivate::grabTouch(QQuickItem *item, QTouchEvent *event)
387 bool handled = handleTouchEvent(item, event);
389 if (!window || !interactive || keepGrab(popupItem) || keepGrab(item) || !event->touchPointStates().testFlag(QEventPoint::Updated))
392 bool overThreshold =
false;
393 for (
const QTouchEvent::TouchPoint &point : event->points()) {
394 if (!acceptTouch(point) || point.state() != QEventPoint::Updated)
397 const QPointF movePoint = point.scenePosition();
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;
412 overThreshold = yOverThreshold && !xOverThreshold;
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;
420 overThreshold = qAbs(movePoint.y() - q->height()) < dragMargin;
424 if (delayedEnterTransition) {
425 prepareEnterTransition();
427 delayedEnterTransition =
false;
429 event->setExclusiveGrabber(point, popupItem);
430 popupItem->setKeepTouchGrab(
true);
431 offset = offsetAt(movePoint);
435 return overThreshold;
448bool QQuickDrawerPrivate::blockInput(QQuickItem *item,
const QPointF &point)
const
451 if (popupItem->keepMouseGrab() || popupItem->keepTouchGrab())
455 if (popupItem->isAncestorOf(item))
459 if (dimmer && !dimmer->contains(dimmer->mapFromScene(point)))
463 if (isWithinDragMargin(point))
470bool QQuickDrawerPrivate::handlePress(QQuickItem *item,
const QPointF &point, ulong timestamp)
473 velocityCalculator.startMeasuring(point, timestamp);
475 return QQuickPopupPrivate::handlePress(item, point, timestamp)
476 || (interactive && popupItem == item);
479bool QQuickDrawerPrivate::handleMove(QQuickItem *item,
const QPointF &point, ulong timestamp)
482 if (!QQuickPopupPrivate::handleMove(item, point, timestamp))
486 if (qFuzzyCompare(position, qreal(1.0)) && !contains(point))
489 bool isGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab();
491 q->setPosition(positionAt(point) - offset);
496bool QQuickDrawerPrivate::handleRelease(QQuickItem *item,
const QPointF &point, ulong timestamp)
498 auto cleanup = qScopeGuard([
this] {
499 popupItem->setKeepMouseGrab(
false);
500 popupItem->setKeepTouchGrab(
false);
501 pressPoint = QPointF();
504 if (pressPoint.isNull())
506 if (!popupItem->keepMouseGrab() && !popupItem->keepTouchGrab()) {
507 velocityCalculator.reset();
508 return QQuickPopupPrivate::handleRelease(item, point, timestamp);
511 velocityCalculator.stopMeasuring(point, timestamp);
512 Qt::Edge effEdge = effectiveEdge();
514 if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
515 velocity = velocityCalculator.velocity().x();
517 velocity = velocityCalculator.velocity().y();
527 if (effEdge == Qt::RightEdge || effEdge == Qt::BottomEdge)
528 velocity = -velocity;
530 if (position > 0.7 || velocity > openCloseVelocityThreshold) {
531 transitionManager.transitionEnter();
532 }
else if (position < 0.3 || velocity < -openCloseVelocityThreshold) {
533 transitionManager.transitionExit();
537 if (point.x() - pressPoint.x() > 0)
538 transitionManager.transitionEnter();
540 transitionManager.transitionExit();
543 if (point.x() - pressPoint.x() < 0)
544 transitionManager.transitionEnter();
546 transitionManager.transitionExit();
549 if (point.y() - pressPoint.y() > 0)
550 transitionManager.transitionEnter();
552 transitionManager.transitionExit();
555 if (point.y() - pressPoint.y() < 0)
556 transitionManager.transitionEnter();
558 transitionManager.transitionExit();
564 return popupItem->keepMouseGrab() || popupItem->keepTouchGrab();
567void QQuickDrawerPrivate::handleUngrab()
569 QQuickPopupPrivate::handleUngrab();
571 velocityCalculator.reset();
576 QList<QQuickStateAction> actions;
577 if (!transition || !QQuickPopupPrivate::get(drawer)->window || !transition->enabled())
580 qmlExecuteDeferred(transition);
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);
590 actions << QQuickStateAction(drawer, QLatin1String(
"position"), to);
594bool QQuickDrawerPrivate::prepareEnterTransition()
597 enterActions = prepareTransition(q, enter, 1.0);
598 return QQuickPopupPrivate::prepareEnterTransition();
601bool QQuickDrawerPrivate::prepareExitTransition()
604 exitActions = prepareTransition(q, exit, 0.0);
605 return QQuickPopupPrivate::prepareExitTransition();
608QQuickPopup::PopupType QQuickDrawerPrivate::resolvedPopupType()
const
611 return QQuickPopup::Item;
614bool QQuickDrawerPrivate::setEdge(Qt::Edge e)
620 allowVerticalMove =
true;
621 allowVerticalResize =
true;
622 allowHorizontalMove =
false;
623 allowHorizontalResize =
false;
627 allowVerticalMove =
false;
628 allowVerticalResize =
false;
629 allowHorizontalMove =
true;
630 allowHorizontalResize =
true;
633 qmlWarning(q) <<
"invalid edge value - valid values are: "
634 <<
"Qt.TopEdge, Qt.LeftEdge, Qt.RightEdge, Qt.BottomEdge";
642QQuickDrawer::QQuickDrawer(QObject *parent)
643 : QQuickPopup(*(
new QQuickDrawerPrivate), parent)
646 d->dragMargin = QGuiApplication::styleHints()->startDragDistance();
647 d->setEdge(Qt::LeftEdge);
652 QQuickItemPrivate::get(d->popupItem)->isTabFence = isModal();
653 connect(
this, &QQuickPopup::modalChanged,
this, [
this] {
654 QQuickItemPrivate::get(d_func()->popupItem)->isTabFence = isModal();
657 setFiltersChildMouseEvents(
true);
658 setClosePolicy(CloseOnEscape | CloseOnReleaseOutside);
662
663
664
665
666
667
668
669
670
671
672Qt::Edge QQuickDrawer::edge()
const
674 Q_D(
const QQuickDrawer);
678Qt::Edge QQuickDrawerPrivate::effectiveEdge()
const
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;
687 realEdge = clockwise ? Qt::TopEdge : Qt::BottomEdge;
690 realEdge = clockwise ? Qt::RightEdge : Qt::LeftEdge;
693 realEdge = clockwise ? Qt::BottomEdge : Qt::TopEdge;
696 realEdge = clockwise ? Qt::LeftEdge : Qt::RightEdge;
703void QQuickDrawer::setEdge(Qt::Edge edge)
709 if (!d->setEdge(edge))
712 if (isComponentComplete())
718
719
720
721
722
723
724qreal QQuickDrawer::position()
const
726 Q_D(
const QQuickDrawer);
730void QQuickDrawer::setPosition(qreal position)
733 position = std::clamp(position, qreal(0.0), qreal(1.0));
734 if (qFuzzyCompare(d->position, position))
737 d->position = position;
738 if (isComponentComplete())
741 d->dimmer->setOpacity(position);
742 emit positionChanged();
746
747
748
749
750
751
752
753
754
755
756qreal QQuickDrawer::dragMargin()
const
758 Q_D(
const QQuickDrawer);
759 return d->dragMargin;
762void QQuickDrawer::setDragMargin(qreal margin)
765 if (qFuzzyCompare(d->dragMargin, margin))
768 d->dragMargin = margin;
769 emit dragMarginChanged();
772void QQuickDrawer::resetDragMargin()
774 setDragMargin(QGuiApplication::styleHints()->startDragDistance());
778
779
780
781
782
783
784
785
786
787
788bool QQuickDrawer::isInteractive()
const
790 Q_D(
const QQuickDrawer);
791 return d->interactive;
794void QQuickDrawer::setInteractive(
bool interactive)
797 if (d->interactive == interactive)
800 setFiltersChildMouseEvents(interactive);
801 d->interactive = interactive;
802 emit interactiveChanged();
805bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event)
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));
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));
827void QQuickDrawer::mouseMoveEvent(QMouseEvent *event)
830 d->grabMouse(d->popupItem, event);
833bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event)
836 switch (event->type()) {
837#if QT_CONFIG(quicktemplates2_multitouch)
838 case QEvent::TouchUpdate:
839 return d->grabTouch(item,
static_cast<QTouchEvent *>(event));
841 case QEvent::MouseMove:
842 return d->grabMouse(item,
static_cast<QMouseEvent *>(event));
846 return QQuickPopup::overlayEvent(item, event);
849#if QT_CONFIG(quicktemplates2_multitouch)
850void QQuickDrawer::touchEvent(QTouchEvent *event)
853 d->grabTouch(d->popupItem, event);
857void QQuickDrawer::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
860 QQuickPopup::geometryChange(newGeometry, oldGeometry);
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