diff --git a/src/plugins/cmakeprojectmanager/cmakeproject.h b/src/plugins/cmakeprojectmanager/cmakeproject.h index 09a10c29a6a..6187f92e19f 100644 --- a/src/plugins/cmakeprojectmanager/cmakeproject.h +++ b/src/plugins/cmakeprojectmanager/cmakeproject.h @@ -116,7 +116,7 @@ private: void handleActiveBuildConfigurationChanged(); void handleParsingStarted(const Internal::CMakeBuildConfiguration *bc); void handleTreeScanningFinished(); - void updateProjectData(Internal::CMakeBuildConfiguration *cmakeBc); + void updateProjectData(Internal::CMakeBuildConfiguration *bc); void handleParsingError(Internal::CMakeBuildConfiguration *bc); void updateQmlJSCodeModel(); diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index d3582241b5b..4a11a1b32bd 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -85,7 +85,8 @@ public: enum ModelRoles { // Absolute file path FilePathRole = QFileSystemModel::FilePathRole, - EnabledRole + EnabledRole, + isParsingRole }; Project(const QString &mimeType, const Utils::FileName &fileName, diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index 6faf60158ad..54b982524b4 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -32,15 +32,18 @@ #include "session.h" #include +#include #include #include +#include -#include #include #include #include #include +#include + using namespace Utils; namespace ProjectExplorer { @@ -106,10 +109,19 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const } case Qt::DecorationRole: { if (folderNode) { - result = folderNode->icon(); + static QIcon emptyIcon = Utils::Icons::EMPTY16.icon(); if (ContainerNode *containerNode = folderNode->asContainerNode()) { - if (ProjectNode *projectNode = containerNode->rootProjectNode()) - result = projectNode->icon(); + WrapperNode *wn = wrapperForNode(node); + Project *project = Utils::findOrDefault(SessionManager::projects(), [this, wn](const Project *p) { + return nodeForProject(p) == wn; + }); + if (project->isParsing()) + result = emptyIcon; + else + result = containerNode->rootProjectNode() ? containerNode->rootProjectNode()->icon() : + folderNode->icon(); + } else { + result = folderNode->icon(); } } else { result = Core::FileIconProvider::icon(node->filePath().toString()); @@ -133,6 +145,17 @@ QVariant FlatModel::data(const QModelIndex &index, int role) const result = node->isEnabled(); break; } + case Project::isParsingRole: { + const Project *project = nullptr; + if (node->asContainerNode()) { + WrapperNode *wn = wrapperForNode(node); + project = Utils::findOrDefault(SessionManager::projects(), [this, wn](const Project *p) { + return nodeForProject(p) == wn; + }); + } + result = project ? project->isParsing() : false; + break; + } } } @@ -216,6 +239,13 @@ void FlatModel::addOrRebuildProjectModel(Project *project) emit requestExpansion(container->index()); } +void FlatModel::parsingStateChanged(Project *project) +{ + const WrapperNode *const node = nodeForProject(project); + const QModelIndex nodeIdx = indexForNode(node->m_node); + emit dataChanged(nodeIdx, nodeIdx); +} + void FlatModel::updateSubtree(FolderNode *node) { // FIXME: This is still excessive, should be limited to the affected subtree. @@ -261,6 +291,12 @@ ExpandData FlatModel::expandDataForNode(const Node *node) const void FlatModel::handleProjectAdded(Project *project) { + QTC_ASSERT(project, return); + + connect(project, &Project::parsingStarted, + this, [this, project]() { parsingStateChanged(project); }); + connect(project, &Project::parsingFinished, + this, [this, project]() { parsingStateChanged(project); }); addOrRebuildProjectModel(project); } @@ -269,7 +305,7 @@ void FlatModel::handleProjectRemoved(Project *project) destroyItem(nodeForProject(project)); } -WrapperNode *FlatModel::nodeForProject(Project *project) +WrapperNode *FlatModel::nodeForProject(const Project *project) const { QTC_ASSERT(project, return nullptr); ContainerNode *containerNode = project->containerNode(); diff --git a/src/plugins/projectexplorer/projectmodels.h b/src/plugins/projectexplorer/projectmodels.h index 18129a7ddce..1a1d02b620a 100644 --- a/src/plugins/projectexplorer/projectmodels.h +++ b/src/plugins/projectexplorer/projectmodels.h @@ -99,9 +99,11 @@ private: void saveExpandData(); void handleProjectAdded(Project *project); void handleProjectRemoved(Project *project); - WrapperNode *nodeForProject(Project *project); + WrapperNode *nodeForProject(const Project *project) const; void addOrRebuildProjectModel(Project *project); + void parsingStateChanged(Project *project); + QTimer m_timer; QSet m_toExpand; }; diff --git a/src/plugins/projectexplorer/projecttreewidget.cpp b/src/plugins/projectexplorer/projecttreewidget.cpp index 2a98fbe86e7..3cd66273016 100644 --- a/src/plugins/projectexplorer/projecttreewidget.cpp +++ b/src/plugins/projectexplorer/projecttreewidget.cpp @@ -40,23 +40,28 @@ #include #include -#include #include +#include +#include #include #include -#include +#include #include #include #include #include +#include #include #include +#include + using namespace Core; using namespace ProjectExplorer; using namespace ProjectExplorer::Internal; +using namespace Utils; QList ProjectTreeWidget::m_projectTreeWidgets; @@ -65,22 +70,78 @@ namespace { class ProjectTreeItemDelegate : public QStyledItemDelegate { public: - ProjectTreeItemDelegate(QObject *parent) : QStyledItemDelegate(parent) - { } + ProjectTreeItemDelegate(QTreeView *view) : QStyledItemDelegate(view), + m_view(view) + { + connect(m_view->model(), &QAbstractItemModel::modelReset, + this, &ProjectTreeItemDelegate::deleteAllIndicators); - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const + // Actually this only needs to delete the indicators in the effected rows and *after* it, + // but just be lazy and nuke all the indicators. + connect(m_view->model(), &QAbstractItemModel::rowsAboutToBeRemoved, + this, &ProjectTreeItemDelegate::deleteAllIndicators); + connect(m_view->model(), &QAbstractItemModel::rowsAboutToBeInserted, + this, &ProjectTreeItemDelegate::deleteAllIndicators); + } + + ~ProjectTreeItemDelegate() override + { + deleteAllIndicators(); + } + + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { QStyleOptionViewItem opt = option; if (!index.data(Project::EnabledRole).toBool()) opt.state &= ~QStyle::State_Enabled; QStyledItemDelegate::paint(painter, opt, index); + + if (index.data(Project::isParsingRole).toBool()) { + initStyleOption(&opt, index); + ProgressIndicatorPainter *indicator = findOrCreateIndicatorPainter(index); + + QStyle *style = option.widget ? option.widget->style() : QApplication::style(); + const QRect rect = style->subElementRect(QStyle::SE_ItemViewItemDecoration, &opt, opt.widget); + + indicator->paint(*painter, rect); + } else { + delete m_indicators.value(index); + m_indicators.remove(index); + } } + +private: + ProgressIndicatorPainter *findOrCreateIndicatorPainter(const QModelIndex &index) const + { + ProgressIndicatorPainter *indicator = m_indicators.value(index); + if (!indicator) + indicator = createIndicatorPainter(index); + return indicator; + } + + ProgressIndicatorPainter *createIndicatorPainter(const QModelIndex &index) const + { + auto indicator = new ProgressIndicatorPainter(ProgressIndicatorSize::Small); + indicator->setUpdateCallback([index, this]() { m_view->update(index); }); + indicator->startAnimation(); + m_indicators.insert(index, indicator); + return indicator; + } + + void deleteAllIndicators() + { + qDeleteAll(m_indicators); + m_indicators.clear(); + } + + mutable QHash m_indicators; + QTreeView *m_view; }; bool debug = false; } -class ProjectTreeView : public Utils::NavigationTreeView +class ProjectTreeView : public NavigationTreeView { public: ProjectTreeView() @@ -138,7 +199,7 @@ public: connect(newModel, &QAbstractItemModel::rowsRemoved, this, &ProjectTreeView::invalidateSize); } - Utils::NavigationTreeView::setModel(newModel); + NavigationTreeView::setModel(newModel); } ~ProjectTreeView() @@ -150,7 +211,7 @@ public: int sizeHintForColumn(int column) const override { if (m_cachedSize < 0) - m_cachedSize = Utils::NavigationTreeView::sizeHintForColumn(column); + m_cachedSize = NavigationTreeView::sizeHintForColumn(column); return m_cachedSize; } @@ -172,7 +233,7 @@ ProjectTreeWidget::ProjectTreeWidget(QWidget *parent) : QWidget(parent) m_model = new FlatModel(this); m_view = new ProjectTreeView; m_view->setModel(m_model); - m_view->setItemDelegate(new ProjectTreeItemDelegate(this)); + m_view->setItemDelegate(new ProjectTreeItemDelegate(m_view)); setFocusProxy(m_view); m_view->installEventFilter(this); @@ -211,7 +272,7 @@ ProjectTreeWidget::ProjectTreeWidget(QWidget *parent) : QWidget(parent) m_model, &FlatModel::onCollapsed); m_toggleSync = new QToolButton; - m_toggleSync->setIcon(Utils::Icons::LINK.icon()); + m_toggleSync->setIcon(Icons::LINK.icon()); m_toggleSync->setCheckable(true); m_toggleSync->setChecked(autoSynchronization()); m_toggleSync->setToolTip(tr("Synchronize with Editor")); @@ -270,7 +331,7 @@ void ProjectTreeWidget::rowsInserted(const QModelIndex &parent, int start, int e } } -Node *ProjectTreeWidget::nodeForFile(const Utils::FileName &fileName) +Node *ProjectTreeWidget::nodeForFile(const FileName &fileName) { Node *bestNode = nullptr; int bestNodeExpandCount = INT_MAX; @@ -328,7 +389,7 @@ void ProjectTreeWidget::setAutoSynchronization(bool sync) if (m_autoSync) { // sync from document manager - Utils::FileName fileName; + FileName fileName; if (IDocument *doc = EditorManager::currentDocument()) fileName = doc->filePath(); if (!currentNode() || currentNode()->filePath() != fileName) @@ -348,7 +409,7 @@ void ProjectTreeWidget::editCurrentItem() m_view->edit(m_view->selectionModel()->currentIndex()); } -void ProjectTreeWidget::renamed(const Utils::FileName &oldPath, const Utils::FileName &newPath) +void ProjectTreeWidget::renamed(const FileName &oldPath, const FileName &newPath) { update(); Q_UNUSED(oldPath); @@ -400,8 +461,8 @@ void ProjectTreeWidget::showMessage(Node *node, const QString &message) m_view->scrollTo(idx); QPoint pos = m_view->mapToGlobal(m_view->visualRect(idx).bottomLeft()); - pos -= Utils::ToolTip::offsetFromPosition(); - Utils::ToolTip::show(pos, message); + pos -= ToolTip::offsetFromPosition(); + ToolTip::show(pos, message); } void ProjectTreeWidget::showContextMenu(const QPoint &pos) @@ -459,7 +520,7 @@ NavigationView ProjectTreeWidgetFactory::createWidget() n.widget = ptw; auto filter = new QToolButton; - filter->setIcon(Utils::Icons::FILTER.icon()); + filter->setIcon(Icons::FILTER.icon()); filter->setToolTip(tr("Filter Tree")); filter->setPopupMode(QToolButton::InstantPopup); filter->setProperty("noArrow", true);