diff options
author | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2019-10-02 15:35:43 +0300 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@qt.io> | 2019-10-04 11:30:35 +0000 |
commit | f58dca918f12c118046f4f498b05e42da4974607 (patch) | |
tree | d41535c2433fc1681cd43163f5d3ad43bccedae4 /src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp | |
parent | a0f3ce69bd20aeb77f76186a7773c4b4af5bf984 (diff) |
Add import options to asset import dialog
Available import options are queried from the importer and displayed
on the import dialog.
Change-Id: Id47dde29f41a1c91042623ebbd1156c77434e3a3
Fixes: QDS-1101
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Diffstat (limited to 'src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp')
-rw-r--r-- | src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp | 270 |
1 files changed, 258 insertions, 12 deletions
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp index 0917a22abd..5f72ef3750 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp @@ -30,15 +30,20 @@ #include "model.h" #include "utils/outputformatter.h" +#include "theme.h" #include <QtCore/qfileinfo.h> #include <QtCore/qdir.h> #include <QtCore/qloggingcategory.h> #include <QtCore/qtimer.h> +#include <QtCore/qjsonarray.h> #include <QtWidgets/qpushbutton.h> -#include <QtWidgets/qformlayout.h> +#include <QtWidgets/qgridlayout.h> #include <QtWidgets/qlabel.h> +#include <QtWidgets/qcheckbox.h> +#include <QtWidgets/qspinbox.h> #include <QtWidgets/qscrollbar.h> +#include <QtWidgets/qtabbar.h> namespace QmlDesigner { @@ -83,8 +88,6 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im if (skipSome) addWarning("Cannot import 3D and other assets simultaneously. Skipping non-3D assets."); - // Import button will be used in near future when we add import options. Hide for now. - ui->buttonBox->button(QDialogButtonBox::Ok)->hide(); ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Import")); connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onImport); @@ -137,8 +140,236 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im } m_quick3DImportPath = candidatePath; - // Queue import immediately until we have some options - QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::onImport); + // Create UI controls for options + if (!importFiles.isEmpty()) { + QJsonObject supportedOptions = QJsonObject::fromVariantMap( + m_importer.supportedOptions(importFiles[0])); + m_importOptions = supportedOptions.value("options").toObject(); + const QJsonObject groups = supportedOptions.value("groups").toObject(); + + const int labelWidth = 210; + const int valueWidth = 75; + const int groupIndent = 10; + const int columnSpacing = 10; + const int rowHeight = 26; + int rowIndex[2] = {0, 0}; + + // First index has ungrouped widgets, rest are groups + // First item in each real group is group label + QVector<QVector<QPair<QWidget *, QWidget *>>> widgets; + QHash<QString, int> groupIndexMap; + QHash<QString, QPair<QWidget *, QWidget *>> optionToWidgetsMap; + QHash<QString, QJsonArray> conditionMap; + QHash<QString, QString> optionToGroupMap; + + auto layout = new QGridLayout(ui->optionsAreaContents); + layout->setColumnMinimumWidth(0, groupIndent); + layout->setColumnMinimumWidth(1, labelWidth); + layout->setColumnMinimumWidth(2, valueWidth); + layout->setColumnMinimumWidth(3, columnSpacing); + layout->setColumnMinimumWidth(4, groupIndent); + layout->setColumnMinimumWidth(5, labelWidth); + layout->setColumnMinimumWidth(6, valueWidth); + + widgets.append(QVector<QPair<QWidget *, QWidget *>>()); + + for (const auto group : groups) { + const QString name = group.toObject().value("name").toString(); + const QJsonArray items = group.toObject().value("items").toArray(); + for (const auto item : items) + optionToGroupMap.insert(item.toString(), name); + auto groupLabel = new QLabel(name, ui->optionsAreaContents); + QFont labelFont = groupLabel->font(); + labelFont.setBold(true); + groupLabel->setFont(labelFont); + widgets.append({{groupLabel, nullptr}}); + groupIndexMap.insert(name, widgets.size() - 1); + } + + const auto optKeys = m_importOptions.keys(); + for (const auto &optKey : optKeys) { + QJsonObject optObj = m_importOptions.value(optKey).toObject(); + const QString optName = optObj.value("name").toString(); + const QString optDesc = optObj.value("description").toString(); + const QString optType = optObj.value("type").toString(); + QJsonObject optRange = optObj.value("range").toObject(); + QJsonValue optValue = optObj.value("value"); + QJsonArray conditions = optObj.value("conditions").toArray(); + + QWidget *optControl = nullptr; + if (optType == "Boolean") { + auto *optCheck = new QCheckBox(ui->optionsAreaContents); + optCheck->setChecked(optValue.toBool()); + optControl = optCheck; + QObject::connect(optCheck, &QCheckBox::toggled, [this, optCheck, optKey]() { + QJsonObject optObj = m_importOptions.value(optKey).toObject(); + QJsonValue value(optCheck->isChecked()); + optObj.insert("value", value); + m_importOptions.insert(optKey, optObj); + }); + } else if (optType == "Real") { + auto *optSpin = new QDoubleSpinBox(ui->optionsAreaContents); + double min = -999999999.; + double max = 999999999.; + double step = 1.; + int decimals = 3; + if (!optRange.isEmpty()) { + min = optRange.value("minimum").toDouble(); + max = optRange.value("maximum").toDouble(); + // Ensure step is reasonable for small ranges + double range = max - min; + while (range <= 10.) { + step /= 10.; + range *= 10.; + if (step < 0.02) + ++decimals; + } + + } + optSpin->setRange(min, max); + optSpin->setDecimals(decimals); + optSpin->setValue(optValue.toDouble()); + optSpin->setSingleStep(step); + optControl = optSpin; + QObject::connect(optSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), + [this, optSpin, optKey]() { + QJsonObject optObj = m_importOptions.value(optKey).toObject(); + QJsonValue value(optSpin->value()); + optObj.insert("value", value); + m_importOptions.insert(optKey, optObj); + }); + } else { + qWarning() << __FUNCTION__ << "Unsupported option type:" << optType; + continue; + } + + if (!conditions.isEmpty()) + conditionMap.insert(optKey, conditions); + + auto *optLabel = new QLabel(ui->optionsAreaContents); + optLabel->setText(optName); + optLabel->setToolTip(optDesc); + optControl->setToolTip(optDesc); + + const QString &groupName = optionToGroupMap.value(optKey); + if (!groupName.isEmpty() && groupIndexMap.contains(groupName)) + widgets[groupIndexMap[groupName]].append({optLabel, optControl}); + else + widgets[0].append({optLabel, optControl}); + optionToWidgetsMap.insert(optKey, {optLabel, optControl}); + } + + // Add option widgets to layout. Grouped options are added to the tops of the columns + for (int i = 1; i < widgets.size(); ++i) { + int col = rowIndex[1] < rowIndex[0] ? 1 : 0; + const auto &groupWidgets = widgets[i]; + if (!groupWidgets.isEmpty()) { + // First widget in each group is the group label + layout->addWidget(groupWidgets[0].first, rowIndex[col], col * 4, 1, 3); + layout->setRowMinimumHeight(rowIndex[col],rowHeight); + ++rowIndex[col]; + for (int j = 1; j < groupWidgets.size(); ++j) { + layout->addWidget(groupWidgets[j].first, rowIndex[col], col * 4 + 1); + layout->addWidget(groupWidgets[j].second, rowIndex[col], col * 4 + 2); + layout->setRowMinimumHeight(rowIndex[col],rowHeight); + ++rowIndex[col]; + } + } + } + + // Ungrouped options are spread evenly under the groups + int totalRowCount = (rowIndex[0] + rowIndex[1] + widgets[0].size() + 1) / 2; + for (const auto &widgets : qAsConst(widgets[0])) { + int col = rowIndex[0] < totalRowCount ? 0 : 1; + layout->addWidget(widgets.first, rowIndex[col], col * 4, 1, 2); + layout->addWidget(widgets.second, rowIndex[col], col * 4 + 2); + layout->setRowMinimumHeight(rowIndex[col],rowHeight); + ++rowIndex[col]; + } + + // Handle conditions + QHash<QString, QJsonArray>::const_iterator it = conditionMap.begin(); + while (it != conditionMap.end()) { + const QString &option = it.key(); + const QJsonArray &conditions = it.value(); + const auto &conWidgets = optionToWidgetsMap.value(option); + QWidget *conLabel = conWidgets.first; + QWidget *conControl = conWidgets.second; + // Currently we only support single condition per option, though the schema allows for + // multiple, as no real life option currently has multiple conditions and connections + // get complicated if we need to comply to multiple conditions. + if (!conditions.isEmpty() && conLabel && conControl) { + const auto &conObj = conditions[0].toObject(); + const QString optItem = conObj.value("property").toString(); + const auto &optWidgets = optionToWidgetsMap.value(optItem); + const QString optMode = conObj.value("mode").toString(); + const QVariant optValue = conObj.value("value").toVariant(); + enum class Mode { equals, notEquals, greaterThan, lessThan }; + Mode mode; + if (optMode == "NotEquals") + mode = Mode::notEquals; + else if (optMode == "GreaterThan") + mode = Mode::greaterThan; + else if (optMode == "LessThan") + mode = Mode::lessThan; + else + mode = Mode::equals; // Default to equals + + if (optWidgets.first && optWidgets.second) { + auto optCb = qobject_cast<QCheckBox *>(optWidgets.second); + auto optSpin = qobject_cast<QDoubleSpinBox *>(optWidgets.second); + if (optCb) { + auto enableConditionally = [optValue](QCheckBox *cb, QWidget *w1, + QWidget *w2, Mode mode) { + bool equals = (mode == Mode::equals) == optValue.toBool(); + bool enable = cb->isChecked() == equals; + w1->setEnabled(enable); + w2->setEnabled(enable); + }; + enableConditionally(optCb, conLabel, conControl, mode); + QObject::connect( + optCb, &QCheckBox::toggled, + [optCb, conLabel, conControl, mode, enableConditionally]() { + enableConditionally(optCb, conLabel, conControl, mode); + }); + } + if (optSpin) { + auto enableConditionally = [optValue](QDoubleSpinBox *sb, QWidget *w1, + QWidget *w2, Mode mode) { + bool enable = false; + double value = optValue.toDouble(); + if (mode == Mode::equals) + enable = qFuzzyCompare(value, sb->value()); + else if (mode == Mode::notEquals) + enable = !qFuzzyCompare(value, sb->value()); + else if (mode == Mode::greaterThan) + enable = sb->value() > value; + else if (mode == Mode::lessThan) + enable = sb->value() < value; + w1->setEnabled(enable); + w2->setEnabled(enable); + }; + enableConditionally(optSpin, conLabel, conControl, mode); + QObject::connect( + optSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged), + [optSpin, conLabel, conControl, mode, enableConditionally]() { + enableConditionally(optSpin, conLabel, conControl, mode); + }); + } + } + } + ++it; + } + + ui->optionsAreaContents->setLayout(layout); + ui->optionsAreaContents->resize( + groupIndent * 2 + labelWidth * 2 + valueWidth * 2 + columnSpacing, + rowHeight * qMax(rowIndex[0], rowIndex[1])); + } + + ui->optionsArea->setStyleSheet("QScrollArea {background-color: transparent}"); + ui->optionsAreaContents->setStyleSheet( + "QWidget#optionsAreaContents {background-color: transparent}"); connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onClose); @@ -155,6 +386,10 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(const QStringList &im this, &ItemLibraryAssetImportDialog::onImportFinished); connect(&m_importer, &ItemLibraryAssetImporter::progressChanged, this, &ItemLibraryAssetImportDialog::setImportProgress); + + addInfo(tr("Select import options and press \"Import\" to import the following files:")); + for (const auto &file : m_quick3DFiles) + addInfo(file); } ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog() @@ -162,9 +397,18 @@ ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog() delete ui; } -void ItemLibraryAssetImportDialog::setImportUiState(bool importing) +void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event) + int scrollBarHeight = ui->optionsArea->horizontalScrollBar()->isVisible() + ? ui->optionsArea->horizontalScrollBar()->height() : 0; + ui->tabWidget->setMaximumHeight(ui->optionsAreaContents->height() + scrollBarHeight + + ui->tabWidget->tabBar()->height() + 8); + ui->optionsArea->resize(ui->tabWidget->currentWidget()->size()); +} + +void ItemLibraryAssetImportDialog::setCloseButtonState(bool importing) { - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!importing); ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(true); ui->buttonBox->button(QDialogButtonBox::Close)->setText(importing ? tr("Cancel") : tr("Close")); } @@ -186,12 +430,14 @@ void ItemLibraryAssetImportDialog::addInfo(const QString &info, const QString &s void ItemLibraryAssetImportDialog::onImport() { - setImportUiState(true); + ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + setCloseButtonState(true); ui->progressBar->setValue(0); - ui->plainTextEdit->clear(); - if (!m_quick3DFiles.isEmpty()) - m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath); + if (!m_quick3DFiles.isEmpty()) { + m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath, + m_importOptions.toVariantMap()); + } } void ItemLibraryAssetImportDialog::setImportProgress(int value, const QString &text) @@ -212,7 +458,7 @@ void ItemLibraryAssetImportDialog::onImportNearlyFinished() void ItemLibraryAssetImportDialog::onImportFinished() { - setImportUiState(false); + setCloseButtonState(false); if (m_importer.isCancelled()) { QString interruptStr = tr("Import interrupted."); addError(interruptStr); |