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)
474 return QQuickPopupPrivate::handlePress(item, point, timestamp)
475 || (interactive && popupItem == item);
478bool QQuickDrawerPrivate::handleMove(QQuickItem *item,
const QPointF &point, ulong timestamp)
481 if (!QQuickPopupPrivate::handleMove(item, point, timestamp))
485 if (qFuzzyCompare(position, qreal(1.0)) && !contains(point))
488 bool isGrabbed = popupItem->keepMouseGrab() || popupItem->keepTouchGrab();
490 q->setPosition(positionAt(point) - offset);
495bool QQuickDrawerPrivate::handleRelease(QQuickItem *item,
const QEventPoint &point)
497 auto cleanup = qScopeGuard([
this] {
498 popupItem->setKeepMouseGrab(
false);
499 popupItem->setKeepTouchGrab(
false);
500 pressPoint = QPointF();
503 if (pressPoint.isNull())
505 if (!popupItem->keepMouseGrab() && !popupItem->keepTouchGrab())
506 return QQuickPopupPrivate::handleRelease(item, point);
508 const QPointF scenePosition = point.scenePosition();
509 Qt::Edge effEdge = effectiveEdge();
513 const qreal elapsed = (point.timestamp() - point.pressTimestamp()) / qreal(1000);
514 const QPointF delta = scenePosition - point.scenePressPosition();
516 if (!qFuzzyIsNull(elapsed)) {
517 if (effEdge == Qt::LeftEdge || effEdge == Qt::RightEdge)
518 velocity = delta.x() / elapsed;
520 velocity = delta.y() / elapsed;
531 if (effEdge == Qt::RightEdge || effEdge == Qt::BottomEdge)
532 velocity = -velocity;
534 if (position > 0.7 || velocity > openCloseVelocityThreshold) {
535 transitionManager.transitionEnter();
536 }
else if (position < 0.3 || velocity < -openCloseVelocityThreshold) {
537 transitionManager.transitionExit();
541 if (scenePosition.x() - pressPoint.x() > 0)
542 transitionManager.transitionEnter();
544 transitionManager.transitionExit();
547 if (scenePosition.x() - pressPoint.x() < 0)
548 transitionManager.transitionEnter();
550 transitionManager.transitionExit();
553 if (scenePosition.y() - pressPoint.y() > 0)
554 transitionManager.transitionEnter();
556 transitionManager.transitionExit();
559 if (scenePosition.y() - pressPoint.y() < 0)
560 transitionManager.transitionEnter();
562 transitionManager.transitionExit();
568 return popupItem->keepMouseGrab() || popupItem->keepTouchGrab();
571void QQuickDrawerPrivate::handleUngrab()
573 QQuickPopupPrivate::handleUngrab();
578 QList<QQuickStateAction> actions;
579 if (!transition || !QQuickPopupPrivate::get(drawer)->window || !transition->enabled())
582 qmlExecuteDeferred(transition);
584 QQmlProperty defaultTarget(drawer, QLatin1String(
"position"));
585 QQmlListProperty<QQuickAbstractAnimation> animations = transition->animations();
586 int count = animations.count(&animations);
587 for (
int i = 0; i < count; ++i) {
588 QQuickAbstractAnimation *anim = animations.at(&animations, i);
589 anim->setDefaultTarget(defaultTarget);
592 actions << QQuickStateAction(drawer, QLatin1String(
"position"), to);
596bool QQuickDrawerPrivate::prepareEnterTransition()
599 enterActions = prepareTransition(q, enter, 1.0);
600 return QQuickPopupPrivate::prepareEnterTransition();
603bool QQuickDrawerPrivate::prepareExitTransition()
606 exitActions = prepareTransition(q, exit, 0.0);
607 return QQuickPopupPrivate::prepareExitTransition();
610QQuickPopup::PopupType QQuickDrawerPrivate::resolvedPopupType()
const
613 return QQuickPopup::Item;
616bool QQuickDrawerPrivate::setEdge(Qt::Edge e)
622 allowVerticalMove =
true;
623 allowVerticalResize =
true;
624 allowHorizontalMove =
false;
625 allowHorizontalResize =
false;
629 allowVerticalMove =
false;
630 allowVerticalResize =
false;
631 allowHorizontalMove =
true;
632 allowHorizontalResize =
true;
635 qmlWarning(q) <<
"invalid edge value - valid values are: "
636 <<
"Qt.TopEdge, Qt.LeftEdge, Qt.RightEdge, Qt.BottomEdge";
644QQuickDrawer::QQuickDrawer(QObject *parent)
645 : QQuickPopup(*(
new QQuickDrawerPrivate), parent)
648 d->dragMargin = QGuiApplication::styleHints()->startDragDistance();
649 d->setEdge(Qt::LeftEdge);
654 QQuickItemPrivate::get(d->popupItem)->isTabFence = isModal();
655 connect(
this, &QQuickPopup::modalChanged,
this, [
this] {
656 QQuickItemPrivate::get(d_func()->popupItem)->isTabFence = isModal();
659 setFiltersChildMouseEvents(
true);
660 setClosePolicy(CloseOnEscape | CloseOnReleaseOutside);
664
665
666
667
668
669
670
671
672
673
674Qt::Edge QQuickDrawer::edge()
const
676 Q_D(
const QQuickDrawer);
680Qt::Edge QQuickDrawerPrivate::effectiveEdge()
const
682 auto realEdge = edge;
683 qreal rotation = window->contentItem()->rotation();
684 const bool clockwise = rotation > 0;
685 while (qAbs(rotation) >= 90) {
686 rotation -= clockwise ? 90 : -90;
689 realEdge = clockwise ? Qt::TopEdge : Qt::BottomEdge;
692 realEdge = clockwise ? Qt::RightEdge : Qt::LeftEdge;
695 realEdge = clockwise ? Qt::BottomEdge : Qt::TopEdge;
698 realEdge = clockwise ? Qt::LeftEdge : Qt::RightEdge;
705void QQuickDrawer::setEdge(Qt::Edge edge)
711 if (!d->setEdge(edge))
714 if (isComponentComplete())
720
721
722
723
724
725
726qreal QQuickDrawer::position()
const
728 Q_D(
const QQuickDrawer);
732void QQuickDrawer::setPosition(qreal position)
735 position = std::clamp(position, qreal(0.0), qreal(1.0));
736 if (qFuzzyCompare(d->position, position))
739 d->position = position;
740 if (isComponentComplete())
743 d->dimmer->setOpacity(position);
744 emit positionChanged();
748
749
750
751
752
753
754
755
756
757
758qreal QQuickDrawer::dragMargin()
const
760 Q_D(
const QQuickDrawer);
761 return d->dragMargin;
764void QQuickDrawer::setDragMargin(qreal margin)
767 if (qFuzzyCompare(d->dragMargin, margin))
770 d->dragMargin = margin;
771 emit dragMarginChanged();
774void QQuickDrawer::resetDragMargin()
776 setDragMargin(QGuiApplication::styleHints()->startDragDistance());
780
781
782
783
784
785
786
787
788
789
790bool QQuickDrawer::isInteractive()
const
792 Q_D(
const QQuickDrawer);
793 return d->interactive;
796void QQuickDrawer::setInteractive(
bool interactive)
799 if (d->interactive == interactive)
802 setFiltersChildMouseEvents(interactive);
803 d->interactive = interactive;
804 emit interactiveChanged();
807bool QQuickDrawer::childMouseEventFilter(QQuickItem *child, QEvent *event)
810 switch (event->type()) {
811#if QT_CONFIG(quicktemplates2_multitouch)
812 case QEvent::TouchUpdate:
813 return d->grabTouch(child,
static_cast<QTouchEvent *>(event));
814 case QEvent::TouchBegin:
815 case QEvent::TouchEnd:
816 return d->handleTouchEvent(child,
static_cast<QTouchEvent *>(event));
818 case QEvent::MouseMove:
819 return d->grabMouse(child,
static_cast<QMouseEvent *>(event));
820 case QEvent::MouseButtonPress:
821 case QEvent::MouseButtonRelease:
822 return d->handleMouseEvent(child,
static_cast<QMouseEvent *>(event));
829void QQuickDrawer::mouseMoveEvent(QMouseEvent *event)
832 d->grabMouse(d->popupItem, event);
835bool QQuickDrawer::overlayEvent(QQuickItem *item, QEvent *event)
838 switch (event->type()) {
839#if QT_CONFIG(quicktemplates2_multitouch)
840 case QEvent::TouchUpdate:
841 return d->grabTouch(item,
static_cast<QTouchEvent *>(event));
843 case QEvent::MouseMove:
844 return d->grabMouse(item,
static_cast<QMouseEvent *>(event));
848 return QQuickPopup::overlayEvent(item, event);
851#if QT_CONFIG(quicktemplates2_multitouch)
852void QQuickDrawer::touchEvent(QTouchEvent *event)
855 d->grabTouch(d->popupItem, event);
859void QQuickDrawer::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
862 QQuickPopup::geometryChange(newGeometry, oldGeometry);
868#include "moc_qquickdrawer_p.cpp"
static bool keepGrab(QQuickItem *item)
static QList< QQuickStateAction > prepareTransition(QQuickDrawer *drawer, QQuickTransition *transition, qreal to)
static const qreal openCloseVelocityThreshold