Qt 6.x
The Qt SDK
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
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 <memory>
22
24
25Q_LOGGING_CATEGORY(lcTouch, "qt.quick.touch")
26Q_LOGGING_CATEGORY(lcTouchCmprs, "qt.quick.touch.compression")
27Q_LOGGING_CATEGORY(lcTouchTarget, "qt.quick.touch.target")
28Q_LOGGING_CATEGORY(lcMouse, "qt.quick.mouse")
29Q_LOGGING_CATEGORY(lcMouseTarget, "qt.quick.mouse.target")
30Q_LOGGING_CATEGORY(lcTablet, "qt.quick.tablet")
31Q_LOGGING_CATEGORY(lcPtr, "qt.quick.pointer")
32Q_LOGGING_CATEGORY(lcPtrLoc, "qt.quick.pointer.localization")
33Q_LOGGING_CATEGORY(lcPtrGrab, "qt.quick.pointer.grab")
34Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target")
35Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target")
36Q_LOGGING_CATEGORY(lcHoverTrace, "qt.quick.hover.trace")
37Q_LOGGING_CATEGORY(lcFocus, "qt.quick.focus")
38
39extern 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);
40
41bool QQuickDeliveryAgentPrivate::subsceneAgentsExist(false);
42QQuickDeliveryAgent *QQuickDeliveryAgentPrivate::currentEventDeliveryAgent(nullptr);
43
45{
46 static int allowRightClick = -1;
47 if (allowRightClick < 0) {
48 bool ok = false;
49 allowRightClick = qEnvironmentVariableIntValue("QT_QUICK_ALLOW_SYNTHETIC_RIGHT_CLICK", &ok);
50 if (!ok)
51 allowRightClick = 1; // user didn't opt out
52 }
53 return allowRightClick != 0;
54}
55
57{
59 QMutableSinglePointEvent ret(type, touchEvent->pointingDevice(), p,
62 touchEvent->modifiers(), Qt::MouseEventSynthesizedByQt);
63 ret.setAccepted(true); // this now causes the persistent touchpoint to be accepted too
64 ret.setTimestamp(touchEvent->timestamp());
65 *mouseEvent = ret;
66 // It's very important that the recipient of the event shall be able to see that
67 // this "mouse" event actually comes from a touch device.
68 Q_ASSERT(mouseEvent->device() == touchEvent->device());
70 qWarning() << "Unexpected: synthesized an indistinguishable mouse event" << mouseEvent;
71}
72
73bool QQuickDeliveryAgentPrivate::checkIfDoubleTapped(ulong newPressEventTimestamp, QPoint newPressPos)
74{
75 bool doubleClicked = false;
76
78 QPoint distanceBetweenPresses = newPressPos - touchMousePressPos;
79 const int doubleTapDistance = QGuiApplication::styleHints()->touchDoubleTapDistance();
80 doubleClicked = (qAbs(distanceBetweenPresses.x()) <= doubleTapDistance) && (qAbs(distanceBetweenPresses.y()) <= doubleTapDistance);
81
82 if (doubleClicked) {
83 ulong timeBetweenPresses = newPressEventTimestamp - touchMousePressTimestamp;
84 ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->
85 mouseDoubleClickInterval());
86 doubleClicked = timeBetweenPresses < doubleClickInterval;
87 }
88 }
89 if (doubleClicked) {
91 } else {
92 touchMousePressTimestamp = newPressEventTimestamp;
93 touchMousePressPos = newPressPos;
94 }
95
96 return doubleClicked;
97}
98
108{
110 return nullptr;
111 return eventsInDelivery.top();
112}
113
120{
122 return nullptr;
124 return devPriv->pointById(isDeliveringTouchAsMouse() ? touchMouseId : 0);
125}
126
128{
129 qCDebug(lcTouchTarget) << "id" << touchMouseId << "on" << touchMouseDevice;
130 touchMouseId = -1;
131 touchMouseDevice = nullptr;
132}
133
135{
138 auto device = pointerEvent->pointingDevice();
139
140 // A touch event from a trackpad is likely to be followed by a mouse or gesture event, so mouse event synth is redundant
142 qCDebug(lcTouchTarget) << q << "skipping delivery of synth-mouse event from" << device;
143 return false;
144 }
145
146 // FIXME: make this work for mouse events too and get rid of the asTouchEvent in here.
148 QQuickItemPrivate::get(item)->localizedTouchEvent(pointerEvent, false, &event);
149 if (!event.points().size())
150 return false;
151
152 // For each point, check if it is accepted, if not, try the next point.
153 // Any of the fingers can become the mouse one.
154 // This can happen because a mouse area might not accept an event at some point but another.
155 for (auto &p : event.points()) {
156 // A new touch point
157 if (touchMouseId == -1 && p.state() & QEventPoint::State::Pressed) {
158 QPointF pos = item->mapFromScene(p.scenePosition());
159
160 // probably redundant, we check bounds in the calling function (matchingNewPoints)
161 if (!item->contains(pos))
162 break;
163
164 qCDebug(lcTouchTarget) << q << device << "TP (mouse)" << Qt::hex << p.id() << "->" << item;
165 QMutableSinglePointEvent mousePress;
167
168 // Send a single press and see if that's accepted
169 QCoreApplication::sendEvent(item, &mousePress);
170 event.setAccepted(mousePress.isAccepted());
171 if (mousePress.isAccepted()) {
173 touchMouseId = p.id();
174 const auto &pt = mousePress.point(0);
175 if (!mousePress.exclusiveGrabber(pt))
176 mousePress.setExclusiveGrabber(pt, item);
177
178 if (checkIfDoubleTapped(event.timestamp(), p.globalPosition().toPoint())) {
179 // since we synth the mouse event from from touch, we respect the
180 // QPlatformTheme::TouchDoubleTapDistance instead of QPlatformTheme::MouseDoubleClickDistance
181 QMutableSinglePointEvent mouseDoubleClick;
183 QCoreApplication::sendEvent(item, &mouseDoubleClick);
184 event.setAccepted(mouseDoubleClick.isAccepted());
185 if (!mouseDoubleClick.isAccepted())
187 }
188
189 return true;
190 }
191 // try the next point
192
193 // Touch point was there before and moved
194 } else if (touchMouseDevice == device && p.id() == touchMouseId) {
195 if (p.state() & QEventPoint::State::Updated) {
196 if (touchMousePressTimestamp != 0) {
198 const QPoint moveDelta = p.globalPosition().toPoint() - touchMousePressPos;
199 if (moveDelta.x() >= doubleTapDistance || moveDelta.y() >= doubleTapDistance)
200 touchMousePressTimestamp = 0; // Got dragged too far, dismiss the double tap
201 }
202 if (QQuickItem *mouseGrabberItem = qmlobject_cast<QQuickItem *>(pointerEvent->exclusiveGrabber(p))) {
206 event.setAccepted(me.isAccepted());
207 if (me.isAccepted())
208 qCDebug(lcTouchTarget) << q << device << "TP (mouse)" << Qt::hex << p.id() << "->" << mouseGrabberItem;
209 return event.isAccepted();
210 } else {
211 // no grabber, check if we care about mouse hover
212 // FIXME: this should only happen once, not recursively... I'll ignore it just ignore hover now.
213 // hover for touch???
220
221 deliverHoverEvent(me.scenePosition(), last, me.modifiers(), me.timestamp());
222 break;
223 }
224 } else if (p.state() & QEventPoint::State::Released) {
225 // currently handled point was released
226 if (QQuickItem *mouseGrabberItem = qmlobject_cast<QQuickItem *>(pointerEvent->exclusiveGrabber(p))) {
230
232 QPointF localMousePos(qInf(), qInf());
233 if (QWindow *w = item->window())
234 localMousePos = item->mapFromScene(w->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition));
236 Qt::NoButton, Qt::NoButton, event.modifiers());
238 }
239 if (pointerEvent->exclusiveGrabber(p) == mouseGrabberItem) // might have ungrabbed due to event
240 pointerEvent->setExclusiveGrabber(p, nullptr);
241
243 return me.isAccepted();
244 }
245 }
246 break;
247 }
248 }
249 return false;
250}
251
261void QQuickDeliveryAgentPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch, bool cancel)
262{
265 // do it the expensive way
266 for (auto dev : knownPointingDevices) {
267 auto devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(dev));
268 devPriv->removeGrabber(grabber, cancel);
269 }
270 return;
271 }
273 if (Q_LIKELY(mouse) && eventInDelivery) {
274 auto epd = mousePointData();
275 if (epd && epd->exclusiveGrabber == grabber && epd->exclusiveGrabberContext.data() == q) {
276 QQuickItem *oldGrabber = qobject_cast<QQuickItem *>(epd->exclusiveGrabber);
277 qCDebug(lcMouseTarget) << "removeGrabber" << oldGrabber << "-> null";
278 eventInDelivery->setExclusiveGrabber(epd->eventPoint, nullptr);
279 }
280 }
281 if (Q_LIKELY(touch)) {
282 bool ungrab = false;
283 const auto touchDevices = QPointingDevice::devices();
284 for (auto device : touchDevices) {
286 continue;
287 if (QPointingDevicePrivate::get(const_cast<QPointingDevice *>(static_cast<const QPointingDevice *>(device)))->
288 removeExclusiveGrabber(eventInDelivery, grabber))
289 ungrab = true;
290 }
291 if (ungrab)
292 grabber->touchUngrabEvent();
293 }
294}
295
302{
303 for (qsizetype i = 0; i != touchEvent->pointCount(); ++i) {
304 auto &pt = touchEvent->point(i);
305 QMutableEventPoint::setScenePosition(pt, pt.position());
306 }
307}
308
309
310static inline bool windowHasFocus(QQuickWindow *win)
311{
312 const QWindow *focusWindow = QGuiApplication::focusWindow();
313 return win == focusWindow || QQuickRenderControlPrivate::isRenderWindowFor(win, focusWindow) || !focusWindow;
314}
315
316#ifdef Q_OS_WEBOS
317// Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
318static inline bool singleWindowOnScreen(QQuickWindow *win)
319{
320 const QWindowList windowList = QGuiApplication::allWindows();
321 for (int i = 0; i < windowList.count(); i++) {
322 QWindow *ii = windowList.at(i);
323 if (ii == win)
324 continue;
325 if (ii->screen() == win->screen())
326 return false;
327 }
328
329 return true;
330}
331#endif
332
339 Qt::FocusReason reason, FocusOptions options)
340{
342 Q_ASSERT(item);
343 Q_ASSERT(scope || item == rootItem);
344
345 qCDebug(lcFocus) << q << "focus" << item << "in scope" << scope;
346 if (scope)
347 qCDebug(lcFocus) << " scopeSubFocusItem:" << QQuickItemPrivate::get(scope)->subFocusItem;
348
349 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : nullptr;
351
352 QQuickItem *oldActiveFocusItem = nullptr;
353 QQuickItem *currentActiveFocusItem = activeFocusItem;
354 QQuickItem *newActiveFocusItem = nullptr;
355 bool sendFocusIn = false;
356
357 lastFocusReason = reason;
358
360
361 // Does this change the active focus?
362 if (item == rootItem || scopePrivate->activeFocus) {
363 oldActiveFocusItem = activeFocusItem;
364 if (item->isEnabled()) {
365 newActiveFocusItem = item;
366 while (newActiveFocusItem->isFocusScope()
367 && newActiveFocusItem->scopedFocusItem()
368 && newActiveFocusItem->scopedFocusItem()->isEnabled()) {
369 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
370 }
371 } else {
372 newActiveFocusItem = scope;
373 }
374
375 if (oldActiveFocusItem) {
376#if QT_CONFIG(im)
378#endif
379
380 activeFocusItem = nullptr;
381
382 QQuickItem *afi = oldActiveFocusItem;
383 while (afi && afi != scope) {
384 if (QQuickItemPrivate::get(afi)->activeFocus) {
386 changed << afi;
387 }
388 afi = afi->parentItem();
389 }
390 }
391 }
392
393 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
394 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
395 if (oldSubFocusItem) {
397 priv->focus = false;
398 priv->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, oldSubFocusItem, reason);
399 changed << oldSubFocusItem;
400 }
401
403 }
404
405 if (!(options & DontChangeFocusProperty)) {
407#ifdef Q_OS_WEBOS
408 // Allow focused if there is only one window in the screen where it belongs.
409 // Temporary fix for webOS until multi-seat is implemented see QTBUG-85272
410 || singleWindowOnScreen(rootItem->window())
411#endif
412 ) {
413 itemPrivate->focus = true;
415 changed << item;
416 }
417 }
418
419 if (newActiveFocusItem && rootItem->hasFocus()) {
420 activeFocusItem = newActiveFocusItem;
421
422 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
423 changed << newActiveFocusItem;
424
425 QQuickItem *afi = newActiveFocusItem->parentItem();
426 while (afi && afi != scope) {
427 if (afi->isFocusScope()) {
429 changed << afi;
430 }
431 afi = afi->parentItem();
432 }
434 sendFocusIn = true;
435 }
436
437 // Now that all the state is changed, emit signals & events
438 // We must do this last, as this process may result in further changes to focus.
439 if (oldActiveFocusItem) {
441 QCoreApplication::sendEvent(oldActiveFocusItem, &event);
442 }
443
444 // Make sure that the FocusOut didn't result in another focus change.
445 if (sendFocusIn && activeFocusItem == newActiveFocusItem) {
447 QCoreApplication::sendEvent(newActiveFocusItem, &event);
448 }
449
450 if (activeFocusItem != currentActiveFocusItem)
452
453 if (!changed.isEmpty())
454 notifyFocusChangesRecur(changed.data(), changed.size() - 1, reason);
455 if (isSubsceneAgent) {
457 qCDebug(lcFocus) << " delegating setFocusInScope to" << da;
458 QQuickWindowPrivate::get(rootItem->window())->deliveryAgentPrivate()->setFocusInScope(da->rootItem(), item, reason, options);
459 }
460 if (oldActiveFocusItem == activeFocusItem)
461 qCDebug(lcFocus) << " activeFocusItem remains" << activeFocusItem << "in" << q;
462 else
463 qCDebug(lcFocus) << " activeFocusItem" << oldActiveFocusItem << "->" << activeFocusItem << "in" << q;
464}
465
467{
468 Q_ASSERT(item);
469 Q_ASSERT(scope || item == rootItem);
471 qCDebug(lcFocus) << q << "clear focus" << item << "in scope" << scope;
472
473 QQuickItemPrivate *scopePrivate = nullptr;
474 if (scope) {
475 scopePrivate = QQuickItemPrivate::get(scope);
476 if ( !scopePrivate->subFocusItem )
477 return; // No focus, nothing to do.
478 }
479
480 QQuickItem *currentActiveFocusItem = activeFocusItem;
481 QQuickItem *oldActiveFocusItem = nullptr;
482 QQuickItem *newActiveFocusItem = nullptr;
483
484 lastFocusReason = reason;
485
487
488 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
489
490 // Does this change the active focus?
491 if (item == rootItem || scopePrivate->activeFocus) {
492 oldActiveFocusItem = activeFocusItem;
493 newActiveFocusItem = scope;
494
495#if QT_CONFIG(im)
497#endif
498
499 activeFocusItem = nullptr;
500
501 if (oldActiveFocusItem) {
502 QQuickItem *afi = oldActiveFocusItem;
503 while (afi && afi != scope) {
504 if (QQuickItemPrivate::get(afi)->activeFocus) {
506 changed << afi;
507 }
508 afi = afi->parentItem();
509 }
510 }
511 }
512
513 if (item != rootItem && !(options & DontChangeSubFocusItem)) {
514 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
515 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
517 priv->focus = false;
518 priv->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, oldSubFocusItem, reason);
519 changed << oldSubFocusItem;
520 }
521
523
524 } else if (!(options & DontChangeFocusProperty)) {
526 priv->focus = false;
528 changed << item;
529 }
530
531 if (newActiveFocusItem) {
532 Q_ASSERT(newActiveFocusItem == scope);
533 activeFocusItem = scope;
535 }
536
537 // Now that all the state is changed, emit signals & events
538 // We must do this last, as this process may result in further changes to focus.
539 if (oldActiveFocusItem) {
541 QCoreApplication::sendEvent(oldActiveFocusItem, &event);
542 }
543
544 // Make sure that the FocusOut didn't result in another focus change.
545 if (newActiveFocusItem && activeFocusItem == newActiveFocusItem) {
547 QCoreApplication::sendEvent(newActiveFocusItem, &event);
548 }
549
550 if (activeFocusItem != currentActiveFocusItem)
552
553 if (!changed.isEmpty())
554 notifyFocusChangesRecur(changed.data(), changed.size() - 1, reason);
555 if (isSubsceneAgent) {
557 qCDebug(lcFocus) << " delegating clearFocusInScope to" << da;
558 QQuickWindowPrivate::get(rootItem->window())->deliveryAgentPrivate()->clearFocusInScope(da->rootItem(), item, reason, options);
559 }
560 if (oldActiveFocusItem == activeFocusItem)
561 qCDebug(lcFocus) << "activeFocusItem remains" << activeFocusItem << "in" << q;
562 else
563 qCDebug(lcFocus) << " activeFocusItem" << oldActiveFocusItem << "->" << activeFocusItem << "in" << q;
564}
565
567{
569 return;
570
572}
573
575{
577
578 if (item) {
580
581 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
582 itemPrivate->notifiedFocus = itemPrivate->focus;
584 emit item->focusChanged(itemPrivate->focus);
585 }
586
587 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
588 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
589 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, bool(itemPrivate->activeFocus));
591 emit item->activeFocusChanged(itemPrivate->activeFocus);
592 }
593 }
594
595 if (remaining)
596 notifyFocusChangesRecur(items + 1, remaining - 1, reason);
597}
598
600{
601 if (hoverItems.isEmpty())
602 return false;
603
605 if (!window)
606 return false;
607
608 const QPointF lastPos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition);
610 const bool clearHover = true;
611
612 for (auto hoverItem : hoverItems) {
613 auto item = hoverItem.first;
614 if (item)
615 deliverHoverEventToItem(item, lastPos, lastPos, modifiers, timestamp, clearHover);
616 }
617
619
620 return true;
621}
622
624{
625#if QT_CONFIG(im)
629 QGuiApplication::inputMethod()->setInputItemRectangle(QRectF(0, 0, focusPrivate->width, focusPrivate->height));
631 }
632#endif
633}
634
642{
645 if (item)
646 return QQuickItemPrivate::get(const_cast<QQuickItem *>(item))->deliveryAgent();
647 return nullptr;
648}
649
657{
658}
659
661{
662}
663
665{
666}
667
673{
674 Q_D(const QQuickDeliveryAgent);
675 return d->rootItem;
676}
677
684{
685 Q_D(const QQuickDeliveryAgent);
686 return d->sceneTransform;
687}
688
695{
697 if (d->sceneTransform == transform)
698 return;
699 qCDebug(lcPtr) << this << d->sceneTransform << "->" << transform;
700 if (d->sceneTransform)
701 delete d->sceneTransform;
702 d->sceneTransform = transform;
703}
704
713{
715 d->currentEventDeliveryAgent = this;
716 auto cleanup = qScopeGuard([d] { d->currentEventDeliveryAgent = nullptr; });
717
718 switch (ev->type()) {
722 case QEvent::MouseMove: {
723 QMouseEvent *me = static_cast<QMouseEvent*>(ev);
724 d->handleMouseEvent(me);
725 break;
726 }
729 case QEvent::HoverMove: {
730 QHoverEvent *he = static_cast<QHoverEvent*>(ev);
731 bool accepted = d->deliverHoverEvent(he->scenePosition(),
733 he->modifiers(), he->timestamp());
734 d->lastMousePosition = he->scenePosition();
735 he->setAccepted(accepted);
736#if QT_CONFIG(cursor)
737 QQuickWindowPrivate::get(d->rootItem->window())->updateCursor(d->sceneTransform ?
738 d->sceneTransform->map(he->scenePosition()) : he->scenePosition(), d->rootItem);
739#endif
740 return accepted;
741 }
744 case QEvent::TouchEnd: {
745 QTouchEvent *touch = static_cast<QTouchEvent*>(ev);
746 d->handleTouchEvent(touch);
748 // we consume all touch events ourselves to avoid duplicate
749 // mouse delivery by QtGui mouse synthesis
750 ev->accept();
751 }
752 break;
753 }
755 // return in order to avoid the QWindow::event below
756 return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(ev));
757 break;
758 case QEvent::Enter: {
759 if (!d->rootItem)
760 return false;
761 QEnterEvent *enter = static_cast<QEnterEvent*>(ev);
762 const auto scenePos = enter->scenePosition();
763 bool accepted = d->deliverHoverEvent(scenePos,
764 enter->points().first().sceneLastPosition(),
765 enter->modifiers(), enter->timestamp());
766 d->lastMousePosition = scenePos;
767 // deliverHoverEvent() constructs QHoverEvents: check that EPD didn't end up with corrupted scenePos
768 Q_ASSERT(enter->scenePosition() == scenePos);
769 enter->setAccepted(accepted);
770#if QT_CONFIG(cursor)
771 QQuickWindowPrivate::get(d->rootItem->window())->updateCursor(enter->scenePosition(), d->rootItem);
772#endif
773 return accepted;
774 }
775 case QEvent::Leave:
776 d->clearHover();
777 d->lastMousePosition = QPointF();
778 break;
779#if QT_CONFIG(quick_draganddrop)
782 case QEvent::DragMove:
783 case QEvent::Drop:
784 d->deliverDragEvent(d->dragGrabber, ev);
785 break;
786#endif
788#if QT_CONFIG(im)
789 if (d->activeFocusItem)
790 qGuiApp->inputMethod()->commit();
791#endif
792 break;
793#if QT_CONFIG(gestures)
795 d->deliverSinglePointEventUntilAccepted(static_cast<QPointerEvent *>(ev));
796 break;
797#endif
799 d->deliverKeyEvent(static_cast<QKeyEvent *>(ev));
800 break;
803 {
804 QQuickItem *target = d->activeFocusItem;
805 // while an input method delivers the event, this window might still be inactive
806 if (!target) {
807 target = d->rootItem;
808 if (!target || !target->isEnabled())
809 break;
810 // see setFocusInScope for a similar loop
811 while (target->isFocusScope() && target->scopedFocusItem() && target->scopedFocusItem()->isEnabled())
812 target = target->scopedFocusItem();
813 }
814 if (target)
816 }
817 break;
818#if QT_CONFIG(wheelevent)
819 case QEvent::Wheel: {
820 auto event = static_cast<QWheelEvent *>(ev);
821 qCDebug(lcMouse) << event;
822
823 //if the actual wheel event was accepted, accept the compatibility wheel event and return early
824 if (d->lastWheelEventAccepted && event->angleDelta().isNull() && event->phase() == Qt::ScrollUpdate)
825 return true;
826
827 event->ignore();
828 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseWheel,
829 event->angleDelta().x(), event->angleDelta().y());
830 d->deliverSinglePointEventUntilAccepted(event);
831 d->lastWheelEventAccepted = event->isAccepted();
832 break;
833 }
834#endif
835#if QT_CONFIG(tabletevent)
839 {
840 auto *tabletEvent = static_cast<QTabletEvent *>(ev);
841 d->deliverPointerEvent(tabletEvent); // visits HoverHandlers too (unlike the mouse event case)
842#if QT_CONFIG(cursor)
843 QQuickWindowPrivate::get(d->rootItem->window())->updateCursor(tabletEvent->scenePosition(), d->rootItem);
844#endif
845 }
846 break;
847#endif
848 default:
849 return false;
850 }
851
852 return true;
853}
854
856{
857 if (activeFocusItem) {
858 const bool keyPress = (e->type() == QEvent::KeyPress);
859 switch (e->type()) {
860 case QEvent::KeyPress:
861 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(), e->modifiers());
862 break;
864 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(), e->modifiers());
865 break;
866 default:
867 break;
868 }
869
870 QQuickItem *item = activeFocusItem;
871
872 // In case of generated event, trigger ShortcutOverride event
873 if (keyPress && e->spontaneous() == false)
874 qt_sendShortcutOverrideEvent(item, e->timestamp(),
875 e->key(), e->modifiers(), e->text(),
876 e->isAutoRepeat(), e->count());
877
878 do {
879 e->accept();
881 } while (!e->isAccepted() && (item = item->parentItem()));
882 }
883}
884
887 rootItem(root),
888 // a plain QQuickItem can be a subscene root; a QQuickRootItem always belongs directly to a QQuickWindow
889 isSubsceneAgent(!qmlobject_cast<QQuickRootItem *>(rootItem))
890{
891#if QT_CONFIG(quick_draganddrop)
892 dragGrabber = new QQuickDragGrabber;
893#endif
894 if (isSubsceneAgent)
895 subsceneAgentsExist = true;
896}
897
899{
900#if QT_CONFIG(quick_draganddrop)
901 delete dragGrabber;
902 dragGrabber = nullptr;
903#endif
904 delete sceneTransform;
905}
906
915{
916 QPointerEvent *ret = event->clone();
917 QEventPoint &point = ret->point(0);
920 if (transformedLocalPos)
921 QMutableEventPoint::setPosition(point, *transformedLocalPos);
922
923 return ret;
924}
925
926void QQuickDeliveryAgentPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QObject> > &passiveGrabbers,
927 QPointerEvent *pointerEvent)
928{
929 const QVector<QObject *> &eventDeliveryTargets =
931 QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult;
932 hasFiltered.clear();
933 for (QObject *grabberObject : passiveGrabbers) {
934 // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically
935 if (Q_UNLIKELY(!grabberObject))
936 continue;
937 // a passiveGrabber might be an item or a handler
938 if (QQuickPointerHandler *handler = qobject_cast<QQuickPointerHandler *>(grabberObject)) {
939 if (handler && !eventDeliveryTargets.contains(handler)) {
940 bool alreadyFiltered = false;
941 QQuickItem *par = handler->parentItem();
942
943 // see if we already have sent a filter event to the parent
944 auto it = std::find_if(sendFilteredPointerEventResult.begin(), sendFilteredPointerEventResult.end(),
945 [par](const QPair<QQuickItem *, bool> &pair) { return pair.first == par; });
946 if (it != sendFilteredPointerEventResult.end()) {
947 // Yes, the event was sent to that parent for filtering: do not call it again, but use
948 // the result of the previous call to determine whether we should call the handler.
949 alreadyFiltered = it->second;
950 } else if (par) {
951 alreadyFiltered = sendFilteredPointerEvent(pointerEvent, par);
952 sendFilteredPointerEventResult << qMakePair(par, alreadyFiltered);
953 }
954 if (!alreadyFiltered) {
955 if (par)
956 localizePointerEvent(pointerEvent, par);
957 handler->handlePointerEvent(pointerEvent);
958 }
959 }
960 } else if (QQuickItem *grabberItem = static_cast<QQuickItem *>(grabberObject)) {
961 // don't steal the grab if input should remain with the exclusive grabber only
962 if (QQuickItem *excGrabber = static_cast<QQuickItem *>(pointerEvent->exclusiveGrabber(pointerEvent->point(0)))) {
963 if ((isMouseEvent(pointerEvent) && excGrabber->keepMouseGrab())
964 || (isTouchEvent(pointerEvent) && excGrabber->keepTouchGrab())) {
965 return;
966 }
967 }
968 localizePointerEvent(pointerEvent, grabberItem);
969 QCoreApplication::sendEvent(grabberItem, pointerEvent);
970 pointerEvent->accept();
971 }
972 }
973}
974
976 const QPointF &scenePos, const QPointF &lastScenePos,
977 Qt::KeyboardModifiers modifiers, ulong timestamp)
978{
979 auto itemPrivate = QQuickItemPrivate::get(item);
980 const auto transform = itemPrivate->windowToItemTransform();
981 const auto transformToGlobal = itemPrivate->windowToGlobalTransform();
982 auto globalPos = transformToGlobal.map(scenePos);
983 QHoverEvent hoverEvent(type, scenePos, globalPos, transform.map(lastScenePos), modifiers);
984 hoverEvent.setTimestamp(timestamp);
985 hoverEvent.setAccepted(true);
986 QEventPoint &point = hoverEvent.point(0);
987 QMutableEventPoint::setPosition(point, transform.map(scenePos));
988 QMutableEventPoint::setGlobalLastPosition(point, transformToGlobal.map(lastScenePos));
989
990 hasFiltered.clear();
991 if (sendFilteredMouseEvent(&hoverEvent, item, item->parentItem()))
992 return true;
993
994 QCoreApplication::sendEvent(item, &hoverEvent);
995
996 return hoverEvent.isAccepted();
997}
998
1004// TODO later: specify the device in case of multi-mouse scenario, or mouse and tablet both in use
1006 const QPointF &scenePos, const QPointF &lastScenePos,
1007 Qt::KeyboardModifiers modifiers, ulong timestamp)
1008{
1009 // The first time this function is called, hoverItems is empty.
1010 // We then call deliverHoverEventRecursive from the rootItem, and
1011 // populate the list with all the children and grandchildren that
1012 // we find that should receive hover events (in addition to sending
1013 // hover events to them and their HoverHandlers). We also set the
1014 // hoverId for each item to the currentHoverId.
1015 // The next time this function is called, we bump currentHoverId,
1016 // and call deliverHoverEventRecursive once more.
1017 // When that call returns, the list will contain the items that
1018 // were hovered the first time, as well as the items that were hovered
1019 // this time. But only the items that were hovered this time
1020 // will have their hoverId equal to currentHoverId; the ones we didn't
1021 // visit will still have an old hoverId. We can therefore go through the
1022 // list at the end of this function and look for items with an old hoverId,
1023 // remove them from the list, and update their state accordingly.
1024
1025 const bool subtreeHoverEnabled = QQuickItemPrivate::get(rootItem)->subtreeHoverEnabled;
1026 const bool itemsWasHovered = !hoverItems.isEmpty();
1027
1028 if (!subtreeHoverEnabled && !itemsWasHovered)
1029 return false;
1030
1032
1033 if (subtreeHoverEnabled) {
1034 hoveredLeafItemFound = false;
1035 deliverHoverEventRecursive(rootItem, scenePos, lastScenePos, modifiers, timestamp);
1036 }
1037
1038 // Prune the list for items that are no longer hovered
1039 for (auto it = hoverItems.begin(); it != hoverItems.end();) {
1040 auto item = (*it).first.data();
1041 auto hoverId = (*it).second;
1042 if (hoverId == currentHoverId) {
1043 // Still being hovered
1044 it++;
1045 } else {
1046 // No longer hovered. If hoverId is 0, it means that we have sent a HoverLeave
1047 // event to the item already, and it can just be removed from the list. Note that
1048 // the item can have been deleted as well.
1049 if (item && hoverId != 0) {
1050 const bool clearHover = true;
1051 deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, clearHover);
1052 }
1053 it = hoverItems.erase(it);
1054 }
1055 }
1056
1057 const bool itemsAreHovered = !hoverItems.isEmpty();
1058 return itemsWasHovered || itemsAreHovered;
1059}
1060
1098 QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1099 Qt::KeyboardModifiers modifiers, ulong timestamp)
1100{
1101
1102 const QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1103 const QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1104
1105 for (int ii = children.size() - 1; ii >= 0; --ii) {
1106 QQuickItem *child = children.at(ii);
1107 const QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
1108
1109 if (!child->isVisible() || childPrivate->culled)
1110 continue;
1111 if (!childPrivate->subtreeHoverEnabled)
1112 continue;
1113 if (childPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1114 const QPointF localPos = child->mapFromScene(scenePos);
1115 if (!child->contains(localPos))
1116 continue;
1117 }
1118
1119 // Recurse into the child
1120 const bool accepted = deliverHoverEventRecursive(child, scenePos, lastScenePos, modifiers, timestamp);
1121 if (accepted) {
1122 // Stop propagation / recursion
1123 return true;
1124 }
1126 // Don't propagate to siblings, only to ancestors
1127 break;
1128 }
1129 }
1130
1131 // All decendants have been visited.
1132 // Now deliver the event to the item
1133 return deliverHoverEventToItem(item, scenePos, lastScenePos, modifiers, timestamp, false);
1134
1135 // Continue propagation / recursion
1136 return false;
1137}
1138
1148 QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1149 Qt::KeyboardModifiers modifiers, ulong timestamp, bool clearHover)
1150{
1152 const QPointF localPos = item->mapFromScene(scenePos);
1153 const QPointF globalPos = item->mapToGlobal(localPos);
1154 const bool isHovering = item->contains(localPos);
1155 const bool wasHovering = hoverItems.contains(item);
1156
1157 qCDebug(lcHoverTrace) << "item:" << item << "scene pos:" << scenePos << "localPos:" << localPos
1158 << "wasHovering:" << wasHovering << "isHovering:" << isHovering;
1159
1160 bool accepted = false;
1161
1162 // Start by sending out enter/move/leave events to the item.
1163 // Note that hoverEnabled only controls if we should send out hover events to the
1164 // item itself. HoverHandlers are not included, and are dealt with separately below.
1165 if (itemPrivate->hoverEnabled && isHovering && !clearHover) {
1166 // Add the item to the list of hovered items (if it doesn't exist there
1167 // from before), and update hoverId to mark that it's (still) hovered.
1168 // Also set hoveredLeafItemFound, so that only propagate in a straight
1169 // line towards the root from now on.
1170 hoveredLeafItemFound = true;
1172 if (wasHovering)
1173 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, timestamp);
1174 else
1175 accepted = sendHoverEvent(QEvent::HoverEnter, item, scenePos, lastScenePos, modifiers, timestamp);
1176 } else if (wasHovering) {
1177 // A leave should never stop propagation
1178 hoverItems[item] = 0;
1179 sendHoverEvent(QEvent::HoverLeave, item, scenePos, lastScenePos, modifiers, timestamp);
1180 }
1181
1182 if (!itemPrivate->hasPointerHandlers())
1183 return accepted;
1184
1185 // Next, send out hover events to the hover handlers.
1186 // If the item didn't accept the hover event, 'accepted' is now false.
1187 // Otherwise it's true, and then it should stay the way regardless of
1188 // whether or not the hoverhandlers themselves are hovered.
1189 // Note that since a HoverHandler can have a margin, a HoverHandler
1190 // can be hovered even if the item itself is not.
1191
1192 if (clearHover) {
1193 // Note: a leave should never stop propagation
1194 QHoverEvent hoverEvent(QEvent::HoverLeave, scenePos, globalPos, lastScenePos, modifiers);
1195 hoverEvent.setTimestamp(timestamp);
1196
1197 for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) {
1198 if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h)) {
1199 if (!hh->isHovered())
1200 continue;
1201 hoverEvent.setAccepted(true);
1202 QCoreApplication::sendEvent(hh, &hoverEvent);
1203 }
1204 }
1205 } else {
1206 QMouseEvent hoverEvent(QEvent::MouseMove, localPos, scenePos, globalPos, Qt::NoButton, Qt::NoButton, modifiers);
1207 hoverEvent.setTimestamp(timestamp);
1208
1209 for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers) {
1210 if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h)) {
1211 if (!hh->enabled())
1212 continue;
1213 hoverEvent.setAccepted(true);
1214 hh->handlePointerEvent(&hoverEvent);
1215 if (hh->isHovered()) {
1216 // Mark the whole item as updated, even if only the handler is
1217 // actually in a hovered state (because of HoverHandler.margins)
1218 hoveredLeafItemFound = true;
1220 if (hh->isBlocking()) {
1221 qCDebug(lcHoverTrace) << "skipping rest of hover delivery due to blocking" << hh;
1222 accepted = true;
1223 break;
1224 }
1225 }
1226 }
1227 }
1228 }
1229
1230 return accepted;
1231}
1232
1233// Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers
1234// in the usual reverse-paint-order until propagation is stopped
1236{
1237 Q_ASSERT(event->points().size() == 1);
1239 QEventPoint &point = event->point(0);
1240 QVector<QQuickItem *> targetItems = pointerTargets(rootItem, event, point, false, false);
1241 point.setAccepted(false);
1242
1243 // Let passive grabbers see the event. This must be done before we deliver the
1244 // event to the target and to handlers that might stop event propagation.
1245 // Passive grabbers cannot stop event delivery.
1246 for (const auto &passiveGrabber : event->passiveGrabbers(point)) {
1247 if (auto *grabberItem = qobject_cast<QQuickItem *>(passiveGrabber)) {
1248 if (targetItems.contains(grabberItem))
1249 continue;
1250 localizePointerEvent(event, grabberItem);
1251 QCoreApplication::sendEvent(grabberItem, event);
1252 }
1253 }
1254 // Maintain the invariant that items receive input events in accepted state.
1255 // A passive grabber might have explicitly ignored the event.
1256 event->accept();
1257
1258 for (QQuickItem *item : targetItems) {
1261 // Let Pointer Handlers have the first shot
1262 itemPrivate->handlePointerEvent(event);
1263 if (point.isAccepted())
1264 return true;
1265 event->accept();
1267 if (event->isAccepted()) {
1268 qCDebug(lcWheelTarget) << event << "->" << item;
1269 return true;
1270 }
1271 }
1272
1273 return false; // it wasn't handled
1274}
1275
1277{
1278 qCDebug(lcTouch) << event;
1279
1280 // An incoming TouchCancel event will typically not contain any points,
1281 // but sendTouchCancelEvent() adds the points that have grabbers to the event.
1282 // Deliver it to all items and handlers that have active touches.
1283 const_cast<QPointingDevicePrivate *>(QPointingDevicePrivate::get(event->pointingDevice()))->
1284 sendTouchCancelEvent(event);
1285
1287
1288 return true;
1289}
1290
1292{
1293 // Deliver and delete delayedTouch.
1294 // Set delayedTouch to nullptr before delivery to avoid redelivery in case of
1295 // event loop recursions (e.g if it the touch starts a dnd session).
1296 std::unique_ptr<QTouchEvent> e(std::move(delayedTouch));
1297 qCDebug(lcTouchCmprs) << "delivering" << e.get();
1299 deliverPointerEvent(e.get());
1300}
1301
1313{
1315 qCDebug(lcFocus) << "deactivated" << win->title();
1316 const auto inputDevices = QInputDevice::devices();
1317 for (auto device : inputDevices) {
1318 if (auto pointingDevice = qobject_cast<const QPointingDevice *>(device)) {
1319 auto devPriv = QPointingDevicePrivate::get(const_cast<QPointingDevice *>(pointingDevice));
1320 for (auto epd : devPriv->activePoints.values()) {
1321 if (!epd.exclusiveGrabber.isNull()) {
1322 bool relevant = false;
1323 if (QQuickItem *item = qmlobject_cast<QQuickItem *>(epd.exclusiveGrabber.data()))
1324 relevant = (item->window() == win);
1325 else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(epd.exclusiveGrabber.data())) {
1326 if (handler->parentItem())
1327 relevant = (handler->parentItem()->window() == win && epd.exclusiveGrabberContext.data() == q);
1328 else
1329 // a handler with no Item parent probably has a 3D Model parent.
1330 // TODO actually check the window somehow
1331 relevant = true;
1332 }
1333 if (relevant)
1334 devPriv->setExclusiveGrabber(nullptr, epd.eventPoint, nullptr);
1335 }
1336 // For now, we don't clearPassiveGrabbers(), just in case passive grabs
1337 // can be useful to keep monitoring the mouse even after window deactivation.
1338 }
1339 }
1340 }
1341}
1342
1344{
1345 qCDebug(lcFocus) << "hidden" << win->title();
1346 clearHover();
1348}
1349
1351{
1352 for (auto &point : ev->points()) {
1353 if (point.state() != QEventPoint::State::Pressed && !point.isAccepted())
1354 return false;
1355 }
1356 return true;
1357}
1358
1366{
1367 for (int i = 0; i < ev->pointCount(); ++i) {
1368 auto &point = ev->point(i);
1369 QMutableEventPoint::setPosition(point, dest->mapFromScene(point.scenePosition()));
1370 qCDebug(lcPtrLoc) << ev->type() << "@" << point.scenePosition() << "to"
1371 << dest << "@" << dest->mapToScene(QPointF()) << "->" << point;
1372 }
1373}
1374
1376{
1378 for (const QEventPoint &point : ev->points()) {
1379 if (QObject *grabber = ev->exclusiveGrabber(point)) {
1380 if (!result.contains(grabber))
1381 result << grabber;
1382 }
1383 }
1384 return result;
1385}
1386
1388{
1389 for (const QEventPoint &point : ev->points()) {
1390 if (ev->exclusiveGrabber(point) || !ev->passiveGrabbers(point).isEmpty())
1391 return true;
1392 }
1393 return false;
1394}
1395
1397{
1398 for (const auto &point : ev->points()) {
1399 if (!ev->exclusiveGrabber(point) && ev->passiveGrabbers(point).isEmpty())
1400 return false;
1401 }
1402 return true;
1403}
1404
1406{
1407 switch (ev->type()) {
1411 case QEvent::MouseMove:
1412 return true;
1413 default:
1414 return false;
1415 }
1416}
1417
1419{
1420 return isMouseEvent(ev) || ev->type() == QEvent::Wheel;
1421}
1422
1424{
1425 switch (ev->type()) {
1426 case QEvent::HoverEnter:
1427 case QEvent::HoverMove:
1428 case QEvent::HoverLeave:
1429 return true;
1430 default:
1431 return false;
1432 }
1433}
1434
1436{
1437 switch (ev->type()) {
1438 case QEvent::TouchBegin:
1440 case QEvent::TouchEnd:
1442 return true;
1443 default:
1444 return false;
1445 }
1446}
1447
1449{
1450 switch (ev->type()) {
1452 case QEvent::TabletMove:
1456 return true;
1457 default:
1458 return false;
1459 }
1460}
1461
1463{
1464 const auto devType = ev->device()->type();
1465 return devType == QInputDevice::DeviceType::Mouse ||
1467}
1468
1470{
1471 return (!isEventFromMouseOrTouchpad(ev) && isMouseEvent(ev));
1472}
1473
1475{
1477 if (devPriv->qqExtra)
1478 return static_cast<QQuickPointingDeviceExtra *>(devPriv->qqExtra);
1479 auto extra = new QQuickPointingDeviceExtra;
1480 devPriv->qqExtra = extra;
1482 delete static_cast<QQuickPointingDeviceExtra *>(devPriv->qqExtra);
1483 devPriv->qqExtra = nullptr;
1484 });
1485 return extra;
1486}
1487
1513{
1514 // If this is a subscene agent, don't store any events, because
1515 // flushFrameSynchronousEvents() is only called on the window's DA.
1516 if (isSubsceneAgent)
1517 return false;
1518
1519 QEventPoint::States states = event->touchPointStates();
1521 qCDebug(lcTouchCmprs) << "no compression" << event;
1522 // we can only compress an event that doesn't include any pressed or released points
1523 return false;
1524 }
1525
1526 if (!delayedTouch) {
1527 delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), event->points()));
1528 delayedTouch->setTimestamp(event->timestamp());
1529 for (qsizetype i = 0; i < delayedTouch->pointCount(); ++i) {
1530 auto &tp = delayedTouch->point(i);
1532 }
1534 qCDebug(lcTouchCmprs) << "delayed" << compressedTouchCount << delayedTouch.get();
1536 window->maybeUpdate();
1537 return true;
1538 }
1539
1540 // check if this looks like the last touch event
1541 if (delayedTouch->type() == event->type() &&
1542 delayedTouch->device() == event->device() &&
1543 delayedTouch->modifiers() == event->modifiers() &&
1544 delayedTouch->pointCount() == event->pointCount())
1545 {
1546 // possible match.. is it really the same?
1547 bool mismatch = false;
1548
1549 auto tpts = event->points();
1550 for (qsizetype i = 0; i < event->pointCount(); ++i) {
1551 const auto &tp = tpts.at(i);
1552 const auto &tpDelayed = delayedTouch->point(i);
1553 if (tp.id() != tpDelayed.id()) {
1554 mismatch = true;
1555 break;
1556 }
1557
1558 if (tpDelayed.state() == QEventPoint::State::Updated && tp.state() == QEventPoint::State::Stationary)
1559 QMutableEventPoint::setState(tpts[i], QEventPoint::State::Updated);
1560 }
1561
1562 // matching touch event? then give delayedTouch a merged set of touchpoints
1563 if (!mismatch) {
1564 // have to create a new event because QMutableTouchEvent::setTouchPoints() is missing
1565 // TODO optimize, or move event compression elsewhere
1566 delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(), event->modifiers(), tpts));
1567 delayedTouch->setTimestamp(event->timestamp());
1568 for (qsizetype i = 0; i < delayedTouch->pointCount(); ++i) {
1569 auto &tp = delayedTouch->point(i);
1571 }
1573 qCDebug(lcTouchCmprs) << "coalesced" << compressedTouchCount << delayedTouch.get();
1575 window->maybeUpdate();
1576 return true;
1577 }
1578 }
1579
1580 // merging wasn't possible, so deliver the delayed event first, and then delay this one
1582 delayedTouch.reset(new QMutableTouchEvent(event->type(), event->pointingDevice(),
1583 event->modifiers(), event->points()));
1584 delayedTouch->setTimestamp(event->timestamp());
1585 return true;
1586}
1587
1588// entry point for touch event delivery:
1589// - translate the event to window coordinates
1590// - compress the event instead of delivering it if applicable
1591// - call deliverTouchPoints to actually dispatch the points
1593{
1596 // TODO remove: touch and mouse should be independent until we come to touch->mouse synth
1597 if (event->pointCount()) {
1598 auto &point = event->point(0);
1599 if (point.state() == QEventPoint::State::Released) {
1601 } else {
1602 lastMousePosition = point.position();
1603 }
1604 }
1605
1606 qCDebug(lcTouch) << q << event;
1607
1608 static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION");
1609
1610 if (qquickwindow_no_touch_compression || pointerEventRecursionGuard) {
1612 return;
1613 }
1614
1615 if (!compressTouchEvent(event)) {
1616 if (delayedTouch) {
1618 qCDebug(lcTouchCmprs) << "resuming delivery" << event;
1619 }
1621 }
1622}
1623
1628{
1630 // We generally don't want OS-synthesized mouse events, because Qt Quick does its own touch->mouse synthesis.
1631 // But if the platform converts long-press to right-click, it's ok to react to that,
1632 // unless the user has opted out by setting QT_QUICK_ALLOW_SYNTHETIC_RIGHT_CLICK=0.
1633 if (event->source() == Qt::MouseEventSynthesizedBySystem &&
1634 !(event->button() == Qt::RightButton && allowSyntheticRightClick())) {
1635 event->accept();
1636 return;
1637 }
1638 qCDebug(lcMouse) << q << event;
1639
1640 switch (event->type()) {
1642 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMousePress, event->button(),
1643 event->buttons());
1645 break;
1647 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseRelease, event->button(),
1648 event->buttons());
1650#if QT_CONFIG(cursor)
1651 QQuickWindowPrivate::get(rootItem->window())->updateCursor(event->scenePosition());
1652#endif
1653 break;
1655 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick,
1656 event->button(), event->buttons());
1658 break;
1659 case QEvent::MouseMove: {
1660 Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove,
1661 event->position().x(), event->position().y());
1662
1663 const QPointF last = lastMousePosition.isNull() ? event->scenePosition() : lastMousePosition;
1664 lastMousePosition = event->scenePosition();
1665 qCDebug(lcHoverTrace) << q << "mouse pos" << last << "->" << lastMousePosition;
1666 if (!event->points().size() || !event->exclusiveGrabber(event->point(0))) {
1667 bool accepted = deliverHoverEvent(event->scenePosition(), last, event->modifiers(), event->timestamp());
1668 event->setAccepted(accepted);
1669 }
1671#if QT_CONFIG(cursor)
1672 // The pointer event could result in a cursor change (reaction), so update it afterwards.
1673 QQuickWindowPrivate::get(rootItem->window())->updateCursor(event->scenePosition());
1674#endif
1675 break;
1676 }
1677 default:
1678 Q_ASSERT(false);
1679 break;
1680 }
1681}
1682
1697{
1701
1702 if (delayedTouch) {
1704
1705 // Touch events which constantly start animations (such as a behavior tracking
1706 // the mouse point) need animations to start.
1708 if (ut && ut->hasStartAnimationPending())
1709 ut->startAnimations();
1710 }
1711
1712 // In webOS we already have the alternative to the issue that this
1713 // wanted to address and thus skipping this part won't break anything.
1714#if !defined(Q_OS_WEBOS)
1715 // Once per frame, if any items are dirty, send a synthetic hover,
1716 // in case items have changed position, visibility, etc.
1717 // For instance, during animation (including the case of a ListView
1718 // whose delegates contain MouseAreas), a MouseArea needs to know
1719 // whether it has moved into a position where it is now under the cursor.
1720 // TODO do this for each known mouse device or come up with a different strategy
1721 if (frameSynchronousHoverEnabled && !win->mouseGrabberItem() &&
1723 qCDebug(lcHoverTrace) << q << "delivering frame-sync hover to root @" << lastMousePosition;
1725 qCDebug(lcHoverTrace) << q << "frame-sync hover delivery done";
1726 }
1727#else
1728 Q_UNUSED(win);
1729#endif
1732 qCWarning(lcPtr, "detected interleaved frame-sync and actual events");
1734}
1735
1745 const QPointerEvent *event, const QEventPoint &point)
1746{
1748 const bool grabGained = (transition == QPointingDevice::GrabTransition::GrabExclusive ||
1750
1751 QQuickDeliveryAgent *deliveryAgent = nullptr;
1752
1753 // note: event can be null, if the signal was emitted from QPointingDevicePrivate::removeGrabber(grabber)
1754 if (auto *handler = qmlobject_cast<QQuickPointerHandler *>(grabber)) {
1755 if (handler->parentItem()) {
1756 auto itemPriv = QQuickItemPrivate::get(handler->parentItem());
1757 deliveryAgent = itemPriv->deliveryAgent();
1758 if (deliveryAgent == q) {
1759 handler->onGrabChanged(handler, transition, const_cast<QPointerEvent *>(event),
1760 const_cast<QEventPoint &>(point));
1761 }
1762 if (grabGained) {
1763 // An item that is NOT a subscene root needs to track whether it got a grab via a subscene delivery agent,
1764 // whereas the subscene root item already knows it has its own DA.
1765 if (isSubsceneAgent && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
1766 itemPriv->maybeHasSubsceneDeliveryAgent = true;
1767 }
1768 } else if (!isSubsceneAgent) {
1769 handler->onGrabChanged(handler, transition, const_cast<QPointerEvent *>(event),
1770 const_cast<QEventPoint &>(point));
1771 }
1772 } else if (auto *grabberItem = qmlobject_cast<QQuickItem *>(grabber)) {
1773 switch (transition) {
1780 hasFiltered.clear();
1781 if (!sendFilteredMouseEvent(&e, grabberItem, grabberItem->parentItem())) {
1782 lastUngrabbed = grabberItem;
1783 grabberItem->mouseUngrabEvent();
1784 }
1785 }
1787 bool allReleasedOrCancelled = true;
1788 if (transition == QPointingDevice::UngrabExclusive && event) {
1789 for (const auto &pt : event->points()) {
1790 if (pt.state() != QEventPoint::State::Released) {
1791 allReleasedOrCancelled = false;
1792 break;
1793 }
1794 }
1795 }
1796 if (allReleasedOrCancelled)
1797 grabberItem->touchUngrabEvent();
1798 }
1799 break;
1800 default:
1801 break;
1802 }
1803 auto *itemPriv = QQuickItemPrivate::get(grabberItem);
1804 deliveryAgent = itemPriv->deliveryAgent();
1805 // An item that is NOT a subscene root needs to track whether it got a grab via a subscene delivery agent,
1806 // whereas the subscene root item already knows it has its own DA.
1807 if (isSubsceneAgent && grabGained && (!itemPriv->extra.isAllocated() || !itemPriv->extra->subsceneDeliveryAgent))
1808 itemPriv->maybeHasSubsceneDeliveryAgent = true;
1809 }
1810
1811 if (currentEventDeliveryAgent == q && event && event->device()) {
1812 switch (transition) {
1814 auto epd = QPointingDevicePrivate::get(const_cast<QPointingDevice*>(event->pointingDevice()))->queryPointById(point.id());
1815 Q_ASSERT(epd);
1817 qCDebug(lcPtr) << "remembering that" << q << "handles point" << point.id() << "after" << transition;
1818 } break;
1820 auto epd = QPointingDevicePrivate::get(const_cast<QPointingDevice*>(event->pointingDevice()))->queryPointById(point.id());
1821 Q_ASSERT(epd);
1822 epd->exclusiveGrabberContext = q;
1823 qCDebug(lcPtr) << "remembering that" << q << "handles point" << point.id() << "after" << transition;
1824 } break;
1827 // taken care of in QPointingDevicePrivate::setExclusiveGrabber(,,nullptr), removeExclusiveGrabber()
1828 break;
1831 // taken care of in QPointingDevicePrivate::removePassiveGrabber(), clearPassiveGrabbers()
1832 break;
1834 // not in use at this time
1835 break;
1836 }
1837 }
1838}
1839
1850{
1853 return;
1856 QObject::connect(dev, &QObject::destroyed, q, [this, dev] {this->knownPointingDevices.removeAll(dev);});
1857}
1858
1867{
1869 if (isTabletEvent(event))
1870 qCDebug(lcTablet) << q << event;
1871
1872 // If users spin the eventloop as a result of event delivery, we disable
1873 // event compression and send events directly. This is because we consider
1874 // the usecase a bit evil, but we at least don't want to lose events.
1877
1878 // So far this is for use in Qt Quick 3D: if a QEventPoint is grabbed,
1879 // updates get delivered here pretty directly, bypassing picking; but we need to
1880 // be able to map the 2D viewport coordinate to a 2D coordinate within
1881 // d->rootItem, a 2D scene that has been arbitrarily mapped onto a 3D object.
1882 QVarLengthArray<QPointF, 16> originalScenePositions;
1883 if (sceneTransform) {
1884 originalScenePositions.resize(event->pointCount());
1885 for (int i = 0; i < event->pointCount(); ++i) {
1886 auto &pt = event->point(i);
1887 originalScenePositions[i] = pt.scenePosition();
1888 QMutableEventPoint::setScenePosition(pt, sceneTransform->map(pt.scenePosition()));
1889 qCDebug(lcPtrLoc) << q << event->type() << pt.id() << "transformed scene pos" << pt.scenePosition();
1890 }
1891 } else if (isSubsceneAgent) {
1892 qCDebug(lcPtrLoc) << q << event->type() << "no scene transform set";
1893 }
1894
1895 skipDelivery.clear();
1897 if (sceneTransform)
1898 qCDebug(lcPtr) << q << "delivering with" << sceneTransform << event;
1899 else
1900 qCDebug(lcPtr) << q << "delivering" << event;
1901 for (int i = 0; i < event->pointCount(); ++i)
1902 event->point(i).setAccepted(false);
1903
1904 if (event->isBeginEvent()) {
1905 ensureDeviceConnected(event->pointingDevice());
1907 event->setAccepted(false);
1908 }
1911 if (event->isEndEvent())
1913
1914 // failsafe: never allow touch->mouse synthesis to persist after all touchpoints are released,
1915 // or after the touchmouse is released
1916 if (isTouchEvent(event) && touchMouseId >= 0) {
1917 if (static_cast<QTouchEvent *>(event)->touchPointStates() == QEventPoint::State::Released) {
1919 } else {
1920 auto touchMousePoint = event->pointById(touchMouseId);
1921 if (touchMousePoint && touchMousePoint->state() == QEventPoint::State::Released)
1923 }
1924 }
1925
1927 if (sceneTransform) {
1928 for (int i = 0; i < event->pointCount(); ++i)
1929 QMutableEventPoint::setScenePosition(event->point(i), originalScenePositions.at(i));
1930 }
1932 lastUngrabbed = nullptr;
1933}
1934
1958// FIXME: should this be iterative instead of recursive?
1960 bool checkMouseButtons, bool checkAcceptsTouch) const
1961{
1962 Q_Q(const QQuickDeliveryAgent);
1963 QVector<QQuickItem *> targets;
1964 auto itemPrivate = QQuickItemPrivate::get(item);
1965 QPointF itemPos = item->mapFromScene(point.scenePosition());
1966 bool relevant = item->contains(itemPos);
1967 qCDebug(lcPtrLoc) << q << "point" << point.id() << point.scenePosition() << "->" << itemPos << ": relevant?" << relevant << "to" << item << point;
1968 // if the item clips, we can potentially return early
1969 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1970 if (!item->clipRect().contains(itemPos))
1971 return targets;
1972 }
1973
1974 if (itemPrivate->hasPointerHandlers()) {
1975 if (!relevant)
1976 if (itemPrivate->anyPointerHandlerWants(event, point))
1977 relevant = true;
1978 } else {
1979 if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
1980 relevant = false;
1981 if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
1982 relevant = false;
1983 }
1984
1985 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1986 if (relevant) {
1987 auto it = std::lower_bound(children.begin(), children.end(), 0,
1988 [](auto lhs, auto rhs) -> bool { return lhs->z() < rhs; });
1990 }
1991
1992 for (int ii = children.size() - 1; ii >= 0; --ii) {
1993 QQuickItem *child = children.at(ii);
1994 auto childPrivate = QQuickItemPrivate::get(child);
1995 if (!child->isVisible() || !child->isEnabled() || childPrivate->culled ||
1996 (child != item && childPrivate->extra.isAllocated() && childPrivate->extra->subsceneDeliveryAgent))
1997 continue;
1998
1999 if (child != item)
2000 targets << pointerTargets(child, event, point, checkMouseButtons, checkAcceptsTouch);
2001 else
2002 targets << child;
2003 }
2004
2005 return targets;
2006}
2007
2013{
2014 QVector<QQuickItem *> targets = list1;
2015 // start at the end of list2
2016 // if item not in list, append it
2017 // if item found, move to next one, inserting before the last found one
2018 int insertPosition = targets.size();
2019 for (int i = list2.size() - 1; i >= 0; --i) {
2020 int newInsertPosition = targets.lastIndexOf(list2.at(i), insertPosition);
2021 if (newInsertPosition >= 0) {
2022 Q_ASSERT(newInsertPosition <= insertPosition);
2023 insertPosition = newInsertPosition;
2024 }
2025 // check for duplicates, only insert if the item isn't there already
2026 if (insertPosition == targets.size() || list2.at(i) != targets.at(insertPosition))
2027 targets.insert(insertPosition, list2.at(i));
2028 }
2029 return targets;
2030}
2031
2036{
2037 Q_Q(const QQuickDeliveryAgent);
2038 bool done = false;
2039 const auto grabbers = exclusiveGrabbers(event);
2040 hasFiltered.clear();
2041 for (auto grabber : grabbers) {
2042 // The grabber is guaranteed to be either an item or a handler.
2043 QQuickItem *receiver = qmlobject_cast<QQuickItem *>(grabber);
2044 if (!receiver) {
2045 // The grabber is not an item? It's a handler then. Let it have the event first.
2046 QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber);
2047 receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem();
2048 // Filtering via QQuickItem::childMouseEventFilter() is only possible
2049 // if the handler's parent is an Item. It could be a QQ3D object.
2050 if (receiver) {
2051 hasFiltered.clear();
2052 if (sendFilteredPointerEvent(event, receiver))
2053 done = true;
2054 localizePointerEvent(event, receiver);
2055 }
2056 handler->handlePointerEvent(event);
2057 }
2058 if (done)
2059 break;
2060 // If the grabber is an item or the grabbing handler didn't handle it,
2061 // then deliver the event to the item (which may have multiple handlers).
2062 hasFiltered.clear();
2063 if (receiver)
2064 deliverMatchingPointsToItem(receiver, true, event);
2065 }
2066
2067 // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once)
2068 for (auto &point : event->points()) {
2069 auto epd = QPointingDevicePrivate::get(event->pointingDevice())->queryPointById(point.id());
2070 if (Q_UNLIKELY(!epd)) {
2071 qWarning() << "point is not in activePoints" << point;
2072 continue;
2073 }
2074 QList<QPointer<QObject>> relevantPassiveGrabbers;
2075 for (int i = 0; i < epd->passiveGrabbersContext.size(); ++i) {
2076 if (epd->passiveGrabbersContext.at(i).data() == q)
2077 relevantPassiveGrabbers << epd->passiveGrabbers.at(i);
2078 }
2079 if (!relevantPassiveGrabbers.isEmpty())
2080 deliverToPassiveGrabbers(relevantPassiveGrabbers, event);
2081 }
2082
2083 if (done)
2084 return;
2085
2086 // If some points weren't grabbed, deliver only to non-grabber PointerHandlers in reverse paint order
2087 if (!allPointsGrabbed(event)) {
2088 QVector<QQuickItem *> targetItems;
2089 for (auto &point : event->points()) {
2090 // Presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints.
2091 // Don't find handlers for points that are already grabbed by an Item (such as Flickable).
2092 if (point.state() == QEventPoint::Pressed || qmlobject_cast<QQuickItem *>(event->exclusiveGrabber(point)))
2093 continue;
2094 QVector<QQuickItem *> targetItemsForPoint = pointerTargets(rootItem, event, point, false, false);
2095 if (targetItems.size()) {
2096 targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
2097 } else {
2098 targetItems = targetItemsForPoint;
2099 }
2100 }
2101 for (QQuickItem *item : targetItems) {
2102 if (grabbers.contains(item))
2103 continue;
2106 itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers
2108 break;
2109 }
2110 }
2111}
2112
2153{
2154 QVector<QQuickItem *> targetItems;
2155 const bool isTouch = isTouchEvent(event);
2156 if (isTouch && event->isBeginEvent() && isDeliveringTouchAsMouse()) {
2157 if (auto point = const_cast<QPointingDevicePrivate *>(QPointingDevicePrivate::get(touchMouseDevice))->queryPointById(touchMouseId)) {
2158 // When a second point is pressed, if the first point's existing
2159 // grabber was a pointer handler while a filtering parent is filtering
2160 // the same first point _as mouse_: we're starting over with delivery,
2161 // so we need to allow the second point to now be sent as a synth-mouse
2162 // instead of the first one, so that filtering parents (maybe even the
2163 // same one) can get a chance to see the second touchpoint as a
2164 // synth-mouse and perhaps grab it. Ideally we would always do this
2165 // when a new touchpoint is pressed, but this compromise fixes
2166 // QTBUG-70998 and avoids breaking tst_FlickableInterop::touchDragSliderAndFlickable
2167 if (qobject_cast<QQuickPointerHandler *>(event->exclusiveGrabber(point->eventPoint)))
2169 } else {
2170 qCWarning(lcTouchTarget) << "during delivery of touch press, synth-mouse ID" << Qt::hex << touchMouseId << "is missing from" << event;
2171 }
2172 }
2173 for (int i = 0; i < event->pointCount(); ++i) {
2174 auto &point = event->point(i);
2175 QVector<QQuickItem *> targetItemsForPoint = pointerTargets(rootItem, event, point, !isTouch, isTouch);
2176 if (targetItems.size()) {
2177 targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
2178 } else {
2179 targetItems = targetItemsForPoint;
2180 }
2181 }
2182
2183 for (QQuickItem *item : targetItems) {
2184 // failsafe: when items get into a subscene somehow, ensure that QQuickItemPrivate::deliveryAgent() can find it
2185 if (isSubsceneAgent)
2187
2188 hasFiltered.clear();
2189 if (!handlersOnly && sendFilteredPointerEvent(event, item)) {
2190 if (event->isAccepted())
2191 return true;
2192 skipDelivery.append(item);
2193 }
2194
2195 // Do not deliverMatchingPointsTo any item for which the filtering parent already intercepted the event,
2196 // nor to any item which already had a chance to filter.
2197 if (skipDelivery.contains(item))
2198 continue;
2199
2200 // sendFilteredPointerEvent() changed the QEventPoint::accepted() state,
2201 // but per-point acceptance is opt-in during normal delivery to items.
2202 for (int i = 0; i < event->pointCount(); ++i)
2203 event->point(i).setAccepted(false);
2204
2205 deliverMatchingPointsToItem(item, false, event, handlersOnly);
2206 if (event->allPointsAccepted())
2207 handlersOnly = true;
2208 }
2209
2210 return event->allPointsAccepted();
2211}
2212
2221void QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem(QQuickItem *item, bool isGrabber, QPointerEvent *pointerEvent, bool handlersOnly)
2222{
2224#if defined(Q_OS_ANDROID) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2225 // QTBUG-85379
2226 // In QT_VERSION below 6.0.0 touchEnabled for QtQuickItems is set by default to true
2227 // It causes delivering touch events to Items which are not interested
2228 // In some cases (like using Material Style in Android) it may cause a crash
2229 if (itemPrivate->wasDeleted)
2230 return;
2231#endif
2232 localizePointerEvent(pointerEvent, item);
2233 bool isMouse = isMouseEvent(pointerEvent);
2234
2235 // Let the Item's handlers (if any) have the event first.
2236 // However, double click should never be delivered to handlers.
2237 if (pointerEvent->type() != QEvent::MouseButtonDblClick)
2238 itemPrivate->handlePointerEvent(pointerEvent);
2239
2240 if (handlersOnly)
2241 return;
2242
2243 // If all points are released and the item is not the grabber, it doesn't get the event.
2244 // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario.
2245 if (pointerEvent->isEndEvent() && !pointerEvent->isUpdateEvent()
2246 && !exclusiveGrabbers(pointerEvent).contains(item))
2247 return;
2248
2249 // If any parent filters the event, we're done.
2250 if (sendFilteredPointerEvent(pointerEvent, item))
2251 return;
2252
2253 // TODO: unite this mouse point delivery with the synthetic mouse event below
2254 // TODO: remove isGrabber then?
2255 if (isMouse) {
2256 auto button = static_cast<QSinglePointEvent *>(pointerEvent)->button();
2257 if ((isGrabber && button == Qt::NoButton) || item->acceptedMouseButtons().testFlag(button)) {
2258 // The only reason to already have a mouse grabber here is
2259 // synthetic events - flickable sends one when setPressDelay is used.
2260 auto oldMouseGrabber = pointerEvent->exclusiveGrabber(pointerEvent->point(0));
2261 pointerEvent->accept();
2262 if (isGrabber && sendFilteredPointerEvent(pointerEvent, item))
2263 return;
2264 localizePointerEvent(pointerEvent, item);
2265 QCoreApplication::sendEvent(item, pointerEvent);
2266 if (pointerEvent->isAccepted()) {
2267 auto &point = pointerEvent->point(0);
2268 auto mouseGrabber = pointerEvent->exclusiveGrabber(point);
2269 if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) {
2270 // Normally we don't need item->mouseUngrabEvent() here, because QQuickDeliveryAgentPrivate::onGrabChanged does it.
2271 // However, if one item accepted the mouse event, it expects to have the grab and be in "pressed" state,
2272 // because accepting implies grabbing. But before it actually gets the grab, another item could steal it.
2273 // In that case, onGrabChanged() does NOT notify the item that accepted the event that it's not getting the grab after all.
2274 // So after ensuring that it's not redundant, we send a notification here, for that case (QTBUG-55325).
2275 if (item != lastUngrabbed) {
2276 item->mouseUngrabEvent();
2278 }
2279 } else if (item->isEnabled() && item->isVisible() && point.state() == QEventPoint::State::Pressed) {
2280 pointerEvent->setExclusiveGrabber(point, item);
2281 }
2282 point.setAccepted(true);
2283 }
2284 return;
2285 }
2286 }
2287
2288 if (!isTouchEvent(pointerEvent))
2289 return;
2290
2291 bool eventAccepted = false;
2292 QMutableTouchEvent touchEvent;
2293 itemPrivate->localizedTouchEvent(static_cast<QTouchEvent *>(pointerEvent), false, &touchEvent);
2294 if (touchEvent.type() == QEvent::None)
2295 return; // no points inside this item
2296
2297 if (item->acceptTouchEvents()) {
2298 qCDebug(lcTouch) << "considering delivering" << &touchEvent << " to " << item;
2299
2300 // Deliver the touch event to the given item
2301 qCDebug(lcTouch) << "actually delivering" << &touchEvent << " to " << item;
2302 QCoreApplication::sendEvent(item, &touchEvent);
2303 eventAccepted = touchEvent.isAccepted();
2304 } else {
2305 // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
2307 !eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton))
2308 deliverTouchAsMouse(item, &touchEvent);
2309 return;
2310 }
2311
2312 Q_ASSERT(item->acceptTouchEvents()); // else we would've returned early above
2313 if (eventAccepted) {
2314 bool isPressOrRelease = pointerEvent->isBeginEvent() || pointerEvent->isEndEvent();
2315 for (int i = 0; i < touchEvent.pointCount(); ++i) {
2316 auto &point = touchEvent.point(i);
2317 // legacy-style delivery: if the item doesn't reject the event, that means it handled ALL the points
2318 point.setAccepted();
2319 // but don't let the root of a subscene implicitly steal the grab from some other item (such as one of its children)
2320 if (isPressOrRelease && !(itemPrivate->deliveryAgent() && pointerEvent->exclusiveGrabber(point)))
2321 pointerEvent->setExclusiveGrabber(point, item);
2322 }
2323 } else {
2324 // But if the event was not accepted then we know this item
2325 // will not be interested in further updates for those touchpoint IDs either.
2326 for (const auto &point: touchEvent.points()) {
2327 if (point.state() == QEventPoint::State::Pressed) {
2328 if (pointerEvent->exclusiveGrabber(point) == item) {
2329 qCDebug(lcTouchTarget) << "TP" << Qt::hex << point.id() << "disassociated";
2330 pointerEvent->setExclusiveGrabber(point, nullptr);
2331 }
2332 }
2333 }
2334 }
2335}
2336
2337#if QT_CONFIG(quick_draganddrop)
2338void QQuickDeliveryAgentPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
2339{
2340 QObject *formerTarget = grabber->target();
2341 grabber->resetTarget();
2342 QQuickDragGrabber::iterator grabItem = grabber->begin();
2343 if (grabItem != grabber->end()) {
2344 Q_ASSERT(event->type() != QEvent::DragEnter);
2345 if (event->type() == QEvent::Drop) {
2346 QDropEvent *e = static_cast<QDropEvent *>(event);
2347 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
2348 QPointF p = (**grabItem)->mapFromScene(e->position().toPoint());
2349 QDropEvent translatedEvent(
2350 p.toPoint(),
2351 e->possibleActions(),
2352 e->mimeData(),
2353 e->buttons(),
2354 e->modifiers());
2355 QQuickDropEventEx::copyActions(&translatedEvent, *e);
2356 QCoreApplication::sendEvent(**grabItem, &translatedEvent);
2357 e->setAccepted(translatedEvent.isAccepted());
2358 e->setDropAction(translatedEvent.dropAction());
2359 grabber->setTarget(**grabItem);
2360 }
2361 }
2362 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
2363 QDragLeaveEvent leaveEvent;
2364 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
2365 QCoreApplication::sendEvent(**grabItem, &leaveEvent);
2366 grabber->ignoreList().clear();
2367 return;
2368 } else {
2369 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
2370
2371 // Used to ensure we don't send DragEnterEvents to current drop targets,
2372 // and to detect which current drop targets we have left
2373 QVarLengthArray<QQuickItem*, 64> currentGrabItems;
2374 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
2375 currentGrabItems.append(**grabItem);
2376
2377 // Look for any other potential drop targets that are higher than the current ones
2378 QDragEnterEvent enterEvent(
2379 moveEvent->position().toPoint(),
2380 moveEvent->possibleActions(),
2381 moveEvent->mimeData(),
2382 moveEvent->buttons(),
2383 moveEvent->modifiers());
2384 QQuickDropEventEx::copyActions(&enterEvent, *moveEvent);
2385 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent, &currentGrabItems,
2386 formerTarget));
2387
2388 for (grabItem = grabber->begin(); grabItem != grabber->end(); ++grabItem) {
2389 int i = currentGrabItems.indexOf(**grabItem);
2390 if (i >= 0) {
2391 currentGrabItems.remove(i);
2392 // Still grabbed: send move event
2393 QDragMoveEvent translatedEvent(
2394 (**grabItem)->mapFromScene(moveEvent->position().toPoint()).toPoint(),
2395 moveEvent->possibleActions(),
2396 moveEvent->mimeData(),
2397 moveEvent->buttons(),
2398 moveEvent->modifiers());
2399 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
2400 QCoreApplication::sendEvent(**grabItem, &translatedEvent);
2401 event->setAccepted(translatedEvent.isAccepted());
2402 QQuickDropEventEx::copyActions(moveEvent, translatedEvent);
2403 }
2404 }
2405
2406 // Anything left in currentGrabItems is no longer a drop target and should be sent a DragLeaveEvent
2407 QDragLeaveEvent leaveEvent;
2408 for (QQuickItem *i : currentGrabItems)
2409 QCoreApplication::sendEvent(i, &leaveEvent);
2410
2411 return;
2412 }
2413 }
2414 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
2415 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
2416 QDragEnterEvent enterEvent(
2417 e->position().toPoint(),
2418 e->possibleActions(),
2419 e->mimeData(),
2420 e->buttons(),
2421 e->modifiers());
2422 QQuickDropEventEx::copyActions(&enterEvent, *e);
2423 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
2424 } else {
2425 grabber->ignoreList().clear();
2426 }
2427}
2428
2429bool QQuickDeliveryAgentPrivate::deliverDragEvent(
2430 QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event,
2431 QVarLengthArray<QQuickItem *, 64> *currentGrabItems, QObject *formerTarget)
2432{
2435 return false;
2436 QPointF p = item->mapFromScene(event->position().toPoint());
2437 bool itemContained = item->contains(p);
2438
2439 const int itemIndex = grabber->ignoreList().indexOf(item);
2440 if (!itemContained) {
2441 if (itemIndex >= 0)
2442 grabber->ignoreList().remove(itemIndex);
2443
2445 return false;
2446 }
2447
2448 QDragEnterEvent enterEvent(
2449 event->position().toPoint(),
2450 event->possibleActions(),
2451 event->mimeData(),
2452 event->buttons(),
2453 event->modifiers());
2456
2457 // Check children in front of this item first
2458 for (int ii = children.size() - 1; ii >= 0; --ii) {
2459 if (children.at(ii)->z() < 0)
2460 continue;
2461 if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems, formerTarget))
2462 return true;
2463 }
2464
2465 if (itemContained) {
2466 // If this item is currently grabbed, don't send it another DragEnter,
2467 // just grab it again if it's still contained.
2468 if (currentGrabItems && currentGrabItems->contains(item)) {
2469 grabber->grab(item);
2470 grabber->setTarget(item);
2471 return true;
2472 }
2473
2474 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
2475 if (event->type() == QEvent::DragEnter) {
2476 if (formerTarget) {
2477 QQuickItem *formerTargetItem = qobject_cast<QQuickItem *>(formerTarget);
2478 if (formerTargetItem && currentGrabItems) {
2479 QDragLeaveEvent leaveEvent;
2480 QCoreApplication::sendEvent(formerTarget, &leaveEvent);
2481
2482 // Remove the item from the currentGrabItems so a leave event won't be generated
2483 // later on
2484 currentGrabItems->removeAll(formerTarget);
2485 }
2486 } else if (itemIndex >= 0) {
2487 return false;
2488 }
2489 }
2490
2491 QDragMoveEvent translatedEvent(p.toPoint(), event->possibleActions(), event->mimeData(),
2492 event->buttons(), event->modifiers(), event->type());
2493 QQuickDropEventEx::copyActions(&translatedEvent, *event);
2494 translatedEvent.setAccepted(event->isAccepted());
2495 QCoreApplication::sendEvent(item, &translatedEvent);
2496 event->setAccepted(translatedEvent.isAccepted());
2497 event->setDropAction(translatedEvent.dropAction());
2498 if (event->type() == QEvent::DragEnter) {
2499 if (translatedEvent.isAccepted()) {
2500 grabber->grab(item);
2501 grabber->setTarget(item);
2502 return true;
2503 } else if (itemIndex < 0) {
2504 grabber->ignoreList().append(item);
2505 }
2506 } else {
2507 return true;
2508 }
2509 }
2510 }
2511
2512 // Check children behind this item if this item or any higher children have not accepted
2513 for (int ii = children.size() - 1; ii >= 0; --ii) {
2514 if (children.at(ii)->z() >= 0)
2515 continue;
2516 if (deliverDragEvent(grabber, children.at(ii), &enterEvent, currentGrabItems, formerTarget))
2517 return true;
2518 }
2519
2520 return false;
2521}
2522#endif // quick_draganddrop
2523
2533{
2534 return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem());
2535}
2536
2541{
2543 return false;
2544 if (!filteringParent)
2545 return false;
2546 bool filtered = false;
2547 const bool hasHandlers = QQuickItemPrivate::get(receiver)->hasPointerHandlers();
2548 if (filteringParent->filtersChildMouseEvents() && !hasFiltered.contains(filteringParent)) {
2549 hasFiltered.append(filteringParent);
2550 if (isMouseEvent(event)) {
2551 if (receiver->acceptedMouseButtons()) {
2552 const bool wasAccepted = event->allPointsAccepted();
2553 Q_ASSERT(event->pointCount());
2554 localizePointerEvent(event, receiver);
2555 event->setAccepted(true);
2556 auto oldMouseGrabber = event->exclusiveGrabber(event->point(0));
2557 if (filteringParent->childMouseEventFilter(receiver, event)) {
2558 qCDebug(lcMouse) << "mouse event intercepted by childMouseEventFilter of " << filteringParent;
2559 skipDelivery.append(filteringParent);
2560 filtered = true;
2561 if (event->isAccepted() && event->isBeginEvent()) {
2562 auto &point = event->point(0);
2563 auto mouseGrabber = event->exclusiveGrabber(point);
2564 if (mouseGrabber && mouseGrabber != receiver && mouseGrabber != oldMouseGrabber) {
2565 receiver->mouseUngrabEvent();
2566 } else {
2567 event->setExclusiveGrabber(point, receiver);
2568 }
2569 }
2570 } else {
2571 // Restore accepted state if the event was not filtered.
2572 event->setAccepted(wasAccepted);
2573 }
2574 }
2575 } else if (isTouchEvent(event)) {
2576 const bool acceptsTouchEvents = receiver->acceptTouchEvents() || hasHandlers;
2577 auto device = event->device();
2579 device->capabilities().testFlag(QInputDevice::Capability::MouseEmulation)) {
2580 qCDebug(lcTouchTarget) << "skipping filtering of synth-mouse event from" << device;
2581 } else if (acceptsTouchEvents || receiver->acceptedMouseButtons()) {
2582 // get a touch event customized for delivery to filteringParent
2583 // TODO should not be necessary? because QQuickDeliveryAgentPrivate::deliverMatchingPointsToItem() does it
2584 QMutableTouchEvent filteringParentTouchEvent;
2585 QQuickItemPrivate::get(receiver)->localizedTouchEvent(static_cast<QTouchEvent *>(event), true, &filteringParentTouchEvent);
2586 if (filteringParentTouchEvent.type() != QEvent::None) {
2587 qCDebug(lcTouch) << "letting parent" << filteringParent << "filter for" << receiver << &filteringParentTouchEvent;
2588 if (filteringParent->childMouseEventFilter(receiver, &filteringParentTouchEvent)) {
2589 qCDebug(lcTouch) << "touch event intercepted by childMouseEventFilter of " << filteringParent;
2590 skipDelivery.append(filteringParent);
2591 for (auto point : filteringParentTouchEvent.points()) {
2592 const QQuickItem *exclusiveGrabber = qobject_cast<const QQuickItem *>(event->exclusiveGrabber(point));
2593 if (!exclusiveGrabber || !exclusiveGrabber->keepTouchGrab())
2594 event->setExclusiveGrabber(point, filteringParent);
2595 }
2596 return true;
2598 !filteringParent->acceptTouchEvents()) {
2599 qCDebug(lcTouch) << "touch event NOT intercepted by childMouseEventFilter of " << filteringParent
2600 << "; accepts touch?" << filteringParent->acceptTouchEvents()
2601 << "receiver accepts touch?" << acceptsTouchEvents
2602 << "so, letting parent filter a synth-mouse event";
2603 // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event.
2604 for (auto &tp : filteringParentTouchEvent.points()) {
2606 switch (tp.state()) {
2609 break;
2612 break;
2614 continue;
2615 default:
2617 break;
2618 }
2619
2620 bool touchMouseUnset = (touchMouseId == -1);
2621 // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId
2622 if (touchMouseUnset || touchMouseId == tp.id()) {
2623 // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.)
2624 // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that
2625 QMutableSinglePointEvent mouseEvent;
2626 touchToMouseEvent(t, tp, &filteringParentTouchEvent, &mouseEvent);
2627 // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should
2628 // report the touchpoint's grabber. Whenever we send a synthetic mouse event,
2629 // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed.
2630 touchMouseId = tp.id();
2631 touchMouseDevice = event->pointingDevice();
2632 if (filteringParent->childMouseEventFilter(receiver, &mouseEvent)) {
2633 qCDebug(lcTouch) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent;
2634 skipDelivery.append(filteringParent);
2636 qCDebug(lcTouchTarget) << "TP (mouse)" << Qt::hex << tp.id() << "->" << filteringParent;
2637 filteringParentTouchEvent.setExclusiveGrabber(tp, filteringParent);
2638 touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set
2639 if (mouseEvent.isAccepted())
2640 filteringParent->grabMouse();
2641 }
2642 filtered = true;
2643 }
2644 if (touchMouseUnset)
2645 // Now that we're done sending a synth mouse event, and it wasn't grabbed,
2646 // the touchpoint is no longer acting as a synthetic mouse. Restore previous state.
2648 mouseEvent.point(0).setAccepted(false); // because touchToMouseEvent() set it true
2649 // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter
2650 // has been called once, we're done with this loop over the touchpoints.
2651 break;
2652 }
2653 }
2654 }
2655 }
2656 }
2657 }
2658 }
2659 return sendFilteredPointerEventImpl(event, receiver, filteringParent->parentItem()) || filtered;
2660}
2661
2674{
2675 if (!filteringParent)
2676 return false;
2677
2678 QQuickItemPrivate *filteringParentPrivate = QQuickItemPrivate::get(filteringParent);
2679 if (filteringParentPrivate->replayingPressEvent)
2680 return false;
2681
2682 bool filtered = false;
2683 if (filteringParentPrivate->filtersChildMouseEvents && !hasFiltered.contains(filteringParent)) {
2684 hasFiltered.append(filteringParent);
2685 if (filteringParent->childMouseEventFilter(receiver, event)) {
2686 filtered = true;
2687 skipDelivery.append(filteringParent);
2688 }
2689 qCDebug(lcMouseTarget) << "for" << receiver << filteringParent << "childMouseEventFilter ->" << filtered;
2690 }
2691
2692 return sendFilteredMouseEvent(event, receiver, filteringParent->parentItem()) || filtered;
2693}
2694
2703{
2705 bool dragVelocityLimitAvailable = event->device()->capabilities().testFlag(QInputDevice::Capability::Velocity)
2706 && styleHints->startDragVelocity();
2707 bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
2708 if (dragVelocityLimitAvailable) {
2709 QVector2D velocityVec = event->point(0).velocity();
2710 qreal velocity = axis == Qt::XAxis ? velocityVec.x() : velocityVec.y();
2711 overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
2712 }
2713 return overThreshold;
2714}
2715
2723bool QQuickDeliveryAgentPrivate::dragOverThreshold(qreal d, Qt::Axis axis, const QEventPoint &tp, int startDragThreshold)
2724{
2725 QStyleHints *styleHints = qApp->styleHints();
2726 bool overThreshold = qAbs(d) > (startDragThreshold >= 0 ? startDragThreshold : styleHints->startDragDistance());
2727 const bool dragVelocityLimitAvailable = (styleHints->startDragVelocity() > 0);
2728 if (!overThreshold && dragVelocityLimitAvailable) {
2729 qreal velocity = axis == Qt::XAxis ? tp.velocity().x() : tp.velocity().y();
2730 overThreshold |= qAbs(velocity) > styleHints->startDragVelocity();
2731 }
2732 return overThreshold;
2733}
2734
2741{
2742 int threshold = qApp->styleHints()->startDragDistance();
2743 return qAbs(delta.x()) > threshold || qAbs(delta.y()) > threshold;
2744}
2745
2746#ifndef QT_NO_DEBUG_STREAM
2748{
2749 QDebugStateSaver saver(debug);
2750 debug.nospace();
2751 if (!da) {
2752 debug << "QQuickDeliveryAgent(0)";
2753 return debug;
2754 }
2755
2756 debug << "QQuickDeliveryAgent(";
2757 if (!da->objectName().isEmpty())
2758 debug << da->objectName() << ' ';
2759 auto root = da->rootItem();
2760 if (Q_LIKELY(root)) {
2761 debug << "root=" << root->metaObject()->className();
2762 if (!root->objectName().isEmpty())
2763 debug << ' ' << root->objectName();
2764 } else {
2765 debug << "root=0";
2766 }
2767 debug << ')';
2768 return debug;
2769}
2770#endif
2771
2773
2774#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:164
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:25
QPointF scenePosition
the scene position of this point.
Definition qeventpoint.h:40
void setAccepted(bool accepted=true)
ulong timestamp
the most recent time at which this point was included in a QPointerEvent.
Definition qeventpoint.h:28
QPointF sceneLastPosition
the scene position of this point from the previous press or move event.
Definition qeventpoint.h:43
const QPointingDevice * device
the pointing device from which this event point originates.
Definition qeventpoint.h:24
QVector2D velocity
a velocity vector, in units of pixels per second, in the coordinate.
Definition qeventpoint.h:35
\inmodule QtCore
Definition qcoreevent.h:45
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:299
bool isAccepted() const
Definition qcoreevent.h:303
void accept()
Sets the accept flag of the event object, the equivalent of calling setAccepted(true).
Definition qcoreevent.h:305
bool isEmpty() const noexcept
Definition qflatmap_p.h:579
iterator begin()
Definition qflatmap_p.h:769
iterator end()
Definition qflatmap_p.h:773
bool contains(const Key &key) const
Definition qflatmap_p.h:625
iterator erase(iterator it)
Definition qflatmap_p.h:608
void clear()
Definition qflatmap_p.h:591
The QFocusEvent class contains event parameters for widget focus events.
Definition qevent.h:469
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.
QVariant data(int key) const
Returns this item's custom data for the key key as a QVariant.
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:245
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
void setInputItemTransform(const QTransform &transform)
Sets the transformation from input item coordinates to window coordinates to be transform.
void commit()
Commits the word user is currently composing to the editor.
void setInputItemRectangle(const QRectF &rect)
The QKeyEvent class describes a key event.
Definition qevent.h:423
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
T & first()
Definition qlist.h:628
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:471
iterator end()
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
qsizetype removeAll(const AT &t)
Definition qlist.h:575
qsizetype count() const noexcept
Definition qlist.h:387
iterator begin()
Definition qlist.h:608
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtGui
Definition qevent.h:195
static Q_GUI_EXPORT void detach(QEventPoint &p)
static Q_GUI_EXPORT void setTimestamp(QEventPoint &p, ulong t)
uint wasDeleted
Definition qobject.h:66
QObjectList children
Definition qobject.h:62
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:298
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
QString objectName
the name of this object
Definition qobject.h:94
void destroyed(QObject *=nullptr)
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointe...
virtual QVariant themeHint(ThemeHint hint) const
\inmodule QtCore\reentrant
Definition qpoint.h:214
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:328
\inmodule QtCore\reentrant
Definition qpoint.h:23
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:127
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:132
A base class for pointer events.
Definition qevent.h:73
virtual bool isEndEvent() const
Definition qevent.h:91
qsizetype pointCount() const
Returns the number of points in this pointer event.
Definition qevent.h:84
const QPointingDevice * pointingDevice() const
Returns the source device from which this event originates.
Definition qevent.cpp:327
void setTimestamp(quint64 timestamp) override
Definition qevent.cpp:335
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:366
virtual bool isBeginEvent() const
Definition qevent.h:89
QEventPoint & point(qsizetype i)
Returns a QEventPoint reference for the point at index i.
Definition qevent.cpp:237
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:348
QList< QPointer< QObject > > passiveGrabbers(const QEventPoint &point) const
Returns the list of objects that have been requested to receive all future update events and the rele...
Definition qevent.cpp:381
virtual void setAccepted(bool accepted) override
\reimp
Definition qevent.cpp:315
const QList< QEventPoint > & points() const
Returns a list of points in this pointer event.
Definition qevent.h:86
virtual bool isUpdateEvent() const
Definition qevent.h:90
\inmodule QtCore
Definition qpointer.h:18
static bool setPassiveGrabberContext(EventPointData *epd, QObject *grabber, QObject *context)
EventPointData * queryPointById(int id) const
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 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
bool deliverHoverEventToItem(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, ulong timestamp, bool clearHover)
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 *)
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.
iterator release(iterator at)
ItemList::iterator iterator
void grab(QQuickItem *item)
QObject * target() const
void setTarget(QObject *target)
void copyActions(const QDropEvent &from)
virtual void itemFocusChanged(QQuickItem *, Qt::FocusReason)
quint32 replayingPressEvent
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)
quint32 maybeHasSubsceneDeliveryAgent
bool hasPointerHandlers() const
quint32 subtreeHoverEnabled
QPointer< QQuickItem > subFocusItem
QTransform itemToWindowTransform() const
Returns a transform that maps points from item space into window space.
void itemChange(QQuickItem::ItemChange, const QQuickItem::ItemChangeData &)
quint32 notifiedActiveFocus
quint32 filtersChildMouseEvents
void localizedTouchEvent(const QTouchEvent *event, bool isFiltering, QMutableTouchEvent *localized)
Qt::MouseButtons acceptedMouseButtons() const
void updateSubFocusItem(QQuickItem *scope, bool focus)
Clears all sub focus items from scope.
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
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...
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 filtersChildMouseEvents() const
Returns whether pointer events intended for this item's children should be filtered through this item...
virtual void touchUngrabEvent()
This event handler can be reimplemented in a subclass to be notified when a touch ungrab event has oc...
bool isEnabled() const
void grabMouse()
virtual bool childMouseEventFilter(QQuickItem *, QEvent *)
Reimplement this method to filter the pointer events that are received by this item's children.
@ ItemActiveFocusHasChanged
Definition qquickitem.h:150
@ ItemClipsChildrenToShape
Definition qquickitem.h:129
static QVector< QObject * > & deviceDeliveryTargets(const QInputDevice *device)
void handlePointerEvent(QPointerEvent *event)
static bool isRenderWindowFor(QQuickWindow *quickWin, const QWindow *renderWin)
static QQuickWindowPrivate * get(QQuickWindow *c)
QQuickItem * dirtyItemList
QQuickDeliveryAgentPrivate * deliveryAgentPrivate() const
QQuickDeliveryAgent * deliveryAgent
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:483
A base class for pointer events containing a single point, such as mouse events.
Definition qevent.h:108
QPointF scenePosition() const
Returns the position of the point in this event, relative to the window or scene.
Definition qevent.h:120
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:127
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
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:39
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:41
int touchDoubleTapDistance
the maximum distance, in pixels, that a finger can be moved between two consecutive taps and still ha...
Definition qstylehints.h:54
QEventPoint & point(int touchId)
QMap< int, QEventPoint > points
The QTouchEvent class contains parameters that describe a touch event.
Definition qevent.h:916
constexpr size_type size() const noexcept
bool isEmpty() const
void remove(qsizetype i, qsizetype n=1)
const T & at(qsizetype idx) const
void resize(qsizetype sz)
iterator end() noexcept
qsizetype indexOf(const AT &t, qsizetype from=0) const
qsizetype removeAll(const AT &t)
void append(const T &t)
bool contains(const AT &t) const
T * data() noexcept
iterator begin() noexcept
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
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:2503
\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]
double e
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ ImInputItemClipRectangle
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
@ LeftButton
Definition qnamespace.h:57
@ RightButton
Definition qnamespace.h:58
@ NoButton
Definition qnamespace.h:56
@ MouseEventSynthesizedByQt
@ MouseEventSynthesizedBySystem
@ AA_SynthesizeMouseForUnhandledTouchEvents
Definition qnamespace.h:436
@ ScrollUpdate
FocusReason
@ OtherFocusReason
@ XAxis
Definition qctf_p.h:77
#define Q_UNLIKELY(x)
#define Q_LIKELY(x)
std::pair< T1, T2 > QPair
#define qApp
#define qGuiApp
#define qWarning
Definition qlogging.h:162
#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
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
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)
QDebug operator<<(QDebug debug, const QQuickDeliveryAgent *da)
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:483
const QQuickItem * qobject_cast< const QQuickItem * >(const QObject *o)
Definition qquickitem.h:489
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:30
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned short ushort
Definition qtypes.h:28
double qreal
Definition qtypes.h:92
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:44
qsizetype lastIndexOf(const AT &t, qsizetype from=-1) const noexcept
Definition qlist.h:962
virtual QPointF map(const QPointF &point)=0