diff options
author | Tim Jenssen <tim.jenssen@qt.io> | 2017-10-27 17:13:18 +0200 |
---|---|---|
committer | Tim Jenssen <tim.jenssen@qt.io> | 2017-10-30 11:55:35 +0000 |
commit | 30d123e2ecf7717c7d4f2dc2dcca1856abb239d5 (patch) | |
tree | 7503e73041f2ac061602d7d1e041f7ca5087f672 /src/plugins/projectexplorer/jsonwizard | |
parent | 5c5bac67a4908718812f84899568691d11dc3c16 (diff) |
Wizards: add IconListField
- reuse some code from ComboBoxField throw an abstract ListField class
- ListField can handle more data like: icon, trToolTip
- fix disabledIndex in ComboBoxField
- adjust documentation
Change-Id: I00b6ab787fb2fad97dafff32786cf73c636c772d
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
Diffstat (limited to 'src/plugins/projectexplorer/jsonwizard')
5 files changed, 324 insertions, 112 deletions
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp index 6cf29aaff8..293714ccb6 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp @@ -33,9 +33,9 @@ #include <utils/fancylineedit.h> #include <utils/qtcassert.h> #include <utils/stringutils.h> -#include <utils/textfieldcombobox.h> #include <utils/theme/theme.h> +#include <QComboBox> #include <QCheckBox> #include <QApplication> #include <QDebug> @@ -46,6 +46,10 @@ #include <QVariant> #include <QVariantMap> #include <QVBoxLayout> +#include <QListView> +#include <QStandardItem> +#include <QItemSelectionModel> +#include <QDir> using namespace Utils; @@ -792,49 +796,52 @@ void CheckBoxField::initializeData(MacroExpander *expander) } // -------------------------------------------------------------------- -// ComboBoxFieldData: +// ListFieldData: // -------------------------------------------------------------------- -struct ComboBoxItem { - ComboBoxItem(const QString &k = QString(), const QString &v = QString(), const QVariant &c = true) : - key(k), value(v), condition(c) - { } - - QString key; - QString value; - QVariant condition; -}; - -ComboBoxItem parseComboBoxItem(const QVariant &item, QString *errorMessage) +std::unique_ptr<QStandardItem> createStandardItemFromListItem(const QVariant &item, QString *errorMessage) { if (item.type() == QVariant::List) { *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage", - "No lists allowed inside ComboBox items list."); - return ComboBoxItem(); - } else if (item.type() == QVariant::Map) { + "No JSON lists allowed inside List items."); + return {}; + } + auto standardItem = std::make_unique<QStandardItem>(); + if (item.type() == QVariant::Map) { QVariantMap tmp = item.toMap(); - QString key = JsonWizardFactory::localizedString(consumeValue(tmp, QLatin1String("trKey"), QString()).toString()); - QString value = consumeValue(tmp, QLatin1String("value"), QString()).toString(); - QVariant condition = consumeValue(tmp, QLatin1String("condition"), true); + const QString key = JsonWizardFactory::localizedString(consumeValue(tmp, "trKey", QString()).toString()); + const QString value = consumeValue(tmp, "value", key).toString(); + if (key.isNull() || key.isEmpty()) { *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage", - "No \"key\" found in ComboBox items."); - return ComboBoxItem(); + "No \"key\" found in List items."); + return {}; } - if (value.isNull()) - value = key; - return ComboBoxItem(key, value, condition); + standardItem->setText(key); + standardItem->setData(value, ListField::ValueRole); + standardItem->setData(consumeValue(tmp, "condition", true), ListField::ConditionRole); + standardItem->setData(consumeValue(tmp, "icon"), ListField::IconStringRole); + standardItem->setToolTip(JsonWizardFactory::localizedString(consumeValue(tmp, "trToolTip", QString()).toString())); + warnAboutUnsupportedKeys(tmp, QString(), "List"); } else { - QString keyvalue = item.toString(); - return ComboBoxItem(keyvalue, keyvalue); + const QString keyvalue = item.toString(); + standardItem->setText(keyvalue); + standardItem->setData(keyvalue, ListField::ValueRole); + standardItem->setData(true, ListField::ConditionRole); } + return standardItem; } -bool ComboBoxField::parseData(const QVariant &data, QString *errorMessage) +ListField::ListField() = default; + +ListField::~ListField() = default; + +bool ListField::parseData(const QVariant &data, QString *errorMessage) { if (data.type() != QVariant::Map) { *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage", - "ComboBox data is not an object."); + "%1(\"%2\") data is not an object.") + .arg(type(), name()); return false; } @@ -844,118 +851,235 @@ bool ComboBoxField::parseData(const QVariant &data, QString *errorMessage) m_index = consumeValue(tmp, "index", 0).toInt(&ok); if (!ok) { *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage", - "ComboBox(\"%1\") \"index\" is not an integer value.") - .arg(name()); + "%1(\"%2\") \"index\" is not an integer value.") + .arg(type(), name()); return false; } m_disabledIndex = consumeValue(tmp, "disabledIndex", -1).toInt(&ok); if (!ok) { *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage", - "ComboBox(\"%1\") \"disabledIndex\" is not an integer value.") - .arg(name()); + "%1(\"%2\") \"disabledIndex\" is not an integer value.") + .arg(type(), name()); return false; } - QVariant value = consumeValue(tmp, "items"); + const QVariant value = consumeValue(tmp, "items"); if (value.isNull()) { *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage", - "ComboBox(\"%1\") \"items\" missing.") - .arg(name()); + "%1(\"%2\") \"items\" missing.") + .arg(type(), name()); return false; } if (value.type() != QVariant::List) { *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage", - "ComboBox(\"%1\") \"items\" is not a list.") - .arg(name()); + "%1(\"%2\") \"items\" is not a JSON list.") + .arg(type(), name()); return false; } - foreach (const QVariant &i, value.toList()) { - ComboBoxItem keyValue = parseComboBoxItem(i, errorMessage); - if (keyValue.key.isNull()) - return false; // an error happened... - m_itemList.append(keyValue.key); - m_itemDataList.append(keyValue.value); - m_itemConditionList.append(keyValue.condition); + for (const QVariant &i : value.toList()) { + std::unique_ptr<QStandardItem> item = createStandardItemFromListItem(i, errorMessage); + QString test = item->text(); + QTC_ASSERT(!item || !item->text().isEmpty(), continue); + m_itemList.emplace_back(std::move(item)); } - if (m_itemConditionList.count() != m_itemDataList.count() - || m_itemConditionList.count() != m_itemList.count()) { - m_itemConditionList.clear(); - m_itemDataList.clear(); - m_itemList.clear(); - *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage", - "Internal Error: ComboBox(\"%1\") items lists got mixed up.") - .arg(name()); - return false; - } warnAboutUnsupportedKeys(tmp, name(), type()); return true; } -QWidget *ComboBoxField::createWidget(const QString &displayName, JsonFieldPage *page) + +bool ListField::validate(MacroExpander *expander, QString *message) { - Q_UNUSED(displayName); - Q_UNUSED(page); - return new TextFieldComboBox; + if (!JsonFieldPage::Field::validate(expander, message)) + return false; + + updateIndex(); + if (selectionModel()->hasSelection()) + return true; + return false; } -void ComboBoxField::setup(JsonFieldPage *page, const QString &name) +void ListField::initializeData(MacroExpander *expander) { - auto w = qobject_cast<TextFieldComboBox *>(widget()); - QTC_ASSERT(w, return); - page->registerFieldWithName(name, w, "indexText", SIGNAL(text4Changed(QString))); - QObject::connect(w, &TextFieldComboBox::text4Changed, - page, [page](QString) { page->completeChanged(); }); + QTC_ASSERT(widget(), return); + + QStandardItem *currentItem = m_index >= 0 ? m_itemList[uint(m_index)].get() : nullptr; + QList<QStandardItem*> expandedValuesItems; + expandedValuesItems.reserve(int(m_itemList.size())); + + QSize maxIconSize; + + for (const std::unique_ptr<QStandardItem> &item : m_itemList) { + bool condition = JsonWizard::boolFromVariant(item->data(ConditionRole), expander); + if (!condition) + continue; + QStandardItem *expandedValuesItem = item->clone(); + if (item.get() == currentItem) + currentItem = expandedValuesItem; + expandedValuesItem->setText(expander->expand(item->text())); + expandedValuesItem->setData(expander->expand(item->data(ValueRole).toString()), ValueRole); + expandedValuesItem->setData(expander->expand(item->data(IconStringRole).toString()), IconStringRole); + expandedValuesItem->setData(condition, ConditionRole); + + QString iconPath = expandedValuesItem->data(IconStringRole).toString(); + if (!iconPath.isEmpty()) { + if (JsonFieldPage *page = qobject_cast<JsonFieldPage*>(widget()->parentWidget())) { + const QString wizardDirectory = page->value("WizardDir").toString(); + iconPath = QDir::cleanPath(QDir(wizardDirectory).absoluteFilePath(iconPath)); + if (QFileInfo::exists(iconPath)) { + QIcon icon(iconPath); + expandedValuesItem->setIcon(icon); + addPossibleIconSize(icon); + } else { + qWarning().noquote() << QString("Icon file \"%1\" not found.").arg(QDir::toNativeSeparators(iconPath)); + } + } else { + qWarning().noquote() << QString("%1(\"%2\") has no parentWidget JsonFieldPage to get the icon path.").arg(type(), name()); + } + } + expandedValuesItems.append(expandedValuesItem); + } + + itemModel()->clear(); + itemModel()->appendColumn(expandedValuesItems); // inserts the first column + + selectionModel()->setCurrentIndex(itemModel()->indexFromItem(currentItem), QItemSelectionModel::ClearAndSelect); + + updateIndex(); } -bool ComboBoxField::validate(MacroExpander *expander, QString *message) +QStandardItemModel *ListField::itemModel() { - if (!JsonFieldPage::Field::validate(expander, message)) - return false; + if (!m_itemModel) + m_itemModel = new QStandardItemModel(widget()); + return m_itemModel; +} - auto w = qobject_cast<TextFieldComboBox *>(widget()); - QTC_ASSERT(w, return false); - if (!w->isEnabled() && m_disabledIndex >= 0 && m_savedIndex < 0) { - m_savedIndex = w->currentIndex(); - w->setCurrentIndex(m_disabledIndex); - } else if (w->isEnabled() && m_savedIndex >= 0) { - w->setCurrentIndex(m_savedIndex); +QItemSelectionModel *ListField::selectionModel() +{ + return m_selectionModel; +} + +void ListField::setSelectionModel(QItemSelectionModel *selectionModel) +{ + m_selectionModel = selectionModel; +} + +QSize ListField::maxIconSize() +{ + return m_maxIconSize; +} + +void ListField::addPossibleIconSize(const QIcon &icon) +{ + const QSize iconSize = icon.availableSizes().value(0); + if (iconSize.height() > m_maxIconSize.height()) + m_maxIconSize = iconSize; +} + +void ListField::updateIndex() +{ + if (!widget()->isEnabled() && m_disabledIndex >= 0 && m_savedIndex < 0) { + m_savedIndex = selectionModel()->currentIndex().row(); + selectionModel()->setCurrentIndex(itemModel()->index(m_disabledIndex, 0), QItemSelectionModel::ClearAndSelect); + } else if (widget()->isEnabled() && m_savedIndex >= 0) { + selectionModel()->setCurrentIndex(itemModel()->index(m_savedIndex, 0), QItemSelectionModel::ClearAndSelect); m_savedIndex = -1; } +} - return true; +void ComboBoxField::setup(JsonFieldPage *page, const QString &name) +{ + auto w = qobject_cast<QComboBox*>(widget()); + QTC_ASSERT(w, return); + w->setModel(itemModel()); + w->setInsertPolicy(QComboBox::NoInsert); + + QSizePolicy s = w->sizePolicy(); + s.setHorizontalPolicy(QSizePolicy::Expanding); + w->setSizePolicy(s); + + setSelectionModel(w->view()->selectionModel()); + + // the selectionModel does not behave like expected and wanted - so we block signals here + // (for example there was some losing focus thing when hovering over items, ...) + selectionModel()->blockSignals(true); + QObject::connect(w, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated), [w, this](int index) { + w->blockSignals(true); + selectionModel()->clearSelection(); + + selectionModel()->blockSignals(false); + selectionModel()->setCurrentIndex(w->model()->index(index, 0), + QItemSelectionModel::ClearAndSelect); + selectionModel()->blockSignals(true); + w->blockSignals(false); + }); + page->registerObjectAsFieldWithName<QItemSelectionModel>(name, selectionModel(), &QItemSelectionModel::selectionChanged, [this]() { + const QModelIndex i = selectionModel()->currentIndex(); + if (i.isValid()) + return i.data(ValueRole).toString(); + return QString(); + }); + QObject::connect(selectionModel(), &QItemSelectionModel::selectionChanged, page, [page]() { + emit page->completeChanged(); + }); +} + +QWidget *ComboBoxField::createWidget(const QString & /*displayName*/, JsonFieldPage * /*page*/) +{ + return new QComboBox; } void ComboBoxField::initializeData(MacroExpander *expander) { - auto w = qobject_cast<TextFieldComboBox *>(widget()); - QTC_ASSERT(widget(), return); - QStringList tmpItems - = Utils::transform(m_itemList, - [expander](const QString &i) { return expander->expand(i); }); - QStringList tmpData - = Utils::transform(m_itemDataList, - [expander](const QString &i) { return expander->expand(i); }); - QList<bool> tmpConditions - = Utils::transform(m_itemConditionList, - [expander](const QVariant &v) { return JsonWizard::boolFromVariant(v, expander); }); - - int index = m_index; - for (int i = tmpConditions.count() - 1; i >= 0; --i) { - if (!tmpConditions.at(i)) { - tmpItems.removeAt(i); - tmpData.removeAt(i); - if (i < index && index > 0) - --index; - } - } + ListField::initializeData(expander); + // refresh also the current text of the combobox + auto w = qobject_cast<QComboBox*>(widget()); + w->setCurrentIndex(selectionModel()->currentIndex().row()); +} - if (index < 0 || index >= tmpData.count()) - index = 0; - w->setItems(tmpItems, tmpData); - w->setInsertPolicy(QComboBox::NoInsert); - w->setCurrentIndex(index); +void IconListField::setup(JsonFieldPage *page, const QString &name) +{ + auto w = qobject_cast<QListView*>(widget()); + QTC_ASSERT(w, return); + + w->setViewMode(QListView::IconMode); + w->setMovement(QListView::Static); + w->setResizeMode(QListView::Adjust); + w->setSelectionRectVisible(false); + w->setWrapping(true); + w->setWordWrap(true); + + w->setModel(itemModel()); + setSelectionModel(w->selectionModel()); + page->registerObjectAsFieldWithName<QItemSelectionModel>(name, selectionModel(), &QItemSelectionModel::selectionChanged, [this]() { + const QModelIndex i = selectionModel()->currentIndex(); + if (i.isValid()) + return i.data(ValueRole).toString(); + return QString(); + }); + QObject::connect(selectionModel(), &QItemSelectionModel::selectionChanged, page, [page]() { + page->completeChanged(); + }); +} + +QWidget *IconListField::createWidget(const QString & /*displayName*/, JsonFieldPage * /*page*/) +{ + return new QListView; +} + +void IconListField::initializeData(MacroExpander *expander) +{ + ListField::initializeData(expander); + auto w = qobject_cast<QListView*>(widget()); + const int spacing = 4; + w->setSpacing(spacing); + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + // adding a third hight of the icon to see following items if there are some + w->setMinimumHeight(maxIconSize().height() + maxIconSize().height() / 3); + w->setIconSize(maxIconSize()); } // -------------------------------------------------------------------- diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.h b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.h index fb23876c76..7cdb50c544 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.h @@ -42,7 +42,6 @@ QT_END_NAMESPACE namespace Utils { class MacroExpander; -class TextFieldComboBox; } // namespace Utils namespace ProjectExplorer { diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h index c8f07f5c83..874ca8735d 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage_p.h @@ -32,6 +32,16 @@ #include <QWidget> #include <QString> #include <QVariant> +#include <QDir> + +#include <memory> +#include <vector> + +QT_BEGIN_NAMESPACE +class QStandardItem; +class QStandardItemModel; +class QItemSelectionModel; +QT_END_NAMESPACE namespace ProjectExplorer { @@ -170,25 +180,58 @@ private: bool m_isModified = false; }; -class ComboBoxField : public JsonFieldPage::Field +class ListField : public JsonFieldPage::Field { -private: +public: + enum SpecialRoles { + ValueRole = Qt::UserRole, + ConditionRole = Qt::UserRole + 1, + IconStringRole = Qt::UserRole + 2 + }; + ListField(); + virtual ~ListField() override; + + protected: bool parseData(const QVariant &data, QString *errorMessage) override; - QWidget *createWidget(const QString &displayName, JsonFieldPage *page) override; - - void setup(JsonFieldPage *page, const QString &name) override; + QWidget *createWidget(const QString &displayName, JsonFieldPage *page) override = 0; + void setup(JsonFieldPage *page, const QString &name) override = 0; bool validate(Utils::MacroExpander *expander, QString *message) override; void initializeData(Utils::MacroExpander *expander) override; + QStandardItemModel *itemModel(); + QItemSelectionModel *selectionModel(); + void setSelectionModel(QItemSelectionModel *selectionModel); + QSize maxIconSize(); + +private: + void addPossibleIconSize(const QIcon &icon); + void updateIndex(); - QStringList m_itemList; - QStringList m_itemDataList; - QVariantList m_itemConditionList; + std::vector<std::unique_ptr<QStandardItem>> m_itemList; + QStandardItemModel *m_itemModel = nullptr; + QItemSelectionModel *m_selectionModel = nullptr; int m_index = -1; int m_disabledIndex = -1; + QSize m_maxIconSize; mutable int m_savedIndex = -1; }; +class ComboBoxField : public ListField +{ +public: + void setup(JsonFieldPage *page, const QString &name) override; + QWidget *createWidget(const QString &displayName, JsonFieldPage *page) override; + void initializeData(Utils::MacroExpander *expander) override; +}; + +class IconListField : public ListField +{ +public: + void setup(JsonFieldPage *page, const QString &name) override; + QWidget *createWidget(const QString &displayName, JsonFieldPage *page) override; + void initializeData(Utils::MacroExpander *expander) override; +}; + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizard_test.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizard_test.cpp index 0ce5b08e04..34b45e2891 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizard_test.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizard_test.cpp @@ -34,6 +34,7 @@ #include <QCheckBox> #include <QLineEdit> #include <QComboBox> +#include <QListView> #include <functional> namespace { @@ -209,8 +210,8 @@ void ProjectExplorer::ProjectExplorerPlugin::testJsonWizardsLineEdit() void ProjectExplorer::ProjectExplorerPlugin::testJsonWizardsComboBox() { QString errorMessage; - QWidget parent; + const QJsonArray items({"abc", "cde", "fgh"}); QJsonObject disabledComboBoxObject = createWidget("ComboBox", "Disabled", QJsonObject({ {{"disabledIndex", 2}, {"items", items}} })); disabledComboBoxObject.insert("enabled", false); @@ -228,6 +229,8 @@ void ProjectExplorer::ProjectExplorerPlugin::testJsonWizardsComboBox() QComboBox *defaultComboBox = findComboBox(wizard, "Default"); QVERIFY(defaultComboBox); + QCOMPARE(defaultComboBox->count(), items.count()); + QCOMPARE(qPrintable(defaultComboBox->currentText()), "abc"); defaultComboBox->setCurrentIndex(2); QCOMPARE(qPrintable(defaultComboBox->currentText()), "fgh"); @@ -238,7 +241,49 @@ void ProjectExplorer::ProjectExplorerPlugin::testJsonWizardsComboBox() QComboBox *disabledComboBox = findComboBox(wizard, "Disabled"); QVERIFY(disabledComboBox); - QEXPECT_FAIL("", "This is wrong, since ComboBox got condition items", Continue); QCOMPARE(qPrintable(disabledComboBox->currentText()), "fgh"); +} + +void ProjectExplorer::ProjectExplorerPlugin::testJsonWizardsIconList() +{ + QString errorMessage; + QWidget parent; + + const QJsonArray items({ + QJsonObject{ + {"trKey", "item no1"}, + {"condition", true}, + {"icon", "../share/qtcreator/templates/wizards/qtquickstyleicons/default.png"} + + }, + QJsonObject{ + {"trKey", "item no2"}, + {"condition", false}, + {"icon", "not_existing_path"} + }, + QJsonObject{ + {"trKey", "item no3"}, + {"condition", true}, + {"trToolTip", "MyToolTip"}, + {"icon", "../share/qtcreator/templates/wizards/qtquickstyleicons/default.png"} + } + }); + + const QJsonArray widgets({ + createWidget("IconList", "Fancy", QJsonObject{{"index", -1}, {"items", items}}) + }); + + const QJsonObject pages = createFieldPageJsonObject(widgets); + const QJsonObject wizardObject = createGeneralWizard(pages); + JsonWizardFactory *factory = ProjectExplorer::JsonWizardFactory::createWizardFactory(wizardObject.toVariantMap(), QDir(), &errorMessage); + QVERIFY2(factory, qPrintable(errorMessage)); + Utils::Wizard *wizard = factory->runWizard(QString(), &parent, Core::Id(), QVariantMap()); + + auto view = wizard->findChild<QListView *>("FancyIconList"); + QCOMPARE(view->model()->rowCount(), 2); + QVERIFY(view->model()->index(0,0).data(Qt::DecorationRole).canConvert<QIcon>()); + QIcon icon = view->model()->index(0,0).data(Qt::DecorationRole).value<QIcon>(); + QVERIFY(!icon.isNull()); + QVERIFY(!wizard->page(0)->isComplete()); } diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory_p.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory_p.cpp index 58c0e20030..2e7c93b514 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory_p.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardpagefactory_p.cpp @@ -57,6 +57,7 @@ FieldPageFactory::FieldPageFactory() JsonFieldPage::registerFieldFactory(QLatin1String("PathChooser"), []() { return new PathChooserField; }); JsonFieldPage::registerFieldFactory(QLatin1String("CheckBox"), []() { return new CheckBoxField; }); JsonFieldPage::registerFieldFactory(QLatin1String("ComboBox"), []() { return new ComboBoxField; }); + JsonFieldPage::registerFieldFactory(QLatin1String("IconList"), []() { return new IconListField; }); } Utils::WizardPage *FieldPageFactory::create(JsonWizard *wizard, Core::Id typeId, const QVariant &data) |