blob: a573ab1b8ff538c771c1bbaed2bc2b0a2b60514e [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Quick 3D.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qquick3dobject_p.h"
#include "qquick3dobject_p_p.h"
#include "qquick3dscenemanager_p.h"
#include <QtQuick3DRuntimeRender/private/qssgrendergraphobject_p.h>
#include <QtQml/private/qqmlglobal_p.h>
#include <QtQuick/private/qquickstategroup_p.h>
#include <QtQuick/private/qquickstate_p.h>
#include <private/qv4object_p.h>
#include <private/qv4qobjectwrapper_p.h>
QT_BEGIN_NAMESPACE
/*!
\qmltype Object3D
\inqmlmodule QtQuick3D
\brief Abstact Subclass of all 3D nodes and resources.
*/
QQuick3DObject::QQuick3DObject(QQuick3DObject *parent)
: QObject(*(new QQuick3DObjectPrivate), parent)
{
Q_D(QQuick3DObject);
d->init(parent);
}
QQuick3DObject::~QQuick3DObject()
{
Q_D(QQuick3DObject);
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->sceneManager)
d->derefSceneManager();
// XXX todo - optimize
while (!d->childItems.isEmpty())
d->childItems.constFirst()->setParentItem(nullptr);
delete d->_stateGroup;
d->_stateGroup = nullptr;
}
void QQuick3DObject::update()
{
Q_D(QQuick3DObject);
d->dirty(QQuick3DObjectPrivate::Content);
}
void QQuick3DObject::setParentItem(QQuick3DObject *parentItem)
{
Q_D(QQuick3DObject);
if (parentItem == d->parentItem)
return;
if (parentItem) {
QQuick3DObject *itemAncestor = parentItem;
while (itemAncestor != nullptr) {
if (Q_UNLIKELY(itemAncestor == this)) {
qWarning() << "QSSGObject::setParentItem: Parent" << parentItem << "is already part of the subtree of" << this;
return;
}
itemAncestor = itemAncestor->parentItem();
}
}
d->removeFromDirtyList();
QQuick3DObject *oldParentItem = d->parentItem;
if (oldParentItem) {
QQuick3DObjectPrivate *op = QQuick3DObjectPrivate::get(oldParentItem);
op->removeChild(this);
} else if (d->sceneManager) {
d->sceneManager->parentlessItems.remove(this);
}
QQuick3DSceneManager *parentSceneManager = parentItem ? QQuick3DObjectPrivate::get(parentItem)->sceneManager : nullptr;
if (d->sceneManager == parentSceneManager) {
// Avoid freeing and reallocating resources if the window stays the same.
d->parentItem = parentItem;
} else {
if (d->sceneManager)
d->derefSceneManager();
d->parentItem = parentItem;
if (parentSceneManager)
d->refSceneManager(parentSceneManager);
}
d->dirty(QQuick3DObjectPrivate::ParentChanged);
if (d->parentItem)
QQuick3DObjectPrivate::get(d->parentItem)->addChild(this);
else if (d->sceneManager)
d->sceneManager->parentlessItems.insert(this);
d->itemChange(ItemParentHasChanged, d->parentItem);
emit parentChanged();
}
QString QQuick3DObject::state() const
{
Q_D(const QQuick3DObject);
return d->state();
}
void QQuick3DObject::setState(const QString &state)
{
Q_D(QQuick3DObject);
d->setState(state);
}
QList<QQuick3DObject *> QQuick3DObject::childItems() const
{
Q_D(const QQuick3DObject);
return d->childItems;
}
QQuick3DSceneManager *QQuick3DObject::sceneManager() const
{
Q_D(const QQuick3DObject);
return d->sceneManager;
}
QQuick3DObject *QQuick3DObject::parentItem() const
{
Q_D(const QQuick3DObject);
return d->parentItem;
}
void QQuick3DObject::markAllDirty()
{
}
void QQuick3DObject::itemChange(QQuick3DObject::ItemChange change, const QQuick3DObject::ItemChangeData &)
{
if (change == ItemSceneChange)
emit sceneManagerChanged();
}
QQuick3DObject::QQuick3DObject(QQuick3DObjectPrivate &dd, QQuick3DObject *parent)
: QObject(dd, parent)
{
Q_D(QQuick3DObject);
d->init(parent);
}
void QQuick3DObject::classBegin()
{
Q_D(QQuick3DObject);
d->componentComplete = false;
if (d->_stateGroup)
d->_stateGroup->classBegin();
}
void QQuick3DObject::componentComplete()
{
Q_D(QQuick3DObject);
d->componentComplete = true;
if (d->_stateGroup)
d->_stateGroup->componentComplete();
if (d->sceneManager && d->dirtyAttributes) {
d->addToDirtyList();
d->sceneManager->dirtyItem(this);
}
}
bool QQuick3DObject::isComponentComplete() const
{
Q_D(const QQuick3DObject);
return d->componentComplete;
}
QQuick3DObjectPrivate::QQuick3DObjectPrivate()
: _stateGroup(nullptr)
, dirtyAttributes(0)
, nextDirtyItem(nullptr)
, prevDirtyItem(nullptr)
, sceneManager(nullptr)
, windowRefCount(0)
, parentItem(nullptr)
, sortedChildItems(&childItems)
, subFocusItem(nullptr)
{
}
QQuick3DObjectPrivate::~QQuick3DObjectPrivate()
{
if (sortedChildItems != &childItems)
delete sortedChildItems;
}
void QQuick3DObjectPrivate::init(QQuick3DObject *parent)
{
Q_Q(QQuick3DObject);
if (parent)
q->setParentItem(parent);
}
QQmlListProperty<QObject> QQuick3DObjectPrivate::data()
{
return QQmlListProperty<QObject>(q_func(),
nullptr,
QQuick3DObjectPrivate::data_append,
QQuick3DObjectPrivate::data_count,
QQuick3DObjectPrivate::data_at,
QQuick3DObjectPrivate::data_clear);
}
QQmlListProperty<QObject> QQuick3DObjectPrivate::resources()
{
return QQmlListProperty<QObject>(q_func(),
nullptr,
QQuick3DObjectPrivate::resources_append,
QQuick3DObjectPrivate::resources_count,
QQuick3DObjectPrivate::resources_at,
QQuick3DObjectPrivate::resources_clear);
}
QQmlListProperty<QQuick3DObject> QQuick3DObjectPrivate::children()
{
return QQmlListProperty<QQuick3DObject>(q_func(),
nullptr,
QQuick3DObjectPrivate::children_append,
QQuick3DObjectPrivate::children_count,
QQuick3DObjectPrivate::children_at,
QQuick3DObjectPrivate::children_clear);
}
QQmlListProperty<QQuickState> QQuick3DObjectPrivate::states()
{
return _states()->statesProperty();
}
QQmlListProperty<QQuickTransition> QQuick3DObjectPrivate::transitions()
{
return _states()->transitionsProperty();
}
QString QQuick3DObjectPrivate::state() const
{
if (!_stateGroup)
return QString();
else
return _stateGroup->state();
}
void QQuick3DObjectPrivate::setState(const QString &state)
{
_states()->setState(state);
}
void QQuick3DObjectPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
{
if (!o)
return;
QQuick3DObject *that = static_cast<QQuick3DObject *>(prop->object);
if (QQuick3DObject *item = qmlobject_cast<QQuick3DObject *>(o)) {
item->setParentItem(that);
} else {
// QSSGSceneRenderer *thisSceneRenderer = qmlobject_cast<QSSGSceneRenderer *>(o);
// item = that;
// QSSGSceneRenderer *itemSceneRenderer = that->sceneRenderer();
// while (!itemSceneRenderer && item && item->parentItem()) {
// item = item->parentItem();
// itemSceneRenderer = item->sceneRenderer();
// }
// if (thisSceneRenderer) {
// if (itemSceneRenderer) {
// // qCDebug(lcTransient) << thisWindow << "is transient for" << itemWindow;
// thisSceneRenderer->setTransientParent(itemSceneRenderer);
// } else {
// QObject::connect(item, SIGNAL(sceneRendererChanged(QSSGSceneRenderer *)), thisSceneRenderer, SLOT(setTransientParent_helper(QSSGSceneRenderer *)));
// }
// }
o->setParent(that);
}
resources_append(prop, o);
}
int QQuick3DObjectPrivate::data_count(QQmlListProperty<QObject> *property)
{
QQuick3DObject *item = static_cast<QQuick3DObject *>(property->object);
QQuick3DObjectPrivate *privateItem = QQuick3DObjectPrivate::get(item);
QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
QQmlListProperty<QQuick3DObject> childrenProperty = privateItem->children();
return resources_count(&resourcesProperty) + children_count(&childrenProperty);
}
QObject *QQuick3DObjectPrivate::data_at(QQmlListProperty<QObject> *property, int i)
{
QQuick3DObject *item = static_cast<QQuick3DObject *>(property->object);
QQuick3DObjectPrivate *privateItem = QQuick3DObjectPrivate::get(item);
QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
QQmlListProperty<QQuick3DObject> childrenProperty = privateItem->children();
int resourcesCount = resources_count(&resourcesProperty);
if (i < resourcesCount)
return resources_at(&resourcesProperty, i);
const int j = i - resourcesCount;
if (j < children_count(&childrenProperty))
return children_at(&childrenProperty, j);
return nullptr;
}
void QQuick3DObjectPrivate::data_clear(QQmlListProperty<QObject> *property)
{
QQuick3DObject *item = static_cast<QQuick3DObject *>(property->object);
QQuick3DObjectPrivate *privateItem = QQuick3DObjectPrivate::get(item);
QQmlListProperty<QObject> resourcesProperty = privateItem->resources();
QQmlListProperty<QQuick3DObject> childrenProperty = privateItem->children();
resources_clear(&resourcesProperty);
children_clear(&childrenProperty);
}
QObject *QQuick3DObjectPrivate::resources_at(QQmlListProperty<QObject> *prop, int index)
{
QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(static_cast<QQuick3DObject *>(prop->object));
return quickItemPrivate->extra.isAllocated() ? quickItemPrivate->extra->resourcesList.value(index) : 0;
}
void QQuick3DObjectPrivate::resources_append(QQmlListProperty<QObject> *prop, QObject *object)
{
QQuick3DObject *quickItem = static_cast<QQuick3DObject *>(prop->object);
QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(quickItem);
if (!quickItemPrivate->extra.value().resourcesList.contains(object)) {
quickItemPrivate->extra.value().resourcesList.append(object);
// clang-format off
qmlobject_connect(object, QObject, SIGNAL(destroyed(QObject*)),
quickItem, QQuick3DObject, SLOT(_q_resourceObjectDeleted(QObject*)));
// clang-format on
}
}
int QQuick3DObjectPrivate::resources_count(QQmlListProperty<QObject> *prop)
{
QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(static_cast<QQuick3DObject *>(prop->object));
return quickItemPrivate->extra.isAllocated() ? quickItemPrivate->extra->resourcesList.count() : 0;
}
void QQuick3DObjectPrivate::resources_clear(QQmlListProperty<QObject> *prop)
{
QQuick3DObject *quickItem = static_cast<QQuick3DObject *>(prop->object);
QQuick3DObjectPrivate *quickItemPrivate = QQuick3DObjectPrivate::get(quickItem);
if (quickItemPrivate->extra.isAllocated()) { // If extra is not allocated resources is empty.
for (QObject *object : qAsConst(quickItemPrivate->extra->resourcesList)) {
// clang-format off
qmlobject_disconnect(object, QObject, SIGNAL(destroyed(QObject*)),
quickItem, QQuick3DObject, SLOT(_q_resourceObjectDeleted(QObject*)));
// clang-format on
}
quickItemPrivate->extra->resourcesList.clear();
}
}
void QQuick3DObjectPrivate::children_append(QQmlListProperty<QQuick3DObject> *prop, QQuick3DObject *o)
{
if (!o)
return;
QQuick3DObject *that = static_cast<QQuick3DObject *>(prop->object);
if (o->parentItem() == that)
o->setParentItem(nullptr);
o->setParentItem(that);
}
int QQuick3DObjectPrivate::children_count(QQmlListProperty<QQuick3DObject> *prop)
{
QQuick3DObjectPrivate *p = QQuick3DObjectPrivate::get(static_cast<QQuick3DObject *>(prop->object));
return p->childItems.count();
}
QQuick3DObject *QQuick3DObjectPrivate::children_at(QQmlListProperty<QQuick3DObject> *prop, int index)
{
QQuick3DObjectPrivate *p = QQuick3DObjectPrivate::get(static_cast<QQuick3DObject *>(prop->object));
if (index >= p->childItems.count() || index < 0)
return nullptr;
else
return p->childItems.at(index);
}
void QQuick3DObjectPrivate::children_clear(QQmlListProperty<QQuick3DObject> *prop)
{
QQuick3DObject *that = static_cast<QQuick3DObject *>(prop->object);
QQuick3DObjectPrivate *p = QQuick3DObjectPrivate::get(that);
while (!p->childItems.isEmpty())
p->childItems.at(0)->setParentItem(nullptr);
}
void QQuick3DObjectPrivate::_q_resourceObjectDeleted(QObject *object)
{
if (extra.isAllocated() && extra->resourcesList.contains(object))
extra->resourcesList.removeAll(object);
}
void QQuick3DObjectPrivate::addItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types)
{
changeListeners.append(ChangeListener(listener, types));
}
void QQuick3DObjectPrivate::updateOrAddItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types)
{
const ChangeListener changeListener(listener, types);
const int index = changeListeners.indexOf(changeListener);
if (index > -1)
changeListeners[index].types = changeListener.types;
else
changeListeners.append(changeListener);
}
void QQuick3DObjectPrivate::removeItemChangeListener(QQuick3DObjectChangeListener *listener, ChangeTypes types)
{
ChangeListener change(listener, types);
changeListeners.removeOne(change);
}
QQuickStateGroup *QQuick3DObjectPrivate::_states()
{
Q_Q(QQuick3DObject);
if (!_stateGroup) {
_stateGroup = new QQuickStateGroup;
if (!componentComplete)
_stateGroup->classBegin();
// clang-format off
qmlobject_connect(_stateGroup, QQuickStateGroup, SIGNAL(stateChanged(QString)),
q, QQuick3DObject, SIGNAL(stateChanged()));
// clang-format on
}
return _stateGroup;
}
QString QQuick3DObjectPrivate::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 QQuick3DObjectPrivate::dirty(QQuick3DObjectPrivate::DirtyType type)
{
Q_Q(QQuick3DObject);
if (!(dirtyAttributes & type) || (sceneManager && !prevDirtyItem)) {
dirtyAttributes |= type;
if (sceneManager && componentComplete) {
addToDirtyList();
sceneManager->dirtyItem(q);
}
}
}
void QQuick3DObjectPrivate::addToDirtyList()
{
Q_Q(QQuick3DObject);
Q_ASSERT(sceneManager);
if (!prevDirtyItem) {
Q_ASSERT(!nextDirtyItem);
if (isResourceNode()) {
if (q->type() == QQuick3DObject::Image) {
// Will likely need to refactor this, but images need to come before other
// resources
nextDirtyItem = sceneManager->dirtyImageList;
if (nextDirtyItem)
QQuick3DObjectPrivate::get(nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
prevDirtyItem = &sceneManager->dirtyImageList;
sceneManager->dirtyImageList = q;
} else {
nextDirtyItem = sceneManager->dirtyResourceList;
if (nextDirtyItem)
QQuick3DObjectPrivate::get(nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
prevDirtyItem = &sceneManager->dirtyResourceList;
sceneManager->dirtyResourceList = q;
}
} else {
if (q->type() == QQuick3DObject::Light) {
// needed for scoped lights second pass
sceneManager->dirtyLightList.append(q);
}
nextDirtyItem = sceneManager->dirtySpatialNodeList;
if (nextDirtyItem)
QQuick3DObjectPrivate::get(nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
prevDirtyItem = &sceneManager->dirtySpatialNodeList;
sceneManager->dirtySpatialNodeList = q;
}
sceneManager->dirtyItem(q);
}
Q_ASSERT(prevDirtyItem);
}
void QQuick3DObjectPrivate::removeFromDirtyList()
{
if (prevDirtyItem) {
if (nextDirtyItem)
QQuick3DObjectPrivate::get(nextDirtyItem)->prevDirtyItem = prevDirtyItem;
*prevDirtyItem = nextDirtyItem;
prevDirtyItem = nullptr;
nextDirtyItem = nullptr;
}
Q_ASSERT(!prevDirtyItem);
Q_ASSERT(!nextDirtyItem);
}
bool QQuick3DObjectPrivate::isResourceNode() const
{
Q_Q(const QQuick3DObject);
switch (q->type()) {
case QQuick3DObject::Layer:
case QQuick3DObject::Node:
case QQuick3DObject::Light:
case QQuick3DObject::Camera:
case QQuick3DObject::Model:
case QQuick3DObject::Text:
return false;
case QQuick3DObject::SceneEnvironment:
case QQuick3DObject::DefaultMaterial:
case QQuick3DObject::PrincipledMaterial:
case QQuick3DObject::Image:
case QQuick3DObject::CustomMaterial:
case QQuick3DObject::Lightmaps:
case QQuick3DObject::Geometry:
return true;
default:
return false;
}
}
bool QQuick3DObjectPrivate::isSpatialNode() const
{
Q_Q(const QQuick3DObject);
switch (q->type()) {
case QQuick3DObject::Layer:
case QQuick3DObject::Node:
case QQuick3DObject::Light:
case QQuick3DObject::Camera:
case QQuick3DObject::Model:
case QQuick3DObject::Text:
return true;
case QQuick3DObject::SceneEnvironment:
case QQuick3DObject::DefaultMaterial:
case QQuick3DObject::PrincipledMaterial:
case QQuick3DObject::Image:
case QQuick3DObject::CustomMaterial:
case QQuick3DObject::Lightmaps:
case QQuick3DObject::Geometry:
default:
return false;
}
}
void QQuick3DObjectPrivate::setCulled(bool cull)
{
if (cull == culled)
return;
culled = cull;
if ((cull && ++extra.value().hideRefCount == 1) || (!cull && --extra.value().hideRefCount == 0))
dirty(HideReference);
}
QList<QQuick3DObject *> QQuick3DObjectPrivate::paintOrderChildItems() const
{
if (sortedChildItems)
return *sortedChildItems;
sortedChildItems = const_cast<QList<QQuick3DObject *> *>(&childItems);
return childItems;
}
void QQuick3DObjectPrivate::addChild(QQuick3DObject *child)
{
Q_Q(QQuick3DObject);
Q_ASSERT(!childItems.contains(child));
childItems.append(child);
markSortedChildrenDirty(child);
dirty(QQuick3DObjectPrivate::ChildrenChanged);
itemChange(QQuick3DObject::ItemChildAddedChange, child);
emit q->childrenChanged();
}
void QQuick3DObjectPrivate::removeChild(QQuick3DObject *child)
{
Q_Q(QQuick3DObject);
Q_ASSERT(child);
Q_ASSERT(childItems.contains(child));
childItems.removeOne(child);
Q_ASSERT(!childItems.contains(child));
markSortedChildrenDirty(child);
dirty(QQuick3DObjectPrivate::ChildrenChanged);
itemChange(QQuick3DObject::ItemChildRemovedChange, child);
emit q->childrenChanged();
}
void QQuick3DObjectPrivate::siblingOrderChanged()
{
Q_Q(QQuick3DObject);
if (!changeListeners.isEmpty()) {
const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
if (change.types & QQuick3DObjectPrivate::SiblingOrder) {
change.listener->itemSiblingOrderChanged(q);
}
}
}
}
void QQuick3DObjectPrivate::markSortedChildrenDirty(QQuick3DObject *child)
{
Q_UNUSED(child);
}
void QQuick3DObjectPrivate::refSceneManager(QQuick3DSceneManager *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(QQuick3DObject);
Q_ASSERT((sceneManager != nullptr) == (windowRefCount > 0));
Q_ASSERT(c);
if (++windowRefCount > 1) {
if (c != sceneManager)
qWarning("QSSGObject: Cannot use same item on different windows at the same time.");
return; // Window already set.
}
Q_ASSERT(sceneManager == nullptr);
sceneManager = c;
// if (polishScheduled)
// QSSGWindowPrivate::get(window)->itemsToPolish.append(q);
if (!parentItem)
sceneManager->parentlessItems.insert(q);
for (int ii = 0; ii < childItems.count(); ++ii) {
QQuick3DObject *child = childItems.at(ii);
QQuick3DObjectPrivate::get(child)->refSceneManager(c);
}
dirty(Window);
itemChange(QQuick3DObject::ItemSceneChange, c);
}
void QQuick3DObjectPrivate::derefSceneManager()
{
Q_Q(QQuick3DObject);
if (!sceneManager)
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.
removeFromDirtyList();
QQuick3DSceneManager *c = sceneManager;
if (spatialNode)
c->cleanup(spatialNode);
if (!parentItem)
c->parentlessItems.remove(q);
sceneManager = nullptr;
spatialNode = nullptr;
for (int ii = 0; ii < childItems.count(); ++ii) {
QQuick3DObject *child = childItems.at(ii);
QQuick3DObjectPrivate::get(child)->derefSceneManager();
}
dirty(Window);
itemChange(QQuick3DObject::ItemSceneChange, (QQuick3DSceneManager *)nullptr);
}
void QQuick3DObjectPrivate::updateSubFocusItem(QQuick3DObject *scope, bool focus)
{
Q_Q(QQuick3DObject);
Q_ASSERT(scope);
QQuick3DObjectPrivate *scopePrivate = QQuick3DObjectPrivate::get(scope);
QQuick3DObject *oldSubFocusItem = scopePrivate->subFocusItem;
// Correct focus chain in scope
if (oldSubFocusItem) {
QQuick3DObject *sfi = scopePrivate->subFocusItem->parentItem();
while (sfi && sfi != scope) {
QQuick3DObjectPrivate::get(sfi)->subFocusItem = nullptr;
sfi = sfi->parentItem();
}
}
if (focus) {
scopePrivate->subFocusItem = q;
QQuick3DObject *sfi = scopePrivate->subFocusItem->parentItem();
while (sfi && sfi != scope) {
QQuick3DObjectPrivate::get(sfi)->subFocusItem = q;
sfi = sfi->parentItem();
}
} else {
scopePrivate->subFocusItem = nullptr;
}
}
void QQuick3DObjectPrivate::itemChange(QQuick3DObject::ItemChange change, const QQuick3DObject::ItemChangeData &data)
{
Q_Q(QQuick3DObject);
switch (change) {
case QQuick3DObject::ItemRotationHasChanged:
// TODO:
qWarning("ItemRoationHasChange is unhandled!!!!");
break;
case QQuick3DObject::ItemChildAddedChange: {
q->itemChange(change, data);
if (!changeListeners.isEmpty()) {
const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
if (change.types & QQuick3DObjectPrivate::Children) {
change.listener->itemChildAdded(q, data.item);
}
}
}
break;
}
case QQuick3DObject::ItemChildRemovedChange: {
q->itemChange(change, data);
if (!changeListeners.isEmpty()) {
const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
if (change.types & QQuick3DObjectPrivate::Children) {
change.listener->itemChildRemoved(q, data.item);
}
}
}
break;
}
case QQuick3DObject::ItemSceneChange:
q->itemChange(change, data);
break;
case QQuick3DObject::ItemVisibleHasChanged: {
q->itemChange(change, data);
if (!changeListeners.isEmpty()) {
const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
if (change.types & QQuick3DObjectPrivate::Visibility) {
change.listener->itemVisibilityChanged(q);
}
}
}
break;
}
case QQuick3DObject::ItemEnabledHasChanged: {
q->itemChange(change, data);
if (!changeListeners.isEmpty()) {
const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
if (change.types & QQuick3DObjectPrivate::Enabled) {
change.listener->itemEnabledChanged(q);
}
}
}
break;
}
case QQuick3DObject::ItemParentHasChanged: {
q->itemChange(change, data);
if (!changeListeners.isEmpty()) {
const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
if (change.types & QQuick3DObjectPrivate::Parent) {
change.listener->itemParentChanged(q, data.item);
}
}
}
break;
}
case QQuick3DObject::ItemOpacityHasChanged: {
q->itemChange(change, data);
if (!changeListeners.isEmpty()) {
const auto listeners = changeListeners; // NOTE: intentional copy (QTBUG-54732)
for (const QQuick3DObjectPrivate::ChangeListener &change : listeners) {
if (change.types & QQuick3DObjectPrivate::Opacity) {
change.listener->itemOpacityChanged(q);
}
}
}
break;
}
case QQuick3DObject::ItemActiveFocusHasChanged:
q->itemChange(change, data);
break;
case QQuick3DObject::ItemAntialiasingHasChanged:
// fall through
case QQuick3DObject::ItemDevicePixelRatioHasChanged:
q->itemChange(change, data);
break;
}
}
namespace QV4 {
namespace Heap {
struct QSSGItemWrapper : public QObjectWrapper
{
static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack);
};
}
}
struct QSSGItemWrapper : public QV4::QObjectWrapper
{
V4_OBJECT2(QSSGItemWrapper, QV4::QObjectWrapper)
};
DEFINE_OBJECT_VTABLE(QSSGItemWrapper);
void QV4::Heap::QSSGItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack)
{
QObjectWrapper *This = static_cast<QObjectWrapper *>(that);
if (QQuick3DObject *item = static_cast<QQuick3DObject *>(This->object())) {
for (QQuick3DObject *child : qAsConst(QQuick3DObjectPrivate::get(item)->childItems))
QV4::QObjectWrapper::markWrapper(child, markStack);
}
QObjectWrapper::markObjects(that, markStack);
}
quint64 QQuick3DObjectPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
{
return (engine->memoryManager->allocate<QSSGItemWrapper>(q_func()))->asReturnedValue();
}
QQuick3DObjectPrivate::ExtraData::ExtraData() : hideRefCount(0) {}
QT_END_NAMESPACE
#include "moc_qquick3dobject_p.cpp"