AssetExporter: Let user select files to export

Refine asset exporter dialog. Add progress bar, export notification,
path chooser and other small UI changes

Task-number: QDS-1560
Change-Id: Iaba61575581171e7e1495ebfaf2e345546fb7400
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Vikas Pachdha
2020-06-09 00:23:11 +02:00
parent 7f1b286264
commit 1b6a0a290b
11 changed files with 411 additions and 125 deletions

View File

@@ -51,6 +51,7 @@ add_qtc_plugin(assetexporterplugin
assetexporterplugin/assetexportpluginconstants.h
assetexporterplugin/componentexporter.h assetexporterplugin/componentexporter.cpp
assetexporterplugin/exportnotification.h assetexporterplugin/exportnotification.cpp
assetexporterplugin/filepathmodel.h assetexporterplugin/filepathmodel.cpp
assetexporterplugin/parsers/modelitemnodeparser.h assetexporterplugin/parsers/modelitemnodeparser.cpp
assetexporterplugin/parsers/modelnodeparser.h assetexporterplugin/parsers/modelnodeparser.cpp
assetexporterplugin/assetexporterplugin.qrc

View File

@@ -23,44 +23,97 @@
**
****************************************************************************/
#include "assetexportdialog.h"
#include "ui_assetexportdialog.h"
#include "ui_assetexportdialog.h"
#include "assetexportpluginconstants.h"
#include "filepathmodel.h"
#include "projectexplorer/task.h"
#include "projectexplorer/taskhub.h"
#include "utils/fileutils.h"
#include "utils/outputformatter.h"
#include <QPushButton>
#include <QListView>
#include <QPlainTextEdit>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QScrollBar>
#include <algorithm>
namespace QmlDesigner {
namespace {
static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString &str,
Utils::OutputFormat format) {
if (!formatter)
return;
QPlainTextEdit *edit = formatter->plainTextEdit();
QScrollBar *scroll = edit->verticalScrollBar();
bool isAtBottom = scroll && scroll->value() == scroll->maximum();
QString msg = str + "\n";
formatter->appendMessage(msg, format);
if (isAtBottom)
scroll->setValue(scroll->maximum());
}
}
using namespace ProjectExplorer;
namespace QmlDesigner {
AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath,
AssetExporter &assetExporter, QWidget *parent) :
AssetExporter &assetExporter, FilePathModel &model,
QWidget *parent) :
QDialog(parent),
m_assetExporter(assetExporter),
m_filePathModel(model),
m_ui(new Ui::AssetExportDialog),
m_model(this)
m_filesView(new QListView),
m_exportLogs(new QPlainTextEdit),
m_outputFormatter(new Utils::OutputFormatter())
{
m_ui->setupUi(this);
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
m_ui->filesView->setModel(&m_model);
m_ui->stackedWidget->addWidget(m_filesView);
m_filesView->setModel(&m_filePathModel);
m_exportLogs->setReadOnly(true);
m_outputFormatter->setPlainTextEdit(m_exportLogs);
m_ui->stackedWidget->addWidget(m_exportLogs);
switchView(false);
connect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, [this]() {
m_assetExporter.cancel();
});
m_exportBtn = m_ui->buttonBox->addButton(tr("Export"), QDialogButtonBox::AcceptRole);
m_exportBtn->setEnabled(false);
connect(m_exportBtn, &QPushButton::clicked, this, &AssetExportDialog::onExport);
m_ui->exportPathEdit->setText(exportPath.toString());
connect(&m_assetExporter, &AssetExporter::qmlFileResult, this, [this] (const Utils::FilePath &path) {
m_qmlFiles.append(path);
QStringList files = m_model.stringList();
files.append(path.toString());
m_model.setStringList(files);
connect(&m_filePathModel, &FilePathModel::modelReset, this, [this]() {
m_ui->exportProgress->setRange(0, 1000);
m_ui->exportProgress->setValue(0);
m_exportBtn->setEnabled(true);
});
connect(m_ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, [this]() {
close();
});
m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(false);
m_ui->exportPathEdit->setFileName(exportPath);
m_ui->exportPathEdit->setPromptDialogTitle(tr("Choose Export Path"));
connect(&m_assetExporter, &AssetExporter::stateChanged,
this, &AssetExportDialog::onExportStateChanged);
connect(&m_assetExporter, &AssetExporter::exportProgressChanged,
this, &AssetExportDialog::updateExportProgress);
connect(TaskHub::instance(), &TaskHub::taskAdded, this, &AssetExportDialog::onTaskAdded);
m_ui->exportProgress->setRange(0,0);
}
AssetExportDialog::~AssetExportDialog()
@@ -70,25 +123,61 @@ AssetExportDialog::~AssetExportDialog()
void AssetExportDialog::onExport()
{
m_assetExporter.exportQml(m_qmlFiles,
Utils::FilePath::fromString(m_ui->exportPathEdit->text()));
switchView(true);
updateExportProgress(0.0);
TaskHub::clearTasks(Constants::TASK_CATEGORY_ASSET_EXPORT);
m_exportLogs->clear();
m_assetExporter.exportQml(m_filePathModel.files(), m_ui->exportPathEdit->fileName());
}
void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newState)
{
switch (newState) {
case AssetExporter::ParsingState::PreProcessing:
m_model.setStringList({});
break;
case AssetExporter::ParsingState::ExportingDone:
m_exportBtn->setVisible(false);
m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(true);
QMessageBox::information(this, tr("QML Export"), tr("Done"));
break;
default:
break;
}
m_exportBtn->setEnabled(newState == AssetExporter::ParsingState::PreProcessingFinished ||
newState == AssetExporter::ParsingState::ExportingDone);
m_exportBtn->setEnabled(newState == AssetExporter::ParsingState::ExportingDone);
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_assetExporter.isBusy());
}
void AssetExportDialog::updateExportProgress(double value)
{
value = std::max(0.0, std::min(1.0, value));
m_ui->exportProgress->setValue(std::round(value * 1000));
}
void AssetExportDialog::switchView(bool showExportView)
{
if (showExportView)
m_ui->stackedWidget->setCurrentWidget(m_exportLogs);
else
m_ui->stackedWidget->setCurrentWidget(m_filesView);
}
void AssetExportDialog::onTaskAdded(const ProjectExplorer::Task &task)
{
Utils::OutputFormat format = Utils::NormalMessageFormat;
if (task.category == Constants::TASK_CATEGORY_ASSET_EXPORT) {
switch (task.type) {
case ProjectExplorer::Task::Error:
format = Utils::StdErrFormat;
break;
case ProjectExplorer::Task::Warning:
format = Utils::StdOutFormat;
break;
default:
format = Utils::NormalMessageFormat;
}
addFormattedMessage(m_outputFormatter, task.description, format);
}
}
}

