diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 7a38eb4c014..0e91850e82d 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -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 diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp index f43b5a96b1d..ee5c9af64c1 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -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 +#include +#include #include #include +#include #include -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); + } +} + } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h index bad4b081954..7bf68b6a748 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h @@ -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 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; }; } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui index afe83d257dd..fb45d9c19d2 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui @@ -14,21 +14,52 @@ Export QML - - + + - + + + + 0 + 0 + + + + Export path: + + - + + + + + + + 1000 + + + 0 + + + + - QDialogButtonBox::Cancel + QDialogButtonBox::Cancel|QDialogButtonBox::Close + + + Utils::PathChooser + QWidget +
utils/pathchooser.h
+ 1 +
+
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index 812898e378d..609da3f80e2 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -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 #include #include -#include - -#include 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 &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(this)); - connect(m_preprocessWatcher.get(), &QFutureWatcher::resultReadyAt, this, - [this](int index) { - emit qmlFileResult(m_preprocessWatcher->resultAt(index)); - }); - - connect(m_preprocessWatcher.get(), &QFutureWatcher::finished, this, - [this] () { m_currentState.change(ParsingState::PreProcessingFinished); }); - - QFuture 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); } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h index 3f13e85123e..9c2212f5e23 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h @@ -27,7 +27,6 @@ #include "assetexporterview.h" #include "utils/fileutils.h" -#include #include #include @@ -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> m_preprocessWatcher; }; QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp index 6ac8906946f..1758201bf7a 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.cpp @@ -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(); } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri index 8c8c5126599..d4ff10ec165 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.pri @@ -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 diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs index 538d7b19802..56c7659e102 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporterplugin.qbs @@ -45,6 +45,8 @@ QtcProduct { "componentexporter.h", "exportnotification.cpp", "exportnotification.h", + "filepathmodel.cpp", + "filepathmodel.h", "parsers/modelitemnodeparser.cpp", "parsers/modelitemnodeparser.h", "parsers/modelnodeparser.cpp", diff --git a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp new file mode 100644 index 00000000000..36c175414b5 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp @@ -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 +#include + +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 &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(this)); + connect(m_preprocessWatcher.get(), &QFutureWatcher::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::finished, + this, &FilePathModel::endResetModel); + + QFuture f = Utils::runAsync(&findQmlFiles, m_project); + m_preprocessWatcher->setFuture(f); +} + + +} diff --git a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h new file mode 100644 index 00000000000..91a800c0362 --- /dev/null +++ b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h @@ -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 +#include + +#include "utils/fileutils.h" + +#include +#include + +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> m_preprocessWatcher; + std::unordered_set m_skipped; + Utils::FilePaths m_files; +}; + +}