forked from qt-creator/qt-creator
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:
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -45,6 +45,8 @@ QtcProduct {
|
||||
"componentexporter.h",
|
||||
"exportnotification.cpp",
|
||||
"exportnotification.h",
|
||||
"filepathmodel.cpp",
|
||||
"filepathmodel.h",
|
||||
"parsers/modelitemnodeparser.cpp",
|
||||
"parsers/modelitemnodeparser.h",
|
||||
"parsers/modelnodeparser.cpp",
|
||||
|
163
src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
Normal file
163
src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
}
|
60
src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h
Normal file
60
src/plugins/qmldesigner/assetexporterplugin/filepathmodel.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
Reference in New Issue
Block a user