Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickdeliveryagent.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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 <QtCore/qdebug.h>
5#include <QtGui/private/qevent_p.h>
6#include <QtGui/private/qeventpoint_p.h>
7#include <QtGui/private/qguiapplication_p.h>
8#include <QtGui/qpa/qplatformtheme.h>
9#include <QtQml/private/qabstractanimationjob_p.h>
10#include <QtQuick/private/qquickdeliveryagent_p_p.h>
11#include <QtQuick/private/qquickhoverhandler_p.h>
12#include <QtQuick/private/qquickpointerhandler_p_p.h>
13#if QT_CONFIG(quick_draganddrop)
14#include <QtQuick/private/qquickdrag_p.h>
15#endif
16#include <QtQuick/private/qquickitem_p.h>
17#include <QtQuick/private/qquickprofiler_p.h>
18#include <QtQuick/private/qquickrendercontrol_p.h>
19#include <QtQuick/private/qquickwindow_p.h>
20
21#include <QtCore/qpointer.h>
22
23#include <memory>
24
26
27Q_LOGGING_CATEGORY(lcTouch, "qt.quick.touch")
28Q_LOGGING_CATEGORY(lcTouchCmprs, "qt.quick.touch.compression")
29Q_LOGGING_CATEGORY(lcTouchTarget, "qt.quick.touch.target")
30Q_LOGGING_CATEGORY(lcMouse, "qt.quick.mouse")
31Q_LOGGING_CATEGORY(lcMouseTarget, "qt.quick.mouse.target")
32Q_LOGGING_CATEGORY(lcTablet, "qt.quick.tablet")
33Q_LOGGING_CATEGORY(lcPtr, "qt.quick.pointer")
34Q_LOGGING_CATEGORY(lcPtrLoc, "qt.quick.pointer.localization")
35Q_LOGGING_CATEGORY(lcPtrGrab, "qt.quick.pointer.grab")
36Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target")
37Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target")
38Q_LOGGING_CATEGORY(lcHoverTrace, "qt.quick.hover.trace")
39Q_LOGGING_CATEGORY(lcFocus, "qt.quick.focus")
40
41extern Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1);
42
43bool QQuickDeliveryAgentPrivate::subsceneAgentsExist(false);
44QQuickDeliveryAgent *QQuickDeliveryAgentPrivate::currentEventDeliveryAgent(nullptr);
45
47{
48 static int allowRightClick = -1;
49 if (allowRightClick < 0) {
50 bool ok = false;
51 allowRightClick = qEnvironmentVariableIntValue("QT_QUICK_ALLOW_SYNTHETIC_RIGHT_CLICK", &ok);
52 if (!ok)
53 allowRightClick = 1; // user didn't opt out
54 }
55 return allowRightClick != 0;
56}
57
59{
61 QMutableSinglePointEvent ret(type, touchEvent->pointingDevice(), p,
64 touchEvent->modifiers(), Qt::MouseEventSynthesizedByQt);
65 ret.setAccepted(true); // this now causes the persistent touchpoint to be accepted too
66 ret.setTimestamp(touchEvent->timestamp());
67 *mouseEvent = ret;
68 // It's very important that the recipient of the event shall be able to see that
69 // this "mouse" event actually comes from a touch device.
70 Q_ASSERT(mouseEvent->device() == touchEvent->device());
72 qWarning() << "Unexpected: synthesized an indistinguishable mouse event" << mouseEvent;
73}
74
75bool QQuickDeliveryAgentPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos)
76{
77 bool doubleClicked = false;
78
80 QPoint distanceBetweenPresses = newPressPos - touchMousePressPos;
81 const int doubleTapDistance = QGuiApplication::styleHints()->touchDoubleTapDistance();
82 doubleClicked = (qAbs(distanceBetweenPresses.x()) <= doubleTapDistance) && (qAbs(distanceBetweenPresses.y()) <= doubleTapDistance);
83
84 if (doubleClicked) {
85 ulong timeBetweenPresses = newPressEventTimestamp - touchMousePressTimestamp;
86 ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->
87 mouseDoubleClickInterval());
88 doubleClicked = timeBetweenPresses < doubleClickInterval;
89 }
90 }
91 if (doubleClicked) {
93 } else {
94 touchMousePressTimestamp = newPressEventTimestamp;
95 touchMousePressPos = newPressPos;
96 }
97
98 return doubleClicked;
99}
100
110{
112 return nullptr;
113 return eventsInDelivery.top();
114}
115
128
130{
131 qCDebug(lcTouchTarget) << "id" << touchMouseId << "on" << touchMouseDevice;
132 touchMouseId = -1;
133 touchMouseDevice = nullptr;
134}
135
137{
140 auto device = pointerEvent->pointingDevice();
141
142 // A touch event from a trackpad is likely to be followed by a mouse or gesture event, so mouse event synth is redundant
144 qCDebug(lcTouchTarget) << q << "skipping delivery of synth-mouse event from" << device;
145 return false;
146 }
147
148 // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here.
150 QQuickItemPrivate::get(item)->localizedTouchEvent(pointerEvent, false, &event);
151 if (!event.points().size())
152 return false;
153
154 // For each point, check if it is accepted, if not, try the next point.
155 // Any of the fingers can become the mouse one.
156 // This can happen because a mouse area might not accept an event at some point but another.
157 for (auto &p : event.points()) {
158 // A new touch point
159 if (touchMouseId == -1 && p.state() & QEventPoint::State::Pressed) {
160 QPointF pos = item->mapFromScene(p.scenePosition());
161
162 // probably redundant, we check bounds in the calling function (matchingNewPoints)
163 if (!item->contains(pos))
164 break;
165
166 qCDebug(lcTouchTarget) << q << device << "TP (mouse)" << Qt::hex << p.id() << "->" << item;
167 QMutableSinglePointEvent mousePress;
169
170 // Send a single press and see if that's accepted
171 QCoreApplication::sendEvent(item, &mousePress);
172 event.setAccepted(mousePress.isAccepted());
173 if (mousePress.isAccepted()) {
175 touchMouseId = p.id();
176 const auto &pt = mousePress.point(0);
177 if (!mousePress.exclusiveGrabber(pt))
178 mousePress.setExclusiveGrabber(pt, item);
179
180 if (checkIfDoubleTapped(event.timestamp(), p.globalPosition().toPoint())) {
181 // since we synth the mouse event from from touch, we respect the
182 // QPlatformTheme::TouchDoubleTapDistance instead of QPlatformTheme::MouseDoubleClickDistance
183 QMutableSinglePointEvent mouseDoubleClick;
185 QCoreApplication::sendEvent(item, &mouseDoubleClick);
186 event.setAccepted(mouseDoubleClick.isAccepted());
187 if (!mouseDoubleClick.isAccepted())
189 }
190
191 return true;
192 }
193 // try the next point
194
195 // Touch point was there before and moved
196 } else if (touchMouseDevice == device && p.id() == touchMouseId) {
197 if (p.state() & QEventPoint::State::Updated) {
198 if (touchMousePressTimestamp != 0) {
199 const int doubleTapDistance = QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::TouchDoubleTapDistance).toInt();
200 const QPoint moveDelta = p.globalPosition().toPoint() - touchMousePressPos;
201 if (moveDelta.x() >= doubleTapDistance || moveDelta.y() >= doubleTapDistance)
202 touchMousePressTimestamp = 0; // Got dragged too far, dismiss the double tap
203 }
204 if (QQuickItem *mouseGrabberItem = qmlobject_cast<QQuickItem *>(pointerEvent->exclusiveGrabber(p))) {
208 event.setAccepted(me.isAccepted());
209 if (me.isAccepted())
210 qCDebug(lcTouchTarget) << q << device << "TP (mouse)" << Qt::hex << p.id() << "->" << mouseGrabberItem;
211 return event.isAccepted();
212 } else {
213 // no grabber, check if we care about mouse hover
214 // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
215 // hover for touch???
222
223 deliverHoverEvent(me.scenePosition(), last, me.modifiers(), me.timestamp());
224 break;
225 }
226 } else if (p.state() & QEventPoint::State::Released) {
227 // currently handled point was released
228 if (QQuickItem *mouseGrabberItem = qmlobject_cast<QQuickItem *>(pointerEvent->exclusiveGrabber(p))) {
232
234 QPointF localMousePos(qInf(), qInf());
235 if (QWindow *w = item->window())
236 localMousePos = item->mapFromScene(w->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition));
238 Qt::NoButton, Qt::NoButton, event.modifiers());
240 }
241 if (pointerEvent->exclusiveGrabber(p) == mouseGrabberItem) // might have ungrabbed due to event
242 pointerEvent->setExclusiveGrabber(p, nullptr);
243
245 return me.isAccepted();
246 }
247 }
248 break;
249 }
250 }
251 return false;
252}
253
263void QQuickDeliveryAgentPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch, bool cancel)
264{
267 // do it the expensive way
268 for (auto dev : knownPointingDevices) {
269 auto devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(dev));
270 devPriv->removeGrabber(grabber, cancel);
271 }
272 return;
273 }
275 if (Q_LIKELY(mouse) && eventInDelivery) {
276 auto epd = mousePointData();
277 if (epd && epd->exclusiveGrabber == grabber && epd->exclusiveGrabberContext.data() == q) {
278 QQuickItem *oldGrabber = qobject_cast<QQuickItem *>(epd->exclusiveGrabber);
279 qCDebug(lcMouseTarget) << "removeGrabber" << oldGrabber << "-> null";
280 eventInDelivery->setExclusiveGrabber(epd->eventPoint, nullptr);
281 }
282 }
283 if (Q_LIKELY(touch)) {
284 bool ungrab = false;
285 const auto touchDevices = QPointingDevice::devices();
286 for (auto device : touchDevices) {
288 continue;
289 if (QPointingDevicePrivate::get(const_cast<QPointingDevice *>(static_cast<const QPointingDevice *>(device)))->
290 removeExclusiveGrabber(eventInDelivery, grabber))
291 ungrab = true;
292 }
293 if (ungrab)
294 grabber->touchUngrabEvent();
295 }
296}
297
304{
305 for (qsizetype i = 0; i != touchEvent->pointCount(); ++i) {
306 auto &pt = touchEvent->point(i);
307 QMutableEventPoint::setScenePosition(pt, pt.position());
308 }
309}
310
311
312static inline bool windowHasFocus(QQuickWindow *win)
313{
314 const QWindow *focusWindow = QGuiApplication::focusWindow();
315 return win == focusWindow || QQuickRenderControlPrivate::isRenderWindowFor(win, focusWindow) || !focusWindow;
316}
317
319{
320 QQuickItem *parentItem = item->parentItem();
321
322 if (parentItem && parentItem->flags() & QQuickItem::ItemIsFocusScope)
323 return findFurthestFocusScopeAncestor(parentItem);
324
325 return item;
326}
327
328#ifdef Q_OS_WEBOS
329// Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
330static inline bool singleWindowOnScreen(QQuickWindow *win)
331{
332 const QWindowList windowList = QGuiApplication::allWindows();
333 for (int i = 0; i < windowList.count(); i++) {
334 QWindow *ii = windowList.at(i);
335 if (ii == win)
336 continue;
337 if (ii->screen() == win->screen())
338 return false;
339 }
340
341 return true;
342}
343#endif
344
351 Qt::FocusReason reason, FocusOptions options)
352{
354 Q_ASSERT(item);
355 Q_ASSERT(scope || item == rootItem);
356
357 qCDebug(lcFocus) << q << "focus" << item << "in scope" << scope;
358 if (scope)
359 qCDebug(lcFocus) << " scopeSubFocusItem:" << QQuickItemPrivate::get(scope)->subFocusItem;
360
361 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : nullptr;
363
364 QQuickItem *oldActiveFocusItem = nullptr;
365 QQuickItem *currentActiveFocusItem = activeFocusItem;
366 QQuickItem *newActiveFocusItem = nullptr;
367 bool sendFocusIn = false;
368
369 lastFocusReason = reason;
370
371 QVarLengthArray<QQuickItem *, 20> changed;
372
373 // Does this change the active focus?
374 if (item == rootItem || scopePrivate->activeFocus) {
375 oldActiveFocusItem = activeFocusItem;
376 if (item->isEnabled()) {
377 newActiveFocusItem = item;
378 while (newActiveFocusItem->isFocusScope()
379 && newActiveFocusItem->scopedFocusItem()
380 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
381 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
382 }
383 } else {
384 newActiveFocusItem = scope;
385 }
386
387 if (oldActiveFocusItem) {
388#if QT_CONFIG(im)
390#endif
391
392 activeFocusItem = nullptr;
393
394 QQuickItem *afi = oldActiveFocusItem;
395 while (afi && afi != scope) {
396 if (QQuickItemPrivate::get(afi)->activeFocus) {
397 QQuickItemPrivate::get(afi)->activeFocus = false;
398 changed << afi;
399 }
400 afi = afi->parentItem();
401 }
402 }
403 }
404
405 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
406 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
407 if (oldSubFocusItem) {
409 priv->focus = false;
410 priv->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, oldSubFocusItem, reason);
411 changed << oldSubFocusItem;
412 }
413
414 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, true);
415 }
416
417 if (!(options & DontChangeFocusProperty)) {
419#ifdef Q_OS_WEBOS
420 // Allow focused if there is only one window in the screen where it belongs.
421 // Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
422 || singleWindowOnScreen(rootItem->window())
423#endif
424 ) {
425 itemPrivate->focus = true;
427 changed << item;
428 }
429 }
430
431 if (newActiveFocusItem && rootItem->hasFocus()) {
432 activeFocusItem = newActiveFocusItem;
433
434 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
435 changed << newActiveFocusItem;
436
437 QQuickItem *afi = newActiveFocusItem->parentItem();
438 while (afi && afi != scope) {
439 if (afi->isFocusScope()) {
440 QQuickItemPrivate::get(afi)->activeFocus = true;
441 changed << afi;
442 }
443 afi = afi->parentItem();
444 }
446 sendFocusIn = true;
447 }
448
449 // Now that all the state is changed, emit signals & events
450 // We must do this last, as this process may result in further changes to focus.
451 if (oldActiveFocusItem) {
453 QCoreApplication::sendEvent(oldActiveFocusItem, &event);
454 }
455
456 // Make sure that the FocusOut didn't result in another focus change.
457 if (sendFocusIn && activeFocusItem == newActiveFocusItem) {
459 QCoreApplication::sendEvent(newActiveFocusItem, &event);
460 }
461
462 if (activeFocusItem != currentActiveFocusItem)
464
465 if (!changed.isEmpty())
466 notifyFocusChangesRecur(changed.data(), changed.size() - 1, reason);
467 if (isSubsceneAgent) {
468 auto da = QQuickWindowPrivate::get(rootItem->window())->deliveryAgent;
469 qCDebug(lcFocus) << " delegating setFocusInScope to" << da;
470
471 // When setting subFocusItem, hierarchy is important. Each focus ancestor's
472 // subFocusItem must be its nearest descendant with focus. Changing the rootItem's
473 // subFocusItem to 'item' here would make 'item' the subFocusItem of all ancestor
474 // focus scopes up until root item.
475 // That is why we should avoid altering subFocusItem until having traversed
476 // all the focus hierarchy.
478 if (ancestorFS != item)
480 QQuickWindowPrivate::get(rootItem->window())->deliveryAgentPrivate()->setFocusInScope(da->rootItem(), item, reason, options);
481 }
482 if (oldActiveFocusItem == activeFocusItem)
483 qCDebug(lcFocus) << " activeFocusItem remains" << activeFocusItem << "in" << q;
484 else
485 qCDebug(lcFocus) << " activeFocusItem" << oldActiveFocusItem << "->" << activeFocusItem << "in" << q;
486}
487
489{
490 Q_ASSERT(item);
491 Q_ASSERT(scope || item == rootItem);
493 qCDebug(lcFocus) << q << "clear focus" << item << "in scope" << scope;
494
495 QQuickItemPrivate *scopePrivate = nullptr;
496 if (scope) {
497 scopePrivate = QQuickItemPrivate::get(scope);
498 if ( !scopePrivate->subFocusItem )
499 return; // No focus, nothing to do.
500 }
501
502 QQuickItem *currentActiveFocusItem = activeFocusItem;
503 QQuickItem *oldActiveFocusItem = nullptr;
504 QQuickItem *newActiveFocusItem = nullptr;
505
506 lastFocusReason = reason;
507
508 QVarLengthArray<QQuickItem *, 20> changed;
509
510 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
511
512 // Does this change the active focus?
513 if (item == rootItem || scopePrivate->activeFocus) {
514 oldActiveFocusItem = activeFocusItem;
515 newActiveFocusItem = scope;
516
517#if QT_CONFIG(im)
519#endif
520
521 activeFocusItem = nullptr;
522
523 if (oldActiveFocusItem) {
524 QQuickItem *afi = oldActiveFocusItem;
525 while (afi && afi != scope) {
526 if (QQuickItemPrivate::get(afi)->activeFocus) {
527 QQuickItemPrivate::get(afi)->activeFocus = false;
528 changed << afi;
529 }
530 afi = afi->parentItem();
531 }
532 }
533 }
534
535 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
536 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
537 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
539 priv->focus = false;
540 priv->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, oldSubFocusItem, reason);
541 changed << oldSubFocusItem;
542 }
543
544 QQuickItemPrivate::get(item)->updateSubFocusItem(scope, false);
545
546 } else if (!(options & DontChangeFocusProperty)) {
548 priv->focus = false;
550 changed << item;
551 }
552
553 if (newActiveFocusItem) {
554 Q_ASSERT(newActiveFocusItem == scope);
555 activeFocusItem = scope;
557 }
558
559 // Now that all the state is changed, emit signals & events
560 // We must do this last, as this process may result in further changes to focus.
561 if (oldActiveFocusItem) {
563 QCoreApplication::sendEvent(oldActiveFocusItem, &event);
564 }
565
566 // Make sure that the FocusOut didn't result in another focus change.
567 if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) {
569 QCoreApplication::sendEvent(newActiveFocusItem, &event);
570 }
571
572 if (activeFocusItem != currentActiveFocusItem)
574
575 if (!changed.isEmpty())
576 notifyFocusChangesRecur(changed.data(), changed.size() - 1, reason);
577 if (isSubsceneAgent) {
578 auto da = QQuickWindowPrivate::get(rootItem->window())->deliveryAgent;
579 qCDebug(lcFocus) << " delegating clearFocusInScope to" << da;
580 QQuickWindowPrivate::get(rootItem->window())->deliveryAgentPrivate()->clearFocusInScope(da->rootItem(), item, reason, options);
581 }
582 if (oldActiveFocusItem == activeFocusItem)
583 qCDebug(lcFocus) << "activeFocusItem remains" << activeFocusItem << "in" << q;
584 else
585 qCDebug(lcFocus) << " activeFocusItem" << oldActiveFocusItem << "->" << activeFocusItem << "in" << q;
586}
587
595
597{
598 QPointer<QQuickItem> item(*items);
599
600 if (item) {
602
603 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
604 itemPrivate->notifiedFocus = itemPrivate->focus;
606 emit item->focusChanged(itemPrivate->focus);
607 }
608
609 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
610 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
611 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, bool(itemPrivate->activeFocus));
613 emit item->activeFocusChanged(itemPrivate->activeFocus);
614 }
615 }
616
617 if (remaining)
618 notifyFocusChangesRecur(items + 1, remaining - 1, reason);
619}
620
622{
623 if (hoverItems.isEmpty())
624 return false;
625
627 if (!window)
628 return false;
629
630 const QPointF lastPos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition);
632
633 for (const auto &[item, id] : hoverItems) {
634 if (item) {
635 deliverHoverEventToItem(item, lastPos, lastPos, modifiers, timestamp, HoverChange::Clear);
636 Q_ASSERT(id == 0);
637 }
638 }
639
640 return true;
641}
642
644{
645#if QT_CONFIG(im)
648 QGuiApplication::inputMethod()->setInputItemTransform(focusPrivate->itemToWindowTransform());
649 QGuiApplication::inputMethod()->setInputItemRectangle(QRectF(0, 0, focusPrivate->width, focusPrivate->height));
651 }
652#endif
653}
654
660{
661 if (activeFocusItem)
662 return activeFocusItem;
663
665 QQuickItem *targetItem = rootItem;
666
667 while (targetItem->isFocusScope()
668 && targetItem->scopedFocusItem()
669 && targetItem->scopedFocusItem()->isEnabled()) {
670 targetItem = targetItem->scopedFocusItem();
671 }
672
673 return targetItem;
674}
675
683{
686 if (item)
687 return QQuickItemPrivate::get(const_cast<QQuickItem *>(item))->deliveryAgent();
688 return nullptr;
689}
690
700
704
708
714{
715 Q_D(const QQuickDeliveryAgent);
716 return d->rootItem;
717}
718
725{
726 Q_D(const QQuickDeliveryAgent);
727 return d->sceneTransform;
728}
729
736{
738 if (d->sceneTransform == transform)
739 return;
740 qCDebug(lcPtr) << this << d->sceneTransform << "->" << transform;
741 if (d->sceneTransform)
742 delete d->sceneTransform;
743 d->sceneTransform = transform;
744}
745
754{
756 d->currentEventDeliveryAgent = this;
757 auto cleanup = qScopeGuard([d] { d->currentEventDeliveryAgent = nullptr; });
758
759 switch (ev->type()) {
763 case QEvent::MouseMove: {
764 QMouseEvent *me = static_cast<QMouseEvent*>(ev);
765 d->handleMouseEvent(me);
766 break;
767 }
770 case QEvent::HoverMove: {
771 QHoverEvent *he = static_cast<QHoverEvent*>(ev);
772 bool accepted = d->deliverHoverEvent(he->scenePosition(),
773 he->points().first().sceneLastPosition(),
774 he->modifiers(), he->timestamp());
775 d->lastMousePosition = he->scenePosition();
776 he->setAccepted(accepted);
777#if QT_CONFIG(cursor)
778 QQuickWindowPrivate::get(d->rootItem->window())->updateCursor(d->sceneTransform ?
779 d->sceneTransform->map(he->scenePosition()) : he->scenePosition(), d->rootItem);
780#endif
781 return accepted;
782 }
785 case QEvent::TouchEnd: {
786 QTouchEvent *touch = static_cast<QTouchEvent*>(ev);
787 d->handleTouchEvent(touch);
789 // we consume all touch events ourselves to avoid duplicate
790 // mouse delivery by QtGui mouse synthesis
791 ev->accept();
792 }
793 break;
794 }
796 // return in order to avoid the QWindow::event below
797 return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(ev));
798 break;
799 case QEvent::Enter: {
800 if (!d->rootItem)
801 return false;
802 QEnterEvent *enter = static_cast<QEnterEvent*>(ev);
803 const auto scenePos = enter->scenePosition();
804 qCDebug(lcHoverTrace) << this << "sending hover event due to QEnterEvent" << enter;
805 bool accepted = d->deliverHoverEvent(scenePos,
806 enter->points().first().sceneLastPosition(),
807 enter->modifiers(), enter->timestamp());
808 d->lastMousePosition = scenePos;
809 // deliverHoverEvent() constructs QHoverEvents: check that EPD didn't end up with corrupted scenePos
810 Q_ASSERT(enter->scenePosition() == scenePos);
811 enter->setAccepted(accepted);
812#if QT_CONFIG(cursor)
813 QQuickWindowPrivate::get(d->rootItem->window())->updateCursor(enter->scenePosition(), d->rootItem);
814#endif
815 return accepted;
816 }
817 case QEvent::Leave:
818 d->clearHover();
819 d->lastMousePosition = QPointF();
820 break;
821#if QT_CONFIG(quick_draganddrop)
824 case QEvent::DragMove:
825 case QEvent::Drop:
826 d->deliverDragEvent(d->dragGrabber, ev);
827 break;
828#endif
830#if QT_CONFIG(im)
831 if (d->activeFocusItem)
832 qGuiApp->inputMethod()->commit();
833#endif
834 break;
835#if QT_CONFIG(gestures)
837 d->deliverSinglePointEventUntilAccepted(static_cast<QPointerEvent *>(ev));
838 break;
839#endif
841 d->deliverKeyEvent(static_cast<QKeyEvent *>(ev));
842 break;
845 {
846 QQuickItem *target = d->focusTargetItem();
847 if (target)
849 }
850 break;
851#if QT_CONFIG(wheelevent)
852 case QEvent::Wheel: {
853 auto event = static_cast<QWheelEvent *>(ev);
854 qCDebug(lcMouse) << event;
855
856 //if the actual wheel event was accepted, accept the compatibility wheel event and return early
857 if (d->lastWheelEventAccepted && event->angleDelta().isNull() && event->phase() == Qt::ScrollUpdate)
858 return true;
859
860 event->ignore();
861 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel,
862 event->angleDelta().x(), event->angleDelta().y());
863 d->deliverSinglePointEventUntilAccepted(event);
864 d->lastWheelEventAccepted = event->isAccepted();
865 break;
866 }
867#endif
868#if QT_CONFIG(tabletevent)
872 {
873 auto *tabletEvent = static_cast<QTabletEvent *>(ev);
874 d->deliverPointerEvent(tabletEvent); // visits HoverHandlers too (unlike the mouse event case)
875#if QT_CONFIG(cursor)
876 QQuickWindowPrivate::get(d->rootItem->window())->updateCursor(tabletEvent->scenePosition(), d->rootItem);
877#endif
878 }
879 break;
880#endif
881 default:
882 return false;
883 }
884
885 return true;
886}
887
889{
890 if (activeFocusItem) {
891 const bool keyPress = (e->type() == QEvent::KeyPress);
892 switch (e->type()) {
893 case QEvent::KeyPress:
894 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(), e->modifiers());
895 break;
897 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(), e->modifiers());
898 break;
899 default:
900 break;
901 }
902
903 QQuickItem *item = activeFocusItem;
904
905 // In case of generated event, trigger ShortcutOverride event
906 if (keyPress && e->spontaneous() == false)
908 e->key(), e->modifiers(), e->text(),
909 e->isAutoRepeat(), e->count());
910
911 do {
912 e->accept();
914 } while (!e->isAccepted() && (item = item->parentItem()));
915 }
916}
917
920 rootItem(root),
921 // a plain QQuickItem can be a subscene root; a QQuickRootItem always belongs directly to a QQuickWindow
922 isSubsceneAgent(!qmlobject_cast<QQuickRootItem *>(rootItem))
923{
924#if QT_CONFIG(quick_draganddrop)
925 dragGrabber = new QQuickDragGrabber;
926#endif
927 if (isSubsceneAgent)
928 subsceneAgentsExist = true;
929}
930
932{
933#if QT_CONFIG(quick_draganddrop)
934 delete dragGrabber;
935 dragGrabber = nullptr;
936#endif
937 delete sceneTransform;
938}
939
948{
949 QPointerEvent *ret = event->clone();
950 QEventPoint &point = ret->point(0);
953 if (transformedLocalPos)
954 QMutableEventPoint::setPosition(point, *transformedLocalPos);
955
956 return ret;
957}
958
959void QQuickDeliveryAgentPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QObject> > &passiveGrabbers,
960 QPointerEvent *pointerEvent)
961{
962 const QVector<QObject *> &eventDeliveryTargets =
964 QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult;
965 hasFiltered.clear();
966 for (QObject *grabberObject : passiveGrabbers) {
967 // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically
968 if (Q_UNLIKELY(!grabberObject))
969 continue;
970 // a passiveGrabber might be an item or a handler
971 if (QQuickPointerHandler *handler = qobject_cast<QQuickPointerHandler *>(grabberObject)) {
972 if (handler && !eventDeliveryTargets.contains(handler)) {
973 bool alreadyFiltered = false;
974 QQuickItem *par = handler->parentItem();
975
976 // see if we already have sent a filter event to the parent
977 auto it = std::find_if(sendFilteredPointerEventResult.begin(), sendFilteredPointerEventResult.end(),
978 [par](const QPair<QQuickItem *, bool> &pair) { return pair.first == par; });
979 if (it != sendFilteredPointerEventResult.end()) {
980 // Yes, the event was sent to that parent for filtering: do not call it again, but use
981 // the result of the previous call to determine whether we should call the handler.
982 alreadyFiltered = it->second;
983 } else if (par) {
984 alreadyFiltered = sendFilteredPointerEvent(pointerEvent, par);
985 sendFilteredPointerEventResult << qMakePair(par, alreadyFiltered);
986 }
987 if (!alreadyFiltered) {
988 if (par)
989 localizePointerEvent(pointerEvent, par);
990 handler->handlePointerEvent(pointerEvent);
991 }
992 }
993 } else if (QQuickItem *grabberItem = static_cast<QQuickItem *>(grabberObject)) {
994 // don't steal the grab if input should remain with the exclusive grabber only
995 if (QQuickItem *excGrabber = static_cast<QQuickItem *>(pointerEvent->exclusiveGrabber(pointerEvent->point(0)))) {
996 if ((isMouseEvent(pointerEvent) && excGrabber->keepMouseGrab())
997 || (isTouchEvent(pointerEvent) && excGrabber->keepTouchGrab())) {
998 return;
999 }
1000 }
1001 localizePointerEvent(pointerEvent, grabberItem);
1002 QCoreApplication::sendEvent(grabberItem, pointerEvent);
1003 pointerEvent->accept();
1004 }
1005 }
1006}
1007
1009 const QPointF &scenePos, const QPointF &lastScenePos,
1010 Qt::KeyboardModifiers modifiers, ulong timestamp)
1011{
1012 auto itemPrivate = QQuickItemPrivate::get(item);
1013 const auto transform = itemPrivate->windowToItemTransform();
1014 const auto transformToGlobal = itemPrivate->windowToGlobalTransform();
1015 auto globalPos = transformToGlobal.map(scenePos);
1016 QHoverEvent hoverEvent(type, scenePos, globalPos, transform.map(lastScenePos), modifiers);
1017 hoverEvent.setTimestamp(timestamp);
1018 hoverEvent.setAccepted(true);
1019 QEventPoint &point = hoverEvent.point(0);
1020 QMutableEventPoint::setPosition(point, transform.map(scenePos));
1021 QMutableEventPoint::setGlobalLastPosition(point, transformToGlobal.map(lastScenePos));
1022
1023 hasFiltered.clear();
1024 if (sendFilteredMouseEvent(&hoverEvent, item, item->parentItem()))
1025 return true;
1026
1027 QCoreApplication::sendEvent(item, &hoverEvent);
1028
1029 return hoverEvent.isAccepted();
1030}
1031
1037// TODO later: specify the device in case of multi-mouse scenario, or mouse and tablet both in use
1039 const QPointF &scenePos, const QPointF &lastScenePos,
1040 Qt::KeyboardModifiers modifiers, ulong timestamp)
1041{
1042 // The first time this function is called, hoverItems is empty.
1043 // We then call deliverHoverEventRecursive from the rootItem, and
1044 // populate the list with all the children and grandchildren that
1045 // we find that should receive hover events (in addition to sending
1046 // hover events to them and their HoverHandlers). We also set the
1047 // hoverId for each item to the currentHoverId.
1048 // The next time this function is called, we bump currentHoverId,
1049 // and call deliverHoverEventRecursive once more.
1050 // When that call returns, the list will contain the items that
1051 // were hovered the first time, as well as the items that were hovered
1052 // this time. But only the items that were hovered this time
1053 // will have their hoverId equal to currentHoverId; the ones we didn't
1054 // visit will still have an old hoverId. We can therefore go through the
1055 // list at the end of this function and look for items with an old hoverId,
1056 // remove them from the list, and update their state accordingly.
1057
1058 const bool subtreeHoverEnabled = QQuickItemPrivate::get(rootItem)->subtreeHoverEnabled;
1059 const bool itemsWasHovered = !hoverItems.isEmpty();
1060
1061 if (!subtreeHoverEnabled && !itemsWasHovered)
1062 return false;
1063
1065
1066 if (subtreeHoverEnabled) {
1067 hoveredLeafItemFound = false;
1068 deliverHoverEventRecursive(rootItem, scenePos, lastScenePos, modifiers, timestamp);
1069 }
1070
1071 // Prune the list for items that are no longer hovered
1072 for (auto it = hoverItems.begin(); it != hoverItems.end();) {
1073 const auto &[item, hoverId] = *it;
1074 if (hoverId == currentHoverId) {
1075 // Still being hovered
1076 it++;
1077 } else {
1078 // No longer hovered. If hoverId is 0, it means that we have sent a HoverLeave
1079 // event to the item already, and it can just be removed from the list. Note that
1080 // the item can have been deleted as well.
1081 if (item && hoverId != 0)
1082 deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, HoverChange::Clear);
1083 it = hoverItems.erase(it);
1084 }
1085 }
1086
1087 const bool itemsAreHovered = !hoverItems.isEmpty();
1088 return itemsWasHovered || itemsAreHovered;
1089}
1090
1128 QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1129 Qt::KeyboardModifiers modifiers, ulong timestamp)
1130{
1131
1132 const QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1133 const QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1134
1135 for (int ii = children.size() - 1; ii >= 0; --ii) {
1136 QQuickItem *child = children.at(ii);
1137 const QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
1138
1139 if (!child->isVisible() || childPrivate->culled)
1140 continue;
1141 if (!childPrivate->subtreeHoverEnabled)
1142 continue;
1143 if (childPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1144 const QPointF localPos = child->mapFromScene(scenePos);
1145 if (!child->contains(localPos))
1146 continue;
1147 }
1148
1149 // Recurse into the child
1150 const bool accepted = deliverHoverEventRecursive(child, scenePos, lastScenePos, modifiers, timestamp);
1151 if (accepted) {
1152 // Stop propagation / recursion
1153 return true;
1154 }
1156 // Don't propagate to siblings, only to ancestors
1157 break;
1158 }
1159 }
1160
1161 // All decendants have been visited.
1162 // Now deliver the event to the item
1163 return deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, HoverChange::Set);
1164}
1165
1175 QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1176 Qt::KeyboardModifiers modifiers, ulong timestamp, HoverChange hoverChange)
1177{
1179 const QPointF localPos = item->mapFromScene(scenePos);
1180 const QPointF globalPos = item->mapToGlobal(localPos);
1181 const bool isHovering = item->contains(localPos);
1182 const auto hoverItemIterator = hoverItems.find(item);
1183 const bool wasHovering = hoverItemIterator != hoverItems.end() && hoverItemIterator.value() != 0;
1184
1185 qCDebug(lcHoverTrace) << "item:" << item << "scene pos:" << scenePos << "localPos:" << localPos
1186 << "wasHovering:" << wasHovering << "isHovering:" << isHovering;
1187
1188 bool accepted = false;
1189
1190 // Start by sending out enter/move/leave events to the item.
1191 // Note that hoverEnabled only controls if we should send out hover events to the
1192 // item itself. HoverHandlers are not included, and are dealt with separately below.
1193 if (itemPrivate->hoverEnabled && isHovering && hoverChange == HoverChange::Set) {
1194 // Add the item to the list of hovered items (if it doesn't exist there
1195 // from before), and update hoverId to mark that it's (still) hovered.
1196 // Also set hoveredLeafItemFound, so that only propagate in a straight
1197 // line towards the root from now on.
1198 hoveredLeafItemFound = true;
1199 if (hoverItemIterator != hoverItems.end())
1200 hoverItemIterator.value() = currentHoverId;
1201 else
1203
1204 if (wasHovering)
1205 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp);
1206 else
1207 accepted = sendHoverEvent(QEvent::HoverEnter, item, scenePos, lastScenePos, modifiers, timestamp);
1208 } else if (wasHovering) {
1209 // A leave should never stop propagation
1210 hoverItemIterator.value() = 0;
1211 sendHoverEvent(QEvent::HoverLeave, item, scenePos, lastScenePos, modifiers, timestamp);
1212 }
1213
1214 if (!itemPrivate->hasPointerHandlers())
1215 return accepted;
1216
1217 // Next, send out hover events to the hover handlers.
1218 // If the item didn't accept the hover event, 'accepted' is now false.
1219 // Otherwise it's true, and then it should stay the way regardless of
1220 // whether or not the hoverhandlers themselves are hovered.
1221 // Note that since a HoverHandler can have a margin, a HoverHandler
1222 // can be hovered even if the item itself is not.
1223
1224 if (hoverChange == HoverChange::Clear) {
1225 // Note: a leave should never stop propagation
1226 QHoverEvent hoverEvent(QEvent::HoverLeave, scenePos, globalPos, lastScenePos, modifiers);
1227 hoverEvent.setTimestamp(timestamp);
1228
1229 for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) {
1230 if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h)) {
1231 if (!hh->isHovered())
1232 continue;
1233 hoverEvent.setAccepted(true);
1234 QCoreApplication::sendEvent(hh, &hoverEvent);
1235 }
1236 }
1237 } else {
1238 QMouseEvent hoverEvent(QEvent::MouseMove, localPos, scenePos, globalPos, Qt::NoButton, Qt::NoButton, modifiers);
1239 hoverEvent.setTimestamp(timestamp);
1240
1241 for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) {
1242 if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h)) {
1243 if (!hh->enabled())
1244 continue;
1245 hoverEvent.setAccepted(true);
1246 hh->handlePointerEvent(&hoverEvent);
1247 if (hh->isHovered()) {
1248 // Mark the whole item as updated, even if only the handler is
1249 // actually in a hovered state (because of HoverHandler.margins)
1250 hoveredLeafItemFound = true;
1251 if (hoverItemIterator != hoverItems.end())
1252 hoverItemIterator.value() = currentHoverId;
1253 else
1255 if (hh->isBlocking()) {
1256 qCDebug(lcHoverTrace) << "skipping rest of hover delivery due to blocking" << hh;
1257 accepted = true;
1258 break;
1259 }
1260 }
1261 }
1262 }
1263 }
1264
1265 return accepted;
1266}
1267
1268// Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers
1269// in the usual reverse-paint-order until propagation is stopped
1271{
1272 Q_ASSERT(event->points().size() == 1);
1274 QEventPoint &point = event->point(0);
1275 QVector<QQuickItem *> targetItems = pointerTargets(rootItem, event, point, false, false);
1276 point.setAccepted(false);
1277
1278 // Let passive grabbers see the event. This must be done before we deliver the
1279 // event to the target and to handlers that might stop event propagation.
1280 // Passive grabbers cannot stop event delivery.
1281 for (const auto &passiveGrabber : event->passiveGrabbers(point)) {
1282 if (auto *grabberItem = qobject_cast<QQuickItem *>(passiveGrabber)) {
1283 if (targetItems.contains(grabberItem))
1284 continue;
1285 localizePointerEvent(event, grabberItem);
1286 QCoreApplication::sendEvent(grabberItem, event);
1287 }
1288 }
1289 // Maintain the invariant that items receive input events in accepted state.
1290 // A passive grabber might have explicitly ignored the event.
1291 event->accept();
1292
1293 for (QQuickItem *item : targetItems) {
1296 // Let Pointer Handlers have the first shot
1297 itemPrivate->handlePointerEvent(event);
1298 if (point.isAccepted())
1299 return true;
1300 event->accept();
1302 if (event->isAccepted()) {
1303 qCDebug(lcWheelTarget) << event << "->" << item;
1304 return true;
1305 }
1306 }
1307
1308 return false; // it wasn't handled
1309}
1310
1312{
1313 qCDebug(lcTouch) << event;
1314
1315 // An incoming TouchCancel event will typically not contain any points,
1316 // but sendTouchCancelEvent() adds the points that have grabbers to the event.
1317 // Deliver it to all items and handlers that have active touches.
1318 const_cast<QPointingDevicePrivate *>(QPointingDevicePrivate::get(event->pointingDevice()))->
1319 sendTouchCancelEvent(event);
1320
1322
1323 return true;
1324}
1325
1327{
1328 // Deliver and delete delayedTouch.
1329 // Set delayedTouch to nullptr before delivery to avoid redelivery in case of
1330 // event loop recursions (e.g if it the touch starts a dnd session).
1331 std::unique_ptr<QTouchEvent> e(std::move(delayedTouch));
1332 qCDebug(lcTouchCmprs) << "delivering" << e.get();
1334 deliverPointerEvent(e.get());
1335}
1336
1348{
1350 qCDebug(lcFocus) << "deactivated" << win->title();
1351 const auto inputDevices = QInputDevice::devices();
1352 for (auto device : inputDevices) {
1353 if (auto pointingDevice = qobject_cast<const QPointingDevice *>(device)) {
1354 auto devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(pointingDevice));
1355 for (auto epd : devPriv->activePoints.values()) {
1356 if (!epd.exclusiveGrabber.isNull()) {
1357 bool relevant = false;
1358 if (QQuickItem *item = qmlobject_cast<QQuickItem *>(epd.exclusiveGrabber.data()))
1359 relevant = (item->window() == win);
1360 else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(epd.exclusiveGrabber.data())) {
1361 if (handler->parentItem())
1362 relevant = (handler->parentItem()->window() == win && epd.exclusiveGrabberContext.data() == q);
1363 else
1364 // a handler with no Item parent probably has a 3D Model parent.
1365 // TODO actually check the window somehow
1366 relevant = true;
1367 }
1368 if (relevant)
1369 devPriv->setExclusiveGrabber(nullptr, epd.eventPoint, nullptr);
1370 }
1371 // For now, we don't clearPassiveGrabbers(), just in case passive grabs
1372 // can be useful to keep monitoring the mouse even after window deactivation.
1373 }
1374 }
1375 }
1376}
1377
1379{
1380 qCDebug(lcFocus) << "hidden" << win->title();
1381 clearHover();
1383}
1384
1386{
1387 for (auto &point : ev->points()) {
1388 if (point.state() != QEventPoint::State::Pressed && !point.isAccepted())
1389 return false;
1390 }
1391 return true;
1392}
1393
1401{
1402 for (int i = 0; i < ev->pointCount(); ++i) {
1403 auto &point = ev->point(i);
1404 QMutableEventPoint::setPosition(point, dest->mapFromScene(point.scenePosition()));
1405 qCDebug(lcPtrLoc) << ev->type() << "@" << point.scenePosition() << "to"
1406 << dest << "@" << dest->mapToScene(QPointF()) << "->" << point;
1407 }
1408}
1409
1411{
1412 QList<QObject *> result;
1413 for (const QEventPoint &point : ev->points()) {
1414 if (QObject *grabber = ev->exclusiveGrabber(point)) {
1415 if (!result.contains(grabber))
1416 result << grabber;
1417 }
1418 }
1419 return result;
1420}
1421
1423{
1424 for (const QEventPoint &point : ev->points()) {
1425 if (ev->exclusiveGrabber(point) || !ev->passiveGrabbers(point).isEmpty())
1426 return true;
1427 }
1428 return false;
1429}
1430
1432{
1433 for (const auto &point : ev->points()) {
1434 if (!ev->exclusiveGrabber(point) && ev->passiveGrabbers(point).isEmpty())
1435 return false;
1436 }
1437 return true;
1438}
1439
1441{
1442 switch (ev->type()) {
1446 case QEvent::MouseMove:
1447 return true;
1448 default:
1449 return false;
1450 }
1451}
1452
1454{
1455 return isMouseEvent(ev) || ev->type() == QEvent::Wheel;
1456}
1457
1459{
1460 switch (ev->type()) {
1461 case QEvent::HoverEnter:
1462 case QEvent::HoverMove:
1463 case QEvent::HoverLeave:
1464 return true;
1465 default:
1466 return false;
1467 }
1468}
1469
1471{
1472 switch (ev->type()) {
1473 case QEvent::TouchBegin:
1475 case QEvent::TouchEnd:
1477 return true;
1478 default:
1479 return false;
1480 }
1481}
1482
1484{
1485 switch (ev->type()) {
1487 case QEvent::TabletMove:
1491 return true;
1492 default:
1493 return false;
1494 }
1495}
1496
1498{
1499 const auto devType = ev->device()->type();
1500 return devType == QInputDevice::DeviceType::Mouse ||
1502}
1503
1508
1510{
1512 if (devPriv->qqExtra)
1513 return static_cast<QQuickPointingDeviceExtra *>(devPriv->qqExtra);
1514 auto extra = new QQuickPointingDeviceExtra;
1515 devPriv->qqExtra = extra;
1517 delete static_cast<QQuickPointingDeviceExtra *>(devPriv->qqExtra);
1518 devPriv->qqExtra = nullptr;
1519 });
1520 return extra;
1521}
1522
1548{
1549 // If this is a subscene agent, don't store any events, because
1550 // flushFrameSynchronousEvents() is only called on the window's DA.
1551 if (isSubsceneAgent)
1552 return false;
1553
1554 QEventPoint::States states = event->touchPointStates();
1556 qCDebug(lcTouchCmprs) << "no compression" << event;
1557 // we can only compress an event that doesn't include any pressed or released points
1558 return false;
1559 }
1560
1561 if (!delayedTouch) {
1562 delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), event->points()));
1563 delayedTouch->setTimestamp(event->timestamp());
1564 for (qsizetype i = 0; i < delayedTouch->pointCount(); ++i) {
1565 auto &tp = delayedTouch->point(i);
1567 }
1569 qCDebug(lcTouchCmprs) << "delayed" << compressedTouchCount << delayedTouch.get();
1571 window->maybeUpdate();
1572 return true;
1573 }
1574
1575 // check if this looks like the last touch event
1576 if (delayedTouch->type() == event->type() &&
1577 delayedTouch->device() == event->device() &&
1578 delayedTouch->modifiers() == event->modifiers() &&
1579 delayedTouch->pointCount() == event->pointCount())
1580 {
1581 // possible match.. is it really the same?
1582 bool mismatch = false;
1583
1584 auto tpts = event->points();
1585 for (qsizetype i = 0; i < event->pointCount(); ++i) {
1586 const auto &tp = tpts.at(i);
1587 const auto &tpDelayed = delayedTouch->point(i);
1588 if (tp.id() != tpDelayed.id()) {
1589 mismatch = true;
1590 break;
1591 }
1592
1593 if (tpDelayed.state() == QEventPoint::State::Updated && tp.state() == QEventPoint::State::Stationary)
1594 QMutableEventPoint::setState(tpts[i], QEventPoint::State::Updated);
1595 }
1596
1597 // matching touch event? then give delayedTouch a merged set of touchpoints
1598 if (!mismatch) {
1599 // have to create a new event because QMutableTouchEvent::setTouchPoints() is missing
1600 // TODO optimize, or move event compression elsewhere
1601 delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), tpts));
1602 delayedTouch->setTimestamp(event->timestamp());
1603 for (qsizetype i = 0; i < delayedTouch->pointCount(); ++i) {
1604 auto &tp = delayedTouch->point(i);
1606 }
1608 qCDebug(lcTouchCmprs) << "coalesced" << compressedTouchCount << delayedTouch.get();
1610 window->maybeUpdate();
1611 return true;
1612 }
1613 }
1614
1615 // merging wasn't possible, so deliver the delayed event first, and then delay this one
1617 delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(),
1618 event->modifiers(), event->points()));
1619 delayedTouch->setTimestamp(event->timestamp());
1620 return true;
1621}
1622
1623// entry point for touch event delivery:
1624// - translate the event to window coordinates
1625// - compress the event instead of delivering it if applicable
1626// - call deliverTouchPoints to actually dispatch the points
1628{
1631 // TODO remove: touch and mouse should be independent until we come to touch->mouse synth
1632 if (event->pointCount()) {
1633 auto &point = event->point(0);
1634 if (point.state() == QEventPoint::State::Released) {
1636 } else {
1637 lastMousePosition = point.position();
1638 }
1639 }
1640
1641 qCDebug(lcTouch) << q << event;
1642
1643 static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION");
1644
1645 if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) {
1647 return;
1648 }
1649
1650 if (!compressTouchEvent(event)) {
1651 if (delayedTouch) {
1653 qCDebug(lcTouchCmprs) << "resuming delivery" << event;
1654 }
1656 }
1657}
1658
1663{
1665 // We generally don't want OS-synthesized mouse events, because Qt Quick does its own touch->mouse synthesis.
1666 // But if the platform converts long-press to right-click, it's ok to react to that,
1667 // unless the user has opted out by setting QT_QUICK_ALLOW_SYNTHETIC_RIGHT_CLICK=0.
1668 if (event->source() == Qt::MouseEventSynthesizedBySystem &&
1669 !(event->button() == Qt::RightButton && allowSyntheticRightClick())) {
1670 event->accept();
1671 return;
1672 }
1673 qCDebug(lcMouse) << q << event;
1674
1675 switch (event->type()) {
1677 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(),
1678 event->buttons());
1680 break;
1682 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
1683 event->buttons());
1685#if QT_CONFIG(cursor)
1686 QQuickWindowPrivate::get(rootItem->window())->updateCursor(event->scenePosition());
1687#endif
1688 break;
1690 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
1691 event->button(), event->buttons());
1693 break;
1694 case QEvent::MouseMove: {
1695 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
1696 event->position().x(), event->position().y());
1697
1698 const QPointF last = lastMousePosition.isNull() ? event->scenePosition() : lastMousePosition;
1699 lastMousePosition = event->scenePosition();
1700 qCDebug(lcHoverTrace) << q << event << "mouse pos" << last << "->" << lastMousePosition;
1701 if (!event->points().size() || !event->exclusiveGrabber(event->point(0))) {
1702 bool accepted = deliverHoverEvent(event->scenePosition(), last, event->modifiers(), event->timestamp());
1703 event->setAccepted(accepted);
1704 }
1706#if QT_CONFIG(cursor)
1707 // The pointer event could result in a cursor change (reaction), so update it afterwards.
1708 QQuickWindowPrivate::get(rootItem->window())->updateCursor(event->scenePosition());
1709#endif
1710 break;
1711 }
1712 default:
1713 Q_ASSERT(false);
1714 break;
1715 }
1716}
1717
1734{
1738
1739 if (delayedTouch) {
1741
1742 // Touch events which constantly start animations (such as a behavior tracking
1743 // the mouse point) need animations to start.
1745 if (ut && ut->hasStartAnimationPending())
1746 ut->startAnimations();
1747 }
1748
1749 // In webOS we already have the alternative to the issue that this
1750 // wanted to address and thus skipping this part won't break anything.
1751#if !defined(Q_OS_WEBOS)
1752 // Once per frame, if any items are dirty, send a synthetic hover,
1753 // in case items have changed position, visibility, etc.
1754 // For instance, during animation (including the case of a ListView
1755 // whose delegates contain MouseAreas), a MouseArea needs to know
1756 // whether it has moved into a position where it is now under the cursor.
1757 // TODO do this for each known mouse device or come up with a different strategy
1758 if (frameSynchronousHoverEnabled && !win->mouseGrabberItem() &&
1759 !lastMousePosition.isNull() && QQuickWindowPrivate::get(win)->dirtyItemList) {
1760 qCDebug(lcHoverTrace) << q << "delivering frame-sync hover to root @" << lastMousePosition;
1762#if QT_CONFIG(cursor)
1763 QQuickWindowPrivate::get(rootItem->window())->updateCursor(
1765#endif
1766 }
1767
1768 qCDebug(lcHoverTrace) << q << "frame-sync hover delivery done";
1769 }
1770#else
1771 Q_UNUSED(win);
1772#endif
1775 qCWarning(lcPtr, "detected interleaved frame-sync and actual events");
1777}
1778
1788 const QPointerEvent *event, const QEventPoint &point)
1789{
1791 const bool grabGained = (transition == QPointingDevice::GrabTransition::GrabExclusive ||
1793
1794 QQuickDeliveryAgent *deliveryAgent = nullptr;
1795
1796 // note: event can be null, if the signal was emitted from QPointingDevicePrivate::removeGrabber(grabber)
1797 if (auto *handler = qmlobject_cast<QQuickPointerHandler *>(grabber)) {
1798 if (handler->parentItem()) {
1799 auto itemPriv = QQuickItemPrivate::get(handler->parentItem());
1800 deliveryAgent = itemPriv->deliveryAgent();
1801 if (deliveryAgent == q) {
1802 handler->onGrabChanged(handler, transition, const_cast<QPointerEvent *>(event),
1803 const_cast<QEventPoint &>(point));
1804 }
1805 if (grabGained) {
1806 // An item that is NOT a subscene root needs to track whether it got a grab via a subscene delivery agent,
1807 // whereas the subscene root item already knows it has its own DA.
1808 if (isSubsceneAgent && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
1809 itemPriv->maybeHasSubsceneDeliveryAgent = true;
1810 }
1811 } else if (!isSubsceneAgent) {
1812 handler->onGrabChanged(handler, transition, const_cast<QPointerEvent *>(event),
1813 const_cast<QEventPoint &>(point));
1814 }
1815 } else if (auto *grabberItem = qmlobject_cast<QQuickItem *>(grabber)) {
1816 switch (transition) {
1823 hasFiltered.clear();
1824 if (!sendFilteredMouseEvent(&e, grabberItem, grabberItem->parentItem())) {
1825 lastUngrabbed = grabberItem;
1826 grabberItem->mouseUngrabEvent();
1827 }
1828 }
1830 bool allReleasedOrCancelled = true;
1831 if (transition == QPointingDevice::UngrabExclusive && event) {
1832 for (const auto &pt : event->points()) {
1833 if (pt.state() != QEventPoint::State::Released) {
1834 allReleasedOrCancelled = false;
1835 break;
1836 }
1837 }
1838 }
1839 if (allReleasedOrCancelled)
1840 grabberItem->touchUngrabEvent();
1841 }
1842 break;
1843 default:
1844 break;
1845 }
1846 auto *itemPriv = QQuickItemPrivate::get(grabberItem);
1847 deliveryAgent = itemPriv->deliveryAgent();
1848 // An item that is NOT a subscene root needs to track whether it got a grab via a subscene delivery agent,
1849 // whereas the subscene root item already knows it has its own DA.
1850 if (isSubsceneAgent && grabGained && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
1851 itemPriv->maybeHasSubsceneDeliveryAgent = true;
1852 }
1853
1854 if (currentEventDeliveryAgent == q && event && event->device()) {
1855 switch (transition) {
1857 auto epd = QPointingDevicePrivate::get(const_cast<QPointingDevice*>(event->pointingDevice()))->queryPointById(point.id());
1858 Q_ASSERT(epd);
1860 qCDebug(lcPtr) << "remembering that" << q << "handles point" << point.id() << "after" << transition;
1861 } break;
1863 auto epd = QPointingDevicePrivate::get(const_cast<QPointingDevice*>(event->pointingDevice()))->queryPointById(point.id());
1864 Q_ASSERT(epd);
1865 epd->exclusiveGrabberContext = q;
1866 qCDebug(lcPtr) << "remembering that" << q << "handles point" << point.id() << "after" << transition;
1867 } break;
1870 // taken care of in QPointingDevicePrivate::setExclusiveGrabber(,,nullptr), removeExclusiveGrabber()
1871 break;
1874 // taken care of in QPointingDevicePrivate::removePassiveGrabber(), clearPassiveGrabbers()
1875 break;
1877 // not in use at this time
1878 break;
1879 }
1880 }
1881}
1882
1901
1910{
1912 if (isTabletEvent(event))
1913 qCDebug(lcTablet) << q << event;
1914
1915 // If users spin the eventloop as a result of event delivery, we disable
1916 // event compression and send events directly. This is because we consider
1917 // the usecase a bit evil, but we at least don't want to lose events.
1920
1921 // So far this is for use in Qt Quick 3D: if a QEventPoint is grabbed,
1922 // updates get delivered here pretty directly, bypassing picking; but we need to
1923 // be able to map the 2D viewport coordinate to a 2D coordinate within
1924 // d->rootItem, a 2D scene that has been arbitrarily mapped onto a 3D object.
1925 QVarLengthArray<QPointF, 16> originalScenePositions;
1926 if (sceneTransform) {
1927 originalScenePositions.resize(event->pointCount());
1928 for (int i = 0; i < event->pointCount(); ++i) {
1929 auto &pt = event->point(i);
1930 originalScenePositions[i] = pt.scenePosition();
1931 QMutableEventPoint::setScenePosition(pt, sceneTransform->map(pt.scenePosition()));
1932 qCDebug(lcPtrLoc) << q << event->type() << pt.id() << "transformed scene pos" << pt.scenePosition();
1933 }
1934 } else if (isSubsceneAgent) {
1935 qCDebug(lcPtrLoc) << q << event->type() << "no scene transform set";
1936 }
1937
1938 skipDelivery.clear();
1940 if (sceneTransform)
1941 qCDebug(lcPtr) << q << "delivering with" << sceneTransform << event;
1942 else
1943 qCDebug(lcPtr) << q << "delivering" << event;
1944 for (int i = 0; i < event->pointCount(); ++i)
1945 event->point(i).setAccepted(false);
1946
1947 if (event->isBeginEvent()) {
1948 ensureDeviceConnected(event->pointingDevice());
1950 event->setAccepted(false);
1951 }
1954 if (event->isEndEvent())
1956
1957 // failsafe: never allow touch->mouse synthesis to persist after all touchpoints are released,
1958 // or after the touchmouse is released
1959 if (isTouchEvent(event) && touchMouseId >= 0) {
1960 if (static_cast<QTouchEvent *>(event)->touchPointStates() == QEventPoint::State::Released) {
1962 } else {
1963 auto touchMousePoint = event->pointById(touchMouseId);
1964 if (touchMousePoint && touchMousePoint->state() == QEventPoint::State::Released)
1966 }
1967 }
1968
1970 if (sceneTransform) {
1971 for (int i = 0; i < event->pointCount(); ++i)
1972 QMutableEventPoint::setScenePosition(event->point(i), originalScenePositions.at(i));
1973 }
1975 lastUngrabbed = nullptr;
1976}
1977
2007// FIXME: should this be iterative instead of recursive?
2009 bool checkMouseButtons, bool checkAcceptsTouch) const
2010{
2011 Q_Q(const QQuickDeliveryAgent);
2012 QVector<QQuickItem *> targets;
2013 auto itemPrivate = QQuickItemPrivate::get(item);
2014 QPointF itemPos = item->mapFromScene(point.scenePosition());
2015 bool relevant = item->contains(itemPos);
2016 qCDebug(lcPtrLoc) << q << "point" << point.id() << point.scenePosition() << "->" << itemPos << ": relevant?" << relevant << "to" << item << point;
2017 // if the item clips, we can potentially return early
2018 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
2019 if (!item->clipRect().contains(itemPos))
2020 return targets;
2021 }
2022
2023 if (itemPrivate->hasPointerHandlers()) {
2024 if (!relevant)
2025 if (itemPrivate->anyPointerHandlerWants(event, point))
2026 relevant = true;
2027 } else {
2028 if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
2029 relevant = false;
2030 if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
2031 relevant = false;
2032 }
2033
2034 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
2035 if (relevant) {
2036 auto it = std::lower_bound(children.begin(), children.end(), 0,
2037 [](auto lhs, auto rhs) -> bool { return lhs->z() < rhs; });
2039 }
2040
2041 for (int ii = children.size() - 1; ii >= 0; --ii) {
2042 QQuickItem *child = children.at(ii);
2043 auto childPrivate = QQuickItemPrivate::get(child);
2044 if (!child->isVisible() || !child->isEnabled() || childPrivate->culled ||
2045 (child != item && childPrivate->extra.isAllocated() && childPrivate->extra->subsceneDeliveryAgent))
2046 continue;
2047
2048 if (child != item)
2049 targets << pointerTargets(child, event, point, checkMouseButtons, checkAcceptsTouch);
2050 else
2051 targets << child;
2052 }
2053
2054 return targets;
2055}
2056
2061QVector<QQuickItem *> QQuickDeliveryAgentPrivate::mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const
2062{
2063 QVector<QQuickItem *> targets = list1;
2064 // start at the end of list2
2065 // if item not in list, append it
2066 // if item found, move to next one, inserting before the last found one
2067 int insertPosition = targets.size();
2068 for (int i = list2.size() - 1; i >= 0; --i) {
2069 int newInsertPosition = targets.lastIndexOf(list2.at(i), insertPosition);
2070 if (newInsertPosition >= 0) {
2071 Q_ASSERT(newInsertPosition <= insertPosition);
2072 insertPosition = newInsertPosition;
2073 }
2074 // check for duplicates, only insert if the item isn't there already
2075 if (insertPosition == targets.size() || list2.at(i) != targets.at(insertPosition))
2076 targets.insert(insertPosition, list2.at(i));
2077 }
2078 return targets;
2079}
2080
2085{
2086 Q_Q(const QQuickDeliveryAgent);
2087 bool done = false;
2088 const auto grabbers = exclusiveGrabbers(event);
2089 hasFiltered.clear();
2090 for (auto grabber : grabbers) {
2091 // The grabber is guaranteed to be either an item or a handler.
2092 QQuickItem *receiver = qmlobject_cast<QQuickItem *>(grabber);
2093 if (!receiver) {
2094 // The grabber is not an item? It's a handler then. Let it have the event first.
2095 QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber);
2096 receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem();
2097 // Filtering via QQuickItem::childMouseEventFilter() is only possible
2098 // if the handler's parent is an Item. It could be a QQ3D object.
2099 if (receiver) {
2100 hasFiltered.clear();
2101 if (sendFilteredPointerEvent(event, receiver))
2102 done = true;
2103 localizePointerEvent(event, receiver);
2104 }
2105 handler->handlePointerEvent(event);
2106 }
2107 if (done)
2108 break;
2109 // If the grabber is an item or the grabbing handler didn't handle it,
2110 // then deliver the event to the item (which may have multiple handlers).
2111 hasFiltered.clear();
2112 if (receiver)
2113 deliverMatchingPointsToItem(receiver, true, event);
2114 }
2115
2116 // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once)
2117 for (auto &point : event->points()) {
2118 auto epd = QPointingDevicePrivate::get(event->pointingDevice())->queryPointById(point.id());
2119 if (Q_UNLIKELY(!epd)) {
2120 qWarning() << "point is not in activePoints" << point;
2121 continue;
2122 }
2123 QList<QPointer<QObject>> relevantPassiveGrabbers;
2124 for (int i = 0; i < epd->passiveGrabbersContext.size(); ++i) {
2125 if (epd->passiveGrabbersContext.at(i).data() == q)
2126 relevantPassiveGrabbers << epd->passiveGrabbers.at(i);
2127 }
2128 if (!relevantPassiveGrabbers.isEmpty())
2129 deliverToPassiveGrabbers(relevantPassiveGrabbers, event);
2130
2131 // Ensure that HoverHandlers are updated, in case no items got dirty so far and there's no update request
2132 if (event->type() == QEvent::TouchUpdate) {
2133 for (const auto &[item, id] : hoverItems) {
2134 if (item) {
2135 bool res = deliverHoverEventToItem(item, point.scenePosition(), point.sceneLastPosition(),
2136 event->modifiers(), event->timestamp(), HoverChange::Set);
2137 // if the event was accepted, then the item's ID must be valid
2139 }
2140 }
2141 }
2142 }
2143
2144 if (done)
2145 return;
2146
2147 // If some points weren't grabbed, deliver only to non-grabber PointerHandlers in reverse paint order
2148 if (!allPointsGrabbed(event)) {
2149 QVector<QQuickItem *> targetItems;
2150 for (auto &point : event->points()) {
2151 // Presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints.
2152 // Don't find handlers for points that are already grabbed by an Item (such as Flickable).
2153 if (point.state() == QEventPoint::Pressed || qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(point)))
2154 continue;
2155 QVector<QQuickItem *> targetItemsForPoint = pointerTargets(rootItem, event, point, false, false);
2156 if (targetItems.size()) {
2157 targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
2158 } else {
2159 targetItems = targetItemsForPoint;
2160 }
2161 }
2162 for (QQuickItem *item : targetItems) {
2163 if (grabbers.contains(item))
2164 continue;
2167 itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers
2169 break;
2170 }
2171 }
2172}
2173
2214{
2215 QVector<QQuickItem *> targetItems;
2216 const bool isTouch = isTouchEvent(event);
2217 if (isTouch && event->isBeginEvent() && isDeliveringTouchAsMouse()) {
2218 if (auto point = const_cast<QPointingDevicePrivate *>(QPointingDevicePrivate::get(touchMouseDevice))->queryPointById(touchMouseId)) {
2219 // When a second point is pressed, if the first point's existing
2220 // grabber was a pointer handler while a filtering parent is filtering
2221 // the same first point _as mouse_: we're starting over with delivery,
2222 // so we need to allow the second point to now be sent as a synth-mouse
2223 // instead of the first one, so that filtering parents (maybe even the
2224 // same one) can get a chance to see the second touchpoint as a
2225 // synth-mouse and perhaps grab it. Ideally we would always do this
2226 // when a new touchpoint is pressed, but this compromise fixes
2227 // QTBUG-70998 and avoids breaking tst_FlickableInterop::touchDragSliderAndFlickable
2228 if (qobject_cast<QQuickPointerHandler *>(event->exclusiveGrabber(point->eventPoint)))
2230 } else {
2231 qCWarning(lcTouchTarget) << "during delivery of touch press, synth-mouse ID" << Qt::hex << touchMouseId << "is missing from" << event;
2232 }
2233 }
2234 for (int i = 0; i < event->pointCount(); ++i) {
2235 auto &point = event->point(i);
2236 QVector<QQuickItem *> targetItemsForPoint = pointerTargets(rootItem, event, point, !isTouch, isTouch);
2237 if (targetItems.size()) {
2238 targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
2239 } else {
2240 targetItems = targetItemsForPoint;
2241 }
2242 }
2243
2244 QVector<QPointer<QQuickItem>> safeTargetItems(targetItems.begin(), targetItems.end());
2245
2246 for (auto &item : safeTargetItems) {
2247 if (item.isNull())
2248 continue;
2249 // failsafe: when items get into a subscene somehow, ensure that QQuickItemPrivate::deliveryAgent() can find it
2250 if (isSubsceneAgent)
2251 QQuickItemPrivate::get(item)->maybeHasSubsceneDeliveryAgent = true;
2252
2253 hasFiltered.clear();
2254 if (!handlersOnly && sendFilteredPointerEvent(event, item)) {
2255 if (event->isAccepted())
2256 return true;
2257 skipDelivery.append(item);
2258 }
2259
2260 // Do not deliverMatchingPointsTo any item for which the filtering parent already intercepted the event,
2261 // nor to any item which already had a chance to filter.
2262 if (skipDelivery.contains(item))
2263 continue;
2264
2265 // sendFilteredPointerEvent() changed the QEventPoint::accepted() state,
2266 // but per-point acceptance is opt-in during normal delivery to items.
2267 for (int i = 0; i < event->pointCount(); ++i)
2268 event->point(i).setAccepted(false);
2269
2270 deliverMatchingPointsToItem(item, false, event, handlersOnly);
2271 if (event->allPointsAccepted())
2272 handlersOnly = true;
2273 }
2274
2275 return event->allPointsAccepted();
2276}
2277
2286void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly)
2287{
2289#if defined(Q_OS_ANDROID) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2290 // QTBUG-85379
2291 // In QT_VERSION below 6.0.0 touchEnabled for QtQuickItems is set by default to true
2292 // It causes delivering touch events to Items which are not interested
2293 // In some cases (like using Material Style in Android) it may cause a crash
2294 if (itemPrivate->wasDeleted)
2295 return;
2296#endif
2297 localizePointerEvent(pointerEvent, item);
2298 bool isMouse = isMouseEvent(pointerEvent);
2299
2300 // Let the Item's handlers (if any) have the event first.
2301 // However, double click should never be delivered to handlers.
2302 if (pointerEvent->type() != QEvent::MouseButtonDblClick)
2303 itemPrivate->handlePointerEvent(pointerEvent);
2304
2305 if (handlersOnly)
2306 return;
2307
2308 // If all points are released and the item is not the grabber, it doesn't get the event.
2309 // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario.
2310 if (pointerEvent->isEndEvent() && !pointerEvent->isUpdateEvent()
2311 && !exclusiveGrabbers(pointerEvent).contains(item))
2312 return;
2313
2314 // If any parent filters the event, we're done.
2315 if (sendFilteredPointerEvent(pointerEvent, item))
2316 return;
2317
2318 // TODO: unite this mouse point delivery with the synthetic mouse event below
2319 // TODO: remove isGrabber then?
2320 if (isMouse) {
2321 auto button = static_cast<QSinglePointEvent *>(pointerEvent)->button();
2322 if ((isGrabber && button == Qt::NoButton) || item->acceptedMouseButtons().testFlag(button)) {
2323 // The only reason to already have a mouse grabber here is
2324 // synthetic events - flickable sends one when setPressDelay is used.
2325 auto oldMouseGrabber = pointerEvent->exclusiveGrabber(pointerEvent->point(0));
2326 pointerEvent->accept();
2327 if (isGrabber && sendFilteredPointerEvent(pointerEvent, item))
2328 return;
2329 localizePointerEvent(pointerEvent, item);
2330 QCoreApplication::sendEvent(item, pointerEvent);
2331 if (pointerEvent->isAccepted()) {
2332 auto &point = pointerEvent->point(0);
2333 auto mouseGrabber = pointerEvent->exclusiveGrabber(point);
2334 if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) {
2335 // Normally we don't need item->mouseUngrabEvent() here, because QQuickDeliveryAgentPrivate::onGrabChanged does it.
2336 // However, if one item accepted the mouse event, it expects to have the grab and be in "pressed" state,
2337 // because accepting implies grabbing. But before it actually gets the grab, another item could steal it.
2338 // In that case, onGrabChanged() does NOT notify the item that accepted the event that it's not getting the grab after all.
2339 // So after ensuring that it's not redundant, we send a notification here, for that case (QTBUG-55325).
2340 if (item != lastUngrabbed) {
2341 item->mouseUngrabEvent();
2343 }
2344 } else if (item->isEnabled() && item->isVisible() && point.state() == QEventPoint::State::Pressed) {
2345 pointerEvent->setExclusiveGrabber(point, item);
2346 }
2347 point.setAccepted(true);
2348 }
2349 return;
2350 }
2351 }
2352
2353 if (!isTouchEvent(pointerEvent))
2354 return;
2355
2356 bool eventAccepted = false;
2357 QMutableTouchEvent touchEvent;
2358 itemPrivate->localizedTouchEvent(static_cast<QTouchEvent *>(pointerEvent), false, &touchEvent);
2359 if (touchEvent.type() == QEvent::None)
2360 return; // no points inside this item
2361
2362 if (item->acceptTouchEvents()) {
2363 qCDebug(lcTouch) << "considering delivering" << &touchEvent << " to " << item;
2364
2365 // Deliver the touch event to the given item
2366 qCDebug(lcTouch) << "actually delivering" << &touchEvent << " to " << item;
2367 QCoreApplication::sendEvent(item, &touchEvent);
2368 eventAccepted = touchEvent.isAccepted();
2369 } else {
2370 // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
2372 !eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton))
2373 deliverTouchAsMouse(item, &touchEvent);
2374 return;
2375 }
2376
2377 Q_ASSERT(item->acceptTouchEvents()); // else we would've returned early above
2378 if (eventAccepted) {
2379 bool isPressOrRelease = pointerEvent->isBeginEvent() || pointerEvent->isEndEvent();
2380 for (int i = 0; i < touchEvent.pointCount(); ++i) {
2381 auto &point = touchEvent.point(i);
2382 // legacy-style delivery: if the item doesn't reject the event, that means it handled ALL the points
2383 point.setAccepted();
2384 // but don't let the root of a subscene implicitly steal the grab from some other item (such as one of its children)
2385 if (isPressOrRelease && !(itemPrivate->deliveryAgent() && pointerEvent->exclusiveGrabber(point)))
2386 pointerEvent->setExclusiveGrabber(point, item);
2387 }
2388 } else {
2389 // But if the event was not accepted then we know this item
2390 // will not be interested in further updates for those touchpoint IDs either.
2391 for (const auto &point: touchEvent.points()) {
2392 if (point.state() == QEventPoint::State::Pressed) {
2393 if (pointerEvent->exclusiveGrabber(point) == item) {
2394 qCDebug(lcTouchTarget) << "TP" << Qt::hex << point.id() << "disassociated";
2395 pointerEvent->setExclusiveGrabber(point, nullptr);
2396 }
2397 }
2398 }
2399 }
2400}
2401
2402#if QT_CONFIG(quick_draganddrop)
2403void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
2404{
2405 QObject *formerTarget = grabber->target();
2406 grabber->resetTarget();
2407 QQuickDragGrabber::iterator grabItem = grabber->begin();
2408 if (grabItem != grabber->end()) {
2409 Q_ASSERT(event->type() != QEvent::DragEnter);
2410 if (event->type() == QEvent::Drop) {
2411 QDropEvent *e = static_cast<QDropEvent *>(event);
2412 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
2413 QPointF p = (**grabItem)->mapFromScene(e->position().toPoint());
2414 QDropEvent translatedEvent(
2415 p.toPoint(),
2416 e->possibleActions(),
2417 e->mimeData(),
2418 e->buttons(),
2419 e->modifiers());
2420 QQuickDropEventEx::copyActions(&translatedEvent, *e);
2421 QCoreApplication::sendEvent(**grabItem, &translatedEvent);
2422 e->setAccepted(translatedEvent.isAccepted());
2423 e->setDropAction(translatedEvent.dropAction());
2424 grabber->setTarget(**grabItem);
2425 }
2426 }
2427 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
2428 QDragLeaveEvent leaveEvent;
2429 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
2430 QCoreApplication::sendEvent(**grabItem, &leaveEvent);
2431 grabber->ignoreList().clear();
2432 return;
2433 } else {
2434 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
2435
2436 // Used to ensure we don't send DragEnterEvents to current drop targets,
2437 // and to detect which current drop targets we have left
2438 QVarLengthArray<QQuickItem*, 64> currentGrabItems;
2439 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
2440 currentGrabItems.append(**grabItem);
2441
2442 // Look for any other potential drop targets that are higher than the current ones
2443 QDragEnterEvent enterEvent(
2444 moveEvent->position().toPoint(),
2445 moveEvent->possibleActions(),
2446 moveEvent->mimeData(),
2447 moveEvent->buttons(),
2448 moveEvent->modifiers());
2449 QQuickDropEventEx::copyActions(&enterEvent, *moveEvent);
2450 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent, &currentGrabItems,
2451 formerTarget));
2452
2453 for (grabItem = grabber->begin(); grabItem != grabber->end(); ++grabItem) {
2454 int i = currentGrabItems.indexOf(**grabItem);
2455 if (i >= 0) {
2456 currentGrabItems.remove(i);
2457 // Still grabbed: send move event
2458 QDragMoveEvent translatedEvent(
2459 (**grabItem)->mapFromScene(moveEvent->position().toPoint()).toPoint(),
2460 moveEvent->possibleActions(),
2461 moveEvent->mimeData(),
2462 moveEvent->buttons(),
2463 moveEvent->modifiers());
2464 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
2465 QCoreApplication::sendEvent(**grabItem, &translatedEvent);
2466 event->setAccepted(translatedEvent.isAccepted());
2467 QQuickDropEventEx::copyActions(moveEvent, translatedEvent);
2468 }
2469 }
2470
2471 // Anything left in currentGrabItems is no longer a drop target and should be sent a DragLeaveEvent
2472 QDragLeaveEvent leaveEvent;
2473 for (QQuickItem *i : currentGrabItems)
2474 QCoreApplication::sendEvent(i, &leaveEvent);
2475
2476 return;
2477 }
2478 }
2479 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
2480 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
2481 QDragEnterEvent enterEvent(
2482 e->position().toPoint(),
2483 e->possibleActions(),
2484 e->mimeData(),
2485 e->buttons(),
2486 e->modifiers());
2487 QQuickDropEventEx::copyActions(&enterEvent, *e);
2488 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
2489 } else {
2490 grabber->ignoreList().clear();
2491 }
2492}
2493
2494bool QQuickDeliveryAgentPrivate::deliverDragEvent(
2495 QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event,
2496 QVarLengthArray<QQuickItem *, 64> *currentGrabItems, QObject *formerTarget)
2497{
2499 if (!item->isVisible() || !item->isEnabled() || QQuickItemPrivate::get(item)->culled)
2500 return false;
2501 QPointF p = item->mapFromScene(event->position().toPoint());
2502 bool itemContained = item->contains(p);
2503
2504 const int itemIndex = grabber->ignoreList().indexOf(item);
2505 if (!itemContained) {
2506 if (itemIndex >= 0)
2507 grabber->ignoreList().remove(itemIndex);
2508
2510 return false;
2511 }
2512
2513 QDragEnterEvent enterEvent(
2514 event->position().toPoint(),
2515 event->possibleActions(),
2516 event->mimeData(),
2517 event->buttons(),
2518 event->modifiers());
2520 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
2521
2522 // Check children in front of this item first
2523 for (int ii = children.size() - 1; ii >= 0; --ii) {
2524 if (children.at(ii)->z() < 0)
2525 continue;
2526 if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems, formerTarget))
2527 return true;
2528 }
2529
2530 if (itemContained) {
2531 // If this item is currently grabbed, don't send it another DragEnter,
2532 // just grab it again if it's still contained.
2533 if (currentGrabItems && currentGrabItems->contains(item)) {
2534 grabber->grab(item);
2535 grabber->setTarget(item);
2536 return true;
2537 }
2538
2539 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
2540 if (event->type() == QEvent::DragEnter) {
2541 if (formerTarget) {
2542 QQuickItem *formerTargetItem = qobject_cast<QQuickItem *>(formerTarget);
2543 if (formerTargetItem && currentGrabItems) {
2544 QDragLeaveEvent leaveEvent;
2545 QCoreApplication::sendEvent(formerTarget, &leaveEvent);
2546
2547 // Remove the item from the currentGrabItems so a leave event won't be generated
2548 // later on
2549 currentGrabItems->removeAll(formerTarget);
2550 }
2551 } else if (itemIndex >= 0) {
2552 return false;
2553 }
2554 }
2555
2556 QDragMoveEvent translatedEvent(p.toPoint(), event->possibleActions(), event->mimeData(),
2557 event->buttons(), event->modifiers(), event->type());
2558 QQuickDropEventEx::copyActions(&translatedEvent, *event);
2559 translatedEvent.setAccepted(event->isAccepted());
2560 QCoreApplication::sendEvent(item, &translatedEvent);
2561 event->setAccepted(translatedEvent.isAccepted());
2562 event->setDropAction(translatedEvent.dropAction());
2563 if (event->type() == QEvent::DragEnter) {
2564 if (translatedEvent.isAccepted()) {
2565 grabber->grab(item);
2566 grabber->setTarget(item);
2567 return true;
2568 } else if (itemIndex < 0) {
2569 grabber->ignoreList().append(item);
2570 }
2571 } else {
2572 return true;
2573 }
2574 }
2575 }
2576
2577 // Check children behind this item if this item or any higher children have not accepted
2578 for (int ii = children.size() - 1; ii >= 0; --ii) {
2579 if (children.at(ii)->z() >= 0)
2580 continue;
2581 if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems, formerTarget))
2582 return true;
2583 }
2584
2585 return false;
2586}
2587#endif // quick_draganddrop
2588
2598{
2599 return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem());
2600}
2601
2606{
2608 return false;
2609 if (!filteringParent)
2610 return false;
2611 bool filtered = false;
2612 const bool hasHandlers = QQuickItemPrivate::get(receiver)->hasPointerHandlers();
2613 if (filteringParent->filtersChildMouseEvents() && !hasFiltered.contains(filteringParent)) {
2614 hasFiltered.append(filteringParent);
2615 if (isMouseEvent(event)) {
2616 if (receiver->acceptedMouseButtons()) {
2617 const bool wasAccepted = event->allPointsAccepted();
2618 Q_ASSERT(event->pointCount());
2619 localizePointerEvent(event, receiver);
2620 event->setAccepted(true);
2621 auto oldMouseGrabber = event->exclusiveGrabber(event->point(0));
2622 if (filteringParent->childMouseEventFilter(receiver, event)) {
2623 qCDebug(lcMouse) << "mouse event intercepted by childMouseEventFilter of " << filteringParent;
2624 skipDelivery.append(filteringParent);
2625 filtered = true;
2626 if (event->isAccepted() && event->isBeginEvent()) {
2627 auto &point = event->point(0);
2628 auto mouseGrabber = event->exclusiveGrabber(point);
2629 if (mouseGrabber && mouseGrabber != receiver && mouseGrabber != oldMouseGrabber) {
2630 receiver->mouseUngrabEvent();
2631 } else {
2632 event->setExclusiveGrabber(point, receiver);
2633 }
2634 }
2635 } else {
2636 // Restore accepted state if the event was not filtered.
2637 event->setAccepted(wasAccepted);
2638 }
2639 }
2640 } else if (isTouchEvent(event)) {
2641 const bool acceptsTouchEvents = receiver->acceptTouchEvents() || hasHandlers;
2642 auto device = event->device();
2644 device->capabilities().testFlag(QInputDevice::Capability::MouseEmulation)) {
2645 qCDebug(lcTouchTarget) << "skipping filtering of synth-mouse event from" << device;
2646 } else if (acceptsTouchEvents || receiver->acceptedMouseButtons()) {
2647 // get a touch event customized for delivery to filteringParent
2648 // TODO should not be necessary? because QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem() does it
2649 QMutableTouchEvent filteringParentTouchEvent;
2650 QQuickItemPrivate::get(receiver)->localizedTouchEvent(static_cast<QTouchEvent *>(event), true, &filteringParentTouchEvent);
2651 if (filteringParentTouchEvent.type() != QEvent::None) {
2652 qCDebug(lcTouch) << "letting parent" << filteringParent << "filter for" << receiver << &filteringParentTouchEvent;
2653 filtered = filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent);
2654 if (filtered) {
2655 qCDebug(lcTouch) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
2656 event->setAccepted(filteringParentTouchEvent.isAccepted());
2657 skipDelivery.append(filteringParent);
2658 if (event->isAccepted()) {
2659 for (auto point : filteringParentTouchEvent.points()) {
2660 const QQuickItem *exclusiveGrabber = qobject_cast<const QQuickItem *>(event->exclusiveGrabber(point));
2661 if (!exclusiveGrabber || !exclusiveGrabber->keepTouchGrab())
2662 event->setExclusiveGrabber(point, filteringParent);
2663 }
2664 }
2666 !filteringParent->acceptTouchEvents()) {
2667 qCDebug(lcTouch) << "touch event NOT intercepted by childMouseEventFilter of " << filteringParent
2668 << "; accepts touch?" << filteringParent->acceptTouchEvents()
2669 << "receiver accepts touch?" << acceptsTouchEvents
2670 << "so, letting parent filter a synth-mouse event";
2671 // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event.
2672 for (auto &tp : filteringParentTouchEvent.points()) {
2674 switch (tp.state()) {
2677 break;
2680 break;
2682 continue;
2683 default:
2685 break;
2686 }
2687
2688 bool touchMouseUnset = (touchMouseId == -1);
2689 // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId
2690 if (touchMouseUnset || touchMouseId == tp.id()) {
2691 // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.)
2692 // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that
2693 QMutableSinglePointEvent mouseEvent;
2694 touchToMouseEvent(t, tp, &filteringParentTouchEvent, &mouseEvent);
2695 // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should
2696 // report the touchpoint's grabber. Whenever we send a synthetic mouse event,
2697 // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed.
2698 touchMouseId = tp.id();
2699 touchMouseDevice = event->pointingDevice();
2700 filtered = filteringParent->childMouseEventFilter(receiver, &mouseEvent);
2701 if (filtered) {
2702 qCDebug(lcTouch) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent;
2703 event->setAccepted(mouseEvent.isAccepted());
2704 skipDelivery.append(filteringParent);
2705 if (event->isAccepted() && event->isBeginEvent()) {
2706 qCDebug(lcTouchTarget) << "TP (mouse)" << Qt::hex << tp.id() << "->" << filteringParent;
2707 filteringParentTouchEvent.setExclusiveGrabber(tp, filteringParent);
2708 touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set
2709 filteringParent->grabMouse();
2710 }
2711 }
2712 if (touchMouseUnset)
2713 // Now that we're done sending a synth mouse event, and it wasn't grabbed,
2714 // the touchpoint is no longer acting as a synthetic mouse. Restore previous state.
2716 mouseEvent.point(0).setAccepted(false); // because touchToMouseEvent() set it true
2717 // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter
2718 // has been called once, we're done with this loop over the touchpoints.
2719 break;
2720 }
2721 }
2722 }
2723 }
2724 }
2725 }
2726 }
2727 return sendFilteredPointerEventImpl(event, receiver, filteringParent->parentItem()) || filtered;
2728}
2729
2742{
2743 if (!filteringParent)
2744 return false;
2745
2746 QQuickItemPrivate *filteringParentPrivate = QQuickItemPrivate::get(filteringParent);
2747 if (filteringParentPrivate->replayingPressEvent)
2748 return false;
2749
2750 bool filtered = false;
2751 if (filteringParentPrivate->filtersChildMouseEvents && !hasFiltered.contains(filteringParent)) {
2752 hasFiltered.append(filteringParent);
2753 if (filteringParent->childMouseEventFilter(receiver, event)) {
2754 filtered = true;
2755 skipDelivery.append(filteringParent);
2756 }
2757 qCDebug(lcMouseTarget) << "for" << receiver << filteringParent << "childMouseEventFilter ->" << filtered;
2758 }
2759
2760 return sendFilteredMouseEvent(event, receiver, filteringParent->parentItem()) || filtered;
2761}
2762
2771{
2773 bool dragVelocityLimitAvailable = event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
2774 && styleHints->startDragVelocity();
2775 bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
2776 if (dragVelocityLimitAvailable) {
2777 QVector2D velocityVec = event->point(0).velocity();
2778 qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
2779 overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
2780 }
2781 return overThreshold;
2782}
2783
2791bool QQuickDeliveryAgentPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint &tp, int startDragThreshold)
2792{
2793 QStyleHints *styleHints = qApp->styleHints();
2794 bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
2795 const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0);
2796 if (!overThreshold && dragVelocityLimitAvailable) {
2797 qreal velocity = axis == Qt::XAxis ? tp.velocity().x() : tp.velocity().y();
2798 overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
2799 }
2800 return overThreshold;
2801}
2802
2809{
2810 int threshold = qApp->styleHints()->startDragDistance();
2811 return qAbs(delta.x()) > threshold || qAbs(delta.y()) > threshold;
2812}
2813
2814#ifndef QT_NO_DEBUG_STREAM
2816{
2817 QDebugStateSaver saver(debug);
2818 debug.nospace();
2819 if (!da) {
2820 debug << "QQuickDeliveryAgent(0)";
2821 return debug;
2822 }
2823
2824 debug << "QQuickDeliveryAgent(";
2825 if (!da->objectName().isEmpty())
2826 debug << da->objectName() << ' ';
2827 auto root = da->rootItem();
2828 if (Q_LIKELY(root)) {
2829 debug << "root=" << root->metaObject()->className();
2830 if (!root->objectName().isEmpty())
2831 debug << ' ' << root->objectName();
2832 } else {
2833 debug << "root=0";
2834 }
2835 debug << ')';
2836 return debug;
2837}
2838#endif
2839
2841
2842#include "moc_qquickdeliveryagent_p.cpp"
IOBluetoothDevice * device
\inmodule QtCore
static bool sendEvent(QObject *receiver, QEvent *event)
Sends event event directly to receiver receiver, using the notify() function.
static bool testAttribute(Qt::ApplicationAttribute attribute)
Returns true if attribute attribute is set; otherwise returns false.
\inmodule QtCore
\inmodule QtCore
\inmodule QtGui
Definition qevent.h:165
The QEventPoint class provides information about a point in a QPointerEvent.
Definition qeventpoint.h:20
bool isAccepted() const
int id
the ID number of this event point.
Definition qeventpoint.h:24
QPointF scenePosition
the scene position of this point.
Definition qeventpoint.h:39
void setAccepted(bool accepted=true)
ulong timestamp
the most recent time at which this point was included in a QPointerEvent.
Definition qeventpoint.h:27
QPointF sceneLastPosition
the scene position of this point from the previous press or move event.
Definition qeventpoint.h:42
const QPointingDevice * device
the pointing device from which this event point originates.
Definition qeventpoint.h:23
QVector2D velocity
a velocity vector, in units of pixels per second, in the coordinate.
Definition qeventpoint.h:34
\inmodule QtCore
Definition qcoreevent.h:45
bool spontaneous() const
Returns true if the event originated outside the application (a system event); otherwise returns fals...
Definition qcoreevent.h:305
Type
This enum type defines the valid event types in Qt.
Definition qcoreevent.h:51
@ TabletMove
Definition qcoreevent.h:121
@ TabletEnterProximity
Definition qcoreevent.h:209
@ FocusAboutToChange
Definition qcoreevent.h:68
@ ShortcutOverride
Definition qcoreevent.h:158
@ FocusOut
Definition qcoreevent.h:67
@ InputMethod
Definition qcoreevent.h:120
@ NativeGesture
Definition qcoreevent.h:246
@ DragEnter
Definition qcoreevent.h:101
@ InputMethodQuery
Definition qcoreevent.h:261
@ UngrabMouse
Definition qcoreevent.h:234
@ KeyRelease
Definition qcoreevent.h:65
@ MouseMove
Definition qcoreevent.h:63
@ KeyPress
Definition qcoreevent.h:64
@ FocusIn
Definition qcoreevent.h:66
@ TouchCancel
Definition qcoreevent.h:264
@ MouseButtonPress
Definition qcoreevent.h:60
@ TouchUpdate
Definition qcoreevent.h:242
@ TouchBegin
Definition qcoreevent.h:241
@ HoverLeave
Definition qcoreevent.h:176
@ HoverEnter
Definition qcoreevent.h:175
@ TabletRelease
Definition qcoreevent.h:127
@ DragLeave
Definition qcoreevent.h:103
@ HoverMove
Definition qcoreevent.h:177
@ TabletPress
Definition qcoreevent.h:126
@ MouseButtonDblClick
Definition qcoreevent.h:62
@ TabletLeaveProximity
Definition qcoreevent.h:210
@ MouseButtonRelease
Definition qcoreevent.h:61
Type type() const
Returns the event type.
Definition qcoreevent.h:304
bool isAccepted() const
Definition qcoreevent.h:308
virtual QEvent * clone() const
Creates and returns an identical copy of this event.
void accept()
Sets the accept flag of the event object, the equivalent of calling setAccepted(true).
Definition qcoreevent.h:310
bool isEmpty() const noexcept
Definition qflatmap_p.h:579
iterator begin()
Definition qflatmap_p.h:769
iterator end()
Definition qflatmap_p.h:773
iterator erase(iterator it)
Definition qflatmap_p.h:608
iterator find(const Key &key)
Definition qflatmap_p.h:816
T value(const Key &key, const T &defaultValue) const
Definition qflatmap_p.h:636
The QFocusEvent class contains event parameters for widget focus events.
Definition qevent.h:470
virtual bool contains(const QPointF &point) const
Returns true if this item contains point, which is in local coordinates; otherwise,...
QGraphicsWidget * window() const
bool isEnabled() const
Returns true if the item is enabled; otherwise, false is returned.
bool acceptHoverEvents() const
Qt::MouseButtons acceptedMouseButtons() const
Returns the mouse buttons that this item accepts mouse events for.
QGraphicsItem * parentItem() const
Returns a pointer to this item's parent item.
bool isVisible() const
Returns true if the item is visible; otherwise, false is returned.
bool acceptTouchEvents() const
QPointF mapFromScene(const QPointF &point) const
Maps the point point, which is in this item's scene's coordinate system, to this item's coordinate sy...
static struct QGuiApplicationPrivate::QLastCursorPosition lastCursorPosition
static QPlatformTheme * platformTheme()
static QWindowList allWindows()
Returns a list of all the windows in the application.
static QObject * focusObject()
Returns the QObject in currently active window that will be final receiver of events tied to focus,...
static QStyleHints * styleHints()
Returns the application's style hints.
static QWindow * focusWindow()
Returns the QWindow that receives events tied to focus, such as key events.
static QInputMethod * inputMethod()
returns the input method.
static Qt::KeyboardModifiers keyboardModifiers()
Returns the current state of the modifier keys on the keyboard.
\inmodule QtGui
Definition qevent.h:246
static QInputDevicePrivate * get(QInputDevice *q)
The QInputDevice class describes a device from which a QInputEvent originates.
DeviceType type
static QList< const QInputDevice * > devices()
Returns a list of all registered input devices (keyboards and pointing devices).
const QInputDevice * device() const
Definition qevent.h:54
quint64 timestamp() const
Returns the window system's timestamp for this event.
Definition qevent.h:58
Qt::KeyboardModifiers modifiers() const
Returns the keyboard modifier flags that existed immediately before the event occurred.
Definition qevent.h:56
The QKeyEvent class describes a key event.
Definition qevent.h:424
int count() const
Returns the number of keys involved in this event.
Definition qevent.h:445
Qt::KeyboardModifiers modifiers() const
Returns the keyboard modifier flags that existed immediately after the event occurred.
Definition qevent.cpp:1468
QString text() const
Returns the Unicode text that this key generated.
Definition qevent.h:443
bool isAutoRepeat() const
Returns true if this event comes from an auto-repeating key; returns false if it comes from an initia...
Definition qevent.h:444
int key() const
Returns the code of the key that was pressed or released.
Definition qevent.h:434
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
T & first()
Definition qlist.h:645
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:488
iterator end()
Definition qlist.h:626
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
qsizetype removeAll(const AT &t)
Definition qlist.h:592
iterator begin()
Definition qlist.h:625
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtGui
Definition qevent.h:196
static Q_GUI_EXPORT void detach(QEventPoint &p)
static Q_GUI_EXPORT void setTimestamp(QEventPoint &p, ulong t)
uint wasDeleted
Definition qobject.h:78
QObjectList children
Definition qobject.h:74
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
\inmodule QtCore\reentrant
Definition qpoint.h:217
bool isNull() const noexcept
Returns true if both the x and y coordinates are set to 0.0 (ignoring the sign); otherwise returns fa...
Definition qpoint.h:338
\inmodule QtCore\reentrant
Definition qpoint.h:25
A base class for pointer events.
Definition qevent.h:73
virtual bool isEndEvent() const
Definition qevent.h:92
const QPointingDevice * pointingDevice() const
Returns the source device from which this event originates.
Definition qevent.cpp:330
void setTimestamp(quint64 timestamp) override
Definition qevent.cpp:338
void setExclusiveGrabber(const QEventPoint &point, QObject *exclusiveGrabber)
Informs the delivery logic that the given exclusiveGrabber is to receive all future update events and...
Definition qevent.cpp:369
virtual bool isBeginEvent() const
Definition qevent.h:90
QEventPoint & point(qsizetype i)
Returns a QEventPoint reference for the point at index i.
Definition qevent.cpp:240
QObject * exclusiveGrabber(const QEventPoint &point) const
Returns the object which has been set to receive all future update events and the release event conta...
Definition qevent.cpp:351
virtual void setAccepted(bool accepted) override
\reimp
Definition qevent.cpp:318
const QList< QEventPoint > & points() const
Returns a list of points in this pointer event.
Definition qevent.h:87
virtual bool isUpdateEvent() const
Definition qevent.h:91
static bool setPassiveGrabberContext(EventPointData *epd, QObject *grabber, QObject *context)
static QPointingDevicePrivate * get(QPointingDevice *q)
The QPointingDevice class describes a device from which mouse, touch or tablet events originate.
GrabTransition
This enum represents a transition of exclusive or passive grab from one object (possibly nullptr) to ...
void grabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, const QPointerEvent *event, const QEventPoint &point)
This signal is emitted when the grabber object gains or loses an exclusive or passive grab of point d...
static QQmlAnimationTimer * instance()
void removeGrabber(QQuickItem *grabber, bool mouse=true, bool touch=true, bool cancel=false)
Ungrabs all touchpoint grabs and/or the mouse grab from the given item grabber.
bool deliverPressOrReleaseEvent(QPointerEvent *, bool handlersOnly=false)
void deliverPointerEvent(QPointerEvent *)
void touchToMouseEvent(QEvent::Type type, const QEventPoint &p, const QTouchEvent *touchEvent, QMutableSinglePointEvent *mouseEvent)
QFlatMap< QPointer< QQuickItem >, uint > hoverItems
void handleWindowHidden(QQuickWindow *win)
bool sendFilteredPointerEvent(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent=nullptr)
QStack< QPointerEvent * > eventsInDelivery
QList< const QPointingDevice * > knownPointingDevices
QVector< QQuickItem * > mergePointerTargets(const QVector< QQuickItem * > &list1, const QVector< QQuickItem * > &list2) const
bool sendHoverEvent(QEvent::Type, QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp)
bool deliverHoverEventToItem(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, HoverChange hoverChange)
bool deliverHoverEventRecursive(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp)
bool deliverTouchAsMouse(QQuickItem *item, QTouchEvent *pointerEvent)
QList< QObject * > exclusiveGrabbers(QPointerEvent *ev)
void onGrabChanged(QObject *grabber, QPointingDevice::GrabTransition transition, const QPointerEvent *event, const QEventPoint &point)
QVector< QQuickItem * > hasFiltered
static bool isTabletEvent(const QPointerEvent *ev)
static QQuickPointingDeviceExtra * deviceExtra(const QInputDevice *device)
void deliverUpdatedPoints(QPointerEvent *event)
static bool anyPointGrabbed(const QPointerEvent *ev)
void translateTouchEvent(QTouchEvent *touchEvent)
static bool allPointsGrabbed(const QPointerEvent *ev)
bool sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
bool sendFilteredPointerEventImpl(QPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
static bool isMouseOrWheelEvent(const QPointerEvent *ev)
QVector< QQuickItem * > pointerTargets(QQuickItem *, const QPointerEvent *event, const QEventPoint &point, bool checkMouseButtons, bool checkAcceptsTouch) const
std::unique_ptr< QMutableTouchEvent > delayedTouch
static bool isHoverEvent(const QPointerEvent *ev)
void handleWindowDeactivate(QQuickWindow *win)
static QQuickDeliveryAgent * currentEventDeliveryAgent
QQuickDeliveryAgent::Transform * sceneTransform
void deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly=false)
bool checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos)
static bool isSynthMouse(const QPointerEvent *ev)
static bool isTouchEvent(const QPointerEvent *ev)
static bool isMouseEvent(const QPointerEvent *ev)
static QPointerEvent * clonePointerEvent(QPointerEvent *event, std::optional< QPointF > transformedLocalPos=std::nullopt)
QPointingDevicePrivate::EventPointData * mousePointData()
bool allUpdatedPointsAccepted(const QPointerEvent *ev)
void handleMouseEvent(QMouseEvent *)
Handle event on behalf of this delivery agent's window or subscene.
QPointerEvent * eventInDelivery() const
static void notifyFocusChangesRecur(QQuickItem **item, int remaining, Qt::FocusReason reason)
static QQuickDeliveryAgent * currentOrItemDeliveryAgent(const QQuickItem *item)
bool deliverSinglePointEventUntilAccepted(QPointerEvent *)
QQuickItem * focusTargetItem() const
Returns the item that should get active focus when the root focus scope gets active focus.
bool clearHover(ulong timestamp=0)
bool deliverTouchCancelEvent(QTouchEvent *)
static bool dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold=-1)
QQuickDeliveryAgentPrivate(QQuickItem *root)
void deliverToPassiveGrabbers(const QVector< QPointer< QObject > > &passiveGrabbers, QPointerEvent *pointerEvent)
const QPointingDevice * touchMouseDevice
static bool isEventFromMouseOrTouchpad(const QPointerEvent *ev)
bool deliverHoverEvent(const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp)
void ensureDeviceConnected(const QPointingDevice *dev)
static void localizePointerEvent(QPointerEvent *ev, const QQuickItem *dest)
QVector< QQuickItem * > skipDelivery
void flushFrameSynchronousEvents(QQuickWindow *win)
void setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions={ })
Set the focus inside scope to be item.
void clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions={ })
Transform * sceneTransform() const
void setSceneTransform(Transform *transform)
QQuickItem * rootItem() const
QQuickDeliveryAgent(QQuickItem *rootItem)
bool event(QEvent *ev) override
Handle ev on behalf of this delivery agent's window or subscene.
ItemList::iterator iterator
void copyActions(const QDropEvent &from)
virtual void itemFocusChanged(QQuickItem *, Qt::FocusReason)
QLazilyAllocated< ExtraData, ExtraDataTags > extra
void notifyChangeListeners(QQuickItemPrivate::ChangeTypes changeTypes, Fn &&function, Args &&...args)
QList< QQuickItem * > paintOrderChildItems() const
QQuickDeliveryAgent * deliveryAgent()
virtual bool handlePointerEvent(QPointerEvent *, bool avoidGrabbers=false)
bool hasPointerHandlers() const
void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &)
quint32 notifiedActiveFocus
void localizedTouchEvent(const QTouchEvent *event, bool isFiltering, QMutableTouchEvent *localized)
Qt::MouseButtons acceptedMouseButtons() const
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
QPointF mapToScene(const QPointF &point) const
Maps the given point in this item's coordinate system to the equivalent point within the scene's coor...
Flags flags() const
Returns the item flags for this item.
bool acceptTouchEvents() const
Returns whether touch events are accepted by this item.
Qt::MouseButtons acceptedMouseButtons() const
Returns the mouse buttons accepted by this item.
bool isFocusScope() const
Returns true if this item is a focus scope, and false otherwise.
virtual void mouseUngrabEvent()
This event handler can be reimplemented in a subclass to be notified when a mouse ungrab event has oc...
QPointF mapFromScene(const QPointF &point) const
Maps the given point in the scene's coordinate system to the equivalent point within this item's coor...
bool hasFocus() const
bool keepTouchGrab() const
Returns whether the touch points grabbed by this item should exclusively remain with this item.
QQuickWindow * window() const
Returns the window in which this item is rendered.
QQuickItem * scopedFocusItem() const
If this item is a focus scope, this returns the item in its focus chain that currently has focus.
QQuickItem * parentItem() const
bool isEnabled() const
@ ItemActiveFocusHasChanged
Definition qquickitem.h:151
@ ItemClipsChildrenToShape
Definition qquickitem.h:130
static QVector< QObject * > & deviceDeliveryTargets(const QInputDevice *device)
void handlePointerEvent(QPointerEvent *event)
static bool isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin)
static QQuickWindowPrivate * get(QQuickWindow *c)
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:484
iterator end()
Definition qset.h:140
A base class for pointer events containing a single point, such as mouse events.
Definition qevent.h:109
QPointF scenePosition() const
Returns the position of the point in this event, relative to the window or scene.
Definition qevent.h:121
T & top()
Returns a reference to the stack's top item.
Definition qstack.h:19
T pop()
Removes the top item from the stack and returns it.
Definition qstack.h:18
void push(const T &t)
Adds element t to the top of the stack.
Definition qstack.h:17
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
The QStyleHints class contains platform specific hints and settings. \inmodule QtGui.
Definition qstylehints.h:17
int startDragDistance
the distance, in pixels, that the mouse must be moved with a button held down before a drag and drop ...
Definition qstylehints.h:41
int startDragVelocity
the limit for the velocity, in pixels per second, that the mouse may be moved, with a button held dow...
Definition qstylehints.h:43
QEventPoint & point(int touchId)
QMap< int, QEventPoint > points
The QTouchEvent class contains parameters that describe a touch event.
Definition qevent.h:917
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:502
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:501
QScreen * screen() const
Returns the screen the widget is on.
Definition qwidget.cpp:2496
\inmodule QtGui
Definition qwindow.h:63
void focusObjectChanged(QObject *object)
This signal is emitted when the final receiver of events tied to focus is changed to object.
EGLImageKHR int int EGLuint64KHR * modifiers
QString text
QPushButton * button
[2]
bool focus
[0]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
Definition qcompare.h:63
@ ImInputItemClipRectangle
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
@ LeftButton
Definition qnamespace.h:58
@ RightButton
Definition qnamespace.h:59
@ NoButton
Definition qnamespace.h:57
@ MouseEventSynthesizedByQt
@ MouseEventSynthesizedBySystem
@ AA_SynthesizeMouseForUnhandledTouchEvents
Definition qnamespace.h:437
@ ScrollUpdate
FocusReason
@ OtherFocusReason
@ XAxis
Definition qctf_p.h:75
#define Q_UNLIKELY(x)
#define Q_LIKELY(x)
#define qApp
#define qGuiApp
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
return ret
static const QMetaObjectPrivate * priv(const uint *data)
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION double qInf()
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLfloat GLfloat GLfloat w
[0]
GLenum GLenum GLsizei count
GLenum type
GLenum target
GLfloat GLfloat GLfloat GLfloat h
struct _cl_event * event
GLuint GLenum GLenum transform
GLuint res
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const void * pointer
Definition qopenglext.h:384
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLuint * states
QT_BEGIN_NAMESPACE constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
T qmlobject_cast(QObject *object)
This method is identical to qobject_cast<T>() except that it does not require lazy QMetaObjects to be...
QQuickItem * qmlobject_cast< QQuickItem * >(QObject *object)
static bool allowSyntheticRightClick()
QT_BEGIN_NAMESPACE Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text=QString(), bool autorep=false, ushort count=1)
static bool windowHasFocus(QQuickWindow *win)
static QQuickItem * findFurthestFocusScopeAncestor(QQuickItem *item)
QDebug operator<<(QDebug debug, const QQuickDeliveryAgent *da)
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:492
const QQuickItem * qobject_cast< const QQuickItem * >(const QObject *o)
Definition qquickitem.h:498
const QQuickItem * rootItem(const I &item)
#define Q_QUICK_INPUT_PROFILE(Type, DetailType, A, B)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define emit
#define Q_UNUSED(x)
unsigned long ulong
Definition qtypes.h:35
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned short ushort
Definition qtypes.h:33
double qreal
Definition qtypes.h:187
Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text=QString(), bool autorep=false, ushort count=1)
QWidget * win
Definition settings.cpp:6
future cancel()
QGraphicsItem * item
QList< QTreeWidgetItem * > items
QLayoutItem * child
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]
bool contains(const AT &t) const noexcept
Definition qlist.h:45
virtual QPointF map(const QPointF &point)=0