View File

@@ -34,13 +34,24 @@
QT_BEGIN_NAMESPACE
class QPushButton;
class QListView;
class QPlainTextEdit;
QT_END_NAMESPACE
namespace Ui {
class AssetExportDialog;
}
namespace Utils {
class OutputFormatter;
}
namespace ProjectExplorer {
class Task;
}
namespace QmlDesigner {
class FilePathModel;
class AssetExportDialog : public QDialog
{
@@ -48,19 +59,24 @@ class AssetExportDialog : public QDialog
public:
explicit AssetExportDialog(const Utils::FilePath &exportPath, AssetExporter &assetExporter,
QWidget *parent = nullptr);
FilePathModel& model, QWidget *parent = nullptr);
~AssetExportDialog();
private:
void onExport();
void onExportStateChanged(AssetExporter::ParsingState newState);
void updateExportProgress(double value);
void switchView(bool showExportView);
void onTaskAdded(const ProjectExplorer::Task &task);
private:
AssetExporter &m_assetExporter;
FilePathModel &m_filePathModel;
std::unique_ptr<Ui::AssetExportDialog> m_ui;
QPushButton *m_exportBtn = nullptr;
QStringListModel m_model;
Utils::FilePaths m_qmlFiles;
QListView *m_filesView = nullptr;
QPlainTextEdit *m_exportLogs = nullptr;
Utils::OutputFormatter *m_outputFormatter = nullptr;
};
}

View File

