From 8d1f4834b6fe79f992b3a46f8973d32dbc0dddb2 Mon Sep 17 00:00:00 2001 From: hjk <hjk@qt.io> Date: Tue, 4 Apr 2017 14:36:03 +0200 Subject: [PATCH] ProjectExplorer: Don't rebuild all projects' tree when one is closed Removes some quadratic-in-number-of-projects behavior on session close/switch. Change-Id: If93bb9a67b0bebddda5319a7594a99ae66f50f5a Reviewed-by: Tobias Hunger <tobias.hunger@qt.io> --- src/plugins/projectexplorer/project.cpp | 9 +- src/plugins/projectexplorer/projectmodels.cpp | 102 +++++++++++------- src/plugins/projectexplorer/projectmodels.h | 5 +- src/plugins/projectexplorer/projectnodes.h | 1 + 4 files changed, 76 insertions(+), 41 deletions(-) diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 578cc033ec..c1caa1bd27 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -477,10 +477,13 @@ void Project::setRootProjectNode(ProjectNode *root) ProjectNode *oldNode = d->m_rootProjectNode; d->m_rootProjectNode = root; - if (root) + if (root) { root->setParentFolderNode(d->m_containerNode); - ProjectTree::emitSubtreeChanged(root); - emit fileListChanged(); + // Only announce non-null root, null is only used when project is destroyed. + // In that case SessionManager::projectRemoved() triggers the update. + ProjectTree::emitSubtreeChanged(root); + emit fileListChanged(); + } delete oldNode; } diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index a733c9ae17..9a5bb11407 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -74,15 +74,14 @@ FlatModel::FlatModel(QObject *parent) : TreeModel<WrapperNode, WrapperNode>(new WrapperNode(nullptr), parent) { ProjectTree *tree = ProjectTree::instance(); - connect(tree, &ProjectTree::subtreeChanged, this, &FlatModel::update); + connect(tree, &ProjectTree::subtreeChanged, this, &FlatModel::updateSubtree); SessionManager *sm = SessionManager::instance(); - connect(sm, &SessionManager::projectRemoved, this, &FlatModel::update); - connect(sm, &SessionManager::sessionLoaded, this, &FlatModel::loadExpandData); + connect(sm, &SessionManager::projectRemoved, this, &FlatModel::handleProjectRemoved); + connect(sm, &SessionManager::aboutToLoadSession, this, &FlatModel::loadExpandData); connect(sm, &SessionManager::aboutToSaveSession, this, &FlatModel::saveExpandData); connect(sm, &SessionManager::projectAdded, this, &FlatModel::handleProjectAdded); connect(sm, &SessionManager::startupProjectChanged, this, [this] { layoutChanged(); }); - update(); } QVariant FlatModel::data(const QModelIndex &index, int role) const @@ -170,42 +169,27 @@ bool FlatModel::setData(const QModelIndex &index, const QVariant &value, int rol return true; } -void FlatModel::update() +void FlatModel::addOrRebuildProjectModel(Project *project) { - rebuildModel(); -} - -void FlatModel::rebuildModel() -{ - QList<Project *> projects = SessionManager::projects(); - - Utils::sort(projects, [](Project *p1, Project *p2) { - const int displayNameResult = caseFriendlyCompare(p1->displayName(), p2->displayName()); - if (displayNameResult != 0) - return displayNameResult < 0; - return p1 < p2; // sort by pointer value - }); + WrapperNode *container = nodeForProject(project); + if (container) { + container->removeChildren(); + } else { + container = new WrapperNode(project->containerNode()); + rootItem()->appendChild(container); + } QSet<Node *> seen; - rootItem()->removeChildren(); - for (Project *project : projects) { - WrapperNode *container = new WrapperNode(project->containerNode()); - - ProjectNode *projectNode = project->rootProjectNode(); - if (projectNode) { - addFolderNode(container, projectNode, &seen); - } else { - FileNode *projectFileNode = new FileNode(project->projectFilePath(), FileType::Project, false); - seen.insert(projectFileNode); - container->appendChild(new WrapperNode(projectFileNode)); - } - - container->sortChildren(&sortWrapperNodes); - rootItem()->appendChild(container); + if (ProjectNode *projectNode = project->rootProjectNode()) { + addFolderNode(container, projectNode, &seen); + } else { + FileNode *projectFileNode = new FileNode(project->projectFilePath(), FileType::Project, false); + seen.insert(projectFileNode); + container->appendChild(new WrapperNode(projectFileNode)); } - forAllItems([this](WrapperNode *node) { + container->forAllChildren([this](WrapperNode *node) { if (node->m_node) { const QString path = node->m_node->filePath().toString(); const QString displayName = node->m_node->displayName(); @@ -216,6 +200,37 @@ void FlatModel::rebuildModel() emit requestExpansion(node->index()); } }); + + const QString path = container->m_node->filePath().toString(); + const QString displayName = container->m_node->displayName(); + ExpandData ed(path, displayName); + if (m_toExpand.contains(ed)) + emit requestExpansion(container->index()); +} + +void FlatModel::updateSubtree(FolderNode *node) +{ + // FIXME: This is still excessive, should be limited to the affected subtree. + while (FolderNode *parent = node->parentFolderNode()) + node = parent; + if (ContainerNode *container = node->asContainerNode()) + addOrRebuildProjectModel(container->project()); +} + +void FlatModel::rebuildModel() +{ + QList<Project *> projects = SessionManager::projects(); + QTC_CHECK(projects.size() == rootItem()->childCount()); + + Utils::sort(projects, [](Project *p1, Project *p2) { + const int displayNameResult = caseFriendlyCompare(p1->displayName(), p2->displayName()); + if (displayNameResult != 0) + return displayNameResult < 0; + return p1 < p2; // sort by pointer value + }); + + for (Project *project : projects) + addOrRebuildProjectModel(project); } void FlatModel::onCollapsed(const QModelIndex &idx) @@ -238,9 +253,22 @@ ExpandData FlatModel::expandDataForNode(const Node *node) const void FlatModel::handleProjectAdded(Project *project) { - Node *node = project->containerNode(); - m_toExpand.insert(expandDataForNode(node)); - update(); + addOrRebuildProjectModel(project); +} + +void FlatModel::handleProjectRemoved(Project *project) +{ + destroyItem(nodeForProject(project)); +} + +WrapperNode *FlatModel::nodeForProject(Project *project) +{ + QTC_ASSERT(project, return nullptr); + ContainerNode *containerNode = project->containerNode(); + QTC_ASSERT(containerNode, return nullptr); + return rootItem()->findFirstLevelChild([containerNode](WrapperNode *node) { + return node->m_node == containerNode; + }); } void FlatModel::loadExpandData() diff --git a/src/plugins/projectexplorer/projectmodels.h b/src/plugins/projectexplorer/projectmodels.h index b59e08e04f..18129a7ddc 100644 --- a/src/plugins/projectexplorer/projectmodels.h +++ b/src/plugins/projectexplorer/projectmodels.h @@ -90,7 +90,7 @@ private: static const QLoggingCategory &logger(); - void update(); + void updateSubtree(FolderNode *node); void rebuildModel(); void addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<Node *> *seen); @@ -98,6 +98,9 @@ private: void loadExpandData(); void saveExpandData(); void handleProjectAdded(Project *project); + void handleProjectRemoved(Project *project); + WrapperNode *nodeForProject(Project *project); + void addOrRebuildProjectModel(Project *project); QTimer m_timer; QSet<ExpandData> m_toExpand; diff --git a/src/plugins/projectexplorer/projectnodes.h b/src/plugins/projectexplorer/projectnodes.h index efb94d7079..ac82e9b02c 100644 --- a/src/plugins/projectexplorer/projectnodes.h +++ b/src/plugins/projectexplorer/projectnodes.h @@ -318,6 +318,7 @@ public: const ContainerNode *asContainerNode() const final { return this; } ProjectNode *rootProjectNode() const; + Project *project() const { return m_project; } private: Project *m_project; -- GitLab