diff options
Diffstat (limited to 'src/quick/items/qquickwindow.cpp')
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 749 |
1 files changed, 512 insertions, 237 deletions
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index b58caa061a..65d69a3bcf 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2017 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -46,6 +46,7 @@ #include "qquickevents_p_p.h" #include <private/qquickdrag_p.h> +#include <private/qquickpointerhandler_p.h> #include <QtQuick/private/qsgrenderer_p.h> #include <QtQuick/private/qsgtexture_p.h> @@ -92,6 +93,12 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_ bool QQuickWindowPrivate::defaultAlphaBuffer = false; +#if defined(QT_QUICK_DEFAULT_TEXT_RENDER_TYPE) +QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QT_QUICK_DEFAULT_TEXT_RENDER_TYPE; +#else +QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QtTextRendering; +#endif + void QQuickWindowPrivate::updateFocusItemTransform() { #if QT_CONFIG(im) @@ -499,6 +506,8 @@ QQuickWindowPrivate::QQuickWindowPrivate() , persistentSceneGraph(true) , lastWheelEventAccepted(false) , componentCompleted(true) + , allowChildEventFiltering(true) + , allowDoubleClick(true) , lastFocusReason(Qt::OtherFocusReason) , renderTarget(0) , renderTargetId(0) @@ -652,7 +661,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (!item->contains(pos)) break; - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << item; + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << p.id() << "->" << item; QScopedPointer<QMouseEvent> mousePress(touchToMouseEvent(QEvent::MouseButtonPress, p, event.data(), item, false)); // Send a single press and see if that's accepted @@ -664,7 +673,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve if (!q->mouseGrabberItem()) item->grabMouse(); auto pointerEventPoint = pointerEvent->pointById(p.id()); - pointerEventPoint->setGrabber(item); + pointerEventPoint->setGrabberItem(item); if (checkIfDoubleClicked(event->timestamp())) { QScopedPointer<QMouseEvent> mouseDoubleClick(touchToMouseEvent(QEvent::MouseButtonDblClick, p, event.data(), item, false)); @@ -688,7 +697,7 @@ bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEve QCoreApplication::sendEvent(item, me.data()); event->setAccepted(me->isAccepted()); if (me->isAccepted()) { - qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << p.id() << "->" << mouseGrabberItem; + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << p.id() << "->" << mouseGrabberItem; } return event->isAccepted(); } else { @@ -744,39 +753,42 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) if (q->mouseGrabberItem() == grabber) return; - qCDebug(DBG_MOUSE_TARGET) << "grabber" << q->mouseGrabberItem() << "->" << grabber; QQuickItem *oldGrabber = q->mouseGrabberItem(); - bool fromTouch = false; + qCDebug(DBG_MOUSE_TARGET) << "grabber" << oldGrabber << "->" << grabber; if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << touchMouseId << "->" << q->mouseGrabberItem(); auto point = pointerEventInstance(touchMouseDevice)->pointById(touchMouseId); - if (point) - point->setGrabber(grabber); - fromTouch = true; + if (point) { + auto originalEvent = pointerEventInstance(point->pointerEvent()->device()); + for (int i = 0; i < originalEvent->pointCount(); ++i) + originalEvent->point(i)->cancelExclusiveGrab(); + point->setGrabberItem(grabber); + for (auto handler : point->passiveGrabbers()) + point->cancelPassiveGrab(handler); + } } else { QQuickPointerEvent *event = pointerEventInstance(QQuickPointerDevice::genericMouseDevice()); Q_ASSERT(event->pointCount() == 1); - event->point(0)->setGrabber(grabber); + auto point = event->point(0); + point->setGrabberItem(grabber); + for (auto handler : point->passiveGrabbers()) + point->cancelPassiveGrab(handler); } + if (oldGrabber) { QEvent e(QEvent::UngrabMouse); - QSet<QQuickItem *> hasFiltered; - if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) { + hasFiltered.clear(); + if (!sendFilteredMouseEvent(&e, oldGrabber, oldGrabber->parentItem())) oldGrabber->mouseUngrabEvent(); - if (fromTouch) - oldGrabber->touchUngrabEvent(); - } } } -void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int> &ids) +void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &ids) { - QSet<QQuickItem*> ungrab; for (int i = 0; i < ids.count(); ++i) { - // FIXME: deprecate this function, we need a device int id = ids.at(i); if (Q_UNLIKELY(id < 0)) { qWarning("ignoring grab of touchpoint %d", id); @@ -784,11 +796,11 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int } if (id == touchMouseId) { auto point = pointerEventInstance(touchMouseDevice)->pointById(id); - auto touchMouseGrabber = point->grabber(); + auto touchMouseGrabber = point->grabberItem(); if (touchMouseGrabber) { - point->setGrabber(nullptr); + point->setExclusiveGrabber(nullptr); touchMouseGrabber->mouseUngrabEvent(); - ungrab.insert(touchMouseGrabber); + touchMouseGrabber->touchUngrabEvent(); touchMouseDevice = nullptr; touchMouseId = -1; } @@ -800,19 +812,23 @@ void QQuickWindowPrivate::grabTouchPoints(QQuickItem *grabber, const QVector<int auto point = pointerEventInstance(device)->pointById(id); if (!point) continue; - QQuickItem *oldGrabber = point->grabber(); + QObject *oldGrabber = point->exclusiveGrabber(); if (oldGrabber == grabber) continue; - - point->setGrabber(grabber); - if (oldGrabber) - ungrab.insert(oldGrabber); + point->setExclusiveGrabber(grabber); } } - for (QQuickItem *oldGrabber : qAsConst(ungrab)) - oldGrabber->touchUngrabEvent(); } +/*! + Ungrabs all touchpoint grabs and/or the mouse grab from the given item \a grabber. + This should not be called when processing a release event - that's redundant. + It is called in other cases, when the points may not be released, but the item + nevertheless must lose its grab due to becoming disabled, invisible, etc. + QQuickEventPoint::setGrabberItem() calls touchUngrabEvent() when all points are released, + but if not all points are released, it cannot be sure whether to call touchUngrabEvent() + or not; so we have to do it here. +*/ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool touch) { Q_Q(QQuickWindow); @@ -821,17 +837,19 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to setMouseGrabber(nullptr); } if (Q_LIKELY(touch)) { + bool ungrab = false; const auto touchDevices = QQuickPointerDevice::touchDevices(); for (auto device : touchDevices) { auto pointerEvent = pointerEventInstance(device); for (int i = 0; i < pointerEvent->pointCount(); ++i) { - if (pointerEvent->point(i)->grabber() == grabber) { - pointerEvent->point(i)->setGrabber(nullptr); - // FIXME send ungrab event only once - grabber->touchUngrabEvent(); + if (pointerEvent->point(i)->exclusiveGrabber() == grabber) { + pointerEvent->point(i)->setGrabberItem(nullptr); + ungrab = true; } } } + if (ungrab) + grabber->touchUngrabEvent(); } } @@ -1494,12 +1512,12 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const if (d->touchMouseId != -1 && d->touchMouseDevice) { QQuickPointerEvent *event = d->pointerEventInstance(d->touchMouseDevice); auto point = event->pointById(d->touchMouseId); - return point ? point->grabber() : nullptr; + return point ? point->grabberItem() : nullptr; } QQuickPointerEvent *event = d->pointerEventInstance(QQuickPointerDevice::genericMouseDevice()); Q_ASSERT(event->pointCount()); - return event->point(0)->grabber(); + return event->point(0)->grabberItem(); } @@ -1626,10 +1644,15 @@ void QQuickWindow::keyReleaseEvent(QKeyEvent *e) void QQuickWindowPrivate::deliverKeyEvent(QKeyEvent *e) { - Q_Q(QQuickWindow); - - if (activeFocusItem) - q->sendEvent(activeFocusItem, e); + if (activeFocusItem) { + QQuickItem *item = activeFocusItem; + e->accept(); + QCoreApplication::sendEvent(item, e); + while (!e->isAccepted() && (item = item->parentItem())) { + e->accept(); + QCoreApplication::sendEvent(item, e); + } + } } QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos) @@ -1648,36 +1671,74 @@ QMouseEvent *QQuickWindowPrivate::cloneMouseEvent(QMouseEvent *event, QPointF *t void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEvent) { - Q_Q(QQuickWindow); auto point = pointerEvent->point(0); - lastMousePosition = point->scenePos(); - QQuickItem *grabber = point->grabber(); - if (grabber) { - // if the update consists of changing button state, then don't accept it - // unless the button is one in which the item is interested - if (pointerEvent->button() != Qt::NoButton - && grabber->acceptedMouseButtons() - && !(grabber->acceptedMouseButtons() & pointerEvent->button())) { - pointerEvent->setAccepted(false); - return; - } + lastMousePosition = point->scenePosition(); + const bool mouseIsReleased = (point->state() == QQuickEventPoint::Released && pointerEvent->buttons() == Qt::NoButton); + + if (point->exclusiveGrabber()) { + if (auto grabber = point->grabberItem()) { + if (sendFilteredPointerEvent(pointerEvent, grabber)) + return; + // if the grabber is an Item: + // if the update consists of changing button state, don't accept it unless + // the button is one in which the grabber is interested + Qt::MouseButtons acceptedButtons = grabber->acceptedMouseButtons(); + if (pointerEvent->button() != Qt::NoButton && acceptedButtons + && !(acceptedButtons & pointerEvent->button())) { + pointerEvent->setAccepted(false); + return; + } - // send update - QPointF localPos = grabber->mapFromScene(lastMousePosition); - auto me = pointerEvent->asMouseEvent(localPos); - me->accept(); - q->sendEvent(grabber, me); - point->setAccepted(me->isAccepted()); + // send update + QPointF localPos = grabber->mapFromScene(lastMousePosition); + auto me = pointerEvent->asMouseEvent(localPos); + me->accept(); + QCoreApplication::sendEvent(grabber, me); + point->setAccepted(me->isAccepted()); - // release event, make sure to ungrab if there still is a grabber - if (me->type() == QEvent::MouseButtonRelease && !me->buttons() && q->mouseGrabberItem()) - q->mouseGrabberItem()->ungrabMouse(); + // release event: ungrab if no buttons are pressed anymore + if (mouseIsReleased) + setMouseGrabber(nullptr); + } else { + // if the grabber is not an Item, it must be a PointerHandler + auto handler = point->grabberPointerHandler(); + pointerEvent->localize(handler->parentItem()); + if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) + handler->handlePointerEvent(pointerEvent); + if (mouseIsReleased) + point->setGrabberPointerHandler(nullptr, true); + } } else { - // send initial press bool delivered = false; if (pointerEvent->isPressEvent()) { - QSet<QQuickItem*> hasFiltered; - delivered = deliverPressEvent(pointerEvent, &hasFiltered); + // send initial press + delivered = deliverPressOrReleaseEvent(pointerEvent); + } else if (pointerEvent->device()->type() == QQuickPointerDevice::Mouse) { + // if this is an update or release from an actual mouse, + // and the point wasn't grabbed, deliver only to PointerHandlers: + // passive grabbers first, then the rest + const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets(); + for (auto handler : point->passiveGrabbers()) { + // a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically + if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { + if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem())) + handler->handlePointerEvent(pointerEvent); + } + } + // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order + if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) { + QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePosition(), false, false); + for (QQuickItem *item : targetItems) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + pointerEvent->localize(item); + if (!sendFilteredPointerEvent(pointerEvent, item)) { + if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers + delivered = true; + } + if (point->exclusiveGrabber()) + break; + } + } } if (!delivered) @@ -1698,10 +1759,9 @@ bool QQuickWindowPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, hoverEvent.setTimestamp(timestamp); hoverEvent.setAccepted(accepted); - QSet<QQuickItem *> hasFiltered; - if (sendFilteredMouseEvent(item->parentItem(), item, &hoverEvent, &hasFiltered)) { + hasFiltered.clear(); + if (sendFilteredMouseEvent(&hoverEvent, item, item->parentItem())) return true; - } QCoreApplication::sendEvent(item, &hoverEvent); @@ -1857,10 +1917,20 @@ bool QQuickWindowPrivate::deliverNativeGestureEvent(QQuickItem *item, QNativeGes return true; } - QPointF p = item->mapFromScene(event->localPos()); + // Try the Item's pointer handlers first + QQuickPointerEvent *pointerEvent = pointerEventInstance(event); + pointerEvent->localize(item); + if (itemPrivate->handlePointerEvent(pointerEvent, false)) { + if (pointerEvent->allPointsAccepted()) { + event->accept(); + return true; + } + } + // If still not accepted, try direct delivery to the item + QPointF p = item->mapFromScene(event->localPos()); if (item->contains(p)) { - QNativeGestureEvent copy(event->gestureType(), p, event->windowPos(), event->screenPos(), + QNativeGestureEvent copy(event->gestureType(), event->device(), p, event->windowPos(), event->screenPos(), event->value(), 0L, 0L); // TODO can't copy things I can't access event->accept(); item->event(©); @@ -1880,19 +1950,16 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) Q_Q(QQuickWindow); // A TouchCancel event will typically not contain any points. - // Deliver it to all items that have active touches. + // Deliver it to all items and handlers that have active touches. QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::touchDevice(event->device())); - QVector<QQuickItem *> grabbers = pointerEvent->grabbers(); - - for (QQuickItem *grabber: qAsConst(grabbers)) { - q->sendEvent(grabber, event); - } + for (int i = 0; i < pointerEvent->pointCount(); ++i) + pointerEvent->point(i)->cancelExclusiveGrabImpl(event); touchMouseId = -1; touchMouseDevice = nullptr; if (q->mouseGrabberItem()) q->mouseGrabberItem()->ungrabMouse(); - // The next touch event can only be a TouchBegin so clean up. + // The next touch event can only be a TouchBegin, so clean up. pointerEvent->clearGrabbers(); return true; } @@ -2029,8 +2096,6 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) { - Q_Q(QQuickWindow); - if (event->source() == Qt::MouseEventSynthesizedBySystem) { event->accept(); return; @@ -2051,7 +2116,8 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) case QEvent::MouseButtonDblClick: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseDoubleClick, event->button(), event->buttons()); - deliverPointerEvent(pointerEventInstance(event)); + if (allowDoubleClick) + deliverPointerEvent(pointerEventInstance(event)); break; case QEvent::MouseMove: Q_QUICK_INPUT_PROFILE(QQuickProfiler::Mouse, QQuickProfiler::InputMouseMove, @@ -2063,7 +2129,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) updateCursor(event->windowPos()); #endif - if (!q->mouseGrabberItem()) { + if (!pointerEventInstance(QQuickPointerDevice::genericMouseDevice())->point(0)->exclusiveGrabber()) { QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition; lastMousePosition = event->windowPos(); @@ -2074,7 +2140,6 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event) accepted = clearHover(event->timestamp()); } event->setAccepted(accepted); - return; } deliverPointerEvent(pointerEventInstance(event)); break; @@ -2111,23 +2176,36 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents() } } -QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevice *device) const +QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevice *device, QEvent::Type eventType) const { - // the list of devices should be very small so a linear search should be ok - for (QQuickPointerEvent *e: pointerEventInstances) { + // Search for a matching reusable event object. + for (QQuickPointerEvent *e : pointerEventInstances) { + // If device can generate native gestures (e.g. a trackpad), there might be two QQuickPointerEvents: + // QQuickPointerNativeGestureEvent and QQuickPointerTouchEvent. Use eventType to disambiguate. + if (eventType == QEvent::NativeGesture && !qobject_cast<QQuickPointerNativeGestureEvent*>(e)) + continue; + // Otherwise we assume there's only one event type per device. + // More disambiguation tests might need to be added above if that changes later. if (e->device() == device) return e; } + // Not found: we have to create a suitable event instance. QQuickPointerEvent *ev = nullptr; QQuickWindow *q = const_cast<QQuickWindow*>(q_func()); switch (device->type()) { case QQuickPointerDevice::Mouse: + // QWindowSystemInterface::handleMouseEvent() does not take a device parameter: + // we assume all mouse events come from one mouse (the "core pointer"). + // So when the event is a mouse event, device == QQuickPointerDevice::genericMouseDevice() ev = new QQuickPointerMouseEvent(q, device); break; case QQuickPointerDevice::TouchPad: case QQuickPointerDevice::TouchScreen: - ev = new QQuickPointerTouchEvent(q, device); + if (eventType == QEvent::NativeGesture) + ev = new QQuickPointerNativeGestureEvent(q, device); + else // assume QEvent::Type is one of TouchBegin/Update/End + ev = new QQuickPointerTouchEvent(q, device); break; default: // TODO tablet event types @@ -2147,29 +2225,29 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevic QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) const { QQuickPointerDevice *dev = nullptr; - QQuickPointerEvent *ev = nullptr; switch (event->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: dev = QQuickPointerDevice::genericMouseDevice(); - ev = pointerEventInstance(dev); break; case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: case QEvent::TouchCancel: dev = QQuickPointerDevice::touchDevice(static_cast<QTouchEvent *>(event)->device()); - ev = pointerEventInstance(dev); break; // TODO tablet event types + case QEvent::NativeGesture: + dev = QQuickPointerDevice::touchDevice(static_cast<QNativeGestureEvent *>(event)->device()); + break; default: break; } - Q_ASSERT(ev); - return ev->reset(event); + Q_ASSERT(dev); + return pointerEventInstance(dev, event->type())->reset(event); } void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) @@ -2179,8 +2257,12 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) // the usecase a bit evil, but we at least don't want to lose events. ++pointerEventRecursionGuard; + skipDelivery.clear(); if (event->asPointerMouseEvent()) { deliverMouseEvent(event->asPointerMouseEvent()); + // failsafe: never allow any kind of grab to persist after release + if (event->isReleaseEvent() && event->buttons() == Qt::NoButton) + event->clearGrabbers(); } else if (event->asPointerTouchEvent()) { deliverTouchEvent(event->asPointerTouchEvent()); } else { @@ -2194,7 +2276,10 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event) // check if item or any of its child items contain the point // FIXME: should this be iterative instead of recursive? -QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons) const +// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added. +// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR +// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't. +QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons, bool checkAcceptsTouch) const { QVector<QQuickItem *> targets; auto itemPrivate = QQuickItemPrivate::get(item); @@ -2212,13 +2297,18 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons auto childPrivate = QQuickItemPrivate::get(child); if (!child->isVisible() || !child->isEnabled() || childPrivate->culled) continue; - targets << pointerTargets(child, scenePos, false); + targets << pointerTargets(child, scenePos, checkMouseButtons, checkAcceptsTouch); } - if (item->contains(itemPos) && (!checkMouseButtons || itemPrivate->acceptedMouseButtons())) { - // add this item last - children take precedence - targets << item; + bool relevant = item->contains(itemPos); + if (!(itemPrivate->hasPointerHandlers())) { + if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton) + relevant = false; + if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons())) + relevant = false; } + if (relevant) + targets << item; // add this item last: children take precedence return targets; } @@ -2248,11 +2338,12 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) { qCDebug(DBG_TOUCH) << " - delivering" << event->asTouchEvent(); - QSet<QQuickItem *> hasFiltered; if (event->isPressEvent()) - deliverPressEvent(event, &hasFiltered); - if (!event->allPointsAccepted()) - deliverUpdatedTouchPoints(event, &hasFiltered); + deliverPressOrReleaseEvent(event); + if (!event->allUpdatedPointsAccepted()) + deliverUpdatedTouchPoints(event); + if (event->isReleaseEvent()) + deliverPressOrReleaseEvent(event, true); // Remove released points from itemForTouchPointId bool allReleased = true; @@ -2262,7 +2353,7 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) if (point->state() == QQuickEventPoint::Released) { int id = point->pointId(); qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << id << "released"; - point->setGrabber(nullptr); + point->setGrabberItem(nullptr); if (id == touchMouseId) { touchMouseId = -1; touchMouseDevice = nullptr; @@ -2272,29 +2363,93 @@ void QQuickWindowPrivate::deliverTouchEvent(QQuickPointerTouchEvent *event) } } - if (allReleased && !event->grabbers().isEmpty()) { - qWarning() << "No release received for some grabbers" << event->grabbers(); + if (allReleased) { + if (Q_UNLIKELY(!event->exclusiveGrabbers().isEmpty())) + qWarning() << "No release received for some grabbers" << event->exclusiveGrabbers(); event->clearGrabbers(); } } // Deliver touch points to existing grabbers -bool QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered) -{ - const auto grabbers = event->grabbers(); - for (auto grabber : grabbers) - deliverMatchingPointsToItem(grabber, event, hasFiltered); +void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event) +{ + const auto grabbers = event->exclusiveGrabbers(); + for (auto grabber : grabbers) { + // The grabber is guaranteed to be either an item or a handler. + QQuickItem *receiver = qmlobject_cast<QQuickItem *>(grabber); + if (!receiver) { + // The grabber is not an item? It's a handler then. Let it have the event first. + QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber); + receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem(); + if (sendFilteredPointerEvent(event, receiver)) + return; + event->localize(receiver); + handler->handlePointerEvent(event); + if (event->allPointsAccepted()) + return; + } + // If the grabber is an item or the grabbing handler didn't handle it, + // then deliver the event to the item (which may have multiple handlers). + deliverMatchingPointsToItem(receiver, event); + } - return false; + // If some points weren't grabbed, deliver only to non-grabber PointerHandlers + if (!event->allPointsGrabbed()) { + int pointCount = event->pointCount(); + + // Deliver to each eventpoint's passive grabbers (but don't visit any handler more than once) + const QVector<QQuickPointerHandler *> &eventDeliveryTargets = event->device()->eventDeliveryTargets(); + for (int i = 0; i < pointCount; ++i) { + QQuickEventPoint *point = event->point(i); + for (auto handler : point->passiveGrabbers()) { + if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) { + if (sendFilteredPointerEvent(event, handler->parentItem())) + return; + handler->handlePointerEvent(event); + } + } + } + + // If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order + if (!event->allPointsGrabbed()) { + QVector<QQuickItem *> targetItems; + for (int i = 0; i < pointCount; ++i) { + QQuickEventPoint *point = event->point(i); + if (point->state() == QQuickEventPoint::Pressed) + continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints + QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), false, false); + if (targetItems.count()) { + targetItems = mergePointerTargets(targetItems, targetItemsForPoint); + } else { + targetItems = targetItemsForPoint; + } + } + + for (QQuickItem *item: targetItems) { + if (grabbers.contains(item)) + continue; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + event->localize(item); + itemPrivate->handlePointerEvent(event, true); // avoid re-delivering to grabbers + if (event->allPointsGrabbed()) + break; + } + } + } } -// Deliver newly pressed touch points -bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQuickItem *> *hasFiltered) +// Deliver an event containing newly pressed or released touch points +bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event, bool handlersOnly) { - const QVector<QPointF> points = event->unacceptedPressedPointScenePositions(); + int pointCount = event->pointCount(); QVector<QQuickItem *> targetItems; - for (QPointF point: points) { - QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false); + bool isTouchEvent = (event->asPointerTouchEvent() != nullptr); + for (int i = 0; i < pointCount; ++i) { + auto point = event->point(i); + point->setAccepted(false); // because otherwise touchEventForItem will ignore it + if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released) + point->setGrabberPointerHandler(nullptr, true); + QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), !isTouchEvent, isTouchEvent); if (targetItems.count()) { targetItems = mergePointerTargets(targetItems, targetItemsForPoint); } else { @@ -2302,8 +2457,21 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQui } } - for (QQuickItem *item: targetItems) { - deliverMatchingPointsToItem(item, event, hasFiltered); + for (QQuickItem *item : targetItems) { + if (!handlersOnly && sendFilteredPointerEvent(event, item)) { + if (event->isAccepted()) { + for (int i = 0; i < event->pointCount(); ++i) + event->point(i)->setAccepted(); + return true; + } + skipDelivery.append(item); + } + + // Do not deliverMatchingPointsTo any item for which the filtering parent already intercepted the event, + // nor to any item which already had a chance to filter. + if (skipDelivery.contains(item)) + continue; + deliverMatchingPointsToItem(item, event, handlersOnly); if (event->allPointsAccepted()) break; } @@ -2311,25 +2479,43 @@ bool QQuickWindowPrivate::deliverPressEvent(QQuickPointerEvent *event, QSet<QQui return event->allPointsAccepted(); } -bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, QSet<QQuickItem *> *hasFiltered) +void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly) { Q_Q(QQuickWindow); + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + pointerEvent->localize(item); + + // Let the Item's handlers (if any) have the event first. + // However, double click should never be delivered to handlers. + if (!pointerEvent->isDoubleClickEvent()) { + itemPrivate->handlePointerEvent(pointerEvent); + allowDoubleClick = !(pointerEvent->asPointerMouseEvent() && pointerEvent->isPressEvent() && pointerEvent->allPointsAccepted()); + } + if (handlersOnly) + return; + if (pointerEvent->allPointsAccepted() && !pointerEvent->isReleaseEvent()) + return; + + // If all points are released and the item is not the grabber, it doesn't get the event. + // But if at least one point is still pressed, we might be in a potential gesture-takeover scenario. + if (pointerEvent->isReleaseEvent() && !pointerEvent->isUpdateEvent() + && !pointerEvent->exclusiveGrabbers().contains(item)) + return; // TODO: unite this mouse point delivery with the synthetic mouse event below - if (auto event = pointerEvent->asPointerMouseEvent()) { - if (item->acceptedMouseButtons() & event->button()) { + auto event = pointerEvent->asPointerMouseEvent(); + if (event && item->acceptedMouseButtons() & event->button()) { auto point = event->point(0); if (point->isAccepted()) - return false; - + return; // The only reason to already have a mouse grabber here is // synthetic events - flickable sends one when setPressDelay is used. auto oldMouseGrabber = q->mouseGrabberItem(); - QPointF localPos = item->mapFromScene(point->scenePos()); + QPointF localPos = item->mapFromScene(point->scenePosition()); Q_ASSERT(item->contains(localPos)); // transform is checked already QMouseEvent *me = event->asMouseEvent(localPos); me->accept(); - q->sendEvent(item, me); + QCoreApplication::sendEvent(item, me); if (me->isAccepted()) { auto mouseGrabber = q->mouseGrabberItem(); if (mouseGrabber && mouseGrabber != item && mouseGrabber != oldMouseGrabber) { @@ -2339,33 +2525,23 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo } point->setAccepted(true); } - return me->isAccepted(); - } - return false; + return; } - QQuickPointerTouchEvent *event = pointerEvent->asPointerTouchEvent(); - if (!event) - return false; + QQuickPointerTouchEvent *ptEvent = pointerEvent->asPointerTouchEvent(); + if (!ptEvent) + return; - QScopedPointer<QTouchEvent> touchEvent(event->touchEventForItem(item)); + QScopedPointer<QTouchEvent> touchEvent(ptEvent->touchEventForItem(item)); if (!touchEvent) - return false; + return; - qCDebug(DBG_TOUCH) << " - considering delivering " << touchEvent.data() << " to " << item; + qCDebug(DBG_TOUCH) << "considering delivering " << touchEvent.data() << " to " << item; bool eventAccepted = false; - // First check whether the parent wants to be a filter, - // and if the parent accepts the event we are done. - if (sendFilteredTouchEvent(item->parentItem(), item, event, hasFiltered)) { - // If the touch was accepted (regardless by whom or in what form), - // update acceptedNewPoints - qCDebug(DBG_TOUCH) << " - can't. intercepted " << touchEvent.data() << " to " << item->parentItem() << " instead of " << item; - for (auto point: qAsConst(touchEvent->touchPoints())) { - event->pointById(point.id())->setAccepted(); - } - return true; - } + // If any parent filters the event, we're done. + if (sendFilteredPointerEvent(pointerEvent, item)) + return; // Deliver the touch event to the given item qCDebug(DBG_TOUCH) << " - actually delivering " << touchEvent.data() << " to " << item; @@ -2373,36 +2549,34 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo eventAccepted = touchEvent->isAccepted(); // If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it. - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) { // send mouse event - if (deliverTouchAsMouse(item, event)) + if (deliverTouchAsMouse(item, ptEvent)) eventAccepted = true; } if (eventAccepted) { // If the touch was accepted (regardless by whom or in what form), // update accepted new points. + bool isPressOrRelease = pointerEvent->isPressEvent() || pointerEvent->isReleaseEvent(); for (auto point: qAsConst(touchEvent->touchPoints())) { - auto pointerEventPoint = event->pointById(point.id()); + auto pointerEventPoint = ptEvent->pointById(point.id()); pointerEventPoint->setAccepted(); - if (point.state() == Qt::TouchPointPressed) - pointerEventPoint->setGrabber(item); + if (isPressOrRelease) + pointerEventPoint->setGrabberItem(item); } } else { // But if the event was not accepted then we know this item // will not be interested in further updates for those touchpoint IDs either. for (auto point: qAsConst(touchEvent->touchPoints())) { if (point.state() == Qt::TouchPointPressed) { - if (event->pointById(point.id())->grabber() == item) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << point.id() << "disassociated"; - event->pointById(point.id())->setGrabber(nullptr); + if (ptEvent->pointById(point.id())->exclusiveGrabber() == item) { + qCDebug(DBG_TOUCH_TARGET) << "TP" << hex << point.id() << "disassociated"; + ptEvent->pointById(point.id())->setGrabberItem(nullptr); } } } } - - return eventAccepted; } #if QT_CONFIG(draganddrop) @@ -2577,115 +2751,162 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF } #endif -bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem *item, QQuickPointerTouchEvent *event, QSet<QQuickItem *> *hasFiltered) +bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent) { - Q_Q(QQuickWindow); + hasFiltered.clear(); + return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem()); +} - if (!target) +bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent) +{ + if (!allowChildEventFiltering) + return false; + if (!filteringParent) return false; - bool filtered = false; - - QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); - if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { - hasFiltered->insert(target); - QScopedPointer<QTouchEvent> targetEvent(event->touchEventForItem(target, true)); - if (targetEvent) { - if (target->childMouseEventFilter(item, targetEvent.data())) { - qCDebug(DBG_TOUCH) << " - first chance intercepted on childMouseEventFilter by " << target; - QVector<int> touchIds; - const int touchPointCount = targetEvent->touchPoints().size(); - touchIds.reserve(touchPointCount); - for (int i = 0; i < touchPointCount; ++i) - touchIds.append(targetEvent->touchPoints().at(i).id()); - target->grabTouchPoints(touchIds); - if (q->mouseGrabberItem()) { - q->mouseGrabberItem()->ungrabMouse(); - touchMouseId = -1; - touchMouseDevice = nullptr; + if (filteringParent->filtersChildMouseEvents() && !hasFiltered.contains(filteringParent)) { + hasFiltered.append(filteringParent); + if (QQuickPointerMouseEvent *pme = event->asPointerMouseEvent()) { + if (receiver->acceptedMouseButtons()) { + QPointF localPos = receiver->mapFromScene(pme->point(0)->scenePosition()); + QMouseEvent *me = pme->asMouseEvent(localPos); + const bool wasAccepted = me->isAccepted(); + me->setAccepted(true); + auto oldMouseGrabber = pme->point(0)->grabberItem(); + if (filteringParent->childMouseEventFilter(receiver, me)) { + qCDebug(DBG_MOUSE) << "mouse event intercepted by childMouseEventFilter of " << filteringParent; + skipDelivery.append(filteringParent); + filtered = true; + if (me->isAccepted() && pme->isPressEvent()) { + auto mouseGrabber = pme->point(0)->grabberItem(); + if (mouseGrabber && mouseGrabber != receiver && mouseGrabber != oldMouseGrabber) { + receiver->mouseUngrabEvent(); + } else { + pme->point(0)->setGrabberItem(receiver); + } + } + } else { + // Restore accepted state if the event was not filtered. + me->setAccepted(wasAccepted); } - filtered = true; } + } else if (QQuickPointerTouchEvent *pte = event->asPointerTouchEvent()) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + bool acceptsTouchEvents = receiver->acceptTouchEvents(); +#else + // In versions prior to Qt 6, we can't trust item->acceptTouchEvents() here, because it defaults to true. + bool acceptsTouchEvents = false; +#endif + if (acceptsTouchEvents || receiver->acceptedMouseButtons()) { + // get a touch event customized for delivery to filteringParent + QScopedPointer<QTouchEvent> filteringParentTouchEvent(pte->touchEventForItem(receiver, true)); + if (filteringParentTouchEvent) { + QVarLengthArray<QPair<QQuickPointerHandler *, QQuickEventPoint *>, 32> passiveGrabsToCancel; + if (filteringParent->childMouseEventFilter(receiver, filteringParentTouchEvent.data())) { + qCDebug(DBG_TOUCH) << "touch event intercepted by childMouseEventFilter of " << filteringParent; + skipDelivery.append(filteringParent); + for (auto point: qAsConst(filteringParentTouchEvent->touchPoints())) { + auto pointerEventPoint = pte->pointById(point.id()); + for (auto handler : pointerEventPoint->passiveGrabbers()) { + QPair<QQuickPointerHandler *, QQuickEventPoint *> grab(handler, pointerEventPoint); + if (!passiveGrabsToCancel.contains(grab)) + passiveGrabsToCancel.append(grab); + } + QQuickEventPoint *pt = event->pointById(point.id()); + pt->setAccepted(); + pt->setGrabberItem(filteringParent); + } + return true; + } else { + // filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event. + for (int i = 0; i < filteringParentTouchEvent->touchPoints().size(); ++i) { + const QTouchEvent::TouchPoint &tp = filteringParentTouchEvent->touchPoints().at(i); + + QEvent::Type t; + switch (tp.state()) { + case Qt::TouchPointPressed: + t = QEvent::MouseButtonPress; + break; + case Qt::TouchPointReleased: + t = QEvent::MouseButtonRelease; + break; + case Qt::TouchPointStationary: + continue; + default: + t = QEvent::MouseMove; + break; + } - for (int i = 0; i < targetEvent->touchPoints().size(); ++i) { - const QTouchEvent::TouchPoint &tp = targetEvent->touchPoints().at(i); - - QEvent::Type t; - switch (tp.state()) { - case Qt::TouchPointPressed: - t = QEvent::MouseButtonPress; - break; - case Qt::TouchPointReleased: - t = QEvent::MouseButtonRelease; - break; - case Qt::TouchPointStationary: - continue; - default: - t = QEvent::MouseMove; - break; - } - - bool touchMouseUnset = (touchMouseId == -1); - // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId - if (touchMouseUnset || touchMouseId == tp.id()) { - // targetEvent is already transformed wrt local position, velocity, etc. - - // FIXME: remove asTouchEvent!!! - QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, event->asTouchEvent(), item, false)); - // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should - // report the touchpoint's grabber. Whenever we send a synthetic mouse event, - // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed. - touchMouseId = tp.id(); - touchMouseDevice = event->device(); - if (target->childMouseEventFilter(item, mouseEvent.data())) { - qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target; - if (t != QEvent::MouseButtonRelease) { - qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target; - if (touchMouseUnset) { - // the point was grabbed as a pure touch point before, now it will be treated as mouse - // but the old receiver still needs to be informed - if (auto oldGrabber = pointerEventInstance(touchMouseDevice)->pointById(tp.id())->grabber()) - oldGrabber->touchUngrabEvent(); + bool touchMouseUnset = (touchMouseId == -1); + // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId + if (touchMouseUnset || touchMouseId == tp.id()) { + // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.) + // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that + QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, filteringParentTouchEvent.data(), receiver, false)); + // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should + // report the touchpoint's grabber. Whenever we send a synthetic mouse event, + // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed. + touchMouseId = tp.id(); + touchMouseDevice = event->device(); + if (filteringParent->childMouseEventFilter(receiver, mouseEvent.data())) { + qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent; + skipDelivery.append(filteringParent); + if (t != QEvent::MouseButtonRelease) { + qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << tp.id() << "->" << filteringParent; + pointerEventInstance(touchMouseDevice)->pointById(tp.id())->setGrabberItem(filteringParent); + touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set + if (mouseEvent->isAccepted()) + filteringParent->grabMouse(); + auto pointerEventPoint = pte->pointById(tp.id()); + for (auto handler : pointerEventPoint->passiveGrabbers()) { + QPair<QQuickPointerHandler *, QQuickEventPoint *> grab(handler, pointerEventPoint); + if (!passiveGrabsToCancel.contains(grab)) + passiveGrabsToCancel.append(grab); + } + } + filtered = true; + } + if (touchMouseUnset) { + // Now that we're done sending a synth mouse event, and it wasn't grabbed, + // the touchpoint is no longer acting as a synthetic mouse. Restore previous state. + touchMouseId = -1; + touchMouseDevice = nullptr; + } + // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter + // has been called once, we're done with this loop over the touchpoints. + break; } - touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set - target->grabMouse(); } - filtered = true; - } - if (touchMouseUnset) { - // Now that we're done sending a synth mouse event, and it wasn't grabbed, - // the touchpoint is no longer acting as a synthetic mouse. Restore previous state. - touchMouseId = -1; - touchMouseDevice = nullptr; } - // Only one event can be filtered as a mouse event. - break; + for (auto grab : passiveGrabsToCancel) + grab.second->cancelPassiveGrab(grab.first); } } } } - - return sendFilteredTouchEvent(target->parentItem(), item, event, hasFiltered) || filtered; + return sendFilteredPointerEventImpl(event, receiver, filteringParent->parentItem()) || filtered; } -bool QQuickWindowPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event, QSet<QQuickItem *> *hasFiltered) +bool QQuickWindowPrivate::sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent) { - if (!target) + if (!filteringParent) return false; - QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); - if (targetPrivate->replayingPressEvent) + QQuickItemPrivate *filteringParentPrivate = QQuickItemPrivate::get(filteringParent); + if (filteringParentPrivate->replayingPressEvent) return false; bool filtered = false; - if (targetPrivate->filtersChildMouseEvents && !hasFiltered->contains(target)) { - hasFiltered->insert(target); - if (target->childMouseEventFilter(item, event)) + if (filteringParentPrivate->filtersChildMouseEvents && !hasFiltered.contains(filteringParent)) { + hasFiltered.append(filteringParent); + if (filteringParent->childMouseEventFilter(receiver, event)) { filtered = true; - qCDebug(DBG_MOUSE_TARGET) << target << "childMouseEventFilter ->" << filtered; + skipDelivery.append(filteringParent); + } + qCDebug(DBG_MOUSE_TARGET) << "for" << receiver << filteringParent << "childMouseEventFilter ->" << filtered; } - return sendFilteredMouseEvent(target->parentItem(), item, event, hasFiltered) || filtered; + return sendFilteredMouseEvent(event, receiver, filteringParent->parentItem()) || filtered; } bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent *event, int startDragThreshold) @@ -2797,6 +3018,8 @@ void QQuickWindowPrivate::contextCreationFailureMessage(const QSurfaceFormat &fo #endif // !Q_OS_WIN32 } +#if QT_DEPRECATED_SINCE(5, 8) + /*! Propagates an event \a e to a QQuickItem \a item on the window. @@ -2833,8 +3056,8 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) case QEvent::MouseButtonDblClick: case QEvent::MouseMove: { // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? - QSet<QQuickItem *> hasFiltered; - if (!d->sendFilteredMouseEvent(item->parentItem(), item, e, &hasFiltered)) { + d->hasFiltered.clear(); + if (!d->sendFilteredMouseEvent(e, item, item->parentItem())) { // accept because qml items by default accept and have to explicitly opt out of accepting e->accept(); QCoreApplication::sendEvent(item, e); @@ -2849,6 +3072,8 @@ bool QQuickWindow::sendEvent(QQuickItem *item, QEvent *e) return false; } +#endif + void QQuickWindowPrivate::cleanupNodes() { for (int ii = 0; ii < cleanupNodeList.count(); ++ii) @@ -3450,6 +3675,11 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo) The specified FBO must be created in the context of the window or one that shares with it. + \note \a fboId can also be set to 0. In this case rendering will target the + default framebuffer of whichever surface is current when the scenegraph + renders. \a size must still be valid, specifying the dimensions of the + surface. + \note This function only has an effect when using the default OpenGL scene graph adaptation. @@ -3631,6 +3861,23 @@ QQmlIncubationController *QQuickWindow::incubationController() const */ /*! + \enum QQuickWindow::TextRenderType + \since 5.10 + + This enum describes the default render type of text-like elements in Qt + Quick (\l Text, \l TextInput, etc.). + + Select NativeTextRendering if you prefer text to look native on the target + platform and do not require advanced features such as transformation of the + text. Using such features in combination with the NativeTextRendering + render type will lend poor and sometimes pixelated results. + + \value QtTextRendering Use Qt's own rasterization algorithm. + + \value NativeTextRendering Use the operating system's native rasterizer for text. +*/ + +/*! \fn void QQuickWindow::beforeSynchronizing() This signal is emitted before the scene graph is synchronized with the QML state. @@ -4600,7 +4847,7 @@ void QQuickWindow::setSceneGraphBackend(const QString &backend) } /*! - Returns the requested Qt Quick scenegraph \a backend. + Returns the requested Qt Quick scenegraph backend. \note The return value of this function may still be outdated by subsequent calls to setSceneGraphBackend() until the first QQuickWindow in the @@ -4652,6 +4899,34 @@ QSGNinePatchNode *QQuickWindow::createNinePatchNode() const return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createNinePatchNode() : nullptr; } +/*! + \since 5.10 + + Returns the render type of text-like elements in Qt Quick. + The default is QQuickWindow::QtTextRendering. + + \sa setTextRenderType() +*/ +QQuickWindow::TextRenderType QQuickWindow::textRenderType() +{ + return QQuickWindowPrivate::textRenderType; +} + +/*! + \since 5.10 + + Sets the default render type of text-like elements in Qt Quick to \a renderType. + + \note setting the render type will only affect elements created afterwards; + the render type of existing elements will not be modified. + + \sa textRenderType() +*/ +void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType) +{ + QQuickWindowPrivate::textRenderType = renderType; +} + #include "moc_qquickwindow.cpp" QT_END_NAMESPACE |