8#include <QtCore/qmath.h>
9#include <QtGui/qevent.h>
10#include <QtGui/qguiapplication.h>
11#include <QtGui/qstylehints.h>
12#include <qpa/qplatformintegration.h>
13#include <qpa/qplatformnativeinterface.h>
14#include <private/qguiapplication_p.h>
21Q_STATIC_LOGGING_CATEGORY(lcPA,
"qt.quick.pincharea")
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
45
46
47
48
49
50
51
52
53
54
55
56
59
60
61
62
63
64
65
66
67
68
69
70
73
74
75
76
77
78
79
80
81
82
83
84
85
86
89
90
91
92
93
94
95
96
97
98
99
100
103
104
105
106
107
108
111
112
113
114
115
116
118QQuickPinch::QQuickPinch()
119 : m_target(
nullptr), m_minScale(1.0), m_maxScale(1.0)
120 , m_minRotation(0.0), m_maxRotation(0.0)
121 , m_axis(NoDrag), m_xmin(-FLT_MAX), m_xmax(FLT_MAX)
122 , m_ymin(-FLT_MAX), m_ymax(FLT_MAX), m_active(
false)
126QQuickPinchAreaPrivate::~QQuickPinchAreaPrivate()
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
179
180
181
182
183
184
185
186
187
188
191
192
193
194
195
196
197
198
201
202
203
204
205
206
207
208
209
210
211
212
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
241QQuickPinchArea::QQuickPinchArea(QQuickItem *parent)
242 : QQuickItem(*(
new QQuickPinchAreaPrivate), parent)
244 Q_D(QQuickPinchArea);
246 setAcceptTouchEvents(
true);
248 setAcceptHoverEvents(
true);
252QQuickPinchArea::~QQuickPinchArea()
256
257
258
259
260
261bool QQuickPinchArea::isEnabled()
const
263 Q_D(
const QQuickPinchArea);
267void QQuickPinchArea::setEnabled(
bool a)
269 Q_D(QQuickPinchArea);
270 if (a != d->enabled) {
272 emit enabledChanged();
276void QQuickPinchArea::touchEvent(QTouchEvent *event)
278 Q_D(QQuickPinchArea);
279 if (!d->enabled || !isVisible()) {
280 QQuickItem::touchEvent(event);
297 switch (event->type()) {
298 case QEvent::TouchBegin:
299 case QEvent::TouchUpdate:
300 d->touchPoints.clear();
301 for (
int i = 0; i < event->pointCount(); ++i) {
302 auto &tp = event->point(i);
303 if (tp.state() != QEventPoint::State::Released) {
304 d->touchPoints << tp;
308 updatePinch(event,
false);
310 case QEvent::TouchEnd:
313 case QEvent::TouchCancel:
317 QQuickItem::touchEvent(event);
321void QQuickPinchArea::clearPinch(QTouchEvent *event)
323 Q_D(QQuickPinchArea);
324 qCDebug(lcPA,
"clear: %" PRIdQSIZETYPE
" touchpoints", d->touchPoints.size());
325 d->touchPoints.clear();
328 QPointF pinchCenter = mapFromScene(d->sceneLastCenter);
329 QQuickPinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation);
330 pe.setStartCenter(d->pinchStartCenter);
331 pe.setPreviousCenter(pinchCenter);
332 pe.setPreviousAngle(d->pinchLastAngle);
333 pe.setPreviousScale(d->pinchLastScale);
334 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
335 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
336 pe.setPoint1(mapFromScene(d->lastPoint1));
337 pe.setPoint2(mapFromScene(d->lastPoint2));
338 emit pinchFinished(&pe);
339 if (d->pinch && d->pinch->target())
340 d->pinch->setActive(
false);
342 d->pinchStartDist = 0;
343 d->pinchActivated =
false;
344 d->initPinch =
false;
345 d->pinchRejected =
false;
348 for (
const auto &point : event->points()) {
349 if (event->exclusiveGrabber(point) ==
this)
350 event->setExclusiveGrabber(point,
nullptr);
353 setKeepTouchGrab(
false);
354 setKeepMouseGrab(
false);
357void QQuickPinchArea::cancelPinch(QTouchEvent *event)
359 Q_D(QQuickPinchArea);
360 qCDebug(lcPA,
"cancel: %" PRIdQSIZETYPE
" touchpoints", d->touchPoints.size());
361 d->touchPoints.clear();
364 QPointF pinchCenter = mapFromScene(d->sceneLastCenter);
365 QQuickPinchEvent pe(d->pinchStartCenter, d->pinchStartScale, d->pinchStartAngle, d->pinchStartRotation);
366 pe.setStartCenter(d->pinchStartCenter);
367 pe.setPreviousCenter(pinchCenter);
368 pe.setPreviousAngle(d->pinchLastAngle);
369 pe.setPreviousScale(d->pinchLastScale);
370 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
371 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
372 pe.setPoint1(pe.startPoint1());
373 pe.setPoint2(pe.startPoint2());
374 emit pinchFinished(&pe);
376 d->pinchLastScale = d->pinchStartScale;
377 d->sceneLastCenter = d->sceneStartCenter;
378 d->pinchLastAngle = d->pinchStartAngle;
379 d->lastPoint1 = pe.startPoint1();
380 d->lastPoint2 = pe.startPoint2();
383 if (d->pinch && d->pinch->target())
384 d->pinch->setActive(
false);
386 d->pinchStartDist = 0;
387 d->pinchActivated =
false;
388 d->initPinch =
false;
389 d->pinchRejected =
false;
391 for (
const auto &point : event->points()) {
392 if (event->exclusiveGrabber(point) ==
this)
393 event->setExclusiveGrabber(point,
nullptr);
395 setKeepTouchGrab(
false);
396 setKeepMouseGrab(
false);
399void QQuickPinchArea::updatePinch(QTouchEvent *event,
bool filtering)
401 Q_D(QQuickPinchArea);
403 if (d->touchPoints.size() < 2) {
405 setKeepTouchGrab(
false);
406 setKeepMouseGrab(
false);
413 if (filtering && !d->touchPoints.isEmpty() && event->exclusiveGrabber(d->touchPoints.first()) ==
this)
414 event->setExclusiveGrabber(d->touchPoints.first(),
nullptr);
417 if (d->touchPoints.size() == 0) {
420 QPointF pinchCenter = mapFromScene(d->sceneLastCenter);
421 QQuickPinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation);
422 pe.setStartCenter(d->pinchStartCenter);
423 pe.setPreviousCenter(pinchCenter);
424 pe.setPreviousAngle(d->pinchLastAngle);
425 pe.setPreviousScale(d->pinchLastScale);
426 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
427 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
428 pe.setPoint1(mapFromScene(d->lastPoint1));
429 pe.setPoint2(mapFromScene(d->lastPoint2));
430 setKeepTouchGrab(
false);
431 setKeepMouseGrab(
false);
432 emit pinchFinished(&pe);
433 d->pinchStartDist = 0;
434 d->pinchActivated =
false;
435 if (d->pinch && d->pinch->target())
436 d->pinch->setActive(
false);
438 d->initPinch =
false;
439 d->pinchRejected =
false;
443 QEventPoint touchPoint1 = d->touchPoints.at(0);
444 QEventPoint touchPoint2 = d->touchPoints.at(d->touchPoints.size() >= 2 ? 1 : 0);
446 if (touchPoint1.state() == QEventPoint::State::Pressed)
447 d->sceneStartPoint1 = touchPoint1.scenePosition();
449 if (touchPoint2.state() == QEventPoint::State::Pressed)
450 d->sceneStartPoint2 = touchPoint2.scenePosition();
452 qCDebug(lcPA) <<
"updating based on" << touchPoint1 << touchPoint2;
454 QRectF bounds = clipRect();
458 if (d->touchPoints.size() == 2
459 && (touchPoint1.state() == QEventPoint::State::Pressed || touchPoint2.state() == QEventPoint::State::Pressed) &&
460 bounds.contains(touchPoint1.position()) && bounds.contains(touchPoint2.position())) {
461 d->id1 = touchPoint1.id();
462 if (!d->pinchActivated)
463 qCDebug(lcPA,
"pinch activating");
464 d->pinchActivated =
true;
466 event->setExclusiveGrabber(touchPoint1,
this);
467 event->setExclusiveGrabber(touchPoint2,
this);
468 setKeepTouchGrab(
true);
469 setKeepMouseGrab(
true);
471 if (d->pinchActivated && !d->pinchRejected) {
472 const int dragThreshold = QGuiApplication::styleHints()->startDragDistance();
473 QPointF p1 = touchPoint1.scenePosition();
474 QPointF p2 = touchPoint2.scenePosition();
475 qreal dx = p1.x() - p2.x();
476 qreal dy = p1.y() - p2.y();
477 qreal dist = qSqrt(dx*dx + dy*dy);
478 QPointF sceneCenter = (p1 + p2)/2;
479 qreal angle = QLineF(p1, p2).angle();
480 if (d->touchPoints.size() == 1) {
482 if (d->id1 == touchPoint1.id())
483 sceneCenter = d->sceneLastCenter + touchPoint1.scenePosition() - d->lastPoint1;
485 sceneCenter = d->sceneLastCenter + touchPoint2.scenePosition() - d->lastPoint2;
486 angle = d->pinchLastAngle;
488 d->id1 = touchPoint1.id();
491 qCDebug(lcPA,
"pinch \u2316 %.1lf,%.1lf \u21e4%.1lf\u21e5 \u2220 %.1lf",
492 sceneCenter.x(), sceneCenter.y(), dist, angle);
493 if (!d->inPinch || d->initPinch) {
494 if (d->touchPoints.size() >= 2) {
497 d->pinchStartDist = dist;
498 d->initPinch =
false;
500 d->sceneStartCenter = sceneCenter;
501 d->sceneLastCenter = sceneCenter;
502 d->pinchStartCenter = mapFromScene(sceneCenter);
503 d->pinchStartAngle = angle;
504 d->pinchLastScale = 1.0;
505 d->pinchLastAngle = angle;
506 d->pinchRotation = 0.0;
509 if (qAbs(dist - d->pinchStartDist) >= dragThreshold ||
510 (pinch()->axis() != QQuickPinch::NoDrag &&
511 (qAbs(p1.x()-d->sceneStartPoint1.x()) >= dragThreshold
512 || qAbs(p1.y()-d->sceneStartPoint1.y()) >= dragThreshold
513 || qAbs(p2.x()-d->sceneStartPoint2.x()) >= dragThreshold
514 || qAbs(p2.y()-d->sceneStartPoint2.y()) >= dragThreshold))) {
515 QQuickPinchEvent pe(d->pinchStartCenter, 1.0, angle, 0.0);
516 d->pinchStartDist = dist;
517 pe.setStartCenter(d->pinchStartCenter);
518 pe.setPreviousCenter(d->pinchStartCenter);
519 pe.setPreviousAngle(d->pinchLastAngle);
520 pe.setPreviousScale(d->pinchLastScale);
521 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
522 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
523 pe.setPoint1(mapFromScene(d->lastPoint1));
524 pe.setPoint2(mapFromScene(d->lastPoint2));
525 pe.setPointCount(d->touchPoints.size());
526 emit pinchStarted(&pe);
529 event->setExclusiveGrabber(touchPoint1,
this);
530 event->setExclusiveGrabber(touchPoint2,
this);
531 setKeepTouchGrab(
true);
534 setKeepMouseGrab(
true);
536 if (d->pinch && d->pinch->target()) {
537 auto targetParent = pinch()->target()->parentItem();
538 d->pinchStartPos = targetParent ?
539 targetParent->mapToScene(pinch()->target()->position()) :
540 pinch()->target()->position();
541 d->pinchStartScale = d->pinch->target()->scale();
542 d->pinchStartRotation = d->pinch->target()->rotation();
543 d->pinch->setActive(
true);
546 d->pinchRejected =
true;
550 }
else if (d->pinchStartDist > 0) {
551 qreal scale = dist ? dist / d->pinchStartDist : d->pinchLastScale;
552 qreal da = d->pinchLastAngle - angle;
557 d->pinchRotation += da;
558 QPointF pinchCenter = mapFromScene(sceneCenter);
559 QQuickPinchEvent pe(pinchCenter, scale, angle, d->pinchRotation);
560 pe.setStartCenter(d->pinchStartCenter);
561 pe.setPreviousCenter(mapFromScene(d->sceneLastCenter));
562 pe.setPreviousAngle(d->pinchLastAngle);
563 pe.setPreviousScale(d->pinchLastScale);
564 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
565 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
566 pe.setPoint1(touchPoint1.position());
567 pe.setPoint2(touchPoint2.position());
568 pe.setPointCount(d->touchPoints.size());
569 d->pinchLastScale = scale;
570 d->sceneLastCenter = sceneCenter;
571 d->pinchLastAngle = angle;
572 d->lastPoint1 = touchPoint1.scenePosition();
573 d->lastPoint2 = touchPoint2.scenePosition();
574 emit pinchUpdated(&pe);
580void QQuickPinchArea::updatePinchTarget()
582 Q_D(QQuickPinchArea);
583 if (d->pinch && d->pinch->target()) {
584 qreal s = d->pinchStartScale * d->pinchLastScale;
585 s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale());
586 pinch()->target()->setScale(s);
587 QPointF pos = d->sceneLastCenter - d->sceneStartCenter + d->pinchStartPos;
588 if (
auto targetParent = pinch()->target()->parentItem())
589 pos = targetParent->mapFromScene(pos);
591 if (pinch()->axis() & QQuickPinch::XAxis) {
593 if (x < pinch()->xmin())
595 else if (x > pinch()->xmax())
597 pinch()->target()->setX(x);
599 if (pinch()->axis() & QQuickPinch::YAxis) {
601 if (y < pinch()->ymin())
603 else if (y > pinch()->ymax())
605 pinch()->target()->setY(y);
607 if (d->pinchStartRotation >= pinch()->minimumRotation()
608 && d->pinchStartRotation <= pinch()->maximumRotation()) {
609 qreal r = d->pinchRotation + d->pinchStartRotation;
610 r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation());
611 pinch()->target()->setRotation(r);
617
618
619
620
621
622
623
624
625bool QQuickPinchArea::childMouseEventFilter(QQuickItem *i, QEvent *e)
627 Q_D(QQuickPinchArea);
628 if (!d->enabled || !isVisible())
629 return QQuickItem::childMouseEventFilter(i, e);
630 auto *te =
static_cast<QTouchEvent*>(e);
632 case QEvent::TouchBegin:
635 case QEvent::TouchUpdate: {
636 const auto &points = te->points();
637 d->touchPoints.clear();
638 for (
auto &tp : points) {
639 if (tp.state() != QEventPoint::State::Released)
640 d->touchPoints << tp;
642 updatePinch(te,
true);
644 e->setAccepted(d->inPinch);
646 case QEvent::TouchEnd:
653 return QQuickItem::childMouseEventFilter(i, e);
656void QQuickPinchArea::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
658 QQuickItem::geometryChange(newGeometry, oldGeometry);
661void QQuickPinchArea::itemChange(ItemChange change,
const ItemChangeData &value)
663 QQuickItem::itemChange(change, value);
666bool QQuickPinchArea::event(QEvent *event)
668 Q_D(QQuickPinchArea);
669 if (!d->enabled || !isVisible())
670 return QQuickItem::event(event);
672 switch (event->type()) {
673#if QT_CONFIG(gestures)
674 case QEvent::NativeGesture: {
675 QNativeGestureEvent *gesture =
static_cast<QNativeGestureEvent *>(event);
676 switch (gesture->gestureType()) {
677 case Qt::BeginNativeGesture:
679 d->pinchStartCenter = gesture->position();
680 d->pinchStartAngle = 0.0;
681 d->pinchStartRotation = 0.0;
682 d->pinchRotation = 0.0;
683 d->pinchStartScale = 1.0;
684 d->pinchLastAngle = 0.0;
685 d->pinchLastScale = 1.0;
686 d->sceneStartPoint1 = gesture->scenePosition();
687 d->sceneStartPoint2 = gesture->scenePosition();
688 d->lastPoint1 = gesture->scenePosition();
689 d->lastPoint2 = gesture->scenePosition();
690 if (d->pinch && d->pinch->target()) {
691 d->pinchStartPos = d->pinch->target()->position();
692 d->pinchStartScale = d->pinch->target()->scale();
693 d->pinchStartRotation = d->pinch->target()->rotation();
694 d->pinch->setActive(
true);
697 case Qt::EndNativeGesture:
700 case Qt::ZoomNativeGesture: {
701 if (d->pinchRejected)
703 qreal scale = d->pinchLastScale * (1.0 + gesture->value());
704 QQuickPinchEvent pe(d->pinchStartCenter, scale, d->pinchLastAngle, 0.0);
705 pe.setStartCenter(d->pinchStartCenter);
706 pe.setPreviousCenter(d->pinchStartCenter);
707 pe.setPreviousAngle(d->pinchLastAngle);
708 pe.setPreviousScale(d->pinchLastScale);
709 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
710 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
711 pe.setPoint1(mapFromScene(d->lastPoint1));
712 pe.setPoint2(mapFromScene(d->lastPoint2));
714 d->pinchLastScale = scale;
716 emit pinchUpdated(&pe);
718 emit pinchStarted(&pe);
723 d->pinchRejected =
true;
725 case Qt::SmartZoomNativeGesture: {
726 if (gesture->value() > 0.0 && d->pinch && d->pinch->target()) {
727 d->pinchStartPos = pinch()->target()->position();
728 d->pinchStartCenter = mapToItem(pinch()->target()->parentItem(), pinch()->target()->boundingRect().center());
729 d->pinchStartScale = d->pinch->target()->scale();
730 d->pinchStartRotation = d->pinch->target()->rotation();
731 d->pinchLastScale = d->pinchStartScale = d->pinch->target()->scale();
732 d->pinchLastAngle = d->pinchStartRotation = d->pinch->target()->rotation();
734 QQuickPinchEvent pe(gesture->position(), gesture->value(), d->pinchLastAngle, 0.0);
735 pe.setStartCenter(gesture->position());
736 pe.setPreviousCenter(d->pinchStartCenter);
737 pe.setPreviousAngle(d->pinchLastAngle);
738 pe.setPreviousScale(d->pinchLastScale);
739 pe.setStartPoint1(gesture->position());
740 pe.setStartPoint2(gesture->position());
741 pe.setPoint1(mapFromScene(gesture->scenePosition()));
742 pe.setPoint2(mapFromScene(gesture->scenePosition()));
746 case Qt::RotateNativeGesture: {
747 if (d->pinchRejected)
749 qreal angle = d->pinchLastAngle + gesture->value();
750 QQuickPinchEvent pe(d->pinchStartCenter, d->pinchLastScale, angle, 0.0);
751 pe.setStartCenter(d->pinchStartCenter);
752 pe.setPreviousCenter(d->pinchStartCenter);
753 pe.setPreviousAngle(d->pinchLastAngle);
754 pe.setPreviousScale(d->pinchLastScale);
755 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
756 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
757 pe.setPoint1(mapFromScene(d->lastPoint1));
758 pe.setPoint2(mapFromScene(d->lastPoint2));
760 d->pinchLastAngle = angle;
762 emit pinchUpdated(&pe);
764 emit pinchStarted(&pe);
766 d->pinchRotation = angle;
770 d->pinchRejected =
true;
773 return QQuickItem::event(event);
781 return QQuickItem::event(event);
787QQuickPinch *QQuickPinchArea::pinch()
789 Q_D(QQuickPinchArea);
791 d->pinch =
new QQuickPinch;
797#include "moc_qquickpincharea_p.cpp"