Files
qt-creator/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
Miikka Heikkinen c789186418 QmlDesigner: Handle picking of models under View3D component properly
If a model defined inside the View3D component is picked on 3D editor,
the parent View3D is selected instead as there is no instance for
the model itself. This is similar to how Node based component picking
works.

Fixes: QDS-6934
Change-Id: I4f273972da8cb1c55f03cab323dd9804a5d10def
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
2022-05-31 12:02:49 +00:00

1639 lines
60 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** 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 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** 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.
**
****************************************************************************/
#include "nodeinstanceserver.h"
#include "servernodeinstance.h"
#include "objectnodeinstance.h"
#include "childrenchangeeventfilter.h"
#include "dummycontextobject.h"
#include <propertyabstractcontainer.h>
#include <propertybindingcontainer.h>
#include <propertyvaluecontainer.h>
#include <instancecontainer.h>
#include <commondefines.h>
#include <nodeinstanceclientinterface.h>
#include <qmlprivategate.h>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
#include <private/qquickdesignersupportmetainfo_p.h>
#endif
#include <createinstancescommand.h>
#include <changefileurlcommand.h>
#include <clearscenecommand.h>
#include <reparentinstancescommand.h>
#include <changevaluescommand.h>
#include <changeauxiliarycommand.h>
#include <changebindingscommand.h>
#include <changeidscommand.h>
#include <removeinstancescommand.h>
#include <removepropertiescommand.h>
#include <valueschangedcommand.h>
#include <informationchangedcommand.h>
#include <pixmapchangedcommand.h>
#include <changestatecommand.h>
#include <childrenchangedcommand.h>
#include <completecomponentcommand.h>
#include <componentcompletedcommand.h>
#include <createscenecommand.h>
#include <changenodesourcecommand.h>
#include <tokencommand.h>
#include <removesharedmemorycommand.h>
#include <changeselectioncommand.h>
#include <inputeventcommand.h>
#include <view3dactioncommand.h>
#include <requestmodelnodepreviewimagecommand.h>
#include <changelanguagecommand.h>
// Nanotrace headers are not exported to build dir at all if the feature is disabled, so
// runtime puppet build can't find them.
#if NANOTRACE_ENABLED
#include "nanotrace/nanotrace.h"
#else
#define NANOTRACE_SCOPE(cat, name)
#endif
#include <designersupportdelegate.h>
#include <QAbstractAnimation>
#include <QDebug>
#include <QDir>
#include <QFileSystemWatcher>
#include <QMetaType>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQuickItemGrabResult>
#include <QQuickView>
#include <QSet>
#include <QUrl>
#include <QVariant>
#include <qqmllist.h>
#include <QFontDatabase>
#include <QFileInfo>
#include <QDirIterator>
#include <algorithm>
namespace {
bool testImportStatements(const QStringList &importStatementList,
const QUrl &url, QString *errorMessage = nullptr)
{
if (importStatementList.isEmpty())
return false;
// ToDo: move engine outside of this function, this makes it expensive
QQmlEngine engine;
QQmlComponent testImportComponent(&engine);
QByteArray testComponentCode = QStringList(importStatementList).join('\n').toUtf8();
testImportComponent.setData(testComponentCode.append("\nItem {}\n"), url);
testImportComponent.create();
if (testImportComponent.isError()) {
if (errorMessage) {
errorMessage->append("found not working imports: ");
errorMessage->append(testImportComponent.errorString());
}
return false;
}
return true;
}
void sortFilterImports(const QStringList &imports, QStringList *workingImports, QStringList *failedImports,
const QUrl &url, QString *errorMessage)
{
static QSet<QString> visited;
QString visitCheckId("imports: %1, workingImports: %2, failedImports: %3");
visitCheckId = visitCheckId.arg(imports.join(""), workingImports->join(""), failedImports->join(""));
if (visited.contains(visitCheckId))
return;
else
visited.insert(visitCheckId);
for (const QString &import : imports) {
const QStringList alreadyTestedImports = *workingImports + *failedImports;
if (!alreadyTestedImports.contains(import)) {
QStringList readyForTestImports = *workingImports;
readyForTestImports.append(import);
QString lastErrorMessage;
if (testImportStatements(readyForTestImports, url, &lastErrorMessage)) {
Q_ASSERT(!workingImports->contains(import));
workingImports->append(import);
} else {
if (imports.endsWith(import) == false) {
// the not working import is not the last import, so there could be some
// import dependency which we try with the reorderd remaining imports
QStringList reorderedImports;
std::copy_if(imports.cbegin(), imports.cend(), std::back_inserter(reorderedImports),
[&import, &alreadyTestedImports] (const QString &checkForResortingImport){
if (checkForResortingImport == import)
return false;
return !alreadyTestedImports.contains(checkForResortingImport);
});
reorderedImports.append(import);
sortFilterImports(reorderedImports, workingImports, failedImports, url, errorMessage);
} else {
Q_ASSERT(!failedImports->contains(import));
failedImports->append(import);
if (errorMessage)
errorMessage->append(lastErrorMessage);
}
}
}
}
}
} // anonymous
namespace QmlDesigner {
static NodeInstanceServer *nodeInstanceServerInstance = nullptr;
static void notifyPropertyChangeCallBackFunction(QObject *object, const PropertyName &propertyName)
{
qint32 id = nodeInstanceServerInstance->instanceForObject(object).instanceId();
nodeInstanceServerInstance->notifyPropertyChange(id, propertyName);
}
static void (*notifyPropertyChangeCallBackPointer)(QObject *, const PropertyName &) = &notifyPropertyChangeCallBackFunction;
NodeInstanceServer::NodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient) :
NodeInstanceServerInterface(),
m_childrenChangeEventFilter(new Internal::ChildrenChangeEventFilter(this)),
m_nodeInstanceClient(nodeInstanceClient)
{
m_idInstances.reserve(1000);
qmlRegisterType<DummyContextObject>("QmlDesigner", 1, 0, "DummyContextObject");
connect(m_childrenChangeEventFilter.data(), &Internal::ChildrenChangeEventFilter::childrenChanged,
this, &NodeInstanceServer::emitParentChanged);
nodeInstanceServerInstance = this;
Internal::QmlPrivateGate::registerNotifyPropertyChangeCallBack(notifyPropertyChangeCallBackPointer);
Internal::QmlPrivateGate::registerFixResourcePathsForObjectCallBack();
}
NodeInstanceServer::~NodeInstanceServer()
{
m_objectInstanceHash.clear();
}
QList<ServerNodeInstance> NodeInstanceServer::createInstances(const QVector<InstanceContainer> &containerVector)
{
Q_ASSERT(declarativeView() || quickWindow());
QList<ServerNodeInstance> instanceList;
for (const InstanceContainer &instanceContainer : containerVector) {
ServerNodeInstance instance;
if (instanceContainer.nodeSourceType() == InstanceContainer::ComponentSource) {
instance = ServerNodeInstance::create(this, instanceContainer, ServerNodeInstance::WrapAsComponent);
} else {
instance = ServerNodeInstance::create(this, instanceContainer, ServerNodeInstance::DoNotWrapAsComponent);
}
insertInstanceRelationship(instance);
instanceList.append(instance);
instance.internalObject()->installEventFilter(childrenChangeEventFilter());
if (instanceContainer.instanceId() == 0) {
m_rootNodeInstance = instance;
if (quickView())
quickView()->setContent(fileUrl(), m_importComponent, m_rootNodeInstance.rootQuickItem());
}
const QList<QQmlContext *> subContexts = allSubContextsForObject(instance.internalObject());
for (QQmlContext *context : subContexts)
setupDummysForContext(context);
}
return instanceList;
}
void NodeInstanceServer::createInstances(const CreateInstancesCommand &command)
{
createInstances(command.instances());
startRenderTimer();
}
ServerNodeInstance NodeInstanceServer::instanceForId(qint32 id) const
{
if (id < 0)
return ServerNodeInstance();
Q_ASSERT(m_idInstances.size() > id);
return m_idInstances[id];
}
bool NodeInstanceServer::hasInstanceForId(qint32 id) const
{
if (id < 0)
return false;
return m_idInstances.size() > id && m_idInstances[id].isValid();
}
ServerNodeInstance NodeInstanceServer::instanceForObject(QObject *object) const
{
Q_ASSERT(m_objectInstanceHash.contains(object));
return m_objectInstanceHash.value(object);
}
bool NodeInstanceServer::hasInstanceForObject(QObject *object) const
{
if (object == nullptr)
return false;
return m_objectInstanceHash.contains(object) && m_objectInstanceHash.value(object).isValid();
}
void NodeInstanceServer::setRenderTimerInterval(int timerInterval)
{
m_renderTimerInterval = timerInterval;
}
void NodeInstanceServer::setSlowRenderTimerInterval(int timerInterval)
{
m_timerModeInterval = timerInterval;
}
void NodeInstanceServer::setTimerId(int timerId)
{
m_timer = timerId;
}
int NodeInstanceServer::timerId() const
{
return m_timer;
}
int NodeInstanceServer::renderTimerInterval() const
{
return m_renderTimerInterval;
}
void NodeInstanceServer::startRenderTimer()
{
if (m_timerMode == TimerMode::SlowTimer)
stopRenderTimer();
if (m_timerMode == TimerMode::DisableTimer)
return;
if (m_timer == 0)
m_timer = startTimer(m_renderTimerInterval);
m_timerMode = TimerMode::NormalTimer;
}
void NodeInstanceServer::slowDownRenderTimer()
{
if (m_timer != 0) {
killTimer(m_timer);
m_timer = 0;
}
if (m_timerMode == TimerMode::DisableTimer)
return;
m_timer = startTimer(m_timerModeInterval);
m_timerMode = TimerMode::SlowTimer;
}
void NodeInstanceServer::stopRenderTimer()
{
if (m_timer) {
killTimer(m_timer);
m_timer = 0;
}
}
void NodeInstanceServer::createScene(const CreateSceneCommand &command)
{
initializeView();
registerFonts(command.resourceUrl);
setTranslationLanguage(command.language);
if (!ViewConfig::isParticleViewMode())
Internal::QmlPrivateGate::stopUnifiedTimer();
setupScene(command);
setupState(command.stateInstanceId);
refreshBindings();
startRenderTimer();
}
void NodeInstanceServer::clearScene(const ClearSceneCommand &/*command*/)
{
stopRenderTimer();
removeAllInstanceRelationships();
m_fileSystemWatcherHash.clear();
m_rootNodeInstance.makeInvalid();
m_changedPropertyList.clear();
m_fileUrl.clear();
}
void NodeInstanceServer::update3DViewState(const Update3dViewStateCommand &/*command*/)
{
}
void NodeInstanceServer::changeSelection(const ChangeSelectionCommand &/*command*/)
{
}
void NodeInstanceServer::removeInstances(const RemoveInstancesCommand &command)
{
ServerNodeInstance oldState = activeStateInstance();
if (activeStateInstance().isValid())
activeStateInstance().deactivateState();
const QVector<qint32> instanceIds = command.instanceIds();
for (qint32 instanceId : instanceIds)
removeInstanceRelationsip(instanceId);
if (oldState.isValid())
oldState.activateState();
refreshBindings();
startRenderTimer();
}
void NodeInstanceServer::removeProperties(const RemovePropertiesCommand &command)
{
bool hasDynamicProperties = false;
const QVector<PropertyAbstractContainer> props = command.properties();
for (const PropertyAbstractContainer &container : props) {
hasDynamicProperties |= container.isDynamic();
resetInstanceProperty(container);
}
if (hasDynamicProperties)
refreshBindings();
startRenderTimer();
}
void NodeInstanceServer::reparentInstances(const QVector<ReparentContainer> &containerVector)
{
for (const ReparentContainer &container : containerVector) {
if (hasInstanceForId(container.instanceId())) {
ServerNodeInstance instance = instanceForId(container.instanceId());
if (instance.isValid()) {
ServerNodeInstance newParent = instanceForId(container.newParentInstanceId());
PropertyName newParentProperty = container.newParentProperty();
if (!isInformationServer()) {
// Children of the component wraps are left out of the node tree to avoid
// incorrectly rendering them
if (newParent.isComponentWrap()) {
newParent = {};
newParentProperty.clear();
}
}
instance.reparent(instanceForId(container.oldParentInstanceId()),
container.oldParentProperty(),
newParent, newParentProperty);
}
}
}
}
void NodeInstanceServer::reparentInstances(const ReparentInstancesCommand &command)
{
reparentInstances(command.reparentInstances());
refreshBindings();
startRenderTimer();
}
void NodeInstanceServer::changeState(const ChangeStateCommand &command)
{
setupState(command.stateInstanceId());
startRenderTimer();
}
void NodeInstanceServer::completeComponent(const CompleteComponentCommand &command)
{
QList<ServerNodeInstance> instanceList;
const QVector<qint32> instanceIds = command.instances();
for (qint32 instanceId : instanceIds) {
if (hasInstanceForId(instanceId)) {
ServerNodeInstance instance = instanceForId(instanceId);
instance.doComponentComplete();
instanceList.append(instance);
}
}
startRenderTimer();
}
void NodeInstanceServer::changeNodeSource(const ChangeNodeSourceCommand &command)
{
if (hasInstanceForId(command.instanceId())) {
ServerNodeInstance instance = instanceForId(command.instanceId());
if (instance.isValid())
instance.setNodeSource(command.nodeSource());
}
startRenderTimer();
}
void NodeInstanceServer::token(const TokenCommand &/*command*/)
{
}
void NodeInstanceServer::removeSharedMemory(const RemoveSharedMemoryCommand &/*command*/)
{
}
void NodeInstanceServer::setupImports(const QVector<AddImportContainer> &containerVector)
{
Q_ASSERT(quickWindow());
QSet<QString> importStatementSet;
QString qtQuickImport;
for (const AddImportContainer &container : containerVector) {
QString importStatement = QString("import ");
if (!container.fileName().isEmpty())
importStatement += '"' + container.fileName() + '"';
else if (!container.url().isEmpty())
importStatement += container.url().toString();
if (!container.version().isEmpty())
importStatement += ' ' + container.version();
if (!container.alias().isEmpty())
importStatement += " as " + container.alias();
if (importStatement.startsWith(QLatin1String("import QtQuick") + QChar(QChar::Space)))
qtQuickImport = importStatement;
else
importStatementSet.insert(importStatement);
}
delete m_importComponent.data();
delete m_importComponentObject.data();
const QStringList importStatementList = QtHelpers::toList(importStatementSet);
const QStringList fullImportStatementList(QStringList(qtQuickImport) + importStatementList);
// check possible import statements combinations
// but first try the current order -> maybe it just works
if (testImportStatements(fullImportStatementList, fileUrl())) {
setupOnlyWorkingImports(fullImportStatementList);
} else {
QString errorMessage;
if (!testImportStatements(QStringList(qtQuickImport), fileUrl(), &errorMessage))
qtQuickImport = "import QtQuick 2.0";
if (testImportStatements(QStringList(qtQuickImport), fileUrl(), &errorMessage)) {
QStringList workingImportStatementList;
QStringList failedImportList;
sortFilterImports(QStringList(qtQuickImport) + importStatementList, &workingImportStatementList,
&failedImportList, fileUrl(), &errorMessage);
setupOnlyWorkingImports(workingImportStatementList);
}
if (!errorMessage.isEmpty())
sendDebugOutput(DebugOutputCommand::WarningType, errorMessage);
}
}
void NodeInstanceServer::setupOnlyWorkingImports(const QStringList &workingImportStatementList)
{
QByteArray componentCode = workingImportStatementList.join("\n").toUtf8().append("\n");
m_importCode = componentCode;
m_importComponent = new QQmlComponent(engine(), quickWindow());
if (quickView())
quickView()->setContent(fileUrl(), m_importComponent, quickView()->rootObject());
m_importComponent->setData(componentCode.append("\nItem {}\n"), fileUrl());
m_importComponentObject = m_importComponent->create();
Q_ASSERT(m_importComponent && m_importComponentObject);
Q_ASSERT_X(m_importComponent->errors().isEmpty(), __FUNCTION__, m_importComponent->errorString().toLatin1());
}
void NodeInstanceServer::setupFileUrl(const QUrl &fileUrl)
{
if (!fileUrl.isEmpty()) {
engine()->setBaseUrl(fileUrl);
m_fileUrl = fileUrl;
}
}
void NodeInstanceServer::setupDummyData(const QUrl &fileUrl)
{
if (!fileUrl.isEmpty()) {
const QStringList dummyDataDirectoryList = dummyDataDirectories(QFileInfo(fileUrl.toLocalFile()).path());
for (const QString &dummyDataDirectory : dummyDataDirectoryList) {
loadDummyDataFiles(dummyDataDirectory);
loadDummyDataContext(dummyDataDirectory);
}
}
if (m_dummyContextObject.isNull())
setupDefaultDummyData();
rootContext()->setContextObject(m_dummyContextObject);
}
void NodeInstanceServer::setupDefaultDummyData()
{
QQmlComponent component(engine());
QByteArray defaultContextObjectArray("import QtQml 2.0\n"
"import QmlDesigner 1.0\n"
"DummyContextObject {\n"
" parent: QtObject {\n"
" property real width: 360\n"
" property real height: 640\n"
" }\n"
"}\n");
component.setData(defaultContextObjectArray, fileUrl());
m_dummyContextObject = component.create();
if (component.isError()) {
const QList<QQmlError> errors = component.errors();
for (const QQmlError &error : errors)
qWarning() << error;
}
if (m_dummyContextObject)
m_dummyContextObject->setParent(this);
refreshBindings();
}
QList<ServerNodeInstance> NodeInstanceServer::setupInstances(const CreateSceneCommand &command)
{
QList<ServerNodeInstance> instanceList = createInstances(command.instances);
for (const IdContainer &container : std::as_const(command.ids)) {
if (hasInstanceForId(container.instanceId()))
instanceForId(container.instanceId()).setId(container.id());
}
for (const PropertyValueContainer &container : std::as_const(command.valueChanges)) {
if (container.isDynamic())
setInstancePropertyVariant(container);
}
for (const PropertyValueContainer &container : std::as_const(command.valueChanges)) {
if (!container.isDynamic())
setInstancePropertyVariant(container);
}
reparentInstances(command.reparentInstances);
for (const PropertyBindingContainer &container : std::as_const(command.bindingChanges)) {
if (container.isDynamic())
setInstancePropertyBinding(container);
}
for (const PropertyBindingContainer &container : std::as_const(command.bindingChanges)) {
if (!container.isDynamic())
setInstancePropertyBinding(container);
}
for (const PropertyValueContainer &container : std::as_const(command.auxiliaryChanges))
setInstanceAuxiliaryData(container);
for (int i = instanceList.size(); --i >= 0; )
instanceList[i].doComponentComplete();
return instanceList;
}
void NodeInstanceServer::changeFileUrl(const ChangeFileUrlCommand &command)
{
m_fileUrl = command.fileUrl;
if (engine())
engine()->setBaseUrl(m_fileUrl);
refreshBindings();
startRenderTimer();
}
void NodeInstanceServer::changePropertyValues(const ChangeValuesCommand &command)
{
bool hasDynamicProperties = false;
const QVector<PropertyValueContainer> valueChanges = command.valueChanges();
for (const PropertyValueContainer &container : valueChanges) {
hasDynamicProperties |= container.isDynamic();
setInstancePropertyVariant(container);
}
if (hasDynamicProperties)
refreshBindings();
startRenderTimer();
}
void NodeInstanceServer::changeAuxiliaryValues(const ChangeAuxiliaryCommand &command)
{
const QVector<PropertyValueContainer> auxiliaryChanges = command.auxiliaryChanges;
for (const PropertyValueContainer &container : auxiliaryChanges)
setInstanceAuxiliaryData(container);
startRenderTimer();
}
void NodeInstanceServer::changePropertyBindings(const ChangeBindingsCommand &command)
{
bool hasDynamicProperties = false;
const QVector<PropertyBindingContainer> bindingChanges = command.bindingChanges;
for (const PropertyBindingContainer &container : bindingChanges) {
hasDynamicProperties |= container.isDynamic();
setInstancePropertyBinding(container);
}
if (hasDynamicProperties)
refreshBindings();
startRenderTimer();
}
void NodeInstanceServer::changeIds(const ChangeIdsCommand &command)
{
for (const IdContainer &container : command.ids) {
if (hasInstanceForId(container.instanceId()))
instanceForId(container.instanceId()).setId(container.id());
}
refreshBindings();
startRenderTimer();
}
QQmlContext *NodeInstanceServer::context() const
{
if (m_importComponentObject) {
QQmlContext *importComponentContext = QQmlEngine::contextForObject(m_importComponentObject.data());
if (importComponentContext) // this should be the default
return importComponentContext;
}
if (engine())
return rootContext();
return nullptr;
}
QQmlContext *NodeInstanceServer::rootContext() const
{
return engine()->rootContext();
}
const QVector<NodeInstanceServer::InstancePropertyPair> NodeInstanceServer::changedPropertyList() const
{
return m_changedPropertyList;
}
void NodeInstanceServer::clearChangedPropertyList()
{
m_changedPropertyList.clear();
}
void NodeInstanceServer::setupDummysForContext(QQmlContext *context)
{
for (const DummyPair &dummyPair : std::as_const(m_dummyObjectList)) {
if (dummyPair.second)
context->setContextProperty(dummyPair.first, dummyPair.second.data());
}
}
static bool isTypeAvailable(const MockupTypeContainer &mockupType, QQmlEngine *engine)
{
QString qmlSource;
qmlSource.append("import " +
mockupType.importUri()
+ " "
+ QString::number(mockupType.majorVersion())
+ "." + QString::number(mockupType.minorVersion())
+ "\n");
qmlSource.append(QString::fromUtf8(mockupType.typeName()) + "{\n}\n");
QQmlComponent component(engine);
component.setData(qmlSource.toUtf8(), QUrl());
return !component.isError();
}
void NodeInstanceServer::setupMockupTypes(const QVector<MockupTypeContainer> &container)
{
for (const MockupTypeContainer &mockupType : container) {
if (!isTypeAvailable(mockupType, engine())) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
if (mockupType.majorVersion() == -1 && mockupType.minorVersion() == -1) {
QQuickDesignerSupportMetaInfo::registerMockupObject(mockupType.importUri().toUtf8(),
1,
0,
mockupType.typeName());
} else {
QQuickDesignerSupportMetaInfo::registerMockupObject(mockupType.importUri().toUtf8(),
mockupType.majorVersion(),
mockupType.minorVersion(),
mockupType.typeName());
}
#else
qmlRegisterType(QUrl("qrc:/qtquickplugin/mockfiles/GenericBackend.qml"),
mockupType.importUri().toUtf8(),
mockupType.majorVersion(),
mockupType.minorVersion(),
mockupType.typeName());
#endif
}
}
}
QList<QQmlContext *> NodeInstanceServer::allSubContextsForObject(QObject *object)
{
QList<QQmlContext *> contextList;
if (object) {
const QList<QObject *> subObjects = object->findChildren<QObject *>();
for (QObject *subObject : subObjects) {
QQmlContext *contextOfObject = QQmlEngine::contextForObject(subObject);
if (contextOfObject) {
if (contextOfObject != context() && !contextList.contains(contextOfObject))
contextList.append(contextOfObject);
}
}
}
return contextList;
}
QList<QObject *> NodeInstanceServer::allSubObjectsForObject(QObject *object)
{
QList<QObject *> subChildren;
if (object)
subChildren = object->findChildren<QObject *>();
return subChildren;
}
void NodeInstanceServer::removeAllInstanceRelationships()
{
for (ServerNodeInstance &instance : m_objectInstanceHash) {
if (instance.isValid())
instance.setId({});
}
// First the root object
// This also cleans up all objects that have root object as ancestor
rootNodeInstance().makeInvalid();
// Invalidate any remaining objects
for (ServerNodeInstance &instance : m_objectInstanceHash)
instance.makeInvalid();
m_idInstances.clear();
m_objectInstanceHash.clear();
}
QFileSystemWatcher *NodeInstanceServer::dummydataFileSystemWatcher()
{
if (m_dummdataFileSystemWatcher.isNull()) {
m_dummdataFileSystemWatcher = new QFileSystemWatcher(this);
connect(m_dummdataFileSystemWatcher.data(), &QFileSystemWatcher::fileChanged, this,
&NodeInstanceServer::refreshDummyData);
}
return m_dummdataFileSystemWatcher.data();
}
QFileSystemWatcher *NodeInstanceServer::fileSystemWatcher()
{
if (m_fileSystemWatcher.isNull()) {
m_fileSystemWatcher = new QFileSystemWatcher(this);
connect(m_fileSystemWatcher.data(), &QFileSystemWatcher::fileChanged, this,
&NodeInstanceServer::refreshLocalFileProperty);
}
return m_fileSystemWatcher.data();
}
Internal::ChildrenChangeEventFilter *NodeInstanceServer::childrenChangeEventFilter() const
{
return m_childrenChangeEventFilter.data();
}
void NodeInstanceServer::addFilePropertyToFileSystemWatcher(QObject *object, const PropertyName &propertyName,
const QString &path)
{
if (!m_fileSystemWatcherHash.contains(path)) {
m_fileSystemWatcherHash.insert(path, ObjectPropertyPair(object, propertyName));
fileSystemWatcher()->addPath(path);
}
}
void NodeInstanceServer::removeFilePropertyFromFileSystemWatcher(QObject *object, const PropertyName &propertyName,
const QString &path)
{
if (m_fileSystemWatcherHash.contains(path)) {
fileSystemWatcher()->removePath(path);
m_fileSystemWatcherHash.remove(path, ObjectPropertyPair(object, propertyName));
}
}
void NodeInstanceServer::refreshLocalFileProperty(const QString &path)
{
if (m_fileSystemWatcherHash.contains(path)) {
for (const ObjectPropertyPair &objectPropertyPair : std::as_const(m_fileSystemWatcherHash)) {
QObject *object = objectPropertyPair.first.data();
PropertyName propertyName = objectPropertyPair.second;
if (hasInstanceForObject(object)) {
instanceForObject(object).refreshProperty(propertyName);
}
}
}
}
void NodeInstanceServer::refreshDummyData(const QString &path)
{
engine()->clearComponentCache();
QFileInfo filePath(path);
if (filePath.completeBaseName().contains("_dummycontext"))
loadDummyContextObjectFile(filePath);
else
loadDummyDataFile(filePath);
refreshBindings();
startRenderTimer();
}
void NodeInstanceServer::addChangedProperty(const InstancePropertyPair &property)
{
if (!m_changedPropertyList.contains(property))
m_changedPropertyList.append(property);
}
void NodeInstanceServer::emitParentChanged(QObject *child)
{
if (hasInstanceForObject(child)) {
addChangedProperty(InstancePropertyPair(instanceForObject(child), "parent"));
}
}
Internal::ChildrenChangeEventFilter *NodeInstanceServer::childrenChangeEventFilter()
{
if (m_childrenChangeEventFilter.isNull()) {
m_childrenChangeEventFilter = new Internal::ChildrenChangeEventFilter(this);
connect(m_childrenChangeEventFilter.data(), &Internal::ChildrenChangeEventFilter::childrenChanged,
this, &NodeInstanceServer::emitParentChanged);
}
return m_childrenChangeEventFilter.data();
}
void NodeInstanceServer::resetInstanceProperty(const PropertyAbstractContainer &propertyContainer)
{
if (hasInstanceForId(propertyContainer.instanceId())) { // TODO ugly workaround
ServerNodeInstance instance = instanceForId(propertyContainer.instanceId());
Q_ASSERT(instance.isValid());
const PropertyName name = propertyContainer.name();
if (activeStateInstance().isValid() && !instance.isSubclassOf("QtQuick/PropertyChanges")) {
bool statePropertyWasReseted = activeStateInstance().resetStateProperty(instance, name,
instance.resetVariant(name));
if (!statePropertyWasReseted)
instance.resetProperty(name);
} else {
instance.resetProperty(name);
}
if (propertyContainer.isDynamic() && propertyContainer.instanceId() == 0 && engine())
rootContext()->setContextProperty(QString::fromUtf8(name), QVariant());
}
}
void NodeInstanceServer::setInstancePropertyBinding(const PropertyBindingContainer &bindingContainer)
{
if (hasInstanceForId(bindingContainer.instanceId())) {
ServerNodeInstance instance = instanceForId(bindingContainer.instanceId());
const PropertyName name = bindingContainer.name();
const QString expression = bindingContainer.expression();
if (activeStateInstance().isValid() && !instance.isSubclassOf("QtQuick/PropertyChanges")) {
bool stateBindingWasUpdated = activeStateInstance().updateStateBinding(instance, name, expression);
if (!stateBindingWasUpdated) {
if (bindingContainer.isDynamic())
Internal::QmlPrivateGate::createNewDynamicProperty(instance.internalInstance()->object(), engine(),
QString::fromUtf8(name));
instance.setPropertyBinding(name, expression);
}
} else {
if (bindingContainer.isDynamic())
Internal::QmlPrivateGate::createNewDynamicProperty(instance.internalInstance()->object(), engine(),
QString::fromUtf8(name));
instance.setPropertyBinding(name, expression);
if (instance.instanceId() == 0 && (name == "width" || name == "height"))
resizeCanvasToRootItem();
}
}
}
void NodeInstanceServer::removeProperties(const QList<PropertyAbstractContainer> &propertyList)
{
for (const PropertyAbstractContainer &property : propertyList)
resetInstanceProperty(property);
}
void NodeInstanceServer::setInstancePropertyVariant(const PropertyValueContainer &valueContainer)
{
if (hasInstanceForId(valueContainer.instanceId())) {
ServerNodeInstance instance = instanceForId(valueContainer.instanceId());
const PropertyName name = valueContainer.name();
const QVariant value = valueContainer.value();
if (activeStateInstance().isValid() && !instance.isSubclassOf("QtQuick/PropertyChanges")) {
bool stateValueWasUpdated = activeStateInstance().updateStateVariant(instance, name, value);
if (!stateValueWasUpdated) {
if (valueContainer.isDynamic()) {
Internal::QmlPrivateGate::createNewDynamicProperty(instance.internalInstance()->object(),
engine(), QString::fromUtf8(name));
}
instance.setPropertyVariant(name, value);
}
} else { // base state
if (valueContainer.isDynamic()) {
Internal::QmlPrivateGate::createNewDynamicProperty(instance.internalInstance()->object(),
engine(), QString::fromUtf8(name));
}
instance.setPropertyVariant(name, value);
}
if (valueContainer.isDynamic() && valueContainer.instanceId() == 0 && engine())
rootContext()->setContextProperty(QString::fromUtf8(name), Internal::QmlPrivateGate::fixResourcePaths(value));
if (valueContainer.instanceId() == 0 && (name == "width" || name == "height" || name == "x" || name == "y"))
resizeCanvasToRootItem();
}
}
void NodeInstanceServer::setInstanceAuxiliaryData(const PropertyValueContainer &auxiliaryContainer)
{
if (auxiliaryContainer.instanceId() == 0 && (auxiliaryContainer.name() == "width" ||
auxiliaryContainer.name() == "height")) {
if (!auxiliaryContainer.value().isNull())
setInstancePropertyVariant(auxiliaryContainer);
else
rootNodeInstance().resetProperty(auxiliaryContainer.name());
}
if (auxiliaryContainer.name().endsWith("@NodeInstance")) {
PropertyName propertyName = auxiliaryContainer.name().left(auxiliaryContainer.name().count() - 13);
if (!auxiliaryContainer.value().isNull()) {
setInstancePropertyVariant(PropertyValueContainer(auxiliaryContainer.instanceId(),
propertyName,
auxiliaryContainer.value(),
auxiliaryContainer.dynamicTypeName()));
} else {
rootNodeInstance().resetProperty(propertyName);
}
} else if (auxiliaryContainer.name() == "invisible") {
if (hasInstanceForId(auxiliaryContainer.instanceId())) {
ServerNodeInstance instance = instanceForId(auxiliaryContainer.instanceId());
if (!auxiliaryContainer.value().isNull())
instance.setHiddenInEditor(auxiliaryContainer.value().toBool());
else
instance.setHiddenInEditor(false);
}
} else if (auxiliaryContainer.name() == "locked") {
if (hasInstanceForId(auxiliaryContainer.instanceId())) {
ServerNodeInstance instance = instanceForId(auxiliaryContainer.instanceId());
if (!auxiliaryContainer.value().isNull())
instance.setLockedInEditor(auxiliaryContainer.value().toBool());
else
instance.setLockedInEditor(false);
}
}
}
QUrl NodeInstanceServer::fileUrl() const
{
return m_fileUrl;
}
ServerNodeInstance NodeInstanceServer::activeStateInstance() const
{
return m_activeStateInstance;
}
ServerNodeInstance NodeInstanceServer::rootNodeInstance() const
{
return m_rootNodeInstance;
}
void NodeInstanceServer::setStateInstance(const ServerNodeInstance &stateInstance)
{
m_activeStateInstance = stateInstance;
}
void NodeInstanceServer::clearStateInstance()
{
m_activeStateInstance = ServerNodeInstance();
}
void NodeInstanceServer::timerEvent(QTimerEvent *event)
{
if (event->timerId() == m_timer) {
collectItemChangesAndSendChangeCommands();
}
NodeInstanceServerInterface::timerEvent(event);
}
NodeInstanceClientInterface *NodeInstanceServer::nodeInstanceClient() const
{
return m_nodeInstanceClient;
}
static QVector<InformationContainer> createInformationVector(const QList<ServerNodeInstance> &instanceList, bool initial)
{
QVector<InformationContainer> informationVector;
for (const ServerNodeInstance &instance : instanceList) {
if (instance.isValid()) {
informationVector.append(InformationContainer(instance.instanceId(), Position, instance.position()));
informationVector.append(InformationContainer(instance.instanceId(), Transform, instance.transform()));
informationVector.append(InformationContainer(instance.instanceId(), SceneTransform, instance.sceneTransform()));
informationVector.append(InformationContainer(instance.instanceId(), Size, instance.size()));
informationVector.append(InformationContainer(instance.instanceId(), BoundingRect, instance.boundingRect()));
informationVector.append(InformationContainer(instance.instanceId(), ContentItemBoundingRect, instance.contentItemBoundingRect()));
informationVector.append(InformationContainer(instance.instanceId(), Transform, instance.transform()));
informationVector.append(InformationContainer(instance.instanceId(), ContentTransform, instance.contentTransform()));
informationVector.append(InformationContainer(instance.instanceId(), ContentItemTransform, instance.contentItemTransform()));
informationVector.append(InformationContainer(instance.instanceId(), HasContent, instance.hasContent()));
informationVector.append(InformationContainer(instance.instanceId(), IsMovable, instance.isMovable()));
informationVector.append(InformationContainer(instance.instanceId(), IsResizable, instance.isResizable()));
informationVector.append(InformationContainer(instance.instanceId(), IsInLayoutable, instance.isInLayoutable()));
informationVector.append(InformationContainer(instance.instanceId(), PenWidth, instance.penWidth()));
informationVector.append(InformationContainer(instance.instanceId(), IsAnchoredByChildren, instance.isAnchoredByChildren()));
informationVector.append(InformationContainer(instance.instanceId(), IsAnchoredBySibling, instance.isAnchoredBySibling()));
informationVector.append(InformationContainer(instance.instanceId(), AllStates, instance.allStates()));
informationVector.append(InformationContainer(instance.instanceId(), HasAnchor, PropertyName("anchors.fill"), instance.hasAnchor("anchors.fill")));
informationVector.append(InformationContainer(instance.instanceId(), HasAnchor, PropertyName("anchors.centerIn"), instance.hasAnchor("anchors.centerIn")));
informationVector.append(InformationContainer(instance.instanceId(), HasAnchor, PropertyName("anchors.right"), instance.hasAnchor("anchors.right")));
informationVector.append(InformationContainer(instance.instanceId(), HasAnchor, PropertyName("anchors.top"), instance.hasAnchor("anchors.top")));
informationVector.append(InformationContainer(instance.instanceId(), HasAnchor, PropertyName("anchors.left"), instance.hasAnchor("anchors.left")));
informationVector.append(InformationContainer(instance.instanceId(), HasAnchor, PropertyName("anchors.bottom"), instance.hasAnchor("anchors.bottom")));
informationVector.append(InformationContainer(instance.instanceId(), HasAnchor, PropertyName("anchors.horizontalCenter"), instance.hasAnchor("anchors.horizontalCenter")));
informationVector.append(InformationContainer(instance.instanceId(), HasAnchor, PropertyName("anchors.verticalCenter"), instance.hasAnchor("anchors.verticalCenter")));
informationVector.append(InformationContainer(instance.instanceId(), HasAnchor, PropertyName("anchors.baseline"), instance.hasAnchor("anchors.baseline")));
QPair<PropertyName, ServerNodeInstance> anchorPair = instance.anchor("anchors.fill");
informationVector.append(InformationContainer(instance.instanceId(), Anchor, PropertyName("anchors.fill"), anchorPair.first, anchorPair.second.instanceId()));
anchorPair = instance.anchor("anchors.centerIn");
informationVector.append(InformationContainer(instance.instanceId(), Anchor, PropertyName("anchors.centerIn"), anchorPair.first, anchorPair.second.instanceId()));
anchorPair = instance.anchor("anchors.right");
informationVector.append(InformationContainer(instance.instanceId(), Anchor, PropertyName("anchors.right"), anchorPair.first, anchorPair.second.instanceId()));
anchorPair = instance.anchor("anchors.top");
informationVector.append(InformationContainer(instance.instanceId(), Anchor, PropertyName("anchors.top"), anchorPair.first, anchorPair.second.instanceId()));
anchorPair = instance.anchor("anchors.left");
informationVector.append(InformationContainer(instance.instanceId(), Anchor, PropertyName("anchors.left"), anchorPair.first, anchorPair.second.instanceId()));
anchorPair = instance.anchor("anchors.bottom");
informationVector.append(InformationContainer(instance.instanceId(), Anchor, PropertyName("anchors.bottom"), anchorPair.first, anchorPair.second.instanceId()));
anchorPair = instance.anchor("anchors.horizontalCenter");
informationVector.append(InformationContainer(instance.instanceId(), Anchor, PropertyName("anchors.horizontalCenter"), anchorPair.first, anchorPair.second.instanceId()));
anchorPair = instance.anchor("anchors.verticalCenter");
informationVector.append(InformationContainer(instance.instanceId(), Anchor, PropertyName("anchors.verticalCenter"), anchorPair.first, anchorPair.second.instanceId()));
anchorPair = instance.anchor("anchors.baseline");
informationVector.append(InformationContainer(instance.instanceId(), Anchor, PropertyName("anchors.baseline"), anchorPair.first, anchorPair.second.instanceId()));
const PropertyNameList propertyNames = instance.propertyNames();
if (initial) {
for (const PropertyName &propertyName : propertyNames)
informationVector.append(InformationContainer(instance.instanceId(), InstanceTypeForProperty, propertyName, instance.instanceType(propertyName)));
}
for (const PropertyName &propertyName : propertyNames) {
bool hasChanged = false;
bool hasBinding = instance.hasBindingForProperty(propertyName, &hasChanged);
if (hasChanged)
informationVector.append(InformationContainer(instance.instanceId(), HasBindingForProperty, propertyName, hasBinding));
}
}
}
return informationVector;
}
ChildrenChangedCommand NodeInstanceServer::createChildrenChangedCommand(const ServerNodeInstance &parentInstance,
const QList<ServerNodeInstance> &instanceList) const
{
QVector<qint32> instanceVector;
for (const ServerNodeInstance &instance : instanceList)
instanceVector.append(instance.instanceId());
return ChildrenChangedCommand(parentInstance.instanceId(), instanceVector,
createInformationVector(instanceList, false));
}
InformationChangedCommand NodeInstanceServer::createAllInformationChangedCommand(
const QList<ServerNodeInstance> &instanceList, bool initial) const
{
return InformationChangedCommand(createInformationVector(instanceList, initial));
}
static bool supportedVariantType(int type)
{
return type < int(QVariant::UserType) && type != QMetaType::QObjectStar
&& type != QMetaType::QModelIndex && type != QMetaType::VoidStar;
}
ValuesChangedCommand NodeInstanceServer::createValuesChangedCommand(const QList<ServerNodeInstance> &instanceList) const
{
QVector<PropertyValueContainer> valueVector;
for (const ServerNodeInstance &instance : instanceList) {
const QList<PropertyName> propertyNames = instance.propertyNames();
for (const PropertyName &propertyName : propertyNames) {
QVariant propertyValue = instance.property(propertyName);
if (supportedVariantType(propertyValue.userType())) {
valueVector.append(PropertyValueContainer(instance.instanceId(), propertyName,
propertyValue, PropertyName()));
}
}
}
return ValuesChangedCommand(valueVector);
}
ComponentCompletedCommand NodeInstanceServer::createComponentCompletedCommand(const QList<ServerNodeInstance> &instanceList)
{
QVector<qint32> idVector;
for (const ServerNodeInstance &instance : instanceList) {
if (instance.instanceId() >= 0)
idVector.append(instance.instanceId());
}
return ComponentCompletedCommand(idVector);
}
ChangeSelectionCommand NodeInstanceServer::createChangeSelectionCommand(const QList<ServerNodeInstance> &instanceList)
{
QVector<qint32> idVector;
for (const ServerNodeInstance &instance : instanceList) {
if (instance.instanceId() >= 0)
idVector.append(instance.instanceId());
}
return ChangeSelectionCommand(idVector);
}
ValuesChangedCommand NodeInstanceServer::createValuesChangedCommand(const QVector<InstancePropertyPair> &propertyList) const
{
QVector<PropertyValueContainer> valueVector;
for (const InstancePropertyPair &property : propertyList) {
const PropertyName propertyName = property.second;
const ServerNodeInstance instance = property.first;
if (instance.isValid()) {
QVariant propertyValue = instance.property(propertyName);
bool isValid = QMetaType::isRegistered(propertyValue.userType())
&& supportedVariantType(propertyValue.type());
if (!isValid && propertyValue.userType() == 0) {
// If the property is QVariant type, invalid variant can be a valid value
const QMetaObject *mo = instance.internalObject()->metaObject();
const int idx = mo->indexOfProperty(propertyName);
isValid = idx >= 0 && mo->property(idx).userType() == QMetaType::QVariant;
}
if (isValid) {
valueVector.append(PropertyValueContainer(instance.instanceId(), propertyName,
propertyValue, PropertyName()));
}
}
}
return ValuesChangedCommand(valueVector);
}
ValuesModifiedCommand NodeInstanceServer::createValuesModifiedCommand(
const QVector<InstancePropertyValueTriple> &propertyList) const
{
QVector<PropertyValueContainer> valueVector;
for (const InstancePropertyValueTriple &property : propertyList) {
const PropertyName propertyName = property.propertyName;
const ServerNodeInstance instance = property.instance;
const QVariant propertyValue = property.propertyValue;
if (instance.isValid()) {
if (QMetaType::isRegistered(propertyValue.userType())
&& supportedVariantType(propertyValue.type())) {
valueVector.append(PropertyValueContainer(instance.instanceId(),
propertyName,
propertyValue,
PropertyName()));
}
}
}
return ValuesModifiedCommand(valueVector);
}
QByteArray NodeInstanceServer::importCode() const
{
return m_importCode;
}
QObject *NodeInstanceServer::dummyContextObject() const
{
return m_dummyContextObject.data();
}
void NodeInstanceServer::notifyPropertyChange(qint32 instanceid, const PropertyName &propertyName)
{
if (hasInstanceForId(instanceid))
addChangedProperty(InstancePropertyPair(instanceForId(instanceid), propertyName));
}
void NodeInstanceServer::insertInstanceRelationship(const ServerNodeInstance &instance)
{
Q_ASSERT(instance.isValid());
Q_ASSERT(!m_objectInstanceHash.contains(instance.internalObject()));
m_objectInstanceHash.insert(instance.internalObject(), instance);
if (instance.instanceId() >= m_idInstances.size())
m_idInstances.resize(instance.instanceId() + 1);
m_idInstances[instance.instanceId()] = instance;
}
void NodeInstanceServer::removeInstanceRelationsip(qint32 instanceId)
{
if (hasInstanceForId(instanceId)) {
ServerNodeInstance instance = instanceForId(instanceId);
if (instance.isValid())
instance.setId(QString());
m_idInstances[instanceId] = ServerNodeInstance{};
m_objectInstanceHash.remove(instance.internalObject());
instance.makeInvalid();
}
}
PixmapChangedCommand NodeInstanceServer::createPixmapChangedCommand(const QList<ServerNodeInstance> &instanceList) const
{
NANOTRACE_SCOPE("Update", "createPixmapChangedCommand");
QVector<ImageContainer> imageVector;
for (const ServerNodeInstance &instance : instanceList) {
if (!instance.isValid())
continue;
QImage renderImage;
// We need to return empty image if instance has no content to correctly update the
// item image in case the instance changed from having content to not having content.
if (instance.hasContent())
renderImage = instance.renderImage();
imageVector.append(ImageContainer(instance.instanceId(), renderImage, instance.instanceId()));
}
return PixmapChangedCommand(imageVector);
}
void NodeInstanceServer::loadDummyDataFile(const QFileInfo &qmlFileInfo)
{
QQmlComponent component(engine(), qmlFileInfo.filePath());
QObject *dummyData = component.create();
if (component.isError()) {
const QList<QQmlError> errors = component.errors();
for (const QQmlError &error : errors)
qWarning() << error;
}
QVariant oldDummyDataObject = rootContext()->contextProperty(qmlFileInfo.completeBaseName());
if (dummyData) {
qDebug() << "Loaded dummy data:" << qmlFileInfo.filePath();
rootContext()->setContextProperty(qmlFileInfo.completeBaseName(), dummyData);
dummyData->setParent(this);
m_dummyObjectList.append(DummyPair(qmlFileInfo.completeBaseName(), dummyData));
}
if (!oldDummyDataObject.isNull())
delete oldDummyDataObject.value<QObject*>();
if (!dummydataFileSystemWatcher()->files().contains(qmlFileInfo.filePath()))
dummydataFileSystemWatcher()->addPath(qmlFileInfo.filePath());
if (rootNodeInstance().isValid() && rootNodeInstance().internalObject()) {
const QList<QQmlContext *> subContexts = allSubContextsForObject(rootNodeInstance().internalObject());
for (QQmlContext *context : subContexts)
setupDummysForContext(context);
}
}
void NodeInstanceServer::loadDummyContextObjectFile(const QFileInfo &qmlFileInfo)
{
delete m_dummyContextObject.data();
QQmlComponent component(engine(), qmlFileInfo.filePath());
m_dummyContextObject = component.create();
if (component.isError()) {
const QList<QQmlError> errors = component.errors();
for (const QQmlError &error : errors)
qWarning() << error;
}
if (m_dummyContextObject) {
qWarning() << "Loaded dummy context object:" << qmlFileInfo.filePath();
m_dummyContextObject->setParent(this);
}
if (!dummydataFileSystemWatcher()->files().contains(qmlFileInfo.filePath()))
dummydataFileSystemWatcher()->addPath(qmlFileInfo.filePath());
refreshBindings();
}
void NodeInstanceServer::setTranslationLanguage(const QString &language)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
// if there exists an /i18n directory it sets default translators
engine()->setUiLanguage(language);
#endif
static QPointer<MultiLanguage::Translator> multilanguageTranslator;
if (!MultiLanguage::databaseFilePath().isEmpty()
&& QFileInfo::exists(QString::fromUtf8(MultiLanguage::databaseFilePath()))) {
try {
if (!multilanguageLink) {
multilanguageLink = std::make_unique<MultiLanguage::Link>();
multilanguageTranslator = multilanguageLink->translator().release();
QCoreApplication::installTranslator(multilanguageTranslator);
}
if (multilanguageTranslator)
multilanguageTranslator->setLanguage(language);
} catch (std::exception &e) {
qWarning() << "QmlPuppet is unable to initialize MultiLanguage translator:" << e.what();
}
}
}
void NodeInstanceServer::loadDummyDataFiles(const QString &directory)
{
QDir dir(directory, "*.qml");
const QFileInfoList filePathList = dir.entryInfoList();
for (const QFileInfo &qmlFileInfo : filePathList)
loadDummyDataFile(qmlFileInfo);
}
void NodeInstanceServer::loadDummyDataContext(const QString &directory)
{
QDir dir(directory + "/context", "*.qml");
QString baseName = QFileInfo(fileUrl().toLocalFile()).completeBaseName();
const QFileInfoList filePathList = dir.entryInfoList();
for (const QFileInfo &qmlFileInfo : filePathList) {
if (qmlFileInfo.completeBaseName() == baseName)
loadDummyContextObjectFile(qmlFileInfo);
}
}
void NodeInstanceServer::sendDebugOutput(DebugOutputCommand::Type type, const QString &message,
qint32 instanceId)
{
QVector<qint32> ids;
ids.append(instanceId);
sendDebugOutput(type, message, ids);
}
void NodeInstanceServer::sendDebugOutput(DebugOutputCommand::Type type, const QString &message,
const QVector<qint32> &instanceIds)
{
DebugOutputCommand command(message, type, instanceIds);
nodeInstanceClient()->debugOutput(command);
}
void NodeInstanceServer::removeInstanceRelationsipForDeletedObject(QObject *object, qint32 instanceId)
{
if (m_objectInstanceHash.contains(object)) {
ServerNodeInstance instance = instanceForObject(object);
m_objectInstanceHash.remove(object);
if (instanceId >= 0 && m_idInstances.size() > instanceId)
m_idInstances[instanceId] = {};
}
}
QStringList NodeInstanceServer::dummyDataDirectories(const QString &directoryPath)
{
QStringList dummyDataDirectoryList;
QDir directory(directoryPath);
while (true) {
if (directory.isRoot() || !directory.exists())
return dummyDataDirectoryList;
if (directory.exists("dummydata"))
dummyDataDirectoryList.prepend(directory.absoluteFilePath("dummydata"));
directory.cdUp();
}
}
void NodeInstanceServer::inputEvent(const InputEventCommand &command)
{
Q_UNUSED(command)
}
void NodeInstanceServer::view3DAction(const View3DActionCommand &command)
{
Q_UNUSED(command)
}
void NodeInstanceServer::requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command)
{
Q_UNUSED(command)
}
void NodeInstanceServer::changeLanguage(const ChangeLanguageCommand &command)
{
setTranslationLanguage(command.language);
QEvent ev(QEvent::LanguageChange);
QCoreApplication::sendEvent(QCoreApplication::instance(), &ev);
engine()->retranslate();
}
void NodeInstanceServer::changePreviewImageSize(const ChangePreviewImageSizeCommand &) {}
void NodeInstanceServer::incrementNeedsExtraRender()
{
++m_needsExtraRenderCount;
}
void NodeInstanceServer::decrementNeedsExtraRender()
{
--m_needsExtraRenderCount;
}
void NodeInstanceServer::handleExtraRender()
{
// If multipass is needed, render two additional times to ensure correct result
if (m_extraRenderCurrentPass == 0 && m_needsExtraRenderCount > 0)
m_extraRenderCurrentPass = 3;
if (m_extraRenderCurrentPass > 0) {
--m_extraRenderCurrentPass;
if (m_extraRenderCurrentPass > 0)
startRenderTimer();
}
}
void NodeInstanceServer::disableTimer()
{
m_timerMode = TimerMode::DisableTimer;
}
void NodeInstanceServer::sheduleRootItemRender()
{
QSharedPointer<QQuickItemGrabResult> result = m_rootNodeInstance.createGrabResult();
qint32 instanceId = m_rootNodeInstance.instanceId();
if (result) {
connect(result.data(), &QQuickItemGrabResult::ready, [this, result, instanceId] {
QVector<ImageContainer> imageVector;
ImageContainer container(instanceId, result->image(), instanceId);
imageVector.append(container);
nodeInstanceClient()->pixmapChanged(PixmapChangedCommand(imageVector));
});
}
}
void NodeInstanceServer::initializeAuxiliaryViews()
{
}
void NodeInstanceServer::handleInstanceLocked(const ServerNodeInstance &/*instance*/, bool /*enable*/,
bool /*checkAncestors*/)
{
}
void NodeInstanceServer::handleInstanceHidden(const ServerNodeInstance &/*instance*/, bool /*enable*/,
bool /*checkAncestors*/)
{
}
void NodeInstanceServer::handlePickTarget(const ServerNodeInstance &/*instance*/)
{
}
void NodeInstanceServer::setupState(qint32 stateInstanceId)
{
if (hasInstanceForId(stateInstanceId)) {
if (activeStateInstance().isValid())
activeStateInstance().deactivateState();
ServerNodeInstance instance = instanceForId(stateInstanceId);
instance.activateState();
} else {
if (activeStateInstance().isValid())
activeStateInstance().deactivateState();
}
}
void NodeInstanceServer::registerFonts(const QUrl &resourceUrl) const
{
if (!resourceUrl.isValid())
return;
// Autoregister all fonts found inside the project
QDirIterator it {QFileInfo(resourceUrl.toLocalFile()).absoluteFilePath(),
{"*.ttf", "*.otf"}, QDir::Files, QDirIterator::Subdirectories};
while (it.hasNext())
QFontDatabase::addApplicationFont(it.next());
}
bool NodeInstanceServer::isInformationServer() const
{
return false;
}
bool NodeInstanceServer::isPreviewServer() const
{
return false;
}
static QString baseProperty(const QString &property)
{
int index = property.indexOf('.');
if (index > 0)
return property.left(index);
return property;
}
void NodeInstanceServer::addAnimation(QQuickAbstractAnimation *animation)
{
if (!m_animations.contains(animation)) {
m_animations.push_back(animation);
QQuickPropertyAnimation *panim = qobject_cast<QQuickPropertyAnimation *>(animation);
if (panim && panim->target()) {
QObject *target = panim->target();
QString property = panim->property();
QVariant value = target->property(qPrintable(baseProperty(property)));
m_defaultValues.push_back(value);
} else {
m_defaultValues.push_back({});
}
}
}
QVector<QQuickAbstractAnimation *> NodeInstanceServer::animations() const
{
return m_animations;
}
QVariant NodeInstanceServer::animationDefaultValue(int index) const
{
return m_defaultValues.at(index);
}
} // namespace QmlDesigner