diff options
Diffstat (limited to 'src/quick/items/qquickmousearea.cpp')
-rw-r--r-- | src/quick/items/qquickmousearea.cpp | 168 |
1 files changed, 105 insertions, 63 deletions
diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 1d4c707c76..a6edfc4754 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qquickmousearea_p.h" #include "qquickmousearea_p_p.h" @@ -55,12 +19,12 @@ QT_BEGIN_NAMESPACE -DEFINE_BOOL_CONFIG_OPTION(qmlVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING) +DEFINE_BOOL_CONFIG_OPTION(qmlMaVisualTouchDebugging, QML_VISUAL_TOUCH_DEBUGGING) Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace) QQuickMouseAreaPrivate::QQuickMouseAreaPrivate() -: enabled(true), scrollGestureEnabled(true), hovered(false), longPress(false), +: enabled(true), hoverEnabled(false), scrollGestureEnabled(true), hovered(false), longPress(false), moved(false), stealMouse(false), doubleClick(false), preventStealing(false), propagateComposedEvents(false), overThreshold(false), pressAndHoldInterval(-1) @@ -89,7 +53,7 @@ void QQuickMouseAreaPrivate::init() q->setAcceptedMouseButtons(Qt::LeftButton); q->setAcceptTouchEvents(false); // rely on mouse events synthesized from touch q->setFiltersChildMouseEvents(true); - if (qmlVisualTouchDebugging()) { + if (qmlMaVisualTouchDebugging()) { q->setFlag(QQuickItem::ItemHasContents); } } @@ -122,11 +86,13 @@ bool QQuickMouseAreaPrivate::isClickConnected() IS_SIGNAL_CONNECTED(q, QQuickMouseArea, clicked, (QQuickMouseEvent *)); } +#if QT_CONFIG(wheelevent) bool QQuickMouseAreaPrivate::isWheelConnected() { Q_Q(QQuickMouseArea); IS_SIGNAL_CONNECTED(q, QQuickMouseArea, wheel, (QQuickWheelEvent *)); } +#endif void QQuickMouseAreaPrivate::propagate(QQuickMouseEvent* event, PropagateType t) { @@ -151,7 +117,7 @@ bool QQuickMouseAreaPrivate::propagateHelper(QQuickMouseEvent *ev, QQuickItem *i } QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { + for (int ii = children.size() - 1; ii >= 0; --ii) { QQuickItem *child = children.at(ii); if (!child->isVisible() || !child->isEnabled()) continue; @@ -487,6 +453,7 @@ void QQuickMouseArea::setEnabled(bool a) Q_D(QQuickMouseArea); if (a != d->enabled) { d->enabled = a; + setAcceptHoverEvents(a && d->hoverEnabled); emit enabledChanged(); } } @@ -776,7 +743,9 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) QQuickMouseEvent &me = d->quickMouseEvent; me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress, event->flags()); +#if QT_DEPRECATED_SINCE(6, 6) me.setSource(event->source()); +#endif emit mouseXChanged(&me); me.setPosition(d->lastPos); emit mouseYChanged(&me); @@ -801,12 +770,13 @@ void QQuickMouseArea::mouseReleaseEvent(QMouseEvent *event) d->drag->setActive(false); #endif // If we don't accept hover, we need to reset containsMouse. - if (!acceptHoverEvents()) + if (!hoverEnabled()) setHovered(false); QQuickWindow *w = window(); if (w && w->mouseGrabberItem() == this) ungrabMouse(); - setKeepMouseGrab(false); + if (!d->preventStealing) + setKeepMouseGrab(false); } } d->doubleClick = false; @@ -820,12 +790,18 @@ void QQuickMouseArea::mouseDoubleClickEvent(QMouseEvent *event) QQuickMouseEvent &me = d->quickMouseEvent; me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false, event->flags()); +#if QT_DEPRECATED_SINCE(6, 6) me.setSource(event->source()); +#endif me.setAccepted(d->isDoubleClickConnected()); emit this->doubleClicked(&me); if (!me.isAccepted()) d->propagate(&me, QQuickMouseAreaPrivate::DoubleClick); - d->doubleClick = d->isDoubleClickConnected() || me.isAccepted(); + if (d->pressed) + d->doubleClick = d->isDoubleClickConnected() || me.isAccepted(); + + // Do not call the base implementation: we don't want to call event->ignore(). + return; } QQuickItem::mouseDoubleClickEvent(event); } @@ -834,6 +810,14 @@ void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event) { Q_D(QQuickMouseArea); if (!d->enabled && !d->pressed) { + // Note: The fact that MouseArea doesn't update 'containsMouse' when it's disabled, is a + // legacy behavior that is different from how hover events are supposed to work; Hover + // events are always delivered to both enabled and disabled items (when they explicitly + // subscribe for them), to open up for hover effects, like showing tooltips. Because of + // this difference, you cannot use a MouseArea to e.g trigger a tooltop on a parent that + // is disabled. But since MouseArea has always worked this way, it should (probably) stay + // that way to avoid regressions. HoverHandlers do not suffer from this limitation, and + // can therefore be used as a replacement to solve such cases. QQuickItem::hoverEnterEvent(event); } else { d->lastPos = event->position(); @@ -846,6 +830,9 @@ void QQuickMouseArea::hoverEnterEvent(QHoverEvent *event) emit mouseYChanged(&me); me.setPosition(d->lastPos); } + + // A MouseArea should not block hover events + event->ignore(); } void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event) @@ -864,15 +851,21 @@ void QQuickMouseArea::hoverMoveEvent(QHoverEvent *event) me.setPosition(d->lastPos); emit positionChanged(&me); } + + // A MouseArea should not block hover events + event->ignore(); } void QQuickMouseArea::hoverLeaveEvent(QHoverEvent *event) { Q_D(QQuickMouseArea); - if (!d->enabled && !d->pressed) + if (!d->enabled && !d->pressed && !d->hovered) QQuickItem::hoverLeaveEvent(event); else setHovered(false); + + // A MouseArea should not block hover events + event->ignore(); } #if QT_CONFIG(wheelevent) @@ -916,6 +909,7 @@ void QQuickMouseArea::ungrabMouse() emit pressedButtonsChanged(); if (d->hovered && !isUnderMouse()) { + qCDebug(lcHoverTrace) << "losing hover: not under the mouse"; d->hovered = false; emit hoveredChanged(); } @@ -980,6 +974,7 @@ bool QQuickMouseArea::sendMouseEvent(QMouseEvent *event) emit pressedChanged(); emit containsPressChanged(); if (d->hovered) { + qCDebug(lcHoverTrace) << "losing hover: button released"; d->hovered = false; emit hoveredChanged(); } @@ -1026,7 +1021,9 @@ void QQuickMouseArea::timerEvent(QTimerEvent *event) d->longPress = true; QQuickMouseEvent &me = d->quickMouseEvent; me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress, d->lastFlags); +#if QT_DEPRECATED_SINCE(6, 6) me.setSource(Qt::MouseEventSynthesizedByQt); +#endif me.setAccepted(d->isPressAndHoldConnected()); emit pressAndHold(&me); if (!me.isAccepted()) @@ -1042,7 +1039,7 @@ void QQuickMouseArea::geometryChange(const QRectF &newGeometry, const QRectF &ol Q_D(QQuickMouseArea); QQuickItem::geometryChange(newGeometry, oldGeometry); - if (d->lastScenePos.isNull) + if (!d->lastScenePos.isValid()) d->lastScenePos = mapToScene(d->lastPos); else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y()) d->lastPos = mapFromScene(d->lastScenePos); @@ -1052,17 +1049,53 @@ void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value) { Q_D(QQuickMouseArea); switch (change) { + case ItemEnabledHasChanged: + // If MouseArea becomes effectively disabled by disabling a parent + // (for example, onPressed: parent.enabled = false), cancel the pressed state. + if (d->pressed && !d->effectiveEnable) + ungrabMouse(); + break; case ItemVisibleHasChanged: - if (d->effectiveEnable && d->enabled && acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse())) { - if (!d->hovered) { - QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition; - d->lastScenePos = d->window->mapFromGlobal(cursorPos.toPoint()); - d->lastPos = mapFromScene(d->lastScenePos); + if (d->effectiveEnable && d->enabled && hoverEnabled() + && d->hovered != (isVisible() && isUnderMouse())) { + if (d->hovered) { + // If hovered but no longer under the mouse then un-hover. + setHovered(false); + } else { + // If under the mouse but not hovered then hover the QQuickMouseArea if it is + // marked as a hovered item under the windows QQuickDeliveryAgentPrivate instance. + // This is required as this QQuickMouseArea may be masked by another hoverable + // QQuickMouseArea higher up in the scenes z-index ordering. + QPointF globalPos{ QGuiApplicationPrivate::lastCursorPosition.toPoint() }; + QPointF scenePos{ d->window->mapFromGlobal(globalPos) }; + + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(d->window); + QQuickDeliveryAgentPrivate *dap = wd->deliveryAgentPrivate(); + + // If the QQuickDeliveryAgentPrivate has not already found a hovered leaf + // item then attempt to find one. + if (!dap->hoveredLeafItemFound) { + dap->deliverHoverEvent(scenePos, scenePos, Qt::NoModifier, + QDateTime::currentSecsSinceEpoch()); + } + + // Now if the QQuickDeliveryAgentPrivate has found a hovered leaf item check + // that this QQuickMouseArea item was one of the hovered items. + if (dap->hoveredLeafItemFound) { + for (auto hoverItem : dap->hoverItems) { + if (hoverItem.first == this) { + // Found a match so update the hover state. + d->lastScenePos = scenePos; + d->lastPos = mapFromScene(d->lastScenePos); + setHovered(true); + break; + } + } + } } - setHovered(!d->hovered); } if (d->pressed && (!isVisible())) { - // This happens when the mouse area sets itself disabled or hidden + // This happens when the mouse area hides itself // inside the press handler. In that case we should not keep the internal // state as pressed, since we never became the mouse grabber. ungrabMouse(); @@ -1088,15 +1121,18 @@ void QQuickMouseArea::itemChange(ItemChange change, const ItemChangeData &value) */ bool QQuickMouseArea::hoverEnabled() const { - return acceptHoverEvents(); + return d_func()->hoverEnabled; } void QQuickMouseArea::setHoverEnabled(bool h) { - if (h == acceptHoverEvents()) + Q_D(QQuickMouseArea); + if (h == d->hoverEnabled) return; - setAcceptHoverEvents(h); + d->hoverEnabled = h; + setAcceptHoverEvents(h && d->enabled); + emit hoverEnabledChanged(); } @@ -1105,8 +1141,10 @@ void QQuickMouseArea::setHoverEnabled(bool h) \qmlproperty bool QtQuick::MouseArea::containsMouse This property holds whether the mouse is currently inside the mouse area. - \warning If hoverEnabled is false, containsMouse will only be valid + \warning If hoverEnabled is \c false, \c containsMouse will be \c true when the mouse is pressed while the mouse cursor is inside the MouseArea. + But if you set \c {mouse.accepted = false} in an \c onPressed handler, + \c containsMouse will remain \c false because the press was rejected. */ bool QQuickMouseArea::hovered() const { @@ -1118,7 +1156,7 @@ bool QQuickMouseArea::hovered() const \qmlproperty bool QtQuick::MouseArea::pressed This property holds whether any of the \l acceptedButtons are currently pressed. */ -bool QQuickMouseArea::pressed() const +bool QQuickMouseArea::isPressed() const { Q_D(const QQuickMouseArea); return d->pressed; @@ -1204,7 +1242,9 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS if (wasPressed != p) { QQuickMouseEvent &me = d->quickMouseEvent; me.reset(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress, d->lastFlags); +#if QT_DEPRECATED_SINCE(6, 6) me.setSource(source); +#endif if (p) { d->pressed |= button; if (!d->doubleClick) @@ -1216,6 +1256,8 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS if (!me.isAccepted()) { d->pressed = Qt::NoButton; + if (!hoverEnabled()) + setHovered(false); } if (!oldPressed) { @@ -1242,6 +1284,7 @@ bool QQuickMouseArea::setPressed(Qt::MouseButton button, bool p, Qt::MouseEventS return me.isAccepted(); } + Q_UNUSED(source) return false; } @@ -1385,6 +1428,8 @@ void QQuickMouseArea::resetPressAndHoldInterval() If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This enables a parent MouseArea to handle drags, for example, while descendants handle clicks: + \snippet qml/mousearea/mouseareadragfilter.qml dragfilter + \c drag.threshold determines the threshold in pixels of when the drag operation should start. By default this is bound to a platform dependent value. This property was added in Qt Quick 2.2. @@ -1394,9 +1439,6 @@ void QQuickMouseArea::resetPressAndHoldInterval() By default, this property is \c true. This property was added in Qt Quick 2.4 See the \l Drag attached property and \l DropArea if you want to make a drop. - - \snippet qml/mousearea/mouseareadragfilter.qml dragfilter - */ #if QT_CONFIG(quick_draganddrop) @@ -1414,7 +1456,7 @@ QSGNode *QQuickMouseArea::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData Q_UNUSED(data); Q_D(QQuickMouseArea); - if (!qmlVisualTouchDebugging()) + if (!qmlMaVisualTouchDebugging()) return nullptr; QSGInternalRectangleNode *rectangle = static_cast<QSGInternalRectangleNode *>(oldNode); |