@@ -14,21 +14,52 @@
<string>Export QML</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QListView" name="filesView"/>
<item row="0" column="1">
<widget class="Utils::PathChooser" name="exportPathEdit" native="true"/>
</item>
<item row="0" column="0">
<widget class="QLineEdit" name="exportPathEdit"/>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Export path:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<item row="1" column="0" colspan="2">
<widget class="QStackedWidget" name="stackedWidget"/>
</item>
<item row="2" column="0" colspan="2">
<widget class="QProgressBar" name="exportProgress">
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel</set>
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Utils::PathChooser</class>
<extends>QWidget</extends>
<header location="global">utils/pathchooser.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -26,21 +26,11 @@
#include "componentexporter.h"
#include "exportnotification.h"
#include "plaintexteditmodifier.h"
#include "rewriterview.h"
#include "projectexplorer/project.h"
#include "projectexplorer/projectnodes.h"
#include "utils/runextensions.h"
#include "utils/qtcassert.h"
#include <QJsonArray>
#include <QJsonDocument>
#include <QLoggingCategory>
#include <QMessageBox>
#include <QPlainTextEdit>
using namespace ProjectExplorer;
@@ -48,48 +38,6 @@ namespace {
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.assetExporter", QtInfoMsg)
Q_LOGGING_CATEGORY(loggerWarn, "qtc.designer.assetExportPlugin.assetExporter", QtWarningMsg)
Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.assetExporter", QtCriticalMsg)
void findQmlFiles(QFutureInterface<Utils::FilePath> &f, const Project *project)
{
if (!project && !f.isCanceled())
f.reportFinished({});
int index = 0;
Utils::FilePaths qmlFiles = project->files([&f, &index](const Node* node) ->bool {
if (f.isCanceled())
return false;
Utils::FilePath path = node->filePath();
bool isComponent = !path.fileName().isEmpty() && path.fileName().front().isUpper();
if (isComponent && node->filePath().endsWith(".ui.qml"))
f.reportResult(path, index++);
return true;
});
f.reportFinished();
}
//static QmlDesigner::Model* createModel(const Utils::FilePath &fileName)
//{
// QmlDesigner::Model *model = QmlDesigner::Model::create("Item", 2, 7);
// Utils::FileReader reader;
// QTC_ASSERT(reader.fetch(fileName.toString()), return nullptr);
// auto textEdit = new QPlainTextEdit;
// textEdit->setPlainText(QString::fromUtf8(reader.data()));
// auto modifier = new QmlDesigner::NotIndentingTextEditModifier(textEdit);
// modifier->setParent(model);
// auto rewriterView = new QmlDesigner::RewriterView(QmlDesigner::RewriterView::Validate, model);
// rewriterView->setCheckSemanticErrors(false);
// rewriterView->setTextModifier(modifier);
// model->attachView(rewriterView);
// QTC_ASSERT(rewriterView->rootModelNode().isValid(), return nullptr);
// return model;
//}
}
namespace QmlDesigner {
@@ -109,29 +57,6 @@ AssetExporter::~AssetExporter()
cancel();
}
bool AssetExporter::preProcessProject()
{
if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() &&
!m_preprocessWatcher->isFinished()) {
qCDebug(loggerInfo) << "Previous pre-processing not finished.";
return false;
}
m_currentState.change(ParsingState::PreProcessing);
m_preprocessWatcher.reset(new QFutureWatcher<Utils::FilePath>(this));
connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::resultReadyAt, this,
[this](int index) {
emit qmlFileResult(m_preprocessWatcher->resultAt(index));
});
connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::finished, this,
[this] () { m_currentState.change(ParsingState::PreProcessingFinished); });
QFuture<Utils::FilePath> f = Utils::runAsync(&findQmlFiles, m_project);
m_preprocessWatcher->setFuture(f);
return true;
}
void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath,
bool exportAssets)
{
@@ -139,6 +64,7 @@ void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::Fil
.arg(exportPath.toUserOutput())
.arg(exportAssets? tr("Yes") : tr("No")));
// TODO Asset export
notifyProgress(0.0);
Q_UNUSED(exportAssets);
m_exportFiles = qmlFiles;
m_components = QJsonArray();
@@ -149,18 +75,12 @@ void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::Fil
void AssetExporter::cancel()
{
ExportNotification::addInfo("Cancelling export.");
if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() &&
!m_preprocessWatcher->isFinished()) {
m_preprocessWatcher->cancel();
m_preprocessWatcher->waitForFinished();
}
// TODO Cancel export
}
bool AssetExporter::isBusy() const
{
return m_currentState == AssetExporter::ParsingState::PreProcessing ||
m_currentState == AssetExporter::ParsingState::Parsing ||
return m_currentState == AssetExporter::ParsingState::Parsing ||
m_currentState == AssetExporter::ParsingState::ExportingAssets ||
m_currentState == AssetExporter::ParsingState::WritingJson;
}
@@ -190,6 +110,11 @@ void AssetExporter::notifyLoadError(AssetExporterView::LoadState state)
ExportNotification::addError(tr("Loading QML failed. %1").arg(errorStr));
}
void AssetExporter::notifyProgress(double value) const
{
emit exportProgressChanged(value);
}
void AssetExporter::onQmlFileLoaded()
{
QTC_ASSERT(m_view && m_view->model(), qCDebug(loggerError) << "Null model"; return);
@@ -206,6 +131,7 @@ void AssetExporter::triggerLoadNextFile()
void AssetExporter::loadNextFile()
{
if (m_exportFiles.isEmpty()) {
notifyProgress(0.8);
m_currentState.change(ParsingState::ParsingFinished);
writeMetadata();
return;
@@ -220,8 +146,9 @@ void AssetExporter::loadNextFile()
void AssetExporter::writeMetadata() const
{
Utils::FilePath metadataPath = m_exportPath.pathAppended(m_exportPath.fileName() + ".metadata");
ExportNotification::addInfo(tr("Writing metadata to file %1.").
arg(m_exportPath.toUserOutput()));
arg(metadataPath.toUserOutput()));
m_currentState.change(ParsingState::WritingJson);
QJsonObject jsonRoot; // TODO: Write plugin info to root
jsonRoot.insert("artboards", m_components);
@@ -229,13 +156,14 @@ void AssetExporter::writeMetadata() const
if (doc.isNull() || doc.isEmpty()) {
ExportNotification::addError(tr("Empty JSON document."));
} else {
Utils::FileSaver saver(m_exportPath.toString(), QIODevice::Text);
Utils::FileSaver saver(metadataPath.toString(), QIODevice::Text);
saver.write(doc.toJson(QJsonDocument::Indented));
if (!saver.finalize()) {
ExportNotification::addError(tr("Writing metadata failed. %1").
arg(saver.errorString()));
}
}
notifyProgress(1.0);
m_currentState.change(ParsingState::ExportingDone);
}

View File

@@ -27,7 +27,6 @@
#include "assetexporterview.h"
#include "utils/fileutils.h"
#include <QFutureWatcher>
#include <QJsonArray>
#include <QJsonObject>
@@ -51,8 +50,6 @@ public:
enum class ParsingState {
Idle = 0,
PreProcessing,
PreProcessingFinished,
Parsing,
ParsingFinished,
ExportingAssets,
@@ -65,21 +62,22 @@ public:
QObject *parent = nullptr);
~AssetExporter();
bool preProcessProject();
void exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath, bool exportAssets = false);
void exportQml(const Utils::FilePaths &qmlFiles, const Utils::FilePath &exportPath,
bool exportAssets = false);
void cancel();
bool isBusy() const;
signals:
void qmlFileResult(Utils::FilePath);
void stateChanged(ParsingState);
void exportProgressChanged(double) const;
private:
ParsingState currentState() const { return m_currentState.m_state; }
void exportComponent(const ModelNode &rootNode);
void writeMetadata() const;
void notifyLoadError(AssetExporterView::LoadState state);
void notifyProgress(double value) const;
void triggerLoadNextFile();
void loadNextFile();
@@ -98,7 +96,6 @@ private:
Utils::FilePaths m_exportFiles;
Utils::FilePath m_exportPath;
QJsonArray m_components;
std::unique_ptr<QFutureWatcher<Utils::FilePath>> m_preprocessWatcher;
};
QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s);

