diff options
Diffstat (limited to 'src/quick/items')
22 files changed, 284 insertions, 82 deletions
diff --git a/src/quick/items/qquickaccessibleattached.cpp b/src/quick/items/qquickaccessibleattached.cpp index 730bbe4404..f63ab764f1 100644 --- a/src/quick/items/qquickaccessibleattached.cpp +++ b/src/quick/items/qquickaccessibleattached.cpp @@ -109,7 +109,7 @@ QT_BEGIN_NAMESPACE This property sets an accessible description. Similar to the name it describes the item. The description can be a little more verbose and tell what the item does, - for example the functionallity of the button it describes. + for example the functionality of the button it describes. */ /*! diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h index e17a28d07e..26191c6545 100644 --- a/src/quick/items/qquickdrag_p.h +++ b/src/quick/items/qquickdrag_p.h @@ -108,9 +108,12 @@ public: void grab(QQuickItem *item) { m_items.insert(new Item(item)); } iterator release(iterator at) { Item *item = *at; at = at.erase(); delete item; return at; } + auto& ignoreList() { return m_ignoreDragItems; } + private: ItemList m_items; + QVarLengthArray<QQuickItem *, 4> m_ignoreDragItems; QObject *m_target; }; diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 8dc9a9afe6..4ddbfa509a 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -43,6 +43,8 @@ #include "qquickwindow.h" #include "qquickwindow_p.h" #include "qquickevents_p_p.h" +#include "qquickmousearea_p.h" +#include "qquickdrag_p.h" #include <QtQuick/private/qquickpointerhandler_p.h> #include <QtQuick/private/qquicktransition_p.h> @@ -352,7 +354,7 @@ void QQuickFlickablePrivate::AxisData::updateVelocity() } } -void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &) +void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &oldGeom) { Q_Q(QQuickFlickable); if (item == contentItem) { @@ -361,8 +363,14 @@ void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometr orient |= Qt::Horizontal; if (change.yChange()) orient |= Qt::Vertical; - if (orient) + if (orient) { q->viewportMoved(orient); + const QPointF deltaMoved = item->position() - oldGeom.topLeft(); + if (hData.contentPositionChangedExternallyDuringDrag) + hData.pressPos += deltaMoved.x(); + if (vData.contentPositionChangedExternallyDuringDrag) + vData.pressPos += deltaMoved.y(); + } if (orient & Qt::Horizontal) emit q->contentXChanged(); if (orient & Qt::Vertical) @@ -554,8 +562,8 @@ void QQuickFlickablePrivate::updateBeginningEnd() const qreal maxyextent = -q->maxYExtent(); const qreal minyextent = -q->minYExtent(); const qreal ypos = -vData.move.value(); - bool atBeginning = fuzzyLessThanOrEqualTo(ypos, minyextent); - bool atEnd = fuzzyLessThanOrEqualTo(maxyextent, ypos); + bool atBeginning = fuzzyLessThanOrEqualTo(ypos, std::ceil(minyextent)); + bool atEnd = fuzzyLessThanOrEqualTo(std::floor(maxyextent), ypos); if (atBeginning != vData.atBeginning) { vData.atBeginning = atBeginning; @@ -574,8 +582,8 @@ void QQuickFlickablePrivate::updateBeginningEnd() const qreal maxxextent = -q->maxXExtent(); const qreal minxextent = -q->minXExtent(); const qreal xpos = -hData.move.value(); - atBeginning = fuzzyLessThanOrEqualTo(xpos, minxextent); - atEnd = fuzzyLessThanOrEqualTo(maxxextent, xpos); + atBeginning = fuzzyLessThanOrEqualTo(xpos, std::ceil(minxextent)); + atEnd = fuzzyLessThanOrEqualTo(std::floor(maxxextent), xpos); if (atBeginning != hData.atBeginning) { hData.atBeginning = atBeginning; @@ -822,8 +830,11 @@ void QQuickFlickable::setContentX(qreal pos) d->hData.vTime = d->timeline.time(); if (isMoving() || isFlicking()) movementEnding(true, false); - if (!qFuzzyCompare(-pos, d->hData.move.value())) + if (!qFuzzyCompare(-pos, d->hData.move.value())) { + d->hData.contentPositionChangedExternallyDuringDrag = d->hData.dragging; d->hData.move.setValue(-pos); + d->hData.contentPositionChangedExternallyDuringDrag = false; + } } qreal QQuickFlickable::contentY() const @@ -840,8 +851,11 @@ void QQuickFlickable::setContentY(qreal pos) d->vData.vTime = d->timeline.time(); if (isMoving() || isFlicking()) movementEnding(false, true); - if (!qFuzzyCompare(-pos, d->vData.move.value())) + if (!qFuzzyCompare(-pos, d->vData.move.value())) { + d->vData.contentPositionChangedExternallyDuringDrag = d->vData.dragging; d->vData.move.setValue(-pos); + d->vData.contentPositionChangedExternallyDuringDrag = false; + } } /*! @@ -1655,9 +1669,9 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) d->vData.addVelocitySample(instVelocity, d->maxVelocity); d->vData.updateVelocity(); if ((yDelta > 0 && contentY() > -minYExtent()) || (yDelta < 0 && contentY() < -maxYExtent())) { - d->flickY(d->vData.velocity); - d->flickingStarted(false, true); - if (d->vData.flicking) { + const bool newFlick = d->flickY(d->vData.velocity); + if (newFlick && (d->vData.atBeginning != (yDelta > 0) || d->vData.atEnd != (yDelta < 0))) { + d->flickingStarted(false, true); d->vMoved = true; movementStarting(); } @@ -1672,9 +1686,9 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event) d->hData.addVelocitySample(instVelocity, d->maxVelocity); d->hData.updateVelocity(); if ((xDelta > 0 && contentX() > -minXExtent()) || (xDelta < 0 && contentX() < -maxXExtent())) { - d->flickX(d->hData.velocity); - d->flickingStarted(true, false); - if (d->hData.flicking) { + const bool newFlick = d->flickX(d->hData.velocity); + if (newFlick && (d->hData.atBeginning != (xDelta > 0) || d->hData.atEnd != (xDelta < 0))) { + d->flickingStarted(true, false); d->hMoved = true; movementStarting(); } @@ -2553,6 +2567,23 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev bool receiverDisabled = receiver && !receiver->isEnabled(); bool stealThisEvent = d->stealMouse; bool receiverKeepsGrab = receiver && (receiver->keepMouseGrab() || receiver->keepTouchGrab()); + bool receiverRelinquishGrab = false; + + // Special case for MouseArea, try to guess what it does with the event + if (auto *mouseArea = qmlobject_cast<QQuickMouseArea *>(receiver)) { + bool preventStealing = mouseArea->preventStealing(); + if (mouseArea->drag() && mouseArea->drag()->target()) + preventStealing = true; + if (!preventStealing && receiverKeepsGrab) { + receiverRelinquishGrab = !receiverDisabled + || (QQuickDeliveryAgentPrivate::isMouseEvent(event) + && firstPoint.state() == QEventPoint::State::Pressed + && (receiver->acceptedMouseButtons() & static_cast<QMouseEvent *>(event)->button())); + if (receiverRelinquishGrab) + receiverKeepsGrab = false; + } + } + if ((stealThisEvent || contains(localPos)) && (!receiver || !receiverKeepsGrab || receiverDisabled)) { QScopedPointer<QPointerEvent> localizedEvent(QQuickDeliveryAgentPrivate::clonePointerEvent(event, localPos)); localizedEvent->setAccepted(false); @@ -2563,7 +2594,9 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev case QEventPoint::State::Pressed: d->handlePressEvent(localizedEvent.data()); d->captureDelayedPress(receiver, event); - stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above + // never grab the pointing device on press during filtering: do it later, during a move + d->stealMouse = false; + stealThisEvent = false; break; case QEventPoint::State::Released: d->handleReleaseEvent(localizedEvent.data()); @@ -2580,7 +2613,7 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev event->setExclusiveGrabber(firstPoint, this); } - const bool filtered = stealThisEvent || d->delayedPressEvent || receiverDisabled; + const bool filtered = !receiverRelinquishGrab && (stealThisEvent || d->delayedPressEvent || receiverDisabled); if (filtered) { event->setAccepted(true); } @@ -2606,18 +2639,19 @@ bool QQuickFlickable::filterPointerEvent(QQuickItem *receiver, QPointerEvent *ev bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e) { Q_D(QQuickFlickable); + QPointerEvent *pointerEvent = e->isPointerEvent() ? static_cast<QPointerEvent *>(e) : nullptr; - auto wantsPointerEvent_helper = [this, d, i, e]() { - QPointerEvent *pe = static_cast<QPointerEvent *>(e); - QQuickDeliveryAgentPrivate::localizePointerEvent(pe, this); - const bool wants = d->wantsPointerEvent(pe); + auto wantsPointerEvent_helper = [this, d, i, pointerEvent]() { + Q_ASSERT(pointerEvent); + QQuickDeliveryAgentPrivate::localizePointerEvent(pointerEvent, this); + const bool wants = d->wantsPointerEvent(pointerEvent); // re-localize event back to \a i before returning - QQuickDeliveryAgentPrivate::localizePointerEvent(pe, i); + QQuickDeliveryAgentPrivate::localizePointerEvent(pointerEvent, i); return wants; }; if (!isVisible() || !isEnabled() || !isInteractive() || - (e->isPointerEvent() && !wantsPointerEvent_helper())) { + (pointerEvent && !wantsPointerEvent_helper())) { d->cancelInteraction(); return QQuickItem::childMouseEventFilter(i, e); } @@ -2629,8 +2663,8 @@ bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e) qCDebug(lcFilter) << "filtering UngrabMouse" << spe->points().first() << "for" << i << "grabber is" << grabber; if (grabber != this) mouseUngrabEvent(); // A child has been ungrabbed - } else if (e->isPointerEvent()) { - return filterPointerEvent(i, static_cast<QPointerEvent *>(e)); + } else if (pointerEvent) { + return filterPointerEvent(i, pointerEvent); } return QQuickItem::childMouseEventFilter(i, e); @@ -2898,6 +2932,12 @@ void QQuickFlickable::movementStarting() if (!wasMoving && (d->hData.moving || d->vData.moving)) { emit movingChanged(); emit movementStarted(); +#if QT_CONFIG(accessibility) + if (QAccessible::isActive()) { + QAccessibleEvent ev(this, QAccessible::ScrollingStart); + QAccessible::updateAccessibility(&ev); + } +#endif } } @@ -2942,6 +2982,12 @@ void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding) if (wasMoving && !isMoving()) { emit movingChanged(); emit movementEnded(); +#if QT_CONFIG(accessibility) + if (QAccessible::isActive()) { + QAccessibleEvent ev(this, QAccessible::ScrollingEnd); + QAccessible::updateAccessibility(&ev); + } +#endif } if (hMovementEnding) { diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index 9bc33d436f..a0c3d4ad6c 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -109,6 +109,7 @@ public: , fixingUp(false), inOvershoot(false), inRebound(false), moving(false), flicking(false) , dragging(false), extentsChanged(false) , explicitValue(false), minExtentDirty(true), maxExtentDirty(true) + , contentPositionChangedExternallyDuringDrag(false) , unused(0) {} @@ -119,6 +120,7 @@ public: dragStartOffset = 0; fixingUp = false; inOvershoot = false; + contentPositionChangedExternallyDuringDrag = false; } void markExtentsDirty() { @@ -169,7 +171,8 @@ public: bool explicitValue : 1; mutable bool minExtentDirty : 1; mutable bool maxExtentDirty : 1; - uint unused : 19; + bool contentPositionChangedExternallyDuringDrag : 1; + uint unused : 18; }; bool flickX(qreal velocity); diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 5c4e4d7018..e0adac3337 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -208,6 +208,8 @@ public: void updateHeader() override; void updateFooter() override; + void initializeComponentItem(QQuickItem *item) const override; + void changedVisibleIndex(int newIndex) override; void initializeCurrentItem() override; @@ -853,6 +855,14 @@ void QQuickGridViewPrivate::updateFooter() emit q->footerItemChanged(); } +void QQuickGridViewPrivate::initializeComponentItem(QQuickItem *item) const +{ + QQuickGridViewAttached *attached = static_cast<QQuickGridViewAttached *>( + qmlAttachedPropertiesObject<QQuickGridView>(item)); + if (attached) + attached->setView(const_cast<QQuickGridView*>(q_func())); +} + void QQuickGridViewPrivate::updateHeader() { Q_Q(QQuickGridView); diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 8102a473db..008c078f5d 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -4087,8 +4087,9 @@ void QQuickItem::mouseReleaseEvent(QMouseEvent *event) \input item.qdocinc accepting-events */ -void QQuickItem::mouseDoubleClickEvent(QMouseEvent *) +void QQuickItem::mouseDoubleClickEvent(QMouseEvent *event) { + event->ignore(); } /*! @@ -5521,20 +5522,26 @@ bool QQuickItemPrivate::anyPointerHandlerWants(const QPointerEvent *event, const /*! \internal - Deliver the \a event to all PointerHandlers which are in the pre-determined - eventDeliveryTargets() vector. If \a avoidExclusiveGrabber is true, it skips - delivery to any handler which is the exclusive grabber of any point within this event - (because delivery to exclusive grabbers is handled separately). + Deliver the \a event to all this item's PointerHandlers, but skip + HoverHandlers if the event is a QMouseEvent (they are visited in + QQuickDeliveryAgentPrivate::deliverHoverEventToItem()), and skip handlers + that are in QQuickPointerHandlerPrivate::deviceDeliveryTargets(). + If \a avoidGrabbers is true, also skip delivery to any handler that + is exclusively or passively grabbing any point within \a event + (because delivery to grabbers is handled separately). */ -bool QQuickItemPrivate::handlePointerEvent(QPointerEvent *event, bool avoidExclusiveGrabber) +bool QQuickItemPrivate::handlePointerEvent(QPointerEvent *event, bool avoidGrabbers) { bool delivered = false; if (extra.isAllocated()) { for (QQuickPointerHandler *handler : extra->pointerHandlers) { bool avoidThisHandler = false; - if (avoidExclusiveGrabber) { + if (QQuickDeliveryAgentPrivate::isMouseEvent(event) && + qmlobject_cast<const QQuickHoverHandler *>(handler)) { + avoidThisHandler = true; + } else if (avoidGrabbers) { for (auto &p : event->points()) { - if (event->exclusiveGrabber(p) == handler) { + if (event->exclusiveGrabber(p) == handler || event->passiveGrabbers(p).contains(handler)) { avoidThisHandler = true; break; } @@ -9508,7 +9515,9 @@ void QQuickItemLayer::updateGeometry() { QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource; Q_ASSERT(l); - QRectF bounds = m_item->boundingRect(); + // Avoid calling QQuickImage::boundingRect() or other overrides + // which may not be up-to-date at this time (QTBUG-104442, 104536) + QRectF bounds = m_item->QQuickItem::boundingRect(); l->setSize(bounds.size()); l->setPosition(bounds.topLeft() + m_item->position()); } diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 839cc5bdb4..ce094b15f4 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -669,7 +669,7 @@ public: void deliverShortcutOverrideEvent(QKeyEvent *); bool anyPointerHandlerWants(const QPointerEvent *event, const QEventPoint &point) const; - virtual bool handlePointerEvent(QPointerEvent *, bool avoidExclusiveGrabber = false); + virtual bool handlePointerEvent(QPointerEvent *, bool avoidGrabbers = false); virtual void setVisible(bool visible); diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 39e1f42fe5..943d24b32c 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -57,8 +57,6 @@ FxViewItem::FxViewItem(QQuickItem *i, QQuickItemView *v, bool own, QQuickItemVie , view(v) , attached(attached) { - if (attached) // can be null for default components (see createComponentItem) - attached->setView(view); } QQuickItemViewChangeSet::QQuickItemViewChangeSet() @@ -2502,12 +2500,29 @@ QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, item->setZ(zValue); QQml_setParent_noEvent(item, q->contentItem()); item->setParentItem(q->contentItem()); + + initializeComponentItem(item); } if (component) component->completeCreate(); return item; } +/*! + \internal + + Allows derived classes to do any initialization required for \a item + before completeCreate() is called on it. For example, any attached + properties required by the item can be set. + + This is similar to initItem(), but as that has logic specific to + delegate items, we use a separate function for non-delegates. +*/ +void QQuickItemViewPrivate::initializeComponentItem(QQuickItem *item) const +{ + Q_UNUSED(item); +} + void QQuickItemViewPrivate::updateTrackedItem() { Q_Q(QQuickItemView); diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index d3b12268aa..d48e4160ac 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -178,6 +178,7 @@ public: QQuickItem *createHighlightItem() const; QQuickItem *createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault = false) const; + virtual void initializeComponentItem(QQuickItem *) const; void updateCurrent(int modelIndex); void updateTrackedItem(); diff --git a/src/quick/items/qquickitemviewtransition.cpp b/src/quick/items/qquickitemviewtransition.cpp index 3c84468370..6b03d6c16b 100644 --- a/src/quick/items/qquickitemviewtransition.cpp +++ b/src/quick/items/qquickitemviewtransition.cpp @@ -524,7 +524,8 @@ void QQuickItemViewTransitionableItem::completeTransition(QQuickTransition *quic QQuickStateOperation::ActionList actions; // not used QList<QQmlProperty> after; // not used - auto instance = quickTransition->prepare(actions, after, transition, item); + QScopedPointer<QQuickTransitionInstance> instance( + quickTransition->prepare(actions, after, transition, item)); RETURN_IF_DELETED(instance->complete()); clearCurrentScheduledTransition(); @@ -579,6 +580,8 @@ void QQuickItemViewTransitionableItem::stopTransition() { if (transition) RETURN_IF_DELETED(transition->cancel()); + delete transition; + transition = nullptr; clearCurrentScheduledTransition(); resetNextTransitionPos(); } diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 7ddda6196f..868d70b3fe 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -130,6 +130,8 @@ public: bool hasStickyHeader() const override; bool hasStickyFooter() const override; + void initializeComponentItem(QQuickItem *item) const override; + void changedVisibleIndex(int newIndex) override; void initializeCurrentItem() override; @@ -1575,6 +1577,14 @@ bool QQuickListViewPrivate::hasStickyFooter() const return footer && footerPositioning != QQuickListView::InlineFooter; } +void QQuickListViewPrivate::initializeComponentItem(QQuickItem *item) const +{ + QQuickListViewAttached *attached = static_cast<QQuickListViewAttached *>( + qmlAttachedPropertiesObject<QQuickListView>(item)); + if (attached) // can be null for default components (see createComponentItem) + attached->setView(const_cast<QQuickListView*>(q_func())); +} + void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &oldGeometry) { diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index d6ce5d7769..80299b3c38 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -146,7 +146,8 @@ QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool async) item->setParentItem(q); requestedIndex = -1; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); + itemPrivate->addItemChangeListener( + this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); } inRequest = false; return item; @@ -199,11 +200,14 @@ void QQuickPathView::initItem(int index, QObject *object) void QQuickPathViewPrivate::releaseItem(QQuickItem *item) { - if (!item || !model) + if (!item) return; qCDebug(lcItemViewDelegateLifecycle) << "release" << item; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + itemPrivate->removeItemChangeListener( + this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed); + if (!model) + return; QQmlInstanceModel::ReleaseFlags flags = model->release(item); if (!flags) { // item was not destroyed, and we no longer reference it. diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h index 274086ea7c..b83cd95b95 100644 --- a/src/quick/items/qquickpathview_p_p.h +++ b/src/quick/items/qquickpathview_p_p.h @@ -88,6 +88,12 @@ public: } } + void itemDestroyed(QQuickItem *item) override + { + if (!items.removeOne(item)) + itemCache.removeOne(item); + } + void scheduleLayout() { Q_Q(QQuickPathView); if (!layoutScheduled) { diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index b6e22169b2..37458542d3 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -390,6 +390,7 @@ void QQuickPinchArea::clearPinch(QTouchEvent *event) } } setKeepTouchGrab(false); + setKeepMouseGrab(false); } void QQuickPinchArea::cancelPinch(QTouchEvent *event) @@ -431,6 +432,7 @@ void QQuickPinchArea::cancelPinch(QTouchEvent *event) event->setExclusiveGrabber(point, nullptr); } setKeepTouchGrab(false); + setKeepMouseGrab(false); } void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering) @@ -440,6 +442,7 @@ void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering) if (d->touchPoints.count() < 2) { // A pinch gesture is not occurring, so stealing the grab is permitted. setKeepTouchGrab(false); + setKeepMouseGrab(false); // During filtering, there's no need to hold a grab for one point, // because filtering happens for every event anyway. // But if we receive the event via direct delivery, and give up the grab, @@ -463,6 +466,8 @@ void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering) pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); pe.setPoint1(mapFromScene(d->lastPoint1)); pe.setPoint2(mapFromScene(d->lastPoint2)); + setKeepTouchGrab(false); + setKeepMouseGrab(false); emit pinchFinished(&pe); d->pinchStartDist = 0; d->pinchActivated = false; @@ -499,6 +504,8 @@ void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering) d->initPinch = true; event->setExclusiveGrabber(touchPoint1, this); event->setExclusiveGrabber(touchPoint2, this); + setKeepTouchGrab(true); + setKeepMouseGrab(true); } if (d->pinchActivated && !d->pinchRejected) { const int dragThreshold = QGuiApplication::styleHints()->startDragDistance(); @@ -561,6 +568,9 @@ void QQuickPinchArea::updatePinch(QTouchEvent *event, bool filtering) event->setExclusiveGrabber(touchPoint1, this); event->setExclusiveGrabber(touchPoint2, this); setKeepTouchGrab(true); + // So that PinchArea works in PathView, grab mouse events too. + // We should be able to remove these setKeepMouseGrab calls when QTBUG-105567 is fixed. + setKeepMouseGrab(true); d->inPinch = true; if (d->pinch && d->pinch->target()) { auto targetParent = pinch()->target()->parentItem(); diff --git a/src/quick/items/qquickrepeater.cpp b/src/quick/items/qquickrepeater.cpp index 3703bb38cf..35d9da976d 100644 --- a/src/quick/items/qquickrepeater.cpp +++ b/src/quick/items/qquickrepeater.cpp @@ -439,6 +439,12 @@ void QQuickRepeater::initItem(int index, QObject *object) } d->deletables[index] = item; item->setParentItem(parentItem()); + + // If the item comes from an ObjectModel, it might be used as + // ComboBox/Menu/TabBar's contentItem. These types unconditionally cull items + // that are inserted, so account for that here. + if (d->dataSourceIsObject) + QQuickItemPrivate::get(item)->setCulled(false); if (index > 0 && d->deletables.at(index-1)) { item->stackAfter(d->deletables.at(index-1)); } else { diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 61f4e7af99..a476a906f5 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -2445,21 +2445,23 @@ void QQuickText::geometryChange(const QRectF &newGeometry, const QRectF &oldGeom goto geomChangeDone; if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed - if (newGeometry.height() > oldGeometry.height()) { - if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) { - // Height is adequate and growing, and it wasn't 0 previously. - goto geomChangeDone; - } - if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing. - goto geomChangeDone; - } else if (newGeometry.height() < oldGeometry.height()) { - if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0) // A single line won't be truncated until the text is 0 height. - goto geomChangeDone; - - if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count. - && d->elideMode != QQuickText::ElideRight - && !(d->maximumLineCountValid && d->widthExceeded)) { - goto geomChangeDone; + if (!verticalPositionChanged) { + if (newGeometry.height() > oldGeometry.height()) { + if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) { + // Height is adequate and growing, and it wasn't 0 previously. + goto geomChangeDone; + } + if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing. + goto geomChangeDone; + } else if (newGeometry.height() < oldGeometry.height()) { + if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0) // A single line won't be truncated until the text is 0 height. + goto geomChangeDone; + + if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count. + && d->elideMode != QQuickText::ElideRight + && !(d->maximumLineCountValid && d->widthExceeded)) { + goto geomChangeDone; + } } } } else if (!heightChanged && widthMaximum) { @@ -2987,7 +2989,7 @@ void QQuickTextPrivate::processHoverEvent(QHoverEvent *event) emit q->linkHovered(extra->hoveredLink); } } - event->setAccepted(!link.isEmpty()); + event->ignore(); } void QQuickText::hoverEnterEvent(QHoverEvent *event) diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index f739474908..0df71fc559 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1592,15 +1592,20 @@ bool QQuickTextEdit::selectByMouse() const void QQuickTextEdit::setSelectByMouse(bool on) { Q_D(QQuickTextEdit); - if (d->selectByMouse != on) { - d->selectByMouse = on; - setKeepMouseGrab(on); - if (on) - d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse); - else - d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse); - emit selectByMouseChanged(on); - } + if (d->selectByMouse == on) + return; + + d->selectByMouse = on; + setKeepMouseGrab(on); + if (on) + d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse); + else + d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse); + +#if QT_CONFIG(cursor) + d->updateMouseCursorShape(); +#endif + emit selectByMouseChanged(on); } /*! @@ -1663,6 +1668,9 @@ void QQuickTextEdit::setReadOnly(bool r) #if QT_CONFIG(im) updateInputMethod(Qt::ImEnabled); #endif +#if QT_CONFIG(cursor) + d->updateMouseCursorShape(); +#endif q_canPasteChanged(); emit readOnlyChanged(r); if (!d->selectByKeyboardSet) @@ -2247,9 +2255,13 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * } } + bool createdNodeInView = false; if (inView) { if (!engine.hasContents()) { + if (node && !node->parent()) + d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart); node = d->createTextNode(); + createdNodeInView = true; updateNodeTransform(node, nodeOffset); nodeStart = block.position(); } @@ -2259,13 +2271,13 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos()) break; // last node that needed replacing or last block of the frame - QList<int>::const_iterator lowerBound = std::lower_bound(frameBoundaries.constBegin(), frameBoundaries.constEnd(), block.next().position()); if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) { currentNodeSize = 0; if (!node->parent()) d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart); - node = d->createTextNode(); + if (!createdNodeInView) + node = d->createTextNode(); resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor); nodeStart = block.next().position(); } @@ -2450,7 +2462,7 @@ void QQuickTextEditPrivate::init() updateDefaultTextOption(); q->updateSize(); #if QT_CONFIG(cursor) - q->setCursor(Qt::IBeamCursor); + updateMouseCursorShape(); #endif } @@ -2734,9 +2746,8 @@ void QQuickTextEdit::q_linkHovered(const QString &link) emit linkHovered(link); #if QT_CONFIG(cursor) if (link.isEmpty()) { - setCursor(d->cursorToRestoreAfterHover); + d->updateMouseCursorShape(); } else if (cursor().shape() != Qt::PointingHandCursor) { - d->cursorToRestoreAfterHover = cursor().shape(); setCursor(Qt::PointingHandCursor); } #endif @@ -2747,9 +2758,8 @@ void QQuickTextEdit::q_markerHovered(bool hovered) Q_D(QQuickTextEdit); #if QT_CONFIG(cursor) if (!hovered) { - setCursor(d->cursorToRestoreAfterHover); + d->updateMouseCursorShape(); } else if (cursor().shape() != Qt::PointingHandCursor) { - d->cursorToRestoreAfterHover = cursor().shape(); setCursor(Qt::PointingHandCursor); } #endif @@ -3020,6 +3030,14 @@ bool QQuickTextEditPrivate::isLinkHoveredConnected() IS_SIGNAL_CONNECTED(q, QQuickTextEdit, linkHovered, (const QString &)); } +#if QT_CONFIG(cursor) +void QQuickTextEditPrivate::updateMouseCursorShape() +{ + Q_Q(QQuickTextEdit); + q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor); +} +#endif + /*! \qmlsignal QtQuick::TextEdit::linkHovered(string link) \since 5.2 @@ -3070,6 +3088,7 @@ void QQuickTextEdit::hoverEnterEvent(QHoverEvent *event) Q_D(QQuickTextEdit); if (d->isLinkHoveredConnected()) d->control->processEvent(event, QPointF(-d->xoff, -d->yoff)); + event->ignore(); } void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event) @@ -3077,6 +3096,7 @@ void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event) Q_D(QQuickTextEdit); if (d->isLinkHoveredConnected()) d->control->processEvent(event, QPointF(-d->xoff, -d->yoff)); + event->ignore(); } void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event) @@ -3084,6 +3104,7 @@ void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event) Q_D(QQuickTextEdit); if (d->isLinkHoveredConnected()) d->control->processEvent(event, QPointF(-d->xoff, -d->yoff)); + event->ignore(); } /*! @@ -3303,6 +3324,16 @@ void QQuickTextEdit::clear() d->control->clear(); } +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QQuickTextEditPrivate::Node &n) +{ + QDebugStateSaver saver(debug); + debug.space(); + debug << "Node(startPos:" << n.m_startPos << "dirty:" << n.m_dirty << n.m_node << ')'; + return debug; +} +#endif + QT_END_NAMESPACE #include "moc_qquicktextedit_p.cpp" diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index 2381b46a05..1f0099e00e 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -89,6 +89,10 @@ public: int m_startPos; QQuickTextNode* m_node; bool m_dirty; + +#ifndef QT_NO_DEBUG_STREAM + friend QDebug Q_QUICK_PRIVATE_EXPORT operator<<(QDebug, const Node &); +#endif }; typedef QList<Node>::iterator TextNodeIterator; @@ -147,6 +151,10 @@ public: Qt::LayoutDirection textDirection(const QString &text) const; bool isLinkHoveredConnected(); +#if QT_CONFIG(cursor) + void updateMouseCursorShape(); +#endif + void setNativeCursorEnabled(bool) {} void handleFocusEvent(QFocusEvent *event); void addCurrentTextNodeToRoot(QQuickTextNodeEngine *, QSGTransformNode *, QQuickTextNode*, TextNodeIterator&, int startPos); @@ -211,7 +219,6 @@ public: Qt::InputMethodHints inputMethodHints; #endif UpdateType updateType; - Qt::CursorShape cursorToRestoreAfterHover = Qt::IBeamCursor; bool dirty : 1; bool richText : 1; @@ -234,6 +241,10 @@ public: static const int largeTextSizeThreshold; }; +#ifndef QT_NO_DEBUG_STREAM +QDebug Q_QUICK_PRIVATE_EXPORT operator<<(QDebug debug, const QQuickTextEditPrivate::Node &); +#endif + QT_END_NAMESPACE #endif // QQUICKTEXTEDIT_P_P_H diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 72fe2a56fe..cb50462761 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -517,8 +517,16 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color) } /*! - \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment \qmlproperty enumeration QtQuick::TextInput::effectiveHorizontalAlignment + \readonly + + When using the attached property LayoutMirroring::enabled to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \l horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of TextInput, use the read-only property \c effectiveHorizontalAlignment. +*/ +/*! + \qmlproperty enumeration QtQuick::TextInput::horizontalAlignment \qmlproperty enumeration QtQuick::TextInput::verticalAlignment Sets the horizontal alignment of the text within the TextInput item's @@ -541,7 +549,7 @@ void QQuickTextInput::setSelectedTextColor(const QColor &color) When using the attached property LayoutMirroring::enabled to mirror application layouts, the horizontal alignment of text will also be mirrored. However, the property \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment - of TextInput, use the read-only property \c effectiveHorizontalAlignment. + of TextInput, use the read-only property \l effectiveHorizontalAlignment. */ QQuickTextInput::HAlignment QQuickTextInput::hAlign() const { @@ -861,6 +869,7 @@ void QQuickTextInput::setCursorPosition(int cp) /*! \qmlproperty rectangle QtQuick::TextInput::cursorRectangle + \readonly The rectangle where the standard text cursor is rendered within the text input. Read only. @@ -902,6 +911,7 @@ QRectF QQuickTextInput::cursorRectangle() const This property is read-only. To change the selection, use select(start,end), selectAll(), or selectWord(). + \readonly \sa selectionEnd, cursorPosition, selectedText */ int QQuickTextInput::selectionStart() const @@ -917,6 +927,7 @@ int QQuickTextInput::selectionStart() const This property is read-only. To change the selection, use select(start,end), selectAll(), or selectWord(). + \readonly \sa selectionStart, cursorPosition, selectedText */ int QQuickTextInput::selectionEnd() const @@ -947,6 +958,7 @@ void QQuickTextInput::select(int start, int end) /*! \qmlproperty string QtQuick::TextInput::selectedText + \readonly This read-only property provides the text currently selected in the text input. @@ -2470,6 +2482,7 @@ void QQuickTextInput::setPersistentSelection(bool on) /*! \qmlproperty bool QtQuick::TextInput::canPaste + \readonly Returns true if the TextInput is writable and the content of the clipboard is suitable for pasting into the TextInput. @@ -2491,6 +2504,7 @@ bool QQuickTextInput::canPaste() const /*! \qmlproperty bool QtQuick::TextInput::canUndo + \readonly Returns true if the TextInput is writable and there are previous operations that can be undone. @@ -2504,6 +2518,7 @@ bool QQuickTextInput::canUndo() const /*! \qmlproperty bool QtQuick::TextInput::canRedo + \readonly Returns true if the TextInput is writable and there are \l {undo}{undone} operations that can be redone. @@ -2517,6 +2532,7 @@ bool QQuickTextInput::canRedo() const /*! \qmlproperty real QtQuick::TextInput::contentWidth + \readonly Returns the width of the text, including the width past the width which is covered due to insufficient wrapping if \l wrapMode is set. @@ -2530,6 +2546,7 @@ qreal QQuickTextInput::contentWidth() const /*! \qmlproperty real QtQuick::TextInput::contentHeight + \readonly Returns the height of the text, including the height past the height that is covered if the text does not fit within the set height. @@ -2693,7 +2710,7 @@ void QQuickTextInput::focusOutEvent(QFocusEvent *event) /*! \qmlproperty bool QtQuick::TextInput::inputMethodComposing - + \readonly This property holds whether the TextInput has partial text input from an input method. diff --git a/src/quick/items/qquicktreeview.cpp b/src/quick/items/qquicktreeview.cpp index 2e5c516529..78ac1d7716 100644 --- a/src/quick/items/qquicktreeview.cpp +++ b/src/quick/items/qquicktreeview.cpp @@ -72,7 +72,7 @@ Even if TreeViewDelegate is customizable, there might be situations where you want to render the tree in a different way, or ensure that - the delegate ends up as mininal as possible, e.g for performance reasons. + the delegate ends up as minimal as possible, perhaps for performance reasons. Creating your own delegate from scratch is easy, since TreeView offers a set of properties that can be used to position and render each node in the tree correctly. diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 04dd650c77..5c1c01e8c9 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1102,7 +1102,11 @@ QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent) } /*! - \internal + Constructs a window for displaying a QML scene, whose rendering will + be controlled by the \a control object. + Please refer to QQuickRenderControl's documentation for more information. + + \since 5.4 */ QQuickWindow::QQuickWindow(QQuickRenderControl *control) : QWindow(*(new QQuickWindowPrivate), nullptr) @@ -1497,8 +1501,11 @@ bool QQuickWindow::event(QEvent *e) // or fix QTBUG-90851 so that the event always has points? bool ret = (da && da->event(e)); - // failsafe: never allow any kind of grab to persist after release - if (pe->isEndEvent()) { + // failsafe: never allow any kind of grab to persist after release, + // unless we're waiting for a synth event from QtGui (as with most tablet events) + if (pe->isEndEvent() && !(QQuickDeliveryAgentPrivate::isTabletEvent(pe) && + (qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTabletEvents) || + QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse))) { if (pe->isSinglePointEvent()) { if (static_cast<QSinglePointEvent *>(pe)->buttons() == Qt::NoButton) { auto &firstPt = pe->point(0); @@ -1732,6 +1739,12 @@ QPair<QQuickItem*, QQuickPointerHandler*> QQuickWindowPrivate::findCursorItemAnd } #endif +void QQuickWindowPrivate::clearFocusObject() +{ + if (auto da = deliveryAgentPrivate()) + da->clearFocusObject(); +} + /*! \qmlproperty list<QtObject> Window::data \qmldefault diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 0e69d63239..7dbb337e1f 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -160,6 +160,8 @@ public: QPair<QQuickItem*, QQuickPointerHandler*> findCursorItemAndHandler(QQuickItem *item, const QPointF &scenePos) const; #endif + void clearFocusObject() override; + void dirtyItem(QQuickItem *); void cleanup(QSGNode *); |