aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/qtcreator/qmldesigner/newprojectdialog/NewProjectDialog.qml2
-rw-r--r--share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml10
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp53
-rw-r--r--src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h2
-rw-r--r--src/plugins/studiowelcome/CMakeLists.txt4
-rw-r--r--src/plugins/studiowelcome/algorithm.h108
-rw-r--r--src/plugins/studiowelcome/newprojectmodel.cpp108
-rw-r--r--src/plugins/studiowelcome/presetmodel.cpp156
-rw-r--r--src/plugins/studiowelcome/presetmodel.h (renamed from src/plugins/studiowelcome/newprojectmodel.h)100
-rw-r--r--src/plugins/studiowelcome/qdsnewdialog.cpp62
-rw-r--r--src/plugins/studiowelcome/qdsnewdialog.h23
-rw-r--r--src/plugins/studiowelcome/recentpresets.cpp110
-rw-r--r--src/plugins/studiowelcome/recentpresets.h59
-rw-r--r--src/plugins/studiowelcome/screensizemodel.h2
-rw-r--r--src/plugins/studiowelcome/studiowelcome.pro6
-rw-r--r--src/plugins/studiowelcome/studiowelcome.qbs6
-rw-r--r--src/plugins/studiowelcome/stylemodel.h2
-rw-r--r--src/plugins/studiowelcome/wizardfactories.cpp55
-rw-r--r--src/plugins/studiowelcome/wizardfactories.h24
-rw-r--r--src/plugins/studiowelcome/wizardhandler.cpp51
-rw-r--r--src/plugins/studiowelcome/wizardhandler.h13
-rw-r--r--tests/auto/qml/qmldesigner/wizard/CMakeLists.txt7
-rw-r--r--tests/auto/qml/qmldesigner/wizard/presetmodel-test.cpp234
-rw-r--r--tests/auto/qml/qmldesigner/wizard/recentpresets-test.cpp239
-rw-r--r--tests/auto/qml/qmldesigner/wizard/test-main.cpp52
-rw-r--r--tests/auto/qml/qmldesigner/wizard/test-utilities.h3
-rw-r--r--tests/auto/qml/qmldesigner/wizard/wizardfactories-test.cpp133
27 files changed, 1355 insertions, 269 deletions
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/NewProjectDialog.qml b/share/qtcreator/qmldesigner/newprojectdialog/NewProjectDialog.qml
index aa5b346b4e..9176af9e34 100644
--- a/share/qtcreator/qmldesigner/newprojectdialog/NewProjectDialog.qml
+++ b/share/qtcreator/qmldesigner/newprojectdialog/NewProjectDialog.qml
@@ -185,7 +185,7 @@ Item {
anchors.fill: parent
onClicked: {
tabBarRow.currIndex = index
- projectModel.setPage(index)
+ presetModel.setPage(index)
projectView.currentIndex = 0
projectView.currentIndexChanged()
diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml
index 4270c0946a..e011057892 100644
--- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml
+++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/NewProjectView.qml
@@ -49,17 +49,17 @@ GridView {
}
]
- model: projectModel
+ model: presetModel
// called by onModelReset and when user clicks on an item, or when the header item is changed.
onCurrentIndexChanged: {
- dialogBox.selectedProject = projectView.currentIndex
- var source = dialogBox.currentProjectQmlPath()
+ dialogBox.selectedPreset = projectView.currentIndex
+ var source = dialogBox.currentPresetQmlPath()
loader.source = source
}
Connections {
- target: projectModel
+ target: presetModel
// called when data is set (setWizardFactories)
function onModelReset() {
@@ -76,7 +76,7 @@ GridView {
background: null
function fontIconCode(index) {
- var code = projectModel.fontIconCode(index)
+ var code = presetModel.fontIconCode(index)
return code ? code : StudioTheme.Constants.wizardsUnknown
}
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp
index 5439d98d2b..946d2d92ab 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp
+++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.cpp
@@ -278,6 +278,59 @@ QVariant JsonWizardFactory::getDataValue(const QLatin1String &key, const QVarian
return retVal;
}
+std::pair<int, QStringList> JsonWizardFactory::screenSizeInfoFromPage(const QString &pageType) const
+{
+ /* Retrieving the ScreenFactor "trKey" values from pages[i]/data[j]/data["items"], where
+ * pages[i] is the page of type `pageType` and data[j] is the data item with name ScreenFactor
+ */
+
+ const Utils::Id id = Utils::Id::fromString(Constants::PAGE_ID_PREFIX + pageType);
+
+ const auto it = std::find_if(std::cbegin(m_pages), std::cend(m_pages), [&id](const Page &page) {
+ return page.typeId == id;
+ });
+
+ if (it == std::cend(m_pages))
+ return {};
+
+ const QVariant data = it->data;
+ if (data.type() != QVariant::List)
+ return {};
+
+ const QVariant screenFactorField = Utils::findOrDefault(data.toList(),
+ [](const QVariant &field) {
+ const QVariantMap m = field.toMap();
+ return "ScreenFactor" == m["name"];
+ });
+
+ if (screenFactorField.type() != QVariant::Map)
+ return {};
+
+ const QVariant screenFactorData = screenFactorField.toMap()["data"];
+ if (screenFactorData.type() != QVariant::Map)
+ return {};
+
+ const QVariantMap screenFactorDataMap = screenFactorData.toMap();
+ if (not screenFactorDataMap.contains("items"))
+ return {};
+
+ bool ok = false;
+ const int index = screenFactorDataMap["index"].toInt(&ok);
+ const QVariantList items = screenFactorDataMap["items"].toList();
+ if (items.isEmpty())
+ return {};
+
+ QStringList values = Utils::transform(items, [](const QVariant &item) {
+ const QVariantMap m = item.toMap();
+ return m["trKey"].toString();
+ });
+
+ if (values.isEmpty())
+ return {};
+
+ return std::make_pair(index, values);
+}
+
JsonWizardFactory::Page JsonWizardFactory::parsePage(const QVariant &value, QString *errorMessage)
{
JsonWizardFactory::Page p;
diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h
index 2ecabe6bc3..347692bf65 100644
--- a/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h
+++ b/src/plugins/projectexplorer/jsonwizard/jsonwizardfactory.h
@@ -83,6 +83,8 @@ public:
bool isAvailable(Utils::Id platformId) const override;
+ std::pair<int, QStringList> screenSizeInfoFromPage(const QString &pageType) const;
+
private:
Utils::Wizard *runWizardImpl(const Utils::FilePath &path, QWidget *parent, Utils::Id platform,
const QVariantMap &variables, bool showWizard = true) override;
diff --git a/src/plugins/studiowelcome/CMakeLists.txt b/src/plugins/studiowelcome/CMakeLists.txt
index e1f899d2fd..48c07d15b9 100644
--- a/src/plugins/studiowelcome/CMakeLists.txt
+++ b/src/plugins/studiowelcome/CMakeLists.txt
@@ -6,14 +6,16 @@ add_qtc_plugin(StudioWelcome
SOURCES
studiowelcomeplugin.cpp studiowelcomeplugin.h
newprojectdialogimageprovider.cpp newprojectdialogimageprovider.h
- newprojectmodel.cpp newprojectmodel.h
+ presetmodel.cpp presetmodel.h
examplecheckout.cpp examplecheckout.h
studiowelcome_global.h
qdsnewdialog.cpp qdsnewdialog.h
wizardfactories.cpp wizardfactories.h
createproject.cpp createproject.h
wizardhandler.cpp wizardhandler.h
+ recentpresets.cpp recentpresets.h
screensizemodel.h
+ algorithm.h
stylemodel.h stylemodel.cpp
studiowelcome.qrc
"${PROJECT_SOURCE_DIR}/src/share/3rdparty/studiofonts/studiofonts.qrc"
diff --git a/src/plugins/studiowelcome/algorithm.h b/src/plugins/studiowelcome/algorithm.h
new file mode 100644
index 0000000000..b7e53da5f1
--- /dev/null
+++ b/src/plugins/studiowelcome/algorithm.h
@@ -0,0 +1,108 @@
+
+/****************************************************************************
+**
+** Copyright (C) 2021 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 <utils/algorithm.h>
+
+namespace Utils {
+
+//////// FIND
+template<typename C, typename F>
+[[nodiscard]] typename Utils::optional<typename C::value_type> findOptional(const C &container,
+ F function)
+{
+ auto begin = std::cbegin(container);
+ auto end = std::cend(container);
+
+ auto it = std::find_if(begin, end, function);
+ return it == end ? nullopt : make_optional(*it);
+}
+
+///////// FILTER
+template<typename C, typename T = typename C::value_type>
+[[nodiscard]] C filterOut(const C &container, const T &value = T())
+{
+ C out;
+ std::copy_if(std::begin(container), std::end(container), inserter(out), [&](const auto &item) {
+ return item != value;
+ });
+ return out;
+}
+
+template<typename C>
+[[nodiscard]] C filtered(const C &container)
+{
+ return filterOut(container, typename C::value_type{});
+}
+
+/////// MODIFY
+template<typename SC, typename C>
+void concat(C &out, const SC &container)
+{
+ std::copy(std::begin(container), std::end(container), inserter(out));
+}
+
+template<typename C, typename T>
+void erase_one(C &container, const T &value)
+{
+ typename C::const_iterator i = std::find(std::cbegin(container), std::cend(container), value);
+ if (i == std::cend(container))
+ return;
+
+ container.erase(i);
+}
+
+template<typename C, typename T>
+void prepend(C &container, const T &value)
+{
+ container.insert(std::cbegin(container), value);
+}
+
+/////// OTHER
+template<typename RC, typename SC>
+[[nodiscard]] RC flatten(const SC &container)
+{
+ RC result;
+
+ for (const auto &innerContainer : container)
+ concat(result, innerContainer);
+
+ return result;
+}
+
+template<template<typename, typename...> class C,
+ typename T,
+ typename... TArgs,
+ typename RT = typename std::decay_t<T>::value_type,
+ typename RC = C<RT>>
+[[nodiscard]] auto flatten(const C<T, TArgs...> &container)
+{
+ using SC = C<T, TArgs...>;
+ return flatten<RC, SC>(container);
+}
+
+} // namespace Utils
diff --git a/src/plugins/studiowelcome/newprojectmodel.cpp b/src/plugins/studiowelcome/newprojectmodel.cpp
deleted file mode 100644
index 3d5d9246d2..0000000000
--- a/src/plugins/studiowelcome/newprojectmodel.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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 "newprojectmodel.h"
-
-using namespace StudioWelcome;
-
-/****************** BaseNewProjectModel ******************/
-
-BaseNewProjectModel::BaseNewProjectModel(QObject *parent)
- : QAbstractListModel(parent)
-{}
-
-QHash<int, QByteArray> BaseNewProjectModel::roleNames() const
-{
- QHash<int, QByteArray> roleNames;
- roleNames[Qt::UserRole] = "name";
- return roleNames;
-}
-
-void BaseNewProjectModel::setProjects(const ProjectsByCategory &projectsByCategory)
-{
- beginResetModel();
-
- for (auto &[id, category] : projectsByCategory) {
- m_categories.push_back(category.name);
- m_projects.push_back(category.items);
- }
-
- endResetModel();
-}
-
-/****************** NewProjectCategoryModel ******************/
-
-NewProjectCategoryModel::NewProjectCategoryModel(QObject *parent)
- : BaseNewProjectModel(parent)
-{}
-
-int NewProjectCategoryModel::rowCount(const QModelIndex &) const
-{
- return static_cast<int>(categories().size());
-}
-
-QVariant NewProjectCategoryModel::data(const QModelIndex &index, int role) const
-{
- Q_UNUSED(role)
- return categories().at(index.row());
-}
-
-/****************** NewProjectModel ******************/
-
-NewProjectModel::NewProjectModel(QObject *parent)
- : BaseNewProjectModel(parent)
-{}
-
-int NewProjectModel::rowCount(const QModelIndex &) const
-{
- if (projects().empty())
- return 0;
-
- return static_cast<int>(projectsOfCurrentCategory().size());
-}
-
-QVariant NewProjectModel::data(const QModelIndex &index, int role) const
-{
- Q_UNUSED(role)
- return projectsOfCurrentCategory().at(index.row()).name;
-}
-
-void NewProjectModel::setPage(int index)
-{
- beginResetModel();
-
- m_page = static_cast<size_t>(index);
-
- endResetModel();
-}
-
-QString NewProjectModel::fontIconCode(int index) const
-{
- Utils::optional<ProjectItem> projectItem = project(index);
- if (!projectItem)
- return "";
-
- return projectItem->fontIconCode;
-}
diff --git a/src/plugins/studiowelcome/presetmodel.cpp b/src/plugins/studiowelcome/presetmodel.cpp
new file mode 100644
index 0000000000..b8db0503f2
--- /dev/null
+++ b/src/plugins/studiowelcome/presetmodel.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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 "presetmodel.h"
+#include <utils/optional.h>
+#include <utils/qtcassert.h>
+
+#include "algorithm.h"
+
+using namespace StudioWelcome;
+
+/****************** PresetData ******************/
+
+void PresetData::setData(const PresetsByCategory &presetsByCategory,
+ const std::vector<RecentPreset> &loadedRecents)
+{
+ QTC_ASSERT(!presetsByCategory.empty(), return);
+ m_recents = loadedRecents;
+
+ if (!m_recents.empty()) {
+ m_categories.push_back("Recents");
+ m_presets.push_back({});
+ }
+
+ for (auto &[id, category] : presetsByCategory) {
+ m_categories.push_back(category.name);
+ m_presets.push_back(category.items);
+ }
+
+ PresetItems presets = Utils::flatten(m_presets);
+
+ std::vector<PresetItem> recentPresets = makeRecentPresets(presets);
+
+ if (!m_recents.empty())
+ m_presets[0] = recentPresets;
+}
+
+std::vector<PresetItem> PresetData::makeRecentPresets(const PresetItems &wizardPresets)
+{
+ static const PresetItem empty;
+
+ PresetItems result;
+
+ for (const RecentPreset &recent : m_recents) {
+ auto item = Utils::findOptional(wizardPresets, [&recent](const PresetItem &item) {
+ return item.categoryId == std::get<0>(recent) && item.name == std::get<1>(recent);
+ });
+
+ if (item) {
+ item->screenSizeName = std::get<2>(recent);
+ result.push_back(item.value());
+ }
+ }
+
+ return result;
+}
+
+/****************** BasePresetModel ******************/
+
+BasePresetModel::BasePresetModel(const PresetData *data, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_data{data}
+{}
+
+QHash<int, QByteArray> BasePresetModel::roleNames() const
+{
+ QHash<int, QByteArray> roleNames;
+ roleNames[Qt::UserRole] = "name";
+ return roleNames;
+}
+
+/****************** PresetCategoryModel ******************/
+
+PresetCategoryModel::PresetCategoryModel(const PresetData *data, QObject *parent)
+ : BasePresetModel(data, parent)
+{}
+
+int PresetCategoryModel::rowCount(const QModelIndex &) const
+{
+ return static_cast<int>(m_data->categories().size());
+}
+
+QVariant PresetCategoryModel::data(const QModelIndex &index, int role) const
+{
+ Q_UNUSED(role)
+ return m_data->categories().at(index.row());
+}
+
+/****************** PresetModel ******************/
+
+PresetModel::PresetModel(const PresetData *data, QObject *parent)
+ : BasePresetModel(data, parent)
+{}
+
+QHash<int, QByteArray> PresetModel::roleNames() const
+{
+ QHash<int, QByteArray> roleNames;
+ roleNames[Qt::UserRole] = "name";
+ roleNames[Qt::UserRole + 1] = "size";
+ return roleNames;
+}
+
+int PresetModel::rowCount(const QModelIndex &) const
+{
+ if (m_data->presets().empty())
+ return 0;
+
+ return static_cast<int>(presetsOfCurrentCategory().size());
+}
+
+QVariant PresetModel::data(const QModelIndex &index, int role) const
+{
+ Q_UNUSED(role)
+ PresetItem preset = presetsOfCurrentCategory().at(index.row());
+ return QVariant::fromValue<QString>(preset.name + "\n" + preset.screenSizeName);
+}
+
+void PresetModel::setPage(int index)
+{
+ beginResetModel();
+
+ m_page = static_cast<size_t>(index);
+
+ endResetModel();
+}
+
+QString PresetModel::fontIconCode(int index) const
+{
+ Utils::optional<PresetItem> presetItem = preset(index);
+ if (!presetItem)
+ return {};
+
+ return presetItem->fontIconCode;
+}
diff --git a/src/plugins/studiowelcome/newprojectmodel.h b/src/plugins/studiowelcome/presetmodel.h
index ec7cc005ad..a1c9b0e7d2 100644
--- a/src/plugins/studiowelcome/newprojectmodel.h
+++ b/src/plugins/studiowelcome/presetmodel.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
@@ -32,38 +32,47 @@
#include <utils/filepath.h>
#include <utils/optional.h>
+#include "recentpresets.h"
+
namespace Utils {
class Wizard;
}
namespace StudioWelcome {
-struct ProjectItem
+struct PresetItem
{
QString name;
QString categoryId;
+ QString screenSizeName;
QString description;
QUrl qmlPath;
QString fontIconCode;
std::function<Utils::Wizard *(const Utils::FilePath &path)> create;
};
-inline QDebug &operator<<(QDebug &d, const ProjectItem &item)
+inline QDebug &operator<<(QDebug &d, const PresetItem &item)
{
d << "name=" << item.name;
d << "; category = " << item.categoryId;
+ d << "; size = " << item.screenSizeName;
return d;
}
-struct ProjectCategory
+inline bool operator==(const PresetItem &lhs, const PresetItem &rhs)
+{
+ return lhs.categoryId == rhs.categoryId && lhs.name == rhs.name;
+}
+
+struct WizardCategory
{
QString id;
QString name;
- std::vector<ProjectItem> items;
+ std::vector<PresetItem> items;
};
-inline QDebug &operator<<(QDebug &d, const ProjectCategory &cat)
+inline QDebug &operator<<(QDebug &d, const WizardCategory &cat)
{
d << "id=" << cat.id;
d << "; name=" << cat.name;
@@ -72,46 +81,66 @@ inline QDebug &operator<<(QDebug &d, const ProjectCategory &cat)
return d;
}
-using ProjectsByCategory = std::map<QString, ProjectCategory>;
+using PresetsByCategory = std::map<QString, WizardCategory>;
+using PresetItems = std::vector<PresetItem>;
+using Categories = std::vector<QString>;
-/****************** BaseNewProjectModel ******************/
+/****************** PresetData ******************/
-class BaseNewProjectModel : public QAbstractListModel
+class PresetData
{
- using ProjectItems = std::vector<std::vector<ProjectItem>>;
- using Categories = std::vector<QString>;
-
public:
- explicit BaseNewProjectModel(QObject *parent = nullptr);
- QHash<int, QByteArray> roleNames() const override;
- void setProjects(const ProjectsByCategory &projects);
+ void setData(const PresetsByCategory &presets, const std::vector<RecentPreset> &recents);
-protected:
- const ProjectItems &projects() const { return m_projects; }
+ const std::vector<PresetItems> &presets() const { return m_presets; }
const Categories &categories() const { return m_categories; }
private:
- ProjectItems m_projects;
+ std::vector<PresetItem> makeRecentPresets(const PresetItems &wizardPresets);
+
+private:
+ std::vector<PresetItems> m_presets;
Categories m_categories;
+ std::vector<RecentPreset> m_recents;
};
-/****************** NewProjectCategoryModel ******************/
+/****************** PresetCategoryModel ******************/
-class NewProjectCategoryModel : public BaseNewProjectModel
+class BasePresetModel : public QAbstractListModel
{
public:
- explicit NewProjectCategoryModel(QObject *parent = nullptr);
+ BasePresetModel(const PresetData *data, QObject *parent = nullptr);
+ QHash<int, QByteArray> roleNames() const override;
+
+ void reset()
+ {
+ beginResetModel();
+ endResetModel();
+ }
+
+protected:
+ const PresetData *m_data = nullptr;
+};
+
+/****************** PresetCategoryModel ******************/
+
+class PresetCategoryModel : public BasePresetModel
+{
+public:
+ PresetCategoryModel(const PresetData *data, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
};
-/****************** NewProjectModel ******************/
+/****************** PresetModel ******************/
-class NewProjectModel : public BaseNewProjectModel
+class PresetModel : public BasePresetModel
{
Q_OBJECT
+
public:
- explicit NewProjectModel(QObject *parent = nullptr);
+ PresetModel(const PresetData *data, QObject *parent = nullptr);
+ QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
@@ -120,24 +149,29 @@ public:
int page() const { return static_cast<int>(m_page); }
- Utils::optional<ProjectItem> project(size_t selection) const
+ Utils::optional<PresetItem> preset(size_t selection) const
{
- if (projects().empty())
+ auto presets = m_data->presets();
+ if (presets.empty())
return {};
- if (m_page < projects().size()) {
- const std::vector<ProjectItem> projectsOfCategory = projects().at(m_page);
- if (selection < projectsOfCategory.size())
- return projects().at(m_page).at(selection);
+ if (m_page < presets.size()) {
+ const std::vector<PresetItem> presetsOfCategory = presets.at(m_page);
+ if (selection < presetsOfCategory.size())
+ return presets.at(m_page).at(selection);
}
return {};
}
- bool empty() const { return projects().empty(); }
+ bool empty() const { return m_data->presets().empty(); }
private:
- const std::vector<ProjectItem> projectsOfCurrentCategory() const
- { return projects().at(m_page); }
+ const std::vector<PresetItem> presetsOfCurrentCategory() const
+ {
+ return m_data->presets().at(m_page);
+ }
+
+ std::vector<PresetItems> presets() const { return m_data->presets(); }
private:
size_t m_page = 0;
diff --git a/src/plugins/studiowelcome/qdsnewdialog.cpp b/src/plugins/studiowelcome/qdsnewdialog.cpp
index 50e3a085ca..83a966caeb 100644
--- a/src/plugins/studiowelcome/qdsnewdialog.cpp
+++ b/src/plugins/studiowelcome/qdsnewdialog.cpp
@@ -28,6 +28,7 @@
#include <coreplugin/icore.h>
#include <coreplugin/iwizardfactory.h>
#include <utils/qtcassert.h>
+#include <utils/algorithm.h>
#include <qmldesigner/components/componentcore/theme.h>
#include "createproject.h"
@@ -67,16 +68,17 @@ QString uniqueProjectName(const QString &path)
QdsNewDialog::QdsNewDialog(QWidget *parent)
: m_dialog{new QQuickWidget(parent)}
- , m_categoryModel{new NewProjectCategoryModel(this)}
- , m_projectModel{new NewProjectModel(this)}
+ , m_categoryModel{new PresetCategoryModel(&m_presetData, this)}
+ , m_presetModel{new PresetModel(&m_presetData, this)}
, m_screenSizeModel{new ScreenSizeModel(this)}
, m_styleModel{new StyleModel(this)}
+ , m_recentsStore{Core::ICore::settings()}
{
setParent(m_dialog);
m_dialog->rootContext()->setContextProperties(QVector<QQmlContext::PropertyPair>{
{{"categoryModel"}, QVariant::fromValue(m_categoryModel.data())},
- {{"projectModel"}, QVariant::fromValue(m_projectModel.data())},
+ {{"presetModel"}, QVariant::fromValue(m_presetModel.data())},
{{"screenSizeModel"}, QVariant::fromValue(m_screenSizeModel.data())},
{{"styleModel"}, QVariant::fromValue(m_styleModel.data())},
{{"dialogBox"}, QVariant::fromValue(this)},
@@ -94,7 +96,7 @@ QdsNewDialog::QdsNewDialog(QWidget *parent)
m_dialog->setWindowModality(Qt::ApplicationModal);
m_dialog->setWindowFlags(Qt::Dialog);
m_dialog->setAttribute(Qt::WA_DeleteOnClose);
- m_dialog->setMinimumSize(1066, 554);
+ m_dialog->setMinimumSize(1149, 554);
QSize screenSize = m_dialog->screen()->geometry().size();
if (screenSize.height() < 1080)
@@ -174,7 +176,12 @@ void QdsNewDialog::onWizardCreated(QStandardItemModel *screenSizeModel, QStandar
m_styleModel->setBackendModel(styleModel);
if (m_qmlDetailsLoaded) {
+ int index = m_wizard.screenSizeIndex(m_currentPreset->screenSizeName);
+ if (index > -1)
+ setScreenSizeIndex(index);
+
m_screenSizeModel->reset();
+
emit haveVirtualKeyboardChanged();
emit haveTargetQtVersionChanged();
@@ -186,12 +193,12 @@ void QdsNewDialog::onWizardCreated(QStandardItemModel *screenSizeModel, QStandar
m_styleModel->reset();
}
-QString QdsNewDialog::currentProjectQmlPath() const
+QString QdsNewDialog::currentPresetQmlPath() const
{
- if (!m_currentProject || m_currentProject->qmlPath.isEmpty())
- return "";
+ if (!m_currentPreset || m_currentPreset->qmlPath.isEmpty())
+ return {};
- return m_currentProject->qmlPath.toString();
+ return m_currentPreset->qmlPath.toString();
}
void QdsNewDialog::setScreenSizeIndex(int index)
@@ -259,11 +266,14 @@ void QdsNewDialog::setWizardFactories(QList<Core::IWizardFactory *> factories_,
WizardFactories factories{factories_, m_dialog, platform};
- m_categoryModel->setProjects(factories.projectsGroupedByCategory()); // calls model reset
- m_projectModel->setProjects(factories.projectsGroupedByCategory()); // calls model reset
+ std::vector<RecentPreset> recents = m_recentsStore.fetchAll();
+ m_presetData.setData(factories.presetsGroupedByCategory(), recents);
- if (m_qmlSelectedProject > -1)
- setSelectedProject(m_qmlSelectedProject);
+ m_categoryModel->reset();
+ m_presetModel->reset();
+
+ if (m_qmlSelectedPreset > -1)
+ setSelectedPreset(m_qmlSelectedPreset);
if (factories.empty())
return; // TODO: some message box?
@@ -277,8 +287,13 @@ void QdsNewDialog::setWizardFactories(QList<Core::IWizardFactory *> factories_,
m_qmlProjectLocation = Utils::FilePath::fromString(QDir::toNativeSeparators(projectLocation.toString()));
emit projectLocationChanged(); // So that QML knows to update the field
- if (m_qmlDetailsLoaded)
+ if (m_qmlDetailsLoaded) {
+ int index = m_wizard.screenSizeIndex(m_currentPreset->screenSizeName);
+ if (index > -1)
+ setScreenSizeIndex(index);
+
m_screenSizeModel->reset();
+ }
if (m_qmlStylesLoaded)
m_styleModel->reset();
@@ -318,6 +333,11 @@ void QdsNewDialog::accept()
.withTargetQtVersion(m_qmlTargetQtVersionIndex)
.execute();
+ PresetItem item = m_wizard.preset();
+ QString screenSize = m_wizard.screenSizeName(m_qmlScreenSizeIndex);
+
+ m_recentsStore.add(item.categoryId, item.name, screenSize);
+
m_dialog->close();
m_dialog->deleteLater();
m_dialog = nullptr;
@@ -340,17 +360,17 @@ QString QdsNewDialog::chooseProjectLocation()
return QDir::toNativeSeparators(newPath.toString());
}
-void QdsNewDialog::setSelectedProject(int selection)
+void QdsNewDialog::setSelectedPreset(int selection)
{
- if (m_qmlSelectedProject != selection || m_projectPage != m_projectModel->page()) {
- m_qmlSelectedProject = selection;
+ if (m_qmlSelectedPreset != selection || m_presetPage != m_presetModel->page()) {
+ m_qmlSelectedPreset = selection;
- m_currentProject = m_projectModel->project(m_qmlSelectedProject);
- if (m_currentProject) {
- setProjectDescription(m_currentProject->description);
+ m_currentPreset = m_presetModel->preset(m_qmlSelectedPreset);
+ if (m_currentPreset) {
+ setProjectDescription(m_currentPreset->description);
- m_projectPage = m_projectModel->page();
- m_wizard.reset(m_currentProject.value(), m_qmlSelectedProject, m_qmlProjectLocation);
+ m_presetPage = m_presetModel->page();
+ m_wizard.reset(m_currentPreset.value(), m_qmlSelectedPreset);
}
}
}
diff --git a/src/plugins/studiowelcome/qdsnewdialog.h b/src/plugins/studiowelcome/qdsnewdialog.h
index 8e331bd125..476750e540 100644
--- a/src/plugins/studiowelcome/qdsnewdialog.h
+++ b/src/plugins/studiowelcome/qdsnewdialog.h
@@ -32,9 +32,10 @@
#include <utils/optional.h>
#include "wizardhandler.h"
-#include "newprojectmodel.h"
+#include "presetmodel.h"
#include "screensizemodel.h"
#include "stylemodel.h"
+#include "recentpresets.h"
QT_BEGIN_NAMESPACE
class QStandardItemModel;
@@ -46,7 +47,7 @@ class QdsNewDialog : public QObject, public Core::NewDialog
Q_OBJECT
public:
- Q_PROPERTY(int selectedProject MEMBER m_qmlSelectedProject WRITE setSelectedProject)
+ Q_PROPERTY(int selectedPreset MEMBER m_qmlSelectedPreset WRITE setSelectedPreset)
Q_PROPERTY(QString projectName MEMBER m_qmlProjectName WRITE setProjectName NOTIFY projectNameChanged)
Q_PROPERTY(QString projectLocation MEMBER m_qmlProjectLocation READ projectLocation WRITE setProjectLocation NOTIFY projectLocationChanged)
Q_PROPERTY(QString projectDescription MEMBER m_qmlProjectDescription READ projectDescription WRITE setProjectDescription NOTIFY projectDescriptionChanged)
@@ -64,7 +65,8 @@ public:
Q_PROPERTY(bool detailsLoaded MEMBER m_qmlDetailsLoaded)
Q_PROPERTY(bool stylesLoaded MEMBER m_qmlStylesLoaded)
- Q_INVOKABLE QString currentProjectQmlPath() const;
+ Q_INVOKABLE QString currentPresetQmlPath() const;
+ // TODO: screen size index should better be a property
Q_INVOKABLE void setScreenSizeIndex(int index); // called when ComboBox item is "activated"
Q_INVOKABLE int screenSizeIndex() const;
Q_INVOKABLE void setTargetQtVersion(int index);
@@ -79,7 +81,7 @@ public:
const QVariantMap &extraVariables) override;
void setWindowTitle(const QString &title) override { m_dialog->setWindowTitle(title); }
void showDialog() override;
- void setSelectedProject(int selection);
+ void setSelectedPreset(int selection);
void setStyleIndex(int index);
int getStyleIndex() const;
@@ -135,14 +137,16 @@ private slots:
private:
QQuickWidget *m_dialog = nullptr;
- QPointer<NewProjectCategoryModel> m_categoryModel;
- QPointer<NewProjectModel> m_projectModel;
+
+ PresetData m_presetData;
+ QPointer<PresetCategoryModel> m_categoryModel;
+ QPointer<PresetModel> m_presetModel;
QPointer<ScreenSizeModel> m_screenSizeModel;
QPointer<StyleModel> m_styleModel;
QString m_qmlProjectName;
Utils::FilePath m_qmlProjectLocation;
QString m_qmlProjectDescription;
- int m_qmlSelectedProject = -1;
+ int m_qmlSelectedPreset = -1;
int m_qmlScreenSizeIndex = -1;
int m_qmlTargetQtVersionIndex = -1;
// m_qmlStyleIndex is like a cache, so it needs to be updated on get()
@@ -155,7 +159,7 @@ private:
QString m_qmlStatusMessage;
QString m_qmlStatusType;
- int m_projectPage = -1; // i.e. the page in the Presets View
+ int m_presetPage = -1; // i.e. the page in the Presets View
QString m_qmlCustomWidth;
QString m_qmlCustomHeight;
@@ -163,9 +167,10 @@ private:
bool m_qmlDetailsLoaded = false;
bool m_qmlStylesLoaded = false;
- Utils::optional<ProjectItem> m_currentProject;
+ Utils::optional<PresetItem> m_currentPreset;
WizardHandler m_wizard;
+ RecentPresetsStore m_recentsStore;
};
} //namespace StudioWelcome
diff --git a/src/plugins/studiowelcome/recentpresets.cpp b/src/plugins/studiowelcome/recentpresets.cpp
new file mode 100644
index 0000000000..f7ea5b9d61
--- /dev/null
+++ b/src/plugins/studiowelcome/recentpresets.cpp
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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 "recentpresets.h"
+#include "algorithm.h"
+
+#include <QRegularExpression>
+
+#include <coreplugin/icore.h>
+#include <utils/qtcassert.h>
+#include <utils/qtcsettings.h>
+
+using Core::ICore;
+using Utils::QtcSettings;
+
+using namespace StudioWelcome;
+
+constexpr char GROUP_NAME[] = "RecentPresets";
+constexpr char WIZARDS[] = "Wizards";
+
+void RecentPresetsStore::add(const QString &categoryId, const QString &name, const QString &sizeName)
+{
+ std::vector<RecentPreset> existing = fetchAll();
+ QStringList encodedRecents = addRecentToExisting(RecentPreset{categoryId, name, sizeName},
+ existing);
+
+ m_settings->beginGroup(GROUP_NAME);
+ m_settings->setValue(WIZARDS, encodedRecents);
+ m_settings->endGroup();
+ m_settings->sync();
+}
+
+QStringList RecentPresetsStore::addRecentToExisting(const RecentPreset &preset,
+ std::vector<RecentPreset> &recents)
+{
+ Utils::erase_one(recents, preset);
+ Utils::prepend(recents, preset);
+
+ if (recents.size() > m_max)
+ recents.pop_back();
+
+ return encodeRecentPresets(recents);
+}
+
+std::vector<RecentPreset> RecentPresetsStore::fetchAll() const
+{
+ m_settings->beginGroup(GROUP_NAME);
+ QVariant value = m_settings->value(WIZARDS);
+ m_settings->endGroup();
+
+ std::vector<RecentPreset> result;
+
+ if (value.type() == QVariant::String)
+ result.push_back(decodeOneRecentPreset(value.toString()));
+ else if (value.type() == QVariant::StringList)
+ Utils::concat(result, decodeRecentPresets(value.toList()));
+
+ const RecentPreset empty;
+ return Utils::filtered(result, [&empty](const RecentPreset &recent) { return recent != empty; });
+}
+
+QStringList RecentPresetsStore::encodeRecentPresets(const std::vector<RecentPreset> &recents)
+{
+ return Utils::transform<QList>(recents, [](const RecentPreset &p) -> QString {
+ return std::get<0>(p) + "/" + std::get<1>(p) + ":" + std::get<2>(p);
+ });
+}
+
+RecentPreset RecentPresetsStore::decodeOneRecentPreset(const QString &encoded)
+{
+ QRegularExpression pattern{R"(^(\S+)/(.+):(\d+ x \d+))"};
+ auto m = pattern.match(encoded);
+ if (!m.hasMatch())
+ return RecentPreset{};
+
+ QString category = m.captured(1);
+ QString name = m.captured(2);
+ QString size = m.captured(3);
+
+ return std::make_tuple(category, name, size);
+}
+
+std::vector<RecentPreset> RecentPresetsStore::decodeRecentPresets(const QVariantList &values)
+{
+ return Utils::transform<std::vector>(values, [](const QVariant &value) {
+ return decodeOneRecentPreset(value.toString());
+ });
+}
diff --git a/src/plugins/studiowelcome/recentpresets.h b/src/plugins/studiowelcome/recentpresets.h
new file mode 100644
index 0000000000..3c223d8df5
--- /dev/null
+++ b/src/plugins/studiowelcome/recentpresets.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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 <vector>
+#include <QPair>
+#include <QSettings>
+
+namespace StudioWelcome {
+
+// preset category, preset name, size name
+using RecentPreset = std::tuple<QString, QString, QString>;
+
+class RecentPresetsStore
+{
+public:
+ explicit RecentPresetsStore(QSettings *settings)
+ : m_settings{settings}
+ {}
+
+ void setMaximum(int n) { m_max = n; }
+ void add(const QString &categoryId, const QString &name, const QString &sizeName);
+ std::vector<RecentPreset> fetchAll() const;
+
+private:
+ QStringList addRecentToExisting(const RecentPreset &preset, std::vector<RecentPreset> &recents);
+ static QStringList encodeRecentPresets(const std::vector<RecentPreset> &recents);
+ static std::vector<RecentPreset> decodeRecentPresets(const QVariantList &values);
+ static RecentPreset decodeOneRecentPreset(const QString &encoded);
+
+private:
+ QSettings *m_settings = nullptr;
+ int m_max = 10;
+};
+
+} // namespace StudioWelcome
diff --git a/src/plugins/studiowelcome/screensizemodel.h b/src/plugins/studiowelcome/screensizemodel.h
index 7ea68966b8..597811146e 100644
--- a/src/plugins/studiowelcome/screensizemodel.h
+++ b/src/plugins/studiowelcome/screensizemodel.h
@@ -84,7 +84,7 @@ public:
return item->text();
}
- return "";
+ return {};
}
QHash<int, QByteArray> roleNames() const override
diff --git a/src/plugins/studiowelcome/studiowelcome.pro b/src/plugins/studiowelcome/studiowelcome.pro
index 9b7e756a56..3fb9fbb1cc 100644
--- a/src/plugins/studiowelcome/studiowelcome.pro
+++ b/src/plugins/studiowelcome/studiowelcome.pro
@@ -17,9 +17,10 @@ HEADERS += \
wizardfactories.h \
wizardhandler.h \
createproject.h \
- newprojectmodel.h \
+ presetmodel.h \
examplecheckout.h \
screensizemodel.h \
+ recentpresets.h \
stylemodel.h
SOURCES += \
@@ -29,8 +30,9 @@ SOURCES += \
wizardhandler.cpp \
createproject.cpp \
newprojectdialogimageprovider.cpp \
- newprojectmodel.cpp \
+ presetmodel.cpp \
examplecheckout.cpp \
+ recentpresets.cpp \
stylemodel.cpp
OTHER_FILES += \
diff --git a/src/plugins/studiowelcome/studiowelcome.qbs b/src/plugins/studiowelcome/studiowelcome.qbs
index a0b81b872d..efb5c64e4f 100644
--- a/src/plugins/studiowelcome/studiowelcome.qbs
+++ b/src/plugins/studiowelcome/studiowelcome.qbs
@@ -21,8 +21,8 @@ QtcPlugin {
"examplecheckout.cpp",
"newprojectdialogimageprovider.h",
"newprojectdialogimageprovider.cpp",
- "newprojectmodel.cpp",
- "newprojectmodel.h",
+ "presetmodel.cpp",
+ "presetmodel.h",
"qdsnewdialog.cpp",
"qdsnewdialog.h",
"screensizemodel.h",
@@ -36,6 +36,8 @@ QtcPlugin {
"wizardfactories.h",
"wizardhandler.cpp",
"wizardhandler.h",
+ "recentpresets.cpp",
+ "recentpresets.h"
]
Group {
diff --git a/src/plugins/studiowelcome/stylemodel.h b/src/plugins/studiowelcome/stylemodel.h
index 132b7251f8..554a71c02b 100644
--- a/src/plugins/studiowelcome/stylemodel.h
+++ b/src/plugins/studiowelcome/stylemodel.h
@@ -58,7 +58,7 @@ public:
return item->text();
}
- return "";
+ return {};
}
QHash<int, QByteArray> roleNames() const override
diff --git a/src/plugins/studiowelcome/wizardfactories.cpp b/src/plugins/studiowelcome/wizardfactories.cpp
index 08479ac65b..dfd21918e8 100644
--- a/src/plugins/studiowelcome/wizardfactories.cpp
+++ b/src/plugins/studiowelcome/wizardfactories.cpp
@@ -23,30 +23,42 @@
**
****************************************************************************/
+#include "wizardfactories.h"
+#include "algorithm.h"
+
#include <coreplugin/icore.h>
#include <coreplugin/iwizardfactory.h>
-#include <utils/algorithm.h>
-#include "wizardfactories.h"
+#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
#include <qmldesigner/components/componentcore/theme.h>
using namespace StudioWelcome;
WizardFactories::GetIconUnicodeFunc WizardFactories::m_getIconUnicode = &QmlDesigner::Theme::getIconUnicode;
-WizardFactories::WizardFactories(QList<Core::IWizardFactory *> &factories, QWidget *wizardParent, const Utils::Id &platform)
+WizardFactories::WizardFactories(const QList<Core::IWizardFactory *> &factories,
+ QWidget *wizardParent,
+ const Utils::Id &platform)
: m_wizardParent{wizardParent}
, m_platform{platform}
- , m_factories{factories}
{
+ m_factories = Utils::filtered(Utils::transform(factories, [](Core::IWizardFactory *f) {
+ return qobject_cast<JsonWizardFactory *>(f);
+ }));
+
sortByCategoryAndId();
filter();
- m_projectItems = makeProjectItemsGroupedByCategory();
+ m_presetItems = makePresetItemsGroupedByCategory();
+}
+
+const Core::IWizardFactory *WizardFactories::front() const
+{
+ return m_factories.front();
}
void WizardFactories::sortByCategoryAndId()
{
- Utils::sort(m_factories, [](Core::IWizardFactory *lhs, Core::IWizardFactory *rhs) {
+ Utils::sort(m_factories, [](JsonWizardFactory *lhs, JsonWizardFactory *rhs) {
if (lhs->category() == rhs->category())
return lhs->id().toString() < rhs->id().toString();
else
@@ -56,34 +68,43 @@ void WizardFactories::sortByCategoryAndId()
void WizardFactories::filter()
{
- QList<Core::IWizardFactory *> acceptedFactories = Utils::filtered(m_factories, [&](auto *wizard) {
+ QList<JsonWizardFactory *> acceptedFactories = Utils::filtered(m_factories, [&](auto *wizard) {
return wizard->isAvailable(m_platform)
- && wizard->kind() == Core::IWizardFactory::ProjectWizard
- && wizard->requiredFeatures().contains("QtStudio");
+ && wizard->kind() == JsonWizardFactory::ProjectWizard
+ && wizard->requiredFeatures().contains("QtStudio");
});
m_factories = acceptedFactories;
}
-ProjectItem WizardFactories::makeProjectItem(Core::IWizardFactory *f, QWidget *parent,
+PresetItem WizardFactories::makePresetItem(JsonWizardFactory *f, QWidget *parent,
const Utils::Id &platform)
{
using namespace std::placeholders;
+ QString sizeName;
+ auto [index, screenSizes] = f->screenSizeInfoFromPage("Fields");
+
+ if (index < 0 || index >= screenSizes.size())
+ sizeName.clear();
+ else
+ sizeName = screenSizes[index];
+
return {
/*.name =*/f->displayName(),
/*.categoryId =*/f->category(),
- /*. description =*/f->description(),
+ /*.screenSizeName=*/sizeName,
+ /*.description =*/f->description(),
/*.qmlPath =*/f->detailsPageQmlPath(),
/*.fontIconCode =*/m_getIconUnicode(f->fontIconName()),
- /*.create =*/ std::bind(&Core::IWizardFactory::runWizard, f, _1, parent, platform,
+ /*.create =*/ std::bind(&JsonWizardFactory::runWizard, f, _1, parent, platform,
QVariantMap(), false),
};
}
-std::map<QString, ProjectCategory> WizardFactories::makeProjectItemsGroupedByCategory()
+std::map<QString, WizardCategory> WizardFactories::makePresetItemsGroupedByCategory()
{
- QMap<QString, ProjectCategory> categories;
+ QMap<QString, WizardCategory> categories;
for (auto *f : std::as_const(m_factories)) {
if (!categories.contains(f->category())) {
@@ -92,12 +113,12 @@ std::map<QString, ProjectCategory> WizardFactories::makeProjectItemsGroupedByCat
/*.name =*/ f->displayCategory(),
/*.items = */
{
- makeProjectItem(f, m_wizardParent, m_platform),
+ makePresetItem(f, m_wizardParent, m_platform),
},
};
} else {
- auto projectItem = makeProjectItem(f, m_wizardParent, m_platform);
- categories[f->category()].items.push_back(projectItem);
+ auto presetItem = makePresetItem(f, m_wizardParent, m_platform);
+ categories[f->category()].items.push_back(presetItem);
}
}
diff --git a/src/plugins/studiowelcome/wizardfactories.h b/src/plugins/studiowelcome/wizardfactories.h
index ce3179e82c..aa209362de 100644
--- a/src/plugins/studiowelcome/wizardfactories.h
+++ b/src/plugins/studiowelcome/wizardfactories.h
@@ -25,7 +25,7 @@
#pragma once
-#include "newprojectmodel.h"
+#include "presetmodel.h"
#include <utils/id.h>
@@ -33,6 +33,12 @@ namespace Core {
class IWizardFactory;
}
+namespace ProjectExplorer {
+class JsonWizardFactory;
+}
+
+using ProjectExplorer::JsonWizardFactory;
+
namespace StudioWelcome {
class WizardFactories
@@ -41,12 +47,12 @@ public:
using GetIconUnicodeFunc = QString (*)(const QString &);
public:
- WizardFactories(QList<Core::IWizardFactory *> &factories, QWidget *wizardParent,
+ WizardFactories(const QList<Core::IWizardFactory *> &factories, QWidget *wizardParent,
const Utils::Id &platform);
- const Core::IWizardFactory *front() const { return m_factories.front(); }
- const std::map<QString, ProjectCategory> &projectsGroupedByCategory() const
- { return m_projectItems; }
+ const Core::IWizardFactory *front() const;
+ const std::map<QString, WizardCategory> &presetsGroupedByCategory() const
+ { return m_presetItems; }
bool empty() const { return m_factories.empty(); }
static GetIconUnicodeFunc setIconUnicodeCallback(GetIconUnicodeFunc cb)
@@ -58,15 +64,15 @@ private:
void sortByCategoryAndId();
void filter();
- ProjectItem makeProjectItem(Core::IWizardFactory *f, QWidget *parent, const Utils::Id &platform);
- std::map<QString, ProjectCategory> makeProjectItemsGroupedByCategory();
+ PresetItem makePresetItem(JsonWizardFactory *f, QWidget *parent, const Utils::Id &platform);
+ std::map<QString, WizardCategory> makePresetItemsGroupedByCategory();
private:
QWidget *m_wizardParent;
Utils::Id m_platform; // filter wizards to only those supported by this platform.
- QList<Core::IWizardFactory *> m_factories;
- std::map<QString, ProjectCategory> m_projectItems;
+ QList<JsonWizardFactory *> m_factories;
+ std::map<QString, WizardCategory> m_presetItems;
static GetIconUnicodeFunc m_getIconUnicode;
};
diff --git a/src/plugins/studiowelcome/wizardhandler.cpp b/src/plugins/studiowelcome/wizardhandler.cpp
index bf9f0f31f7..cd176707ac 100644
--- a/src/plugins/studiowelcome/wizardhandler.cpp
+++ b/src/plugins/studiowelcome/wizardhandler.cpp
@@ -38,18 +38,17 @@
using namespace StudioWelcome;
-void WizardHandler::reset(const ProjectItem &projectInfo, int projectSelection, const Utils::FilePath &location)
+void WizardHandler::reset(const PresetItem &presetInfo, int presetSelection)
{
- m_projectItem = projectInfo;
- m_projectLocation = location;
- m_selectedProject = projectSelection;
+ m_preset = presetInfo;
+ m_selectedPreset = presetSelection;
if (!m_wizard) {
setupWizard();
} else {
QObject::connect(m_wizard, &QObject::destroyed, this, &WizardHandler::onWizardResetting);
- // DON'T SET `m_selectedProject = -1` --- we are switching now to a separate project.
+ // DON'T SET `m_selectedPreset = -1` --- we are switching now to a separate preset.
emit deletingWizard();
m_wizard->deleteLater();
@@ -60,7 +59,7 @@ void WizardHandler::destroyWizard()
{
emit deletingWizard();
- m_selectedProject = -1;
+ m_selectedPreset = -1;
m_wizard->deleteLater();
m_wizard = nullptr;
m_detailsPage = nullptr;
@@ -68,7 +67,7 @@ void WizardHandler::destroyWizard()
void WizardHandler::setupWizard()
{
- m_wizard = m_projectItem.create(m_projectLocation);
+ m_wizard = m_preset.create(m_projectLocation);
if (!m_wizard) {
emit wizardCreationFailed();
return;
@@ -161,8 +160,8 @@ void WizardHandler::onWizardResetting()
// if have a wizard request pending => create new wizard
// note: we always have a wizard request pending here, unless the dialogbox was requested to be destroyed.
- // if m_selectedProject != -1 => the wizard was destroyed as a result of reset to a different project type
- if (m_selectedProject > -1)
+ // if m_selectedPreset != -1 => the wizard was destroyed as a result of reset to a different preset type
+ if (m_selectedPreset > -1)
setupWizard();
}
@@ -175,6 +174,20 @@ void WizardHandler::setScreenSizeIndex(int index)
cbfield->selectRow(index);
}
+QString WizardHandler::screenSizeName(int index) const
+{
+ auto *field = m_detailsPage->jsonField("ScreenFactor");
+ auto *cbfield = dynamic_cast<ProjectExplorer::ComboBoxField *>(field);
+ QTC_ASSERT(cbfield, return "");
+
+ QStandardItemModel *model = cbfield->model();
+ if (index < 0 || index >= model->rowCount())
+ return {};
+
+ QString text = model->item(index)->text();
+ return text;
+}
+
int WizardHandler::screenSizeIndex() const
{
auto *field = m_detailsPage->jsonField("ScreenFactor");
@@ -184,6 +197,24 @@ int WizardHandler::screenSizeIndex() const
return cbfield->selectedRow();
}
+int WizardHandler::screenSizeIndex(const QString &sizeName) const
+{
+ auto *field = m_detailsPage->jsonField("ScreenFactor");
+ auto *cbfield = dynamic_cast<ProjectExplorer::ComboBoxField *>(field);
+ QTC_ASSERT(cbfield, return false);
+
+ const QStandardItemModel *model = cbfield->model();
+ for (int i = 0; i < model->rowCount(); ++i) {
+ const QStandardItem *item = model->item(i, 0);
+ const QString text = item->text();
+
+ if (text == sizeName)
+ return i;
+ }
+
+ return -1;
+}
+
void WizardHandler::setTargetQtVersionIndex(int index)
{
auto *field = m_detailsPage->jsonField("TargetQtVersion");
@@ -250,7 +281,7 @@ void WizardHandler::run(const std::function<void(QWizardPage *)> &processPage)
m_wizard->next();
} while (-1 != nextId);
- m_selectedProject = -1;
+ m_selectedPreset = -1;
// Note: don't call `emit deletingWizard()` here.
diff --git a/src/plugins/studiowelcome/wizardhandler.h b/src/plugins/studiowelcome/wizardhandler.h
index ad9424bc44..112accfb15 100644
--- a/src/plugins/studiowelcome/wizardhandler.h
+++ b/src/plugins/studiowelcome/wizardhandler.h
@@ -25,7 +25,7 @@
#pragma once
-#include "newprojectmodel.h"
+#include "presetmodel.h"
#include <utils/filepath.h>
#include <utils/infolabel.h>
@@ -47,9 +47,10 @@ class WizardHandler: public QObject
Q_OBJECT
public:
- //TODO: location should not be needed in reset() -- only when creating the project
- void reset(const ProjectItem &projectInfo, int projectSelection, const Utils::FilePath &location);
+ void reset(const PresetItem &presetInfo, int presetSelection);
void setScreenSizeIndex(int index);
+ int screenSizeIndex(const QString &sizeName) const;
+ QString screenSizeName(int index) const;
int screenSizeIndex() const;
void setTargetQtVersionIndex(int index);
bool haveTargetQtVersion() const;
@@ -65,6 +66,8 @@ public:
void run(const std::function<void (QWizardPage *)> &processPage);
+ PresetItem preset() const { return m_preset; }
+
signals:
void deletingWizard();
void wizardCreated(QStandardItemModel *screenSizeModel, QStandardItemModel *styleModel);
@@ -88,9 +91,9 @@ private:
Utils::Wizard *m_wizard = nullptr;
ProjectExplorer::JsonFieldPage *m_detailsPage = nullptr;
- int m_selectedProject = -1;
+ int m_selectedPreset = -1;
- ProjectItem m_projectItem;
+ PresetItem m_preset;
Utils::FilePath m_projectLocation;
};
diff --git a/tests/auto/qml/qmldesigner/wizard/CMakeLists.txt b/tests/auto/qml/qmldesigner/wizard/CMakeLists.txt
index ca3a0ac7ec..de84a6e931 100644
--- a/tests/auto/qml/qmldesigner/wizard/CMakeLists.txt
+++ b/tests/auto/qml/qmldesigner/wizard/CMakeLists.txt
@@ -5,7 +5,7 @@ set(WITH_TESTS ON)
find_package(Googletest MODULE)
add_qtc_test(tst_qml_wizard
- DEPENDS Core Utils StudioWelcome QmlDesigner Googletest
+ DEPENDS Core Utils StudioWelcome ProjectExplorer QmlDesigner Googletest
DEFINES
QT_CREATOR
QMLDESIGNER_TEST
@@ -17,8 +17,13 @@ add_qtc_test(tst_qml_wizard
SOURCES
wizardfactories-test.cpp
stylemodel-test.cpp
+ recentpresets-test.cpp
+ presetmodel-test.cpp
test-utilities.h
+ test-main.cpp
"${StudioWelcomeDir}/wizardfactories.cpp"
"${StudioWelcomeDir}/stylemodel.cpp"
+ "${StudioWelcomeDir}/recentpresets.cpp"
+ "${StudioWelcomeDir}/presetmodel.cpp"
)
diff --git a/tests/auto/qml/qmldesigner/wizard/presetmodel-test.cpp b/tests/auto/qml/qmldesigner/wizard/presetmodel-test.cpp
new file mode 100644
index 0000000000..dc855b67a3
--- /dev/null
+++ b/tests/auto/qml/qmldesigner/wizard/presetmodel-test.cpp
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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 "test-utilities.h"
+
+#include "presetmodel.h"
+
+using namespace StudioWelcome;
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::PrintToString;
+
+namespace StudioWelcome {
+void PrintTo(const PresetItem &item, std::ostream *os)
+{
+ *os << "{categId: " << item.categoryId << ", "
+ << "name: " << item.name;
+
+ if (!item.screenSizeName.isEmpty())
+ *os << ", size: " << item.screenSizeName;
+
+ *os << "}";
+}
+
+} // namespace StudioWelcome
+
+namespace {
+std::pair<QString, WizardCategory> aCategory(const QString &categId,
+ const QString &categName,
+ const std::vector<QString> &names)
+{
+ std::vector<PresetItem> items = Utils::transform(names, [&categId](const QString &name) {
+ return PresetItem{name, categId};
+ });
+ return std::make_pair(categId, WizardCategory{categId, categName, items});
+}
+
+MATCHER_P2(PresetIs, category, name, PrintToString(PresetItem{name, category}))
+{
+ return arg.categoryId == category && arg.name == name;
+}
+
+MATCHER_P3(PresetIs, category, name, size, PrintToString(PresetItem{name, category, size}))
+{
+ return arg.categoryId == category && arg.name == name && size == arg.screenSizeName;
+}
+
+} // namespace
+
+/******************* TESTS *******************/
+
+TEST(QdsPresetModel, whenHaveNoPresetsNoRecentsReturnEmpty)
+{
+ PresetData data;
+
+ ASSERT_THAT(data.presets(), SizeIs(0));
+ ASSERT_THAT(data.categories(), SizeIs(0));
+}
+
+TEST(QdsPresetModel, haveSameArraySizeForPresetsAndCategories)
+{
+ PresetData data;
+
+ data.setData(
+ {
+ aCategory("A.categ", "A", {"item a", "item b"}),
+ aCategory("B.categ", "B", {"item c", "item d"}),
+ },
+ {/*recents*/});
+
+ ASSERT_THAT(data.presets(), SizeIs(2));
+ ASSERT_THAT(data.categories(), SizeIs(2));
+}
+
+TEST(QdsPresetModel, haveWizardPresetsNoRecents)
+{
+ // Given
+ PresetData data;
+
+ // When
+ data.setData(
+ {
+ aCategory("A.categ", "A", {"item a", "item b"}),
+ aCategory("B.categ", "B", {"item c", "item d"}),
+ },
+ {/*recents*/});
+
+ // Then
+ ASSERT_THAT(data.categories(), ElementsAre("A", "B"));
+ ASSERT_THAT(data.presets()[0],
+ ElementsAre(PresetIs("A.categ", "item a"), PresetIs("A.categ", "item b")));
+ ASSERT_THAT(data.presets()[1],
+ ElementsAre(PresetIs("B.categ", "item c"), PresetIs("B.categ", "item d")));
+}
+
+TEST(QdsPresetModel, haveRecentsNoWizardPresets)
+{
+ PresetData data;
+
+ data.setData({/*wizardPresets*/},
+ {
+ {"A.categ", "Desktop", "640 x 480"},
+ {"B.categ", "Mobile", "800 x 600"},
+ });
+
+ ASSERT_THAT(data.categories(), IsEmpty());
+ ASSERT_THAT(data.presets(), IsEmpty());
+}
+
+TEST(QdsPresetModel, recentsAddedBeforeWizardPresets)
+{
+ // Given
+ PresetData data;
+
+ // When
+ data.setData(
+ /*wizard presets*/
+ {
+ aCategory("A.categ", "A", {"Desktop", "item b"}),
+ aCategory("B.categ", "B", {"item c", "Mobile"}),
+ },
+ /*recents*/
+ {
+ {"A.categ", "Desktop", "800 x 600"},
+ {"B.categ", "Mobile", "640 x 480"},
+ });
+
+ // Then
+ ASSERT_THAT(data.categories(), ElementsAre("Recents", "A", "B"));
+
+ ASSERT_THAT(data.presets(),
+ ElementsAreArray(
+ {ElementsAre(PresetIs("A.categ", "Desktop"), PresetIs("B.categ", "Mobile")),
+
+ ElementsAre(PresetIs("A.categ", "Desktop"), PresetIs("A.categ", "item b")),
+ ElementsAre(PresetIs("B.categ", "item c"), PresetIs("B.categ", "Mobile"))}));
+}
+
+TEST(QdsPresetModel, recentsShouldNotSorted)
+{
+ // Given
+ PresetData data;
+
+ // When
+ data.setData(
+ /*wizard presets*/
+ {
+ aCategory("A.categ", "A", {"Desktop", "item b"}),
+ aCategory("B.categ", "B", {"item c", "Mobile"}),
+ aCategory("Z.categ", "Z", {"Z.desktop"}),
+ },
+ /*recents*/
+ {
+ {"Z.categ", "Z.desktop", "200 x 300"},
+ {"B.categ", "Mobile", "200 x 300"},
+ {"A.categ", "Desktop", "200 x 300"},
+ });
+
+ // Then
+ ASSERT_THAT(data.presets()[0],
+ ElementsAre(PresetIs("Z.categ", "Z.desktop"),
+ PresetIs("B.categ", "Mobile"),
+ PresetIs("A.categ", "Desktop")));
+}
+
+TEST(QdsPresetModel, recentsOfSameWizardProjectButDifferentSizesAreRecognizedAsDifferentPresets)
+{
+ // Given
+ PresetData data;
+
+ // When
+ data.setData(
+ /*wizard presets*/
+ {
+ aCategory("A.categ", "A", {"Desktop"}),
+ aCategory("B.categ", "B", {"Mobile"}),
+ },
+ /*recents*/
+ {
+ {"B.categ", "Mobile", "400 x 400"},
+ {"B.categ", "Mobile", "200 x 300"},
+ {"A.categ", "Desktop", "640 x 480"},
+ });
+
+ // Then
+ ASSERT_THAT(data.presets()[0],
+ ElementsAre(PresetIs("B.categ", "Mobile", "400 x 400"),
+ PresetIs("B.categ", "Mobile", "200 x 300"),
+ PresetIs("A.categ", "Desktop", "640 x 480")));
+}
+
+TEST(QdsPresetModel, outdatedRecentsAreNotShown)
+{
+ // Given
+ PresetData data;
+
+ // When
+ data.setData(
+ /*wizard presets*/
+ {
+ aCategory("A.categ", "A", {"Desktop"}),
+ aCategory("B.categ", "B", {"Mobile"}),
+ },
+ /*recents*/
+ {
+ {"B.categ", "NoLongerExists", "400 x 400"},
+ {"A.categ", "Desktop", "640 x 480"},
+ });
+
+ // Then
+ ASSERT_THAT(data.presets()[0], ElementsAre(PresetIs("A.categ", "Desktop", "640 x 480")));
+}
diff --git a/tests/auto/qml/qmldesigner/wizard/recentpresets-test.cpp b/tests/auto/qml/qmldesigner/wizard/recentpresets-test.cpp
new file mode 100644
index 0000000000..b1bb0e0626
--- /dev/null
+++ b/tests/auto/qml/qmldesigner/wizard/recentpresets-test.cpp
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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 "test-utilities.h"
+
+#include <QDir>
+#include <QRandomGenerator>
+#include <QTime>
+
+#include "recentpresets.h"
+#include "utils/filepath.h"
+#include "utils/temporarydirectory.h"
+
+using namespace StudioWelcome;
+
+constexpr char GROUP_NAME[] = "RecentPresets";
+constexpr char ITEMS[] = "Wizards";
+
+class QdsRecentPresets : public ::testing::Test
+{
+protected:
+ RecentPresetsStore aStoreWithRecents(const QStringList &items)
+ {
+ settings.beginGroup(GROUP_NAME);
+ settings.setValue(ITEMS, items);
+ settings.endGroup();
+
+ return RecentPresetsStore{&settings};
+ }
+
+ RecentPresetsStore aStoreWithOne(const QVariant &item)
+ {
+ settings.beginGroup(GROUP_NAME);
+ settings.setValue(ITEMS, item);
+ settings.endGroup();
+
+ return RecentPresetsStore{&settings};
+ }
+
+protected:
+ Utils::TemporaryDirectory tempDir{"recentpresets-XXXXXX"};
+ QSettings settings{tempDir.filePath("test").toString(), QSettings::IniFormat};
+
+private:
+ QString settingsPath;
+};
+
+/******************* TESTS *******************/
+
+TEST_F(QdsRecentPresets, readFromEmptyStore)
+{
+ RecentPresetsStore store{&settings};
+
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents, IsEmpty());
+}
+
+TEST_F(QdsRecentPresets, readEmptyRecentPresets)
+{
+ RecentPresetsStore store = aStoreWithOne("");
+
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents, IsEmpty());
+}
+
+TEST_F(QdsRecentPresets, readOneRecentPresetAsList)
+{
+ RecentPresetsStore store = aStoreWithRecents({"category/preset:640 x 480"});
+
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents, ElementsAre(RecentPreset("category", "preset", "640 x 480")));
+}
+
+TEST_F(QdsRecentPresets, readOneRecentPresetAsString)
+{
+ RecentPresetsStore store = aStoreWithOne("category/preset:200 x 300");
+
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents, ElementsAre(RecentPreset("category", "preset", "200 x 300")));
+}
+
+TEST_F(QdsRecentPresets, readBadRecentPresetAsString)
+{
+ RecentPresetsStore store = aStoreWithOne("no_category_only_preset");
+
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents, IsEmpty());
+}
+
+TEST_F(QdsRecentPresets, readBadRecentPresetAsInt)
+{
+ RecentPresetsStore store = aStoreWithOne(32);
+
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents, IsEmpty());
+}
+
+TEST_F(QdsRecentPresets, readBadRecentPresetsInList)
+{
+ RecentPresetsStore store = aStoreWithRecents({"bad1", // no category, no size
+ "categ/name:800 x 600", // good
+ "categ/bad2", //no size
+ "categ/bad3:", //no size
+ "categ 1/bad4:200 x 300", // category has space
+ "categ/bad5: 400 x 300", // size starts with space
+ "categ/bad6:400"}); // bad size
+
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents, ElementsAre(RecentPreset("categ", "name", "800 x 600")));
+}
+
+TEST_F(QdsRecentPresets, readTwoRecentPresets)
+{
+ RecentPresetsStore store = aStoreWithRecents(
+ {"category_1/preset 1:640 x 480", "category_2/preset 2:320 x 200"});
+
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents,
+ ElementsAre(RecentPreset("category_1", "preset 1", "640 x 480"),
+ RecentPreset("category_2", "preset 2", "320 x 200")));
+}
+
+TEST_F(QdsRecentPresets, addFirstRecentPreset)
+{
+ RecentPresetsStore store{&settings};
+
+ store.add("A.Category", "Normal Application", "400 x 600");
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents, ElementsAre(RecentPreset("A.Category", "Normal Application", "400 x 600")));
+}
+
+TEST_F(QdsRecentPresets, addExistingFirstRecentPreset)
+{
+ RecentPresetsStore store = aStoreWithRecents({"category/preset"});
+
+ store.add("category", "preset", "200 x 300");
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents, ElementsAre(RecentPreset("category", "preset", "200 x 300")));
+}
+
+TEST_F(QdsRecentPresets, addSecondRecentPreset)
+{
+ RecentPresetsStore store = aStoreWithRecents({"A.Category/Preset 1:800 x 600"});
+
+ store.add("A.Category", "Preset 2", "640 x 480");
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents,
+ ElementsAre(RecentPreset("A.Category", "Preset 2", "640 x 480"),
+ RecentPreset("A.Category", "Preset 1", "800 x 600")));
+}
+
+TEST_F(QdsRecentPresets, addSecondRecentPresetSameKindButDifferentSize)
+{
+ RecentPresetsStore store = aStoreWithRecents({"A.Category/Preset:800 x 600"});
+
+ store.add("A.Category", "Preset", "640 x 480");
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents,
+ ElementsAre(RecentPreset("A.Category", "Preset", "640 x 480"),
+ RecentPreset("A.Category", "Preset", "800 x 600")));
+}
+
+TEST_F(QdsRecentPresets, fetchesRecentPresetsInTheReverseOrderTheyWereAdded)
+{
+ RecentPresetsStore store{&settings};
+
+ store.add("A.Category", "Preset 1", "640 x 480");
+ store.add("A.Category", "Preset 2", "640 x 480");
+ store.add("A.Category", "Preset 3", "800 x 600");
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents,
+ ElementsAre(RecentPreset("A.Category", "Preset 3", "800 x 600"),
+ RecentPreset("A.Category", "Preset 2", "640 x 480"),
+ RecentPreset("A.Category", "Preset 1", "640 x 480")));
+}
+
+TEST_F(QdsRecentPresets, addingAnExistingRecentPresetMakesItTheFirst)
+{
+ RecentPresetsStore store = aStoreWithRecents({"A.Category/Preset 1:200 x 300",
+ "A.Category/Preset 2:200 x 300",
+ "A.Category/Preset 3:640 x 480"});
+
+ store.add("A.Category", "Preset 3", "640 x 480");
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents,
+ ElementsAre(RecentPreset("A.Category", "Preset 3", "640 x 480"),
+ RecentPreset("A.Category", "Preset 1", "200 x 300"),
+ RecentPreset("A.Category", "Preset 2", "200 x 300")));
+}
+
+TEST_F(QdsRecentPresets, addingTooManyRecentPresetsRemovesTheOldestOne)
+{
+ RecentPresetsStore store = aStoreWithRecents(
+ {"A.Category/Preset 2:200 x 300", "A.Category/Preset 1:200 x 300"});
+ store.setMaximum(2);
+
+ store.add("A.Category", "Preset 3", "200 x 300");
+ std::vector<RecentPreset> recents = store.fetchAll();
+
+ ASSERT_THAT(recents,
+ ElementsAre(RecentPreset("A.Category", "Preset 3", "200 x 300"),
+ RecentPreset("A.Category", "Preset 2", "200 x 300")));
+}
diff --git a/tests/auto/qml/qmldesigner/wizard/test-main.cpp b/tests/auto/qml/qmldesigner/wizard/test-main.cpp
new file mode 100644
index 0000000000..d419674ea1
--- /dev/null
+++ b/tests/auto/qml/qmldesigner/wizard/test-main.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 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 "test-utilities.h"
+
+#include <utils/temporarydirectory.h>
+
+class Environment : public testing::Environment
+{
+public:
+ void SetUp() override
+ {
+ const QString temporayDirectoryPath = QDir::tempPath() + "/QtCreator-UnitTests-XXXXXX";
+ Utils::TemporaryDirectory::setMasterTemporaryDirectory(temporayDirectoryPath);
+ qputenv("TMPDIR", Utils::TemporaryDirectory::masterDirectoryPath().toUtf8());
+ qputenv("TEMP", Utils::TemporaryDirectory::masterDirectoryPath().toUtf8());
+ }
+
+ void TearDown() override {}
+};
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ auto environment = std::make_unique<Environment>();
+ testing::AddGlobalTestEnvironment(environment.release());
+
+ return RUN_ALL_TESTS();
+}
+
diff --git a/tests/auto/qml/qmldesigner/wizard/test-utilities.h b/tests/auto/qml/qmldesigner/wizard/test-utilities.h
index 3f708e121d..b368e532f4 100644
--- a/tests/auto/qml/qmldesigner/wizard/test-utilities.h
+++ b/tests/auto/qml/qmldesigner/wizard/test-utilities.h
@@ -26,6 +26,7 @@
**
****************************************************************************/
+#include "gmock/gmock-matchers.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -38,8 +39,10 @@
using ::testing::Return;
using ::testing::AtLeast;
using ::testing::ElementsAreArray;
+using ::testing::ElementsAre;
using ::testing::IsEmpty;
using ::testing::Not;
+using ::testing::SizeIs;
QT_BEGIN_NAMESPACE
diff --git a/tests/auto/qml/qmldesigner/wizard/wizardfactories-test.cpp b/tests/auto/qml/qmldesigner/wizard/wizardfactories-test.cpp
index 97ae04cccc..2e6a87617d 100644
--- a/tests/auto/qml/qmldesigner/wizard/wizardfactories-test.cpp
+++ b/tests/auto/qml/qmldesigner/wizard/wizardfactories-test.cpp
@@ -26,15 +26,17 @@
#include "test-utilities.h"
#include <coreplugin/iwizardfactory.h>
+#include <projectexplorer/jsonwizard/jsonwizardfactory.h>
#include "wizardfactories.h"
using namespace StudioWelcome;
using Core::IWizardFactory;
+using ProjectExplorer::JsonWizardFactory;
namespace {
-class MockWizardFactory : public IWizardFactory
+class MockWizardFactory : public JsonWizardFactory
{
public:
MOCK_METHOD(Utils::Wizard *, runWizardImpl,
@@ -47,6 +49,8 @@ public:
),
(override));
+ MOCK_METHOD((std::pair<int, QStringList>), screenSizeInfoFromPage, (const QString &), (const));
+
MOCK_METHOD(bool, isAvailable, (Utils::Id), (const, override));
};
@@ -78,12 +82,14 @@ protected:
// a good wizard factory is a wizard factory that is not filtered out, and which is available on
// platform `this->platform`
- IWizardFactory *aGoodWizardFactory(const QString &name = "", const QString &id = "", const QString &categoryId = "")
+ IWizardFactory *aGoodWizardFactory(const QString &name = "", const QString &id = "",
+ const QString &categoryId = "", const std::pair<int, QStringList> &sizes = {})
{
MockWizardFactory *factory = new MockWizardFactory;
m_factories.push_back(std::unique_ptr<IWizardFactory>(factory));
- configureFactory(*factory, IWizardFactory::ProjectWizard, /*req QtStudio*/true, {platform, true});
+ configureFactory(*factory, IWizardFactory::ProjectWizard, /*req QtStudio*/true,
+ {platform, true}, sizes);
if (!name.isEmpty())
factory->setDisplayName(name);
@@ -97,7 +103,8 @@ protected:
void configureFactory(MockWizardFactory &factory, IWizardFactory::WizardKind kind,
bool requiresQtStudio = true,
- const QPair<QString, bool> &availableOnPlatform = {})
+ const QPair<QString, bool> &availableOnPlatform = {},
+ const QPair<int, QStringList> &sizes = {})
{
if (kind == IWizardFactory::ProjectWizard) {
QSet<Utils::Id> supported{Utils::Id{"QmlProjectManager.QmlProject"}};
@@ -127,6 +134,14 @@ protected:
.Times(AtLeast(1))
.WillRepeatedly(Return(value));
}
+
+ auto screenSizes = (sizes == std::pair<int, QStringList>{}
+ ? std::make_pair(0, QStringList({"640 x 480"}))
+ : sizes);
+
+ EXPECT_CALL(factory, screenSizeInfoFromPage(QString("Fields")))
+ .Times(AtLeast(0))
+ .WillRepeatedly(Return(screenSizes));
}
WizardFactories makeWizardFactoriesHandler(QList<IWizardFactory *> source,
@@ -143,15 +158,21 @@ private:
WizardFactories::GetIconUnicodeFunc oldIconUnicodeFunc;
};
-inline QStringList projectNames(const ProjectCategory &cat)
+QStringList presetNames(const WizardCategory &cat)
+{
+ QStringList result = Utils::transform<QStringList>(cat.items, &PresetItem::name);
+ return result;
+}
+
+QStringList screenSizes(const WizardCategory &cat)
{
- QStringList result = Utils::transform<QStringList>(cat.items, &ProjectItem::name);
+ QStringList result = Utils::transform<QStringList>(cat.items, &PresetItem::screenSizeName);
return result;
}
-inline QStringList categoryNames(const std::map<QString, ProjectCategory> &projects)
+QStringList categoryNames(const std::map<QString, WizardCategory> &presets)
{
- QMap<QString, ProjectCategory> qmap{projects};
+ const QMap<QString, WizardCategory> qmap{presets};
return qmap.keys();
}
@@ -166,9 +187,9 @@ TEST_F(QdsWizardFactories, haveEmptyListOfWizardFactories)
/*get wizards supporting platform*/ "platform"
);
- std::map<QString, ProjectCategory> projects = wf.projectsGroupedByCategory();
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
- ASSERT_THAT(projects, IsEmpty());
+ ASSERT_THAT(presets, IsEmpty());
}
TEST_F(QdsWizardFactories, filtersOutNonProjectWizardFactories)
@@ -178,9 +199,9 @@ TEST_F(QdsWizardFactories, filtersOutNonProjectWizardFactories)
/*get wizards supporting platform*/ platform
);
- std::map<QString, ProjectCategory> projects = wf.projectsGroupedByCategory();
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
- ASSERT_THAT(projects, IsEmpty());
+ ASSERT_THAT(presets, IsEmpty());
}
TEST_F(QdsWizardFactories, filtersOutWizardFactoriesUnavailableForPlatform)
@@ -190,9 +211,9 @@ TEST_F(QdsWizardFactories, filtersOutWizardFactoriesUnavailableForPlatform)
/*get wizards supporting platform*/ "Non-Desktop"
);
- std::map<QString, ProjectCategory> projects = wf.projectsGroupedByCategory();
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
- ASSERT_THAT(projects, IsEmpty());
+ ASSERT_THAT(presets, IsEmpty());
}
TEST_F(QdsWizardFactories, filtersOutWizardFactoriesThatDontRequireQtStudio)
@@ -203,18 +224,48 @@ TEST_F(QdsWizardFactories, filtersOutWizardFactoriesThatDontRequireQtStudio)
},
/*get wizards supporting platform*/ platform);
- std::map<QString, ProjectCategory> projects = wf.projectsGroupedByCategory();
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
- ASSERT_THAT(projects, IsEmpty());
+ ASSERT_THAT(presets, IsEmpty());
}
TEST_F(QdsWizardFactories, doesNotFilterOutAGoodWizardFactory)
{
WizardFactories wf = makeWizardFactoriesHandler({aGoodWizardFactory()}, platform);
- std::map<QString, ProjectCategory> projects = wf.projectsGroupedByCategory();
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
+
+ ASSERT_THAT(presets, Not(IsEmpty()));
+}
+
+TEST_F(QdsWizardFactories, DISABLED_buildsPresetItemWithCorrectSizeName)
+{
+ WizardFactories wf = makeWizardFactoriesHandler(
+ {
+ aGoodWizardFactory("A", "A_id", "A.category", {1, {"size 0", "size 1"}}),
+ },
+ platform);
+
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
- ASSERT_THAT(projects, Not(IsEmpty()));
+ ASSERT_THAT(categoryNames(presets), ElementsAreArray({"A.category"}));
+ ASSERT_THAT(presetNames(presets["A.category"]), ElementsAreArray({"A"}));
+ ASSERT_THAT(screenSizes(presets["A.category"]), ElementsAreArray({"size 1"}));
+}
+
+TEST_F(QdsWizardFactories, whenSizeInfoIsBadBuildsPresetItemWithEmptySizeName)
+{
+ WizardFactories wf = makeWizardFactoriesHandler(
+ {
+ aGoodWizardFactory("A", "A_id", "A.category", {1, {/*empty*/}}),
+ },
+ platform);
+
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
+
+ ASSERT_THAT(categoryNames(presets), ElementsAreArray({"A.category"}));
+ ASSERT_THAT(presetNames(presets["A.category"]), ElementsAreArray({"A"}));
+ ASSERT_THAT(screenSizes(presets["A.category"]), ElementsAreArray({""}));
}
TEST_F(QdsWizardFactories, sortsWizardFactoriesByCategory)
@@ -226,11 +277,11 @@ TEST_F(QdsWizardFactories, sortsWizardFactoriesByCategory)
},
platform);
- std::map<QString, ProjectCategory> projects = wf.projectsGroupedByCategory();
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
- ASSERT_THAT(categoryNames(projects), ElementsAreArray({"A.category", "Z.category"}));
- ASSERT_THAT(projectNames(projects["A.category"]), ElementsAreArray({"X"}));
- ASSERT_THAT(projectNames(projects["Z.category"]), ElementsAreArray({"B"}));
+ ASSERT_THAT(categoryNames(presets), ElementsAreArray({"A.category", "Z.category"}));
+ ASSERT_THAT(presetNames(presets["A.category"]), ElementsAreArray({"X"}));
+ ASSERT_THAT(presetNames(presets["Z.category"]), ElementsAreArray({"B"}));
}
TEST_F(QdsWizardFactories, sortsWizardFactoriesById)
@@ -242,10 +293,10 @@ TEST_F(QdsWizardFactories, sortsWizardFactoriesById)
},
platform);
- std::map<QString, ProjectCategory> projects = wf.projectsGroupedByCategory();
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
- ASSERT_THAT(categoryNames(projects), ElementsAreArray({"category"}));
- ASSERT_THAT(projectNames(projects["category"]), ElementsAreArray({"X", "B"}));
+ ASSERT_THAT(categoryNames(presets), ElementsAreArray({"category"}));
+ ASSERT_THAT(presetNames(presets["category"]), ElementsAreArray({"X", "B"}));
}
TEST_F(QdsWizardFactories, groupsWizardFactoriesByCategory)
@@ -258,14 +309,14 @@ TEST_F(QdsWizardFactories, groupsWizardFactoriesByCategory)
},
platform);
- std::map<QString, ProjectCategory> projects = wf.projectsGroupedByCategory();
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
- ASSERT_THAT(categoryNames(projects), ElementsAreArray({"A.category", "Z.category"}));
- ASSERT_THAT(projectNames(projects["A.category"]), ElementsAreArray({"A", "B"}));
- ASSERT_THAT(projectNames(projects["Z.category"]), ElementsAreArray({"C"}));
+ ASSERT_THAT(categoryNames(presets), ElementsAreArray({"A.category", "Z.category"}));
+ ASSERT_THAT(presetNames(presets["A.category"]), ElementsAreArray({"A", "B"}));
+ ASSERT_THAT(presetNames(presets["Z.category"]), ElementsAreArray({"C"}));
}
-TEST_F(QdsWizardFactories, createsProjectItemAndCategoryCorrectlyFromWizardFactory)
+TEST_F(QdsWizardFactories, createsPresetItemAndCategoryCorrectlyFromWizardFactory)
{
IWizardFactory *source = aGoodWizardFactory("myName", "myId", "myCategoryId");
@@ -280,23 +331,19 @@ TEST_F(QdsWizardFactories, createsProjectItemAndCategoryCorrectlyFromWizardFacto
WizardFactories wf = makeWizardFactoriesHandler({source}, platform);
- std::map<QString, ProjectCategory> projects = wf.projectsGroupedByCategory();
+ std::map<QString, WizardCategory> presets = wf.presetsGroupedByCategory();
- ASSERT_THAT(categoryNames(projects), ElementsAreArray({"myCategoryId"}));
- ASSERT_THAT(projectNames(projects["myCategoryId"]), ElementsAreArray({"myName"}));
+ ASSERT_THAT(categoryNames(presets), ElementsAreArray({"myCategoryId"}));
+ ASSERT_THAT(presetNames(presets["myCategoryId"]), ElementsAreArray({"myName"}));
- auto category = projects["myCategoryId"];
+ auto category = presets["myCategoryId"];
ASSERT_EQ("myCategoryId", category.id);
ASSERT_EQ("myDisplayCategory", category.name);
- auto projectItem = projects["myCategoryId"].items[0];
- ASSERT_EQ("myName", projectItem.name);
- ASSERT_EQ("myDescription", projectItem.description);
- ASSERT_EQ("qrc:/my/qml/path", projectItem.qmlPath.toString());
- ASSERT_EQ("\uABCD", projectItem.fontIconCode);
+ auto presetItem = presets["myCategoryId"].items[0];
+ ASSERT_EQ("myName", presetItem.name);
+ ASSERT_EQ("myDescription", presetItem.description);
+ ASSERT_EQ("qrc:/my/qml/path", presetItem.qmlPath.toString());
+ ASSERT_EQ("\uABCD", presetItem.fontIconCode);
}
-int main(int argc, char **argv) {
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}