View File

@@ -29,6 +29,7 @@
#include "assetexportdialog.h"
#include "assetexporter.h"
#include "assetexporterview.h"
#include "filepathmodel.h"
#include "componentexporter.h"
#include "parsers/modelitemnodeparser.h"
@@ -89,14 +90,10 @@ void AssetExporterPlugin::onExport()
if (!startupProject)
return;
FilePathModel model(startupProject);
auto exportDir = startupProject->projectFilePath().parentDir();
if (!exportDir.toFileInfo().isRoot())
exportDir = exportDir.parentDir();
auto defaultMetadataPath = exportDir.pathAppended(startupProject->displayName() + ".metadata");
AssetExporter assetExporter(m_view, startupProject);
AssetExportDialog assetExporterDialog(defaultMetadataPath, assetExporter);
assetExporter.preProcessProject();
AssetExportDialog assetExporterDialog(exportDir, assetExporter, model);
assetExporterDialog.exec();
}

View File

@@ -14,6 +14,7 @@ HEADERS += \
assetexportpluginconstants.h \
componentexporter.h \
exportnotification.h \
filepathmodel.h \
parsers/modelitemnodeparser.h \
parsers/modelnodeparser.h
@@ -24,6 +25,7 @@ SOURCES += \
assetexporterview.cpp \
componentexporter.cpp \
exportnotification.cpp \
filepathmodel.cpp \
parsers/modelitemnodeparser.cpp \
parsers/modelnodeparser.cpp

View File

