Source code

Revision control

Copy as Markdown

// 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
// Qt-Security score:significant reason:default
#include "qquickitem.h"
#include "qquickwindow.h"
#include "qquickrendercontrol.h"
#include <QtQml/qjsengine.h>
#include "qquickwindow_p.h"
#include "qquickevents_p_p.h"
#include "qquickscreen_p.h"
#include "qquicksafearea_p.h"
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include <QtQml/qqmlinfo.h>
#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>
#include <QtCore/qcoreevent.h>
#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>
#include <QtQuick/private/qquickstategroup_p.h>
#include <private/qqmlopenmetaobject_p.h>
#include <QtQuick/private/qquickstate_p.h>
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
#include <QtQuick/private/qquickattachedpropertypropagator_p.h>
#include <QtQuick/private/qquickhoverhandler_p.h>
#include <QtQuick/private/qquickpointerhandler_p.h>
#include <QtQuick/private/qquickpointerhandler_p_p.h>
#include <private/qv4engine_p.h>
#include <private/qv4object_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <private/qdebug_p.h>
#include <private/qqmlvaluetypewrapper_p.h>
#if QT_CONFIG(cursor)
# include <QtGui/qcursor.h>
#endif
#if QT_CONFIG(accessibility)
# include <private/qaccessiblecache_p.h>
#endif
#include <QtCore/qpointer.h>
#include <algorithm>
#include <limits>
// XXX todo Check that elements that create items handle memory correctly after visual ownership change
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcHandlerParent, "qt.quick.handler.parent")
Q_LOGGING_CATEGORY(lcVP, "qt.quick.viewport")
Q_STATIC_LOGGING_CATEGORY(lcEffClip, "qt.quick.effectiveclip")
Q_STATIC_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)
{
if (lcFocus().isEnabled(QtDebugMsg)) {
qCDebug(lcFocus)
<< QByteArray(depth, '\t').constData()
<< (scope && QQuickItemPrivate::get(scope)->subFocusItem == item ? '*' : ' ')
<< item->hasFocus()
<< item->hasActiveFocus()
<< item->isFocusScope()
<< item;
const auto childItems = item->childItems();
for (QQuickItem *child : childItems) {
debugFocusTree(
child,
item->isFocusScope() || !scope ? item : scope,
item->isFocusScope() || !scope ? depth + 1 : depth);
}
}
}
static void setActiveFocus(QQuickItem *item, Qt::FocusReason reason)
{
QQuickItemPrivate *d = QQuickItemPrivate::get(item);
if (d->subFocusItem && d->window && d->flags & QQuickItem::ItemIsFocusScope)
d->deliveryAgentPrivate()->clearFocusInScope(item, d->subFocusItem, reason);
item->forceActiveFocus(reason);
}
/*!
\qmltype Transform
\nativetype QQuickTransform
\inqmlmodule QtQuick
\ingroup qtquick-visual-transforms
\brief For specifying advanced transformations on Items.
The Transform type is a base type which cannot be instantiated directly.
The following concrete Transform types are available:
\list
\li \l Rotation
\li \l Scale
\li \l Translate
\li \l Shear
\li \l Matrix4x4
\endlist
The Transform types let you create and control advanced transformations that can be configured
independently using specialized properties.
You can assign any number of Transforms to an \l Item. Each Transform is applied in order,
one at a time.
*/
QQuickTransformPrivate::QQuickTransformPrivate()
{
}
QQuickTransform::QQuickTransform(QObject *parent)
: QObject(*(new QQuickTransformPrivate), parent)
{
}
QQuickTransform::QQuickTransform(QQuickTransformPrivate &dd, QObject *parent)
: QObject(dd, parent)
{
}
QQuickTransform::~QQuickTransform()
{
Q_D(QQuickTransform);
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);
}
}
void QQuickTransform::update()
{
Q_D(QQuickTransform);
for (int ii = 0; ii < d->items.size(); ++ii) {
QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii));
p->dirty(QQuickItemPrivate::Transform);
}
}
QQuickContents::QQuickContents(QQuickItem *item)
: m_item(item)
{
}
QQuickContents::~QQuickContents()
{
inDestructor = true;
QList<QQuickItem *> children = m_item->childItems();
for (int i = 0; i < children.size(); ++i) {
QQuickItem *child = children.at(i);
QQuickItemPrivate::get(child)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
}
}
bool QQuickContents::calcHeight(QQuickItem *changed)
{
qreal oldy = m_contents.y();
qreal oldheight = m_contents.height();
if (changed) {
qreal top = oldy;
qreal bottom = oldy + oldheight;
qreal y = changed->y();
if (y + changed->height() > bottom)
bottom = y + changed->height();
if (y < top)
top = y;
m_contents.setY(top);
m_contents.setHeight(bottom - top);
} else {
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.size(); ++i) {
QQuickItem *child = children.at(i);
qreal y = child->y();
if (y + child->height() > bottom)
bottom = y + child->height();
if (y < top)
top = y;
}
if (!children.isEmpty())
m_contents.setY(top);
m_contents.setHeight(qMax(bottom - top, qreal(0.0)));
}
return (m_contents.height() != oldheight || m_contents.y() != oldy);
}
bool QQuickContents::calcWidth(QQuickItem *changed)
{
qreal oldx = m_contents.x();
qreal oldwidth = m_contents.width();
if (changed) {
qreal left = oldx;
qreal right = oldx + oldwidth;
qreal x = changed->x();
if (x + changed->width() > right)
right = x + changed->width();
if (x < left)
left = x;
m_contents.setX(left);
m_contents.setWidth(right - left);
} else {
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.size(); ++i) {
QQuickItem *child = children.at(i);
qreal x = child->x();
if (x + child->width() > right)
right = x + child->width();
if (x < left)
left = x;
}
if (!children.isEmpty())
m_contents.setX(left);
m_contents.setWidth(qMax(right - left, qreal(0.0)));
}
return (m_contents.width() != oldwidth || m_contents.x() != oldx);
}
void QQuickContents::complete()
{
QQuickItemPrivate::get(m_item)->addItemChangeListener(this, QQuickItemPrivate::Children);
QList<QQuickItem *> children = m_item->childItems();
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?
}
calcGeometry();
}
void QQuickContents::updateRect()
{
QQuickItemPrivate::get(m_item)->emitChildrenRectChanged(rectF());
}
void QQuickContents::itemGeometryChanged(QQuickItem *changed, QQuickGeometryChange change, const QRectF &)
{
Q_UNUSED(changed);
bool wChanged = false;
bool hChanged = false;
//### we can only pass changed if the left edge has moved left, or the right edge has moved right
if (change.horizontalChange())
wChanged = calcWidth(/*changed*/);
if (change.verticalChange())
hChanged = calcHeight(/*changed*/);
if (wChanged || hChanged)
updateRect();
}
void QQuickContents::itemDestroyed(QQuickItem *item)
{
if (item)
QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
calcGeometry();
}
void QQuickContents::itemChildRemoved(QQuickItem *, QQuickItem *item)
{
if (item)
QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
calcGeometry();
}
void QQuickContents::itemChildAdded(QQuickItem *, QQuickItem *item)
{
if (item)
QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
calcGeometry(item);
}
QQuickItemKeyFilter::QQuickItemKeyFilter(QQuickItem *item)
: m_processPost(false), m_next(nullptr)
{
QQuickItemPrivate *p = item?QQuickItemPrivate::get(item):nullptr;
if (p) {
m_next = p->extra.value().keyHandler;
p->extra->keyHandler = this;
}
}
QQuickItemKeyFilter::~QQuickItemKeyFilter()
{
}
void QQuickItemKeyFilter::keyPressed(QKeyEvent *event, bool post)
{
if (m_next) m_next->keyPressed(event, post);
}
void QQuickItemKeyFilter::keyReleased(QKeyEvent *event, bool post)
{
if (m_next) m_next->keyReleased(event, post);
}
#if QT_CONFIG(im)
void QQuickItemKeyFilter::inputMethodEvent(QInputMethodEvent *event, bool post)
{
if (m_next)
m_next->inputMethodEvent(event, post);
else
event->ignore();
}
QVariant QQuickItemKeyFilter::inputMethodQuery(Qt::InputMethodQuery query) const
{
if (m_next) return m_next->inputMethodQuery(query);
return QVariant();
}
#endif // im
void QQuickItemKeyFilter::shortcutOverrideEvent(QKeyEvent *event)
{
if (m_next)
m_next->shortcutOverrideEvent(event);
else
event->ignore();
}
void QQuickItemKeyFilter::componentComplete()
{
if (m_next) m_next->componentComplete();
}
/*!
\qmltype KeyNavigation
\nativetype QQuickKeyNavigationAttached
\inqmlmodule QtQuick
\ingroup qtquick-input-handlers
\brief Supports key navigation by arrow keys.
Key-based user interfaces commonly allow the use of arrow keys to navigate between
focusable items. The KeyNavigation attaching type enables this behavior by providing a
convenient way to specify the item that should gain focus when an arrow or tab key is pressed.
The following example provides key navigation for a 2x2 grid of items:
\snippet qml/keynavigation.qml 0
The top-left item initially receives focus by setting \l {Item::}{focus} to
\c true. When an arrow key is pressed, the focus will move to the
appropriate item, as defined by the value that has been set for
the KeyNavigation \l left, \l right, \l up or \l down properties.
Note that if a KeyNavigation attaching type receives the key press and release
events for a requested arrow or tab key, the event is accepted and does not
propagate any further.
By default, KeyNavigation receives key events after the item to which it is attached.
If the item accepts the key event, the KeyNavigation attaching type will not
receive an event for that key. Setting the \l priority property to
\c KeyNavigation.BeforeItem allows the event to be used for key navigation
before the item, rather than after.
If the item to which the focus is switching is not enabled or visible, an attempt will
be made to skip this item and focus on the next. This is possible if there are
a chain of items with the same KeyNavigation handler. If multiple items in a row are not enabled
or visible, they will also be skipped.
KeyNavigation will implicitly set the other direction to return focus to this item. So if you set
\l left to another item, \l right will be set on that item's KeyNavigation to set focus back to this
item. However, if that item's KeyNavigation has had right explicitly set then no change will occur.
This means that the example above could achieve the same behavior without specifying
KeyNavigation.right or KeyNavigation.down for any of the items.
\sa {Keys}{Keys attached property}
*/
/*!
\qmlattachedproperty Item QtQuick::KeyNavigation::left
This property holds the item to assign focus to
when the left cursor key is pressed.
*/
/*!
\qmlattachedproperty Item QtQuick::KeyNavigation::right
This property holds the item to assign focus to
when the right cursor key is pressed.
*/
/*!
\qmlattachedproperty Item QtQuick::KeyNavigation::up
This property holds the item to assign focus to
when the up cursor key is pressed.
*/
/*!
\qmlattachedproperty Item QtQuick::KeyNavigation::down
This property holds the item to assign focus to
when the down cursor key is pressed.
*/
/*!
\qmlattachedproperty Item QtQuick::KeyNavigation::tab
This property holds the item to assign focus to
when the Tab key is pressed.
*/
/*!
\qmlattachedproperty Item QtQuick::KeyNavigation::backtab
This property holds the item to assign focus to
when the Shift+Tab key combination (Backtab) is pressed.
*/
QQuickKeyNavigationAttached::QQuickKeyNavigationAttached(QObject *parent)
: QObject(*(new QQuickKeyNavigationAttachedPrivate), parent),
QQuickItemKeyFilter(qmlobject_cast<QQuickItem*>(parent))
{
m_processPost = true;
}
QQuickKeyNavigationAttached *
QQuickKeyNavigationAttached::qmlAttachedProperties(QObject *obj)
{
return new QQuickKeyNavigationAttached(obj);
}
QQuickItem *QQuickKeyNavigationAttached::left() const
{
Q_D(const QQuickKeyNavigationAttached);
return d->left;
}
void QQuickKeyNavigationAttached::setLeft(QQuickItem *i)
{
Q_D(QQuickKeyNavigationAttached);
if (d->leftSet && d->left == i)
return;
d->leftSet = d->left != i;
d->left = i;
QQuickKeyNavigationAttached* other =
qobject_cast<QQuickKeyNavigationAttached*>(qmlAttachedPropertiesObject<QQuickKeyNavigationAttached>(i));
if (other && !other->d_func()->rightSet){
other->d_func()->right = qobject_cast<QQuickItem*>(parent());
emit other->rightChanged();
}
emit leftChanged();
}
QQuickItem *QQuickKeyNavigationAttached::right() const
{
Q_D(const QQuickKeyNavigationAttached);
return d->right;
}
void QQuickKeyNavigationAttached::setRight(QQuickItem *i)
{
Q_D(QQuickKeyNavigationAttached);
if (d->rightSet && d->right == i)
return;
d->rightSet = d->right != i;
d->right = i;
QQuickKeyNavigationAttached* other =
qobject_cast<QQuickKeyNavigationAttached*>(qmlAttachedPropertiesObject<QQuickKeyNavigationAttached>(i));
if (other && !other->d_func()->leftSet){
other->d_func()->left = qobject_cast<QQuickItem*>(parent());
emit other->leftChanged();
}
emit rightChanged();
}
QQuickItem *QQuickKeyNavigationAttached::up() const
{
Q_D(const QQuickKeyNavigationAttached);
return d->up;
}
void QQuickKeyNavigationAttached::setUp(QQuickItem *i)
{
Q_D(QQuickKeyNavigationAttached);
if (d->upSet && d->up == i)
return;
d->upSet = d->up != i;
d->up = i;
QQuickKeyNavigationAttached* other =
qobject_cast<QQuickKeyNavigationAttached*>(qmlAttachedPropertiesObject<QQuickKeyNavigationAttached>(i));
if (other && !other->d_func()->downSet){
other->d_func()->down = qobject_cast<QQuickItem*>(parent());
emit other->downChanged();
}
emit upChanged();
}
QQuickItem *QQuickKeyNavigationAttached::down() const
{
Q_D(const QQuickKeyNavigationAttached);
return d->down;
}
void QQuickKeyNavigationAttached::setDown(QQuickItem *i)
{
Q_D(QQuickKeyNavigationAttached);
if (d->downSet && d->down == i)
return;
d->downSet = d->down != i;
d->down = i;
QQuickKeyNavigationAttached* other =
qobject_cast<QQuickKeyNavigationAttached*>(qmlAttachedPropertiesObject<QQuickKeyNavigationAttached>(i));
if (other && !other->d_func()->upSet) {
other->d_func()->up = qobject_cast<QQuickItem*>(parent());
emit other->upChanged();
}
emit downChanged();
}
QQuickItem *QQuickKeyNavigationAttached::tab() const
{
Q_D(const QQuickKeyNavigationAttached);
return d->tab;
}
void QQuickKeyNavigationAttached::setTab(QQuickItem *i)
{
Q_D(QQuickKeyNavigationAttached);
if (d->tabSet && d->tab == i)
return;
d->tabSet = d->tab != i;
d->tab = i;
QQuickKeyNavigationAttached* other =
qobject_cast<QQuickKeyNavigationAttached*>(qmlAttachedPropertiesObject<QQuickKeyNavigationAttached>(i));
if (other && !other->d_func()->backtabSet) {
other->d_func()->backtab = qobject_cast<QQuickItem*>(parent());
emit other->backtabChanged();
}
emit tabChanged();
}
QQuickItem *QQuickKeyNavigationAttached::backtab() const
{
Q_D(const QQuickKeyNavigationAttached);
return d->backtab;
}
void QQuickKeyNavigationAttached::setBacktab(QQuickItem *i)
{
Q_D(QQuickKeyNavigationAttached);
if (d->backtabSet && d->backtab == i)
return;
d->backtabSet = d->backtab != i;
d->backtab = i;
QQuickKeyNavigationAttached* other =
qobject_cast<QQuickKeyNavigationAttached*>(qmlAttachedPropertiesObject<QQuickKeyNavigationAttached>(i));
if (other && !other->d_func()->tabSet) {
other->d_func()->tab = qobject_cast<QQuickItem*>(parent());
emit other->tabChanged();
}
emit backtabChanged();
}
/*!
\qmlattachedproperty enumeration QtQuick::KeyNavigation::priority
This property determines whether the keys are processed before
or after the attached item's own key handling.
\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
{
return m_processPost ? AfterItem : BeforeItem;
}
void QQuickKeyNavigationAttached::setPriority(Priority order)
{
bool processPost = order == AfterItem;
if (processPost != m_processPost) {
m_processPost = processPost;
emit priorityChanged();
}
}
void QQuickKeyNavigationAttached::keyPressed(QKeyEvent *event, bool post)
{
Q_D(QQuickKeyNavigationAttached);
event->ignore();
if (post != m_processPost) {
QQuickItemKeyFilter::keyPressed(event, post);
return;
}
bool mirror = false;
switch (event->key()) {
case Qt::Key_Left: {
if (QQuickItem *parentItem = qobject_cast<QQuickItem*>(parent()))
mirror = QQuickItemPrivate::get(parentItem)->effectiveLayoutMirror;
QQuickItem* leftItem = mirror ? d->right : d->left;
if (leftItem) {
setFocusNavigation(leftItem, mirror ? "right" : "left", mirror ? Qt::TabFocusReason : Qt::BacktabFocusReason);
event->accept();
}
break;
}
case Qt::Key_Right: {
if (QQuickItem *parentItem = qobject_cast<QQuickItem*>(parent()))
mirror = QQuickItemPrivate::get(parentItem)->effectiveLayoutMirror;
QQuickItem* rightItem = mirror ? d->left : d->right;
if (rightItem) {
setFocusNavigation(rightItem, mirror ? "left" : "right", mirror ? Qt::BacktabFocusReason : Qt::TabFocusReason);
event->accept();
}
break;
}
case Qt::Key_Up:
if (d->up) {
setFocusNavigation(d->up, "up", Qt::BacktabFocusReason);
event->accept();
}
break;
case Qt::Key_Down:
if (d->down) {
setFocusNavigation(d->down, "down", Qt::TabFocusReason);
event->accept();
}
break;
case Qt::Key_Tab:
if (d->tab) {
setFocusNavigation(d->tab, "tab", Qt::TabFocusReason);
event->accept();
}
break;
case Qt::Key_Backtab:
if (d->backtab) {
setFocusNavigation(d->backtab, "backtab", Qt::BacktabFocusReason);
event->accept();
}
break;
default:
break;
}
if (!event->isAccepted()) QQuickItemKeyFilter::keyPressed(event, post);
}
void QQuickKeyNavigationAttached::keyReleased(QKeyEvent *event, bool post)
{
Q_D(QQuickKeyNavigationAttached);
event->ignore();
if (post != m_processPost) {
QQuickItemKeyFilter::keyReleased(event, post);
return;
}
bool mirror = false;
switch (event->key()) {
case Qt::Key_Left:
if (QQuickItem *parentItem = qobject_cast<QQuickItem*>(parent()))
mirror = QQuickItemPrivate::get(parentItem)->effectiveLayoutMirror;
if (mirror ? d->right : d->left)
event->accept();
break;
case Qt::Key_Right:
if (QQuickItem *parentItem = qobject_cast<QQuickItem*>(parent()))
mirror = QQuickItemPrivate::get(parentItem)->effectiveLayoutMirror;
if (mirror ? d->left : d->right)
event->accept();
break;
case Qt::Key_Up:
if (d->up) {
event->accept();
}
break;
case Qt::Key_Down:
if (d->down) {
event->accept();
}
break;
case Qt::Key_Tab:
if (d->tab) {
event->accept();
}
break;
case Qt::Key_Backtab:
if (d->backtab) {
event->accept();
}
break;
default:
break;
}
if (!event->isAccepted()) QQuickItemKeyFilter::keyReleased(event, post);
}
void QQuickKeyNavigationAttached::setFocusNavigation(QQuickItem *currentItem, const char *dir,
Qt::FocusReason reason)
{
QQuickItem *initialItem = currentItem;
bool isNextItem = false;
QList<QQuickItem *> visitedItems;
do {
isNextItem = false;
if (currentItem->isVisible() && currentItem->isEnabled()) {
currentItem->forceActiveFocus(reason);
} else {
QObject *attached =
qmlAttachedPropertiesObject<QQuickKeyNavigationAttached>(currentItem, false);
if (attached) {
QQuickItem *tempItem = qvariant_cast<QQuickItem*>(attached->property(dir));
if (tempItem) {
visitedItems.append(currentItem);
currentItem = tempItem;
isNextItem = true;
}
}
}
}
while (currentItem != initialItem && isNextItem && !visitedItems.contains(currentItem));
}
struct SigMap {
int key;
const char *sig;
};
const SigMap sigMap[] = {
{ Qt::Key_Left, "leftPressed" },
{ Qt::Key_Right, "rightPressed" },
{ Qt::Key_Up, "upPressed" },
{ Qt::Key_Down, "downPressed" },
{ Qt::Key_Tab, "tabPressed" },
{ Qt::Key_Backtab, "backtabPressed" },
{ Qt::Key_Asterisk, "asteriskPressed" },
{ Qt::Key_NumberSign, "numberSignPressed" },
{ Qt::Key_Escape, "escapePressed" },
{ Qt::Key_Return, "returnPressed" },
{ Qt::Key_Enter, "enterPressed" },
{ Qt::Key_Delete, "deletePressed" },
{ Qt::Key_Space, "spacePressed" },
{ Qt::Key_Back, "backPressed" },
{ Qt::Key_Cancel, "cancelPressed" },
{ Qt::Key_Select, "selectPressed" },
{ Qt::Key_Yes, "yesPressed" },
{ Qt::Key_No, "noPressed" },
{ Qt::Key_Context1, "context1Pressed" },
{ Qt::Key_Context2, "context2Pressed" },
{ Qt::Key_Context3, "context3Pressed" },
{ Qt::Key_Context4, "context4Pressed" },
{ Qt::Key_Call, "callPressed" },
{ Qt::Key_Hangup, "hangupPressed" },
{ Qt::Key_Flip, "flipPressed" },
{ Qt::Key_Menu, "menuPressed" },
{ Qt::Key_VolumeUp, "volumeUpPressed" },
{ Qt::Key_VolumeDown, "volumeDownPressed" },
{ 0, nullptr }
};
QByteArray QQuickKeysAttached::keyToSignal(int key)
{
QByteArray keySignal;
if (key >= Qt::Key_0 && key <= Qt::Key_9) {
keySignal = "digit0Pressed";
keySignal[5] = '0' + (key - Qt::Key_0);
} else {
int i = 0;
while (sigMap[i].key && sigMap[i].key != key)
++i;
keySignal = sigMap[i].sig;
}
return keySignal;
}
bool QQuickKeysAttached::isConnected(const char *signalName) const
{
Q_D(const QQuickKeysAttached);
int signal_index = d->signalIndex(signalName);
return d->isSignalConnected(signal_index);
}
/*!
\qmltype Keys
\nativetype QQuickKeysAttached
\inqmlmodule QtQuick
\ingroup qtquick-input-handlers
\brief Provides key handling to Items.
All visual primitives support key handling via the Keys
attaching type. Keys can be handled via the onPressed
and onReleased signal properties.
The signal properties have a \l KeyEvent parameter, named
\e event which contains details of the event. If a key is
handled \e event.accepted should be set to true to prevent the
event from propagating up the item hierarchy.
\section1 Example Usage
The following example shows how the general onPressed handler can
be used to test for a certain key; in this case, the left cursor
key:
\snippet qml/keys/keys-pressed.qml key item
Some keys may alternatively be handled via specific signal properties,
for example \e onSelectPressed. These handlers automatically set
\e event.accepted to true.
\snippet qml/keys/keys-handler.qml key item
See \l{Qt::Key}{Qt.Key} for the list of keyboard codes.
\section1 Key Handling Priorities
The Keys attaching type can be configured to handle key events
before or after the item it is attached to. This makes it possible
to intercept events in order to override an item's default behavior,
or act as a fallback for keys not handled by the item.
If \l priority is Keys.BeforeItem (default) the order of key event processing is:
\list 1
\li Items specified in \c forwardTo
\li specific key handlers, e.g. onReturnPressed
\li onPressed, onReleased handlers
\li Item specific key handling, e.g. TextInput key handling
\li parent item
\endlist
If priority is Keys.AfterItem the order of key event processing is:
\list 1
\li Item specific key handling, e.g. TextInput key handling
\li Items specified in \c forwardTo
\li specific key handlers, e.g. onReturnPressed
\li onPressed, onReleased handlers
\li parent item
\endlist
If the event is accepted during any of the above steps, key
propagation stops.
\sa KeyEvent, {KeyNavigation}{KeyNavigation attached property}
*/
/*!
\qmlattachedproperty bool QtQuick::Keys::enabled
This flags enables key handling if true (default); otherwise
no key handlers will be called.
*/
/*!
\qmlattachedproperty enumeration QtQuick::Keys::priority
This property determines whether the keys are processed before
or after the attached item's own key handling.
\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}
*/
/*!
\qmlattachedproperty 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
one item to handle some keys (e.g. the up and down arrow keys), and another item to
handle other keys (e.g. the left and right arrow keys). Once an item that has been
forwarded keys accepts the event it is no longer forwarded to items later in the
list.
This example forwards key events to two lists:
\qml
Item {
ListView {
id: list1
// ...
}
ListView {
id: list2
// ...
}
Keys.forwardTo: [list1, list2]
focus: true
}
\endqml
To see the order in which events are received when using forwardTo, see
\l {Key Handling Priorities}.
*/
/*!
\qmlattachedsignal QtQuick::Keys::pressed(KeyEvent event)
This signal is emitted when a key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::released(KeyEvent event)
This signal is emitted when a key has been released. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::shortcutOverride(KeyEvent event)
\since 5.9
This signal is emitted when a key has been pressed that could potentially
be used as a shortcut. The \a event parameter provides information about
the event.
Set \c event.accepted to \c true if you wish to prevent the pressed key
from being used as a shortcut by other types, such as \l Shortcut. For
example:
\code
Item {
id: escapeItem
focus: true
// Ensure that we get escape key press events first.
Keys.onShortcutOverride: (event)=> event.accepted = (event.key === Qt.Key_Escape)
Keys.onEscapePressed: {
console.log("escapeItem is handling escape");
// event.accepted is set to true by default for the specific key handlers
}
}
Shortcut {
sequence: "Escape"
onActivated: console.log("Shortcut is handling escape")
}
\endcode
As with the other signals, \c shortcutOverride will only be emitted for an
item if that item has \l {Item::}{activeFocus}.
\sa Shortcut
*/
/*!
\qmlattachedsignal QtQuick::Keys::digit0Pressed(KeyEvent event)
This signal is emitted when the digit '0' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::digit1Pressed(KeyEvent event)
This signal is emitted when the digit '1' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::digit2Pressed(KeyEvent event)
This signal is emitted when the digit '2' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::digit3Pressed(KeyEvent event)
This signal is emitted when the digit '3' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::digit4Pressed(KeyEvent event)
This signal is emitted when the digit '4' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::digit5Pressed(KeyEvent event)
This signal is emitted when the digit '5' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::digit6Pressed(KeyEvent event)
This signal is emitted when the digit '6' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::digit7Pressed(KeyEvent event)
This signal is emitted when the digit '7' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::digit8Pressed(KeyEvent event)
This signal is emitted when the digit '8' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::digit9Pressed(KeyEvent event)
This signal is emitted when the digit '9' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::leftPressed(KeyEvent event)
This signal is emitted when the Left arrow has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::rightPressed(KeyEvent event)
This signal is emitted when the Right arrow has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::upPressed(KeyEvent event)
This signal is emitted when the Up arrow has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::downPressed(KeyEvent event)
This signal is emitted when the Down arrow has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::tabPressed(KeyEvent event)
This signal is emitted when the Tab key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::backtabPressed(KeyEvent event)
This signal is emitted when the Shift+Tab key combination (Backtab) has
been pressed. The \a event parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::asteriskPressed(KeyEvent event)
This signal is emitted when the Asterisk '*' has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::escapePressed(KeyEvent event)
This signal is emitted when the Escape key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::returnPressed(KeyEvent event)
This signal is emitted when the Return key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::enterPressed(KeyEvent event)
This signal is emitted when the Enter key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::deletePressed(KeyEvent event)
This signal is emitted when the Delete key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::spacePressed(KeyEvent event)
This signal is emitted when the Space key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::backPressed(KeyEvent event)
This signal is emitted when the Back key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::cancelPressed(KeyEvent event)
This signal is emitted when the Cancel key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::selectPressed(KeyEvent event)
This signal is emitted when the Select key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::yesPressed(KeyEvent event)
This signal is emitted when the Yes key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::noPressed(KeyEvent event)
This signal is emitted when the No key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::context1Pressed(KeyEvent event)
This signal is emitted when the Context1 key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::context2Pressed(KeyEvent event)
This signal is emitted when the Context2 key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::context3Pressed(KeyEvent event)
This signal is emitted when the Context3 key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::context4Pressed(KeyEvent event)
This signal is emitted when the Context4 key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::callPressed(KeyEvent event)
This signal is emitted when the Call key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::hangupPressed(KeyEvent event)
This signal is emitted when the Hangup key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::flipPressed(KeyEvent event)
This signal is emitted when the Flip key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::menuPressed(KeyEvent event)
This signal is emitted when the Menu key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::volumeUpPressed(KeyEvent event)
This signal is emitted when the VolumeUp key has been pressed. The \a event
parameter provides information about the event.
*/
/*!
\qmlattachedsignal QtQuick::Keys::volumeDownPressed(KeyEvent event)
This signal is emitted when the VolumeDown key has been pressed. The \a event
parameter provides information about the event.
*/
QQuickKeysAttached::QQuickKeysAttached(QObject *parent)
: QObject(*(new QQuickKeysAttachedPrivate), parent),
QQuickItemKeyFilter(qmlobject_cast<QQuickItem*>(parent))
{
Q_D(QQuickKeysAttached);
m_processPost = false;
d->item = qmlobject_cast<QQuickItem*>(parent);
if (d->item != parent)
qWarning() << "Could not attach Keys property to: " << parent << " is not an Item";
}
QQuickKeysAttached::~QQuickKeysAttached()
{
}
QQuickKeysAttached::Priority QQuickKeysAttached::priority() const
{
return m_processPost ? AfterItem : BeforeItem;
}
void QQuickKeysAttached::setPriority(Priority order)
{
bool processPost = order == AfterItem;
if (processPost != m_processPost) {
m_processPost = processPost;
emit priorityChanged();
}
}
void QQuickKeysAttached::componentComplete()
{
#if QT_CONFIG(im)
Q_D(QQuickKeysAttached);
if (d->item) {
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);
break;
}
}
}
#endif
}
void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post)
{
Q_D(QQuickKeysAttached);
if (post != m_processPost || !d->enabled || d->inPress) {
event->ignore();
QQuickItemKeyFilter::keyPressed(event, post);
return;
}
// first process forwards
if (d->item && d->item->window()) {
d->inPress = true;
for (int ii = 0; ii < d->targets.size(); ++ii) {
QQuickItem *i = d->targets.at(ii);
if (i && i->isVisible()) {
event->accept();
QCoreApplication::sendEvent(i, event);
if (event->isAccepted()) {
d->inPress = false;
return;
}
}
}
d->inPress = false;
}
QQuickKeyEvent &ke = d->theKeyEvent;
ke.reset(*event);
QByteArray keySignal = keyToSignal(event->key());
if (!keySignal.isEmpty()) {
keySignal += "(QQuickKeyEvent*)";
if (isConnected(keySignal)) {
// If we specifically handle a key then default to accepted
ke.setAccepted(true);
int idx = QQuickKeysAttached::staticMetaObject.indexOfSignal(keySignal);
metaObject()->method(idx).invoke(this, Qt::DirectConnection, Q_ARG(QQuickKeyEvent*, &ke));
}
}
if (!ke.isAccepted())
emit pressed(&ke);
event->setAccepted(ke.isAccepted());
if (!event->isAccepted()) QQuickItemKeyFilter::keyPressed(event, post);
}
void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post)
{
Q_D(QQuickKeysAttached);
if (post != m_processPost || !d->enabled || d->inRelease) {
event->ignore();
QQuickItemKeyFilter::keyReleased(event, post);
return;
}
if (d->item && d->item->window()) {
d->inRelease = true;
for (int ii = 0; ii < d->targets.size(); ++ii) {
QQuickItem *i = d->targets.at(ii);
if (i && i->isVisible()) {
event->accept();
QCoreApplication::sendEvent(i, event);
if (event->isAccepted()) {
d->inRelease = false;
return;
}
}
}
d->inRelease = false;
}
QQuickKeyEvent &ke = d->theKeyEvent;
ke.reset(*event);
emit released(&ke);
event->setAccepted(ke.isAccepted());
if (!event->isAccepted()) QQuickItemKeyFilter::keyReleased(event, post);
}
#if QT_CONFIG(im)
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.size(); ++ii) {
QQuickItem *targetItem = d->targets.at(ii);
if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) {
QCoreApplication::sendEvent(targetItem, event);
if (event->isAccepted()) {
d->imeItem = targetItem;
d->inIM = false;
return;
}
}
}
d->inIM = false;
}
QQuickItemKeyFilter::inputMethodEvent(event, post);
}
QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const
{
Q_D(const QQuickKeysAttached);
if (d->item) {
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?
QVariant v = i->inputMethodQuery(query);
if (v.userType() == QMetaType::QRectF)
v = d->item->mapRectFromItem(i, v.toRectF()); //### cost?
return v;
}
}
}
return QQuickItemKeyFilter::inputMethodQuery(query);
}
#endif // im
void QQuickKeysAttached::shortcutOverrideEvent(QKeyEvent *event)
{
Q_D(QQuickKeysAttached);
QQuickKeyEvent &keyEvent = d->theKeyEvent;
keyEvent.reset(*event);
emit shortcutOverride(&keyEvent);
event->setAccepted(keyEvent.isAccepted());
}
QQuickKeysAttached *QQuickKeysAttached::qmlAttachedProperties(QObject *obj)
{
return new QQuickKeysAttached(obj);
}
/*!
\qmltype LayoutMirroring
\nativetype QQuickLayoutMirroringAttached
\inqmlmodule QtQuick
\ingroup qtquick-positioners
\ingroup qml-utility-elements
\brief Property used to mirror layout behavior.
The LayoutMirroring attaching type is used to horizontally mirror \l {anchor-layout}{Item anchors},
\l{Item Positioners}{positioner} types (such as \l Row and \l Grid)
and views (such as \l GridView and horizontal \l ListView). Mirroring is a visual change: left
anchors become right anchors, and positioner types like \l Grid and \l Row reverse the
horizontal layout of child items.
Mirroring is enabled for an item by setting the \l enabled property to true. By default, this
only affects the item itself; setting the \l childrenInherit property to true propagates the mirroring
behavior to all child items as well. If the \c LayoutMirroring attaching type has not been defined
for an item, mirroring is not enabled.
\note Since Qt 5.8, \c LayoutMirroring can be attached to a \l Window. In practice, it is the same as
attaching \c LayoutMirroring to the window's \c contentItem.
The following example shows mirroring in action. The \l Row below is specified as being anchored
to the left of its parent. However, since mirroring has been enabled, the anchor is horizontally
reversed and it is now anchored to the right. Also, since items in a \l Row are positioned
from left to right by default, they are now positioned from right to left instead, as demonstrated
by the numbering and opacity of the items:
\snippet qml/layoutmirroring.qml 0
\image layoutmirroring.png {Row with items numbered 5 to 1 positioned
right-to-left demonstrating layout mirroring}
Layout mirroring is useful when it is necessary to support both left-to-right and right-to-left
layout versions of an application to target different language areas. The \l childrenInherit
property allows layout mirroring to be applied without manually setting layout configurations
for every item in an application. Keep in mind, however, that mirroring does not affect any
positioning that is defined by the \l Item \l {Item::}{x} coordinate value, so even with
mirroring enabled, it will often be necessary to apply some layout fixes to support the
desired layout direction. Also, it may be necessary to disable the mirroring of individual
child items (by setting \l {enabled}{LayoutMirroring.enabled} to false for such items) if
mirroring is not the desired behavior, or if the child item already implements mirroring in
some custom way.
To set the layout direction based on the \l {Default Layout Direction}{default layout direction}
of the application, use the following code:
\code
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
\endcode
See \l {Right-to-left User Interfaces} for further details on using \c LayoutMirroring and
other related features to implement right-to-left support for an application.
*/
/*!
\qmlproperty bool QtQuick::LayoutMirroring::enabled
This property holds whether the item's layout is mirrored horizontally. Setting this to true
horizontally reverses \l {anchor-layout}{anchor} settings such that left anchors become right,
and right anchors become left. For \l{Item Positioners}{positioner} types
(such as \l Row and \l Grid) and view types (such as \l {GridView}{GridView} and \l {ListView}{ListView})
this also mirrors the horizontal layout direction of the item.
The default value is false.
*/
/*!
\qmlproperty bool QtQuick::LayoutMirroring::childrenInherit
This property holds whether the \l {enabled}{LayoutMirroring.enabled} value for this item
is inherited by its children.
The default value is false.
*/
QQuickLayoutMirroringAttached::QQuickLayoutMirroringAttached(QObject *parent) : QObject(parent), itemPrivate(nullptr)
{
if (QQuickItem *item = qobject_cast<QQuickItem *>(parent))
itemPrivate = QQuickItemPrivate::get(item);
else if (QQuickWindow *window = qobject_cast<QQuickWindow *>(parent))
itemPrivate = QQuickItemPrivate::get(window->contentItem());
if (itemPrivate)
itemPrivate->extra.value().layoutDirectionAttached = this;
else
qmlWarning(parent) << tr("LayoutMirroring attached property only works with Items and Windows");
}
QQuickLayoutMirroringAttached * QQuickLayoutMirroringAttached::qmlAttachedProperties(QObject *object)
{
return new QQuickLayoutMirroringAttached(object);
}
bool QQuickLayoutMirroringAttached::enabled() const
{
return itemPrivate ? itemPrivate->effectiveLayoutMirror : false;
}
void QQuickLayoutMirroringAttached::setEnabled(bool enabled)
{
if (!itemPrivate)
return;
itemPrivate->isMirrorImplicit = false;
if (enabled != itemPrivate->effectiveLayoutMirror) {
itemPrivate->setLayoutMirror(enabled);
if (itemPrivate->inheritMirrorFromItem)
itemPrivate->resolveLayoutMirror();
}
}
void QQuickLayoutMirroringAttached::resetEnabled()
{
if (itemPrivate && !itemPrivate->isMirrorImplicit) {
itemPrivate->isMirrorImplicit = true;
itemPrivate->resolveLayoutMirror();
}
}
bool QQuickLayoutMirroringAttached::childrenInherit() const
{
return itemPrivate ? itemPrivate->inheritMirrorFromItem : false;
}
void QQuickLayoutMirroringAttached::setChildrenInherit(bool childrenInherit) {
if (itemPrivate && childrenInherit != itemPrivate->inheritMirrorFromItem) {
itemPrivate->inheritMirrorFromItem = childrenInherit;
itemPrivate->resolveLayoutMirror();
childrenInheritChanged();
}
}
void QQuickItemPrivate::resolveLayoutMirror()
{
Q_Q(QQuickItem);
if (QQuickItem *parentItem = q->parentItem()) {
QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parentItem);
setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent);
} else {
setImplicitLayoutMirror(isMirrorImplicit ? false : effectiveLayoutMirror, inheritMirrorFromItem);
}
}
void QQuickItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit)
{
inherit = inherit || inheritMirrorFromItem;
if (!isMirrorImplicit && inheritMirrorFromItem)
mirror = effectiveLayoutMirror;
if (mirror == inheritedLayoutMirror && inherit == inheritMirrorFromParent)
return;
inheritMirrorFromParent = inherit;
inheritedLayoutMirror = inheritMirrorFromParent ? mirror : false;
if (isMirrorImplicit)
setLayoutMirror(inherit ? inheritedLayoutMirror : false);
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);
}
}
}
void QQuickItemPrivate::setLayoutMirror(bool mirror)
{
if (mirror != effectiveLayoutMirror) {
effectiveLayoutMirror = mirror;
if (_anchors) {
QQuickAnchorsPrivate *anchor_d = QQuickAnchorsPrivate::get(_anchors);
anchor_d->fillChanged();
anchor_d->centerInChanged();
anchor_d->updateHorizontalAnchors();
}
mirrorChange();
if (extra.isAllocated() && extra->layoutDirectionAttached) {
emit extra->layoutDirectionAttached->enabledChanged();
}
}
}
/*!
\qmltype EnterKey
\nativetype QQuickEnterKeyAttached
\inqmlmodule QtQuick
\ingroup qtquick-input
\since 5.6
\brief Provides a property to manipulate the appearance of Enter key on
an on-screen keyboard.
The EnterKey attached property is used to manipulate the appearance and
behavior of the Enter key on an on-screen keyboard.
*/
/*!
\qmlattachedproperty enumeration QtQuick::EnterKey::type
Holds the type of the Enter key.
\note Not all of these values are supported on all platforms. For
unsupported values the default key is used instead.
\value Qt.EnterKeyDefault The default Enter key. This can be either a
button to accept the input and close the
keyboard, or a \e Return button to enter a
newline in case of a multi-line input field.
\value Qt.EnterKeyReturn Show a \e Return button that inserts a
newline.
\value Qt.EnterKeyDone Show a \e {"Done"} button. Typically, the
keyboard is expected to close when the button
is pressed.
\value Qt.EnterKeyGo Show a \e {"Go"} button. Typically used in an
address bar when entering a URL.
\value Qt.EnterKeySend Show a \e {"Send"} button.
\value Qt.EnterKeySearch Show a \e {"Search"} button.
\value Qt.EnterKeyNext Show a \e {"Next"} button. Typically used in a
form to allow navigating to the next input
field without the keyboard closing.
\value Qt.EnterKeyPrevious Show a \e {"Previous"} button.
*/
QQuickEnterKeyAttached::QQuickEnterKeyAttached(QObject *parent)
: QObject(parent), itemPrivate(nullptr), keyType(Qt::EnterKeyDefault)
{
if (QQuickItem *item = qobject_cast<QQuickItem*>(parent)) {
itemPrivate = QQuickItemPrivate::get(item);
itemPrivate->extra.value().enterKeyAttached = this;
} else
qmlWarning(parent) << tr("EnterKey attached property only works with Items");
}
QQuickEnterKeyAttached *QQuickEnterKeyAttached::qmlAttachedProperties(QObject *object)
{
return new QQuickEnterKeyAttached(object);
}
Qt::EnterKeyType QQuickEnterKeyAttached::type() const
{
return keyType;
}
void QQuickEnterKeyAttached::setType(Qt::EnterKeyType type)
{
if (keyType != type) {
keyType = type;
#if QT_CONFIG(im)
if (itemPrivate && itemPrivate->activeFocus)
QGuiApplication::inputMethod()->update(Qt::ImEnterKeyType);
#endif
typeChanged();
}
}
void QQuickItemPrivate::setAccessible()
{
isAccessible = true;
}
/*!
Clears all sub focus items from \a scope.
If \a focus is true, sets the scope's subFocusItem
to be this item.
*/
void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus)
{
Q_Q(QQuickItem);
Q_ASSERT(scope);
QQuickItemPrivate *scopePrivate = QQuickItemPrivate::get(scope);
QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
// Correct focus chain in scope
if (oldSubFocusItem) {
QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
while (sfi && sfi != scope) {
QQuickItemPrivate::get(sfi)->subFocusItem = nullptr;
sfi = sfi->parentItem();
}
}
if (focus) {
scopePrivate->subFocusItem = q;
QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
while (sfi && sfi != scope) {
QQuickItemPrivate::get(sfi)->subFocusItem = q;
sfi = sfi->parentItem();
}
} else {
scopePrivate->subFocusItem = nullptr;
}
}
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);
}
bool QQuickItemPrivate::setLastFocusChangeReason(Qt::FocusReason reason)
{
if (focusReason == reason)
return false;
focusReason = reason;
return true;
}
/*!
\class QQuickItem
\brief The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
\inmodule QtQuick
All visual items in Qt Quick inherit from QQuickItem. Although a QQuickItem
instance has no visual appearance, it defines all the attributes that are
common across visual items, such as x and y position, width and height,
\l {Positioning with Anchors}{anchoring} and key handling support.
You can subclass QQuickItem to provide your own custom visual item
that inherits these features.
\section1 Custom Scene Graph Items
All visual QML items are rendered using the scene graph, the
default implementation of which is a low-level, high-performance
rendering stack, closely tied to accelerated graphics APIs, such
as OpenGL, Vulkan, Metal, or Direct 3D. It is possible for
subclasses of QQuickItem to add their own custom content into the
scene graph by setting the QQuickItem::ItemHasContents flag and
reimplementing the QQuickItem::updatePaintNode() function.
\warning It is crucial that graphics operations and interaction with
the scene graph happens exclusively on the rendering thread,
primarily during the updatePaintNode() call. The best rule of
thumb is to only use classes with the "QSG" prefix inside the
QQuickItem::updatePaintNode() function.
\note All classes with QSG prefix should be used solely on the scene graph's
rendering thread. See \l {Scene Graph and Rendering} for more information.
\section2 Graphics Resource Handling
The preferred way to handle cleanup of graphics resources used in
the scene graph, is to rely on the automatic cleanup of nodes. A
QSGNode returned from QQuickItem::updatePaintNode() is
automatically deleted on the right thread at the right time. Trees
of QSGNode instances are managed through the use of
QSGNode::OwnedByParent, which is set by default. So, for the
majority of custom scene graph items, no extra work will be
required.
Implementations that store graphics resources outside the node
tree, such as an item implementing QQuickItem::textureProvider(),
will need to take care in cleaning it up correctly depending on
how the item is used in QML. The situations to handle are:
\list
\li The scene graph is invalidated; This can happen, depending on
the platform and QQuickWindow configuration, when the window is
hidden using QQuickWindow::hide(), or when it is closed. If the
item class implements a \c slot named \c invalidateSceneGraph(),
this slot will be called on the rendering thread while the GUI
thread is blocked. This is equivalent to connecting to
QQuickWindow::sceneGraphInvalidated(). When rendering through
OpenGL, the OpenGL context of this item's window will be bound
when this slot is called. The only exception is if the native
OpenGL has been destroyed outside Qt's control, for instance
through \c EGL_CONTEXT_LOST.
\li The item is removed from the scene; If an item is taken out of
the scene, for instance because it's parent was set to \c null or
an item in another window, the QQuickItem::releaseResources() will
be called on the GUI thread. QQuickWindow::scheduleRenderJob()
should be used to schedule cleanup of rendering resources.
\li The item is deleted; When the destructor if an item runs, it
should delete any graphics resources it has. If neither of the two
conditions above were already met, the item will be part of a
window and it is possible to use QQuickWindow::scheduleRenderJob()
to have them cleaned up. If an implementation ignores the call to
QQuickItem::releaseResources(), the item will in many cases no
longer have access to a QQuickWindow and thus no means of
scheduling cleanup.
\endlist
When scheduling cleanup of graphics resources using
QQuickWindow::scheduleRenderJob(), one should use either
QQuickWindow::BeforeSynchronizingStage or
QQuickWindow::AfterSynchronizingStage. The \l {Scene Graph and
Rendering}{synchronization stage} is where the scene graph is
changed as a result of changes to the QML tree. If cleanup is
scheduled at any other time, it may result in other parts of the
scene graph referencing the newly deleted objects as these parts
have not been updated.
\note Use of QObject::deleteLater() to clean up graphics resources
is strongly discouraged as this will make the \c delete operation
run at an arbitrary time and it is unknown if there will be an
OpenGL context bound when the deletion takes place.
\section1 Custom QPainter Items
The QQuickItem provides a subclass, QQuickPaintedItem, which
allows the users to render content using QPainter.
\warning Using QQuickPaintedItem uses an indirect 2D surface to
render its content, using software rasterization, so the rendering
is a two-step operation. First rasterize the surface, then draw
the surface. Using scene graph API directly is always
significantly faster.
\section1 Behavior Animations
If your Item uses the \l Behavior type to define animations for property
changes, you should always use either QObject::setProperty(),
QQmlProperty(), or QMetaProperty::write() when you need to modify those
properties from C++. This ensures that the QML engine knows about the
property change. Otherwise, the engine won't be able to carry out your
requested animation.
Note that these functions incur a slight performance penalty. For more
details, see \l {Accessing Members of a QML Object Type from C++}.
\sa QQuickWindow, QQuickPaintedItem
*/
/*!
\qmltype Item
\nativetype QQuickItem
\inherits QtObject
\inqmlmodule QtQuick
\ingroup qtquick-visual
\brief A basic visual QML type.
The Item type is the base type for all visual items in Qt Quick.
All visual items in Qt Quick inherit from Item. Although an Item
object has no visual appearance, it defines all the attributes that are
common across visual items, such as x and y position, width and height,
\l {Positioning with Anchors}{anchoring} and key handling support.
The Item type can be useful for grouping several items under a single
root visual item. For example:
\qml
import QtQuick 2.0
Item {
Image {
source: "tile.png"
}
Image {
x: 80
width: 100
height: 100
source: "tile.png"
}
Image {
x: 190
width: 100
height: 100
fillMode: Image.Tile
source: "tile.png"
}
}
\endqml
\section2 Event Handling
All Item-based visual types can use \l {Qt Quick Input Handlers}{Input Handlers}
to handle incoming input events (subclasses of QInputEvent), such as mouse,
touch and key events. This is the preferred declarative way to handle events.
An alternative way to handle touch events is to subclass QQuickItem, call
setAcceptTouchEvents() in the constructor, and override touchEvent().
\l {QEvent::setAccepted()}{Accept} the entire event to stop delivery to
items underneath, and to exclusively grab for all the event's touch points.
Use QPointerEvent::setExclusiveGrabber() to grab only certain touchpoints,
and allow the event to be delivered further.
Likewise, a QQuickItem subclass can call setAcceptedMouseButtons()
to register to receive mouse button events, setAcceptHoverEvents()
to receive hover events (mouse movements while no button is pressed),
and override the virtual functions mousePressEvent(), mouseMoveEvent(), and
mouseReleaseEvent(). Those can also accept the event to prevent further
delivery and get an implicit grab at the same time; or explicitly
\l {QPointerEvent::setExclusiveGrabber()}{grab} the single QEventPoint
that the QMouseEvent carries.
Key handling is available to all Item-based visual types via the \l Keys
attached property. The \e Keys attached property provides basic signals
such as \l {Keys::}{pressed} and \l {Keys::}{released}, as well as
signals for specific keys, such as \l {Keys::}{spacePressed}. The
example below assigns \l {Keyboard Focus in Qt Quick}{keyboard focus} to
the item and handles the left key via the general \c onPressed handler
and the return key via the \c onReturnPressed handler:
\qml
import QtQuick 2.0
Item {
focus: true
Keys.onPressed: (event)=> {
if (event.key == Qt.Key_Left) {
console.log("move left");
event.accepted = true;
}
}
Keys.onReturnPressed: console.log("Pressed return");
}
\endqml
See the \l Keys attached property for detailed documentation.
\section2 Layout Mirroring
Item layouts can be mirrored using the \l LayoutMirroring attached
property. This causes \l{anchors.top}{anchors} to be horizontally
reversed, and also causes items that lay out or position their children
(such as ListView or \l Row) to horizontally reverse the direction of
their layouts.
See LayoutMirroring for more details.
\section1 Item Layers
An Item will normally be rendered directly into the window it
belongs to. However, by setting \l layer.enabled, it is possible
to delegate the item and its entire subtree into an offscreen
surface. Only the offscreen surface, a texture, will be then drawn
into the window.
If it is desired to have a texture size different from that of the
item, this is possible using \l layer.textureSize. To render only
a section of the item into the texture, use \l
layer.sourceRect. It is also possible to specify \l
layer.sourceRect so it extends beyond the bounds of the item. In
this case, the exterior will be padded with transparent pixels.
The item will use linear interpolation for scaling if
\l layer.smooth is set to \c true and will use mipmap for
downsampling if \l layer.mipmap is set to \c true. Mipmapping may
improve visual quality of downscaled items. For mipmapping of
single Image items, prefer Image::mipmap.
\section2 Layer Opacity vs Item Opacity
When applying \l opacity to an item hierarchy the opacity is
applied to each item individually. This can lead to undesired
visual results when the opacity is applied to a subtree. Consider
the following example:
\table
\row
\li \inlineimage qml-blending-nonlayered.png
{Two overlapping rectangles showing non-layered opacity}
\li \b {Non-layered Opacity} \snippet qml/layerblending.qml non-layered
\endtable
A layer is rendered with the root item's opacity being 1, and then
the root item's opacity is applied to the texture when it is
drawn. This means that fading in a large item hierarchy from
transparent to opaque, or vice versa, can be done without the
overlap artifacts that the normal item by item alpha blending
has. Here is the same example with layer enabled:
\table
\row
\li \image qml-blending-layered.png {Two overlapping white rectangles
rendered as a layer without alpha blending artifacts}
\li \b {Layered Opacity} \snippet qml/layerblending.qml layered
\endtable
\section2 Combined with ShaderEffects
Setting \l layer.enabled to true will turn the item into a \l
{QQuickItem::isTextureProvider}{texture provider}, making it
possible to use the item directly as a texture, for instance
in combination with the ShaderEffect type.
It is possible to apply an effect on a layer at runtime using
layer.effect:
\qml
Item {
id: layerRoot
layer.enabled: true
layer.effect: ShaderEffect {
fragmentShader: "effect.frag.qsb"
}
}
\endqml
See ShaderEffect for more information about using effects.
\note \l layer.enabled is actually just a more convenient way of using
ShaderEffectSource.
\section2 Memory and Performance
When an item's layer is enabled, the scene graph will allocate memory
in the GPU equal to \c {width x height x 4}. In memory constrained
configurations, large layers should be used with care.
In the QPainter / QWidget world, it is sometimes favorable to
cache complex content in a pixmap, image or texture. In Qt Quick,
because of the techniques already applied by the \l {Qt Quick
Scene Graph Default Renderer} {scene graph renderer}, this will in most
cases not be the case. Excessive draw calls are already reduced
because of batching and a cache will in most cases end up blending
more pixels than the original content. The overhead of rendering
to an offscreen and the blending involved with drawing the
resulting texture is therefore often more costly than simply
letting the item and its children be drawn normally.
Also, an item using a layer can not be \l {Batching} {batched} during
rendering. This means that a scene with many layered items may
have performance problems.
Layering can be convenient and useful for visual effects, but
should in most cases be enabled for the duration of the effect and
disabled afterwards.
*/
/*!
\enum QQuickItem::Flag
This enum type is used to specify various item properties.
\value ItemClipsChildrenToShape Indicates this item should visually clip
its children so that they are rendered only within the boundaries of this
item.
\value ItemAcceptsInputMethod Indicates the item supports text input
methods.
\value ItemIsFocusScope Indicates the item is a focus scope. See
\l {Keyboard Focus in Qt Quick} for more information.
\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()
*/
/*!
\enum QQuickItem::ItemChange
\brief Used in conjunction with QQuickItem::itemChange() to notify
the item about certain types of changes.
\value ItemChildAddedChange A child was added. ItemChangeData::item contains
the added child.
\value ItemChildRemovedChange A child was removed. ItemChangeData::item
contains the removed child.
\value ItemSceneChange The item was added to or removed from a scene. The
QQuickWindow rendering the scene is specified in using ItemChangeData::window.
The window parameter is null when the item is removed from a scene.
\value ItemVisibleHasChanged The item's visibility has changed.
ItemChangeData::boolValue contains the new visibility.
\value ItemParentHasChanged The item's parent has changed.
ItemChangeData::item contains the new parent.
\value ItemOpacityHasChanged The item's opacity has changed.
ItemChangeData::realValue contains the new opacity.
\value ItemActiveFocusHasChanged The item's focus has changed.
ItemChangeData::boolValue contains whether the item has focus or not.
\value ItemRotationHasChanged The item's rotation has changed.
ItemChangeData::realValue contains the new rotation.
\value ItemDevicePixelRatioHasChanged The device pixel ratio of the screen
the item is on has changed. ItemChangedData::realValue contains the new
device pixel ratio.
\value ItemAntialiasingHasChanged The antialiasing has changed. The current
(boolean) value can be found in QQuickItem::antialiasing.
\value ItemEnabledHasChanged The item's enabled state has changed.
ItemChangeData::boolValue contains the new enabled state. (since Qt 5.10)
\value ItemScaleHasChanged The item's scale has changed.
ItemChangeData::realValue contains the scale. (since Qt 6.9)
\value ItemTransformHasChanged The item's transform has changed. This
occurs when the item's position, size, rotation, scale, transformOrigin
or attached transforms change. ItemChangeData::item contains the item
that caused the change. (since Qt 6.9)
*/
/*!
\class QQuickItem::ItemChangeData
\inmodule QtQuick
\brief Adds supplementary information to the QQuickItem::itemChange()
function.
The meaning of each member of this class is defined by the change type.
\sa QQuickItem::ItemChange
*/
/*!
\fn QQuickItem::ItemChangeData::ItemChangeData(QQuickItem *)
\internal
*/
/*!
\fn QQuickItem::ItemChangeData::ItemChangeData(QQuickWindow *)
\internal
*/
/*!
\fn QQuickItem::ItemChangeData::ItemChangeData(qreal)
\internal
*/
/*!
\fn QQuickItem::ItemChangeData::ItemChangeData(bool)
\internal
*/
/*!
\variable QQuickItem::ItemChangeData::realValue
The numeric value that has changed: \l {QQuickItem::opacity()}{opacity},
\l {QQuickItem::rotation()}{rotation}, or
\l {QQuickItem::scale()}{scale}, or
\l {QScreen::devicePixelRatio}{device pixel ratio}.
\sa QQuickItem::ItemChange
*/
/*!
\variable QQuickItem::ItemChangeData::boolValue
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
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
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
*/
/*!
\enum QQuickItem::TransformOrigin
Controls the point about which simple transforms like scale apply.
\value TopLeft The top-left corner of the item.
\value Top The center point of the top of the item.
\value TopRight The top-right corner of the item.
\value Left The left most point of the vertical middle.
\value Center The center of the item.
\value Right The right most point of the vertical middle.
\value BottomLeft The bottom-left corner of the item.
\value Bottom The center point of the bottom of the item.
\value BottomRight The bottom-right corner of the item.
\sa transformOrigin(), setTransformOrigin()
*/
/*!
\fn void QQuickItem::childrenRectChanged(const QRectF &)
\internal
*/
/*!
\fn void QQuickItem::baselineOffsetChanged(qreal)
\internal
*/
/*!
\fn void QQuickItem::stateChanged(const QString &state)
\internal
*/
/*!
\fn void QQuickItem::parentChanged(QQuickItem *)
\internal
*/
/*!
\fn void QQuickItem::smoothChanged(bool)
\internal
*/
/*!
\fn void QQuickItem::antialiasingChanged(bool)
\internal
*/
/*!
\fn void QQuickItem::clipChanged(bool)
\internal
*/
/*!
\fn void QQuickItem::transformOriginChanged(TransformOrigin)
\internal
*/
/*!
\fn void QQuickItem::focusChanged(bool)
\internal
*/
/*!
\fn void QQuickItem::activeFocusChanged(bool)
\internal
*/
/*!
\fn void QQuickItem::focusPolicyChanged(Qt::FocusPolicy)
\internal
*/
/*!
\fn void QQuickItem::activeFocusOnTabChanged(bool)
\internal
*/
/*!
\fn void QQuickItem::childrenChanged()
\internal
*/
/*!
\fn void QQuickItem::opacityChanged()
\internal
*/
/*!
\fn void QQuickItem::enabledChanged()
\internal
*/
/*!
\fn void QQuickItem::visibleChanged()
\internal
*/
/*!
\fn void QQuickItem::visibleChildrenChanged()
\internal
*/
/*!
\fn void QQuickItem::rotationChanged()
\internal
*/
/*!
\fn void QQuickItem::scaleChanged()
\internal
*/
/*!
\fn void QQuickItem::xChanged()
\internal
*/
/*!
\fn void QQuickItem::yChanged()
\internal
*/
/*!
\fn void QQuickItem::widthChanged()
\internal
*/
/*!
\fn void QQuickItem::heightChanged()
\internal
*/
/*!
\fn void QQuickItem::zChanged()
\internal
*/
/*!
\fn void QQuickItem::implicitWidthChanged()
\internal
*/
/*!
\fn void QQuickItem::implicitHeightChanged()
\internal
*/
/*!
\fn void QQuickItem::mutabilityGroupChanged(int)
\internal
*/
/*!
\fn QQuickItem::QQuickItem(QQuickItem *parent)
Constructs a QQuickItem with the given \a parent.
The \c parent will be used as both the \l {setParentItem()}{visual parent}
and the \l QObject parent.
*/
QQuickItem::QQuickItem(QQuickItem* parent)
: QObject(*(new QQuickItemPrivate), parent)
{
Q_D(QQuickItem);
d->init(parent);
}
/*! \internal
*/
QQuickItem::QQuickItem(QQuickItemPrivate &dd, QQuickItem *parent)
: QObject(dd, parent)
{
Q_D(QQuickItem);
d->init(parent);
}
/*!
Destroys the QQuickItem.
*/
QQuickItem::~QQuickItem()
{
Q_D(QQuickItem);
d->inDestructor = true;
#if QT_CONFIG(accessibility)
if (QGuiApplicationPrivate::is_app_running && !QGuiApplicationPrivate::is_app_closing && QAccessible::isActive())
QAccessibleCache::instance()->sendObjectDestroyedEvent(this);
d->isAccessible = false;
#endif
if (d->windowRefCount > 1)
d->windowRefCount = 1; // Make sure window is set to null in next call to derefWindow().
if (d->parentItem)
setParentItem(nullptr);
else if (d->window)
d->derefWindow();
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.
*/
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.size(); ++ii) {
QQuickTransform *t = d->transforms.at(ii);
QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t);
tp->items.removeOne(this);
}
if (d->extra.isAllocated()) {
delete d->extra->contents; d->extra->contents = nullptr;
#if QT_CONFIG(quick_shadereffect)
delete d->extra->layer; d->extra->layer = nullptr;
#endif
}
delete d->_anchors; d->_anchors = nullptr;
delete d->_stateGroup; d->_stateGroup = nullptr;
d->isQuickItem = false;
}
/*!
\internal
*/
bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
{
if (!item->window())
return false;
if (item == item->window()->contentItem())
return true;
const auto tabFocus = QGuiApplication::styleHints()->tabFocusBehavior();
if (tabFocus == Qt::NoTabFocus)
return false;
if (tabFocus == Qt::TabFocusAllControls)
return true;
QVariant editable = item->property("editable");
if (editable.isValid())
return editable.toBool();
QVariant readonly = item->property("readOnly");
if (readonly.isValid())
return !readonly.toBool() && item->property("text").isValid();
#if QT_CONFIG(accessibility)
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) {
if (QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(item))
return iface->state().editable;
}
#endif
return false;
}
/*!
\internal
\brief QQuickItemPrivate::focusNextPrev focuses the next/prev item in the tab-focus-chain
\param item The item that currently has the focus
\param forward The direction
\return Whether the next item in the focus chain is found or not
If \a next is true, the next item visited will be in depth-first order relative to \a item.
If \a next is false, the next item visited will be in reverse depth-first order relative to \a item.
*/
bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward)
{
QQuickWindow *window = item->window();
const bool wrap = !window || window->isTopLevel();
QQuickItem *next = QQuickItemPrivate::nextPrevItemInTabFocusChain(item, forward, wrap);
if (next == item)
return false;
const auto reason = forward ? Qt::TabFocusReason : Qt::BacktabFocusReason;
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,
reason);
window->parent()->requestActivate();
return true;
}
next->forceActiveFocus(reason);
return true;
}
QQuickItem *QQuickItemPrivate::nextTabChildItem(const QQuickItem *item, int start)
{
if (!item) {
qWarning() << "QQuickItemPrivate::nextTabChildItem called with null item.";
return nullptr;
}
const QList<QQuickItem *> &children = item->childItems();
const int count = children.size();
if (start < 0 || start >= count) {
qWarning() << "QQuickItemPrivate::nextTabChildItem: Start index value out of range for item" << item;
return nullptr;
}
while (start < count) {
QQuickItem *child = children.at(start);
if (!child->d_func()->isTabFence)
return child;
++start;
}
return nullptr;
}
QQuickItem *QQuickItemPrivate::prevTabChildItem(const QQuickItem *item, int start)
{
if (!item) {
qWarning() << "QQuickItemPrivate::prevTabChildItem called with null item.";
return nullptr;
}
const QList<QQuickItem *> &children = item->childItems();
const int count = children.size();
if (start == -1)
start = count - 1;
if (start < 0 || start >= count) {
qWarning() << "QQuickItemPrivate::prevTabChildItem: Start index value out of range for item" << item;
return nullptr;
}
while (start >= 0) {
QQuickItem *child = children.at(start);
if (!child->d_func()->isTabFence)
return child;
--start;
}
return nullptr;
}
QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, bool forward, bool wrap)
{
Q_ASSERT(item);
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: item:" << item << ", forward:" << forward;
if (!item->window())
return item;
const QQuickItem * const contentItem = item->window()->contentItem();
if (!contentItem)
return item;
QQuickItem *from = nullptr;
bool isTabFence = item->d_func()->isTabFence;
if (forward) {
if (!isTabFence)
from = item->parentItem();
} else {
if (!item->childItems().isEmpty())
from = item->d_func()->childItems.constFirst();
else if (!isTabFence)
from = item->parentItem();
}
bool skip = false;
QQuickItem *startItem = item;
QQuickItem *originalStartItem = startItem;
// Protect from endless loop:
// If we start on an invisible item we will not find it again.
// If there is no other item which can become the focus item, we have a forever loop,
// since the protection only works if we encounter the first item again.
while (startItem && !startItem->isVisible()) {
startItem = startItem->parentItem();
}
if (!startItem)
return item;
QQuickItem *firstFromItem = from;
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;
skip = false;
QQuickItem *last = current;
bool hasChildren = !current->childItems().isEmpty() && current->isEnabled() && current->isVisible();
QQuickItem *firstChild = nullptr;
QQuickItem *lastChild = nullptr;
if (hasChildren) {
firstChild = nextTabChildItem(current, 0);
if (!firstChild)
hasChildren = false;
else
lastChild = prevTabChildItem(current, -1);
}
isTabFence = current->d_func()->isTabFence;
if (isTabFence && !hasChildren)
return current;
// coming from parent: check children
if (hasChildren && from == current->parentItem()) {
if (forward) {
current = firstChild;
} else {
current = lastChild;
if (!current->childItems().isEmpty())
skip = true;
}
} else if (hasChildren && forward && from != lastChild) {
// not last child going forwards
int nextChild = current->childItems().indexOf(from) + 1;
current = nextTabChildItem(current, nextChild);
} else if (hasChildren && !forward && from != firstChild) {
// not first child going backwards
int prevChild = current->childItems().indexOf(from) - 1;
current = prevTabChildItem(current, prevChild);
if (!current->childItems().isEmpty())
skip = true;
// back to the parent
} else if (QQuickItem *parent = !isTabFence ? current->parentItem() : nullptr) {
// we would evaluate the parent twice, thus we skip
if (forward) {
skip = true;
} else if (QQuickItem *firstSibling = !forward ? nextTabChildItem(parent, 0) : nullptr) {
if (last != firstSibling
|| (parent->isFocusScope() && parent->activeFocusOnTab() && parent->hasActiveFocus()))
skip = true;
}
current = parent;
} else if (hasChildren) {
if (!wrap && !isTabFence) {
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain:"
<< "Focus chain about to wrap but we're outside a tab fence and wrapping was set to false."
<< "Returning.";
return nullptr;
}
// Wrap around after checking all items forward
if (forward) {
current = firstChild;
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain:"
<< "wrapping from last to first:" << current;
} else {
current = lastChild;
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain:"
<< "wrapping from first to last:" << current;
if (!current->childItems().isEmpty())
skip = true;
}
}
from = last;
// if [from] item is equal to [firstFromItem], means we have traversed one path and
// jump back to parent of the chain, and then we have to check whether we have
// 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
// 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";
return item;
} else {
qCDebug(lcFocus) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return " << startItem;
return startItem;
}
}
if (!firstFromItem) {
if (startItem->d_func()->isTabFence) {
if (current == startItem)
firstFromItem = from;
} else { //start from root
startItem = current;
firstFromItem = from;
}
}
} while (skip || !current->activeFocusOnTab() || !current->isEnabled() || !current->isVisible()
|| !(QQuickItemPrivate::canAcceptTabFocus(current)));
return current;
}
/*!
\qmlproperty Item QtQuick::Item::parent
This property holds the visual parent of the item.
\note The concept of the \e {visual parent} differs from that of the
\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.
*/
/*!
\property QQuickItem::parent
This property holds the visual parent of the item.
\note The concept of the \e {visual parent} differs from that of the
\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
{
Q_D(const QQuickItem);
return d->parentItem;
}
void QQuickItem::setParentItem(QQuickItem *parentItem)
{
Q_D(QQuickItem);
if (parentItem == d->parentItem)
return;
if (parentItem) {
QQuickItem *itemAncestor = parentItem;
while (itemAncestor != nullptr) {
if (Q_UNLIKELY(itemAncestor == this)) {
qWarning() << "QQuickItem::setParentItem: Parent" << parentItem << "is already part of the subtree of" << this;
return;
}
itemAncestor = itemAncestor->parentItem();
}
auto engine = qmlEngine(this);
if (engine) {
QV4::ExecutionEngine *v4 = engine->handle();
QV4::WriteBarrier::markCustom(v4, [this](QV4::MarkStack *ms){
QV4::QObjectWrapper::markWrapper(this, ms);
});
}
}
d->removeFromDirtyList();
QQuickItem *oldParentItem = d->parentItem;
QQuickItem *scopeFocusedItem = nullptr;
if (oldParentItem) {
QQuickItemPrivate *op = QQuickItemPrivate::get(oldParentItem);
QQuickItem *scopeItem = nullptr;
if (hasFocus() || op->subFocusItem == this)
scopeFocusedItem = this;
else if (!isFocusScope() && d->subFocusItem)
scopeFocusedItem = d->subFocusItem;
if (scopeFocusedItem) {
scopeItem = oldParentItem;
while (!scopeItem->isFocusScope() && scopeItem->parentItem())
scopeItem = scopeItem->parentItem();
if (d->window) {
if (QQuickDeliveryAgentPrivate *da = d->deliveryAgentPrivate()) {
da->clearFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
QQuickDeliveryAgentPrivate::DontChangeFocusProperty);
}
if (scopeFocusedItem != this)
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, true);
} else {
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, false);
}
}
const bool wasVisible = isVisible();
op->removeChild(this);
if (wasVisible && !op->inDestructor)
emit oldParentItem->visibleChildrenChanged();
} else if (d->window) {
QQuickWindowPrivate::get(d->window)->parentlessItems.remove(this);
}
QQuickWindow *parentWindow = parentItem ? QQuickItemPrivate::get(parentItem)->window : nullptr;
bool alreadyAddedChild = false;
if (d->window == parentWindow) {
// Avoid freeing and reallocating resources if the window stays the same.
d->parentItem = parentItem;
} else {
auto oldParentItem = d->parentItem;
d->parentItem = parentItem;
if (d->parentItem) {
QQuickItemPrivate::get(d->parentItem)->addChild(this);
alreadyAddedChild = true;
}
if (d->window) {
d->derefWindow();
// as we potentially changed d->parentWindow above
// the check in derefWindow could not work
// thus, we redo it here with the old parent
// Also, the window may have been deleted by derefWindow()
if (!oldParentItem && d->window) {
QQuickWindowPrivate::get(d->window)->parentlessItems.remove(this);
}
}
if (parentWindow)
d->refWindow(parentWindow);
}
d->dirty(QQuickItemPrivate::ParentChanged);
if (d->parentItem && !alreadyAddedChild)
QQuickItemPrivate::get(d->parentItem)->addChild(this);
else if (d->window && !alreadyAddedChild)
QQuickWindowPrivate::get(d->window)->parentlessItems.insert(this);
d->setEffectiveVisibleRecur(d->calcEffectiveVisible());
d->setEffectiveEnableRecur(nullptr, d->calcEffectiveEnable());
if (d->parentItem) {
if (!scopeFocusedItem) {
if (hasFocus())
scopeFocusedItem = this;
else if (!isFocusScope() && d->subFocusItem)
scopeFocusedItem = d->subFocusItem;
}
if (scopeFocusedItem) {
// We need to test whether this item becomes scope focused
QQuickItem *scopeItem = d->parentItem;
while (!scopeItem->isFocusScope() && scopeItem->parentItem())
scopeItem = scopeItem->parentItem();
if (QQuickItemPrivate::get(scopeItem)->subFocusItem
|| (!scopeItem->isFocusScope() && scopeItem->hasFocus())) {
if (scopeFocusedItem != this)
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, false);
QQuickItemPrivate::get(scopeFocusedItem)->focus = false;
emit scopeFocusedItem->focusChanged(false);
} else {
if (d->window) {
if (QQuickDeliveryAgentPrivate *da = d->deliveryAgentPrivate()) {
da->setFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason,
QQuickDeliveryAgentPrivate::DontChangeFocusProperty);
}
} else {
QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, true);
}
}
}
}
if (d->parentItem)
d->resolveLayoutMirror();
d->itemChange(ItemParentHasChanged, d->parentItem);
if (!d->inDestructor)
emit parentChanged(d->parentItem);
if (isVisible() && d->parentItem && !QQuickItemPrivate::get(d->parentItem)->inDestructor)
emit d->parentItem->visibleChildrenChanged();
#if QT_CONFIG(accessibility)
if (QGuiApplicationPrivate::is_app_running && !QGuiApplicationPrivate::is_app_closing && d->isAccessible && QAccessible::isActive()) {
QAccessibleEvent qaEvent(this, QAccessible::ParentChanged);
QAccessible::updateAccessibility(&qaEvent);
}
#endif
}
/*!
Moves this item to the index before the specified
sibling item within the list of children.
The order of children affects both the
visual stacking order and tab focus navigation order.
Assuming the z values of both items are the same, this will cause \a
sibling to be rendered above this item.
If both items have activeFocusOnTab set to \c true, this will also cause
the tab focus order to change, with \a sibling receiving focus after this
item.
The given \a sibling must be a sibling of this item; that is, they must
have the same immediate \l parent.
\sa {Concepts - Visual Parent in Qt Quick}
*/
void QQuickItem::stackBefore(const QQuickItem *sibling)
{
Q_D(QQuickItem);
if (!sibling || sibling == this || !d->parentItem || d->parentItem != QQuickItemPrivate::get(sibling)->parentItem) {
qWarning().nospace() << "QQuickItem::stackBefore: Cannot stack "
<< this << " before " << sibling << ", which must be a sibling";
return;
}
QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(d->parentItem);
int myIndex = parentPrivate->childItems.lastIndexOf(this);
int siblingIndex = parentPrivate->childItems.lastIndexOf(const_cast<QQuickItem *>(sibling));
Q_ASSERT(myIndex != -1 && siblingIndex != -1);
if (myIndex == siblingIndex - 1)
return;
parentPrivate->childItems.move(myIndex, myIndex < siblingIndex ? siblingIndex - 1 : siblingIndex);
parentPrivate->markSortedChildrenDirty(this);
parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
for (int ii = qMin(siblingIndex, myIndex); ii < parentPrivate->childItems.size(); ++ii)
QQuickItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
}
/*!
Moves this item to the index after the specified
sibling item within the list of children.
The order of children affects both the
visual stacking order and tab focus navigation order.
Assuming the z values of both items are the same, this will cause \a
sibling to be rendered below this item.
If both items have activeFocusOnTab set to \c true, this will also cause
the tab focus order to change, with \a sibling receiving focus before this
item.
The given \a sibling must be a sibling of this item; that is, they must
have the same immediate \l parent.
\sa {Concepts - Visual Parent in Qt Quick}
*/
void QQuickItem::stackAfter(const QQuickItem *sibling)
{
Q_D(QQuickItem);
if (!sibling || sibling == this || !d->parentItem || d->parentItem != QQuickItemPrivate::get(sibling)->parentItem) {
qWarning().nospace() << "QQuickItem::stackAfter: Cannot stack "
<< this << " after " << sibling << ", which must be a sibling";
return;
}
QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(d->parentItem);
int myIndex = parentPrivate->childItems.lastIndexOf(this);
int siblingIndex = parentPrivate->childItems.lastIndexOf(const_cast<QQuickItem *>(sibling));
Q_ASSERT(myIndex != -1 && siblingIndex != -1);
if (myIndex == siblingIndex + 1)
return;
parentPrivate->childItems.move(myIndex, myIndex > siblingIndex ? siblingIndex + 1 : siblingIndex);
parentPrivate->markSortedChildrenDirty(this);
parentPrivate->dirty(QQuickItemPrivate::ChildrenStackingChanged);
for (int ii = qMin(myIndex, siblingIndex + 1); ii < parentPrivate->childItems.size(); ++ii)
QQuickItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
}
/*! \fn void QQuickItem::windowChanged(QQuickWindow *window)
This signal is emitted when the item's \a window changes.
*/
/*!
Returns the window in which this item is rendered.
The item does not have a window until it has been assigned into a scene. The
\l windowChanged() signal provides a notification both when the item is entered
into a scene and when it is removed from a scene.
*/
QQuickWindow *QQuickItem::window() const
{
Q_D(const QQuickItem);
return d->window;
}
static bool itemZOrder_sort(QQuickItem *lhs, QQuickItem *rhs)
{
return lhs->z() < rhs->z();
}
QList<QQuickItem *> QQuickItemPrivate::paintOrderChildItems() const
{
if (sortedChildItems)
return *sortedChildItems;
// 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.size(); ++i) {
if (QQuickItemPrivate::get(childItems.at(i))->z() != 0.) {
haveZ = true;
break;
}
}
if (haveZ) {
sortedChildItems = new QList<QQuickItem*>(childItems);
std::stable_sort(sortedChildItems->begin(), sortedChildItems->end(), itemZOrder_sort);
return *sortedChildItems;
}
sortedChildItems = const_cast<QList<QQuickItem*>*>(&childItems);
return childItems;
}
void QQuickItemPrivate::addChild(QQuickItem *child)
{
Q_Q(QQuickItem);
Q_ASSERT(!childItems.contains(child));
childItems.append(child);
QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
#if QT_CONFIG(cursor)
// if the added child has a cursor and we do not currently have any children
// with cursors, bubble the notification up
if (childPrivate->subtreeCursorEnabled && !subtreeCursorEnabled)
setHasCursorInChild(true);
#endif
if (childPrivate->subtreeHoverEnabled && !subtreeHoverEnabled)
setHasHoverInChild(true);
childPrivate->recursiveRefFromEffectItem(extra.value().recursiveEffectRefCount);
markSortedChildrenDirty(child);
dirty(QQuickItemPrivate::ChildrenChanged);
itemChange(QQuickItem::ItemChildAddedChange, child);
emit q->childrenChanged();
}
void QQuickItemPrivate::removeChild(QQuickItem *child)
{
Q_Q(QQuickItem);
Q_ASSERT(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);
#if QT_CONFIG(cursor)
// turn it off, if nothing else is using it
if (childPrivate->subtreeCursorEnabled && subtreeCursorEnabled)
setHasCursorInChild(false);
#endif
if (childPrivate->subtreeHoverEnabled && subtreeHoverEnabled)
setHasHoverInChild(false);
childPrivate->recursiveRefFromEffectItem(-extra.value().recursiveEffectRefCount);
if (!inDestructor) {
markSortedChildrenDirty(child);
dirty(QQuickItemPrivate::ChildrenChanged);
}
itemChange(QQuickItem::ItemChildRemovedChange, child);
if (!inDestructor)
emit q->childrenChanged();
}
void QQuickItemPrivate::refWindow(QQuickWindow *c)
{
// An item needs a window if it is referenced by another item which has a window.
// Typically the item is referenced by a parent, but can also be referenced by a
// ShaderEffect or ShaderEffectSource. 'windowRefCount' counts how many items with
// a window is referencing this item. When the reference count goes from zero to one,
// or one to zero, the window of this item is updated and propagated to the children.
// As long as the reference count stays above zero, the window is unchanged.
// refWindow() increments the reference count.
// derefWindow() decrements the reference count.
Q_Q(QQuickItem);
Q_ASSERT((window != nullptr) == (windowRefCount > 0));
Q_ASSERT(c);
if (++windowRefCount > 1) {
if (c != window)
qWarning("QQuickItem: Cannot use same item on different windows at the same time.");
return; // Window already set.
}
Q_ASSERT(window == nullptr);
window = c;
if (polishScheduled)
QQuickWindowPrivate::get(window)->itemsToPolish.append(q);
if (!parentItem)
QQuickWindowPrivate::get(window)->parentlessItems.insert(q);
for (int ii = 0; ii < childItems.size(); ++ii) {
QQuickItem *child = childItems.at(ii);
QQuickItemPrivate::get(child)->refWindow(c);
}
dirty(Window);
if (extra.isAllocated() && extra->screenAttached)
extra->screenAttached->windowChanged(c);
itemChange(QQuickItem::ItemSceneChange, c);
}
void QQuickItemPrivate::derefWindow()
{
Q_Q(QQuickItem);
Q_ASSERT((window != nullptr) == (windowRefCount > 0));
if (!window)
return; // This can happen when destroying recursive shader effect sources.
if (--windowRefCount > 0)
return; // There are still other references, so don't set window to null yet.
q->releaseResources();
removeFromDirtyList();
QQuickWindowPrivate *c = QQuickWindowPrivate::get(window);
if (polishScheduled)
c->itemsToPolish.removeOne(q);
#if QT_CONFIG(cursor)
if (c->cursorItem == q) {
c->cursorItem = nullptr;
window->unsetCursor();
}
#endif
if (itemNodeInstance)
c->cleanup(itemNodeInstance);
if (!parentItem)
c->parentlessItems.remove(q);
if (auto *da = deliveryAgentPrivate()) {
if (da->activeFocusItem == q) {
qCDebug(lcFocus) << "Removing active focus item from window's delivery agent";
da->activeFocusItem = nullptr;
}
}
window = nullptr;
itemNodeInstance = nullptr;
if (extra.isAllocated()) {
extra->opacityNode = nullptr;
extra->clipNode = nullptr;
extra->rootNode = nullptr;
}
paintNode = nullptr;
for (int ii = 0; ii < childItems.size(); ++ii) {
if (QQuickItem *child = childItems.at(ii))
QQuickItemPrivate::get(child)->derefWindow();
}
dirty(Window);
if (extra.isAllocated() && extra->screenAttached)
extra->screenAttached->windowChanged(nullptr);
itemChange(QQuickItem::ItemSceneChange, (QQuickWindow *)nullptr);
}
qreal QQuickItemPrivate::effectiveDevicePixelRatio() const
{
return (window ? window->effectiveDevicePixelRatio() : qApp->devicePixelRatio());
}
#ifdef QT_BUILD_INTERNAL
quint32 QQuickItemPrivate::item_counter = 0;
quint32 QQuickItemPrivate::itemExtra_counter = 0;
quint32 QQuickItemPrivate::eventHandlingChildrenWithinBounds_counter = 0;
quint64 QQuickItemPrivate::itemToParentTransform_counter = 0;
quint64 QQuickItemPrivate::itemToWindowTransform_counter = 0;
quint64 QQuickItemPrivate::windowToItemTransform_counter = 0;
quint64 QQuickItemPrivate::effectiveClippingSkips_counter = 0;
#endif
/*!
Returns a transform that maps points from window space into item space.
*/
QTransform QQuickItemPrivate::windowToItemTransform() const
{
// XXX todo - optimize
#ifdef QT_BUILD_INTERNAL
++windowToItemTransform_counter;
#endif
return itemToWindowTransform().inverted();
}
/*!
Returns a transform that maps points from item space into window space.
*/
QTransform QQuickItemPrivate::itemToWindowTransform() const
{
#ifdef QT_BUILD_INTERNAL
++itemToWindowTransform_counter;
#endif
// 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;
}
/*!
Modifies \a t with this item's local transform relative to its parent.
*/
void QQuickItemPrivate::itemToParentTransform(QTransform *t) const
{
#ifdef QT_BUILD_INTERNAL
++itemToParentTransform_counter;
#endif
/* 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);
if (!transforms.isEmpty()) {
QMatrix4x4 m(*t);
for (int ii = transforms.size() - 1; ii >= 0; --ii)
transforms.at(ii)->applyTo(&m);
*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());
}
}
/*!
Returns true if construction of the QML component is complete; otherwise
returns false.
It is often desirable to delay some processing until the component is
completed.
\sa componentComplete()
*/
bool QQuickItem::isComponentComplete() const
{
Q_D(const QQuickItem);
return d->componentComplete;
}
QQuickItemPrivate::QQuickItemPrivate()
: _anchors(nullptr)
, _stateGroup(nullptr)
, flags(0)
, widthValidFlag(false)
, heightValidFlag(false)
, componentComplete(true)
, keepMouse(false)
, keepTouch(false)
, hoverEnabled(false)
, smooth(true)
, antialiasing(false)
, focus(false)
, activeFocus(false)
, notifiedFocus(false)
, notifiedActiveFocus(false)
, filtersChildMouseEvents(false)
, explicitVisible(true)
, effectiveVisible(true)
, explicitEnable(true)
, effectiveEnable(true)
, polishScheduled(false)
, inheritedLayoutMirror(false)
, effectiveLayoutMirror(false)
, isMirrorImplicit(true)
, inheritMirrorFromParent(false)
, inheritMirrorFromItem(false)
, isAccessible(false)
, culled(false)
, hasCursor(false)
, subtreeCursorEnabled(false)
, subtreeHoverEnabled(false)
, activeFocusOnTab(false)
, implicitAntialiasing(false)
, antialiasingValid(false)
, isTabFence(false)
, replayingPressEvent(false)
, touchEnabled(false)
, hasCursorHandler(false)
, maybeHasSubsceneDeliveryAgent(true)
, subtreeTransformChangedEnabled(true)
, inDestructor(false)
, focusReason(Qt::OtherFocusReason)
, focusPolicy(Qt::NoFocus)
, eventHandlingChildrenWithinBounds(false)
, eventHandlingChildrenWithinBoundsSet(false)
, customOverlay(false)
, dirtyAttributes(0)
, nextDirtyItem(nullptr)
, prevDirtyItem(nullptr)
, window(nullptr)
, windowRefCount(0)
, parentItem(nullptr)
, sortedChildItems(&childItems)
, subFocusItem(nullptr)
, x(0)
, y(0)
, width(0)
, height(0)
, implicitWidth(0)
, implicitHeight(0)
, baselineOffset(0)
, itemNodeInstance(nullptr)
, paintNode(nullptr)
, szPolicy(QLayoutPolicy::Fixed, QLayoutPolicy::Fixed)
{
#ifdef QT_BUILD_INTERNAL
++item_counter;
#endif
}
QQuickItemPrivate::~QQuickItemPrivate()
{
if (sortedChildItems != &childItems)
delete sortedChildItems;
}
void QQuickItemPrivate::init(QQuickItem *parent)
{
Q_Q(QQuickItem);
isQuickItem = true;
baselineOffset = 0.0;
if (parent) {
q->setParentItem(parent);
QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parent);
setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent);
}
}
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)
return;
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
if (QQuickItem *item = qmlobject_cast<QQuickItem *>(o)) {
item->setParentItem(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<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
a child and if you assign any other object type, it is added as a resource.
So you can write:
\qml
Item {
Text {}
Rectangle {}
Timer {}
}
\endqml
instead of:
\qml
Item {
children: [
Text {},
Rectangle {}
]
resources: [
Timer {}
]
}
\endqml
It should not generally be necessary to refer to the \c data property,
as it is the default property for Item and thus all child items are
automatically assigned to this property.
*/
qsizetype QQuickItemPrivate::data_count(QQmlListProperty<QObject> *property)
{
QQuickItem *item = static_cast<QQuickItem*>(property->object);
QQuickItemPrivate *privateItem = QQuickItemPrivate::get(item);
QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
QQmlListProperty<QQuickItem> childrenProperty = privateItem->children();
return resources_count(&resourcesProperty) + children_count(&childrenProperty);
}
QObject *QQuickItemPrivate::data_at(QQmlListProperty<QObject> *property, qsizetype i)
{
QQuickItem *item = static_cast<QQuickItem*>(property->object);
QQuickItemPrivate *privateItem = QQuickItemPrivate::get(item);
QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
QQmlListProperty<QQuickItem> childrenProperty = privateItem->children();
qsizetype resourcesCount = resources_count(&resourcesProperty);
if (i < resourcesCount)
return resources_at(&resourcesProperty, i);
const qsizetype j = i - resourcesCount;
if (j < children_count(&childrenProperty))
return children_at(&childrenProperty, j);
return nullptr;
}
void QQuickItemPrivate::data_clear(QQmlListProperty<QObject> *property)
{
QQuickItem *item = static_cast<QQuickItem*>(property->object);
QQuickItemPrivate *privateItem = QQuickItemPrivate::get(item);
QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
QQmlListProperty<QQuickItem> childrenProperty = privateItem->children();
resources_clear(&resourcesProperty);
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));
return quickItemPrivate->extra.isAllocated() ? quickItemPrivate->extra->resourcesList.value(index) : 0;
}
void QQuickItemPrivate::resources_append(QQmlListProperty<QObject> *prop, QObject *object)
{
QQuickItem *quickItem = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(quickItem);
if (!quickItemPrivate->extra.value().resourcesList.contains(object)) {
quickItemPrivate->extra.value().resourcesList.append(object);
qmlobject_connect(object, QObject, SIGNAL(destroyed(QObject*)),
quickItem, QQuickItem, SLOT(_q_resourceObjectDeleted(QObject*)));
}
}
qsizetype QQuickItemPrivate::resources_count(QQmlListProperty<QObject> *prop)
{
QQuickItemPrivate *quickItemPrivate = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
return quickItemPrivate->extra.isAllocated() ? quickItemPrivate->extra->resourcesList.size() : 0;
}
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 : std::as_const(quickItemPrivate->extra->resourcesList)) {
qmlobject_disconnect(object, QObject, SIGNAL(destroyed(QObject*)),
quickItem, QQuickItem, SLOT(_q_resourceObjectDeleted(QObject*)));
}
quickItemPrivate->extra->resourcesList.clear();
}
}
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.size() || index < 0)
return nullptr;
else
return p->childItems.at(index);
}
void QQuickItemPrivate::children_append(QQmlListProperty<QQuickItem> *prop, QQuickItem *o)
{
if (!o)
return;
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
if (o->parentItem() == that)
o->setParentItem(nullptr);
o->setParentItem(that);
}
qsizetype QQuickItemPrivate::children_count(QQmlListProperty<QQuickItem> *prop)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
return p->childItems.size();
}
void QQuickItemPrivate::children_clear(QQmlListProperty<QQuickItem> *prop)
{
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *p = QQuickItemPrivate::get(that);
while (!p->childItems.isEmpty())
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.size();
while (c--) {
if (p->childItems.at(c)->isVisible()) visibleCount++;
}
return visibleCount;
}
QQuickItem *QQuickItemPrivate::visibleChildren_at(QQmlListProperty<QQuickItem> *prop, qsizetype index)
{
QQuickItemPrivate *p = QQuickItemPrivate::get(static_cast<QQuickItem *>(prop->object));
const qsizetype childCount = p->childItems.size();
if (index >= childCount || index < 0)
return nullptr;
qsizetype visibleCount = -1;
for (qsizetype i = 0; i < childCount; i++) {
if (p->childItems.at(i)->isVisible()) visibleCount++;
if (visibleCount == index) return p->childItems.at(i);
}
return nullptr;
}
qsizetype QQuickItemPrivate::transform_count(QQmlListProperty<QQuickTransform> *prop)
{
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *p = QQuickItemPrivate::get(that);
return p->transforms.size();
}
void QQuickTransform::appendToItem(QQuickItem *item)
{
Q_D(QQuickTransform);
if (!item)
return;
QQuickItemPrivate *p = QQuickItemPrivate::get(item);
if (!d->items.isEmpty() && !p->transforms.isEmpty() && p->transforms.contains(this)) {
p->transforms.removeOne(this);
p->transforms.append(this);
} else {
p->transforms.append(this);
d->items.append(item);
}
p->dirty(QQuickItemPrivate::Transform);
}
void QQuickTransform::prependToItem(QQuickItem *item)
{
Q_D(QQuickTransform);
if (!item)
return;
QQuickItemPrivate *p = QQuickItemPrivate::get(item);
if (!d->items.isEmpty() && !p->transforms.isEmpty() && p->transforms.contains(this)) {
p->transforms.removeOne(this);
p->transforms.prepend(this);
} else {
p->transforms.prepend(this);
d->items.append(item);
}
p->dirty(QQuickItemPrivate::Transform);
}
void QQuickItemPrivate::transform_append(QQmlListProperty<QQuickTransform> *prop, QQuickTransform *transform)
{
if (!transform)
return;
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
transform->appendToItem(that);
}
QQuickTransform *QQuickItemPrivate::transform_at(QQmlListProperty<QQuickTransform> *prop, qsizetype idx)
{
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *p = QQuickItemPrivate::get(that);
if (idx < 0 || idx >= p->transforms.size())
return nullptr;
else
return p->transforms.at(idx);
}
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.size(); ++ii) {
QQuickTransform *t = p->transforms.at(ii);
QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t);
tp->items.removeOne(that);
}
p->transforms.clear();
p->dirty(QQuickItemPrivate::Transform);
}
void QQuickItemPrivate::_q_resourceObjectDeleted(QObject *object)
{
if (extra.isAllocated() && extra->resourcesList.contains(object))
extra->resourcesList.removeAll(object);
}
/*!
\qmlpropertygroup QtQuick::Item::anchors
\qmlproperty AnchorLine QtQuick::Item::anchors.top
\qmlproperty AnchorLine QtQuick::Item::anchors.bottom
\qmlproperty AnchorLine QtQuick::Item::anchors.left
\qmlproperty AnchorLine QtQuick::Item::anchors.right
\qmlproperty AnchorLine QtQuick::Item::anchors.horizontalCenter
\qmlproperty AnchorLine QtQuick::Item::anchors.verticalCenter
\qmlproperty AnchorLine QtQuick::Item::anchors.baseline
\qmlproperty Item QtQuick::Item::anchors.fill
\qmlproperty Item QtQuick::Item::anchors.centerIn
\qmlproperty real QtQuick::Item::anchors.margins
\qmlproperty real QtQuick::Item::anchors.topMargin
\qmlproperty real QtQuick::Item::anchors.bottomMargin
\qmlproperty real QtQuick::Item::anchors.leftMargin
\qmlproperty real QtQuick::Item::anchors.rightMargin
\qmlproperty real QtQuick::Item::anchors.horizontalCenterOffset
\qmlproperty real QtQuick::Item::anchors.verticalCenterOffset
\qmlproperty real QtQuick::Item::anchors.baselineOffset
\qmlproperty bool QtQuick::Item::anchors.alignWhenCentered
Anchors provide a way to position an item by specifying its
relationship with other items.
Margins apply to top, bottom, left, right, and fill anchors.
The \l anchors.margins property can be used to set all of the various margins at once, to the same value.
It will not override a specific margin that has been previously set; to clear an explicit margin
set its value to \c undefined.
Note that margins are anchor-specific and are not applied if an item does not
use anchors.
Offsets apply for horizontal center, vertical center, and baseline anchors.
\table
\row
\li \image declarative-anchors_example.png {Text labeled 'label' anchored
horizontally centered below item labeled 'pic'}
\li Text anchored to Image, horizontally centered and vertically below, with a margin.
\qml
Item {
Image {
id: pic
// ...
}
Text {
id: label
anchors.horizontalCenter: pic.horizontalCenter
anchors.top: pic.bottom
anchors.topMargin: 5
// ...
}
}
\endqml
\row
\li \image declarative-anchors_example2.png {Text labeled 'label' anchored
to the right of item labeled 'pic' with margin}
\li
Left of Text anchored to right of Image, with a margin. The y
property of both defaults to 0.
\qml
Item {
Image {
id: pic
// ...
}
Text {
id: label
anchors.left: pic.right
anchors.leftMargin: 5
// ...
}
}
\endqml
\endtable
\l anchors.fill provides a convenient way for one item to have the
same geometry as another item, and is equivalent to connecting all
four directional anchors.
To clear an anchor value, set it to \c undefined.
\l anchors.alignWhenCentered (default \c true) forces centered anchors to align to a
whole pixel; if the item being centered has an odd \l width or \l height, the item
will be positioned on a whole pixel rather than being placed on a half-pixel.
This ensures the item is painted crisply. There are cases where this is not
desirable, for example when rotating the item jitters may be apparent as the
center is rounded.
\note You can only anchor an item to siblings or a parent.
For more information see \l {anchor-layout}{Anchor Layouts}.
*/
QQuickAnchors *QQuickItemPrivate::anchors() const
{
if (!_anchors) {
Q_Q(const QQuickItem);
_anchors = new QQuickAnchors(const_cast<QQuickItem *>(q));
if (!componentComplete)
_anchors->classBegin();
}
return _anchors;
}
void QQuickItemPrivate::siblingOrderChanged()
{
Q_Q(QQuickItem);
notifyChangeListeners(QQuickItemPrivate::SiblingOrder, &QQuickItemChangeListener::itemSiblingOrderChanged, q);
}
QQmlListProperty<QObject> QQuickItemPrivate::data()
{
// 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;
}
/*!
\qmlpropertygroup QtQuick::Item::childrenRect
\qmlproperty real QtQuick::Item::childrenRect.x
\qmlproperty real QtQuick::Item::childrenRect.y
\qmlproperty real QtQuick::Item::childrenRect.width
\qmlproperty real QtQuick::Item::childrenRect.height
\readonly
This read-only property holds the collective position and size of the item's
children.
This property is useful if you need to access the collective geometry
of an item's children in order to correctly size the item.
The geometry that is returned is local to the item. For example:
\snippet qml/item/childrenRect.qml local
*/
/*!
\property QQuickItem::childrenRect
This property holds the collective position and size of the item's
children.
This property is useful if you need to access the collective geometry
of an item's children in order to correctly size the item.
The geometry that is returned is local to the item. For example:
\snippet qml/item/childrenRect.qml local
*/
QRectF QQuickItem::childrenRect()
{
Q_D(QQuickItem);
if (!d->extra.isAllocated() || !d->extra->contents) {
d->extra.value().contents = new QQuickContents(this);
if (d->componentComplete)
d->extra->contents->complete();
}
return d->extra->contents->rectF();
}
/*!
Returns the children of this item.
*/
QList<QQuickItem *> QQuickItem::childItems() const
{
Q_D(const QQuickItem);
return d->childItems;
}
/*!
\qmlproperty bool QtQuick::Item::clip
This property holds whether clipping is enabled. The default clip value is \c false.
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
This property holds whether clipping is enabled. The default clip value is \c false.
If clipping is enabled, an item will clip its own painting, as well
as the painting of its children, to its bounding rectangle. If you set
clipping during an item's paint operation, remember to re-set it to
prevent clipping the rest of your scene.
\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
{
return flags() & ItemClipsChildrenToShape;
}
void QQuickItem::setClip(bool c)
{
if (clip() == c)
return;
setFlag(ItemClipsChildrenToShape, c);
if (c)
setFlag(ItemIsViewport);
else if (!(inherits("QQuickFlickable") || inherits("QQuickRootItem")))
setFlag(ItemIsViewport, false);
emit clipChanged(c);
}
/*!
\since 6.0
This function is called to handle this item's changes in
geometry from \a oldGeometry to \a newGeometry. If the two
geometries are the same, it doesn't do anything.
Derived classes must call the base class method within their implementation.
*/
void QQuickItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickItem);
if (d->_anchors)
QQuickAnchorsPrivate::get(d->_anchors)->updateMe();
QQuickGeometryChange change;
change.setXChange(newGeometry.x() != oldGeometry.x());
change.setYChange(newGeometry.y() != oldGeometry.y());
change.setWidthChange(newGeometry.width() != oldGeometry.width());
change.setHeightChange(newGeometry.height() != oldGeometry.height());
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())
d->x.notify();
if (change.yChange())
d->y.notify();
if (change.widthChange())
d->width.notify();
if (change.heightChange())
d->height.notify();
#if QT_CONFIG(accessibility)
if (d->isAccessible && QAccessible::isActive() && d->effectiveVisible) {
QAccessibleEvent ev(this, QAccessible::LocationChanged);
QAccessible::updateAccessibility(&ev);
}
#endif
}
/*!
Called on the render thread when it is time to sync the state
of the item with the scene graph.
The function is called as a result of QQuickItem::update(), if
the user has set the QQuickItem::ItemHasContents flag on the item.
The function should return the root of the scene graph subtree for
this item. Most implementations will return a single
QSGGeometryNode containing the visual representation of this item.
\a oldNode is the node that was returned the last time the
function was called. \a updatePaintNodeData provides a pointer to
the QSGTransformNode associated with this QQuickItem.
\code
QSGNode *MyItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
{
QSGSimpleRectNode *n = static_cast<QSGSimpleRectNode *>(node);
if (!n) {
n = new QSGSimpleRectNode();
n->setColor(Qt::red);
}
n->setRect(boundingRect());
return n;
}
\endcode
The main thread is blocked while this function is executed so it is safe to read
values from the QQuickItem instance and other objects in the main thread.
If no call to QQuickItem::updatePaintNode() result in actual scene graph
changes, like QSGNode::markDirty() or adding and removing nodes, then
the underlying implementation may decide to not render the scene again as
the visual outcome is identical.
\warning It is crucial that graphics operations and interaction with
the scene graph happens exclusively on the render thread,
primarily during the QQuickItem::updatePaintNode() call. The best
rule of thumb is to only use classes with the "QSG" prefix inside
the QQuickItem::updatePaintNode() function.
\warning This function is called on the render thread. This means any
QObjects or thread local storage that is created will have affinity to the
render thread, so apply caution when doing anything other than rendering
in this function. Similarly for signals, these will be emitted on the render
thread and will thus often be delivered via queued connections.
\note All classes with QSG prefix should be used solely on the scene graph's
rendering thread. See \l {Scene Graph and Rendering} for more information.
\sa QSGMaterial, QSGGeometryNode, QSGGeometry,
QSGFlatColorMaterial, QSGTextureMaterial, QSGNode::markDirty(), {Graphics Resource Handling}
*/
QSGNode *QQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
{
Q_UNUSED(updatePaintNodeData);
delete oldNode;
return nullptr;
}
QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
: transformNode(nullptr)
{
}
/*!
This function is called when an item should release graphics
resources which are not already managed by the nodes returned from
QQuickItem::updatePaintNode().
This happens when the item is about to be removed from the window it
was previously rendering to. The item is guaranteed to have a
\l {QQuickItem::window()}{window} when the function is called.
The function is called on the GUI thread and the state of the
rendering thread, when it is used, is unknown. Objects should
not be deleted directly, but instead scheduled for cleanup
using QQuickWindow::scheduleRenderJob().
\sa {Graphics Resource Handling}
*/
void QQuickItem::releaseResources()
{
}
QSGTransformNode *QQuickItemPrivate::createTransformNode()
{
return new QSGTransformNode;
}
/*!
This function should perform any layout as required for this item.
When polish() is called, the scene graph schedules a polish event for this
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)
{
Q_Q(QQuickItem);
changeListeners.append(ChangeListener(listener, types));
listener->addSourceItem(q);
if (lcChangeListeners().isDebugEnabled())
PRINT_LISTENERS();
}
void QQuickItemPrivate::updateOrAddItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types)
{
Q_Q(QQuickItem);
const ChangeListener changeListener(listener, types);
const int index = changeListeners.indexOf(changeListener);
if (index > -1) {
changeListeners[index].types = changeListener.types;
} else {
changeListeners.append(changeListener);
listener->addSourceItem(q);
}
if (lcChangeListeners().isDebugEnabled())
PRINT_LISTENERS();
}
void QQuickItemPrivate::removeItemChangeListener(QQuickItemChangeListener *listener, ChangeTypes types)
{
Q_Q(QQuickItem);
ChangeListener change(listener, types);
changeListeners.removeOne(change);
listener->removeSourceItem(q);
if (lcChangeListeners().isDebugEnabled())
PRINT_LISTENERS();
}
void QQuickItemPrivate::updateOrAddGeometryChangeListener(QQuickItemChangeListener *listener,
QQuickGeometryChange types)
{
Q_Q(QQuickItem);
ChangeListener change(listener, types);
int index = changeListeners.indexOf(change);
if (index > -1) {
changeListeners[index].gTypes = change.gTypes; //we may have different GeometryChangeTypes
} else {
changeListeners.append(change);
listener->addSourceItem(q);
}
if (lcChangeListeners().isDebugEnabled())
PRINT_LISTENERS();
}
void QQuickItemPrivate::updateOrRemoveGeometryChangeListener(QQuickItemChangeListener *listener,
QQuickGeometryChange types)
{
Q_Q(QQuickItem);
ChangeListener change(listener, types);
if (types.noChange()) {
changeListeners.removeOne(change);
listener->removeSourceItem(q);
} else {
int index = changeListeners.indexOf(change);
if (index > -1)
changeListeners[index].gTypes = change.gTypes; //we may have different GeometryChangeTypes
}
if (lcChangeListeners().isDebugEnabled())
PRINT_LISTENERS();
}
/*!
This event handler can be reimplemented in a subclass to receive key
press events for an item. The event information is provided by the
\a event parameter.
\input item.qdocinc accepting-events
*/
void QQuickItem::keyPressEvent(QKeyEvent *event)
{
event->ignore();
}
/*!
This event handler can be reimplemented in a subclass to receive key
release events for an item. The event information is provided by the
\a event parameter.
\input item.qdocinc accepting-events
*/
void QQuickItem::keyReleaseEvent(QKeyEvent *event)
{
event->ignore();
}
#if QT_CONFIG(im)
/*!
This event handler can be reimplemented in a subclass to receive input
method events for an item. The event information is provided by the
\a event parameter.
\input item.qdocinc accepting-events
*/
void QQuickItem::inputMethodEvent(QInputMethodEvent *event)
{
event->ignore();
}
#endif // im
/*!
This event handler can be reimplemented in a subclass to receive focus-in
events for an item. The event information is provided by the \a event
parameter.
\input item.qdocinc accepting-events
If you do reimplement this function, you should call the base class
implementation.
*/
void QQuickItem::focusInEvent(QFocusEvent *event)
{
Q_D(QQuickItem);
#if QT_CONFIG(accessibility)
if (d->isAccessible && QAccessible::isActive()) {
if (QObject *acc = QQuickAccessibleAttached::findAccessible(this)) {
QAccessibleEvent ev(acc, QAccessible::Focus);
QAccessible::updateAccessibility(&ev);
}
}
#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 \a event
parameter.
\input item.qdocinc accepting-events
*/
void QQuickItem::focusOutEvent(QFocusEvent *event)
{
Q_D(QQuickItem);
d->setLastFocusChangeReason(event->reason());
}
/*!
This event handler can be reimplemented in a subclass to receive mouse
press events for an item. The event information is provided by the
\a event parameter.
In order to receive mouse press events, \l acceptedMouseButtons() must
return the relevant mouse button.
\input item.qdocinc accepting-events
*/
void QQuickItem::mousePressEvent(QMouseEvent *event)
{
event->ignore();
}
/*!
This event handler can be reimplemented in a subclass to receive mouse
move events for an item. The event information is provided by the
\a event parameter.
In order to receive mouse movement events, the preceding mouse press event
must be accepted (by overriding \l mousePressEvent(), for example) and
\l acceptedMouseButtons() must return the relevant mouse button.
\input item.qdocinc accepting-events
*/
void QQuickItem::mouseMoveEvent(QMouseEvent *event)
{
event->ignore();
}
/*!
This event handler can be reimplemented in a subclass to receive mouse
release events for an item. The event information is provided by the
\a event parameter.
In order to receive mouse release events, the preceding mouse press event
must be accepted (by overriding \l mousePressEvent(), for example) and
\l acceptedMouseButtons() must return the relevant mouse button.
\input item.qdocinc accepting-events
*/
void QQuickItem::mouseReleaseEvent(QMouseEvent *event)
{
event->ignore();
}
/*!
This event handler can be reimplemented in a subclass to receive mouse
double-click events for an item. The event information is provided by the
\a event parameter.
\input item.qdocinc accepting-events
*/
void QQuickItem::mouseDoubleClickEvent(QMouseEvent *event)
{
event->ignore();
}
/*!
This event handler can be reimplemented in a subclass to be notified
when a mouse ungrab event has occurred on this item.
*/
void QQuickItem::mouseUngrabEvent()
{
// XXX todo
}
/*!
This event handler can be reimplemented in a subclass to be notified
when a touch ungrab event has occurred on this item.
*/
void QQuickItem::touchUngrabEvent()
{
// XXX todo
}
#if QT_CONFIG(wheelevent)
/*!
This event handler can be reimplemented in a subclass to receive
wheel events for an item. The event information is provided by the
\a event parameter.
\input item.qdocinc accepting-events
*/
void QQuickItem::wheelEvent(QWheelEvent *event)
{
event->ignore();
}
#endif
/*!
This event handler can be reimplemented in a subclass to receive touch
events for an item. The event information is provided by the
\a event parameter.
\input item.qdocinc accepting-events
*/
void QQuickItem::touchEvent(QTouchEvent *event)
{
event->ignore();
}
/*!
This event handler can be reimplemented in a subclass to receive hover-enter
events for an item. The event information is provided by the
\a event parameter.
Hover events are only provided if acceptHoverEvents() is true.
\input item.qdocinc accepting-events
*/
void QQuickItem::hoverEnterEvent(QHoverEvent *event)
{
event->ignore();
}
/*!
This event handler can be reimplemented in a subclass to receive hover-move
events for an item. The event information is provided by the
\a event parameter.
Hover events are only provided if acceptHoverEvents() is true.
\input item.qdocinc accepting-events
*/
void QQuickItem::hoverMoveEvent(QHoverEvent *event)
{
event->ignore();
}
/*!
This event handler can be reimplemented in a subclass to receive hover-leave
events for an item. The event information is provided by the
\a event parameter.
Hover events are only provided if acceptHoverEvents() is true.
\input item.qdocinc accepting-events
*/
void QQuickItem::hoverLeaveEvent(QHoverEvent *event)
{
event->ignore();
}
#if QT_CONFIG(quick_draganddrop)
/*!
This event handler can be reimplemented in a subclass to receive drag-enter
events for an item. The event information is provided by the
\a event parameter.
Drag and drop events are only provided if the ItemAcceptsDrops flag
has been set for this item.
\input item.qdocinc accepting-events
\sa Drag, {Drag and Drop}
*/
void QQuickItem::dragEnterEvent(QDragEnterEvent *event)
{
Q_UNUSED(event);
}
/*!
This event handler can be reimplemented in a subclass to receive drag-move
events for an item. The event information is provided by the
\a event parameter.
Drag and drop events are only provided if the ItemAcceptsDrops flag
has been set for this item.
\input item.qdocinc accepting-events
\sa Drag, {Drag and Drop}
*/
void QQuickItem::dragMoveEvent(QDragMoveEvent *event)
{
Q_UNUSED(event);
}
/*!
This event handler can be reimplemented in a subclass to receive drag-leave
events for an item. The event information is provided by the
\a event parameter.
Drag and drop events are only provided if the ItemAcceptsDrops flag
has been set for this item.
\input item.qdocinc accepting-events
\sa Drag, {Drag and Drop}
*/
void QQuickItem::dragLeaveEvent(QDragLeaveEvent *event)
{
Q_UNUSED(event);
}
/*!
This event handler can be reimplemented in a subclass to receive drop
events for an item. The event information is provided by the
\a event parameter.
Drag and drop events are only provided if the ItemAcceptsDrops flag
has been set for this item.
\input item.qdocinc accepting-events
\sa Drag, {Drag and Drop}
*/
void QQuickItem::dropEvent(QDropEvent *event)
{
Q_UNUSED(event);
}
#endif // quick_draganddrop
/*!
Reimplement this method to filter the pointer events that are received by
this item's children.
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. 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
events). When overriding this function in a subclass, we suggest writing
generic event-handling code using only the accessors found in
QPointerEvent. Alternatively you can switch on \c event->type() and/or
\c event->device()->type() to handle different event types in different ways.
\note Filtering is just one way to share responsibility in case of gestural
ambiguity (for example on press, you don't know whether the user will tap
or drag). Another way is to call QPointerEvent::addPassiveGrabber() on
press, so as to non-exclusively monitor the progress of the QEventPoint.
In either case, the item or pointer handler that is monitoring can steal
the exclusive grab later on, when it becomes clear that the gesture fits
the pattern that it is expecting.
\sa setFiltersChildMouseEvents()
*/
bool QQuickItem::childMouseEventFilter(QQuickItem *item, QEvent *event)
{
Q_UNUSED(item);
Q_UNUSED(event);
return false;
}
#if QT_CONFIG(im)
/*!
This method is only relevant for input items.
If this item is an input item, this method should be reimplemented to
return the relevant input method flags for the given \a query.
\sa QWidget::inputMethodQuery()
*/
QVariant QQuickItem::inputMethodQuery(Qt::InputMethodQuery query) const
{
Q_D(const QQuickItem);
QVariant v;
switch (query) {
case Qt::ImEnabled:
v = (bool)(flags() & ItemAcceptsInputMethod);
break;
case Qt::ImHints:
case Qt::ImAnchorRectangle:
case Qt::ImCursorRectangle:
case Qt::ImFont:
case Qt::ImCursorPosition:
case Qt::ImSurroundingText:
case Qt::ImCurrentSelection:
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;
case Qt::ImEnterKeyType:
if (d->extra.isAllocated() && d->extra->enterKeyAttached)
v = d->extra->enterKeyAttached->type();
break;
case Qt::ImInputItemClipRectangle:
if (!(!window() ||!isVisible() || qFuzzyIsNull(opacity()))) {
QRectF rect = QRectF(0,0, width(), height());
const QQuickItem *par = this;
while (QQuickItem *parpar = par->parentItem()) {
rect = parpar->mapRectFromItem(par, rect);
if (parpar->clip())
rect = rect.intersected(parpar->clipRect());
par = parpar;
}
rect = par->mapRectToScene(rect);
// once we have the rect in scene coordinates, clip to window
rect = rect.intersected(QRectF(QPoint(0,0), window()->size()));
// map it back to local coordinates
v = mapRectFromScene(rect);
}
break;
default:
break;
}
return v;
}
#endif // im
QQuickAnchorLine QQuickItemPrivate::left() const
{
Q_Q(const QQuickItem);
return QQuickAnchorLine(const_cast<QQuickItem *>(q), QQuickAnchors::LeftAnchor);
}
QQuickAnchorLine QQuickItemPrivate::right() const
{
Q_Q(const QQuickItem);
return QQuickAnchorLine(const_cast<QQuickItem *>(q), QQuickAnchors::RightAnchor);
}
QQuickAnchorLine QQuickItemPrivate::horizontalCenter() const
{
Q_Q(const QQuickItem);
return QQuickAnchorLine(const_cast<QQuickItem *>(q), QQuickAnchors::HCenterAnchor);
}
QQuickAnchorLine QQuickItemPrivate::top() const
{
Q_Q(const QQuickItem);
return QQuickAnchorLine(const_cast<QQuickItem *>(q), QQuickAnchors::TopAnchor);
}
QQuickAnchorLine QQuickItemPrivate::bottom() const
{
Q_Q(const QQuickItem);
return QQuickAnchorLine(const_cast<QQuickItem *>(q), QQuickAnchors::BottomAnchor);
}
QQuickAnchorLine QQuickItemPrivate::verticalCenter() const
{
Q_Q(const QQuickItem);
return QQuickAnchorLine(const_cast<QQuickItem *>(q), QQuickAnchors::VCenterAnchor);
}
QQuickAnchorLine QQuickItemPrivate::baseline() const
{
Q_Q(const QQuickItem);
return QQuickAnchorLine(const_cast<QQuickItem *>(q), QQuickAnchors::BaselineAnchor);
}
/*!
\qmlproperty int QtQuick::Item::baselineOffset
Specifies the position of the item's baseline in local coordinates.
The baseline of a \l Text item is the imaginary line on which the text
sits. Controls containing text usually set their baseline to the
baseline of their text.
For non-text items, a default baseline offset of 0 is used.
*/
/*!
\property QQuickItem::baselineOffset
Specifies the position of the item's baseline in local coordinates.
The baseline of a \l Text item is the imaginary line on which the text
sits. Controls containing text usually set their baseline to the
baseline of their text.
For non-text items, a default baseline offset of 0 is used.
*/
qreal QQuickItem::baselineOffset() const
{
Q_D(const QQuickItem);
return d->baselineOffset;
}
void QQuickItem::setBaselineOffset(qreal offset)
{
Q_D(QQuickItem);
if (offset == d->baselineOffset)
return;
d->baselineOffset = offset;
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();
emit baselineOffsetChanged(offset);
}
/*!
* Schedules a call to updatePaintNode() for this item.
*
* The call to QQuickItem::updatePaintNode() will always happen if the
* item is showing in a QQuickWindow.
*
* Only items which specify QQuickItem::ItemHasContents are allowed
* to call QQuickItem::update().
*/
void QQuickItem::update()
{
Q_D(QQuickItem);
if (!(flags() & ItemHasContents)) {
#ifndef QT_NO_DEBUG
qWarning() << metaObject()->className() << ": Update called for a item without content";
#endif
return;
}
d->dirty(QQuickItemPrivate::Content);
}
/*!
Schedules a polish event for this item.
When the scene graph processes the request, it will call updatePolish()
on this item.
\sa updatePolish(), QQuickTest::qIsPolishScheduled(), ensurePolished()
*/
void QQuickItem::polish()
{
Q_D(QQuickItem);
if (!d->polishScheduled) {
d->polishScheduled = true;
if (d->window) {
QQuickWindowPrivate *p = QQuickWindowPrivate::get(d->window);
bool maybeupdate = p->itemsToPolish.isEmpty();
p->itemsToPolish.append(this);
if (maybeupdate) d->window->maybeUpdate();
}
}
}
/*!
\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();
if (args->length() != 2 && args->length() != 3 && args->length() != 5) {
v4->throwTypeError();
return false;
}
QV4::Scope scope(v4);
QV4::ScopedValue item(scope, (*args)[0]);
*itemObj = nullptr;
if (!item->isNull()) {
QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, item->as<QV4::QObjectWrapper>());
if (qobjectWrapper)
*itemObj = qobject_cast<QQuickItem*>(qobjectWrapper->object());
}
if (!(*itemObj) && !item->isNull()) {
qmlWarning(itemForWarning) << functionNameForWarning << " given argument \"" << item->toQStringNoThrow()
<< "\" which is neither null nor an Item";
v4->throwTypeError();
return false;
}
*isRect = false;
if (args->length() == 2) {
QV4::ScopedValue sv(scope, (*args)[1]);
if (sv->isNull()) {
qmlWarning(itemForWarning) << functionNameForWarning << "given argument \"" << sv->toQStringNoThrow()
<< "\" which is neither a point nor a rect";
v4->throwTypeError();
return false;
}
const QV4::Scoped<QV4::QQmlValueTypeWrapper> variantWrapper(scope, sv->as<QV4::QQmlValueTypeWrapper>());
const QVariant v = variantWrapper ? variantWrapper->toVariant() : QVariant();
if (v.canConvert<QPointF>()) {
const QPointF p = v.toPointF();
*x = p.x();
*y = p.y();
} else if (v.canConvert<QRectF>()) {
const QRectF r = v.toRectF();
*x = r.x();
*y = r.y();
*w = r.width();
*h = r.height();
*isRect = true;
} else {
qmlWarning(itemForWarning) << functionNameForWarning << "given argument \"" << sv->toQStringNoThrow()
<< "\" which is neither a point nor a rect";
v4->throwTypeError();
return false;
}
} else {
QV4::ScopedValue vx(scope, (*args)[1]);
QV4::ScopedValue vy(scope, (*args)[2]);
if (!vx->isNumber() || !vy->isNumber()) {
v4->throwTypeError();
return false;
}
*x = vx->asDouble();
*y = vy->asDouble();
if (args->length() > 3) {
QV4::ScopedValue vw(scope, (*args)[3]);
QV4::ScopedValue vh(scope, (*args)[4]);
if (!vw->isNumber() || !vh->isNumber()) {
v4->throwTypeError();
return false;
}
*w = vw->asDouble();
*h = vh->asDouble();
*isRect = true;
}
}
return true;
}
#endif
/*!
\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
matching the mapped coordinate.
\input item.qdocinc mapping
If \a item is a \c null value, this maps the point or rect from the coordinate system of
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(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
qreal x, y, w, h;
bool isRect;
QQuickItem *itemObj;
if (!unwrapMapFromToFromItemArgs(args, this, QStringLiteral("mapFromItem()"), &itemObj, &x, &y, &w, &h, &isRect))
return;
const QVariant result = isRect ? QVariant(mapRectFromItem(itemObj, QRectF(x, y, w, h)))
: QVariant(mapFromItem(itemObj, QPointF(x, y)));
QV4::ScopedObject rv(scope, v4->fromVariant(result));
args->setReturnValue(rv.asReturnedValue());
}
#endif
/*!
\internal
*/
QTransform QQuickItem::itemTransform(QQuickItem *other, bool *ok) const
{
Q_D(const QQuickItem);
// XXX todo - we need to be able to handle common parents better and detect
// invalid cases
if (ok) *ok = true;
QTransform t = d->itemToWindowTransform();
if (other) t *= QQuickItemPrivate::get(other)->windowToItemTransform();
return t;
}
/*!
\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
matching the mapped coordinate.
\input item.qdocinc mapping
If \a item is a \c null value, this maps the point or rect to the coordinate system of 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::mapToItem(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
qreal x, y, w, h;
bool isRect;
QQuickItem *itemObj;
if (!unwrapMapFromToFromItemArgs(args, this, QStringLiteral("mapToItem()"), &itemObj, &x, &y, &w, &h, &isRect))
return;
const QVariant result = isRect ? QVariant(mapRectToItem(itemObj, QRectF(x, y, w, h)))
: QVariant(mapToItem(itemObj, QPointF(x, y)));
QV4::ScopedObject rv(scope, v4->fromVariant(result));
args->setReturnValue(rv.asReturnedValue());
}
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) {
v4->throwTypeError();
return false;
}
QV4::Scope scope(v4);
if (args->length() == 1) {
QV4::ScopedValue sv(scope, (*args)[0]);
if (sv->isNull()) {
qmlWarning(itemForWarning) << functionNameForWarning << "given argument \"" << sv->toQStringNoThrow()
<< "\" which is not a point";
v4->throwTypeError();
return false;
}
const QV4::Scoped<QV4::QQmlValueTypeWrapper> variantWrapper(scope, sv->as<QV4::QQmlValueTypeWrapper>());
const QVariant v = variantWrapper ? variantWrapper->toVariant() : QVariant();
if (v.canConvert<QPointF>()) {
const QPointF p = v.toPointF();
*x = p.x();
*y = p.y();
} else {
qmlWarning(itemForWarning) << functionNameForWarning << "given argument \"" << sv->toQStringNoThrow()
<< "\" which is not a point";
v4->throwTypeError();
return false;
}
} else {
QV4::ScopedValue vx(scope, (*args)[0]);
QV4::ScopedValue vy(scope, (*args)[1]);
if (!vx->isNumber() || !vy->isNumber()) {
v4->throwTypeError();
return false;
}
*x = vx->asDouble();
*y = vy->asDouble();
}
return true;
}
/*!
\since 5.7
\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.
\input item.qdocinc mapping
*/
/*!
\internal
*/
void QQuickItem::mapFromGlobal(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
qreal x, y;
if (!unwrapMapFromToFromGlobalArgs(args, this, QStringLiteral("mapFromGlobal()"), &x, &y))
return;
QVariant result = mapFromGlobal(QPointF(x, y));
QV4::ScopedObject rv(scope, v4->fromVariant(result));
args->setReturnValue(rv.asReturnedValue());
}
#endif
/*!
\since 5.7
\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(QQmlV4FunctionPtr args) const
{
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
qreal x, y;
if (!unwrapMapFromToFromGlobalArgs(args, this, QStringLiteral("mapFromGlobal()"), &x, &y))
return;
QVariant result = mapToGlobal(QPointF(x, y));
QV4::ScopedObject rv(scope, v4->fromVariant(result));
args->setReturnValue(rv.asReturnedValue());
}
#endif
/*!
\qmlmethod void QtQuick::Item::forceActiveFocus()
Forces active focus on the item.
This method sets focus on the item and ensures that all ancestor
FocusScope objects in the object hierarchy are also given \l focus.
The reason for the focus change will be \l [CPP] Qt::OtherFocusReason. Use
the overloaded method to specify the focus reason to enable better
handling of the focus change.
\sa activeFocus
*/
/*!
Forces active focus on the item.
This method sets focus on the item and ensures that all ancestor
FocusScope objects in the object hierarchy are also given \l focus.
The reason for the focus change will be \l [CPP] Qt::OtherFocusReason. Use
the overloaded method to specify the focus reason to enable better
handling of the focus change.
\sa activeFocus
*/
void QQuickItem::forceActiveFocus()
{
forceActiveFocus(Qt::OtherFocusReason);
}
/*!
\qmlmethod void QtQuick::Item::forceActiveFocus(Qt::FocusReason reason)
\overload
Forces active focus on the item with the given \a reason.
This method sets focus on the item and ensures that all ancestor
FocusScope objects in the object hierarchy are also given \l focus.
\since 5.1
\sa activeFocus, Qt::FocusReason
*/
/*!
\overload
Forces active focus on the item with the given \a reason.
This method sets focus on the item and ensures that all ancestor
FocusScope objects in the object hierarchy are also given \l focus.
\since 5.1
\sa activeFocus, Qt::FocusReason
*/
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();
}
}
/*!
\qmlmethod Item QtQuick::Item::nextItemInFocusChain(bool forward)
\since 5.1
Returns the item in the focus chain which is next to this item.
If \a forward is \c true, or not supplied, it is the next item in
the forwards direction. If \a forward is \c false, it is the next
item in the backwards direction.
*/
/*!
Returns the item in the focus chain which is next to this item.
If \a forward is \c true, or not supplied, it is the next item in
the forwards direction. If \a forward is \c false, it is the next
item in the backwards direction.
*/
QQuickItem *QQuickItem::nextItemInFocusChain(bool forward)
{
return QQuickItemPrivate::nextPrevItemInTabFocusChain(this, forward);
}
/*!
\qmlmethod Item QtQuick::Item::childAt(real x, real y)
Returns the first visible child item found at point (\a x, \a y) within
the coordinate system of this item.
Returns \c null if there is no such item.
*/
/*!
Returns the first visible child item found at point (\a x, \a y) within
the coordinate system of this item.
Returns \nullptr if there is no such item.
*/
QQuickItem *QQuickItem::childAt(qreal x, qreal y) const
{
const QList<QQuickItem *> children = childItems();
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() && child->contains(point))
return child;
}
return nullptr;
}
/*!
\qmlmethod void 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}
*/
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()
{
// 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<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
reference by name.
It is not generally necessary to refer to these properties when adding
child items or resources, as the default \l data property will
automatically assign child objects to the \c children and \c resources
properties as appropriate. See the \l data documentation for details.
*/
/*!
\property QQuickItem::children
\internal
*/
QQmlListProperty<QQuickItem> QQuickItemPrivate::children()
{
// 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;
}
/*!
\qmlproperty list<Item> QtQuick::Item::visibleChildren
This read-only property lists all of the item's children that are currently visible.
Note that a child's visibility may have changed explicitly, or because the visibility
of this (it's parent) item or another grandparent changed.
*/
/*!
\property QQuickItem::visibleChildren
\internal
*/
QQmlListProperty<QQuickItem> QQuickItemPrivate::visibleChildren()
{
return QQmlListProperty<QQuickItem>(q_func(),
nullptr,
QQuickItemPrivate::visibleChildren_count,
QQuickItemPrivate::visibleChildren_at);
}
/*!
\qmlproperty list<State> QtQuick::Item::states
This property holds the list of possible states for this item. To change
the state of this item, set the \l state property to one of these states,
or set the \l state property to an empty string to revert the item to its
default state.
This property is specified as a list of \l State objects. For example,
below is an item with "red_color" and "blue_color" states:
\qml
import QtQuick 2.0
Rectangle {
id: root
width: 100; height: 100
states: [
State {
name: "red_color"
PropertyChanges { root.color: "red" }
},
State {
name: "blue_color"
PropertyChanges { root.color: "blue" }
}
]
}
\endqml
See \l{Qt Quick States} and \l{Animation and Transitions in Qt Quick} for
more details on using states and transitions.
\sa transitions
*/
/*!
\property QQuickItem::states
\internal
*/
QQmlListProperty<QQuickState> QQuickItemPrivate::states()
{
return _states()->statesProperty();
}
/*!
\qmlproperty list<Transition> QtQuick::Item::transitions
This property holds the list of transitions for this item. These define the
transitions to be applied to the item whenever it changes its \l state.
This property is specified as a list of \l Transition objects. For example:
\qml
import QtQuick 2.0
Item {
transitions: [
Transition {
//...
},
Transition {
//...
}
]
}
\endqml
See \l{Qt Quick States} and \l{Animation and Transitions in Qt Quick} for
more details on using states and transitions.
\sa states
*/
/*!
\property QQuickItem::transitions
\internal
*/
QQmlListProperty<QQuickTransition> QQuickItemPrivate::transitions()
{
return _states()->transitionsProperty();
}
QString QQuickItemPrivate::state() const
{
if (!_stateGroup)
return QString();
else
return _stateGroup->state();
}
void QQuickItemPrivate::setState(const QString &state)
{
_states()->setState(state);
}
/*!
\qmlproperty string QtQuick::Item::state
This property holds the name of the current state of the item.
If the item is in its default state, that is, no explicit state has been
set, then this property holds an empty string. Likewise, you can return
an item to its default state by setting this property to an empty string.
\sa {Qt Quick States}
*/
/*!
\property QQuickItem::state
This property holds the name of the current state of the item.
If the item is in its default state, that is, no explicit state has been
set, then this property holds an empty string. Likewise, you can return
an item to its default state by setting this property to an empty string.
\sa {Qt Quick States}
*/
QString QQuickItem::state() const
{
Q_D(const QQuickItem);
return d->state();
}
void QQuickItem::setState(const QString &state)
{
Q_D(QQuickItem);
d->setState(state);
}
/*!
\qmlproperty list<Transform> QtQuick::Item::transform
This property holds the list of transformations to apply.
This property is specified as a list of \l {Transform}-derived objects.
For example:
\snippet qml/two-transforms.qml entire
For more information see \l Transform.
*/
/*!
\property QQuickItem::transform
\internal
*/
/*!
\internal
*/
QQmlListProperty<QQuickTransform> QQuickItem::transform()
{
return QQmlListProperty<QQuickTransform>(this, nullptr, QQuickItemPrivate::transform_append,
QQuickItemPrivate::transform_count,
QQuickItemPrivate::transform_at,
QQuickItemPrivate::transform_clear);
}
/*!
\reimp
Derived classes should call the base class method before adding their own action to
perform at classBegin.
*/
void QQuickItem::classBegin()
{
Q_D(QQuickItem);
d->componentComplete = false;
if (d->_stateGroup)
d->_stateGroup->classBegin();
if (d->_anchors)
d->_anchors->classBegin();
#if QT_CONFIG(quick_shadereffect)
if (d->extra.isAllocated() && d->extra->layer)
d->extra->layer->classBegin();
#endif
}
/*!
\reimp
Derived classes should call the base class method before adding their own actions to
perform at componentComplete.
*/
void QQuickItem::componentComplete()
{
Q_D(QQuickItem);
d->componentComplete = true;
if (d->_stateGroup)
d->_stateGroup->componentComplete();
if (d->_anchors) {
d->_anchors->componentComplete();
QQuickAnchorsPrivate::get(d->_anchors)->updateOnComplete();
}
if (auto *safeArea = findChild<QQuickSafeArea*>(Qt::FindDirectChildrenOnly))
safeArea->updateSafeArea();
if (d->extra.isAllocated()) {
#if QT_CONFIG(quick_shadereffect)
if (d->extra->layer)
d->extra->layer->componentComplete();
#endif
if (d->extra->keyHandler)
d->extra->keyHandler->componentComplete();
if (d->extra->contents)
d->extra->contents->complete();
}
if (d->window && d->dirtyAttributes) {
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()
{
Q_Q(QQuickItem);
if (!_stateGroup) {
_stateGroup = new QQuickStateGroup;
if (!componentComplete)
_stateGroup->classBegin();
qmlobject_connect(_stateGroup, QQuickStateGroup, SIGNAL(stateChanged(QString)),
q, QQuickItem, SIGNAL(stateChanged(QString)));
}
return _stateGroup;
}
bool QQuickItemPrivate::customOverlayRequested = false;
void QQuickItemPrivate::requestCustomOverlay()
{
customOverlayRequested = true;
customOverlay = true;
}
QPointF QQuickItemPrivate::computeTransformOrigin() const
{
switch (origin()) {
default:
case QQuickItem::TopLeft:
return QPointF(0, 0);
case QQuickItem::Top:
return QPointF(width / 2., 0);
case QQuickItem::TopRight:
return QPointF(width, 0);
case QQuickItem::Left:
return QPointF(0, height / 2.);
case QQuickItem::Center:
return QPointF(width / 2., height / 2.);
case QQuickItem::Right:
return QPointF(width, height / 2.);
case QQuickItem::BottomLeft:
return QPointF(0, height);
case QQuickItem::Bottom:
return QPointF(width / 2., height);
case QQuickItem::BottomRight:
return QPointF(width, height);
}
}
/*!
\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);
#if QT_CONFIG(quick_shadereffect)
if (q == transformedItem) {
if (extra.isAllocated() && extra->layer)
extra->layer->updateMatrix();
}
#endif
itemChange(QQuickItem::ItemTransformHasChanged, transformedItem);
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);
}
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);
// Recheck each parent that so far has had all its children within bounds.
// If this item or any ancestor has moved out of the bounds of its parent,
// consider it to be a rogue from now on, and don't check anymore.
QQuickItemPrivate *itemPriv = this;
while (itemPriv->parentItem) {
auto *parentPriv = QQuickItemPrivate::get(itemPriv->parentItem);
if (parentPriv->eventHandlingChildrenWithinBounds) {
Q_ASSERT(parentPriv->eventHandlingChildrenWithinBoundsSet);
if (itemPriv->parentFullyContains())
break; // child moved, but did not move outside its parent: no change to any parents then
else
parentPriv->eventHandlingChildrenWithinBounds = false; // keep checking further up
}
itemPriv = parentPriv;
}
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,
qreal startScale,
qreal activeScale,
qreal startRotation,
qreal activeRotation)
{
Q_Q(QQuickItem);
QVector3D xformOrigin(q->transformOriginPoint());
QMatrix4x4 startMatrix;
startMatrix.translate(float(startPos.x()), float(startPos.y()));
startMatrix.translate(xformOrigin);
startMatrix.scale(float(startScale));
startMatrix.rotate(float(startRotation), 0, 0, -1);
startMatrix.translate(-xformOrigin);
const QVector3D centroidParentVector(centroidParentPos);
QMatrix4x4 mat;
mat.translate(centroidParentVector);
mat.rotate(float(activeRotation), 0, 0, 1);
mat.scale(float(activeScale));
mat.translate(-centroidParentVector);
mat.translate(QVector3D(activeTranslation));
mat = mat * startMatrix;
QPointF xformOriginPoint = q->transformOriginPoint();
QPointF pos = mat.map(xformOriginPoint);
pos -= xformOriginPoint;
return pos;
}
/*! \internal
Returns the delivery agent for the narrowest subscene containing this item,
but falls back to QQuickWindowPrivate::deliveryAgent if there are no subscenes.
If this item is not sure whether it's in a subscene (as by default), we need to
explore the parents to find out.
If this item is in a subscene, we will find that DA during the exploration,
and return it.
If we find the root item without finding a DA, then we know that this item
does NOT belong to a subscene, so we remember that by setting
maybeHasSubsceneDeliveryAgent to false, so that exploration of the parents
can be avoided next time.
In the usual case in normal 2D scenes without subscenes,
maybeHasSubsceneDeliveryAgent gets set to false here.
\note When a Qt Quick scene is shown in the usual way in its own window,
subscenes are ignored, and QQuickWindowPrivate::deliveryAgent is used.
Subscene delivery agents are used only in QtQuick 3D so far.
*/
QQuickDeliveryAgent *QQuickItemPrivate::deliveryAgent()
{
Q_Q(QQuickItem);
if (maybeHasSubsceneDeliveryAgent) {
QQuickItemPrivate *p = this;
do {
if (qmlobject_cast<QQuickRootItem *>(p->q_ptr)) {
// found the root item without finding a different DA:
// it means we don't need to repeat this search next time.
// TODO maybe optimize further: make this function recursive, and
// set it to false on each item that we visit in the tail
maybeHasSubsceneDeliveryAgent = false;
break;
}
if (p->extra.isAllocated()) {
if (auto da = p->extra->subsceneDeliveryAgent)
return da;
}
p = p->parentItem ? QQuickItemPrivate::get(p->parentItem) : nullptr;
} while (p);
// arriving here is somewhat unexpected: a detached root can easily be created (just set an item's parent to null),
// but why would we deliver events to that subtree? only if root got detached while an item in that subtree still has a grab?
qCDebug(lcPtr) << "detached root of" << q << "is not a QQuickRootItem and also does not have its own DeliveryAgent";
}
if (window)
return QQuickWindowPrivate::get(window)->deliveryAgent;
return nullptr;
}
QQuickDeliveryAgentPrivate *QQuickItemPrivate::deliveryAgentPrivate()
{
auto da = deliveryAgent();
return da ? static_cast<QQuickDeliveryAgentPrivate *>(QQuickDeliveryAgentPrivate::get(da)) : nullptr;
}
/*! \internal
Ensures that this item, presumably the root of a subscene (e.g. because it
is mapped onto a 3D object in Qt Quick 3D), has a delivery agent to be used
when delivering events to the subscene: i.e. when the viewport delivers an
event to the subscene, or when the outer delivery agent delivers an update
to an item that grabbed during a previous subscene delivery. Creates a new
agent if it was not already created, and returns a pointer to the instance.
*/
QQuickDeliveryAgent *QQuickItemPrivate::ensureSubsceneDeliveryAgent()
{
Q_Q(QQuickItem);
// We are (about to be) sure that it has one now; but just to save space,
// we avoid storing a DA pointer in each item; so deliveryAgent() always needs to
// go up the hierarchy to find it. maybeHasSubsceneDeliveryAgent tells it to do that.
maybeHasSubsceneDeliveryAgent = true;
if (extra.isAllocated() && extra->subsceneDeliveryAgent)
return extra->subsceneDeliveryAgent;
extra.value().subsceneDeliveryAgent = new QQuickDeliveryAgent(q);
qCDebug(lcPtr) << "created new" << extra->subsceneDeliveryAgent;
// every subscene root needs to be a focus scope so that when QQuickItem::forceActiveFocus()
// goes up the parent hierarchy, it finds the subscene root and calls setFocus() on it
q->setFlag(QQuickItem::ItemIsFocusScope);
return extra->subsceneDeliveryAgent;
}
bool QQuickItemPrivate::filterKeyEvent(QKeyEvent *e, bool post)
{
if (!extra.isAllocated() || !extra->keyHandler)
return false;
if (post)
e->accept();
if (e->type() == QEvent::KeyPress)
extra->keyHandler->keyPressed(e, post);
else
extra->keyHandler->keyReleased(e, 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);
Q_ASSERT(e->isAccepted());
if (filterKeyEvent(e, false))
return;
else
e->accept();
if (e->type() == QEvent::KeyPress)
q->keyPressEvent(e);
else
q->keyReleaseEvent(e);
if (e->isAccepted())
return;
if (filterKeyEvent(e, true) || !q->window())
return;
//only care about KeyPress now
if (e->type() == QEvent::KeyPress &&
(q == q->window()->contentItem() || q->activeFocusOnTab())) {
bool res = false;
if (!(e->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
if (e->key() == Qt::Key_Backtab
|| (e->key() == Qt::Key_Tab && (e->modifiers() & Qt::ShiftModifier)))
res = QQuickItemPrivate::focusNextPrev(q, false);
else if (e->key() == Qt::Key_Tab)
res = QQuickItemPrivate::focusNextPrev(q, true);
if (res)
e->setAccepted(true);
}
}
}
#if QT_CONFIG(im)
void QQuickItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e)
{
Q_Q(QQuickItem);
Q_ASSERT(e->isAccepted());
if (extra.isAllocated() && extra->keyHandler) {
extra->keyHandler->inputMethodEvent(e, false);
if (e->isAccepted())
return;
else
e->accept();
}
q->inputMethodEvent(e);
if (e->isAccepted())
return;
if (extra.isAllocated() && extra->keyHandler) {
e->accept();
extra->keyHandler->inputMethodEvent(e, true);
}
}
#endif // im
void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
{
if (extra.isAllocated() && extra->keyHandler)
extra->keyHandler->shortcutOverrideEvent(event);
else
event->ignore();
}
bool QQuickItemPrivate::anyPointerHandlerWants(const QPointerEvent *event, const QEventPoint &point) const
{
if (!hasPointerHandlers())
return false;
for (QQuickPointerHandler *handler : extra->pointerHandlers) {
if (handler->wantsEventPoint(event, point))
return true;
}
return false;
}
/*!
\internal
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().
However if the event is a QTabletEvent, we do NOT skip delivery here:
this is the means by which HoverHandler can change the cursor when the
tablet stylus hovers over its parent item.
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 avoidGrabbers)
{
bool delivered = false;
if (extra.isAllocated()) {
for (QQuickPointerHandler *handler : extra->pointerHandlers) {
bool avoidThisHandler = false;
if (QQuickDeliveryAgentPrivate::isMouseOrWheelEvent(event) &&
qmlobject_cast<const QQuickHoverHandler *>(handler)) {
avoidThisHandler = true;
} else if (avoidGrabbers) {
for (auto &p : event->points()) {
if (event->exclusiveGrabber(p) == handler || event->passiveGrabbers(p).contains(handler)) {
avoidThisHandler = true;
break;
}
}
}
if (!avoidThisHandler &&
!QQuickPointerHandlerPrivate::deviceDeliveryTargets(event->device()).contains(handler)) {
handler->handlePointerEvent(event);
delivered = true;
}
}
}
return delivered;
}
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
bool QQuickItemPrivate::handleContextMenuEvent(QContextMenuEvent *event)
#else
bool QQuickItem::contextMenuEvent(QContextMenuEvent *event)
#endif
{
if (extra.isAllocated() && extra->contextMenu)
return extra->contextMenu->event(event);
event->ignore();
return false;
}
/*!
Called when \a change occurs for this item.
\a value contains extra information relating to the change, when
applicable.
If you re-implement this method in a subclass, be sure to call
\code
QQuickItem::itemChange(change, value);
\endcode
typically at the end of your implementation, to ensure the
\l windowChanged() signal will be emitted.
*/
void QQuickItem::itemChange(ItemChange change, const ItemChangeData &value)
{
if (change == ItemSceneChange)
emit windowChanged(value.window);
}
#if QT_CONFIG(im)
/*!
Notify input method on updated query values if needed. \a queries indicates
the changed attributes.
*/
void QQuickItem::updateInputMethod(Qt::InputMethodQueries queries)
{
if (hasActiveFocus())
QGuiApplication::inputMethod()->update(queries);
}
#endif // im
/*!
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);
}
/*!
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);
QRectF ret(0, 0, d->width.valueBypassingBindings(), d->height.valueBypassingBindings());
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);
}
/*!
\qmlproperty enumeration QtQuick::Item::transformOrigin
This property holds the origin point around which scale and rotation transform.
Nine transform origins are available, as shown in the image below.
The default transform origin is \c Item.Center.
\image declarative-transformorigin.png {Rectangle showing nine transform
origin points: TopLeft, Top, TopRight, Left, Center, Right,
BottomLeft, Bottom, BottomRight}
This example rotates an image around its bottom-right corner.
\qml
Image {
source: "myimage.png"
transformOrigin: Item.BottomRight
rotation: 45
}
\endqml
To set an arbitrary transform origin point use the \l Scale or \l Rotation
transform types with \l transform.
*/
/*!
\property QQuickItem::transformOrigin
This property holds the origin point around which scale and rotation transform.
Nine transform origins are available, as shown in the image below.
The default transform origin is \c Item.Center.
\image declarative-transformorigin.png {Rectangle showing nine transform
origin points: TopLeft, Top, TopRight, Left, Center, Right,
BottomLeft, Bottom, BottomRight}
*/
QQuickItem::TransformOrigin QQuickItem::transformOrigin() const
{
Q_D(const QQuickItem);
return d->origin();
}
void QQuickItem::setTransformOrigin(TransformOrigin origin)
{
Q_D(QQuickItem);
if (origin == d->origin())
return;
d->extra.value().origin = origin;
d->dirty(QQuickItemPrivate::TransformOrigin);
emit transformOriginChanged(d->origin());
}
/*!
\property QQuickItem::transformOriginPoint
\internal
*/
/*!
\internal
*/
QPointF QQuickItem::transformOriginPoint() const
{
Q_D(const QQuickItem);
if (d->extra.isAllocated() && !d->extra->userTransformOriginPoint.isNull())
return d->extra->userTransformOriginPoint;
return d->computeTransformOrigin();
}
/*!
\internal
*/
void QQuickItem::setTransformOriginPoint(const QPointF &point)
{
Q_D(QQuickItem);
if (d->extra.value().userTransformOriginPoint == point)
return;
d->extra->userTransformOriginPoint = point;
d->dirty(QQuickItemPrivate::TransformOrigin);
}
/*!
\qmlproperty real QtQuick::Item::z
Sets the stacking order of sibling items. By default the stacking order is 0.
Items with a higher stacking value are drawn on top of siblings with a
lower stacking order. Items with the same stacking value are drawn
bottom up in the order they appear. Items with a negative stacking
value are drawn under their parent's content.
The following example shows the various effects of stacking order.
\table
\row
\li \image declarative-item_stacking1.png {Blue rectangle above red,
later sibling stacked on top}
\li Same \c z - later children above earlier children:
\qml
Item {
Rectangle {
color: "red"
width: 100; height: 100
}
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
\endqml
\row
\li \image declarative-item_stacking2.png {Red rectangle above blue,
higher z value stacked on top}
\li Higher \c z on top:
\qml
Item {
Rectangle {
z: 1
color: "red"
width: 100; height: 100
}
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
\endqml
\row
\li \image declarative-item_stacking3.png {Blue rectangle above red,
child stacked above parent}
\li Same \c z - children above parents:
\qml
Item {
Rectangle {
color: "red"
width: 100; height: 100
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
}
\endqml
\row
\li \image declarative-item_stacking4.png {Red rectangle above blue,
negative z value stacked below parent}
\li Lower \c z below:
\qml
Item {
Rectangle {
color: "red"
width: 100; height: 100
Rectangle {
z: -1
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
}
\endqml
\endtable
*/
/*!
\property QQuickItem::z
Sets the stacking order of sibling items. By default the stacking order is 0.
Items with a higher stacking value are drawn on top of siblings with a
lower stacking order. Items with the same stacking value are drawn
bottom up in the order they appear. Items with a negative stacking
value are drawn under their parent's content.
The following example shows the various effects of stacking order.
\table
\row
\li \image declarative-item_stacking1.png {Blue rectangle above red,
later sibling stacked on top}
\li Same \c z - later children above earlier children:
\qml
Item {
Rectangle {
color: "red"
width: 100; height: 100
}
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
\endqml
\row
\li \image declarative-item_stacking2.png {Red rectangle above blue,
higher z value stacked on top}
\li Higher \c z on top:
\qml
Item {
Rectangle {
z: 1
color: "red"
width: 100; height: 100
}
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
\endqml
\row
\li \image declarative-item_stacking3.png {Blue rectangle above red,
child stacked above parent}
\li Same \c z - children above parents:
\qml
Item {
Rectangle {
color: "red"
width: 100; height: 100
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
}
\endqml
\row
\li \image declarative-item_stacking4.png {Red rectangle above blue,
negative z value stacked below parent}
\li Lower \c z below:
\qml
Item {
Rectangle {
color: "red"
width: 100; height: 100
Rectangle {
z: -1
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
}
\endqml
\endtable
*/
qreal QQuickItem::z() const
{
Q_D(const QQuickItem);
return d->z();
}
void QQuickItem::setZ(qreal v)
{
Q_D(QQuickItem);
if (d->z() == v)
return;
d->extra.value().z = v;
d->dirty(QQuickItemPrivate::ZValue);
if (d->parentItem) {
QQuickItemPrivate::get(d->parentItem)->markSortedChildrenDirty(this);
QQuickItemPrivate::get(d->parentItem)->dirty(QQuickItemPrivate::ChildrenStackingChanged);
}
emit zChanged();
#if QT_CONFIG(quick_shadereffect)
if (d->extra.isAllocated() && d->extra->layer)
d->extra->layer->updateZ();
#endif
}
/*!
\qmlproperty real QtQuick::Item::rotation
This property holds the rotation of the item in degrees clockwise around
its transformOrigin.
The default value is 0 degrees (that is, no rotation).
\table
\row
\li \image declarative-rotation.png {Red square rotated 30 degrees
inside a blue square}
\li
\qml
Rectangle {
color: "blue"
width: 100; height: 100
Rectangle {
color: "red"
x: 25; y: 25; width: 50; height: 50
rotation: 30
}
}
\endqml
\endtable
\sa Transform, Rotation
*/
/*!
\property QQuickItem::rotation
This property holds the rotation of the item in degrees clockwise around
its transformOrigin.
The default value is 0 degrees (that is, no rotation).
\table
\row
\li \image declarative-rotation.png {Red square rotated 30 degrees
inside a blue square}
\li
\qml
Rectangle {
color: "blue"
width: 100; height: 100
Rectangle {
color: "red"
x: 25; y: 25; width: 50; height: 50
rotation: 30
}
}
\endqml
\endtable
\sa Transform, Rotation
*/
qreal QQuickItem::rotation() const
{
Q_D(const QQuickItem);
return d->rotation();
}
void QQuickItem::setRotation(qreal r)
{
Q_D(QQuickItem);
if (d->rotation() == r)
return;
d->extra.value().rotation = r;
d->dirty(QQuickItemPrivate::BasicTransform);
d->itemChange(ItemRotationHasChanged, r);
emit rotationChanged();
}
/*!
\qmlproperty real QtQuick::Item::scale
This property holds the scale factor for this item.
A scale of less than 1.0 causes the item to be rendered at a smaller
size, and a scale greater than 1.0 renders the item at a larger size.
A negative scale causes the item to be mirrored when rendered.
The default value is 1.0.
Scaling is applied from the transformOrigin.
\table
\row
\li \image declarative-scale.png {Blue and red squares with specific
scaling and positioning}
\li
\qml
import QtQuick 2.0
Rectangle {
color: "blue"
width: 100; height: 100
Rectangle {
color: "green"
width: 25; height: 25
}
Rectangle {
color: "red"
x: 25; y: 25; width: 50; height: 50
scale: 1.4
transformOrigin: Item.TopLeft
}
}
\endqml
\endtable
\sa Transform, Scale
*/
/*!
\property QQuickItem::scale
This property holds the scale factor for this item.
A scale of less than 1.0 causes the item to be rendered at a smaller
size, and a scale greater than 1.0 renders the item at a larger size.
A negative scale causes the item to be mirrored when rendered.
The default value is 1.0.
Scaling is applied from the transformOrigin.
\table
\row
\li \image declarative-scale.png {Blue and red squares with specific
scaling and positioning}
\li
\qml
import QtQuick 2.0
Rectangle {
color: "blue"
width: 100; height: 100
Rectangle {
color: "green"
width: 25; height: 25
}
Rectangle {
color: "red"
x: 25; y: 25; width: 50; height: 50
scale: 1.4
}
}
\endqml
\endtable
\sa Transform, Scale
*/
qreal QQuickItem::scale() const
{
Q_D(const QQuickItem);
return d->scale();
}
void QQuickItem::setScale(qreal s)
{
Q_D(QQuickItem);
if (d->scale() == s)
return;
d->extra.value().scale = s;
d->dirty(QQuickItemPrivate::BasicTransform);
d->itemChange(ItemScaleHasChanged, s);
emit scaleChanged();
}
/*!
\qmlproperty real QtQuick::Item::opacity
This property holds the opacity of the item. Opacity is specified as a
number between 0.0 (fully transparent) and 1.0 (fully opaque). The default
value is 1.0.
When this property is set, the specified opacity is also applied
individually to child items. This may have an unintended effect in some
circumstances. For example in the second set of rectangles below, the red
rectangle has specified an opacity of 0.5, which affects the opacity of
its blue child rectangle even though the child has not specified an opacity.
\table
\row
\li \image declarative-item_opacity1.png {Red and blue rectangles
at full opacity}
\li
\qml
Item {
Rectangle {
color: "red"
width: 100; height: 100
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
}
\endqml
\row
\li \image declarative-item_opacity2.png {Red and blue rectangles
both semi-transparent from parent's 0.5 opacity}
\li
\qml
Item {
Rectangle {
opacity: 0.5
color: "red"
width: 100; height: 100
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
}
\endqml
\endtable
Changing an item's opacity does not affect whether the item receives user
input events. (In contrast, setting \l visible property to \c false stops
mouse events, and setting the \l enabled property to \c false stops mouse
and keyboard events, and also removes active focus from the item.)
\sa visible
*/
/*!
\property QQuickItem::opacity
This property holds the opacity of the item. Opacity is specified as a
number between 0.0 (fully transparent) and 1.0 (fully opaque). The default
value is 1.0.
When this property is set, the specified opacity is also applied
individually to child items. This may have an unintended effect in some
circumstances. For example in the second set of rectangles below, the red
rectangle has specified an opacity of 0.5, which affects the opacity of
its blue child rectangle even though the child has not specified an opacity.
Values outside the range of 0 to 1 will be clamped.
\table
\row
\li \image declarative-item_opacity1.png {Red and blue rectangles
at full opacity}
\li
\qml
Item {
Rectangle {
color: "red"
width: 100; height: 100
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
}
\endqml
\row
\li \image declarative-item_opacity2.png {Red and blue rectangles
both semi-transparent from parent's 0.5 opacity}
\li
\qml
Item {
Rectangle {
opacity: 0.5
color: "red"
width: 100; height: 100
Rectangle {
color: "blue"
x: 50; y: 50; width: 100; height: 100
}
}
}
\endqml
\endtable
Changing an item's opacity does not affect whether the item receives user
input events. (In contrast, setting \l visible property to \c false stops
mouse events, and setting the \l enabled property to \c false stops mouse
and keyboard events, and also removes active focus from the item.)
\sa visible
*/
qreal QQuickItem::opacity() const
{
Q_D(const QQuickItem);
return d->opacity();
}
void QQuickItem::setOpacity(qreal newOpacity)
{
Q_D(QQuickItem);
qreal o = std::clamp(newOpacity, qreal(0.0), qreal(1.0));
if (d->opacity() == o)
return;
d->extra.value().opacity = o;
d->dirty(QQuickItemPrivate::OpacityValue);
d->itemChange(ItemOpacityHasChanged, o);
emit opacityChanged();
}
/*!
\qmlproperty bool QtQuick::Item::visible
This property holds whether the item is visible. By default this is true.
Setting this property directly affects the \c visible value of child
items. When set to \c false, the \c visible values of all child items also
become \c false. When set to \c true, the \c visible values of child items
are returned to \c true, unless they have explicitly been set to \c false.
(Because of this flow-on behavior, using the \c visible property may not
have the intended effect if a property binding should only respond to
explicit property changes. In such cases it may be better to use the
\l opacity property instead.)
If this property is set to \c false, the item will no longer receive mouse
events, but will continue to receive key events and will retain the keyboard
\l focus if it has been set. (In contrast, setting the \l enabled property
to \c false stops both mouse and keyboard events, and also removes focus
from the item.)
\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.
\sa opacity, enabled
*/
/*!
\property QQuickItem::visible
This property holds whether the item is visible. By default this is true.
Setting this property directly affects the \c visible value of child
items. When set to \c false, the \c visible values of all child items also
become \c false. When set to \c true, the \c visible values of child items
are returned to \c true, unless they have explicitly been set to \c false.
(Because of this flow-on behavior, using the \c visible property may not
have the intended effect if a property binding should only respond to
explicit property changes. In such cases it may be better to use the
\l opacity property instead.)
If this property is set to \c false, the item will no longer receive mouse
events, but will continue to receive key events and will retain the keyboard
\l focus if it has been set. (In contrast, setting the \l enabled property
to \c false stops both mouse and keyboard events, and also removes focus
from the item.)
\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. 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
*/
bool QQuickItem::isVisible() const
{
Q_D(const QQuickItem);
return d->effectiveVisible;
}
void QQuickItemPrivate::setVisible(bool visible)
{
if (visible == explicitVisible)
return;
explicitVisible = visible;
if (!visible)
dirty(QQuickItemPrivate::Visible);
const bool childVisibilityChanged = setEffectiveVisibleRecur(calcEffectiveVisible());
if (childVisibilityChanged && parentItem)
emit parentItem->visibleChildrenChanged(); // signal the parent, not this!
}
void QQuickItem::setVisible(bool v)
{
Q_D(QQuickItem);
d->setVisible(v);
}
/*!
\qmlproperty bool QtQuick::Item::enabled
This property holds whether the item receives mouse and keyboard events.
By default, this is \c true.
When set to \c false, the item does not receive keyboard or pointing device
events, such as press, release, or click, but can still receive hover
events.
\note In Qt 5, setting \c enabled to \c false also blocked hover events.
This was changed in Qt 6 to allow \l {QtQuick.Controls::ToolTip}{tooltips}
and similar features to work on disabled items.
Setting this property directly affects the \c enabled value of child
items. When set to \c false, the \c enabled values of all child items also
become \c false. When set to \c true, the \c enabled values of child items
are returned to \c true, unless they have explicitly been set to \c false.
Setting this property to \c false automatically causes \l activeFocus to be
set to \c false, and this item will no longer receive keyboard events.
\sa visible
*/
/*!
\property QQuickItem::enabled
This property holds whether the item receives mouse and keyboard events.
By default this is true.
Setting this property directly affects the \c enabled value of child
items. When set to \c false, the \c enabled values of all child items also
become \c false. When set to \c true, the \c enabled values of child items
are returned to \c true, unless they have explicitly been set to \c false.
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
{
Q_D(const QQuickItem);
return d->effectiveEnable;
}
void QQuickItem::setEnabled(bool e)
{
Q_D(QQuickItem);
if (e == d->explicitEnable)
return;
d->explicitEnable = e;
QQuickItem *scope = parentItem();
while (scope && !scope->isFocusScope())
scope = scope->parentItem();
d->setEffectiveEnableRecur(scope, d->calcEffectiveEnable());
}
bool QQuickItemPrivate::calcEffectiveVisible() const
{
// 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)
{
Q_Q(QQuickItem);
if (newEffectiveVisible && !explicitVisible) {
// This item locally overrides visibility
return false; // effective visibility didn't change
}
if (newEffectiveVisible == effectiveVisible) {
// No change necessary
return false; // effective visibility didn't change
}
effectiveVisible = newEffectiveVisible;
dirty(Visible);
if (parentItem)
QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged);
if (window) {
if (auto agent = deliveryAgentPrivate())
agent->removeGrabber(q, true, true, true);
}
bool childVisibilityChanged = false;
for (int ii = 0; ii < childItems.size(); ++ii)
childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible);
itemChange(QQuickItem::ItemVisibleHasChanged, bool(effectiveVisible));
#if QT_CONFIG(accessibility)
if (isAccessible) {
QAccessibleEvent ev(q, effectiveVisible ? QAccessible::ObjectShow : QAccessible::ObjectHide);
QAccessible::updateAccessibility(&ev);
}
#endif
if (!inDestructor) {
emit q->visibleChanged();
if (childVisibilityChanged)
emit q->visibleChildrenChanged();
}
return true; // effective visibility DID change
}
bool QQuickItemPrivate::calcEffectiveEnable() const
{
// XXX todo - Should the effective enable of an element with no parent just be the current
// effective enable? 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 explicitEnable && (!parentItem || QQuickItemPrivate::get(parentItem)->effectiveEnable);
}
void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffectiveEnable)
{
Q_Q(QQuickItem);
if (newEffectiveEnable && !explicitEnable) {
// This item locally overrides enable
return;
}
if (newEffectiveEnable == effectiveEnable) {
// No change necessary
return;
}
effectiveEnable = newEffectiveEnable;
QQuickDeliveryAgentPrivate *da = deliveryAgentPrivate();
if (da) {
da->removeGrabber(q, true, true, true);
if (scope && !effectiveEnable && activeFocus) {
da->clearFocusInScope(scope, q, Qt::OtherFocusReason,
QQuickDeliveryAgentPrivate::DontChangeFocusProperty |
QQuickDeliveryAgentPrivate::DontChangeSubFocusItem);
}
}
for (int ii = 0; ii < childItems.size(); ++ii) {
QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(
(flags & QQuickItem::ItemIsFocusScope) && scope ? q : scope, newEffectiveEnable);
}
if (scope && effectiveEnable && focus && da) {
da->setFocusInScope(scope, q, Qt::OtherFocusReason,
QQuickDeliveryAgentPrivate::DontChangeFocusProperty |
QQuickDeliveryAgentPrivate::DontChangeSubFocusItem);
}
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();
}
/*! \internal
Check all the item's pointer handlers to find the biggest value
of the QQuickPointerHandler::margin property. (Usually \c 0)
*/
qreal QQuickItemPrivate::biggestPointerHandlerMargin() const
{
if (hasPointerHandlers()) {
if (extra->biggestPointerHandlerMarginCache < 0) {
const auto maxMarginIt = std::max_element(extra->pointerHandlers.constBegin(),
extra->pointerHandlers.constEnd(),
[](const QQuickPointerHandler *a, const QQuickPointerHandler *b) {
return a->margin() < b->margin(); });
Q_ASSERT(maxMarginIt != extra->pointerHandlers.constEnd());
extra->biggestPointerHandlerMarginCache = (*maxMarginIt)->margin();
}
return extra->biggestPointerHandlerMarginCache;
}
return 0;
}
/*! \internal
The rectangular bounds within which events should be delivered to the item,
as a first approximation: like QQuickItem::boundingRect() but with \a margin added,
if given, or if any of the item's handlers have the QQuickPointerHandler::margin property set.
This function is used for a quick precheck, but QQuickItem::contains() is more
authoritative (and complex).
*/
QRectF QQuickItemPrivate::eventHandlingBounds(qreal margin) const
{
const qreal biggestMargin = margin > 0 ? margin : biggestPointerHandlerMargin();
return QRectF(-biggestMargin, -biggestMargin, width + biggestMargin * 2, height + biggestMargin * 2);
}
/*! \internal
Returns whether this item's bounding box fully fits within the
parent item's bounding box.
*/
bool QQuickItemPrivate::parentFullyContains() const
{
Q_Q(const QQuickItem);
if (!parentItem)
return true;
QTransform t;
itemToParentTransform(&t);
const auto bounds = eventHandlingBounds();
const auto boundsInParent = t.mapRect(bounds);
const bool ret = parentItem->clipRect().contains(boundsInParent);
qCDebug(lcEffClip) << "in parent bounds?" << ret << q << boundsInParent << parentItem << parentItem->clipRect();
return ret;
}
/*! \internal
Returns whether it's ok to skip pointer event delivery to this item and its children
when we can see that none of the QEventPoints fall inside.
*/
bool QQuickItemPrivate::effectivelyClipsEventHandlingChildren() const
{
Q_Q(const QQuickItem);
// if clipping is turned on, then by definition nothing appears outside
if (flags & QQuickItem::ItemClipsChildrenToShape) {
qCDebug(lcEffClip) << q << "result: true because clip is true";
return true;
}
if (!eventHandlingChildrenWithinBoundsSet) {
// start optimistic, then check for outlying children
eventHandlingChildrenWithinBounds = true;
for (const auto *child : childItems) {
const auto *childPriv = QQuickItemPrivate::get(child);
// If the child doesn't handle pointer events and has no children,
// it doesn't matter whether it goes outside its parent
// (shadows and other control-external decorations should be in this category, for example).
if (childPriv->childItems.isEmpty() &&
!(childPriv->hoverEnabled || childPriv->subtreeHoverEnabled || childPriv->touchEnabled ||
childPriv->hasCursor || childPriv->hasCursorHandler || child->acceptedMouseButtons() ||
childPriv->hasPointerHandlers())) {
qCDebug(lcEffClip) << child << "doesn't handle pointer events";
continue;
}
if (!childPriv->parentFullyContains()) {
eventHandlingChildrenWithinBounds = false;
qCDebug(lcEffClip) << "child goes outside: giving up" << child;
break; // out of for loop
}
if (!childPriv->eventHandlingChildrenWithinBoundsSet) {
eventHandlingChildrenWithinBounds = childPriv->effectivelyClipsEventHandlingChildren();
if (!eventHandlingChildrenWithinBounds)
qCDebug(lcEffClip) << "child has children that go outside: giving up" << child;
}
}
#ifdef QT_BUILD_INTERNAL
if (!eventHandlingChildrenWithinBoundsSet && eventHandlingChildrenWithinBounds)
++eventHandlingChildrenWithinBounds_counter;
#endif
// now we know... but we'll check again if transformChanged() happens
eventHandlingChildrenWithinBoundsSet = true;
qCDebug(lcEffClip) << q << q->clipRect() << "result:" << static_cast<bool>(eventHandlingChildrenWithinBounds);
}
return eventHandlingChildrenWithinBounds;
}
bool QQuickItemPrivate::isTransparentForPositioner() const
{
return extra.isAllocated() && extra.value().transparentForPositioner;
}
void QQuickItemPrivate::setTransparentForPositioner(bool transparent)
{
extra.value().transparentForPositioner = transparent;
}
QString QQuickItemPrivate::dirtyToString() const
{
#define DIRTY_TO_STRING(value) if (dirtyAttributes & value) { \
if (!rv.isEmpty()) \
rv.append(QLatin1Char('|')); \
rv.append(QLatin1String(#value)); \
}
// QString rv = QLatin1String("0x") + QString::number(dirtyAttributes, 16);
QString rv;
DIRTY_TO_STRING(TransformOrigin);
DIRTY_TO_STRING(Transform);
DIRTY_TO_STRING(BasicTransform);
DIRTY_TO_STRING(Position);
DIRTY_TO_STRING(Size);
DIRTY_TO_STRING(ZValue);
DIRTY_TO_STRING(Content);
DIRTY_TO_STRING(Smooth);
DIRTY_TO_STRING(OpacityValue);
DIRTY_TO_STRING(ChildrenChanged);
DIRTY_TO_STRING(ChildrenStackingChanged);
DIRTY_TO_STRING(ParentChanged);
DIRTY_TO_STRING(Clip);
DIRTY_TO_STRING(Window);
DIRTY_TO_STRING(EffectReference);
DIRTY_TO_STRING(Visible);
DIRTY_TO_STRING(HideReference);
DIRTY_TO_STRING(Antialiasing);
return rv;
}
void QQuickItemPrivate::dirty(DirtyType type)
{
Q_Q(QQuickItem);
if (!(dirtyAttributes & type) || (window && !prevDirtyItem)) {
dirtyAttributes |= type;
if (window && componentComplete) {
addToDirtyList();
QQuickWindowPrivate::get(window)->dirtyItem(q, true);
}
}
if (type & (TransformOrigin | Transform | BasicTransform | Position | Size | Clip))
transformChanged(q);
}
void QQuickItemPrivate::addToDirtyList()
{
Q_Q(QQuickItem);
Q_ASSERT(window);
if (!prevDirtyItem) {
Q_ASSERT(!nextDirtyItem);
QQuickWindowPrivate *p = QQuickWindowPrivate::get(window);
nextDirtyItem = p->dirtyItemList;
if (nextDirtyItem) QQuickItemPrivate::get(nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
prevDirtyItem = &p->dirtyItemList;
p->dirtyItemList = q;
p->dirtyItem(q, true);
}
Q_ASSERT(prevDirtyItem);
}
void QQuickItemPrivate::removeFromDirtyList()
{
if (prevDirtyItem) {
if (nextDirtyItem) QQuickItemPrivate::get(nextDirtyItem)->prevDirtyItem = prevDirtyItem;
*prevDirtyItem = nextDirtyItem;
prevDirtyItem = nullptr;
nextDirtyItem = nullptr;
}
Q_ASSERT(!prevDirtyItem);
Q_ASSERT(!nextDirtyItem);
}
void QQuickItemPrivate::refFromEffectItem(bool hide)
{
++extra.value().effectRefCount;
if (extra->effectRefCount == 1) {
dirty(EffectReference);
if (parentItem)
QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged);
}
if (hide) {
if (++extra->hideRefCount == 1)
dirty(HideReference);
}
recursiveRefFromEffectItem(1);
}
void QQuickItemPrivate::recursiveRefFromEffectItem(int refs)
{
Q_Q(QQuickItem);
if (!refs)
return;
extra.value().recursiveEffectRefCount += refs;
for (int ii = 0; ii < childItems.size(); ++ii) {
QQuickItem *child = childItems.at(ii);
QQuickItemPrivate::get(child)->recursiveRefFromEffectItem(refs);
}
// Polish may rely on the effect ref count so trigger one, if item is not visible
// (if visible, it will be triggered automatically).
if (!effectiveVisible && refs > 0 && extra.value().recursiveEffectRefCount == 1) // it wasn't referenced, now it's referenced
q->polish();
}
void QQuickItemPrivate::derefFromEffectItem(bool unhide)
{
Q_ASSERT(extra->effectRefCount);
--extra->effectRefCount;
if (extra->effectRefCount == 0) {
dirty(EffectReference);
if (parentItem)
QQuickItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged);
}
if (unhide) {
if (--extra->hideRefCount == 0)
dirty(HideReference);
}
recursiveRefFromEffectItem(-1);
}
void QQuickItemPrivate::setCulled(bool cull)
{
if (cull == culled)
return;
culled = cull;
if ((cull && ++extra.value().hideRefCount == 1) || (!cull && --extra.value().hideRefCount == 0))
dirty(HideReference);
}
void QQuickItemPrivate::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &data)
{
Q_Q(QQuickItem);
switch (change) {
case QQuickItem::ItemChildAddedChange: {
q->itemChange(change, data);
// 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);
notifyChangeListeners(QQuickItemPrivate::Children, &QQuickItemChangeListener::itemChildRemoved, q, data.item);
break;
}
case QQuickItem::ItemSceneChange:
q->itemChange(change, data);
break;
case QQuickItem::ItemVisibleHasChanged: {
q->itemChange(change, data);
notifyChangeListeners(QQuickItemPrivate::Visibility, &QQuickItemChangeListener::itemVisibilityChanged, q);
break;
}
case QQuickItem::ItemEnabledHasChanged: {
q->itemChange(change, data);
notifyChangeListeners(QQuickItemPrivate::Enabled, &QQuickItemChangeListener::itemEnabledChanged, q);
break;
}
case QQuickItem::ItemParentHasChanged: {
q->itemChange(change, data);
notifyChangeListeners(QQuickItemPrivate::Parent, &QQuickItemChangeListener::itemParentChanged, q, data.item);
break;
}
case QQuickItem::ItemOpacityHasChanged: {
q->itemChange(change, data);
notifyChangeListeners(QQuickItemPrivate::Opacity, &QQuickItemChangeListener::itemOpacityChanged, q);
break;
}
case QQuickItem::ItemActiveFocusHasChanged:
q->itemChange(change, data);
break;
case QQuickItem::ItemRotationHasChanged: {
q->itemChange(change, data);
notifyChangeListeners(QQuickItemPrivate::Rotation, &QQuickItemChangeListener::itemRotationChanged, q);
break;
}
case QQuickItem::ItemScaleHasChanged: {
q->itemChange(change, data);
notifyChangeListeners(QQuickItemPrivate::Scale, &QQuickItemChangeListener::itemScaleChanged, q);
break;
}
case QQuickItem::ItemTransformHasChanged: {
q->itemChange(change, data);
notifyChangeListeners(QQuickItemPrivate::Matrix, &QQuickItemChangeListener::itemTransformChanged, q, data.item);
break;
}
case QQuickItem::ItemAntialiasingHasChanged:
// fall through
case QQuickItem::ItemDevicePixelRatioHasChanged:
q->itemChange(change, data);
break;
}
}
/*!
\qmlproperty bool QtQuick::Item::smooth
Primarily used in image based items to decide if the item should use smooth
sampling or not. Smooth sampling is performed using linear interpolation, while
non-smooth is performed using nearest neighbor.
In Qt Quick 2.0, this property has minimal impact on performance.
By default, this property is set to \c true.
*/
/*!
\property QQuickItem::smooth
\brief Specifies whether the item is smoothed or not
Primarily used in image based items to decide if the item should use smooth
sampling or not. Smooth sampling is performed using linear interpolation, while
non-smooth is performed using nearest neighbor.
In Qt Quick 2.0, this property has minimal impact on performance.
By default, this property is set to \c true.
*/
bool QQuickItem::smooth() const
{
Q_D(const QQuickItem);
return d->smooth;
}
void QQuickItem::setSmooth(bool smooth)
{
Q_D(QQuickItem);
if (d->smooth == smooth)
return;
d->smooth = smooth;
d->dirty(QQuickItemPrivate::Smooth);
emit smoothChanged(smooth);
}
/*!
\qmlproperty bool QtQuick::Item::activeFocusOnTab
This property holds whether the item wants to be in the tab focus
chain. By default, this is set to \c false.
The tab focus chain traverses elements by first visiting the
parent, and then its children in the order they occur in the
children property. Pressing the tab key on an item in the tab
focus chain will move keyboard focus to the next item in the
chain. Pressing BackTab (normally Shift+Tab) will move focus
to the previous item.
To set up a manual tab focus chain, see \l KeyNavigation. Tab
key events used by Keys or KeyNavigation have precedence over
focus chain behavior; ignore the events in other key handlers
to allow it to propagate.
\note \l{QStyleHints::tabFocusBehavior}{tabFocusBehavior} can further limit focus
to only specific types of controls, such as only text or list controls. This is
the case on macOS, where focus to particular controls may be restricted based on
system settings.
\sa QStyleHints::tabFocusBehavior, focusPolicy
*/
/*!
\property QQuickItem::activeFocusOnTab
This property holds whether the item wants to be in the tab focus
chain. By default, this is set to \c false.
\note \l{QStyleHints::tabFocusBehavior}{tabFocusBehavior} can further limit focus
to only specific types of controls, such as only text or list controls. This is
the case on macOS, where focus to particular controls may be restricted based on
system settings.
\sa QStyleHints::tabFocusBehavior, focusPolicy
*/
// TODO FOCUS: Deprecate
bool QQuickItem::activeFocusOnTab() const
{
Q_D(const QQuickItem);
return d->activeFocusOnTab;
}
void QQuickItem::setActiveFocusOnTab(bool activeFocusOnTab)
{
Q_D(QQuickItem);
if (d->activeFocusOnTab == activeFocusOnTab)
return;
if (window()) {
if ((this == window()->activeFocusItem()) && this != window()->contentItem() && !activeFocusOnTab) {
qWarning("QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item.");
return;
}
}
d->activeFocusOnTab = activeFocusOnTab;
emit activeFocusOnTabChanged(activeFocusOnTab);
}
/*!
\qmlproperty bool QtQuick::Item::antialiasing
Used by visual elements to decide if the item should use antialiasing or not.
In some cases items with antialiasing require more memory and are potentially
slower to render (see \l {Antialiasing} for more details).
The default is false, but may be overridden by derived elements.
*/
/*!
\property QQuickItem::antialiasing
\brief Specifies whether the item is antialiased or not
Used by visual elements to decide if the item should use antialiasing or not.
In some cases items with antialiasing require more memory and are potentially
slower to render (see \l {Antialiasing} for more details).
The default is false, but may be overridden by derived elements.
*/
bool QQuickItem::antialiasing() const
{
Q_D(const QQuickItem);
return d->antialiasingValid ? d->antialiasing : d->implicitAntialiasing;
}
void QQuickItem::setAntialiasing(bool aa)
{
Q_D(QQuickItem);
if (!d->antialiasingValid) {
d->antialiasingValid = true;
d->antialiasing = d->implicitAntialiasing;
}
if (aa == d->antialiasing)
return;
d->antialiasing = aa;
d->dirty(QQuickItemPrivate::Antialiasing);
d->itemChange(ItemAntialiasingHasChanged, bool(d->antialiasing));
emit antialiasingChanged(antialiasing());
}
void QQuickItem::resetAntialiasing()
{
Q_D(QQuickItem);
if (!d->antialiasingValid)
return;
d->antialiasingValid = false;
if (d->implicitAntialiasing != d->antialiasing)
emit antialiasingChanged(antialiasing());
}
void QQuickItemPrivate::setImplicitAntialiasing(bool antialiasing)
{
Q_Q(QQuickItem);
bool prev = q->antialiasing();
implicitAntialiasing = antialiasing;
if (componentComplete && (q->antialiasing() != prev))
emit q->antialiasingChanged(q->antialiasing());
}
/*!
Returns the item flags for this item.
\sa setFlag()
*/
QQuickItem::Flags QQuickItem::flags() const
{
Q_D(const QQuickItem);
return (QQuickItem::Flags)d->flags;
}
/*!
Enables the specified \a flag for this item if \a enabled is true;
if \a enabled is false, the flag is disabled.
These provide various hints for the item; for example, the
ItemClipsChildrenToShape flag indicates that all children of this
item should be clipped to fit within the item area.
*/
void QQuickItem::setFlag(Flag flag, bool enabled)
{
Q_D(QQuickItem);
if (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();
}
}
/*!
Enables the specified \a flags for this item.
\sa setFlag()
*/
void QQuickItem::setFlags(Flags flags)
{
Q_D(QQuickItem);
if (int(flags & ItemIsFocusScope) != int(d->flags & ItemIsFocusScope)) {
if (flags & ItemIsFocusScope && !d->childItems.isEmpty() && d->window) {
qWarning("QQuickItem: Cannot set FocusScope once item has children and is in a window.");
flags &= ~ItemIsFocusScope;
} else if (d->flags & ItemIsFocusScope) {
qWarning("QQuickItem: Cannot unset FocusScope flag.");
flags |= ItemIsFocusScope;
}
}
if (int(flags & ItemClipsChildrenToShape) != int(d->flags & ItemClipsChildrenToShape))
d->dirty(QQuickItemPrivate::Clip);
d->flags = flags;
}
/*!
\qmlproperty real QtQuick::Item::x
\qmlproperty real QtQuick::Item::y
\qmlproperty real QtQuick::Item::width
\qmlproperty real QtQuick::Item::height
Defines the item's position and size.
The default value is \c 0.
The (x,y) position is relative to the \l parent.
\qml
Item { x: 100; y: 100; width: 100; height: 100 }
\endqml
*/
/*!
\property QQuickItem::x
Defines the item's x position relative to its parent.
*/
/*!
\property QQuickItem::y
Defines the item's y position relative to its parent.
*/
qreal QQuickItem::x() const
{
Q_D(const QQuickItem);
return d->x;
}
qreal QQuickItem::y() const
{
Q_D(const QQuickItem);
return d->y;
}
/*!
\internal
*/
QPointF QQuickItem::position() const
{
Q_D(const QQuickItem);
return QPointF(d->x, d->y);
}
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;
const qreal oldx = d->x.valueBypassingBindings();
if (oldx == v)
return;
d->x.setValueBypassingBindings(v);
d->dirty(QQuickItemPrivate::Position);
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;
const qreal oldy = d->y.valueBypassingBindings();
if (oldy == v)
return;
d->y.setValueBypassingBindings(v);
d->dirty(QQuickItemPrivate::Position);
// 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));
}
/*!
\internal
*/
void QQuickItem::setPosition(const QPointF &pos)
{
Q_D(QQuickItem);
const qreal oldx = d->x.valueBypassingBindings();
const qreal oldy = d->y.valueBypassingBindings();
if (QPointF(oldx, oldy) == pos)
return;
/* 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.setValueBypassingBindings(pos.x()); //TODO: investigate whether to break binding here or not
d->y.setValueBypassingBindings(pos.y());
d->dirty(QQuickItemPrivate::Position);
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);
}
/*!
\property QQuickItem::width
This property holds the width of this item.
*/
qreal QQuickItem::width() const
{
Q_D(const QQuickItem);
return d->width;
}
void QQuickItem::setWidth(qreal w)
{
Q_D(QQuickItem);
d->width.removeBindingUnlessInWrapper();
if (qt_is_nan(w))
return;
d->widthValidFlag = true;
const qreal oldWidth = d->width.valueBypassingBindings();
if (oldWidth == w)
return;
d->width.setValueBypassingBindings(w);
d->dirty(QQuickItemPrivate::Size);
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->width.takeBinding();
d->widthValidFlag = false;
setImplicitWidth(implicitWidth());
}
void QQuickItemPrivate::implicitWidthChanged()
{
Q_Q(QQuickItem);
notifyChangeListeners(QQuickItemPrivate::ImplicitWidth, &QQuickItemChangeListener::itemImplicitWidthChanged, q);
emit q->implicitWidthChanged();
}
qreal QQuickItemPrivate::getImplicitWidth() const
{
return implicitWidth;
}
/*!
Returns the width of the item that is implied by other properties that determine the content.
*/
qreal QQuickItem::implicitWidth() const
{
Q_D(const QQuickItem);
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 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.
Setting the implicit size is useful for defining components that have a preferred size
based on their content, for example:
\qml
// Label.qml
import QtQuick 2.0
Item {
property alias icon: image.source
property alias label: text.text
implicitWidth: text.implicitWidth + image.implicitWidth
implicitHeight: Math.max(text.implicitHeight, image.implicitHeight)
Image { id: image }
Text {
id: text
wrapMode: Text.Wrap
anchors.left: image.right; anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
\endqml
\note Using implicitWidth of \l [QML] Text or \l [QML] TextEdit and setting the width explicitly
incurs a performance penalty as the text must be laid out twice.
*/
/*!
\property QQuickItem::implicitWidth
\property QQuickItem::implicitHeight
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.
Setting the implicit size is useful for defining components that have a preferred size
based on their content, for example:
\qml
// Label.qml
import QtQuick 2.0
Item {
property alias icon: image.source
property alias label: text.text
implicitWidth: text.implicitWidth + image.implicitWidth
implicitHeight: Math.max(text.implicitHeight, image.implicitHeight)
Image { id: image }
Text {
id: text
wrapMode: Text.Wrap
anchors.left: image.right; anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
\endqml
\note Using implicitWidth of \l [QML] Text or \l [QML] TextEdit and setting the width explicitly
incurs a performance penalty as the text must be laid out twice.
*/
void QQuickItem::setImplicitWidth(qreal w)
{
Q_D(QQuickItem);
bool changed = w != d->implicitWidth;
d->implicitWidth = w;
// 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.valueBypassingBindings() == w || widthValid())
return;
changed = false;
}
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);
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();
}
/*!
Returns whether the width property has been set explicitly.
*/
bool QQuickItem::widthValid() const
{
Q_D(const QQuickItem);
/* 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();
}
/*!
\property QQuickItem::height
This property holds the height of this item.
*/
qreal QQuickItem::height() const
{
Q_D(const QQuickItem);
return d->height;
}
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->heightValidFlag = true;
const qreal oldHeight = d->height.valueBypassingBindings();
if (oldHeight == h)
return;
d->height.setValueBypassingBindings(h);
d->dirty(QQuickItemPrivate::Size);
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);
// 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);
notifyChangeListeners(QQuickItemPrivate::ImplicitHeight, &QQuickItemChangeListener::itemImplicitHeightChanged, q);
emit q->implicitHeightChanged();
}
qreal QQuickItemPrivate::getImplicitHeight() const
{
return implicitHeight;
}
qreal QQuickItem::implicitHeight() const
{
Q_D(const QQuickItem);
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.valueBypassingBindings() == h || heightValid()) {
if (changed)
d->implicitHeightChanged();
if (d->height.valueBypassingBindings() == h || heightValid())
return;
changed = false;
}
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);
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();
}
/*!
\internal
*/
void QQuickItem::setImplicitSize(qreal w, qreal h)
{
Q_D(QQuickItem);
bool wChanged = w != d->implicitWidth;
bool hChanged = h != d->implicitHeight;
d->implicitWidth = w;
d->implicitHeight = h;
bool wDone = false;
bool hDone = false;
qreal width = d->width.valueBypassingBindings();
qreal height = d->height.valueBypassingBindings();
if (width == w || widthValid()) {
if (wChanged)
d->implicitWidthChanged();
wDone = width == w || widthValid();
wChanged = false;
}
if (height == h || heightValid()) {
if (hChanged)
d->implicitHeightChanged();
hDone = height == h || heightValid();
hChanged = false;
}
if (wDone && hDone)
return;
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);
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();
if (!hDone && hChanged)
d->implicitHeightChanged();
}
/*!
Returns whether the height property has been set explicitly.
*/
bool QQuickItem::heightValid() const
{
Q_D(const QQuickItem);
return d->heightValid();
}
/*!
\since 5.10
Returns the size of the item.
\sa setSize, width, height
*/
QSizeF QQuickItem::size() const
{
Q_D(const QQuickItem);
return QSizeF(d->width, d->height);
}
/*!
\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->heightValidFlag = true;
d->widthValidFlag = true;
const qreal oldHeight = d->height.valueBypassingBindings();
const qreal oldWidth = d->width.valueBypassingBindings();
if (oldWidth == size.width() && oldHeight == size.height())
return;
d->height.setValueBypassingBindings(size.height());
d->width.setValueBypassingBindings(size.width());
d->dirty(QQuickItemPrivate::Size);
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.
If activeFocus is true, either this item is the one that currently
receives keyboard input, or it is a FocusScope ancestor of the item
that currently receives keyboard input.
Usually, activeFocus is gained by setting \l focus on an item and its
enclosing FocusScope objects. In the following example, the \c input
and \c focusScope objects will have active focus, while the root
rectangle object will not.
\qml
import QtQuick 2.0
Rectangle {
width: 100; height: 100
FocusScope {
id: focusScope
focus: true
TextInput {
id: input
focus: true
}
}
}
\endqml
\sa focus, {Keyboard Focus in Qt Quick}
*/
/*!
\property QQuickItem::activeFocus
\readonly
This read-only property indicates whether the item has active focus.
If activeFocus is true, either this item is the one that currently
receives keyboard input, or it is a FocusScope ancestor of the item
that currently receives keyboard input.
Usually, activeFocus is gained by setting \l focus on an item and its
enclosing FocusScope objects. In the following example, the \c input
and \c focusScope objects will have active focus, while the root
rectangle object will not.
\qml
import QtQuick 2.0
Rectangle {
width: 100; height: 100
FocusScope {
focus: true
TextInput {
id: input
focus: true
}
}
}
\endqml
\sa focus, {Keyboard Focus in Qt Quick}
*/
bool QQuickItem::hasActiveFocus() const
{
Q_D(const QQuickItem);
return d->activeFocus;
}
/*!
\qmlproperty bool QtQuick::Item::focus
This property holds whether the item has focus within the enclosing
FocusScope. If true, this item will gain active focus when the
enclosing FocusScope gains active focus.
In the following example, \c input will be given active focus when
\c scope gains active focus:
\qml
import QtQuick 2.0
Rectangle {
width: 100; height: 100
FocusScope {
id: scope
TextInput {
id: input
focus: true
}
}
}
\endqml
For the purposes of this property, the scene as a whole is assumed
to act like a focus scope. On a practical level, that means the
following QML will give active focus to \c input on startup.
\qml
Rectangle {
width: 100; height: 100
TextInput {
id: input
focus: true
}
}
\endqml
\sa activeFocus, {Keyboard Focus in Qt Quick}
*/
/*!
\property QQuickItem::focus
This property holds whether the item has focus within the enclosing
FocusScope. If true, this item will gain active focus when the
enclosing FocusScope gains active focus.
In the following example, \c input will be given active focus when
\c scope gains active focus:
\qml
import QtQuick 2.0
Rectangle {
width: 100; height: 100
FocusScope {
id: scope
TextInput {
id: input
focus: true
}
}
}
\endqml
For the purposes of this property, the scene as a whole is assumed
to act like a focus scope. On a practical level, that means the
following QML will give active focus to \c input on startup.
\qml
Rectangle {
width: 100; height: 100
TextInput {
id: input
focus: true
}
}
\endqml
\sa activeFocus, {Keyboard Focus in Qt Quick}
*/
bool QQuickItem::hasFocus() const
{
Q_D(const QQuickItem);
return d->focus;
}
void QQuickItem::setFocus(bool focus)
{
setFocus(focus, Qt::OtherFocusReason);
}
void QQuickItem::setFocus(bool focus, Qt::FocusReason reason)
{
Q_D(QQuickItem);
// Need to find our nearest focus scope
QQuickItem *scope = parentItem();
while (scope && !scope->isFocusScope() && scope->parentItem())
scope = scope->parentItem();
if (d->focus == focus && d->activeFocus == focus && (!focus || !scope || QQuickItemPrivate::get(scope)->subFocusItem == this))
return;
bool notifyListeners = false;
if (d->window || d->parentItem) {
if (d->window) {
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
QVarLengthArray<QQuickItem *, 20> changed;
QQuickItem *oldSubFocusItem = QQuickItemPrivate::get(scope)->subFocusItem;
if (oldSubFocusItem) {
QQuickItemPrivate::get(oldSubFocusItem)->updateSubFocusItem(scope, false);
QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
changed << oldSubFocusItem;
} else if (!scope->isFocusScope() && scope->hasFocus()) {
QQuickItemPrivate::get(scope)->focus = false;
changed << scope;
}
d->updateSubFocusItem(scope, focus);
d->focus = focus;
changed << this;
notifyListeners = true;
emit focusChanged(focus);
QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.size() - 1, reason);
}
} else {
QVarLengthArray<QQuickItem *, 20> changed;
QQuickItem *oldSubFocusItem = d->subFocusItem;
if (!isFocusScope() && oldSubFocusItem) {
QQuickItemPrivate::get(oldSubFocusItem)->updateSubFocusItem(this, false);
QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
changed << oldSubFocusItem;
}
d->focus = focus;
changed << this;
notifyListeners = true;
emit focusChanged(focus);
QQuickDeliveryAgentPrivate::notifyFocusChangesRecur(changed.data(), changed.size() - 1, reason);
}
if (notifyListeners)
d->notifyChangeListeners(QQuickItemPrivate::Focus, &QQuickItemChangeListener::itemFocusChanged, this, reason);
}
/*!
Returns true if this item is a focus scope, and false otherwise.
*/
bool QQuickItem::isFocusScope() const
{
return flags() & ItemIsFocusScope;
}
/*!
If this item is a focus scope, this returns the item in its focus chain
that currently has focus.
Returns \nullptr if this item is not a focus scope.
*/
QQuickItem *QQuickItem::scopedFocusItem() const
{
Q_D(const QQuickItem);
if (!isFocusScope())
return nullptr;
else
return d->subFocusItem;
}
/*!
\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).
\since 5.7
\sa parentItem()
*/
bool QQuickItem::isAncestorOf(const QQuickItem *child) const
{
if (!child || child == this)
return false;
const QQuickItem *ancestor = child;
while ((ancestor = ancestor->parentItem())) {
if (ancestor == this)
return true;
}
return false;
}
/*!
Returns the mouse buttons accepted by this item.
The default value is Qt::NoButton; that is, no mouse buttons are accepted.
If an item does not accept the mouse button for a particular mouse event,
the mouse event will not be delivered to the item and will be delivered
to the next item in the item hierarchy instead.
\sa acceptTouchEvents()
*/
Qt::MouseButtons QQuickItem::acceptedMouseButtons() const
{
Q_D(const QQuickItem);
return d->acceptedMouseButtons();
}
/*!
Sets the mouse buttons accepted by this item to \a buttons.
\note In Qt 5, calling setAcceptedMouseButtons() implicitly caused
an item to receive touch events as well as mouse events; but it was
recommended to call setAcceptTouchEvents() to subscribe for them.
In Qt 6, it is necessary to call setAcceptTouchEvents() to continue
to receive them.
*/
void QQuickItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
{
Q_D(QQuickItem);
d->extra.setTag(d->extra.tag().setFlag(QQuickItemPrivate::LeftMouseButtonAccepted, buttons & Qt::LeftButton));
buttons &= ~Qt::LeftButton;
if (buttons || d->extra.isAllocated()) {
d->extra.value().acceptedMouseButtonsWithoutHandlers = buttons;
d->extra.value().acceptedMouseButtons = d->extra->pointerHandlers.isEmpty() ? buttons : Qt::AllButtons;
}
}
/*!
Returns whether pointer events intended for this item's children should be
filtered through this item.
If both this item and a child item have acceptTouchEvents() \c true, then
when a touch interaction occurs, this item will filter the touch event.
But if either this item or the child cannot handle touch events,
childMouseEventFilter() will be called with a synthesized mouse event.
\sa setFiltersChildMouseEvents(), childMouseEventFilter()
*/
bool QQuickItem::filtersChildMouseEvents() const
{
Q_D(const QQuickItem);
return d->filtersChildMouseEvents;
}
/*!
Sets whether pointer events intended for this item's children should be
filtered through this item.
If \a filter is true, childMouseEventFilter() will be called when
a pointer event is triggered for a child item.
\sa filtersChildMouseEvents()
*/
void QQuickItem::setFiltersChildMouseEvents(bool filter)
{
Q_D(QQuickItem);
d->filtersChildMouseEvents = filter;
}
/*!
\internal
*/
bool QQuickItem::isUnderMouse() const
{
Q_D(const QQuickItem);
if (!d->window)
return false;
// QQuickWindow handles QEvent::Leave to reset the lastMousePosition
// FIXME: Using QPointF() as the reset value means an item will not be
// under the mouse if the mouse is at 0,0 of the window.
if (const_cast<QQuickItemPrivate *>(d)->deliveryAgentPrivate()->lastMousePosition == QPointF())
return false;
QPointF cursorPos = QGuiApplicationPrivate::lastCursorPosition;
return contains(mapFromScene(d->window->mapFromGlobal(cursorPos)));
}
/*!
Returns whether hover events are accepted by this item.
The default value is false.
If this is false, then the item will not receive any hover events through
the hoverEnterEvent(), hoverMoveEvent() and hoverLeaveEvent() functions.
*/
bool QQuickItem::acceptHoverEvents() const
{
Q_D(const QQuickItem);
return d->hoverEnabled;
}
/*!
If \a enabled is true, this sets the item to accept hover events;
otherwise, hover events are not accepted by this item.
\sa acceptHoverEvents()
*/
void QQuickItem::setAcceptHoverEvents(bool enabled)
{
Q_D(QQuickItem);
// hoverEnabled causes hoveredLeafItemFound to be set to true when a hover
// event is being delivered to this item, which effectively ends hover
// event delivery, as it will then start sending hover events backwards
// from the child to the root, in a straight line.
d->hoverEnabled = enabled;
// Recursively set subtreeHoverEnabled for all of our parents. Note that
// even though this and hoverEnabled are set to the same values in this
// function, only subtreeHoverEnabled is set for the entire parent chain.
// subtreeHoverEnabled says that a certain tree _may_ want hover events,
// but unlike hoverEnabled, won't prevent delivery to siblings.
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);
}
/*!
Returns whether touch events are accepted by this item.
The default value is \c false.
If this is \c false, then the item will not receive any touch events through
the touchEvent() function.
\since 5.10
*/
bool QQuickItem::acceptTouchEvents() const
{
Q_D(const QQuickItem);
return d->touchEnabled;
}
/*!
If \a enabled is true, this sets the item to accept touch events;
otherwise, touch events are not accepted by this item.
\since 5.10
\sa acceptTouchEvents()
*/
void QQuickItem::setAcceptTouchEvents(bool enabled)
{
Q_D(QQuickItem);
d->touchEnabled = enabled;
}
void QQuickItemPrivate::setHasCursorInChild(bool hc)
{
#if QT_CONFIG(cursor)
Q_Q(QQuickItem);
// if we're asked to turn it off (because of an unsetcursor call, or a node
// removal) then we should make sure it's really ok to turn it off.
if (!hc && subtreeCursorEnabled) {
if (hasCursor || hasCursorHandler)
return; // nope! sorry, I have a cursor myself
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.
}
}
subtreeCursorEnabled = hc;
QQuickItem *parent = q->parentItem();
if (parent) {
QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parent);
parentPrivate->setHasCursorInChild(hc);
}
#else
Q_UNUSED(hc);
#endif
}
void QQuickItemPrivate::setHasHoverInChild(bool hasHover)
{
Q_Q(QQuickItem);
// if we're asked to turn it off (because of a setAcceptHoverEvents call, or a node
// removal) then we should make sure it's really ok to turn it off.
if (!hasHover && subtreeHoverEnabled) {
if (hoverEnabled)
return; // nope! sorry, I need hover myself
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->hasEnabledHoverHandlers())
return; // nope! sorry, we have pointer handlers which are interested.
}
}
qCDebug(lcHoverTrace) << q << subtreeHoverEnabled << "->" << hasHover;
subtreeHoverEnabled = hasHover;
QQuickItem *parent = q->parentItem();
if (parent) {
QQuickItemPrivate *parentPrivate = QQuickItemPrivate::get(parent);
parentPrivate->setHasHoverInChild(hasHover);
}
}
#if QT_CONFIG(cursor)
QWindow *QQuickItemPrivate::renderWindow(QPoint *offset) const
{
QWindow *result = QQuickRenderControl::renderWindowFor(window, offset);
return result ? result : window;
}
/*!
Returns the cursor shape for this item.
The mouse cursor will assume this shape when it is over this
item, unless an override cursor is set.
See the \l{Qt::CursorShape}{list of predefined cursor objects} for a
range of useful shapes.
If no cursor shape has been set this returns a cursor with the Qt::ArrowCursor shape, however
another cursor shape may be displayed if an overlapping item has a valid cursor.
\sa setCursor(), unsetCursor()
*/
QCursor QQuickItem::cursor() const
{
Q_D(const QQuickItem);
return d->extra.isAllocated()
? d->extra->cursor
: QCursor();
}
/*!
Sets the \a cursor shape for this item.
\sa cursor(), unsetCursor()
*/
void QQuickItem::setCursor(const QCursor &cursor)
{
Q_D(QQuickItem);
Qt::CursorShape oldShape = d->extra.isAllocated() ? d->extra->cursor.shape() : Qt::ArrowCursor;
qCDebug(lcHoverCursor) << oldShape << "->" << cursor.shape();
if (oldShape != cursor.shape() || oldShape >= Qt::LastCursor || cursor.shape() >= Qt::LastCursor) {
d->extra.value().cursor = cursor;
if (d->window) {
QWindow *renderWindow = d->renderWindow();
if (QQuickWindowPrivate::get(d->window)->cursorItem == this)
renderWindow->setCursor(cursor);
}
}
QPointF updateCursorPos;
if (!d->hasCursor) {
d->hasCursor = true;
if (d->window) {
QWindow *renderWindow = d->renderWindow();
QPointF pos = renderWindow->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition);
if (contains(mapFromScene(pos)))
updateCursorPos = pos;
}
}
d->setHasCursorInChild(d->hasCursor || d->hasCursorHandler);
if (!updateCursorPos.isNull())
QQuickWindowPrivate::get(d->window)->updateCursor(updateCursorPos);
}
/*!
Clears the cursor shape for this item.
\sa cursor(), setCursor()
*/
void QQuickItem::unsetCursor()
{
Q_D(QQuickItem);
qCDebug(lcHoverTrace) << "clearing cursor";
if (!d->hasCursor)
return;
d->hasCursor = false;
d->setHasCursorInChild(d->hasCursorHandler);
if (d->extra.isAllocated())
d->extra->cursor = QCursor();
if (d->window) {
QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(d->window);
if (windowPrivate->cursorItem == this) {
QPointF pos = d->window->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition);
windowPrivate->updateCursor(pos);
}
}
}
/*!
\internal
Returns the cursor that should actually be shown, allowing the given
\a handler to override the Item cursor if it is active or hovered.
\sa cursor(), setCursor(), QtQuick::PointerHandler::cursor
*/
QCursor QQuickItemPrivate::effectiveCursor(const QQuickPointerHandler *handler) const
{
Q_Q(const QQuickItem);
if (!handler)
return q->cursor();
bool hoverCursorSet = false;
QCursor hoverCursor;
bool activeCursorSet = false;
QCursor activeCursor;
if (const QQuickHoverHandler *hoverHandler = qobject_cast<const QQuickHoverHandler *>(handler)) {
hoverCursorSet = hoverHandler->isCursorShapeExplicitlySet();
hoverCursor = hoverHandler->cursorShape();
} else if (handler->active()) {
activeCursorSet = handler->isCursorShapeExplicitlySet();
activeCursor = handler->cursorShape();
}
if (activeCursorSet)
return activeCursor;
if (hoverCursorSet)
return hoverCursor;
return q->cursor();
}
/*!
\internal
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* activeHandler = nullptr;
QQuickPointerHandler* mouseHandler = nullptr;
QQuickPointerHandler* nonMouseHandler = nullptr;
for (QQuickPointerHandler *h : extra->pointerHandlers) {
if (!h->isCursorShapeExplicitlySet())
continue;
QQuickHoverHandler *hoverHandler = qmlobject_cast<QQuickHoverHandler *>(h);
// 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())
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;
}
if (mouseHandler)
qCDebug(lcHoverTrace) << "mouse handler choosing cursor" << mouseHandler << mouseHandler->cursorShape();
return mouseHandler;
}
#endif
/*!
\deprecated Use QPointerEvent::setExclusiveGrabber().
Grabs the mouse input.
This item will receive all mouse events until ungrabMouse() is called.
Usually this function should not be called, since accepting for example
a mouse press event makes sure that the following events are delivered
to the item.
If an item wants to take over mouse events from the current receiver,
it needs to call this function.
\warning This function should be used with caution.
*/
void QQuickItem::grabMouse()
{
Q_D(QQuickItem);
if (!d->window)
return;
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
auto eventInDelivery = da->eventInDelivery();
if (!eventInDelivery) {
qWarning() << "cannot grab mouse: no event is currently being delivered";
return;
}
auto epd = da->mousePointData();
if (!epd) {
qCDebug(lcPtr) << "cannot grab mouse: the event being delivered has no mouse point";
return;
}
eventInDelivery->setExclusiveGrabber(epd->eventPoint, this);
}
/*!
\deprecated Use QPointerEvent::setExclusiveGrabber().
Releases the mouse grab following a call to grabMouse().
Note that this function should only be called when the item wants
to stop handling further events. There is no need to call this function
after a release or cancel event since no future events will be received
in any case. No move or release events will be delivered after this
function was called.
*/
void QQuickItem::ungrabMouse()
{
Q_D(QQuickItem);
if (!d->window)
return;
auto da = d->deliveryAgentPrivate();
Q_ASSERT(da);
auto eventInDelivery = da->eventInDelivery();
if (!eventInDelivery) {
// do it the expensive way
da->removeGrabber(this);
return;
}
auto epd = da->mousePointData();
if (epd && eventInDelivery->exclusiveGrabber(epd->eventPoint) == this)
eventInDelivery->setExclusiveGrabber(epd->eventPoint, nullptr);
}
/*!
Returns whether mouse input should exclusively remain with this item.
\sa setKeepMouseGrab(), QEvent::accept(), QEvent::ignore()
*/
bool QQuickItem::keepMouseGrab() const
{
Q_D(const QQuickItem);
return d->keepMouse;
}
/*!
Sets whether the mouse input should remain exclusively with this item.
This is useful for items that wish to grab and keep mouse
interaction following a predefined gesture. For example,
an item that is interested in horizontal mouse movement
may set keepMouseGrab to true once a threshold has been
exceeded. Once keepMouseGrab has been set to true, filtering
items will not react to mouse events.
If \a keep is false, a filtering item may steal the grab. For example,
\l Flickable may attempt to steal a mouse grab if it detects that the
user has begun to move the viewport.
\sa keepMouseGrab()
*/
void QQuickItem::setKeepMouseGrab(bool keep)
{
Q_D(QQuickItem);
d->keepMouse = keep;
}
/*!
\deprecated Use QPointerEvent::setExclusiveGrabber().
Grabs the touch points specified by \a ids.
These touch points will be owned by the item until
they are released. Alternatively, the grab can be stolen
by a filtering item like Flickable. Use setKeepTouchGrab()
to prevent the grab from being stolen.
*/
void QQuickItem::grabTouchPoints(const QList<int> &ids)
{
Q_D(QQuickItem);
auto event = d->deliveryAgentPrivate()->eventInDelivery();
if (Q_UNLIKELY(!event)) {
qWarning() << "cannot grab: no event is currently being delivered";
return;
}
for (auto pt : event->points()) {
if (ids.contains(pt.id()))
event->setExclusiveGrabber(pt, this);
}
}
/*!
\deprecated Use QEventPoint::setExclusiveGrabber() instead.
Ungrabs the touch points owned by this item.
*/
void QQuickItem::ungrabTouchPoints()
{
Q_D(QQuickItem);
if (!d->window)
return;
if (QQuickDeliveryAgentPrivate *da = d->deliveryAgentPrivate())
da->removeGrabber(this, false, true);
}
/*!
Returns whether the touch points grabbed by this item should exclusively
remain with this item.
\sa setKeepTouchGrab(), keepMouseGrab(), QEvent::accept(), QEvent::ignore()
*/
bool QQuickItem::keepTouchGrab() const
{
Q_D(const QQuickItem);
return d->keepTouch;
}
/*!
Sets whether the touch points grabbed by this item should remain
exclusively with this item.
This is useful for items that wish to grab and keep specific touch
points following a predefined gesture. For example,
an item that is interested in horizontal touch point movement
may set setKeepTouchGrab to true once a threshold has been
exceeded. Once setKeepTouchGrab has been set to true, filtering
items will not react to the relevant touch points.
If \a keep is false, a filtering item may steal the grab. For example,
\l Flickable may attempt to steal a touch point grab if it detects that the
user has begun to move the viewport.
\sa keepTouchGrab(), setKeepMouseGrab()
*/
void QQuickItem::setKeepTouchGrab(bool keep)
{
Q_D(QQuickItem);
d->keepTouch = keep;
}
/*!
\qmlmethod bool QtQuick::Item::contains(point point)
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 \c true if this item contains \a point, which is in local coordinates;
returns \c false otherwise.
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 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->extra.isAllocated() && d->extra->mask) {
if (auto quickMask = qobject_cast<QQuickItem *>(d->extra->mask))
return quickMask->contains(point - quickMask->position());
bool res = false;
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;
}
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
\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 \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 \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 {D-shaped containment mask
with cursor showing non-rectangular hit testing}
\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 {Circular containment mask
with cursor showing round hit testing region}
\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);
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 (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 = 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("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->maskContainsIndex = methodIndex;
}
d->extra->mask = mask;
quickMask = qobject_cast<QQuickItem *>(mask);
if (quickMask) {
QQuickItemPrivate *maskPrivate = QQuickItemPrivate::get(quickMask);
maskPrivate->registerAsContainmentMask(this, true); // telling maskPrivate that "this" is using it as mask
}
emit containmentMaskChanged();
}
/*!
Maps the given \a point in this item's coordinate system to the equivalent
point within \a item's coordinate system, and returns the mapped
coordinate.
\input item.qdocinc mapping
If \a item is \nullptr, this maps \a point to the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QPointF QQuickItem::mapToItem(const QQuickItem *item, const QPointF &point) const
{
QPointF p = mapToScene(point);
if (item) {
const auto *itemWindow = item->window();
const auto *thisWindow = window();
if (thisWindow && itemWindow && itemWindow != thisWindow)
p = itemWindow->mapFromGlobal(thisWindow->mapToGlobal(p));
p = item->mapFromScene(p);
}
return p;
}
/*!
Maps the given \a point in this item's coordinate system to the equivalent
point within the scene's coordinate system, and returns the mapped
coordinate.
\input item.qdocinc mapping
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QPointF QQuickItem::mapToScene(const QPointF &point) const
{
Q_D(const QQuickItem);
return d->itemToWindowTransform().map(point);
}
/*!
Maps the given \a point in this item's coordinate system to the equivalent
point within global screen coordinate system, and returns the mapped
coordinate.
\input item.qdocinc mapping
For example, this may be helpful to add a popup to a Qt Quick component.
\note Window positioning is done by the window manager and this value is
treated only as a hint. So, the resulting window position may differ from
what is expected.
\since 5.7
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QPointF QQuickItem::mapToGlobal(const QPointF &point) const
{
Q_D(const QQuickItem);
if (Q_UNLIKELY(d->window == nullptr))
return mapToScene(point);
QPoint renderOffset;
QWindow *renderWindow = d->renderWindow(&renderOffset);
return renderWindow->mapToGlobal((mapToScene(point) + renderOffset));
}
/*!
Maps the given \a rect in this item's coordinate system to the equivalent
rectangular area within \a item's coordinate system, and returns the mapped
rectangle value.
\input item.qdocinc mapping
If \a item is \nullptr, this maps \a rect to the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QRectF QQuickItem::mapRectToItem(const QQuickItem *item, const QRectF &rect) const
{
Q_D(const QQuickItem);
QTransform t = d->itemToWindowTransform();
if (item)
t *= QQuickItemPrivate::get(item)->windowToItemTransform();
return t.mapRect(rect);
}
/*!
Maps the given \a rect in this item's coordinate system to the equivalent
rectangular area within the scene's coordinate system, and returns the mapped
rectangle value.
\input item.qdocinc mapping
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QRectF QQuickItem::mapRectToScene(const QRectF &rect) const
{
Q_D(const QQuickItem);
return d->itemToWindowTransform().mapRect(rect);
}
/*!
Maps the given \a point in \a item's coordinate system to the equivalent
point within this item's coordinate system, and returns the mapped
coordinate.
\input item.qdocinc mapping
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 = point;
if (item) {
p = item->mapToScene(point);
const auto *itemWindow = item->window();
const auto *thisWindow = window();
if (thisWindow && itemWindow && itemWindow != thisWindow)
p = thisWindow->mapFromGlobal(itemWindow->mapToGlobal(p));
}
return mapFromScene(p);
}
/*!
Maps the given \a point in the scene's coordinate system to the equivalent
point within this item's coordinate system, and returns the mapped
coordinate.
\input item.qdocinc mapping
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QPointF QQuickItem::mapFromScene(const QPointF &point) const
{
Q_D(const QQuickItem);
return d->windowToItemTransform().map(point);
}
/*!
Maps the given \a point in the global screen coordinate system to the
equivalent point within this item's coordinate system, and returns the
mapped coordinate.
\input item.qdocinc mapping
For example, this may be helpful to add a popup to a Qt Quick component.
\note Window positioning is done by the window manager and this value is
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}
*/
QPointF QQuickItem::mapFromGlobal(const QPointF &point) const
{
Q_D(const QQuickItem);
QPointF scenePoint;
if (Q_LIKELY(d->window)) {
QPoint renderOffset;
QWindow *renderWindow = d->renderWindow();
scenePoint = renderWindow->mapFromGlobal(point) - renderOffset;
} else {
scenePoint = point;
}
if (auto da = QQuickDeliveryAgentPrivate::currentOrItemDeliveryAgent(this)) {
if (auto sceneTransform = da->sceneTransform())
scenePoint = sceneTransform->map(scenePoint);
}
return mapFromScene(scenePoint);
}
/*!
Maps the given \a rect in \a item's coordinate system to the equivalent
rectangular area within this item's coordinate system, and returns the mapped
rectangle value.
\input item.qdocinc mapping
If \a item is \nullptr, this maps \a rect from the coordinate system of the
scene.
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QRectF QQuickItem::mapRectFromItem(const QQuickItem *item, const QRectF &rect) const
{
Q_D(const QQuickItem);
QTransform t = item?QQuickItemPrivate::get(item)->itemToWindowTransform():QTransform();
t *= d->windowToItemTransform();
return t.mapRect(rect);
}
/*!
Maps the given \a rect in the scene's coordinate system to the equivalent
rectangular area within this item's coordinate system, and returns the mapped
rectangle value.
\input item.qdocinc mapping
\sa {Concepts - Visual Coordinates in Qt Quick}
*/
QRectF QQuickItem::mapRectFromScene(const QRectF &rect) const
{
Q_D(const QQuickItem);
return d->windowToItemTransform().mapRect(rect);
}
/*!
\qmlproperty int QtQuick::Item::mutabilityGroup
\since 6.12
This is an advanced property which can be used for low-level optimizations. It serves as a hint
for the renderer about how often an item will be updated. In typical use cases, leaving this
property as its default (\c Item.AutoMutabilityGroup (\c{0})) will suffice.
However, in certain cases an analysis of performance may uncover bottlenecks that the
default behavior of the Qt Quick scenegraph renderer has been unable to optimize. Typically this
can happen if rapidly updated geometry is batched together with static geometry. To avoid this,
you may for instance try assigning the rapidly updated components to
Item.DynamicMutabilityGroup. Geometry from different mutability groups will not be batched
together.
The mutability group only applies to the item itself. It does not propagate to children.
See \l{Qt Quick Scene Graph Default Renderer} for more information about the inner workings of
the Qt Quick Scene Graph Renderer and geometry batching.
Predefined values:
\value Item.AutoMutabilityGroup The default mutability group.
\value Item.StaticMutabilityGroup Indicates that the item is rarely or never updated.
\value Item.ModerateMutabilityGroup Indicates that the item is updated moderately often.
\value Item.DynamicMutabilityGroup Indicates that the item is updated often / every frame.
\note The valid numerical range of mutability groups is [0 .. 15]. The property will be clamped
to this range. By convention, frequency is expected to increase with the numerical value of the
group.
*/
/*!
\property QQuickItem::mutabilityGroup
\since 6.12
\brief Hints renderer on frequency of changes to item
This is an advanced property which can be used for low-level optimizations. It serves as a hint
for the renderer about how often an item will be updated. In typical use cases, leaving this
property as its default (\c QQuickItem::AutoMutabilityGroup (\c{0})) will suffice.
However, in certain cases an analysis of performance may uncover bottlenecks that the
default behavior of the Qt Quick scenegraph renderer has been unable to optimize. Typically this
can happen if rapidly updated geometry is batched together with static geometry. To avoid this,
you may for instance try assigning the rapidly updated components to
QQuickItem::DynamicMutabilityGroup. Geometry from different mutability groups will not be
batched together.
The mutability group only applies to the item itself. It does not propagate to children.
See \l{Qt Quick Scene Graph Default Renderer} for more information about the inner workings of
the Qt Quick Scene Graph Renderer and geometry batching.
\note The valid numerical range of mutability groups is [0 .. 15]. The property will be clamped
to this range. By convention, frequency is expected to increase with the numerical value of the
group.
*/
/*!
\enum QQuickItem::MutabilityGroup
\since 6.12
This enum provides predefined values that may be used for the \l{mutabilityGroup} property.
\value AutoMutabilityGroup The default mutability group.
\value StaticMutabilityGroup Indicates that the item is rarely or never updated.
\value ModerateMutabilityGroup Indicates that the item is updated moderately often.
\value DynamicMutabilityGroup Indicates that the item is updated often / every frame.
*/
int QQuickItem::mutabilityGroup() const
{
Q_D(const QQuickItem);
if (d->extra.isAllocated())
return d->extra->mutabilityGroup;
return int(QQuickItem::AutoMutabilityGroup);
}
void QQuickItem::setMutabilityGroup(int mutabilityGroup)
{
Q_D(QQuickItem);
const int clampedGroup = qBound(int(AutoMutabilityGroup),
mutabilityGroup,
int(DynamicMutabilityGroup));
if (Q_UNLIKELY(clampedGroup != mutabilityGroup)) {
qCDebug(QSG_LOG_RENDERLOOP) << "QQuickItem::setMutabilityGroup: Invalid group"
<< mutabilityGroup
<< ", clamping to"
<< clampedGroup;
}
if (clampedGroup == this->mutabilityGroup())
return;
d->extra.value().mutabilityGroup = clampedGroup;
d->extra.value().mutabilityGroupSet = true;
d->dirty(QQuickItemPrivate::Content); // Trigger updating paint node
emit mutabilityGroupChanged(clampedGroup);
}
/*!
\property QQuickItem::anchors
\internal
*/
/*!
\property QQuickItem::left
\internal
*/
/*!
\property QQuickItem::right
\internal
*/
/*!
\property QQuickItem::horizontalCenter
\internal
*/
/*!
\property QQuickItem::top
\internal
*/
/*!
\property QQuickItem::bottom
\internal
*/
/*!
\property QQuickItem::verticalCenter
\internal
*/
/*!
\property QQuickItem::baseline
\internal
*/
/*!
\property QQuickItem::data
\internal
*/
/*!
\property QQuickItem::resources
\internal
*/
/*!
\reimp
*/
bool QQuickItem::event(QEvent *ev)
{
Q_D(QQuickItem);
switch (ev->type()) {
#if QT_CONFIG(im)
case QEvent::InputMethodQuery: {
QInputMethodQueryEvent *query = static_cast<QInputMethodQueryEvent *>(ev);
Qt::InputMethodQueries queries = query->queries();
for (uint i = 0; i < 32; ++i) {
Qt::InputMethodQuery q = (Qt::InputMethodQuery)(int)(queries & (1<<i));
if (q) {
QVariant v = inputMethodQuery(q);
query->setValue(q, v);
}
}
query->accept();
break;
}
case QEvent::InputMethod:
inputMethodEvent(static_cast<QInputMethodEvent *>(ev));
break;
#endif // im
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
case QEvent::TouchCancel:
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();
update();
}
break;
case QEvent::HoverEnter:
hoverEnterEvent(static_cast<QHoverEvent*>(ev));
break;
case QEvent::HoverLeave:
hoverLeaveEvent(static_cast<QHoverEvent*>(ev));
break;
case QEvent::HoverMove:
hoverMoveEvent(static_cast<QHoverEvent*>(ev));
break;
case QEvent::KeyPress:
case QEvent::KeyRelease:
d->deliverKeyEvent(static_cast<QKeyEvent*>(ev));
break;
case QEvent::ShortcutOverride:
d->deliverShortcutOverrideEvent(static_cast<QKeyEvent*>(ev));
break;
case QEvent::FocusIn:
focusInEvent(static_cast<QFocusEvent*>(ev));
break;
case QEvent::FocusOut:
focusOutEvent(static_cast<QFocusEvent*>(ev));
break;
case QEvent::MouseMove:
mouseMoveEvent(static_cast<QMouseEvent*>(ev));
break;
#if QT_CONFIG(quick_draganddrop)
case QEvent::DragEnter:
dragEnterEvent(static_cast<QDragEnterEvent*>(ev));
break;
case QEvent::DragLeave:
dragLeaveEvent(static_cast<QDragLeaveEvent*>(ev));
break;
case QEvent::DragMove:
dragMoveEvent(static_cast<QDragMoveEvent*>(ev));
break;
case QEvent::Drop:
dropEvent(static_cast<QDropEvent*>(ev));
break;
#endif // quick_draganddrop
#if QT_CONFIG(gestures)
case QEvent::NativeGesture:
ev->ignore();
break;
#endif // gestures
case QEvent::LanguageChange:
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;
case QEvent::ContextMenu:
// ### Qt 7: add virtual contextMenuEvent (and to QWindow?)
d->handleContextMenuEvent(static_cast<QContextMenuEvent*>(ev));
break;
default:
return QObject::event(ev);
}
return true;
}
#ifndef QT_NO_DEBUG_STREAM
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(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() && !QQmlData::wasDeleted(item)) {
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())
<< ", geometry=";
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 // QT_NO_DEBUG_STREAM
/*!
\fn bool QQuickItem::isTextureProvider() const
Returns true if this item is a texture provider. The default
implementation returns false.
This function can be called from any thread.
*/
bool QQuickItem::isTextureProvider() const
{
#if QT_CONFIG(quick_shadereffect)
Q_D(const QQuickItem);
return d->extra.isAllocated() && d->extra->layer && d->extra->layer->effectSource() ?
d->extra->layer->effectSource()->isTextureProvider() : false;
#else
return false;
#endif
}
/*!
\fn QSGTextureProvider *QQuickItem::textureProvider() const
Returns the texture provider for an item. The default implementation
returns \nullptr.
This function may only be called on the rendering thread.
*/
QSGTextureProvider *QQuickItem::textureProvider() const
{
#if QT_CONFIG(quick_shadereffect)
Q_D(const QQuickItem);
return d->extra.isAllocated() && d->extra->layer && d->extra->layer->effectSource() ?
d->extra->layer->effectSource()->textureProvider() : nullptr;
#else
return 0;
#endif
}
/*!
\since 6.0
\qmlproperty Palette QtQuick::Item::palette
This property holds the palette currently set for the item.
This property describes the item's requested palette. The palette is used by the item's style
when rendering all controls, and is available as a means to ensure that custom controls can
maintain consistency with the native platform's native look and feel. It's common that
different platforms, or different styles, define different palettes for an application.
The default palette depends on the system environment. ApplicationWindow maintains a
system/theme palette which serves as a default for all controls. There may also be special
palette defaults for certain types of controls. You can also set the default palette for
controls by either:
\list
\li passing a custom palette to QGuiApplication::setPalette(), before loading any QML; or
\li specifying the colors in the \l {Qt Quick Controls 2 Configuration File}
{qtquickcontrols2.conf file}.
\endlist
Items propagate explicit palette properties from parents to children. If you change a specific
property on a items's palette, that property propagates to all of the item's children,
overriding any system defaults for that property.
\code
Item {
palette {
buttonText: "maroon"
button: "lavender"
}
Button {
text: "Click Me"
}
}
\endcode
\sa Window::palette, Popup::palette, ColorGroup, Palette, SystemPalette
*/
#if QT_CONFIG(quick_shadereffect)
/*!
\property QQuickItem::layer
\internal
*/
QQuickItemLayer *QQuickItemPrivate::layer() const
{
if (!extra.isAllocated() || !extra->layer) {
extra.value().layer = new QQuickItemLayer(const_cast<QQuickItem *>(q_func()));
if (!componentComplete)
extra->layer->classBegin();
}
return extra->layer;
}
#endif
/*!
\internal
Create a modified copy of the given \a event intended for delivery to this
item, containing pointers to only the QEventPoint instances that are
relevant to this item, and transforming their positions to this item's
coordinate system.
Returns an invalid event with type \l QEvent::None if all points are
stationary; or there are no points inside the item; or none of the points
were pressed inside, neither the item nor any of its handlers is grabbing
any of them, and \a isFiltering is false.
When \a isFiltering is true, it is assumed that the item cares about all
points which are inside its bounds, because most filtering items need to
monitor eventpoint movements until a drag threshold is exceeded or the
requirements for a gesture to be recognized are met in some other way.
*/
void QQuickItemPrivate::localizedTouchEvent(const QTouchEvent *event, bool isFiltering, QMutableTouchEvent *localized)
{
Q_Q(QQuickItem);
QList<QEventPoint> touchPoints;
QEventPoint::States eventStates;
bool anyPressOrReleaseInside = false;
bool anyGrabber = false;
for (auto &p : event->points()) {
if (p.isAccepted())
continue;
// include points where item is the grabber, or if any of its handlers is the grabber while some parent is filtering
auto pointGrabber = event->exclusiveGrabber(p);
bool isGrabber = (pointGrabber == q);
if (!isGrabber && pointGrabber && isFiltering) {
auto handlerGrabber = qmlobject_cast<QQuickPointerHandler *>(pointGrabber);
if (handlerGrabber && handlerGrabber->parentItem() == q)
isGrabber = true;
}
if (isGrabber)
anyGrabber = true;
// include points inside the bounds if no other item is the grabber or if the item is filtering
const auto localPos = q->mapFromScene(p.scenePosition());
bool isInside = q->contains(localPos);
bool hasAnotherGrabber = pointGrabber && pointGrabber != q;
// if there's no exclusive grabber, look for passive grabbers during filtering
if (isFiltering && !pointGrabber) {
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.constFirst());
if (handler)
pointGrabber = handler->parentItem();
}
}
// filtering: (childMouseEventFilter) include points that are grabbed by children of the target item
bool grabberIsChild = false;
auto parent = qobject_cast<QQuickItem*>(pointGrabber);
while (isFiltering && parent) {
if (parent == q) {
grabberIsChild = true;
break;
}
parent = parent->parentItem();
}
bool filterRelevant = isFiltering && grabberIsChild;
if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant))
continue;
if ((p.state() == QEventPoint::State::Pressed || p.state() == QEventPoint::State::Released) && isInside)
anyPressOrReleaseInside = true;
QEventPoint pCopy(p);
eventStates |= p.state();
if (p.state() == QEventPoint::State::Released)
QMutableEventPoint::detach(pCopy);
QMutableEventPoint::setPosition(pCopy, localPos);
touchPoints.append(std::move(pCopy));
}
// Now touchPoints will have only points which are inside the item.
// But if none of them were just pressed inside, and the item has no other reason to care, ignore them anyway.
if (touchPoints.isEmpty() || (!anyPressOrReleaseInside && !anyGrabber && !isFiltering)) {
*localized = QMutableTouchEvent(QEvent::None);
return;
}
// if all points have the same state, set the event type accordingly
QEvent::Type eventType = event->type();
switch (eventStates) {
case QEventPoint::State::Pressed:
eventType = QEvent::TouchBegin;
break;
case QEventPoint::State::Released:
eventType = QEvent::TouchEnd;
break;
default:
eventType = QEvent::TouchUpdate;
break;
}
QMutableTouchEvent ret(eventType, event->pointingDevice(), event->modifiers(), touchPoints);
ret.setTarget(q);
ret.setTimestamp(event->timestamp());
ret.accept();
*localized = ret;
}
bool QQuickItemPrivate::hasPointerHandlers() const
{
return extra.isAllocated() && !extra->pointerHandlers.isEmpty();
}
bool QQuickItemPrivate::hasEnabledHoverHandlers() const
{
if (!hasPointerHandlers())
return false;
for (QQuickPointerHandler *h : extra->pointerHandlers)
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...
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;
}
/*! \internal
Replaces any existing context menu with the given \a menu,
and returns the one that was already set before, or \c nullptr.
*/
QObject *QQuickItemPrivate::setContextMenu(QObject *menu)
{
QObject *ret = (extra.isAllocated() ? extra->contextMenu : nullptr);
extra.value().contextMenu = menu;
return ret;
}
QtPrivate::QQuickAttachedPropertyPropagator *QQuickItemPrivate::attachedPropertyPropagator_parent(
const QMetaObject *attachedType)
{
Q_Q(QQuickItem);
qCDebug(lcAttachedPropertyPropagator).noquote() << "- attachedPropertyPropagator_parent called on"
<< q << "with attachedType" << attachedType->metaType().name();
QQuickItem *parent = q->parentItem();
if (auto *attached = QtPrivate::QQuickAttachedPropertyPropagator::attachedObject(attachedType, parent)) {
qCDebug(lcAttachedPropertyPropagator).noquote() << " - parent item has attached object"
<< attached << "- returning";
return attached;
}
return nullptr;
}
#if QT_CONFIG(quick_shadereffect)
QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
: m_item(item)
, m_enabled(false)
, m_mipmap(false)
, m_smooth(false)
, m_live(true)
, m_componentComplete(true)
, m_wrapMode(QQuickShaderEffectSource::ClampToEdge)
, m_format(QQuickShaderEffectSource::RGBA8)
, m_name("source")
, m_effectComponent(nullptr)
, m_effect(nullptr)
, m_effectSource(nullptr)
, m_textureMirroring(QQuickShaderEffectSource::MirrorVertically)
, m_samples(0)
{
}
QQuickItemLayer::~QQuickItemLayer()
{
delete m_effectSource;
delete m_effect;
}
/*!
\qmlproperty bool QtQuick::Item::layer.enabled
Holds whether the item is layered or not. Layering is disabled by default.
A layered item is rendered into an offscreen surface and cached until
it is changed. Enabling layering for complex QML item hierarchies can
sometimes be an optimization.
None of the other layer properties have any effect when the layer
is disabled.
\sa {Item Layers}
*/
void QQuickItemLayer::setEnabled(bool e)
{
if (e == m_enabled)
return;
m_enabled = e;
if (m_componentComplete) {
if (m_enabled)
activate();
else
deactivate();
}
emit enabledChanged(e);
}
void QQuickItemLayer::classBegin()
{
Q_ASSERT(!m_effectSource);
Q_ASSERT(!m_effect);
m_componentComplete = false;
}
void QQuickItemLayer::componentComplete()
{
Q_ASSERT(!m_componentComplete);
m_componentComplete = true;
if (m_enabled)
activate();
}
void QQuickItemLayer::activate()
{
Q_ASSERT(!m_effectSource);
m_effectSource = new QQuickShaderEffectSource();
QQuickItemPrivate::get(m_effectSource)->setTransparentForPositioner(true);
QQuickItem *parentItem = m_item->parentItem();
if (parentItem) {
m_effectSource->setParentItem(parentItem);
m_effectSource->stackAfter(m_item);
}
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);
m_effectSource->setWrapMode(m_wrapMode);
m_effectSource->setFormat(m_format);
m_effectSource->setTextureMirroring(m_textureMirroring);
m_effectSource->setSamples(m_samples);
if (m_effectComponent)
activateEffect();
m_effectSource->setVisible(m_item->isVisible() && !m_effect);
updateZ();
updateGeometry();
updateOpacity();
updateMatrix();
QQuickItemPrivate *id = QQuickItemPrivate::get(m_item);
id->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Opacity | QQuickItemPrivate::Parent | QQuickItemPrivate::Visibility | QQuickItemPrivate::SiblingOrder);
}
void QQuickItemLayer::deactivate()
{
Q_ASSERT(m_effectSource);
if (m_effectComponent)
deactivateEffect();
delete m_effectSource;
m_effectSource = nullptr;
QQuickItemPrivate *id = QQuickItemPrivate::get(m_item);
id->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Opacity | QQuickItemPrivate::Parent | QQuickItemPrivate::Visibility | QQuickItemPrivate::SiblingOrder);
}
void QQuickItemLayer::activateEffect()
{
Q_ASSERT(m_effectSource);
Q_ASSERT(m_effectComponent);
Q_ASSERT(!m_effect);
QObject *created = m_effectComponent->beginCreate(m_effectComponent->creationContext());
m_effect = qobject_cast<QQuickItem *>(created);
if (!m_effect) {
qWarning("Item: layer.effect is not a QML Item.");
m_effectComponent->completeCreate();
delete created;
return;
}
QQuickItem *parentItem = m_item->parentItem();
if (parentItem) {
m_effect->setParentItem(parentItem);
m_effect->stackAfter(m_effectSource);
}
m_effect->setVisible(m_item->isVisible());
m_effect->setProperty(m_name, QVariant::fromValue<QObject *>(m_effectSource));
QQuickItemPrivate::get(m_effect)->setTransparentForPositioner(true);
m_effectComponent->completeCreate();
}
void QQuickItemLayer::deactivateEffect()
{
Q_ASSERT(m_effectSource);
Q_ASSERT(m_effectComponent);
delete m_effect;
m_effect = nullptr;
}
/*!
\qmlproperty Component QtQuick::Item::layer.effect
Holds the effect that is applied to this layer.
The effect is typically a \l ShaderEffect component, although any \l Item component can be
assigned. The effect should have a source texture property with a name matching \l layer.samplerName.
\sa layer.samplerName, {Item Layers}
*/
void QQuickItemLayer::setEffect(QQmlComponent *component)
{
if (component == m_effectComponent)
return;
bool updateNeeded = false;
if (m_effectSource && m_effectComponent) {
deactivateEffect();
updateNeeded = true;
}
m_effectComponent = component;
if (m_effectSource && m_effectComponent) {
activateEffect();
updateNeeded = true;
}
if (updateNeeded) {
updateZ();
updateGeometry();
updateOpacity();
updateMatrix();
m_effectSource->setVisible(m_item->isVisible() && !m_effect);
}
emit effectChanged(component);
}
/*!
\qmlproperty bool QtQuick::Item::layer.mipmap
If this property is true, mipmaps are generated for the texture.
\note Some OpenGL ES 2 implementations do not support mipmapping of
non-power-of-two textures.
\sa {Item Layers}
*/
void QQuickItemLayer::setMipmap(bool mipmap)
{
if (mipmap == m_mipmap)
return;
m_mipmap = mipmap;
if (m_effectSource)
m_effectSource->setMipmap(m_mipmap);
emit mipmapChanged(mipmap);
}
/*!
\qmlproperty enumeration QtQuick::Item::layer.format
This property defines the format of the backing texture.
Modifying this property makes most sense when the \a layer.effect is also
specified.
\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}
*/
void QQuickItemLayer::setFormat(QQuickShaderEffectSource::Format f)
{
if (f == m_format)
return;
m_format = f;
if (m_effectSource)
m_effectSource->setFormat(m_format);
emit formatChanged(m_format);
}
/*!
\qmlproperty rect QtQuick::Item::layer.sourceRect
This property defines the rectangular area of the item that should be
rendered into the texture. The source rectangle can be larger than
the item itself. If the rectangle is null, which is the default,
then the whole item is rendered to the texture.
\sa {Item Layers}
*/
void QQuickItemLayer::setSourceRect(const QRectF &sourceRect)
{
if (sourceRect == m_sourceRect)
return;
m_sourceRect = sourceRect;
if (m_effectSource)
m_effectSource->setSourceRect(m_sourceRect);
emit sourceRectChanged(sourceRect);
}
/*!
\qmlproperty bool QtQuick::Item::layer.smooth
Holds whether the layer is smoothly transformed. When enabled, sampling the
layer's texture is performed using \c linear interpolation, while
non-smooth results in using the \c nearest filtering mode.
By default, this property is set to \c false.
\sa {Item Layers}
*/
void QQuickItemLayer::setSmooth(bool s)
{
if (m_smooth == s)
return;
m_smooth = s;
if (m_effectSource)
m_effectSource->setSmooth(m_smooth);
emit smoothChanged(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,
which is the default, the size of the item is used.
\note Some platforms have a limit on how small framebuffer objects can be,
which means the actual texture size might be larger than the requested
size.
\sa {Item Layers}
*/
void QQuickItemLayer::setSize(const QSize &size)
{
if (size == m_size)
return;
m_size = size;
if (m_effectSource)
m_effectSource->setTextureSize(size);
emit sizeChanged(size);
}
/*!
\qmlproperty enumeration QtQuick::Item::layer.wrapMode
This property defines the wrap modes associated with the texture.
Modifying this property makes most sense when the \a layer.effect is
specified.
\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.
\sa {Item Layers}
*/
void QQuickItemLayer::setWrapMode(QQuickShaderEffectSource::WrapMode mode)
{
if (mode == m_wrapMode)
return;
m_wrapMode = mode;
if (m_effectSource)
m_effectSource->setWrapMode(m_wrapMode);
emit wrapModeChanged(mode);
}
/*!
\qmlproperty enumeration QtQuick::Item::layer.textureMirroring
\since 5.6
This property defines how the generated texture should be mirrored.
The default value is \c{ShaderEffectSource.MirrorVertically}.
Custom mirroring can be useful if the generated texture is directly accessed by custom shaders,
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.
\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)
{
if (mirroring == m_textureMirroring)
return;
m_textureMirroring = mirroring;
if (m_effectSource)
m_effectSource->setTextureMirroring(m_textureMirroring);
emit textureMirroringChanged(mirroring);
}
/*!
\qmlproperty enumeration QtQuick::Item::layer.samples
\since 5.10
This property allows requesting multisampled rendering in the layer.
By default multisampling is enabled whenever multisampling is
enabled for the entire window, assuming the scenegraph renderer in
use and the underlying graphics API supports this.
By setting the value to 2, 4, etc. multisampled rendering can be requested
for a part of the scene without enabling multisampling for the entire
scene. This way multisampling is applied only to a given subtree, which can
lead to significant performance gains since multisampling is not applied to
other parts of the scene.
\note Enabling multisampling can be potentially expensive regardless of the
layer's size, as it incurs a hardware and driver dependent performance and
memory cost.
\note This property is only functional when support for multisample
renderbuffers and framebuffer blits is available. Otherwise the value is
silently ignored.
*/
void QQuickItemLayer::setSamples(int count)
{
if (m_samples == count)
return;
m_samples = count;
if (m_effectSource)
m_effectSource->setSamples(m_samples);
emit samplesChanged(count);
}
/*!
\qmlproperty string QtQuick::Item::layer.samplerName
Holds the name of the effect's source texture property.
This value must match the name of the effect's source texture property
so that the Item can pass the layer's offscreen surface to the effect correctly.
\sa layer.effect, ShaderEffect, {Item Layers}
*/
void QQuickItemLayer::setName(const QByteArray &name) {
if (m_name == name)
return;
if (m_effect) {
m_effect->setProperty(m_name, QVariant());
m_effect->setProperty(name, QVariant::fromValue<QObject *>(m_effectSource));
}
m_name = name;
emit nameChanged(name);
}
void QQuickItemLayer::itemOpacityChanged(QQuickItem *item)
{
Q_UNUSED(item);
updateOpacity();
}
void QQuickItemLayer::itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &)
{
updateGeometry();
}
void QQuickItemLayer::itemParentChanged(QQuickItem *item, QQuickItem *parent)
{
Q_UNUSED(item);
Q_ASSERT(item == m_item);
Q_ASSERT(parent != m_effectSource);
Q_ASSERT(parent == nullptr || parent != m_effect);
m_effectSource->setParentItem(parent);
if (parent)
m_effectSource->stackAfter(m_item);
if (m_effect) {
m_effect->setParentItem(parent);
if (parent)
m_effect->stackAfter(m_effectSource);
}
}
void QQuickItemLayer::itemSiblingOrderChanged(QQuickItem *)
{
m_effectSource->stackAfter(m_item);
if (m_effect)
m_effect->stackAfter(m_effectSource);
}
void QQuickItemLayer::itemVisibilityChanged(QQuickItem *)
{
QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource;
if (!l)
return;
l->setVisible(m_item->isVisible());
}
void QQuickItemLayer::updateZ()
{
if (!m_componentComplete || !m_enabled)
return;
QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource;
if (!l)
return;
l->setZ(m_item->z());
}
void QQuickItemLayer::updateOpacity()
{
QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource;
if (!l)
return;
l->setOpacity(m_item->opacity());
}
void QQuickItemLayer::updateGeometry()
{
QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource;
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());
}
void QQuickItemLayer::updateMatrix()
{
// Called directly from transformChanged(), so needs some extra
// checks.
if (!m_componentComplete || !m_enabled)
return;
QQuickItem *l = m_effect ? (QQuickItem *) m_effect : (QQuickItem *) m_effectSource;
if (!l)
return;
QQuickItemPrivate *ld = QQuickItemPrivate::get(l);
l->setScale(m_item->scale());
l->setRotation(m_item->rotation());
ld->transforms = QQuickItemPrivate::get(m_item)->transforms;
if (ld->origin() != QQuickItemPrivate::get(m_item)->origin())
ld->extra.value().origin = QQuickItemPrivate::get(m_item)->origin();
ld->dirty(QQuickItemPrivate::Transform);
}
#endif // quick_shadereffect
QQuickItemPrivate::ExtraData::ExtraData()
: z(0), scale(1), rotation(0), opacity(1), biggestPointerHandlerMarginCache(-1),
contents(nullptr), screenAttached(nullptr), layoutDirectionAttached(nullptr),
enterKeyAttached(nullptr),
keyHandler(nullptr), contextMenu(nullptr),
#if QT_CONFIG(quick_shadereffect)
layer(nullptr),
#endif
effectRefCount(0), hideRefCount(0),
recursiveEffectRefCount(0),
opacityNode(nullptr), clipNode(nullptr), rootNode(nullptr),
origin(QQuickItem::Center),
transparentForPositioner(false),
mutabilityGroup(uint(QQuickItem::AutoMutabilityGroup)),
mutabilityGroupSet(false)
{
#ifdef QT_BUILD_INTERNAL
++QQuickItemPrivate::itemExtra_counter;
#endif
}
#if QT_CONFIG(accessibility)
QAccessible::Role QQuickItemPrivate::effectiveAccessibleRole() const
{
auto role = QAccessible::NoRole;
if (!inDestructor) { // we might get called back while emitting QAccessible::ObjectDestroy
Q_Q(const QQuickItem);
auto *attached = qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, false);
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
// helper code to let a visual parent mark its visual children for the garbage collector
namespace QV4 {
namespace Heap {
struct QQuickItemWrapper : public QObjectWrapper {
static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack);
};
}
}
struct QQuickItemWrapper : public QV4::QObjectWrapper {
V4_OBJECT2(QQuickItemWrapper, QV4::QObjectWrapper)
};
DEFINE_OBJECT_VTABLE(QQuickItemWrapper);
void QV4::Heap::QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack)
{
QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
if (QQuickItem *item = static_cast<QQuickItem*>(This->object())) {
for (QQuickItem *child : std::as_const(QQuickItemPrivate::get(item)->childItems))
QV4::QObjectWrapper::markWrapper(child, markStack);
}
QObjectWrapper::markObjects(that, markStack);
}
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, QPointF(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;
//! \internal
bool QQuickItemChangeListener::baseDeleted(const QObject *caller) const
{
if (auto ptr = dynamic_cast<const QObjectPrivate *>(this))
return ptr->q_ptr != caller && QQmlData::wasDeleted(ptr);
return false;
}
//! \internal
QString QQuickItemChangeListener::debugName() const
{
return QStringLiteral("QQuickItemChangeListener(0x%1)").arg(quintptr(this), 0, 16);
}
QT_END_NAMESPACE
#include <moc_qquickitem.cpp>
#include "moc_qquickitem_p.cpp"