diff --git a/src/plugins/qmldesigner/designercore/include/modelcache.h b/src/plugins/qmldesigner/designercore/include/modelcache.h new file mode 100644 index 00000000000..bd4704294dd --- /dev/null +++ b/src/plugins/qmldesigner/designercore/include/modelcache.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2022 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. +** +****************************************************************************/ + +#pragma once + +#include + +#include + +#include + +#include +#include + +namespace QmlDesigner { + +template +class ModelCache +{ +public: + ModelCache(int max = 20) + : m_maxEntries(max) + {} + + void insert(Model *model, const DataType &data) + { + QObject::connect(model, &Model::destroyed, [this](QObject *o) { + QObject *deletedModel = o; + + if (deletedModel) { + m_content.remove(deletedModel); + m_queue.removeAll(deletedModel); + } + }); + + m_content.insert(model, data); + if (!m_queue.contains(model)) + m_queue.append(model); + if (m_queue.length() > m_maxEntries) { + QObject *first = m_queue.takeFirst(); + m_content.remove(first); + } + } + + Utils::optional take(Model *model) + { + if (!m_content.contains(model)) + return {}; + m_queue.removeOne(model); + return m_content.take(model); + } + +private: + QHash m_content; + QQueue m_queue; + int m_maxEntries = 20; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h index e29498e1438..48ecd4119f6 100644 --- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h +++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h @@ -27,6 +27,7 @@ #include "qmldesignercorelib_global.h" #include "abstractview.h" +#include "modelcache.h" #include #include @@ -232,11 +233,29 @@ private: // functions PropertyChangeFlags flags); private: + struct NodeInstanceCacheData + { + NodeInstanceCacheData(const QHash &i, + const QHash &p) + : instances(i) + , previewImages(p) + {} + + NodeInstanceCacheData() = default; + + QHash instances; + QHash previewImages; + }; + + QList loadInstancesFromCache(const QList &nodeList, + const NodeInstanceCacheData &cache); + QHash m_imageDataMap; NodeInstance m_rootNodeInstance; NodeInstance m_activeStateInstance; QHash m_nodeInstanceHash; + ModelCache m_nodeInstanceCache; QHash m_statePreviewImage; ConnectionManagerInterface &m_connectionManager; std::unique_ptr m_nodeInstanceServer; diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 5b83b637953..48391fe4b9c 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -265,6 +265,9 @@ void NodeInstanceView::modelAboutToBeDetached(Model * model) { m_connectionManager.setCrashCallback({}); + m_nodeInstanceCache.insert(model, + NodeInstanceCacheData(m_nodeInstanceHash, m_statePreviewImage)); + removeAllInstanceNodeRelationships(); if (m_nodeInstanceServer) { m_nodeInstanceServer->clearScene(createClearSceneCommand()); @@ -935,10 +938,16 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() QList nodeList = allModelNodes(); QList instanceList; - for (const ModelNode &node : std::as_const(nodeList)) { - NodeInstance instance = loadNode(node); - if (!isSkippedNode(node)) - instanceList.append(instance); + Utils::optional oldNodeInstanceHash = m_nodeInstanceCache.take(model()); + if (oldNodeInstanceHash + && oldNodeInstanceHash->instances.value(rootModelNode()).isValid()) { + instanceList = loadInstancesFromCache(nodeList, oldNodeInstanceHash.value()); + } else { + for (const ModelNode &node : std::as_const(nodeList)) { + NodeInstance instance = loadNode(node); + if (!isSkippedNode(node)) + instanceList.append(instance); + } } nodeList = filterNodesForSkipItems(nodeList); @@ -1942,4 +1951,32 @@ void NodeInstanceView::maybeResetOnPropertyChange(const PropertyName &name, cons resetPuppet(); } +QList NodeInstanceView::loadInstancesFromCache(const QList &nodeList, + const NodeInstanceCacheData &cache) +{ + QList instanceList; + + auto previews = cache.previewImages; + auto iterator = previews.begin(); + while (iterator != previews.end()) { + if (iterator.key().isValid()) + m_statePreviewImage.insert(iterator.key(), iterator.value()); + iterator++; + } + + for (const ModelNode &node : std::as_const(nodeList)) { + NodeInstance instance = cache.instances.value(node); + if (instance.isValid()) + insertInstanceRelationships(instance); + else + instance = loadNode(node); + + if (node.isRootNode()) + m_rootNodeInstance = instance; + if (!isSkippedNode(node)) + instanceList.append(instanceForModelNode(node)); + } + + return instanceList; } +} // namespace QmlDesigner