@@ -45,6 +45,8 @@ QtcProduct {
"componentexporter.h",
"exportnotification.cpp",
"exportnotification.h",
"filepathmodel.cpp",
"filepathmodel.h",
"parsers/modelitemnodeparser.cpp",
"parsers/modelitemnodeparser.h",
"parsers/modelnodeparser.cpp",

View File

@@ -0,0 +1,163 @@
/****************************************************************************
**
** Copyright (C) 2020 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 "filepathmodel.h"
#include "exportnotification.h"
#include "projectexplorer/project.h"
#include "projectexplorer/projectnodes.h"
#include "utils/runextensions.h"
#include <QLoggingCategory>
#include <QTimer>
using namespace ProjectExplorer;
namespace {
Q_LOGGING_CATEGORY(loggerError, "qtc.designer.assetExportPlugin.filePathModel", QtCriticalMsg)
Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.filePathModel", QtInfoMsg)
void findQmlFiles(QFutureInterface<Utils::FilePath> &f, const Project *project)
{
if (!project && !f.isCanceled())
f.reportFinished({});
int index = 0;
Utils::FilePaths qmlFiles = project->files([&f, &index](const Node* node) ->bool {
if (f.isCanceled())
return false;
Utils::FilePath path = node->filePath();
bool isComponent = !path.fileName().isEmpty() && path.fileName().front().isUpper();
if (isComponent && node->filePath().endsWith(".ui.qml"))
f.reportResult(path, index++);
return true;
});
f.reportFinished();
}
}
namespace QmlDesigner {
FilePathModel::FilePathModel(ProjectExplorer::Project *project, QObject *parent)
: QAbstractListModel(parent),
m_project(project)
{
QTimer::singleShot(0, this, &FilePathModel::processProject);
}
FilePathModel::~FilePathModel()
{
if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() &&
!m_preprocessWatcher->isFinished()) {
ExportNotification::addInfo(tr("Canceling QML files preparation."));
m_preprocessWatcher->cancel();
m_preprocessWatcher->waitForFinished();
qCDebug(loggerInfo) << "Canceling QML files preparation done.";
}
}
Qt::ItemFlags FilePathModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags itemFlags = QAbstractListModel::flags(index);
if (index.isValid())
itemFlags |= Qt::ItemIsUserCheckable;
return itemFlags;
}
int FilePathModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid())
return m_files.count();
return 0;
}
QVariant FilePathModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return {};
switch (role) {
case Qt::DisplayRole:
return m_files[index.row()].toUserOutput();
case Qt::CheckStateRole:
return m_skipped.count(m_files[index.row()]) ? Qt::Unchecked : Qt::Checked;
default:
break;
}
return {};
}
bool FilePathModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (!index.isValid() || role != Qt::CheckStateRole)
return false;
const Utils::FilePath path = m_files[index.row()];
if (value == Qt::Checked)
m_skipped.erase(path);
else
m_skipped.insert(path);
emit dataChanged(index, index);
return true;
}
Utils::FilePaths FilePathModel::files() const
{
Utils::FilePaths selectedPaths;
std::copy_if(m_files.begin(), m_files.end(), std::back_inserter(selectedPaths),
[this](const Utils::FilePath &path) {
return !m_skipped.count(path);
});
return selectedPaths;
}
void FilePathModel::processProject()
{
if (m_preprocessWatcher && !m_preprocessWatcher->isCanceled() &&
!m_preprocessWatcher->isFinished()) {
qCDebug(loggerError) << "Previous model load not finished.";
return;
}
beginResetModel();
m_preprocessWatcher.reset(new QFutureWatcher<Utils::FilePath>(this));
connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::resultReadyAt, this,
[this](int resultIndex) {
beginInsertRows(index(0, 0) , m_files.count(), m_files.count());
m_files.append(m_preprocessWatcher->resultAt(resultIndex));
endInsertRows();
});
connect(m_preprocessWatcher.get(), &QFutureWatcher<Utils::FilePath>::finished,
this, &FilePathModel::endResetModel);
QFuture<Utils::FilePath> f = Utils::runAsync(&findQmlFiles, m_project);
m_preprocessWatcher->setFuture(f);
}
}

View File

@@ -0,0 +1,60 @@
/****************************************************************************
**
** Copyright (C) 2020 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 <QAbstractListModel>
#include <QFutureWatcher>
#include "utils/fileutils.h"
#include <memory>
#include <unordered_set>
namespace ProjectExplorer {
class Project;
}
namespace QmlDesigner {
class FilePathModel : public QAbstractListModel
{
public:
FilePathModel(ProjectExplorer::Project *project, QObject *parent = nullptr);
~FilePathModel() override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
Utils::FilePaths files() const;
private:
void processProject();
ProjectExplorer::Project *m_project = nullptr;
std::unique_ptr<QFutureWatcher<Utils::FilePath>> m_preprocessWatcher;
std::unordered_set<Utils::FilePath> m_skipped;
Utils::FilePaths m_files;
};
}