aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickitem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickitem.cpp')
-rw-r--r--src/quick/items/qquickitem.cpp2070
1 files changed, 1466 insertions, 604 deletions
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index cab7364919..75037079fb 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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) 2021 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 "qquickitem.h"
@@ -53,6 +17,7 @@
#include <QtGui/qpen.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qstylehints.h>
+#include <QtGui/private/qeventpoint_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qpointingdevice_p.h>
#include <QtGui/qinputmethod.h>
@@ -60,6 +25,7 @@
#include <QtCore/private/qnumeric_p.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtCore/qloggingcategory.h>
+#include <QtCore/private/qduplicatetracker_p.h>
#include <private/qqmlglobal_p.h>
#include <private/qqmlengine_p.h>
@@ -82,6 +48,8 @@
# include <QtGui/qcursor.h>
#endif
+#include <QtCore/qpointer.h>
+
#include <algorithm>
#include <limits>
@@ -94,6 +62,11 @@ Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace)
Q_DECLARE_LOGGING_CATEGORY(lcPtr)
Q_DECLARE_LOGGING_CATEGORY(lcTransient)
Q_LOGGING_CATEGORY(lcHandlerParent, "qt.quick.handler.parent")
+Q_LOGGING_CATEGORY(lcVP, "qt.quick.viewport")
+Q_LOGGING_CATEGORY(lcChangeListeners, "qt.quick.item.changelisteners")
+
+// after 100ms, a mouse/non-mouse cursor conflict is resolved in favor of the mouse handler
+static const quint64 kCursorOverrideTimeout = 100;
void debugFocusTree(QQuickItem *item, QQuickItem *scope = nullptr, int depth = 1)
{
@@ -115,6 +88,14 @@ void debugFocusTree(QQuickItem *item, QQuickItem *scope = nullptr, int depth = 1
}
}
+static void setActiveFocus(QQuickItem *item, Qt::FocusReason reason)
+{
+ QQuickItemPrivate *d = QQuickItemPrivate::get(item);
+ if (d->subFocusItem && d->window && d->flags & QQuickItem::ItemIsFocusScope)
+ QQuickWindowPrivate::get(d->window)->clearFocusInScope(item, d->subFocusItem, reason);
+ item->forceActiveFocus(reason);
+}
+
/*!
\qmltype Transform
\instantiates QQuickTransform
@@ -155,7 +136,7 @@ QQuickTransform::QQuickTransform(QQuickTransformPrivate &dd, QObject *parent)
QQuickTransform::~QQuickTransform()
{
Q_D(QQuickTransform);
- for (int ii = 0; ii < d->items.count(); ++ii) {
+ for (int ii = 0; ii < d->items.size(); ++ii) {
QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii));
p->transforms.removeOne(this);
p->dirty(QQuickItemPrivate::Transform);
@@ -165,7 +146,7 @@ QQuickTransform::~QQuickTransform()
void QQuickTransform::update()
{
Q_D(QQuickTransform);
- for (int ii = 0; ii < d->items.count(); ++ii) {
+ for (int ii = 0; ii < d->items.size(); ++ii) {
QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii));
p->dirty(QQuickItemPrivate::Transform);
}
@@ -179,7 +160,7 @@ QQuickContents::QQuickContents(QQuickItem *item)
QQuickContents::~QQuickContents()
{
QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
+ for (int i = 0; i < children.size(); ++i) {
QQuickItem *child = children.at(i);
QQuickItemPrivate::get(child)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
}
@@ -204,7 +185,7 @@ bool QQuickContents::calcHeight(QQuickItem *changed)
qreal top = std::numeric_limits<qreal>::max();
qreal bottom = -std::numeric_limits<qreal>::max();
QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
+ for (int i = 0; i < children.size(); ++i) {
QQuickItem *child = children.at(i);
qreal y = child->y();
if (y + child->height() > bottom)
@@ -239,7 +220,7 @@ bool QQuickContents::calcWidth(QQuickItem *changed)
qreal left = std::numeric_limits<qreal>::max();
qreal right = -std::numeric_limits<qreal>::max();
QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
+ for (int i = 0; i < children.size(); ++i) {
QQuickItem *child = children.at(i);
qreal x = child->x();
if (x + child->width() > right)
@@ -260,7 +241,7 @@ void QQuickContents::complete()
QQuickItemPrivate::get(m_item)->addItemChangeListener(this, QQuickItemPrivate::Children);
QList<QQuickItem *> children = m_item->childItems();
- for (int i = 0; i < children.count(); ++i) {
+ for (int i = 0; i < children.size(); ++i) {
QQuickItem *child = children.at(i);
QQuickItemPrivate::get(child)->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
//###what about changes to visibility?
@@ -348,10 +329,12 @@ QVariant QQuickItemKeyFilter::inputMethodQuery(Qt::InputMethodQuery query) const
}
#endif // im
-void QQuickItemKeyFilter::shortcutOverride(QKeyEvent *event)
+void QQuickItemKeyFilter::shortcutOverrideEvent(QKeyEvent *event)
{
if (m_next)
- m_next->shortcutOverride(event);
+ m_next->shortcutOverrideEvent(event);
+ else
+ event->ignore();
}
void QQuickItemKeyFilter::componentComplete()
@@ -403,42 +386,42 @@ void QQuickItemKeyFilter::componentComplete()
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::left
+ \qmlattachedproperty Item QtQuick::KeyNavigation::left
This property holds the item to assign focus to
when the left cursor key is pressed.
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::right
+ \qmlattachedproperty Item QtQuick::KeyNavigation::right
This property holds the item to assign focus to
when the right cursor key is pressed.
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::up
+ \qmlattachedproperty Item QtQuick::KeyNavigation::up
This property holds the item to assign focus to
when the up cursor key is pressed.
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::down
+ \qmlattachedproperty Item QtQuick::KeyNavigation::down
This property holds the item to assign focus to
when the down cursor key is pressed.
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::tab
+ \qmlattachedproperty Item QtQuick::KeyNavigation::tab
This property holds the item to assign focus to
when the Tab key is pressed.
*/
/*!
- \qmlproperty Item QtQuick::KeyNavigation::backtab
+ \qmlattachedproperty Item QtQuick::KeyNavigation::backtab
This property holds the item to assign focus to
when the Shift+Tab key combination (Backtab) is pressed.
@@ -590,19 +573,17 @@ void QQuickKeyNavigationAttached::setBacktab(QQuickItem *i)
}
/*!
- \qmlproperty enumeration QtQuick::KeyNavigation::priority
+ \qmlattachedproperty enumeration QtQuick::KeyNavigation::priority
This property determines whether the keys are processed before
or after the attached item's own key handling.
- \list
- \li KeyNavigation.BeforeItem - process the key events before normal
- item key processing. If the event is used for key navigation, it will be accepted and will not
- be passed on to the item.
- \li KeyNavigation.AfterItem (default) - process the key events after normal item key
- handling. If the item accepts the key event it will not be
- handled by the KeyNavigation attached property handler.
- \endlist
+ \value KeyNavigation.BeforeItem process the key events before normal
+ item key processing. If the event is used for key navigation, it will be accepted and
+ will not be passed on to the item.
+ \value KeyNavigation.AfterItem (default) process the key events after normal item key
+ handling. If the item accepts the key event it will not be
+ handled by the KeyNavigation attached property handler.
*/
QQuickKeyNavigationAttached::Priority QQuickKeyNavigationAttached::priority() const
{
@@ -895,20 +876,17 @@ bool QQuickKeysAttached::isConnected(const char *signalName) const
This property determines whether the keys are processed before
or after the attached item's own key handling.
- \list
- \li Keys.BeforeItem (default) - process the key events before normal
- item key processing. If the event is accepted it will not
- be passed on to the item.
- \li Keys.AfterItem - process the key events after normal item key
- handling. If the item accepts the key event it will not be
- handled by the Keys attached property handler.
- \endlist
+ \value Keys.BeforeItem (default) process the key events before normal item key processing.
+ If the event is accepted, it will not be passed on to the item.
+ \value Keys.AfterItem process the key events after normal item key handling. If the item
+ accepts the key event, it will not be handled by the
+ Keys attached property handler.
\sa {Key Handling Priorities}
*/
/*!
- \qmlproperty list<Object> QtQuick::Keys::forwardTo
+ \qmlproperty list<Item> QtQuick::Keys::forwardTo
This property provides a way to forward key presses, key releases, and keyboard input
coming from input methods to other items. This can be useful when you want
@@ -1282,7 +1260,7 @@ void QQuickKeysAttached::componentComplete()
#if QT_CONFIG(im)
Q_D(QQuickKeysAttached);
if (d->item) {
- for (int ii = 0; ii < d->targets.count(); ++ii) {
+ for (int ii = 0; ii < d->targets.size(); ++ii) {
QQuickItem *targetItem = d->targets.at(ii);
if (targetItem && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) {
d->item->setFlag(QQuickItem::ItemAcceptsInputMethod);
@@ -1305,7 +1283,7 @@ void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post)
// first process forwards
if (d->item && d->item->window()) {
d->inPress = true;
- for (int ii = 0; ii < d->targets.count(); ++ii) {
+ for (int ii = 0; ii < d->targets.size(); ++ii) {
QQuickItem *i = d->targets.at(ii);
if (i && i->isVisible()) {
event->accept();
@@ -1349,7 +1327,7 @@ void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post)
if (d->item && d->item->window()) {
d->inRelease = true;
- for (int ii = 0; ii < d->targets.count(); ++ii) {
+ for (int ii = 0; ii < d->targets.size(); ++ii) {
QQuickItem *i = d->targets.at(ii);
if (i && i->isVisible()) {
event->accept();
@@ -1377,7 +1355,7 @@ void QQuickKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post)
Q_D(QQuickKeysAttached);
if (post == m_processPost && d->item && !d->inIM && d->item->window()) {
d->inIM = true;
- for (int ii = 0; ii < d->targets.count(); ++ii) {
+ for (int ii = 0; ii < d->targets.size(); ++ii) {
QQuickItem *targetItem = d->targets.at(ii);
if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) {
QCoreApplication::sendEvent(targetItem, event);
@@ -1397,7 +1375,7 @@ QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const
{
Q_D(const QQuickKeysAttached);
if (d->item) {
- for (int ii = 0; ii < d->targets.count(); ++ii) {
+ for (int ii = 0; ii < d->targets.size(); ++ii) {
QQuickItem *i = d->targets.at(ii);
if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod) && i == d->imeItem) {
//### how robust is i == d->imeItem check?
@@ -1412,7 +1390,7 @@ QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const
}
#endif // im
-void QQuickKeysAttached::shortcutOverride(QKeyEvent *event)
+void QQuickKeysAttached::shortcutOverrideEvent(QKeyEvent *event)
{
Q_D(QQuickKeysAttached);
QQuickKeyEvent &keyEvent = d->theKeyEvent;
@@ -1584,7 +1562,7 @@ void QQuickItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit)
if (isMirrorImplicit)
setLayoutMirror(inherit ? inheritedLayoutMirror : false);
- for (int i = 0; i < childItems.count(); ++i) {
+ for (int i = 0; i < childItems.size(); ++i) {
if (QQuickItem *child = qmlobject_cast<QQuickItem *>(childItems.at(i))) {
QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent);
@@ -1727,6 +1705,53 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
}
}
+
+bool QQuickItemPrivate::setFocusIfNeeded(QEvent::Type eventType)
+{
+ Q_Q(QQuickItem);
+ const bool setFocusOnRelease = QGuiApplication::styleHints()->setFocusOnTouchRelease();
+ Qt::FocusPolicy policy = Qt::ClickFocus;
+
+ switch (eventType) {
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::TouchBegin:
+ if (setFocusOnRelease)
+ return false;
+ break;
+ case QEvent::MouseButtonRelease:
+ case QEvent::TouchEnd:
+ if (!setFocusOnRelease)
+ return false;
+ break;
+ case QEvent::Wheel:
+ policy = Qt::WheelFocus;
+ break;
+ default:
+ break;
+ }
+
+ if ((focusPolicy & policy) == policy) {
+ setActiveFocus(q, Qt::MouseFocusReason);
+ return true;
+ }
+
+ return false;
+}
+
+Qt::FocusReason QQuickItemPrivate::lastFocusChangeReason() const
+{
+ return static_cast<Qt::FocusReason>(focusReason);
+}
+
+void QQuickItemPrivate::setLastFocusChangeReason(Qt::FocusReason reason)
+{
+ if (focusReason == reason)
+ return;
+
+ focusReason = reason;
+}
+
/*!
\class QQuickItem
\brief The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
@@ -2005,7 +2030,7 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
\qml
Item {
id: layerRoot
- layer.enabled = true
+ layer.enabled: true
layer.effect: ShaderEffect {
fragmentShader: "effect.frag.qsb"
}
@@ -2060,6 +2085,9 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
\value ItemHasContents Indicates the item has visual content and should be
rendered by the scene graph.
\value ItemAcceptsDrops Indicates the item accepts drag and drop events.
+ \value ItemIsViewport Indicates that the item defines a viewport for its children.
+ \value ItemObservesViewport Indicates that the item wishes to know the
+ viewport bounds when any ancestor has the ItemIsViewport flag set.
\sa setFlag(), setFlags(), flags()
*/
@@ -2108,7 +2136,7 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
/*!
\class QQuickItem::ItemChangeData
\inmodule QtQuick
- \brief Adds supplimentary information to the QQuickItem::itemChange()
+ \brief Adds supplementary information to the QQuickItem::itemChange()
function.
The meaning of each member of this class is defined by the change type.
@@ -2138,25 +2166,31 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
/*!
\variable QQuickItem::ItemChangeData::realValue
- Contains supplimentary information to the QQuickItem::itemChange() function.
+ The numeric value that has changed: \l {QQuickItem::opacity()}{opacity},
+ \l {QQuickItem::rotation()}{rotation}, or
+ \l {QScreen::devicePixelRatio}{device pixel ratio}.
\sa QQuickItem::ItemChange
*/
/*!
\variable QQuickItem::ItemChangeData::boolValue
- Contains supplimentary information to the QQuickItem::itemChange() function.
+ The boolean value that has changed: \l {QQuickItem::isVisible()}{visible},
+ \l {QQuickItem::isEnabled()}{enabled}, \l {QQuickItem::hasActiveFocus()}{activeFocus},
+ or \l {QQuickItem::antialiasing()}{antialiasing}.
\sa QQuickItem::ItemChange
*/
/*!
\variable QQuickItem::ItemChangeData::item
- Contains supplimentary information to the QQuickItem::itemChange() function.
+ The item that has been added or removed as a \l{QQuickItem::childItems()}{child},
+ or the new \l{QQuickItem::parentItem()}{parent}.
\sa QQuickItem::ItemChange
*/
/*!
\variable QQuickItem::ItemChangeData::window
- Contains supplimentary information to the QQuickItem::itemChange() function.
+ The \l{QQuickWindow}{window} in which the item has been shown, or \c nullptr
+ if the item has been removed from a window.
\sa QQuickItem::ItemChange
*/
@@ -2229,6 +2263,11 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
*/
/*!
+ \fn void QQuickItem::focusPolicyChanged(Qt::FocusPolicy)
+ \internal
+*/
+
+/*!
\fn void QQuickItem::activeFocusOnTabChanged(bool)
\internal
*/
@@ -2333,6 +2372,7 @@ QQuickItem::QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent)
QQuickItem::~QQuickItem()
{
Q_D(QQuickItem);
+ d->inDestructor = true;
if (d->windowRefCount > 1)
d->windowRefCount = 1; // Make sure window is set to null in next call to derefWindow().
@@ -2341,42 +2381,33 @@ QQuickItem::~QQuickItem()
else if (d->window)
d->derefWindow();
- // XXX todo - optimize
- while (!d->childItems.isEmpty())
- d->childItems.constFirst()->setParentItem(nullptr);
-
- if (!d->changeListeners.isEmpty()) {
- const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
- if (anchor)
- anchor->clearItem(this);
- }
+ for (QQuickItem *child : std::as_const(d->childItems))
+ child->setParentItem(nullptr);
+ d->childItems.clear();
- /*
+ d->notifyChangeListeners(QQuickItemPrivate::AllChanges, [this](const QQuickItemPrivate::ChangeListener &change){
+ QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
+ if (anchor)
+ anchor->clearItem(this);
+ });
+ /*
update item anchors that depended on us unless they are our child (and will also be destroyed),
or our sibling, and our parent is also being destroyed.
*/
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
- if (anchor && anchor->item && anchor->item->parentItem() && anchor->item->parentItem() != this)
- anchor->update();
- }
-
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Destroyed)
- change.listener->itemDestroyed(this);
- }
-
- d->changeListeners.clear();
- }
+ d->notifyChangeListeners(QQuickItemPrivate::AllChanges, [this](const QQuickItemPrivate::ChangeListener &change){
+ QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
+ if (anchor && anchor->item && anchor->item->parentItem() && anchor->item->parentItem() != this)
+ anchor->update();
+ });
+ d->notifyChangeListeners(QQuickItemPrivate::Destroyed, &QQuickItemChangeListener::itemDestroyed, this);
+ d->changeListeners.clear();
/*
Remove any references our transforms have to us, in case they try to
remove themselves from our list of transforms when that list has already
been destroyed after ~QQuickItem() has run.
*/
- for (int ii = 0; ii < d->transforms.count(); ++ii) {
+ for (int ii = 0; ii < d->transforms.size(); ++ii) {
QQuickTransform *t = d->transforms.at(ii);
QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t);
tp->items.removeOne(this);
@@ -2391,6 +2422,8 @@ QQuickItem::~QQuickItem()
delete d->_anchors; d->_anchors = nullptr;
delete d->_stateGroup; d->_stateGroup = nullptr;
+
+ d->isQuickItem = false;
}
/*!
@@ -2405,7 +2438,7 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
return true;
#if QT_CONFIG(accessibility)
- QAccessible::Role role = QQuickItemPrivate::get(item)->accessibleRole();
+ QAccessible::Role role = QQuickItemPrivate::get(item)->effectiveAccessibleRole();
if (role == QAccessible::EditableText || role == QAccessible::Table || role == QAccessible::List) {
return true;
} else if (role == QAccessible::ComboBox || role == QAccessible::SpinBox) {
@@ -2437,11 +2470,27 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
*/
bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward)
{
- QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward);
+ QQuickWindow *window = item->window();
+ const bool wrap = !window || window->isTopLevel();
+
+ QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward, wrap);
if (next == item)
return false;
+ if (!wrap && !next) {
+ // Focus chain wrapped and we are not top-level window
+ // Give focus to parent window
+ Q_ASSERT(window);
+ Q_ASSERT(window->parent());
+
+ qt_window_private(window->parent())->setFocusToTarget(
+ forward ? QWindowPrivate::FocusTarget::Next
+ : QWindowPrivate::FocusTarget::Prev);
+ window->parent()->requestActivate();
+ return true;
+ }
+
next->forceActiveFocus(forward ? Qt::TabFocusReason : Qt::BacktabFocusReason);
return true;
@@ -2454,7 +2503,7 @@ QQuickItem *QQuickItemPrivate::nextTabChildItem(const QQuickItem *item, int star
return nullptr;
}
const QList<QQuickItem *> &children = item->childItems();
- const int count = children.count();
+ const int count = children.size();
if (start < 0 || start >= count) {
qWarning() << "QQuickItemPrivate::nextTabChildItem: Start index value out of range for item" << item;
return nullptr;
@@ -2475,7 +2524,7 @@ QQuickItem *QQuickItemPrivate::prevTabChildItem(const QQuickItem *item, int star
return nullptr;
}
const QList<QQuickItem *> &children = item->childItems();
- const int count = children.count();
+ const int count = children.size();
if (start == -1)
start = count - 1;
if (start < 0 || start >= count) {
@@ -2491,7 +2540,7 @@ QQuickItem *QQuickItemPrivate::prevTabChildItem(const QQuickItem *item, int star
return nullptr;
}
-QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward)
+QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward, bool wrap)
{
Q_ASSERT(item);
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: item:" << item << ", forward:" << forward;
@@ -2533,6 +2582,7 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
QQuickItem *current = item;
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem;
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem;
+ QDuplicateTracker<QQuickItem *> cycleDetector;
do {
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: current:" << current;
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: from:" << from;
@@ -2584,6 +2634,14 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
}
current = parent;
} else if (hasChildren) {
+ if (!wrap && (forward || firstFromItem != from)) {
+ qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain:"
+ << "Focus chain about to wrap.";
+ // If focus chain wraps, we should give the parent window
+ // a chance to get focus, so we should stop here
+ return nullptr;
+ }
+
// Wrap around after checking all items forward
if (forward) {
current = firstChild;
@@ -2599,7 +2657,10 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
// traversed all of the chain (by compare the [current] item with [startItem])
// Since the [startItem] might be promoted to its parent if it is invisible,
// we still have to check [current] item with original start item
- if ((current == startItem || current == originalStartItem) && from == firstFromItem) {
+ // We might also run into a cycle before we reach firstFromItem again
+ // but note that we have to ignore current if we are meant to skip it
+ if (((current == startItem || current == originalStartItem) && from == firstFromItem) ||
+ (!skip && cycleDetector.hasSeen(current))) {
// wrapped around, avoid endless loops
if (item == contentItem) {
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return contentItem";
@@ -2641,6 +2702,12 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
\e {QObject parent}. An item's visual parent may not necessarily be the
same as its object parent. See \l {Concepts - Visual Parent in Qt Quick}
for more details.
+
+ \note The notification signal for this property gets emitted during destruction
+ of the visual parent. C++ signal handlers cannot assume that items in the
+ visual parent hierarchy are still fully constructed. Use \l qobject_cast to
+ verify that items in the parent hierarchy can be used safely as the expected
+ type.
*/
QQuickItem *QQuickItem::parentItem() const
{
@@ -2697,9 +2764,8 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
const bool wasVisible = isVisible();
op->removeChild(this);
- if (wasVisible) {
+ if (wasVisible && !op->inDestructor)
emit oldParentItem->visibleChildrenChanged();
- }
} else if (d->window) {
QQuickWindowPrivate::get(d->window)->parentlessItems.remove(this);
}
@@ -2777,8 +2843,9 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
d->itemChange(ItemParentHasChanged, d->parentItem);
- emit parentChanged(d->parentItem);
- if (isVisible() && d->parentItem)
+ if (!d->inDestructor)
+ emit parentChanged(d->parentItem);
+ if (isVisible() && d->parentItem && !QQuickItemPrivate::get(d->parentItem)->inDestructor)
emit d->parentItem->visibleChildrenChanged();
}
@@ -2820,10 +2887,10 @@ void QQuickItem::stackBefore(const QQuickItem *sibling)
parentPrivate->childItems.move(myIndex, myIndex < siblingIndex ? siblingIndex - 1 : siblingIndex);
- parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
parentPrivate->markSortedChildrenDirty(this);
+ parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
- for (int ii = qMin(siblingIndex, myIndex); ii < parentPrivate->childItems.count(); ++ii)
+ for (int ii = qMin(siblingIndex, myIndex); ii < parentPrivate->childItems.size(); ++ii)
QQuickItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
}
@@ -2865,10 +2932,10 @@ void QQuickItem::stackAfter(const QQuickItem *sibling)
parentPrivate->childItems.move(myIndex, myIndex > siblingIndex ? siblingIndex + 1 : siblingIndex);
- parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
parentPrivate->markSortedChildrenDirty(this);
+ parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
- for (int ii = qMin(myIndex, siblingIndex + 1); ii < parentPrivate->childItems.count(); ++ii)
+ for (int ii = qMin(myIndex, siblingIndex + 1); ii < parentPrivate->childItems.size(); ++ii)
QQuickItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
}
@@ -2902,7 +2969,7 @@ QList<QQuickItem *> QQuickItemPrivate::paintOrderChildItems() const
// If none of the items have set Z then the paint order list is the same as
// the childItems list. This is by far the most common case.
bool haveZ = false;
- for (int i = 0; i < childItems.count(); ++i) {
+ for (int i = 0; i < childItems.size(); ++i) {
if (QQuickItemPrivate::get(childItems.at(i))->z() != 0.) {
haveZ = true;
break;
@@ -2953,9 +3020,12 @@ void QQuickItemPrivate::removeChild(QQuickItem *child)
Q_Q(QQuickItem);
Q_ASSERT(child);
- Q_ASSERT(childItems.contains(child));
- childItems.removeOne(child);
- Q_ASSERT(!childItems.contains(child));
+ if (!inDestructor) {
+ // if we are getting destroyed, then the destructor will clear the list
+ Q_ASSERT(childItems.contains(child));
+ childItems.removeOne(child);
+ Q_ASSERT(!childItems.contains(child));
+ }
QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
@@ -2969,12 +3039,15 @@ void QQuickItemPrivate::removeChild(QQuickItem *child)
setHasHoverInChild(false);
childPrivate->recursiveRefFromEffectItem(-extra.value().recursiveEffectRefCount);
- markSortedChildrenDirty(child);
- dirty(QQuickItemPrivate::ChildrenChanged);
+ if (!inDestructor) {
+ markSortedChildrenDirty(child);
+ dirty(QQuickItemPrivate::ChildrenChanged);
+ }
itemChange(QQuickItem::ItemChildRemovedChange, child);
- emit q->childrenChanged();
+ if (!inDestructor)
+ emit q->childrenChanged();
}
void QQuickItemPrivate::refWindow(QQuickWindow *c)
@@ -3006,7 +3079,7 @@ void QQuickItemPrivate::refWindow(QQuickWindow *c)
if (!parentItem)
QQuickWindowPrivate::get(window)->parentlessItems.insert(q);
- for (int ii = 0; ii < childItems.count(); ++ii) {
+ for (int ii = 0; ii < childItems.size(); ++ii) {
QQuickItem *child = childItems.at(ii);
QQuickItemPrivate::get(child)->refWindow(c);
}
@@ -3057,9 +3130,9 @@ void QQuickItemPrivate::derefWindow()
paintNode = nullptr;
- for (int ii = 0; ii < childItems.count(); ++ii) {
- QQuickItem *child = childItems.at(ii);
- QQuickItemPrivate::get(child)->derefWindow();
+ for (int ii = 0; ii < childItems.size(); ++ii) {
+ if (QQuickItem *child = childItems.at(ii))
+ QQuickItemPrivate::get(child)->derefWindow();
}
dirty(Window);
@@ -3071,7 +3144,7 @@ void QQuickItemPrivate::derefWindow()
/*!
-Returns a transform that maps points from window space into item space.
+ Returns a transform that maps points from window space into item space.
*/
QTransform QQuickItemPrivate::windowToItemTransform() const
{
@@ -3080,37 +3153,47 @@ QTransform QQuickItemPrivate::windowToItemTransform() const
}
/*!
-Returns a transform that maps points from item space into window space.
+ Returns a transform that maps points from item space into window space.
*/
QTransform QQuickItemPrivate::itemToWindowTransform() const
{
- // XXX todo
- QTransform rv = parentItem?QQuickItemPrivate::get(parentItem)->itemToWindowTransform():QTransform();
- itemToParentTransform(rv);
+ // item's parent must not be itself, otherwise calling itemToWindowTransform() on it is infinite recursion
+ Q_ASSERT(!parentItem || QQuickItemPrivate::get(parentItem) != this);
+ QTransform rv = parentItem ? QQuickItemPrivate::get(parentItem)->itemToWindowTransform() : QTransform();
+ itemToParentTransform(&rv);
return rv;
}
/*!
-Motifies \a t with this items local transform relative to its parent.
+ Modifies \a t with this item's local transform relative to its parent.
*/
-void QQuickItemPrivate::itemToParentTransform(QTransform &t) const
+void QQuickItemPrivate::itemToParentTransform(QTransform *t) const
{
+ /* Read the current x and y values. As this is an internal method,
+ we don't care about it being usable in bindings. Instead, we
+ care about performance here, and thus we read the value with
+ valueBypassingBindings. This avoids any checks whether we are
+ in a binding (which sholdn't be too expensive, but can add up).
+ */
+
+ qreal x = this->x.valueBypassingBindings();
+ qreal y = this->y.valueBypassingBindings();
if (x || y)
- t.translate(x, y);
+ t->translate(x, y);
if (!transforms.isEmpty()) {
- QMatrix4x4 m(t);
- for (int ii = transforms.count() - 1; ii >= 0; --ii)
+ QMatrix4x4 m(*t);
+ for (int ii = transforms.size() - 1; ii >= 0; --ii)
transforms.at(ii)->applyTo(&m);
- t = m.toTransform();
+ *t = m.toTransform();
}
if (scale() != 1. || rotation() != 0.) {
QPointF tp = computeTransformOrigin();
- t.translate(tp.x(), tp.y());
- t.scale(scale(), scale());
- t.rotate(rotation());
- t.translate(-tp.x(), -tp.y());
+ t->translate(tp.x(), tp.y());
+ t->scale(scale(), scale());
+ t->rotate(rotation());
+ t->translate(-tp.x(), -tp.y());
}
}
@@ -3161,8 +3244,8 @@ QQuickItemPrivate::QQuickItemPrivate()
: _anchors(nullptr)
, _stateGroup(nullptr)
, flags(0)
- , widthValid(false)
- , heightValid(false)
+ , widthValidFlag(false)
+ , heightValidFlag(false)
, componentComplete(true)
, keepMouse(false)
, keepTouch(false)
@@ -3197,6 +3280,10 @@ QQuickItemPrivate::QQuickItemPrivate()
, touchEnabled(false)
, hasCursorHandler(false)
, maybeHasSubsceneDeliveryAgent(true)
+ , subtreeTransformChangedEnabled(true)
+ , inDestructor(false)
+ , focusReason(Qt::OtherFocusReason)
+ , focusPolicy(Qt::NoFocus)
, dirtyAttributes(0)
, nextDirtyItem(nullptr)
, prevDirtyItem(nullptr)
@@ -3214,6 +3301,7 @@ QQuickItemPrivate::QQuickItemPrivate()
, baselineOffset(0)
, itemNodeInstance(nullptr)
, paintNode(nullptr)
+ , szPolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Fixed)
{
}
@@ -3227,6 +3315,8 @@ void QQuickItemPrivate::init(QQuickItem *parent)
{
Q_Q(QQuickItem);
+ isQuickItem = true;
+
baselineOffset = 0.0;
if (parent) {
@@ -3236,6 +3326,17 @@ void QQuickItemPrivate::init(QQuickItem *parent)
}
}
+QLayoutPolicy QQuickItemPrivate::sizePolicy() const
+{
+ return szPolicy;
+}
+
+void QQuickItemPrivate::setSizePolicy(const QLayoutPolicy::Policy& horizontalPolicy, const QLayoutPolicy::Policy& verticalPolicy)
+{
+ szPolicy.setHorizontalPolicy(horizontalPolicy);
+ szPolicy.setVerticalPolicy(verticalPolicy);
+}
+
void QQuickItemPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
{
if (!o)
@@ -3245,43 +3346,21 @@ void QQuickItemPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
if (QQuickItem *item = qmlobject_cast<QQuickItem *>(o)) {
item->setParentItem(that);
- } else {
- if (o->inherits("QGraphicsItem"))
- qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className());
- else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
- if (pointerHandler->parent() != that) {
- qCDebug(lcHandlerParent) << "reparenting handler" << pointerHandler << ":" << pointerHandler->parent() << "->" << that;
- pointerHandler->setParent(that);
- }
- QQuickItemPrivate::get(that)->addPointerHandler(pointerHandler);
- } else {
- QQuickWindow *thisWindow = qmlobject_cast<QQuickWindow *>(o);
- QQuickItem *item = that;
- QQuickWindow *itemWindow = that->window();
- while (!itemWindow && item && item->parentItem()) {
- item = item->parentItem();
- itemWindow = item->window();
- }
-
- if (thisWindow) {
- if (itemWindow) {
- qCDebug(lcTransient) << thisWindow << "is transient for" << itemWindow;
- thisWindow->setTransientParent(itemWindow);
- } else {
- QObject::connect(item, SIGNAL(windowChanged(QQuickWindow*)),
- thisWindow, SLOT(setTransientParent_helper(QQuickWindow*)));
- }
- }
- o->setParent(that);
+ } else if (QQuickPointerHandler *pointerHandler = qmlobject_cast<QQuickPointerHandler *>(o)) {
+ if (pointerHandler->parent() != that) {
+ qCDebug(lcHandlerParent) << "reparenting handler" << pointerHandler << ":" << pointerHandler->parent() << "->" << that;
+ pointerHandler->setParent(that);
}
-
+ QQuickItemPrivate::get(that)->addPointerHandler(pointerHandler);
+ } else {
+ o->setParent(that);
resources_append(prop, o);
}
}
/*!
- \qmlproperty list<Object> QtQuick::Item::data
- \default
+ \qmlproperty list<QtObject> QtQuick::Item::data
+ \qmldefault
The data property allows you to freely mix visual children and resources
in an item. If you assign a visual item to the data list it becomes
@@ -3351,6 +3430,22 @@ void QQuickItemPrivate::data_clear(QQmlListProperty<QObject> *property)
children_clear(&childrenProperty);
}
+void QQuickItemPrivate::data_removeLast(QQmlListProperty<QObject> *property)
+{
+ QQuickItem *item = static_cast<QQuickItem*>(property->object);
+ QQuickItemPrivate *privateItem = QQuickItemPrivate::get(item);
+
+ QQmlListProperty<QQuickItem> childrenProperty = privateItem->children();
+ if (children_count(&childrenProperty) > 0) {
+ children_removeLast(&childrenProperty);
+ return;
+ }
+
+ QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
+ if (resources_count(&resourcesProperty) > 0)
+ resources_removeLast(&resourcesProperty);
+}
+
QObject *QQuickItemPrivate::resources_at(QQmlListProperty<QObject> *prop, qsizetype index)
{
QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
@@ -3371,7 +3466,7 @@ void QQuickItemPrivate::resources_append(QQmlListProperty<QObject> *prop, QObjec
qsizetype QQuickItemPrivate::resources_count(QQmlListProperty<QObject> *prop)
{
QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
- return quickItemPrivate->extra.isAllocated() ? quickItemPrivate->extra->resourcesList.count() : 0;
+ return quickItemPrivate->extra.isAllocated() ? quickItemPrivate->extra->resourcesList.size() : 0;
}
void QQuickItemPrivate::resources_clear(QQmlListProperty<QObject> *prop)
@@ -3379,7 +3474,7 @@ void QQuickItemPrivate::resources_clear(QQmlListProperty<QObject> *prop)
QQuickItem *quickItem = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(quickItem);
if (quickItemPrivate->extra.isAllocated()) {//If extra is not allocated resources is empty.
- for (QObject *object : qAsConst(quickItemPrivate->extra->resourcesList)) {
+ for (QObject *object : std::as_const(quickItemPrivate->extra->resourcesList)) {
qmlobject_disconnect(object, QObject, SIGNAL(destroyed(QObject*)),
quickItem, QQuickItem, SLOT(_q_resourceObjectDeleted(QObject*)));
}
@@ -3387,10 +3482,25 @@ void QQuickItemPrivate::resources_clear(QQmlListProperty<QObject> *prop)
}
}
+void QQuickItemPrivate::resources_removeLast(QQmlListProperty<QObject> *prop)
+{
+ QQuickItem *quickItem = static_cast<QQuickItem *>(prop->object);
+ QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(quickItem);
+ if (quickItemPrivate->extra.isAllocated()) {//If extra is not allocated resources is empty.
+ QList<QObject *> *resources = &quickItemPrivate->extra->resourcesList;
+ if (resources->isEmpty())
+ return;
+
+ qmlobject_disconnect(resources->last(), QObject, SIGNAL(destroyed(QObject*)),
+ quickItem, QQuickItem, SLOT(_q_resourceObjectDeleted(QObject*)));
+ resources->removeLast();
+ }
+}
+
QQuickItem *QQuickItemPrivate::children_at(QQmlListProperty<QQuickItem> *prop, qsizetype index)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
- if (index >= p->childItems.count() || index < 0)
+ if (index >= p->childItems.size() || index < 0)
return nullptr;
else
return p->childItems.at(index);
@@ -3411,7 +3521,7 @@ void QQuickItemPrivate::children_append(QQmlListProperty<QQuickItem> *prop, QQui
qsizetype QQuickItemPrivate::children_count(QQmlListProperty<QQuickItem> *prop)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
- return p->childItems.count();
+ return p->childItems.size();
}
void QQuickItemPrivate::children_clear(QQmlListProperty<QQuickItem> *prop)
@@ -3422,11 +3532,19 @@ void QQuickItemPrivate::children_clear(QQmlListProperty<QQuickItem> *prop)
p->childItems.at(0)->setParentItem(nullptr);
}
+void QQuickItemPrivate::children_removeLast(QQmlListProperty<QQuickItem> *prop)
+{
+ QQuickItem *that = static_cast<QQuickItem *>(prop->object);
+ QQuickItemPrivate *p = QQuickItemPrivate::get(that);
+ if (!p->childItems.isEmpty())
+ p->childItems.last()->setParentItem(nullptr);
+}
+
qsizetype QQuickItemPrivate::visibleChildren_count(QQmlListProperty<QQuickItem> *prop)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
qsizetype visibleCount = 0;
- qsizetype c = p->childItems.count();
+ qsizetype c = p->childItems.size();
while (c--) {
if (p->childItems.at(c)->isVisible()) visibleCount++;
}
@@ -3437,7 +3555,7 @@ qsizetype QQuickItemPrivate::visibleChildren_count(QQmlListProperty<QQuickItem>
QQuickItem *QQuickItemPrivate::visibleChildren_at(QQmlListProperty<QQuickItem> *prop, qsizetype index)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
- const qsizetype childCount = p->childItems.count();
+ const qsizetype childCount = p->childItems.size();
if (index >= childCount || index < 0)
return nullptr;
@@ -3454,7 +3572,7 @@ qsizetype QQuickItemPrivate::transform_count(QQmlListProperty<QQuickTransform> *
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *p = QQuickItemPrivate::get(that);
- return p->transforms.count();
+ return p->transforms.size();
}
void QQuickTransform::appendToItem(QQuickItem *item)
@@ -3509,7 +3627,7 @@ QQuickTransform *QQuickItemPrivate::transform_at(QQmlListProperty<QQuickTransfor
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *p = QQuickItemPrivate::get(that);
- if (idx < 0 || idx >= p->transforms.count())
+ if (idx < 0 || idx >= p->transforms.size())
return nullptr;
else
return p->transforms.at(idx);
@@ -3520,7 +3638,7 @@ void QQuickItemPrivate::transform_clear(QQmlListProperty<QQuickTransform> *prop)
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *p = QQuickItemPrivate::get(that);
- for (qsizetype ii = 0; ii < p->transforms.count(); ++ii) {
+ for (qsizetype ii = 0; ii < p->transforms.size(); ++ii) {
QQuickTransform *t = p->transforms.at(ii);
QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t);
tp->items.removeOne(that);
@@ -3645,22 +3763,21 @@ QQuickAnchors *QQuickItemPrivate::anchors() const
void QQuickItemPrivate::siblingOrderChanged()
{
Q_Q(QQuickItem);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::SiblingOrder) {
- change.listener->itemSiblingOrderChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::SiblingOrder, &QQuickItemChangeListener::itemSiblingOrderChanged, q);
}
QQmlListProperty<QObject> QQuickItemPrivate::data()
{
- return QQmlListProperty<QObject>(q_func(), nullptr, QQuickItemPrivate::data_append,
- QQuickItemPrivate::data_count,
- QQuickItemPrivate::data_at,
- QQuickItemPrivate::data_clear);
+ // Do not synthesize replace().
+ // It would be extremely expensive and wouldn't work with most methods.
+ QQmlListProperty<QObject> result;
+ result.object = q_func();
+ result.append = QQuickItemPrivate::data_append;
+ result.count = QQuickItemPrivate::data_count;
+ result.at = QQuickItemPrivate::data_at;
+ result.clear = QQuickItemPrivate::data_clear;
+ result.removeLast = QQuickItemPrivate::data_removeLast;
+ return result;
}
/*!
@@ -3720,6 +3837,9 @@ QList<QQuickItem *> QQuickItem::childItems() const
If clipping is enabled, an item will clip its own painting, as well
as the painting of its children, to its bounding rectangle.
+
+ \note Clipping can affect rendering performance. See \l {Clipping} for more
+ information.
*/
/*!
\property QQuickItem::clip
@@ -3732,6 +3852,12 @@ QList<QQuickItem *> QQuickItem::childItems() const
\note Clipping can affect rendering performance. See \l {Clipping} for more
information.
+
+ \note For the sake of QML, setting clip to \c true also sets the
+ \l ItemIsViewport flag, which sometimes acts as an optimization: child items
+ that have the \l ItemObservesViewport flag may forego creating scene graph nodes
+ that fall outside the viewport. But the \c ItemIsViewport flag can also be set
+ independently.
*/
bool QQuickItem::clip() const
{
@@ -3744,6 +3870,10 @@ void QQuickItem::setClip(bool c)
return;
setFlag(ItemClipsChildrenToShape, c);
+ if (c)
+ setFlag(ItemIsViewport);
+ else if (!(inherits("QQuickFlickable") || inherits("QQuickRootItem")))
+ setFlag(ItemIsViewport, false);
emit clipChanged(c);
}
@@ -3770,24 +3900,21 @@ void QQuickItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeom
change.setWidthChange(newGeometry.width() != oldGeometry.width());
change.setHeightChange(newGeometry.height() != oldGeometry.height());
- if (!d->changeListeners.isEmpty()) {
- const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &listener : listeners) {
- if (listener.types & QQuickItemPrivate::Geometry) {
- if (change.matches(listener.gTypes))
- listener.listener->itemGeometryChanged(this, change, oldGeometry);
- }
- }
- }
+ d->notifyChangeListeners(QQuickItemPrivate::Geometry, [&](const QQuickItemPrivate::ChangeListener &listener){
+ if (change.matches(listener.gTypes))
+ listener.listener->itemGeometryChanged(this, change, oldGeometry);
+ });
+ // The notify method takes care of emitting the signal, and also notifies any
+ // property observers.
if (change.xChange())
- emit xChanged();
+ d->x.notify();
if (change.yChange())
- emit yChanged();
+ d->y.notify();
if (change.widthChange())
- emit widthChanged();
+ d->width.notify();
if (change.heightChange())
- emit heightChanged();
+ d->height.notify();
#if QT_CONFIG(accessibility)
if (QAccessible::isActive()) {
if (QObject *acc = QQuickAccessibleAttached::findAccessible(this)) {
@@ -3897,14 +4024,30 @@ QSGTransformNode *QQuickItemPrivate::createTransformNode()
item. When the scene graph is ready to render this item, it calls
updatePolish() to do any item layout as required before it renders the
next frame.
+
+ \sa ensurePolished()
*/
void QQuickItem::updatePolish()
{
}
+#define PRINT_LISTENERS() \
+do { \
+ qDebug().nospace() << q_func() << " (" << this \
+ << ") now has the following listeners:"; \
+ for (const auto &listener : std::as_const(changeListeners)) { \
+ const auto objectPrivate = dynamic_cast<QObjectPrivate*>(listener.listener); \
+ qDebug().nospace() << "- " << listener << " (QObject: " << (objectPrivate ? objectPrivate->q_func() : nullptr) << ")"; \
+ } \
+} \
+while (false)
+
void QQuickItemPrivate::addItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types)
{
changeListeners.append(ChangeListener(listener, types));
+
+ if (lcChangeListeners().isDebugEnabled())
+ PRINT_LISTENERS();
}
void QQuickItemPrivate::updateOrAddItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types)
@@ -3915,12 +4058,18 @@ void QQuickItemPrivate::updateOrAddItemChangeListener(QQuickItemChangeListener *
changeListeners[index].types = changeListener.types;
else
changeListeners.append(changeListener);
+
+ if (lcChangeListeners().isDebugEnabled())
+ PRINT_LISTENERS();
}
void QQuickItemPrivate::removeItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types)
{
ChangeListener change(listener, types);
changeListeners.removeOne(change);
+
+ if (lcChangeListeners().isDebugEnabled())
+ PRINT_LISTENERS();
}
void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener,
@@ -3932,6 +4081,9 @@ void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListen
changeListeners[index].gTypes = change.gTypes; //we may have different GeometryChangeTypes
else
changeListeners.append(change);
+
+ if (lcChangeListeners().isDebugEnabled())
+ PRINT_LISTENERS();
}
void QQuickItemPrivate::updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener,
@@ -3945,6 +4097,9 @@ void QQuickItemPrivate::updateOrRemoveGeometryChangeListener(QQuickItemChangeLis
if (index > -1)
changeListeners[index].gTypes = change.gTypes; //we may have different GeometryChangeTypes
}
+
+ if (lcChangeListeners().isDebugEnabled())
+ PRINT_LISTENERS();
}
/*!
@@ -3987,7 +4142,7 @@ void QQuickItem::inputMethodEvent(QInputMethodEvent *event)
/*!
This event handler can be reimplemented in a subclass to receive focus-in
- events for an item. The event information is provided by the \c event
+ events for an item. The event information is provided by the \a event
parameter.
\input item.qdocinc accepting-events
@@ -3995,8 +4150,9 @@ void QQuickItem::inputMethodEvent(QInputMethodEvent *event)
If you do reimplement this function, you should call the base class
implementation.
*/
-void QQuickItem::focusInEvent(QFocusEvent * /*event*/)
+void QQuickItem::focusInEvent(QFocusEvent *event)
{
+ Q_D(QQuickItem);
#if QT_CONFIG(accessibility)
if (QAccessible::isActive()) {
if (QObject *acc = QQuickAccessibleAttached::findAccessible(this)) {
@@ -4005,17 +4161,20 @@ void QQuickItem::focusInEvent(QFocusEvent * /*event*/)
}
}
#endif
+ d->setLastFocusChangeReason(event->reason());
}
/*!
This event handler can be reimplemented in a subclass to receive focus-out
- events for an item. The event information is provided by the \c event
+ events for an item. The event information is provided by the \a event
parameter.
\input item.qdocinc accepting-events
*/
-void QQuickItem::focusOutEvent(QFocusEvent * /*event*/)
+void QQuickItem::focusOutEvent(QFocusEvent *event)
{
+ Q_D(QQuickItem);
+ d->setLastFocusChangeReason(event->reason());
}
/*!
@@ -4072,8 +4231,9 @@ void QQuickItem::mouseReleaseEvent(QMouseEvent *event)
\input item.qdocinc accepting-events
*/
-void QQuickItem::mouseDoubleClickEvent(QMouseEvent *)
+void QQuickItem::mouseDoubleClickEvent(QMouseEvent *event)
{
+ event->ignore();
}
/*!
@@ -4131,7 +4291,7 @@ void QQuickItem::touchEvent(QTouchEvent *event)
*/
void QQuickItem::hoverEnterEvent(QHoverEvent *event)
{
- Q_UNUSED(event);
+ event->ignore();
}
/*!
@@ -4145,7 +4305,7 @@ void QQuickItem::hoverEnterEvent(QHoverEvent *event)
*/
void QQuickItem::hoverMoveEvent(QHoverEvent *event)
{
- Q_UNUSED(event);
+ event->ignore();
}
/*!
@@ -4159,7 +4319,7 @@ void QQuickItem::hoverMoveEvent(QHoverEvent *event)
*/
void QQuickItem::hoverLeaveEvent(QHoverEvent *event)
{
- Q_UNUSED(event);
+ event->ignore();
}
#if QT_CONFIG(quick_draganddrop)
@@ -4239,7 +4399,11 @@ void QQuickItem::dropEvent(QDropEvent *event)
This method will only be called if filtersChildMouseEvents() is \c true.
Return \c true if the specified \a event should not be passed on to the
- specified child \a item, and \c false otherwise.
+ specified child \a item, and \c false otherwise. If you return \c true, you
+ should also \l {QEvent::accept()}{accept} or \l {QEvent::ignore()}{ignore}
+ the \a event, to signal if event propagation should stop or continue.
+ The \a event will, however, always be sent to all childMouseEventFilters
+ up the parent chain.
\note Despite the name, this function filters all QPointerEvent instances
during delivery to all children (typically mouse, touch, and tablet
@@ -4293,6 +4457,7 @@ QVariant QQuickItem::inputMethodQuery(Qt::InputMethodQuery query) const
case Qt::ImMaximumTextLength:
case Qt::ImAnchorPosition:
case Qt::ImPreferredLanguage:
+ case Qt::ImReadOnly:
if (d->extra.isAllocated() && d->extra->keyHandler)
v = d->extra->keyHandler->inputMethodQuery(query);
break;
@@ -4403,16 +4568,11 @@ void QQuickItem::setBaselineOffset(qreal offset)
d->baselineOffset = offset;
- if (!d->changeListeners.isEmpty()) {
- const auto listeners = d->changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Geometry) {
- QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
- if (anchor)
- anchor->updateVerticalAnchors();
- }
- }
- }
+ d->notifyChangeListeners(QQuickItemPrivate::Geometry, [](const QQuickItemPrivate::ChangeListener &change){
+ QQuickAnchorsPrivate *anchor = change.listener->anchorPrivate();
+ if (anchor)
+ anchor->updateVerticalAnchors();
+ });
if (d->_anchors && (d->_anchors->usedAnchors() & QQuickAnchors::BaselineAnchor))
QQuickAnchorsPrivate::get(d->_anchors)->updateVerticalAnchors();
@@ -4448,7 +4608,7 @@ void QQuickItem::update()
When the scene graph processes the request, it will call updatePolish()
on this item.
- \sa updatePolish(), QQuickTest::qIsPolishScheduled()
+ \sa updatePolish(), QQuickTest::qIsPolishScheduled(), ensurePolished()
*/
void QQuickItem::polish()
{
@@ -4464,7 +4624,28 @@ void QQuickItem::polish()
}
}
-static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *itemForWarning, const QString &functionNameForWarning,
+/*!
+ \since 6.3
+
+ Calls updatePolish()
+
+ This can be useful for items such as Layouts (or Positioners) which delay calculation of
+ their implicitWidth and implicitHeight until they receive a PolishEvent.
+
+ Normally, if e.g. a child item is added or removed to a Layout, the implicit size is not
+ immediately calculated (this is an optimization). In some cases it might be desirable to
+ query the implicit size of the layout right after a child item has been added.
+ If this is the case, use this function right before querying the implicit size.
+
+ \sa updatePolish(), polish()
+ */
+void QQuickItem::ensurePolished()
+{
+ updatePolish();
+}
+
+#if QT_DEPRECATED_SINCE(6, 5)
+static bool unwrapMapFromToFromItemArgs(QQmlV4FunctionPtr args, const QQuickItem *itemForWarning, const QString &functionNameForWarning,
QQuickItem **itemObj, qreal *x, qreal *y, qreal *w, qreal *h, bool *isRect)
{
QV4::ExecutionEngine *v4 = args->v4engine();
@@ -4546,12 +4727,13 @@ static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *
return true;
}
+#endif
/*!
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, real x, real y)
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, point p)
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, real x, real y, real width, real height)
- \qmlmethod object QtQuick::Item::mapFromItem(Item item, rect r)
+ \qmlmethod point QtQuick::Item::mapFromItem(Item item, real x, real y)
+ \qmlmethod point QtQuick::Item::mapFromItem(Item item, point p)
+ \qmlmethod rect QtQuick::Item::mapFromItem(Item item, real x, real y, real width, real height)
+ \qmlmethod rect QtQuick::Item::mapFromItem(Item item, rect r)
Maps the point (\a x, \a y) or rect (\a x, \a y, \a width, \a height), which is in \a
item's coordinate system, to this item's coordinate system, and returns a \l point or \l rect
@@ -4560,14 +4742,16 @@ static bool unwrapMapFromToFromItemArgs(QQmlV4Function *args, const QQuickItem *
\input item.qdocinc mapping
If \a item is a \c null value, this maps the point or rect from the coordinate system of
- the root QML view.
+ the \l{Scene Coordinates}{scene}.
The versions accepting point and rect are since Qt 5.15.
*/
+
+#if QT_DEPRECATED_SINCE(6, 5)
/*!
\internal
*/
-void QQuickItem::mapFromItem(QQmlV4Function *args) const
+void QQuickItem::mapFromItem(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4584,6 +4768,7 @@ void QQuickItem::mapFromItem(QQmlV4Function *args) const
QV4::ScopedObject rv(scope, v4->fromVariant(result));
args->setReturnValue(rv.asReturnedValue());
}
+#endif
/*!
\internal
@@ -4603,10 +4788,10 @@ QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const
}
/*!
- \qmlmethod object QtQuick::Item::mapToItem(Item item, real x, real y)
- \qmlmethod object QtQuick::Item::mapToItem(Item item, point p)
- \qmlmethod object QtQuick::Item::mapToItem(Item item, real x, real y, real width, real height)
- \qmlmethod object QtQuick::Item::mapToItem(Item item, rect r)
+ \qmlmethod point QtQuick::Item::mapToItem(Item item, real x, real y)
+ \qmlmethod point QtQuick::Item::mapToItem(Item item, point p)
+ \qmlmethod rect QtQuick::Item::mapToItem(Item item, real x, real y, real width, real height)
+ \qmlmethod rect QtQuick::Item::mapToItem(Item item, rect r)
Maps the point (\a x, \a y) or rect (\a x, \a y, \a width, \a height), which is in this
item's coordinate system, to \a item's coordinate system, and returns a \l point or \l rect
@@ -4615,14 +4800,16 @@ QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const
\input item.qdocinc mapping
If \a item is a \c null value, this maps the point or rect to the coordinate system of the
- root QML view.
+ \l{Scene Coordinates}{scene}.
The versions accepting point and rect are since Qt 5.15.
*/
+
+#if QT_DEPRECATED_SINCE(6, 5)
/*!
\internal
*/
-void QQuickItem::mapToItem(QQmlV4Function *args) const
+void QQuickItem::mapToItem(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4640,7 +4827,7 @@ void QQuickItem::mapToItem(QQmlV4Function *args) const
args->setReturnValue(rv.asReturnedValue());
}
-static bool unwrapMapFromToFromGlobalArgs(QQmlV4Function *args, const QQuickItem *itemForWarning, const QString &functionNameForWarning, qreal *x, qreal *y)
+static bool unwrapMapFromToFromGlobalArgs(QQmlV4FunctionPtr args, const QQuickItem *itemForWarning, const QString &functionNameForWarning, qreal *x, qreal *y)
{
QV4::ExecutionEngine *v4 = args->v4engine();
if (args->length() != 1 && args->length() != 2) {
@@ -4688,7 +4875,7 @@ static bool unwrapMapFromToFromGlobalArgs(QQmlV4Function *args, const QQuickItem
/*!
\since 5.7
- \qmlmethod object QtQuick::Item::mapFromGlobal(real x, real y)
+ \qmlmethod point QtQuick::Item::mapFromGlobal(real x, real y)
Maps the point (\a x, \a y), which is in the global coordinate system, to the
item's coordinate system, and returns a \l point matching the mapped coordinate.
@@ -4698,7 +4885,7 @@ static bool unwrapMapFromToFromGlobalArgs(QQmlV4Function *args, const QQuickItem
/*!
\internal
*/
-void QQuickItem::mapFromGlobal(QQmlV4Function *args) const
+void QQuickItem::mapFromGlobal(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4712,20 +4899,23 @@ void QQuickItem::mapFromGlobal(QQmlV4Function *args) const
QV4::ScopedObject rv(scope, v4->fromVariant(result));
args->setReturnValue(rv.asReturnedValue());
}
+#endif
/*!
\since 5.7
- \qmlmethod object QtQuick::Item::mapToGlobal(real x, real y)
+ \qmlmethod point QtQuick::Item::mapToGlobal(real x, real y)
Maps the point (\a x, \a y), which is in this item's coordinate system, to the
global coordinate system, and returns a \l point matching the mapped coordinate.
\input item.qdocinc mapping
*/
+
+#if QT_DEPRECATED_SINCE(6, 5)
/*!
\internal
*/
-void QQuickItem::mapToGlobal(QQmlV4Function *args) const
+void QQuickItem::mapToGlobal(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
@@ -4739,6 +4929,7 @@ void QQuickItem::mapToGlobal(QQmlV4Function *args) const
QV4::ScopedObject rv(scope, v4->fromVariant(result));
args->setReturnValue(rv.asReturnedValue());
}
+#endif
/*!
\qmlmethod QtQuick::Item::forceActiveFocus()
@@ -4800,9 +4991,12 @@ void QQuickItem::forceActiveFocus(Qt::FocusReason reason)
{
setFocus(true, reason);
QQuickItem *parent = parentItem();
+ QQuickItem *scope = nullptr;
while (parent) {
if (parent->flags() & QQuickItem::ItemIsFocusScope) {
parent->setFocus(true, reason);
+ if (!scope)
+ scope = parent;
}
parent = parent->parentItem();
}
@@ -4847,30 +5041,101 @@ QQuickItem *QQuickItem::nextItemInFocusChain(bool forward)
QQuickItem *QQuickItem::childAt(qreal x, qreal y) const
{
const QList<QQuickItem *> children = childItems();
- for (int i = children.count()-1; i >= 0; --i) {
+ for (int i = children.size()-1; i >= 0; --i) {
QQuickItem *child = children.at(i);
// Map coordinates to the child element's coordinate space
QPointF point = mapToItem(child, QPointF(x, y));
- if (child->isVisible() && point.x() >= 0
- && child->width() > point.x()
- && point.y() >= 0
- && child->height() > point.y())
+ if (child->isVisible() && child->contains(point))
return child;
}
return nullptr;
}
+/*!
+ \qmlmethod QtQuick::Item::dumpItemTree()
+
+ Dumps some details about the
+ \l {Concepts - Visual Parent in Qt Quick}{visual tree of Items} starting
+ with this item and its children, recursively.
+
+ The output looks similar to that of this QML code:
+
+ \qml
+ function dump(object, indent) {
+ console.log(indent + object)
+ for (const i in object.children)
+ dump(object.children[i], indent + " ")
+ }
+
+ dump(myItem, "")
+ \endqml
+
+ So if you want more details, you can implement your own function and add
+ extra output to the console.log, such as values of specific properties.
+
+ \sa QObject::dumpObjectTree()
+ \since 6.3
+*/
+/*!
+ Dumps some details about the
+ \l {Concepts - Visual Parent in Qt Quick}{visual tree of Items} starting
+ with this item, recursively.
+
+ \note QObject::dumpObjectTree() dumps a similar tree; but, as explained
+ in \l {Concepts - Visual Parent in Qt Quick}, an item's QObject::parent()
+ sometimes differs from its QQuickItem::parentItem(). You can dump
+ both trees to see the difference.
+
+ \note The exact output format may change in future versions of Qt.
+
+ \since 6.3
+ \sa {Debugging Techniques}
+ \sa {https://doc.qt.io/GammaRay/gammaray-qtquick2-inspector.html}{GammaRay's Qt Quick Inspector}
+*/
+void QQuickItem::dumpItemTree() const
+{
+ Q_D(const QQuickItem);
+ d->dumpItemTree(0);
+}
+
+void QQuickItemPrivate::dumpItemTree(int indent) const
+{
+ Q_Q(const QQuickItem);
+
+ const auto indentStr = QString(indent * 4, QLatin1Char(' '));
+ qDebug().nospace().noquote() << indentStr <<
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ const_cast<QQuickItem *>(q);
+#else
+ q;
+#endif
+ if (extra.isAllocated()) {
+ for (const auto handler : extra->pointerHandlers)
+ qDebug().nospace().noquote() << indentStr << u" \u26ee " << handler;
+ }
+ for (const QQuickItem *ch : childItems) {
+ auto itemPriv = QQuickItemPrivate::get(ch);
+ itemPriv->dumpItemTree(indent + 1);
+ }
+}
+
QQmlListProperty<QObject> QQuickItemPrivate::resources()
{
- return QQmlListProperty<QObject>(q_func(), nullptr, QQuickItemPrivate::resources_append,
- QQuickItemPrivate::resources_count,
- QQuickItemPrivate::resources_at,
- QQuickItemPrivate::resources_clear);
+ // Do not synthesize replace().
+ // It would be extremely expensive and wouldn't work with most methods.
+ QQmlListProperty<QObject> result;
+ result.object = q_func();
+ result.append = QQuickItemPrivate::resources_append;
+ result.count = QQuickItemPrivate::resources_count;
+ result.at = QQuickItemPrivate::resources_at;
+ result.clear = QQuickItemPrivate::resources_clear;
+ result.removeLast = QQuickItemPrivate::resources_removeLast;
+ return result;
}
/*!
\qmlproperty list<Item> QtQuick::Item::children
- \qmlproperty list<Object> QtQuick::Item::resources
+ \qmlproperty list<QtObject> QtQuick::Item::resources
The children property contains the list of visual children of this item.
The resources property contains non-visual resources that you want to
@@ -4887,11 +5152,16 @@ QQmlListProperty<QObject> QQuickItemPrivate::resources()
*/
QQmlListProperty<QQuickItem> QQuickItemPrivate::children()
{
- return QQmlListProperty<QQuickItem>(q_func(), nullptr, QQuickItemPrivate::children_append,
- QQuickItemPrivate::children_count,
- QQuickItemPrivate::children_at,
- QQuickItemPrivate::children_clear);
-
+ // Do not synthesize replace().
+ // It would be extremely expensive and wouldn't work with most methods.
+ QQmlListProperty<QQuickItem> result;
+ result.object = q_func();
+ result.append = QQuickItemPrivate::children_append;
+ result.count = QQuickItemPrivate::children_count;
+ result.at = QQuickItemPrivate::children_at;
+ result.clear = QQuickItemPrivate::children_clear;
+ result.removeLast = QQuickItemPrivate::children_removeLast;
+ return result;
}
/*!
@@ -4934,11 +5204,11 @@ QQmlListProperty<QQuickItem> QQuickItemPrivate::visibleChildren()
states: [
State {
name: "red_color"
- PropertyChanges { target: root; color: "red" }
+ PropertyChanges { root.color: "red" }
},
State {
name: "blue_color"
- PropertyChanges { target: root; color: "blue" }
+ PropertyChanges { root.color: "blue" }
}
]
}
@@ -5115,6 +5385,13 @@ void QQuickItem::componentComplete()
d->addToDirtyList();
QQuickWindowPrivate::get(d->window)->dirtyItem(this);
}
+
+#if QT_CONFIG(accessibility)
+ if (d->isAccessible && d->effectiveVisible) {
+ QAccessibleEvent ev(this, QAccessible::ObjectShow);
+ QAccessible::updateAccessibility(&ev);
+ }
+#endif
}
QQuickStateGroup *QQuickItemPrivate::_states()
@@ -5156,21 +5433,76 @@ QPointF QQuickItemPrivate::computeTransformOrigin() const
}
}
-void QQuickItemPrivate::transformChanged()
+/*!
+ \internal
+ QQuickItemPrivate::dirty() calls transformChanged(q) to inform this item and
+ all its children that its transform has changed, with \a transformedItem always
+ being the parent item that caused the change. Override to react, e.g. to
+ call update() if the item needs to re-generate SG nodes based on visible extents.
+ If you override in a subclass, you must also call this (superclass) function
+ and return the value from it.
+
+ This function recursively visits all children as long as
+ subtreeTransformChangedEnabled is true, returns \c true if any of those
+ children still has the ItemObservesViewport flag set, but otherwise
+ turns subtreeTransformChangedEnabled off, if no children are observing.
+*/
+bool QQuickItemPrivate::transformChanged(QQuickItem *transformedItem)
{
+ Q_Q(QQuickItem);
+
+ bool childWantsIt = false;
+ if (subtreeTransformChangedEnabled) {
+ // Inform the children in paint order: by the time we visit leaf items,
+ // they can see any consequences in their parents
+ const auto children = paintOrderChildItems();
+ for (QQuickItem *child : children)
+ childWantsIt |= QQuickItemPrivate::get(child)->transformChanged(transformedItem);
+ }
+
#if QT_CONFIG(quick_shadereffect)
- if (extra.isAllocated() && extra->layer)
- extra->layer->updateMatrix();
+ if (q == transformedItem) {
+ if (extra.isAllocated() && extra->layer)
+ extra->layer->updateMatrix();
+ }
#endif
+ const bool thisWantsIt = q->flags().testFlag(QQuickItem::ItemObservesViewport);
+ const bool ret = childWantsIt || thisWantsIt;
+ if (!ret && componentComplete && subtreeTransformChangedEnabled) {
+ qCDebug(lcVP) << "turned off subtree transformChanged notification after checking all children of" << q;
+ subtreeTransformChangedEnabled = false;
+ }
+ // If ItemObservesViewport, clipRect() calculates the intersection with the viewport;
+ // so each time the item moves in the viewport, its clipnode needs to be updated.
+ if (thisWantsIt && q->clip() && !(dirtyAttributes & QQuickItemPrivate::Clip))
+ dirty(QQuickItemPrivate::Clip);
+ return ret;
}
+/*! \internal
+ Returns the new position (proposed values for the x and y properties)
+ to which this item should be moved to compensate for the given change
+ in scale from \a startScale to \a activeScale and in rotation from
+ \a startRotation to \a activeRotation. \a centroidParentPos is the
+ point that we wish to hold in place (and then apply \a activeTranslation to),
+ in this item's parent's coordinate system. \a startPos is this item's
+ position in its parent's coordinate system when the gesture began.
+ \a activeTranslation is the amount of translation that should be added to
+ the return value, i.e. the displacement by which the centroid is expected
+ to move.
+
+ If \a activeTranslation is \c (0, 0) the centroid is to be held in place.
+ If \a activeScale is \c 1, it means scale is intended to be held constant,
+ the same as \a startScale. If \a activeRotation is \c 0, it means rotation
+ is intended to be held constant, the same as \a startRotation.
+*/
QPointF QQuickItemPrivate::adjustedPosForTransform(const QPointF &centroidParentPos,
const QPointF &startPos,
- const QVector2D &activeTranslation, //[0,0] means no additional translation from startPos
+ const QVector2D &activeTranslation,
qreal startScale,
- qreal activeScale, // 1.0 means no additional scale from startScale
+ qreal activeScale,
qreal startRotation,
- qreal activeRotation) // 0.0 means no additional rotation from startRotation
+ qreal activeRotation)
{
Q_Q(QQuickItem);
QVector3D xformOrigin(q->transformOriginPoint());
@@ -5192,7 +5524,7 @@ QPointF QQuickItemPrivate::adjustedPosForTransform(const QPointF &centroidParent
mat = mat * startMatrix;
QPointF xformOriginPoint = q->transformOriginPoint();
- QPointF pos = mat * xformOriginPoint;
+ QPointF pos = mat.map(xformOriginPoint);
pos -= xformOriginPoint;
return pos;
@@ -5296,6 +5628,41 @@ bool QQuickItemPrivate::filterKeyEvent(QKeyEvent *e, bool post)
return e->isAccepted();
}
+void QQuickItemPrivate::deliverPointerEvent(QEvent *event)
+{
+ Q_Q(QQuickItem);
+ const auto eventType = event->type();
+ const bool focusAccepted = setFocusIfNeeded(eventType);
+
+ switch (eventType) {
+ case QEvent::MouseButtonPress:
+ q->mousePressEvent(static_cast<QMouseEvent *>(event));
+ break;
+ case QEvent::MouseButtonRelease:
+ q->mouseReleaseEvent(static_cast<QMouseEvent *>(event));
+ break;
+ case QEvent::MouseButtonDblClick:
+ q->mouseDoubleClickEvent(static_cast<QMouseEvent *>(event));
+ break;
+#if QT_CONFIG(wheelevent)
+ case QEvent::Wheel:
+ q->wheelEvent(static_cast<QWheelEvent*>(event));
+ break;
+#endif
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ case QEvent::TouchCancel:
+ q->touchEvent(static_cast<QTouchEvent *>(event));
+ break;
+ default:
+ break;
+ }
+
+ if (focusAccepted)
+ event->accept();
+}
+
void QQuickItemPrivate::deliverKeyEvent(QKeyEvent *e)
{
Q_Q(QQuickItem);
@@ -5363,9 +5730,10 @@ void QQuickItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e)
void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
{
- if (extra.isAllocated() && extra->keyHandler) {
- extra->keyHandler->shortcutOverride(event);
- }
+ if (extra.isAllocated() && extra->keyHandler)
+ extra->keyHandler->shortcutOverrideEvent(event);
+ else
+ event->ignore();
}
bool QQuickItemPrivate::anyPointerHandlerWants(const QPointerEvent *event, const QEventPoint &point) const
@@ -5381,20 +5749,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 or QWheelEvent (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::isMouseOrWheelEvent(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;
}
@@ -5441,19 +5815,77 @@ void QQuickItem::updateInputMethod(Qt::InputMethodQueries queries)
}
#endif // im
-// XXX todo - do we want/need this anymore?
-/*! \internal */
+/*!
+ Returns the extents of the item in its own coordinate system:
+ a rectangle from \c{0, 0} to \l width() and \l height().
+*/
QRectF QQuickItem::boundingRect() const
{
Q_D(const QQuickItem);
return QRectF(0, 0, d->width, d->height);
}
-/*! \internal */
+/*!
+ Returns the rectangular area within this item that is currently visible in
+ \l viewportItem(), if there is a viewport and the \l ItemObservesViewport
+ flag is set; otherwise, the extents of this item in its own coordinate
+ system: a rectangle from \c{0, 0} to \l width() and \l height(). This is
+ the region intended to remain visible if \l clip is \c true. It can also be
+ used in updatePaintNode() to limit the graphics added to the scene graph.
+
+ For example, a large drawing or a large text document might be shown in a
+ Flickable that occupies only part of the application's Window: in that
+ case, Flickable is the viewport item, and a custom content-rendering item
+ may choose to omit scene graph nodes that fall outside the area that is
+ currently visible. If the \l ItemObservesViewport flag is set, this area
+ will change each time the user scrolls the content in the Flickable.
+
+ In case of nested viewport items, clipRect() is the intersection of the
+ \c {boundingRect}s of all ancestors that have the \l ItemIsViewport flag set,
+ mapped to the coordinate system of \e this item.
+
+ \sa boundingRect()
+*/
QRectF QQuickItem::clipRect() const
{
Q_D(const QQuickItem);
- return QRectF(0, 0, d->width, d->height);
+ QRectF ret(0, 0, d->width, d->height);
+ if (flags().testFlag(QQuickItem::ItemObservesViewport)) {
+ if (QQuickItem *viewport = viewportItem()) {
+ // if the viewport is already "this", there's nothing to intersect;
+ // and don't call clipRect() again, to avoid infinite recursion
+ if (viewport == this)
+ return ret;
+ const auto mappedViewportRect = mapRectFromItem(viewport, viewport->clipRect());
+ qCDebug(lcVP) << this << "intersecting" << viewport << mappedViewportRect << ret << "->" << mappedViewportRect.intersected(ret);
+ return mappedViewportRect.intersected(ret);
+ }
+ }
+ return ret;
+}
+
+/*!
+ If the \l ItemObservesViewport flag is set,
+ returns the nearest parent with the \l ItemIsViewport flag.
+ Returns the window's contentItem if the flag is not set,
+ or if no other viewport item is found.
+
+ Returns \nullptr only if there is no viewport item and this item is not
+ shown in a window.
+
+ \sa clipRect()
+*/
+QQuickItem *QQuickItem::viewportItem() const
+{
+ if (flags().testFlag(ItemObservesViewport)) {
+ QQuickItem *par = parentItem();
+ while (par) {
+ if (par->flags().testFlag(QQuickItem::ItemIsViewport))
+ return par;
+ par = par->parentItem();
+ }
+ }
+ return (window() ? window()->contentItem() : nullptr);
}
/*!
@@ -5702,8 +6134,8 @@ void QQuickItem::setZ(qreal v)
d->dirty(QQuickItemPrivate::ZValue);
if (d->parentItem) {
- QQuickItemPrivate::get(d->parentItem)->dirty(QQuickItemPrivate::ChildrenStackingChanged);
QQuickItemPrivate::get(d->parentItem)->markSortedChildrenDirty(this);
+ QQuickItemPrivate::get(d->parentItem)->dirty(QQuickItemPrivate::ChildrenStackingChanged);
}
emit zChanged();
@@ -6067,7 +6499,17 @@ void QQuickItem::setOpacity(qreal newOpacity)
\note This property's value is only affected by changes to this property or
the parent's \c visible property. It does not change, for example, if this
- item moves off-screen, or if the \l opacity changes to 0.
+ item moves off-screen, or if the \l opacity changes to 0. However, for
+ historical reasons, this property is true after the item's construction, even
+ if the item hasn't been added to a scene yet. Changing or reading this
+ property of an item that has not been added to a scene might not produce
+ the expected results.
+
+ \note The notification signal for this property gets emitted during destruction
+ of the visual parent. C++ signal handlers cannot assume that items in the
+ visual parent hierarchy are still fully constructed. Use \l qobject_cast to
+ verify that items in the parent hierarchy can be used safely as the expected
+ type.
\sa opacity, enabled
*/
@@ -6127,6 +6569,15 @@ void QQuickItem::setVisible(bool v)
Setting this property to \c false automatically causes \l activeFocus to be
set to \c false, and this item will longer receive keyboard events.
+ \note Hover events are enabled separately by \l setAcceptHoverEvents().
+ Thus, a disabled item can continue to receive hover events, even when this
+ property is \c false. This makes it possible to show informational feedback
+ (such as \l ToolTip) even when an interactive item is disabled.
+ The same is also true for any \l {HoverHandler}{HoverHandlers}
+ added as children of the item. A HoverHandler can, however, be
+ \l {PointerHandler::enabled}{disabled} explicitly, or for example
+ be bound to the \c enabled state of the item.
+
\sa visible
*/
bool QQuickItem::isEnabled() const
@@ -6152,10 +6603,8 @@ void QQuickItem::setEnabled(bool e)
bool QQuickItemPrivate::calcEffectiveVisible() const
{
- // XXX todo - Should the effective visible of an element with no parent just be the current
- // effective visible? This would prevent pointless re-processing in the case of an element
- // moving to/from a no-parent situation, but it is different from what graphics view does.
- return explicitVisible && (!parentItem || QQuickItemPrivate::get(parentItem)->effectiveVisible);
+ // An item is visible if it is a child of a visible parent, and not explicitly hidden.
+ return explicitVisible && parentItem && QQuickItemPrivate::get(parentItem)->effectiveVisible;
}
bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
@@ -6177,22 +6626,25 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
if (parentItem)
QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged);
if (window)
- deliveryAgentPrivate()->removeGrabber(q, true, true, true);
+ if (auto agent = deliveryAgentPrivate(); agent)
+ agent->removeGrabber(q, true, true, true);
bool childVisibilityChanged = false;
- for (int ii = 0; ii < childItems.count(); ++ii)
+ for (int ii = 0; ii < childItems.size(); ++ii)
childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible);
- itemChange(QQuickItem::ItemVisibleHasChanged, effectiveVisible);
+ itemChange(QQuickItem::ItemVisibleHasChanged, bool(effectiveVisible));
#if QT_CONFIG(accessibility)
if (isAccessible) {
QAccessibleEvent ev(q, effectiveVisible ? QAccessible::ObjectShow : QAccessible::ObjectHide);
QAccessible::updateAccessibility(&ev);
}
#endif
- emit q->visibleChanged();
- if (childVisibilityChanged)
- emit q->visibleChildrenChanged();
+ if (!inDestructor) {
+ emit q->visibleChanged();
+ if (childVisibilityChanged)
+ emit q->visibleChildrenChanged();
+ }
return true; // effective visibility DID change
}
@@ -6231,7 +6683,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
}
}
- for (int ii = 0; ii < childItems.count(); ++ii) {
+ for (int ii = 0; ii < childItems.size(); ++ii) {
QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(
(flags & QQuickItem::ItemIsFocusScope) && scope ? q : scope, newEffectiveEnable);
}
@@ -6242,7 +6694,16 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
QQuickDeliveryAgentPrivate::DontChangeSubFocusItem);
}
- itemChange(QQuickItem::ItemEnabledHasChanged, effectiveEnable);
+ itemChange(QQuickItem::ItemEnabledHasChanged, bool(effectiveEnable));
+#if QT_CONFIG(accessibility)
+ if (isAccessible) {
+ QAccessible::State changedState;
+ changedState.disabled = true;
+ changedState.focusable = true;
+ QAccessibleStateChangeEvent ev(q, changedState);
+ QAccessible::updateAccessibility(&ev);
+ }
+#endif
emit q->enabledChanged();
}
@@ -6293,9 +6754,6 @@ QString QQuickItemPrivate::dirtyToString() const
void QQuickItemPrivate::dirty(DirtyType type)
{
Q_Q(QQuickItem);
- if (type & (TransformOrigin | Transform | BasicTransform | Position | Size))
- transformChanged();
-
if (!(dirtyAttributes & type) || (window && !prevDirtyItem)) {
dirtyAttributes |= type;
if (window && componentComplete) {
@@ -6303,6 +6761,8 @@ void QQuickItemPrivate::dirty(DirtyType type)
QQuickWindowPrivate::get(window)->dirtyItem(q);
}
}
+ if (type & (TransformOrigin | Transform | BasicTransform | Position | Size | Clip))
+ transformChanged(q);
}
void QQuickItemPrivate::addToDirtyList()
@@ -6356,7 +6816,7 @@ void QQuickItemPrivate::recursiveRefFromEffectItem(int refs)
if (!refs)
return;
extra.value().recursiveEffectRefCount += refs;
- for (int ii = 0; ii < childItems.count(); ++ii) {
+ for (int ii = 0; ii < childItems.size(); ++ii) {
QQuickItem *child = childItems.at(ii);
QQuickItemPrivate::get(child)->recursiveRefFromEffectItem(refs);
}
@@ -6398,26 +6858,24 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
switch (change) {
case QQuickItem::ItemChildAddedChange: {
q->itemChange(change, data);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Children) {
- change.listener->itemChildAdded(q, data.item);
- }
+ // The newly added child or any of its descendants may have
+ // ItemObservesViewport set, in which case we need to both
+ // inform the item that the transform has changed, and re-apply
+ // subtreeTransformChangedEnabled to both this item and its
+ // ancestors.
+ if (QQuickItemPrivate::get(data.item)->transformChanged(q)) {
+ if (!subtreeTransformChangedEnabled) {
+ qCDebug(lcVP) << "turned on transformChanged notification for subtree of" << q;
+ subtreeTransformChangedEnabled = true;
}
+ enableSubtreeChangeNotificationsForParentHierachy();
}
+ notifyChangeListeners(QQuickItemPrivate::Children, &QQuickItemChangeListener::itemChildAdded, q, data.item);
break;
}
case QQuickItem::ItemChildRemovedChange: {
q->itemChange(change, data);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Children) {
- change.listener->itemChildRemoved(q, data.item);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Children, &QQuickItemChangeListener::itemChildRemoved, q, data.item);
break;
}
case QQuickItem::ItemSceneChange:
@@ -6425,50 +6883,22 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
break;
case QQuickItem::ItemVisibleHasChanged: {
q->itemChange(change, data);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Visibility) {
- change.listener->itemVisibilityChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Visibility, &QQuickItemChangeListener::itemVisibilityChanged, q);
break;
}
case QQuickItem::ItemEnabledHasChanged: {
q->itemChange(change, data);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Enabled) {
- change.listener->itemEnabledChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Enabled, &QQuickItemChangeListener::itemEnabledChanged, q);
break;
}
case QQuickItem::ItemParentHasChanged: {
q->itemChange(change, data);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Parent) {
- change.listener->itemParentChanged(q, data.item);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Parent, &QQuickItemChangeListener::itemParentChanged, q, data.item);
break;
}
case QQuickItem::ItemOpacityHasChanged: {
q->itemChange(change, data);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Opacity) {
- change.listener->itemOpacityChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Opacity, &QQuickItemChangeListener::itemOpacityChanged, q);
break;
}
case QQuickItem::ItemActiveFocusHasChanged:
@@ -6476,14 +6906,7 @@ void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickIt
break;
case QQuickItem::ItemRotationHasChanged: {
q->itemChange(change, data);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::Rotation) {
- change.listener->itemRotationChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::Rotation, &QQuickItemChangeListener::itemRotationChanged, q);
break;
}
case QQuickItem::ItemAntialiasingHasChanged:
@@ -6558,6 +6981,7 @@ void QQuickItem::setSmooth(bool smooth)
This property holds whether the item wants to be in the tab focus
chain. By default, this is set to \c false.
*/
+// TODO FOCUS: Deprecate
bool QQuickItem::activeFocusOnTab() const
{
Q_D(const QQuickItem);
@@ -6621,7 +7045,7 @@ void QQuickItem::setAntialiasing(bool aa)
d->antialiasing = aa;
d->dirty(QQuickItemPrivate::Antialiasing);
- d->itemChange(ItemAntialiasingHasChanged, d->antialiasing);
+ d->itemChange(ItemAntialiasingHasChanged, bool(d->antialiasing));
emit antialiasingChanged(antialiasing());
}
@@ -6673,6 +7097,25 @@ void QQuickItem::setFlag(Flag flag, bool enabled)
setFlags((Flags)(d->flags | (quint32)flag));
else
setFlags((Flags)(d->flags & ~(quint32)flag));
+
+ // We don't return early if the flag did not change. That's useful in case
+ // we need to intentionally trigger this parent-chain traversal again.
+ if (enabled && flag == ItemObservesViewport)
+ d->enableSubtreeChangeNotificationsForParentHierachy();
+}
+
+void QQuickItemPrivate::enableSubtreeChangeNotificationsForParentHierachy()
+{
+ Q_Q(QQuickItem);
+
+ QQuickItem *par = q->parentItem();
+ while (par) {
+ auto parPriv = QQuickItemPrivate::get(par);
+ if (!parPriv->subtreeTransformChangedEnabled)
+ qCDebug(lcVP) << "turned on transformChanged notification for subtree of" << par;
+ parPriv->subtreeTransformChangedEnabled = true;
+ par = par->parentItem();
+ }
}
/*!
@@ -6749,35 +7192,54 @@ QPointF QQuickItem::position() const
void QQuickItem::setX(qreal v)
{
Q_D(QQuickItem);
+ /* There are two ways in which this function might be called:
+ a) Either directly by the user, or
+ b) when a binding has evaluated to a new value and it writes
+ the value back
+ In the first case, we want to remove an existing binding, in
+ the second case, we don't want to remove the binding which
+ just wrote the value.
+ removeBindingUnlessInWrapper takes care of this.
+ */
+ d->x.removeBindingUnlessInWrapper();
if (qt_is_nan(v))
return;
- if (d->x == v)
+
+ const qreal oldx = d->x.valueBypassingBindings();
+ if (oldx == v)
return;
- qreal oldx = d->x;
- d->x = v;
+ d->x.setValueBypassingBindings(v);
d->dirty(QQuickItemPrivate::Position);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(oldx, d->y, d->width, d->height));
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal w = d->width.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
+ geometryChange(QRectF(v, y, w, h), QRectF(oldx, y, w, h));
}
void QQuickItem::setY(qreal v)
{
Q_D(QQuickItem);
+ d->y.removeBindingUnlessInWrapper();
if (qt_is_nan(v))
return;
- if (d->y == v)
+
+ const qreal oldy = d->y.valueBypassingBindings();
+ if (oldy == v)
return;
- qreal oldy = d->y;
- d->y = v;
+ d->y.setValueBypassingBindings(v);
d->dirty(QQuickItemPrivate::Position);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(d->x, oldy, d->width, d->height));
+ // we use v instead of d->y, as that avoid a method call
+ // and we have v anyway in scope
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal w = d->width.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
+ geometryChange(QRectF(x, v, w, h), QRectF(x, oldy, w, h));
}
/*!
@@ -6786,19 +7248,47 @@ void QQuickItem::setY(qreal v)
void QQuickItem::setPosition(const QPointF &pos)
{
Q_D(QQuickItem);
- if (QPointF(d->x, d->y) == pos)
+
+ const qreal oldx = d->x.valueBypassingBindings();
+ const qreal oldy = d->y.valueBypassingBindings();
+
+ if (QPointF(oldx, oldy) == pos)
return;
- qreal oldx = d->x;
- qreal oldy = d->y;
+ /* This preserves the bindings, because that was what the code used to do
+ The effect of this is that you can have
+ Item {
+ Rectangle {
+ x: someValue; y: someValue
+ DragHandler {}
+ }
+ }
+ and you can move the rectangle around; once someValue changes, the position gets
+ reset again (even when a drag is currently ongoing).
+ Whether we want this is up to discussion.
+ */
- d->x = pos.x();
- d->y = pos.y();
+ d->x.setValueBypassingBindings(pos.x()); //TODO: investigate whether to break binding here or not
+ d->y.setValueBypassingBindings(pos.y());
d->dirty(QQuickItemPrivate::Position);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(oldx, oldy, d->width, d->height));
+ const qreal w = d->width.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
+ geometryChange(QRectF(pos.x(), pos.y(), w, h), QRectF(oldx, oldy, w, h));
+}
+
+/* The bindable methods return an object which supports inspection (hasBinding) and
+ modification (setBinding, removeBinding) of the properties bindable state.
+*/
+QBindable<qreal> QQuickItem::bindableX()
+{
+ return QBindable<qreal>(&d_func()->x);
+}
+
+QBindable<qreal> QQuickItem::bindableY()
+{
+ return QBindable<qreal>(&d_func()->y);
}
/*!
@@ -6815,40 +7305,37 @@ qreal QQuickItem::width() const
void QQuickItem::setWidth(qreal w)
{
Q_D(QQuickItem);
+ d->width.removeBindingUnlessInWrapper();
if (qt_is_nan(w))
return;
- d->widthValid = true;
- if (d->width == w)
+ d->widthValidFlag = true;
+ const qreal oldWidth = d->width.valueBypassingBindings();
+ if (oldWidth == w)
return;
- qreal oldWidth = d->width;
- d->width = w;
+ d->width.setValueBypassingBindings(w);
d->dirty(QQuickItemPrivate::Size);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(d->x, d->y, oldWidth, d->height));
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal h = d->height.valueBypassingBindings();
+ geometryChange(QRectF(x, y, w, h), QRectF(x, y, oldWidth, h));
}
void QQuickItem::resetWidth()
{
Q_D(QQuickItem);
- d->widthValid = false;
+ d->width.takeBinding();
+ d->widthValidFlag = false;
setImplicitWidth(implicitWidth());
}
void QQuickItemPrivate::implicitWidthChanged()
{
Q_Q(QQuickItem);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::ImplicitWidth) {
- change.listener->itemImplicitWidthChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::ImplicitWidth, &QQuickItemChangeListener::itemImplicitWidthChanged, q);
emit q->implicitWidthChanged();
}
@@ -6865,11 +7352,23 @@ qreal QQuickItem::implicitWidth() const
return d->getImplicitWidth();
}
+QBindable<qreal> QQuickItem::bindableWidth()
+{
+ return QBindable<qreal>(&d_func()->width);
+}
+
/*!
\qmlproperty real QtQuick::Item::implicitWidth
\qmlproperty real QtQuick::Item::implicitHeight
- Defines the natural width or height of the Item if no \l width or \l height is specified.
+ Defines the preferred width or height of the Item.
+
+ If \l width or \l height is not specified, an item's effective size will be
+ determined by its \l implicitWidth or \l implicitHeight.
+
+ However, if an item is the child of a \l {Qt Quick Layouts}{layout}, the
+ layout will determine the item's preferred size using its implicit size.
+ In such a scenario, the explicit \l width or \l height will be ignored.
The default implicit size for most items is 0x0, however some items have an inherent
implicit size which cannot be overridden, for example, \l [QML] Image and \l [QML] Text.
@@ -6903,7 +7402,14 @@ qreal QQuickItem::implicitWidth() const
\property QQuickItem::implicitWidth
\property QQuickItem::implicitHeight
- Defines the natural width or height of the Item if no \l width or \l height is specified.
+ Defines the preferred width or height of the Item.
+
+ If \l width or \l height is not specified, an item's effective size will be
+ determined by its \l implicitWidth or \l implicitHeight.
+
+ However, if an item is the child of a \l {Qt Quick Layouts}{layout}, the
+ layout will determine the item's preferred size using its implicit size.
+ In such a scenario, the explicit \l width or \l height will be ignored.
The default implicit size for most items is 0x0, however some items have an inherent
implicit size which cannot be overridden, for example, \l [QML] Image and \l [QML] Text.
@@ -6938,21 +7444,27 @@ void QQuickItem::setImplicitWidth(qreal w)
Q_D(QQuickItem);
bool changed = w != d->implicitWidth;
d->implicitWidth = w;
- if (d->width == w || widthValid()) {
+ // this uses valueBypassingBindings simply to avoid repeated "am I in a binding" checks
+ if (d->width.valueBypassingBindings() == w || widthValid()) {
if (changed)
d->implicitWidthChanged();
- if (d->width == w || widthValid())
+ if (d->width.valueBypassingBindings() == w || widthValid())
return;
changed = false;
}
- qreal oldWidth = d->width;
- d->width = w;
+ const qreal oldWidth = d->width.valueBypassingBindings();
+ Q_ASSERT(!d->width.hasBinding() || QQmlPropertyBinding::isUndefined(d->width.binding()));
+ // we need to keep the binding if its undefined (therefore we can't use operator=/setValue)
+ d->width.setValueBypassingBindings(w);
d->dirty(QQuickItemPrivate::Size);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(d->x, d->y, oldWidth, d->height));
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal width = w;
+ const qreal height = d->height.valueBypassingBindings();
+ geometryChange(QRectF(x, y, width, height), QRectF(x, y, oldWidth, height));
if (changed)
d->implicitWidthChanged();
@@ -6964,7 +7476,24 @@ void QQuickItem::setImplicitWidth(qreal w)
bool QQuickItem::widthValid() const
{
Q_D(const QQuickItem);
- return d->widthValid;
+ /* Logic: The width is valid if we assigned a value
+ or a binding to it. Note that a binding evaluation to
+ undefined (and thus calling resetWidth) is detached [1];
+ hasBinding will thus return false for it, which is
+ what we want here, as resetting width should mean that
+ width is invalid (until the binding evaluates to a
+ non-undefined value again).
+
+ [1]: A detached binding is a binding which is not set on a property.
+ In the case of QQmlPropertyBinding and resettable properties, it
+ still gets reevaluated when it was detached due to the binding
+ returning undefined, and it gets re-attached, once the binding changes
+ to a non-undefined value (unless another binding has beenset in the
+ meantime).
+ See QQmlPropertyBinding::isUndefined and handleUndefinedAssignment
+ */
+
+ return d->widthValid();
}
/*!
@@ -6981,40 +7510,43 @@ qreal QQuickItem::height() const
void QQuickItem::setHeight(qreal h)
{
Q_D(QQuickItem);
+ // Note that we call removeUnlessInWrapper before returning in the
+ // NaN and equal value cases; that ensures that an explicit setHeight
+ // always removes the binding
+ d->height.removeBindingUnlessInWrapper();
if (qt_is_nan(h))
return;
- d->heightValid = true;
- if (d->height == h)
+ d->heightValidFlag = true;
+ const qreal oldHeight = d->height.valueBypassingBindings();
+ if (oldHeight == h)
return;
- qreal oldHeight = d->height;
- d->height = h;
+ d->height.setValueBypassingBindings(h);
d->dirty(QQuickItemPrivate::Size);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(d->x, d->y, d->width, oldHeight));
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal w = d->width.valueBypassingBindings();
+ geometryChange(QRectF(x, y, w, h), QRectF(x, y, w, oldHeight));
}
void QQuickItem::resetHeight()
{
Q_D(QQuickItem);
- d->heightValid = false;
+ // using takeBinding, we remove any existing binding from the
+ // property, but preserve the existing value (and avoid some overhead
+ // compared to calling setHeight(height())
+ d->height.takeBinding();
+ d->heightValidFlag = false;
setImplicitHeight(implicitHeight());
}
void QQuickItemPrivate::implicitHeightChanged()
{
Q_Q(QQuickItem);
- if (!changeListeners.isEmpty()) {
- const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
- for (const QQuickItemPrivate::ChangeListener &change : listeners) {
- if (change.types & QQuickItemPrivate::ImplicitHeight) {
- change.listener->itemImplicitHeightChanged(q);
- }
- }
- }
+ notifyChangeListeners(QQuickItemPrivate::ImplicitHeight, &QQuickItemChangeListener::itemImplicitHeightChanged, q);
emit q->implicitHeightChanged();
}
@@ -7029,26 +7561,37 @@ qreal QQuickItem::implicitHeight() const
return d->getImplicitHeight();
}
+QBindable<qreal> QQuickItem::bindableHeight()
+{
+ return QBindable<qreal>(&d_func()->height);
+}
+
void QQuickItem::setImplicitHeight(qreal h)
{
Q_D(QQuickItem);
bool changed = h != d->implicitHeight;
d->implicitHeight = h;
- if (d->height == h || heightValid()) {
+ if (d->height.valueBypassingBindings() == h || heightValid()) {
if (changed)
d->implicitHeightChanged();
- if (d->height == h || heightValid())
+ if (d->height.valueBypassingBindings() == h || heightValid())
return;
changed = false;
}
- qreal oldHeight = d->height;
- d->height = h;
+ const qreal oldHeight = d->height.valueBypassingBindings();
+ Q_ASSERT(!d->height.hasBinding() || QQmlPropertyBinding::isUndefined(d->height.binding()));
+ // we need to keep the binding if its undefined (therefore we can't use operator=/setValue)
+ d->height.setValueBypassingBindings(h);
d->dirty(QQuickItemPrivate::Size);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(d->x, d->y, d->width, oldHeight));
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ const qreal width = d->width.valueBypassingBindings();
+ const qreal height = d->height.valueBypassingBindings();
+ geometryChange(QRectF(x, y, width, height),
+ QRectF(x, y, width, oldHeight));
if (changed)
d->implicitHeightChanged();
@@ -7068,32 +7611,40 @@ void QQuickItem::setImplicitSize(qreal w, qreal h)
bool wDone = false;
bool hDone = false;
- if (d->width == w || widthValid()) {
+ qreal width = d->width.valueBypassingBindings();
+ qreal height = d->height.valueBypassingBindings();
+ if (width == w || widthValid()) {
if (wChanged)
d->implicitWidthChanged();
- wDone = d->width == w || widthValid();
+ wDone = width == w || widthValid();
wChanged = false;
}
- if (d->height == h || heightValid()) {
+ if (height == h || heightValid()) {
if (hChanged)
d->implicitHeightChanged();
- hDone = d->height == h || heightValid();
+ hDone = height == h || heightValid();
hChanged = false;
}
if (wDone && hDone)
return;
- qreal oldWidth = d->width;
- qreal oldHeight = d->height;
- if (!wDone)
- d->width = w;
- if (!hDone)
- d->height = h;
+ const qreal oldWidth = width;
+ const qreal oldHeight = height;
+ if (!wDone) {
+ width = w;
+ d->width.setValueBypassingBindings(w);
+ }
+ if (!hDone) {
+ height = h;
+ d->height.setValueBypassingBindings(h);
+ }
d->dirty(QQuickItemPrivate::Size);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(d->x, d->y, oldWidth, oldHeight));
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ geometryChange(QRectF(x, y, width, height),
+ QRectF(x, y, oldWidth, oldHeight));
if (!wDone && wChanged)
d->implicitWidthChanged();
@@ -7107,7 +7658,7 @@ void QQuickItem::setImplicitSize(qreal w, qreal h)
bool QQuickItem::heightValid() const
{
Q_D(const QQuickItem);
- return d->heightValid;
+ return d->heightValid();
}
/*!
@@ -7129,31 +7680,37 @@ QSizeF QQuickItem::size() const
\since 5.10
Sets the size of the item to \a size.
+ This methods preserves any existing binding on width and height;
+ thus any change that triggers the binding to execute again will
+ override the set values.
\sa size, setWidth, setHeight
*/
void QQuickItem::setSize(const QSizeF &size)
{
Q_D(QQuickItem);
- d->heightValid = true;
- d->widthValid = true;
+ d->heightValidFlag = true;
+ d->widthValidFlag = true;
+
+ const qreal oldHeight = d->height.valueBypassingBindings();
+ const qreal oldWidth = d->width.valueBypassingBindings();
- if (d->width == size.width() && d->height == size.height())
+ if (oldWidth == size.width() && oldHeight == size.height())
return;
- qreal oldHeight = d->height;
- qreal oldWidth = d->width;
- d->height = size.height();
- d->width = size.width();
+ d->height.setValueBypassingBindings(size.height());
+ d->width.setValueBypassingBindings(size.width());
d->dirty(QQuickItemPrivate::Size);
- geometryChange(QRectF(d->x, d->y, d->width, d->height),
- QRectF(d->x, d->y, oldWidth, oldHeight));
+ const qreal x = d->x.valueBypassingBindings();
+ const qreal y = d->y.valueBypassingBindings();
+ geometryChange(QRectF(x, y, size.width(), size.height()), QRectF(x, y, oldWidth, oldHeight));
}
/*!
\qmlproperty bool QtQuick::Item::activeFocus
+ \readonly
This read-only property indicates whether the item has active focus.
@@ -7188,6 +7745,7 @@ void QQuickItem::setSize(const QSizeF &size)
*/
/*!
\property QQuickItem::activeFocus
+ \readonly
This read-only property indicates whether the item has active focus.
@@ -7327,23 +7885,23 @@ void QQuickItem::setFocus(bool focus)
void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
{
Q_D(QQuickItem);
- if (d->focus == focus)
+ // Need to find our nearest focus scope
+ QQuickItem *scope = parentItem();
+ while (scope && !scope->isFocusScope() && scope->parentItem())
+ scope = scope->parentItem();
+
+ if (d->focus == focus && (!focus || !scope || QQuickItemPrivate::get(scope)->subFocusItem == this))
return;
+ bool notifyListeners = false;
if (d->window || d->parentItem) {
- // Need to find our nearest focus scope
- QQuickItem *scope = parentItem();
- while (scope && !scope->isFocusScope() && scope->parentItem())
- scope = scope->parentItem();
if (d->window) {
- if (reason != Qt::PopupFocusReason) {
- auto da = d->deliveryAgentPrivate();
- Q_ASSERT(da);
- if (focus)
- da->setFocusInScope(scope, this, reason);
- else
- da->clearFocusInScope(scope, this, reason);
- }
+ auto da = d->deliveryAgentPrivate();
+ Q_ASSERT(da);
+ if (focus)
+ da->setFocusInScope(scope, this, reason);
+ else
+ da->clearFocusInScope(scope, this, reason);
} else {
// do the focus changes from setFocusInScope/clearFocusInScope that are
// unrelated to a window
@@ -7361,9 +7919,10 @@ void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
d->focus = focus;
changed << this;
+ notifyListeners = true;
emit focusChanged(focus);
- QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+ QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.size() - 1, reason);
}
} else {
QVarLengthArray<QQuickItem *, 20> changed;
@@ -7376,10 +7935,13 @@ void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
d->focus = focus;
changed << this;
+ notifyListeners = true;
emit focusChanged(focus);
- QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+ QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.size() - 1, reason);
}
+ if (notifyListeners)
+ d->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, this, reason);
}
/*!
@@ -7406,6 +7968,52 @@ QQuickItem *QQuickItem::scopedFocusItem() const
}
/*!
+ \qmlproperty enumeration QtQuick::Item::focusPolicy
+ \since 6.7
+
+ This property determines the way the item accepts focus.
+
+ \value Qt.TabFocus The item accepts focus by tabbing.
+ \value Qt.ClickFocus The item accepts focus by clicking.
+ \value Qt.StrongFocus The item accepts focus by both tabbing and clicking.
+ \value Qt.WheelFocus The item accepts focus by tabbing, clicking, and using the mouse wheel.
+ \value Qt.NoFocus The item does not accept focus.
+
+ \note This property was a member of the \l[QML]{Control} QML type in Qt 6.6 and earlier.
+*/
+/*!
+ \property QQuickItem::focusPolicy
+ \since 6.7
+
+ This property determines the way the item accepts focus.
+
+*/
+Qt::FocusPolicy QQuickItem::focusPolicy() const
+{
+ Q_D(const QQuickItem);
+ uint policy = d->focusPolicy;
+ if (activeFocusOnTab())
+ policy |= Qt::TabFocus;
+ return static_cast<Qt::FocusPolicy>(policy);
+}
+
+/*!
+ Sets the focus policy of this item to \a policy.
+
+ \sa focusPolicy()
+*/
+void QQuickItem::setFocusPolicy(Qt::FocusPolicy policy)
+{
+ Q_D(QQuickItem);
+ if (d->focusPolicy == policy)
+ return;
+
+ d->focusPolicy = policy;
+ setActiveFocusOnTab(policy & Qt::TabFocus);
+ emit focusPolicyChanged(policy);
+}
+
+/*!
Returns \c true if this item is an ancestor of \a child (i.e., if this item
is \a child's parent, or one of \a child's parent's ancestors).
@@ -7457,8 +8065,10 @@ void QQuickItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
d->extra.setTag(d->extra.tag().setFlag(QQuickItemPrivate::LeftMouseButtonAccepted, buttons & Qt::LeftButton));
buttons &= ~Qt::LeftButton;
- if (buttons || d->extra.isAllocated())
- d->extra.value().acceptedMouseButtons = buttons;
+ if (buttons || d->extra.isAllocated()) {
+ d->extra.value().acceptedMouseButtonsWithoutHandlers = buttons;
+ d->extra.value().acceptedMouseButtons = d->extra->pointerHandlers.isEmpty() ? buttons : Qt::AllButtons;
+ }
}
/*!
@@ -7509,7 +8119,7 @@ bool QQuickItem::isUnderMouse() const
return false;
QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition;
- return contains(mapFromScene(d->window->mapFromGlobal(cursorPos.toPoint())));
+ return contains(mapFromScene(d->window->mapFromGlobal(cursorPos)));
}
/*!
@@ -7537,6 +8147,10 @@ void QQuickItem::setAcceptHoverEvents(bool enabled)
Q_D(QQuickItem);
d->hoverEnabled = enabled;
d->setHasHoverInChild(enabled);
+ // The DA needs to resolve which items and handlers should now be hovered or unhovered.
+ // Marking this item dirty ensures that flushFrameSynchronousEvents() will be called from the render loop,
+ // even if this change is not in response to a mouse event and no item has already marked itself dirty.
+ d->dirty(QQuickItemPrivate::Content);
}
/*!
@@ -7579,7 +8193,7 @@ void QQuickItemPrivate::setHasCursorInChild(bool hc)
if (!hc && subtreeCursorEnabled) {
if (hasCursor)
return; // nope! sorry, I have a cursor myself
- for (QQuickItem *otherChild : qAsConst(childItems)) {
+ for (QQuickItem *otherChild : std::as_const(childItems)) {
QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild);
if (otherChildPrivate->subtreeCursorEnabled || otherChildPrivate->hasCursor)
return; // nope! sorry, something else wants it kept on.
@@ -7606,11 +8220,14 @@ void QQuickItemPrivate::setHasHoverInChild(bool hasHover)
if (!hasHover && subtreeHoverEnabled) {
if (hoverEnabled)
return; // nope! sorry, I need hover myself
- for (QQuickItem *otherChild : qAsConst(childItems)) {
+ if (hasEnabledHoverHandlers())
+ return; // nope! sorry, this item has enabled HoverHandlers
+
+ for (QQuickItem *otherChild : std::as_const(childItems)) {
QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild);
if (otherChildPrivate->subtreeHoverEnabled || otherChildPrivate->hoverEnabled)
return; // nope! sorry, something else wants it kept on.
- if (otherChildPrivate->hasHoverHandlers())
+ if (otherChildPrivate->hasEnabledHoverHandlers())
return; // nope! sorry, we have pointer handlers which are interested.
}
}
@@ -7659,6 +8276,7 @@ void QQuickItem::setCursor(const QCursor &cursor)
Q_D(QQuickItem);
Qt::CursorShape oldShape = d->extra.isAllocated() ? d->extra->cursor.shape() : Qt::ArrowCursor;
+ qCDebug(lcHoverTrace) << oldShape << "->" << cursor.shape();
if (oldShape != cursor.shape() || oldShape >= Qt::LastCursor || cursor.shape() >= Qt::LastCursor) {
d->extra.value().cursor = cursor;
@@ -7676,7 +8294,7 @@ void QQuickItem::setCursor(const QCursor &cursor)
if (d->window) {
QWindow *renderWindow = QQuickRenderControl::renderWindowFor(d->window);
QWindow *window = renderWindow ? renderWindow : d->window;
- QPointF pos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
+ QPointF pos = window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition);
if (contains(mapFromScene(pos)))
updateCursorPos = pos;
}
@@ -7695,6 +8313,7 @@ void QQuickItem::setCursor(const QCursor &cursor)
void QQuickItem::unsetCursor()
{
Q_D(QQuickItem);
+ qCDebug(lcHoverTrace) << "clearing cursor";
if (!d->hasCursor)
return;
d->hasCursor = false;
@@ -7705,7 +8324,7 @@ void QQuickItem::unsetCursor()
if (d->window) {
QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(d->window);
if (windowPrivate->cursorItem == this) {
- QPointF pos = d->window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
+ QPointF pos = d->window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition);
windowPrivate->updateCursor(pos);
}
}
@@ -7746,33 +8365,88 @@ QCursor QQuickItemPrivate::effectiveCursor(const QQuickPointerHandler *handler)
Returns the Pointer Handler that is currently attempting to set the cursor shape,
or null if there is no such handler.
+ If there are multiple handlers attempting to set the cursor:
+ \list
+ \li an active handler has the highest priority (e.g. a DragHandler being dragged)
+ \li any HoverHandler that is reacting to a non-mouse device has priority for
+ kCursorOverrideTimeout ms (a tablet stylus is jittery so that's enough)
+ \li otherwise a HoverHandler that is reacting to the mouse, if any
+ \endlist
+
+ Within each category, if there are multiple handlers, the last-added one wins
+ (the one that is declared at the bottom wins, because users may intuitively
+ think it's "on top" even though there is no Z-order; or, one that is added
+ in a specific use case overrides an imported component).
+
\sa QtQuick::PointerHandler::cursor
*/
QQuickPointerHandler *QQuickItemPrivate::effectiveCursorHandler() const
{
if (!hasPointerHandlers())
return nullptr;
- QQuickPointerHandler *retHoverHandler = nullptr;
+ QQuickPointerHandler* activeHandler = nullptr;
+ QQuickPointerHandler* mouseHandler = nullptr;
+ QQuickPointerHandler* nonMouseHandler = nullptr;
for (QQuickPointerHandler *h : extra->pointerHandlers) {
if (!h->isCursorShapeExplicitlySet())
continue;
QQuickHoverHandler *hoverHandler = qmlobject_cast<QQuickHoverHandler *>(h);
- // For now, we don't expect multiple hover handlers in one Item, so we choose the first one found;
- // but a future use case could be to have different cursors for different tablet stylus devices.
- // In that case, this function needs more information: which device did the event come from.
- // TODO Qt 6: add QPointerDevice* as argument to this function? (it doesn't exist yet in Qt 5)
- if (!retHoverHandler && hoverHandler)
- retHoverHandler = hoverHandler;
+ // Prioritize any HoverHandler that is reacting to a non-mouse device.
+ // Otherwise, choose the first hovered handler that is found.
+ // TODO maybe: there was an idea to add QPointerDevice* as argument to this function
+ // and check the device type, but why? HoverHandler already does that.
+ if (!activeHandler && hoverHandler && hoverHandler->isHovered()) {
+ qCDebug(lcHoverTrace) << hoverHandler << hoverHandler->acceptedDevices() << "wants to set cursor" << hoverHandler->cursorShape();
+ if (hoverHandler->acceptedDevices().testFlag(QPointingDevice::DeviceType::Mouse)) {
+ // If there's a conflict, the last-added HoverHandler wins. Maybe the user is overriding a default...
+ if (mouseHandler && mouseHandler->cursorShape() != hoverHandler->cursorShape()) {
+ qCDebug(lcHoverTrace) << "mouse cursor conflict:" << mouseHandler << "wants" << mouseHandler->cursorShape()
+ << "but" << hoverHandler << "wants" << hoverHandler->cursorShape();
+ }
+ mouseHandler = hoverHandler;
+ } else {
+ // If there's a conflict, the last-added HoverHandler wins.
+ if (nonMouseHandler && nonMouseHandler->cursorShape() != hoverHandler->cursorShape()) {
+ qCDebug(lcHoverTrace) << "non-mouse cursor conflict:" << nonMouseHandler << "wants" << nonMouseHandler->cursorShape()
+ << "but" << hoverHandler << "wants" << hoverHandler->cursorShape();
+ }
+ nonMouseHandler = hoverHandler;
+ }
+ }
if (!hoverHandler && h->active())
- return h;
+ activeHandler = h;
+ }
+ if (activeHandler) {
+ qCDebug(lcHoverTrace) << "active handler choosing cursor" << activeHandler << activeHandler->cursorShape();
+ return activeHandler;
+ }
+ // Mouse events are often synthetic; so if a HoverHandler for a non-mouse device wanted to set the cursor,
+ // let it win, unless more than kCursorOverrideTimeout ms have passed
+ // since the last time the non-mouse handler actually reacted to an event.
+ // We could miss the fact that a tablet stylus has left proximity, because we don't deliver proximity events to windows.
+ if (nonMouseHandler) {
+ if (mouseHandler) {
+ const bool beforeTimeout =
+ QQuickPointerHandlerPrivate::get(mouseHandler)->lastEventTime <
+ QQuickPointerHandlerPrivate::get(nonMouseHandler)->lastEventTime + kCursorOverrideTimeout;
+ QQuickPointerHandler *winner = (beforeTimeout ? nonMouseHandler : mouseHandler);
+ qCDebug(lcHoverTrace) << "non-mouse handler reacted last time:" << QQuickPointerHandlerPrivate::get(nonMouseHandler)->lastEventTime
+ << "and mouse handler reacted at time:" << QQuickPointerHandlerPrivate::get(mouseHandler)->lastEventTime
+ << "choosing cursor according to" << winner << winner->cursorShape();
+ return winner;
+ }
+ qCDebug(lcHoverTrace) << "non-mouse handler choosing cursor" << nonMouseHandler << nonMouseHandler->cursorShape();
+ return nonMouseHandler;
}
- return retHoverHandler;
+ if (mouseHandler)
+ qCDebug(lcHoverTrace) << "mouse handler choosing cursor" << mouseHandler << mouseHandler->cursorShape();
+ return mouseHandler;
}
#endif
/*!
- \obsolete Use QPointerEvent::setExclusiveGrabber()
+ \deprecated Use QPointerEvent::setExclusiveGrabber().
Grabs the mouse input.
@@ -7802,7 +8476,7 @@ void QQuickItem::grabMouse()
}
/*!
- \obsolete Use QPointerEvent::setExclusiveGrabber()
+ \deprecated Use QPointerEvent::setExclusiveGrabber().
Releases the mouse grab following a call to grabMouse().
@@ -7864,7 +8538,7 @@ void QQuickItem::setKeepMouseGrab(bool keep)
}
/*!
- \obsolete Use QPointerEvent::setExclusiveGrabber().
+ \deprecated Use QPointerEvent::setExclusiveGrabber().
Grabs the touch points specified by \a ids.
These touch points will be owned by the item until
@@ -7887,7 +8561,7 @@ void QQuickItem::grabTouchPoints(const QList<int> &ids)
}
/*!
- \obsolete Use QEventPoint::setExclusiveGrabber() instead.
+ \deprecated Use QEventPoint::setExclusiveGrabber() instead.
Ungrabs the touch points owned by this item.
*/
void QQuickItem::ungrabTouchPoints()
@@ -7934,89 +8608,148 @@ void QQuickItem::setKeepTouchGrab(bool keep)
}
/*!
- \qmlmethod bool QtQuick::Item::contains(point point)
+ \qmlmethod bool QtQuick::Item::contains(point point)
- Returns true if this item contains \a point, which is in local coordinates;
- returns false otherwise.
- */
+ Returns \c true if this item contains \a point, which is in local coordinates;
+ returns \c false otherwise. This is the same check that is used for
+ hit-testing a QEventPoint during event delivery, and is affected by
+ \l containmentMask if it is set.
+*/
/*!
- Returns true if this item contains \a point, which is in local coordinates;
- returns false otherwise.
+ Returns \c true if this item contains \a point, which is in local coordinates;
+ returns \c false otherwise.
- This function can be overwritten in order to handle point collisions in items
- with custom shapes. The default implementation checks if the point is inside
- the item's bounding rect.
+ This function can be overridden in order to handle point collisions in items
+ with custom shapes. The default implementation checks whether the point is inside
+ \l containmentMask() if it is set, or inside the bounding box otherwise.
- Note that this method is generally used to check whether the item is under the mouse cursor,
- and for that reason, the implementation of this function should be as light-weight
- as possible.
+ \note This method is used for hit-testing each QEventPoint during event
+ delivery, so the implementation should be kept as lightweight as possible.
*/
bool QQuickItem::contains(const QPointF &point) const
{
Q_D(const QQuickItem);
- if (d->mask) {
+ if (d->extra.isAllocated() && d->extra->mask) {
+ if (auto quickMask = qobject_cast<QQuickItem *>(d->extra->mask))
+ return quickMask->contains(point - quickMask->position());
+
bool res = false;
- d->extra->maskContains.invoke(d->mask,
+ QMetaMethod maskContains = d->extra->mask->metaObject()->method(d->extra->maskContainsIndex);
+ maskContains.invoke(d->extra->mask,
Qt::DirectConnection,
Q_RETURN_ARG(bool, res),
Q_ARG(QPointF, point));
return res;
- } else {
- qreal x = point.x();
- qreal y = point.y();
- return x >= 0 && y >= 0 && x <= d->width && y <= d->height;
}
+
+ qreal x = point.x();
+ qreal y = point.y();
+ return x >= 0 && y >= 0 && x < d->width && y < d->height;
}
/*!
\qmlproperty QObject* QtQuick::Item::containmentMask
\since 5.11
This property holds an optional mask for the Item to be used in the
- QtQuick::Item::contains method.
- QtQuick::Item::contains main use is currently to determine whether
- an input event has landed into the item or not.
+ \l contains() method. Its main use is currently to determine
+ whether a \l {QPointerEvent}{pointer event} has landed into the item or not.
- By default the \l contains method will return true for any point
- within the Item's bounding box. \c containmentMask allows for a
- more fine-grained control. For example, the developer could
- define and use an AnotherItem element as containmentMask,
- which has a specialized contains method, like:
+ By default the \c contains() method will return true for any point
+ within the Item's bounding box. \c containmentMask allows for
+ more fine-grained control. For example, if a custom C++
+ QQuickItem subclass with a specialized contains() method
+ is used as containmentMask:
\code
Item { id: item; containmentMask: AnotherItem { id: anotherItem } }
\endcode
- \e{item}'s contains method would then return true only if
- \e{anotherItem}'s contains implementation returns true.
+ \e{item}'s contains method would then return \c true only if
+ \e{anotherItem}'s contains() implementation returns \c true.
+
+ A \l Shape can be used as a mask, to make an item react to
+ \l {QPointerEvent}{pointer events} only within a non-rectangular region:
+
+ \table
+ \row
+ \li \image containmentMask-shape.gif
+ \li \snippet qml/item/containmentMask-shape.qml 0
+ \endtable
+
+ It is also possible to define the contains method in QML. For example,
+ to create a circular item that only responds to events within its
+ actual bounds:
+
+ \table
+ \row
+ \li \image containmentMask-circle.gif
+ \li \snippet qml/item/containmentMask-circle-js.qml 0
+ \endtable
+
+ \sa {Qt Quick Examples - Shapes}
+*/
+/*!
+ \property QQuickItem::containmentMask
+ \since 5.11
+ This property holds an optional mask to be used in the contains() method,
+ which is mainly used for hit-testing each \l QPointerEvent.
+
+ By default, \l contains() will return \c true for any point
+ within the Item's bounding box. But any QQuickItem, or any QObject
+ that implements a function of the form
+ \code
+ Q_INVOKABLE bool contains(const QPointF &point) const;
+ \endcode
+ can be used as a mask, to defer hit-testing to that object.
+
+ \note contains() is called frequently during event delivery.
+ Deferring hit-testing to another object slows it down somewhat.
+ containmentMask() can cause performance problems if that object's
+ contains() method is not efficient. If you implement a custom
+ QQuickItem subclass, you can alternatively override contains().
+
+ \sa contains()
*/
QObject *QQuickItem::containmentMask() const
{
Q_D(const QQuickItem);
- return d->mask.data();
+ if (!d->extra.isAllocated())
+ return nullptr;
+ return d->extra->mask.data();
}
void QQuickItem::setContainmentMask(QObject *mask)
{
Q_D(QQuickItem);
+ const bool extraDataExists = d->extra.isAllocated();
// an Item can't mask itself (to prevent infinite loop in contains())
- if (d->mask.data() == mask || mask == static_cast<QObject *>(this))
+ if (mask == static_cast<QObject *>(this))
+ return;
+ // mask is null, and we had no mask
+ if (!extraDataExists && !mask)
+ return;
+ // mask is non-null and the same
+ if (extraDataExists && d->extra->mask == mask)
return;
- QQuickItem *quickMask = qobject_cast<QQuickItem *>(d->mask);
+ QQuickItem *quickMask = d->extra.isAllocated() ? qobject_cast<QQuickItem *>(d->extra->mask)
+ : nullptr;
if (quickMask) {
QQuickItemPrivate *maskPrivate = QQuickItemPrivate::get(quickMask);
maskPrivate->registerAsContainmentMask(this, false); // removed from use as my mask
}
+ if (!extraDataExists)
+ d->extra.value(); // ensure extra exists
if (mask) {
int methodIndex = mask->metaObject()->indexOfMethod(QByteArrayLiteral("contains(QPointF)"));
if (methodIndex < 0) {
qmlWarning(this) << QStringLiteral("QQuickItem: Object set as mask does not have an invokable contains method, ignoring it.");
return;
}
- d->extra.value().maskContains = mask->metaObject()->method(methodIndex);
+ d->extra->maskContainsIndex = methodIndex;
}
- d->mask = mask;
+ d->extra->mask = mask;
quickMask = qobject_cast<QQuickItem *>(mask);
if (quickMask) {
QQuickItemPrivate *maskPrivate = QQuickItemPrivate::get(quickMask);
@@ -8032,7 +8765,7 @@ void QQuickItem::setContainmentMask(QObject *mask)
\input item.qdocinc mapping
- If \a item is 0, this maps \a point to the coordinate system of the
+ If \a item is \nullptr, this maps \a point to the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
@@ -8040,8 +8773,13 @@ void QQuickItem::setContainmentMask(QObject *mask)
QPointF QQuickItem::mapToItem(const QQuickItem *item, const QPointF &point) const
{
QPointF p = mapToScene(point);
- if (item)
+ if (item) {
+ const QQuickWindow *itemWindow = item->window();
+ if (itemWindow != nullptr && itemWindow != window())
+ p = itemWindow->mapFromGlobal(window()->mapToGlobal(p));
+
p = item->mapFromScene(p);
+ }
return p;
}
@@ -8090,7 +8828,7 @@ QPointF QQuickItem::mapToGlobal(const QPointF &point) const
\input item.qdocinc mapping
- If \a item is 0, this maps \a rect to the coordinate system of the
+ If \a item is \nullptr, this maps \a rect to the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
@@ -8126,14 +8864,20 @@ QRectF QQuickItem::mapRectToScene(const QRectF &rect) const
\input item.qdocinc mapping
- If \a item is 0, this maps \a point from the coordinate system of the
+ If \a item is \nullptr, this maps \a point from the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QPointF QQuickItem::mapFromItem(const QQuickItem *item, const QPointF &point) const
{
- QPointF p = item?item->mapToScene(point):point;
+ QPointF p = point;
+ if (item) {
+ p = item->mapToScene(point);
+
+ if (item->window() != window())
+ p = window()->mapFromGlobal(item->window()->mapToGlobal(p));
+ }
return mapFromScene(p);
}
@@ -8165,6 +8909,12 @@ QPointF QQuickItem::mapFromScene(const QPointF &point) const
treated only as a hint. So, the resulting window position may differ from
what is expected.
+ \note If this item is in a subscene, e.g. mapped onto a 3D
+ \l [QtQuick3D QML] {Model}{Model} object, the UV mapping is incorporated
+ into this transformation, so that it really goes from screen coordinates to
+ this item's coordinates, as long as \a point is actually within this item's bounds.
+ The other mapping functions do not yet work that way.
+
\since 5.7
\sa {Concepts - Visual Coordinates in Qt Quick}
@@ -8172,7 +8922,12 @@ QPointF QQuickItem::mapFromScene(const QPointF &point) const
QPointF QQuickItem::mapFromGlobal(const QPointF &point) const
{
Q_D(const QQuickItem);
- return mapFromScene(d->globalToWindowTransform().map(point));
+ QPointF scenePoint = d->globalToWindowTransform().map(point);
+ if (auto da = QQuickDeliveryAgentPrivate::currentOrItemDeliveryAgent(this)) {
+ if (auto sceneTransform = da->sceneTransform())
+ scenePoint = sceneTransform->map(scenePoint);
+ }
+ return mapFromScene(scenePoint);
}
/*!
@@ -8182,7 +8937,7 @@ QPointF QQuickItem::mapFromGlobal(const QPointF &point) const
\input item.qdocinc mapping
- If \a item is 0, this maps \a rect from the coordinate system of the
+ If \a item is \nullptr, this maps \a rect from the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
@@ -8268,12 +9023,6 @@ bool QQuickItem::event(QEvent *ev)
Q_D(QQuickItem);
switch (ev->type()) {
-#if 0
- case QEvent::PolishRequest:
- d->polishScheduled = false;
- updatePolish();
- break;
-#endif
#if QT_CONFIG(im)
case QEvent::InputMethodQuery: {
QInputMethodQueryEvent *query = static_cast<QInputMethodQueryEvent *>(ev);
@@ -8296,8 +9045,14 @@ bool QQuickItem::event(QEvent *ev)
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
case QEvent::TouchCancel:
- touchEvent(static_cast<QTouchEvent*>(ev));
- break;
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+#if QT_CONFIG(wheelevent)
+ case QEvent::Wheel:
+#endif
+ d->deliverPointerEvent(ev);
+ break;
case QEvent::StyleAnimationUpdate:
if (isVisible()) {
ev->accept();
@@ -8329,20 +9084,6 @@ bool QQuickItem::event(QEvent *ev)
case QEvent::MouseMove:
mouseMoveEvent(static_cast<QMouseEvent*>(ev));
break;
- case QEvent::MouseButtonPress:
- mousePressEvent(static_cast<QMouseEvent*>(ev));
- break;
- case QEvent::MouseButtonRelease:
- mouseReleaseEvent(static_cast<QMouseEvent*>(ev));
- break;
- case QEvent::MouseButtonDblClick:
- mouseDoubleClickEvent(static_cast<QMouseEvent*>(ev));
- break;
-#if QT_CONFIG(wheelevent)
- case QEvent::Wheel:
- wheelEvent(static_cast<QWheelEvent*>(ev));
- break;
-#endif
#if QT_CONFIG(quick_draganddrop)
case QEvent::DragEnter:
dragEnterEvent(static_cast<QDragEnterEvent*>(ev));
@@ -8363,7 +9104,19 @@ bool QQuickItem::event(QEvent *ev)
break;
#endif // gestures
case QEvent::LanguageChange:
- for (QQuickItem *item : d->childItems)
+ case QEvent::LocaleChange:
+ for (QQuickItem *item : std::as_const(d->childItems))
+ QCoreApplication::sendEvent(item, ev);
+ break;
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate:
+ if (d->providesPalette())
+ d->setCurrentColorGroup();
+ for (QQuickItem *item : std::as_const(d->childItems))
+ QCoreApplication::sendEvent(item, ev);
+ break;
+ case QEvent::ApplicationPaletteChange:
+ for (QQuickItem *item : std::as_const(d->childItems))
QCoreApplication::sendEvent(item, ev);
break;
default:
@@ -8374,19 +9127,32 @@ bool QQuickItem::event(QEvent *ev)
}
#ifndef QT_NO_DEBUG_STREAM
-// FIXME: Qt 6: Make this QDebug operator<<(QDebug debug, const QQuickItem *item)
-QDebug operator<<(QDebug debug, QQuickItem *item)
+QDebug operator<<(QDebug debug,
+#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
+ const
+#endif
+ QQuickItem *item)
{
QDebugStateSaver saver(debug);
debug.nospace();
if (!item) {
- debug << "QQuickItem(0)";
+ debug << "QQuickItem(nullptr)";
return debug;
}
const QRectF rect(item->position(), QSizeF(item->width(), item->height()));
debug << item->metaObject()->className() << '(' << static_cast<void *>(item);
+
+ // Deferred properties will cause recursion when calling nameForObject
+ // before the component is completed, so guard against this situation.
+ if (item->isComponentComplete()) {
+ if (QQmlContext *context = qmlContext(item)) {
+ const auto objectId = context->nameForObject(item);
+ if (!objectId.isEmpty())
+ debug << ", id=" << objectId;
+ }
+ }
if (!item->objectName().isEmpty())
debug << ", name=" << item->objectName();
debug << ", parent=" << static_cast<void *>(item->parentItem())
@@ -8394,10 +9160,14 @@ QDebug operator<<(QDebug debug, QQuickItem *item)
QtDebugUtils::formatQRect(debug, rect);
if (const qreal z = item->z())
debug << ", z=" << z;
+ if (item->flags().testFlag(QQuickItem::ItemIsViewport))
+ debug << " \U0001f5bc"; // frame with picture
+ if (item->flags().testFlag(QQuickItem::ItemObservesViewport))
+ debug << " \u23ff"; // observer eye
debug << ')';
return debug;
}
-#endif
+#endif // QT_NO_DEBUG_STREAM
/*!
\fn bool QQuickItem::isTextureProvider() const
@@ -8478,26 +9248,24 @@ QSGTextureProvider *QQuickItem::textureProvider() const
}
\endcode
- \sa Window::palette, Popup::palette, ColorGroup, Palette
+ \sa Window::palette, Popup::palette, ColorGroup, Palette, SystemPalette
*/
+#if QT_CONFIG(quick_shadereffect)
/*!
\property QQuickItem::layer
\internal
*/
QQuickItemLayer *QQuickItemPrivate::layer() const
{
-#if QT_CONFIG(quick_shadereffect)
if (!extra.isAllocated() || !extra->layer) {
extra.value().layer = new QQuickItemLayer(const_cast<QQuickItem *>(q_func()));
if (!componentComplete)
extra->layer->classBegin();
}
return extra->layer;
-#else
- return 0;
-#endif
}
+#endif
/*!
\internal
@@ -8545,14 +9313,14 @@ void QQuickItemPrivate::localizedTouchEvent(const QTouchEvent *event, bool isFil
bool hasAnotherGrabber = pointGrabber && pointGrabber != q;
// if there's no exclusive grabber, look for passive grabbers during filtering
if (isFiltering && !pointGrabber) {
- auto pg = event->passiveGrabbers(p);
+ const auto pg = event->passiveGrabbers(p);
if (!pg.isEmpty()) {
// It seems unlikely to have multiple passive grabbers of one eventpoint with different grandparents.
// So hopefully if we start from one passive grabber and go up the parent chain from there,
// we will find any filtering parent items that exist.
- auto handler = qmlobject_cast<QQuickPointerHandler *>(pg.first());
- Q_ASSERT(handler);
- pointGrabber = handler->parentItem();
+ auto handler = qmlobject_cast<QQuickPointerHandler *>(pg.constFirst());
+ if (handler)
+ pointGrabber = handler->parentItem();
}
}
@@ -8573,12 +9341,11 @@ void QQuickItemPrivate::localizedTouchEvent(const QTouchEvent *event, bool isFil
if ((p.state() == QEventPoint::State::Pressed || p.state() == QEventPoint::State::Released) && isInside)
anyPressOrReleaseInside = true;
QEventPoint pCopy(p);
- QMutableEventPoint mut = QMutableEventPoint::from(pCopy);
eventStates |= p.state();
if (p.state() == QEventPoint::State::Released)
- mut.detach();
- mut.setPosition(localPos);
- touchPoints << mut;
+ QMutableEventPoint::detach(pCopy);
+ QMutableEventPoint::setPosition(pCopy, localPos);
+ touchPoints.append(std::move(pCopy));
}
// Now touchPoints will have only points which are inside the item.
@@ -8614,25 +9381,46 @@ bool QQuickItemPrivate::hasPointerHandlers() const
return extra.isAllocated() && !extra->pointerHandlers.isEmpty();
}
-bool QQuickItemPrivate::hasHoverHandlers() const
+bool QQuickItemPrivate::hasEnabledHoverHandlers() const
{
if (!hasPointerHandlers())
return false;
for (QQuickPointerHandler *h : extra->pointerHandlers)
- if (qmlobject_cast<QQuickHoverHandler *>(h))
+ if (auto *hh = qmlobject_cast<QQuickHoverHandler *>(h); hh && hh->enabled())
return true;
return false;
}
void QQuickItemPrivate::addPointerHandler(QQuickPointerHandler *h)
{
+ Q_ASSERT(h);
Q_Q(QQuickItem);
// Accept all buttons, and leave filtering to pointerEvent() and/or user JS,
// because there can be multiple handlers...
- q->setAcceptedMouseButtons(Qt::AllButtons);
+ extra.value().acceptedMouseButtons = Qt::AllButtons;
auto &handlers = extra.value().pointerHandlers;
if (!handlers.contains(h))
handlers.prepend(h);
+ auto &res = extra.value().resourcesList;
+ if (!res.contains(h)) {
+ res.append(h);
+ QObject::connect(h, &QObject::destroyed, q, [this](QObject *o) {
+ _q_resourceObjectDeleted(o);
+ });
+ }
+}
+
+void QQuickItemPrivate::removePointerHandler(QQuickPointerHandler *h)
+{
+ Q_ASSERT(h);
+ Q_Q(QQuickItem);
+ auto &handlers = extra.value().pointerHandlers;
+ handlers.removeOne(h);
+ auto &res = extra.value().resourcesList;
+ res.removeOne(h);
+ QObject::disconnect(h, &QObject::destroyed, q, nullptr);
+ if (handlers.isEmpty())
+ extra.value().acceptedMouseButtons = extra.value().acceptedMouseButtonsWithoutHandlers;
}
#if QT_CONFIG(quick_shadereffect)
@@ -8641,9 +9429,10 @@ QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
, m_enabled(false)
, m_mipmap(false)
, m_smooth(false)
+ , m_live(true)
, m_componentComplete(true)
, m_wrapMode(QQuickShaderEffectSource::ClampToEdge)
- , m_format(QQuickShaderEffectSource::RGBA)
+ , m_format(QQuickShaderEffectSource::RGBA8)
, m_name("source")
, m_effectComponent(nullptr)
, m_effect(nullptr)
@@ -8718,6 +9507,7 @@ void QQuickItemLayer::activate()
m_effectSource->setSourceItem(m_item);
m_effectSource->setHideSource(true);
m_effectSource->setSmooth(m_smooth);
+ m_effectSource->setLive(m_live);
m_effectSource->setTextureSize(m_size);
m_effectSource->setSourceRect(m_sourceRect);
m_effectSource->setMipmap(m_mipmap);
@@ -8857,20 +9647,16 @@ void QQuickItemLayer::setMipmap(bool mipmap)
/*!
\qmlproperty enumeration QtQuick::Item::layer.format
- This property defines the internal format of the texture.
+ This property defines the format of the backing texture.
Modifying this property makes most sense when the \a layer.effect is also
- specified. Depending on the OpenGL implementation, this property might
- allow you to save some texture memory.
-
- \list
- \li ShaderEffectSource.Alpha - GL_ALPHA;
- \li ShaderEffectSource.RGB - GL_RGB
- \li ShaderEffectSource.RGBA - GL_RGBA
- \endlist
+ specified.
- \note ShaderEffectSource.RGB and ShaderEffectSource.Alpha should
- be used with caution, as support for these formats in the underlying
- hardware and driver is often not present.
+ \value ShaderEffectSource.RGBA8
+ \value ShaderEffectSource.RGBA16F
+ \value ShaderEffectSource.RGBA32F
+ \value ShaderEffectSource.Alpha Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
+ \value ShaderEffectSource.RGB Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
+ \value ShaderEffectSource.RGBA Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
\sa {Item Layers}
*/
@@ -8936,6 +9722,30 @@ void QQuickItemLayer::setSmooth(bool s)
}
/*!
+ \qmlproperty bool QtQuick::Item::layer.live
+ \since 6.5
+
+ When this property is true the layer texture is updated whenever the
+ item updates. Otherwise it will always be a frozen image.
+
+ By default, this property is set to \c true.
+
+ \sa {Item Layers}
+ */
+
+void QQuickItemLayer::setLive(bool live)
+{
+ if (m_live == live)
+ return;
+ m_live = live;
+
+ if (m_effectSource)
+ m_effectSource->setLive(m_live);
+
+ emit liveChanged(live);
+}
+
+/*!
\qmlproperty size QtQuick::Item::layer.textureSize
This property holds the requested pixel size of the layers texture. If it is empty,
@@ -8967,12 +9777,10 @@ void QQuickItemLayer::setSize(const QSize &size)
Modifying this property makes most sense when the \a layer.effect is
specified.
- \list
- \li ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
- \li ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
- \li ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
- \li ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
- \endlist
+ \value ShaderEffectSource.ClampToEdge GL_CLAMP_TO_EDGE both horizontally and vertically
+ \value ShaderEffectSource.RepeatHorizontally GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
+ \value ShaderEffectSource.RepeatVertically GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
+ \value ShaderEffectSource.Repeat GL_REPEAT both horizontally and vertically
\note Some OpenGL ES 2 implementations do not support the GL_REPEAT
wrap mode with non-power-of-two textures.
@@ -9002,11 +9810,9 @@ void QQuickItemLayer::setWrapMode(QQuickShaderEffectSource::WrapMode mode)
such as those specified by ShaderEffect. If no effect is specified for the layered
item, mirroring has no effect on the UI representation of the item.
- \list
- \li ShaderEffectSource.NoMirroring - No mirroring
- \li ShaderEffectSource.MirrorHorizontally - The generated texture is flipped along X-axis.
- \li ShaderEffectSource.MirrorVertically - The generated texture is flipped along Y-axis.
- \endlist
+ \value ShaderEffectSource.NoMirroring No mirroring
+ \value ShaderEffectSource.MirrorHorizontally The generated texture is flipped along X-axis.
+ \value ShaderEffectSource.MirrorVertically The generated texture is flipped along Y-axis.
*/
void QQuickItemLayer::setTextureMirroring(QQuickShaderEffectSource::TextureMirroring mirroring)
@@ -9120,7 +9926,8 @@ void QQuickItemLayer::itemSiblingOrderChanged(QQuickItem *)
void QQuickItemLayer::itemVisibilityChanged(QQuickItem *)
{
QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource;
- Q_ASSERT(l);
+ if (!l)
+ return;
l->setVisible(m_item->isVisible());
}
@@ -9129,22 +9936,27 @@ void QQuickItemLayer::updateZ()
if (!m_componentComplete || !m_enabled)
return;
QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource;
- Q_ASSERT(l);
+ if (!l)
+ return;
l->setZ(m_item->z());
}
void QQuickItemLayer::updateOpacity()
{
QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource;
- Q_ASSERT(l);
+ if (!l)
+ return;
l->setOpacity(m_item->opacity());
}
void QQuickItemLayer::updateGeometry()
{
QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource;
- Q_ASSERT(l);
- QRectF bounds = m_item->clipRect();
+ if (!l)
+ return;
+ // 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());
}
@@ -9156,7 +9968,8 @@ void QQuickItemLayer::updateMatrix()
if (!m_componentComplete || !m_enabled)
return;
QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource;
- Q_ASSERT(l);
+ if (!l)
+ return;
QQuickItemPrivate *ld = QQuickItemPrivate::get(l);
l->setScale(m_item->scale());
l->setRotation(m_item->rotation());
@@ -9185,13 +9998,20 @@ QQuickItemPrivate::ExtraData::ExtraData()
#if QT_CONFIG(accessibility)
-QAccessible::Role QQuickItemPrivate::accessibleRole() const
+QAccessible::Role QQuickItemPrivate::effectiveAccessibleRole() const
{
Q_Q(const QQuickItem);
- QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, false));
- if (accessibleAttached)
- return accessibleAttached->role();
+ auto *attached = qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, false);
+ auto role = QAccessible::NoRole;
+ if (auto *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(attached))
+ role = accessibleAttached->role();
+ if (role == QAccessible::NoRole)
+ role = accessibleRole();
+ return role;
+}
+QAccessible::Role QQuickItemPrivate::accessibleRole() const
+{
return QAccessible::NoRole;
}
#endif
@@ -9216,17 +10036,59 @@ void QV4::Heap::QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkS
{
QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
if (QQuickItem *item = static_cast<QQuickItem*>(This->object())) {
- for (QQuickItem *child : qAsConst(QQuickItemPrivate::get(item)->childItems))
+ for (QQuickItem *child : std::as_const(QQuickItemPrivate::get(item)->childItems))
QV4::QObjectWrapper::markWrapper(child, markStack);
}
QObjectWrapper::markObjects(that, markStack);
}
-quint64 QQuickItemPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
+quint64 QQuickItemPrivate::_q_createJSWrapper(QQmlV4ExecutionEnginePtr engine)
{
return (engine->memoryManager->allocate<QQuickItemWrapper>(q_func()))->asReturnedValue();
}
+QDebug operator<<(QDebug debug, const QQuickItemPrivate::ChangeListener &listener)
+{
+ QDebugStateSaver stateSaver(debug);
+ debug.nospace() << "ChangeListener listener=" << listener.listener << " types=" << listener.types;
+ return debug;
+}
+
+//! \internal
+QPointF QQuickItem::mapFromItem(const QQuickItem *item, qreal x, qreal y)
+{ return mapFromItem(item, QPointF(x, y) ); }
+
+//! \internal
+QRectF QQuickItem::mapFromItem(const QQuickItem *item, const QRectF &rect) const
+{ return mapRectFromItem(item, rect); }
+
+//! \internal
+QRectF QQuickItem::mapFromItem(const QQuickItem *item, qreal x, qreal y, qreal width, qreal height) const
+{ return mapFromItem(item, QRectF(x, y, width, height)); }
+
+//! \internal
+QPointF QQuickItem::mapToItem(const QQuickItem *item, qreal x, qreal y)
+{ return mapToItem(item, QPoint(x, y)); }
+
+//! \internal
+QRectF QQuickItem::mapToItem(const QQuickItem *item, const QRectF &rect) const
+{ return mapRectToItem(item, rect); }
+
+//! \internal
+QRectF QQuickItem::mapToItem(const QQuickItem *item, qreal x, qreal y, qreal width, qreal height) const
+{ return mapToItem(item, QRectF(x, y, width, height)); }
+
+//! \internal
+QPointF QQuickItem::mapToGlobal(qreal x, qreal y) const
+{ return mapToGlobal(QPointF(x, y)); }
+
+//! \internal
+QPointF QQuickItem::mapFromGlobal(qreal x, qreal y) const
+{ return mapFromGlobal(QPointF(x, y)); }
+
+//! \internal
+QQuickItemChangeListener::~QQuickItemChangeListener() = default;
+
QT_END_NAMESPACE
#include <moc_qquickitem.cpp>