summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Ghinet <samuel.ghinet@qt.io>2022-03-21 23:18:59 +0200
committerSamuel Ghinet <samuel.ghinet@qt.io>2022-03-24 10:31:16 +0000
commit3935c671dd3adcdb0fad7e8b57735868c81082c5 (patch)
tree78cb56b9cc950a188c221734426b64d00668a14d
parent4b96545a23486687aa04b91199beb302e4b68791 (diff)
downloadqt-creator-3935c671dd3adcdb0fad7e8b57735868c81082c5.tar.gz
QDS New Project Dialog fix: recents should include all project properties
Previously, only the wizard category, name, and size were saved for recent presets. Solved the problem by using the same kind of store (and struct type) for Recent presets as for User/Custom presets - this way we can save all properties. Other changes introduced: * After user creates custom preset C, then creates a project from it (resulting in the creation of a Recent preset R), if the user then deletes custom preset C, then the recent preset R will remain - previously, all recents of the custom preset were deleted * Now we can have multiple recent presets with the same name and size - so, no distinguishing feature inside the Presets view. User will have to look at Details and Styles panes to view differences. * Replaced .ini format with *.json file format. Change-Id: I500e9ac9378d4b9a393c3b0833ef6a34f785585c Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
-rw-r--r--src/plugins/studiowelcome/CMakeLists.txt1
-rw-r--r--src/plugins/studiowelcome/algorithm.h15
-rw-r--r--src/plugins/studiowelcome/presetmodel.cpp45
-rw-r--r--src/plugins/studiowelcome/presetmodel.h12
-rw-r--r--src/plugins/studiowelcome/qdsnewdialog.cpp67
-rw-r--r--src/plugins/studiowelcome/qdsnewdialog.h4
-rw-r--r--src/plugins/studiowelcome/recentpresets.cpp152
-rw-r--r--src/plugins/studiowelcome/recentpresets.h101
-rw-r--r--src/plugins/studiowelcome/studiowelcome.qbs2
-rw-r--r--src/plugins/studiowelcome/userpresets.cpp151
-rw-r--r--src/plugins/studiowelcome/userpresets.h51
-rw-r--r--tests/auto/qml/qmldesigner/wizard/CMakeLists.txt2
-rw-r--r--tests/auto/qml/qmldesigner/wizard/presetmodel-test.cpp116
-rw-r--r--tests/auto/qml/qmldesigner/wizard/recentpresets-test.cpp303
-rw-r--r--tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp174
15 files changed, 395 insertions, 801 deletions
diff --git a/src/plugins/studiowelcome/CMakeLists.txt b/src/plugins/studiowelcome/CMakeLists.txt
index 209bb80b01..82e2c78beb 100644
--- a/src/plugins/studiowelcome/CMakeLists.txt
+++ b/src/plugins/studiowelcome/CMakeLists.txt
@@ -13,7 +13,6 @@ add_qtc_plugin(StudioWelcome
wizardfactories.cpp wizardfactories.h
createproject.cpp createproject.h
wizardhandler.cpp wizardhandler.h
- recentpresets.cpp recentpresets.h
userpresets.cpp userpresets.h
screensizemodel.h
algorithm.h
diff --git a/src/plugins/studiowelcome/algorithm.h b/src/plugins/studiowelcome/algorithm.h
index b7e53da5f1..11d3d1d85d 100644
--- a/src/plugins/studiowelcome/algorithm.h
+++ b/src/plugins/studiowelcome/algorithm.h
@@ -42,6 +42,16 @@ template<typename C, typename F>
return it == end ? nullopt : make_optional(*it);
}
+template<typename C>
+[[nodiscard]] bool containsItem(const C &container, const typename C::value_type &item)
+{
+ auto begin = std::cbegin(container);
+ auto end = std::cend(container);
+
+ auto it = std::find(begin, end, item);
+ return it == end ? false : true;
+}
+
///////// FILTER
template<typename C, typename T = typename C::value_type>
[[nodiscard]] C filterOut(const C &container, const T &value = T())
@@ -67,13 +77,14 @@ void concat(C &out, const SC &container)
}
template<typename C, typename T>
-void erase_one(C &container, const T &value)
+bool 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;
+ return false;
container.erase(i);
+ return true;
}
template<typename C, typename T>
diff --git a/src/plugins/studiowelcome/presetmodel.cpp b/src/plugins/studiowelcome/presetmodel.cpp
index cc06bf59f5..6705b6ca65 100644
--- a/src/plugins/studiowelcome/presetmodel.cpp
+++ b/src/plugins/studiowelcome/presetmodel.cpp
@@ -47,7 +47,7 @@ QString PresetData::recentsTabName()
void PresetData::setData(const PresetsByCategory &presetsByCategory,
const std::vector<UserPresetData> &userPresetsData,
- const std::vector<RecentPresetData> &loadedRecentsData)
+ const std::vector<UserPresetData> &loadedRecentsData)
{
QTC_ASSERT(!presetsByCategory.empty(), return );
m_recents = loadedRecentsData;
@@ -60,16 +60,13 @@ void PresetData::setData(const PresetsByCategory &presetsByCategory,
PresetItems wizardPresets = Utils::flatten(m_presets);
- PresetItems userPresetItems = makeUserPresets(wizardPresets);
+ PresetItems userPresetItems = makeUserPresets(wizardPresets, m_userPresets);
if (!userPresetItems.empty()) {
m_categories.push_back(CustomTabName);
m_presets.push_back(userPresetItems);
}
- PresetItems allWizardPresets = std::move(wizardPresets);
- Utils::concat(allWizardPresets, userPresetItems);
-
- PresetItems recentPresets = makeRecentPresets(allWizardPresets);
+ PresetItems recentPresets = makeUserPresets(wizardPresets, m_recents);
if (!recentPresets.empty()) {
Utils::prepend(m_categories, RecentsTabName);
Utils::prepend(m_presets, recentPresets);
@@ -79,7 +76,7 @@ void PresetData::setData(const PresetsByCategory &presetsByCategory,
}
void PresetData::reload(const std::vector<UserPresetData> &userPresetsData,
- const std::vector<RecentPresetData> &loadedRecentsData)
+ const std::vector<UserPresetData> &loadedRecentsData)
{
m_categories.clear();
m_presets.clear();
@@ -96,11 +93,12 @@ std::shared_ptr<PresetItem> PresetData::findPresetItemForUserPreset(const UserPr
});
}
-PresetItems PresetData::makeUserPresets(const PresetItems &wizardPresets)
+PresetItems PresetData::makeUserPresets(const PresetItems &wizardPresets,
+ const std::vector<UserPresetData> &data)
{
PresetItems result;
- for (const UserPresetData &userPresetData : m_userPresets) {
+ for (const UserPresetData &userPresetData : data) {
std::shared_ptr<PresetItem> foundPreset = findPresetItemForUserPreset(userPresetData,
wizardPresets);
if (!foundPreset)
@@ -128,35 +126,6 @@ PresetItems PresetData::makeUserPresets(const PresetItems &wizardPresets)
return result;
}
-std::shared_ptr<PresetItem> PresetData::findPresetItemForRecent(const RecentPresetData &recent, const PresetItems &wizardPresets)
-{
- return Utils::findOrDefault(wizardPresets, [&recent](const std::shared_ptr<PresetItem> &item) {
- bool sameName = item->categoryId == recent.category
- && item->displayName() == recent.presetName;
-
- bool sameType = (recent.isUserPreset ? item->isUserPreset() : !item->isUserPreset());
-
- return sameName && sameType;
- });
-}
-
-PresetItems PresetData::makeRecentPresets(const PresetItems &wizardPresets)
-{
- PresetItems result;
-
- for (const RecentPresetData &recent : m_recents) {
- std::shared_ptr<PresetItem> preset = findPresetItemForRecent(recent, wizardPresets);
-
- if (preset) {
- auto clone = std::shared_ptr<PresetItem>{preset->clone()};
- clone->screenSizeName = recent.sizeName;
- result.push_back(clone);
- }
- }
-
- return result;
-}
-
/****************** BasePresetModel ******************/
BasePresetModel::BasePresetModel(const PresetData *data, QObject *parent)
diff --git a/src/plugins/studiowelcome/presetmodel.h b/src/plugins/studiowelcome/presetmodel.h
index e4c6712b81..19150e0fe9 100644
--- a/src/plugins/studiowelcome/presetmodel.h
+++ b/src/plugins/studiowelcome/presetmodel.h
@@ -33,7 +33,6 @@
#include <utils/optional.h>
#include <utils/qtcassert.h>
-#include "recentpresets.h"
#include "userpresets.h"
namespace Utils {
@@ -169,10 +168,10 @@ class PresetData
{
public:
void reload(const std::vector<UserPresetData> &userPresets,
- const std::vector<RecentPresetData> &loadedRecents);
+ const std::vector<UserPresetData> &loadedRecents);
void setData(const PresetsByCategory &presets,
const std::vector<UserPresetData> &userPresets,
- const std::vector<RecentPresetData> &recents);
+ const std::vector<UserPresetData> &recents);
const std::vector<PresetItems> &presets() const { return m_presets; }
const Categories &categories() const { return m_categories; }
@@ -180,16 +179,13 @@ public:
static QString recentsTabName();
private:
- PresetItems makeRecentPresets(const PresetItems &wizardPresets);
- PresetItems makeUserPresets(const PresetItems &wizardPresets);
-
+ PresetItems makeUserPresets(const PresetItems &wizardPresets, const std::vector<UserPresetData> &data);
std::shared_ptr<PresetItem> findPresetItemForUserPreset(const UserPresetData &preset, const PresetItems &wizardPresets);
- std::shared_ptr<PresetItem> findPresetItemForRecent(const RecentPresetData &recent, const PresetItems &wizardPresets);
private:
std::vector<PresetItems> m_presets;
Categories m_categories;
- std::vector<RecentPresetData> m_recents;
+ std::vector<UserPresetData> m_recents;
std::vector<UserPresetData> m_userPresets;
PresetsByCategory m_presetsByCategory;
};
diff --git a/src/plugins/studiowelcome/qdsnewdialog.cpp b/src/plugins/studiowelcome/qdsnewdialog.cpp
index 943ca0be32..ea0f741e1a 100644
--- a/src/plugins/studiowelcome/qdsnewdialog.cpp
+++ b/src/plugins/studiowelcome/qdsnewdialog.cpp
@@ -72,10 +72,14 @@ QdsNewDialog::QdsNewDialog(QWidget *parent)
, m_presetModel{new PresetModel(&m_presetData, this)}
, m_screenSizeModel{new ScreenSizeModel(this)}
, m_styleModel{new StyleModel(this)}
- , m_recentsStore{Core::ICore::settings()}
+ , m_recentsStore{"RecentPresets.json", StorePolicy::UniqueValues}
+ , m_userPresetsStore{"UserPresets.json", StorePolicy::UniqueNames}
{
setParent(m_dialog);
+ m_recentsStore.setReverseOrder();
+ m_recentsStore.setMaximum(10);
+
m_dialog->setResizeMode(QQuickWidget::SizeRootObjectToView); // SizeViewToRootObject
m_dialog->engine()->addImageProvider(QStringLiteral("newprojectdialog_library"),
new Internal::NewProjectDialogImageProvider());
@@ -190,8 +194,11 @@ void QdsNewDialog::updateScreenSizes()
void QdsNewDialog::onWizardCreated(QStandardItemModel *screenSizeModel, QStandardItemModel *styleModel)
{
- m_screenSizeModel->setBackendModel(screenSizeModel);
- m_styleModel->setBackendModel(styleModel);
+ if (screenSizeModel)
+ m_screenSizeModel->setBackendModel(screenSizeModel);
+
+ if (styleModel)
+ m_styleModel->setBackendModel(styleModel);
auto userPreset = m_currentPreset->asUserPreset();
@@ -326,7 +333,7 @@ void QdsNewDialog::setWizardFactories(QList<Core::IWizardFactory *> factories_,
WizardFactories factories{factories_, m_dialog, platform};
- std::vector<RecentPresetData> recents = m_recentsStore.fetchAll();
+ std::vector<UserPresetData> recents = m_recentsStore.fetchAll();
std::vector<UserPresetData> userPresets = m_userPresetsStore.fetchAll();
m_presetData.setData(factories.presetsGroupedByCategory(), userPresets, recents);
@@ -360,33 +367,13 @@ void QdsNewDialog::setWizardFactories(QList<Core::IWizardFactory *> factories_,
* sure that all events have occurred before we go ahead and configure the wizard.
*/
- auto userPreset = m_currentPreset->asUserPreset();
-
- if (m_qmlDetailsLoaded) {
- updateScreenSizes();
-
- if (m_wizard.haveTargetQtVersion()) {
- int index = (userPreset ? m_wizard.targetQtVersionIndex(userPreset->qtVersion)
- : m_wizard.targetQtVersionIndex());
- if (index != -1)
- setTargetQtVersionIndex(index);
- }
-
- if (m_wizard.haveVirtualKeyboard() && userPreset)
- setUseVirtualKeyboard(userPreset->useQtVirtualKeyboard);
-
- emit haveVirtualKeyboardChanged();
- emit haveTargetQtVersionChanged();
- }
+ /* onWizardCreated will have been called by this time, as a result of m_presetModel->reset(),
+ * but at that time the Details and Styles panes haven't been loaded yet - only the backend
+ * models loaded. We call it again, cause at this point those panes are now loaded, and we can
+ * set them up.
+ */
- if (m_qmlStylesLoaded && m_wizard.haveStyleModel()) {
- if (userPreset) {
- int index = m_wizard.styleIndex(userPreset->styleName);
- if (index != -1)
- setStyleIndex(index);
- }
- m_styleModel->reset();
- }
+ onWizardCreated(nullptr, nullptr);
}
QString QdsNewDialog::recentsTabName() const
@@ -431,7 +418,8 @@ void QdsNewDialog::accept()
std::shared_ptr<PresetItem> item = m_wizard.preset();
QString customSizeName = m_qmlCustomWidth + " x " + m_qmlCustomHeight;
- m_recentsStore.add(item->categoryId, item->displayName(), customSizeName, item->isUserPreset());
+ UserPresetData preset = currentUserPresetData(m_currentPreset->displayName());
+ m_recentsStore.save(preset);
m_dialog->close();
m_dialog->deleteLater();
@@ -471,7 +459,7 @@ void QdsNewDialog::setSelectedPreset(int selection)
}
}
-void QdsNewDialog::savePresetDialogAccept()
+UserPresetData QdsNewDialog::currentUserPresetData(const QString &displayName) const
{
QString screenSize = m_qmlCustomWidth + " x " + m_qmlCustomHeight;
QString targetQtVersion = "";
@@ -489,12 +477,19 @@ void QdsNewDialog::savePresetDialogAccept()
UserPresetData preset = {m_currentPreset->categoryId,
m_currentPreset->wizardName,
- m_qmlPresetName,
+ displayName,
screenSize,
useVirtualKeyboard,
targetQtVersion,
styleName};
+ return preset;
+}
+
+void QdsNewDialog::savePresetDialogAccept()
+{
+ UserPresetData preset = currentUserPresetData(m_qmlPresetName);
+
if (!m_userPresetsStore.save(preset)) {
QMessageBox::warning(m_dialog,
tr("Save Preset"),
@@ -503,7 +498,7 @@ void QdsNewDialog::savePresetDialogAccept()
}
// reload model
- std::vector<RecentPresetData> recents = m_recentsStore.fetchAll();
+ std::vector<UserPresetData> recents = m_recentsStore.fetchAll();
std::vector<UserPresetData> userPresets = m_userPresetsStore.fetchAll();
m_presetData.reload(userPresets, recents);
@@ -520,8 +515,8 @@ void QdsNewDialog::removeCurrentPreset()
}
// remove preset & reload model
- std::vector<RecentPresetData> recents = m_recentsStore.remove(m_currentPreset->categoryId,
- m_currentPreset->displayName());
+ UserPresetData currentPreset = currentUserPresetData(m_qmlPresetName);
+ std::vector<UserPresetData> recents = m_recentsStore.remove(currentPreset);
auto userPreset = m_currentPreset->asUserPreset();
m_userPresetsStore.remove(userPreset->categoryId, userPreset->displayName());
diff --git a/src/plugins/studiowelcome/qdsnewdialog.h b/src/plugins/studiowelcome/qdsnewdialog.h
index 09a425c499..47779c1b31 100644
--- a/src/plugins/studiowelcome/qdsnewdialog.h
+++ b/src/plugins/studiowelcome/qdsnewdialog.h
@@ -34,7 +34,6 @@
#include "presetmodel.h"
#include "screensizemodel.h"
#include "stylemodel.h"
-#include "recentpresets.h"
#include "userpresets.h"
QT_BEGIN_NAMESPACE
@@ -153,6 +152,7 @@ private:
void updateScreenSizes();
bool eventFilter(QObject *obj, QEvent *ev) override;
+ UserPresetData currentUserPresetData(const QString &displayName) const;
private slots:
void onDeletingWizard();
@@ -194,7 +194,7 @@ private:
std::shared_ptr<PresetItem> m_currentPreset;
WizardHandler m_wizard;
- RecentPresetsStore m_recentsStore;
+ UserPresetsStore m_recentsStore;
UserPresetsStore m_userPresetsStore;
};
diff --git a/src/plugins/studiowelcome/recentpresets.cpp b/src/plugins/studiowelcome/recentpresets.cpp
deleted file mode 100644
index cad2846d75..0000000000
--- a/src/plugins/studiowelcome/recentpresets.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/****************************************************************************
-**
-** 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 namespace StudioWelcome;
-
-constexpr char GROUP_NAME[] = "RecentPresets";
-constexpr char WIZARDS[] = "Wizards";
-
-void RecentPresetsStore::add(const QString &categoryId,
- const QString &name,
- const QString &sizeName,
- bool isUserPreset)
-{
- std::vector<RecentPresetData> existing = fetchAll();
-
- std::vector<RecentPresetData> recents
- = addRecentToExisting(RecentPresetData{categoryId, name, sizeName, isUserPreset}, existing);
-
- save(recents);
-}
-
-void RecentPresetsStore::save(const std::vector<RecentPresetData> &recents)
-{
- QStringList encodedRecents = encodeRecentPresets(recents);
-
- m_settings->beginGroup(GROUP_NAME);
- m_settings->setValue(WIZARDS, encodedRecents);
- m_settings->endGroup();
- m_settings->sync();
-}
-
-std::vector<RecentPresetData> RecentPresetsStore::remove(const QString &categoryId, const QString &presetName)
-{
- std::vector<RecentPresetData> recents = fetchAll();
- size_t countBefore = recents.size();
-
- /* NOTE: when removing one preset, it may happen that there are more than one recent for that
- * preset. In that case, we need to remove all associated recents, for the preset.*/
-
- Utils::erase(recents, [&](const RecentPresetData &p) {
- return p.category == categoryId && p.presetName == presetName;
- });
-
- if (recents.size() < countBefore)
- save(recents);
-
- return recents;
-}
-
-std::vector<RecentPresetData> RecentPresetsStore::addRecentToExisting(
- const RecentPresetData &preset, std::vector<RecentPresetData> &recents)
-{
- Utils::erase_one(recents, preset);
- Utils::prepend(recents, preset);
-
- if (int(recents.size()) > m_max)
- recents.pop_back();
-
- return recents;
-}
-
-std::vector<RecentPresetData> RecentPresetsStore::fetchAll() const
-{
- m_settings->beginGroup(GROUP_NAME);
- QVariant value = m_settings->value(WIZARDS);
- m_settings->endGroup();
-
- std::vector<RecentPresetData> 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 RecentPresetData empty;
- return Utils::filtered(result, [&empty](const RecentPresetData &recent) { return recent != empty; });
-}
-
-QStringList RecentPresetsStore::encodeRecentPresets(const std::vector<RecentPresetData> &recents)
-{
- return Utils::transform<QList>(recents, [](const RecentPresetData &p) -> QString {
- QString name = p.presetName;
- if (p.isUserPreset)
- name.prepend("[U]");
-
- return p.category + "/" + name + ":" + p.sizeName;
- });
-}
-
-RecentPresetData RecentPresetsStore::decodeOneRecentPreset(const QString &encoded)
-{
- QRegularExpression pattern{R"(^(\S+)/(.+):(\d+ x \d+)$)"};
- auto m = pattern.match(encoded);
- if (!m.hasMatch())
- return RecentPresetData{};
-
- QString category = m.captured(1);
- QString name = m.captured(2);
- QString size = m.captured(3);
- bool isUserPreset = name.startsWith("[U]");
- if (isUserPreset)
- name = name.split("[U]")[1];
-
- if (!QRegularExpression{R"(^\w[\w ]*$)"}.match(name).hasMatch())
- return RecentPresetData{};
-
- RecentPresetData result;
- result.category = category;
- result.presetName = name;
- result.sizeName = size;
- result.isUserPreset = isUserPreset;
-
- return result;
-}
-
-std::vector<RecentPresetData> 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
deleted file mode 100644
index 83ab5fb8cc..0000000000
--- a/src/plugins/studiowelcome/recentpresets.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/****************************************************************************
-**
-** 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 {
-
-struct RecentPresetData
-{
- RecentPresetData() = default;
- RecentPresetData(const QString &category,
- const QString &name,
- const QString &size,
- bool isUserPreset = false)
- : category{category}
- , presetName{name}
- , sizeName{size}
- , isUserPreset{isUserPreset}
- {}
-
- QString category;
- QString presetName;
- QString sizeName;
- bool isUserPreset = false;
-};
-
-inline bool operator==(const RecentPresetData &lhs, const RecentPresetData &rhs)
-{
- return lhs.category == rhs.category && lhs.presetName == rhs.presetName
- && lhs.sizeName == rhs.sizeName && lhs.isUserPreset == rhs.isUserPreset;
-}
-
-inline bool operator!=(const RecentPresetData &lhs, const RecentPresetData &rhs)
-{
- return !(lhs == rhs);
-}
-
-inline QDebug &operator<<(QDebug &d, const RecentPresetData &preset)
-{
- d << "RecentPreset{category=" << preset.category << "; name=" << preset.presetName
- << "; size=" << preset.sizeName << "; isUserPreset=" << preset.isUserPreset << "}";
-
- return d;
-}
-
-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,
- bool isUserPreset = false);
-
- std::vector<RecentPresetData> remove(const QString &categoryId, const QString &presetName);
- std::vector<RecentPresetData> fetchAll() const;
-
-private:
- std::vector<RecentPresetData> addRecentToExisting(const RecentPresetData &preset,
- std::vector<RecentPresetData> &recents);
- static QStringList encodeRecentPresets(const std::vector<RecentPresetData> &recents);
- static std::vector<RecentPresetData> decodeRecentPresets(const QVariantList &values);
- static RecentPresetData decodeOneRecentPreset(const QString &encoded);
- void save(const std::vector<RecentPresetData> &recents);
-
-private:
- QSettings *m_settings = nullptr;
- int m_max = 10;
-};
-
-} // namespace StudioWelcome
diff --git a/src/plugins/studiowelcome/studiowelcome.qbs b/src/plugins/studiowelcome/studiowelcome.qbs
index 9648eda14d..4c82c8112e 100644
--- a/src/plugins/studiowelcome/studiowelcome.qbs
+++ b/src/plugins/studiowelcome/studiowelcome.qbs
@@ -37,8 +37,6 @@ QtcPlugin {
"wizardfactories.h",
"wizardhandler.cpp",
"wizardhandler.h",
- "recentpresets.cpp",
- "recentpresets.h",
"userpresets.cpp",
"userpresets.h"
]
diff --git a/src/plugins/studiowelcome/userpresets.cpp b/src/plugins/studiowelcome/userpresets.cpp
index d468c3522e..e32c010bd6 100644
--- a/src/plugins/studiowelcome/userpresets.cpp
+++ b/src/plugins/studiowelcome/userpresets.cpp
@@ -24,43 +24,75 @@
****************************************************************************/
#include "userpresets.h"
+#include "algorithm.h"
+
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <QJsonValue>
#include <coreplugin/icore.h>
-#include <utils/algorithm.h>
+#include <memory>
#include <utils/qtcassert.h>
using namespace StudioWelcome;
-constexpr char PREFIX[] = "UserPresets";
+FileStoreIo::FileStoreIo(const QString &fileName)
+ : m_file{std::make_unique<QFile>(fullFilePath(fileName))}
+{}
+
+QByteArray FileStoreIo::read() const
+{
+ m_file->open(QFile::ReadOnly | QFile::Text);
+ QByteArray data = m_file->readAll();
+ m_file->close();
+
+ return data;
+}
+
+void FileStoreIo::write(const QByteArray &data)
+{
+ m_file->open(QFile::WriteOnly | QFile::Text);
+ m_file->write(data);
+ m_file->close();
+}
-UserPresetsStore::UserPresetsStore()
+QString FileStoreIo::fullFilePath(const QString &fileName) const
{
- m_settings = std::make_unique<QSettings>(fullFilePath(), QSettings::IniFormat);
+ return Core::ICore::userResourcePath(fileName).toString();
}
-UserPresetsStore::UserPresetsStore(std::unique_ptr<QSettings> &&settings)
- : m_settings{std::move(settings)}
+UserPresetsStore::UserPresetsStore(const QString &fileName, StorePolicy policy)
+ : m_store{std::make_unique<FileStoreIo>(fileName)}
+ , m_policy{policy}
+{}
+
+UserPresetsStore::UserPresetsStore(std::unique_ptr<StoreIo> &&fileStore,
+ StorePolicy policy)
+ : m_store{std::move(fileStore)}
+ , m_policy{policy}
{}
-void UserPresetsStore::savePresets(const std::vector<UserPresetData> &presets)
+void UserPresetsStore::savePresets(const std::vector<UserPresetData> &presetItems)
{
- m_settings->beginWriteArray(PREFIX, static_cast<int>(presets.size()));
-
- for (size_t i = 0; i < presets.size(); ++i) {
- m_settings->setArrayIndex(static_cast<int>(i));
- const auto &preset = presets[i];
-
- m_settings->setValue("categoryId", preset.categoryId);
- m_settings->setValue("wizardName", preset.wizardName);
- m_settings->setValue("name", preset.name);
- m_settings->setValue("screenSize", preset.screenSize);
- m_settings->setValue("useQtVirtualKeyboard", preset.useQtVirtualKeyboard);
- m_settings->setValue("qtVersion", preset.qtVersion);
- m_settings->setValue("styleName", preset.styleName);
+ QJsonArray jsonArray;
+
+ for (const auto &preset : presetItems) {
+ QJsonObject obj({{"categoryId", preset.categoryId},
+ {"wizardName", preset.wizardName},
+ {"name", preset.name},
+ {"screenSize", preset.screenSize},
+ {"useQtVirtualKeyboard", preset.useQtVirtualKeyboard},
+ {"qtVersion", preset.qtVersion},
+ {"styleName", preset.styleName}});
+
+ jsonArray.append(QJsonValue{obj});
}
- m_settings->endArray();
- m_settings->sync();
+ QJsonDocument doc(jsonArray);
+ QByteArray data = doc.toJson();
+
+ m_store->write(data);
}
bool UserPresetsStore::save(const UserPresetData &newPreset)
@@ -68,12 +100,30 @@ bool UserPresetsStore::save(const UserPresetData &newPreset)
QTC_ASSERT(newPreset.isValid(), return false);
std::vector<UserPresetData> presetItems = fetchAll();
- if (Utils::anyOf(presetItems,
- [&newPreset](const UserPresetData &p) { return p.name == newPreset.name; })) {
- return false;
+
+ if (m_policy == StorePolicy::UniqueNames) {
+ if (Utils::anyOf(presetItems, [&newPreset](const UserPresetData &p) {
+ return p.name == newPreset.name;
+ })) {
+ return false;
+ }
+ } else if (m_policy == StorePolicy::UniqueValues) {
+ if (Utils::containsItem(presetItems, newPreset))
+ return false;
+ }
+
+ if (m_reverse)
+ Utils::prepend(presetItems, newPreset);
+ else
+ presetItems.push_back(newPreset);
+
+ if (m_maximum > -1 && static_cast<int>(presetItems.size()) > m_maximum) {
+ if (m_reverse)
+ presetItems.pop_back();
+ else
+ presetItems.erase(std::cbegin(presetItems));
}
- presetItems.push_back(newPreset);
savePresets(presetItems);
return true;
@@ -92,34 +142,45 @@ void UserPresetsStore::remove(const QString &category, const QString &name)
savePresets(presetItems);
}
+std::vector<UserPresetData> UserPresetsStore::remove(const UserPresetData &preset)
+{
+ std::vector<UserPresetData> presetItems = fetchAll();
+ bool erased = Utils::erase_one(presetItems, preset);
+ if (erased)
+ savePresets(presetItems);
+
+ return presetItems;
+}
+
std::vector<UserPresetData> UserPresetsStore::fetchAll() const
{
+ QByteArray data = m_store->read();
+
+ const QJsonDocument doc = QJsonDocument::fromJson(data);
+ if (!doc.isArray())
+ return {};
+
std::vector<UserPresetData> result;
- int size = m_settings->beginReadArray(PREFIX);
- if (size >= 0)
- result.reserve(static_cast<size_t>(size) + 1);
+ const QJsonArray jsonArray = doc.array();
- for (int i = 0; i < size; ++i) {
- m_settings->setArrayIndex(i);
+ for (const QJsonValue &value: jsonArray) {
+ if (!value.isObject())
+ continue;
+ const QJsonObject obj = value.toObject();
UserPresetData preset;
- preset.categoryId = m_settings->value("categoryId").toString();
- preset.wizardName = m_settings->value("wizardName").toString();
- preset.name = m_settings->value("name").toString();
- preset.screenSize = m_settings->value("screenSize").toString();
- preset.useQtVirtualKeyboard = m_settings->value("useQtVirtualKeyboard").toBool();
- preset.qtVersion = m_settings->value("qtVersion").toString();
- preset.styleName = m_settings->value("styleName").toString();
+
+ preset.categoryId = obj["categoryId"].toString();
+ preset.wizardName = obj["wizardName"].toString();
+ preset.name = obj["name"].toString();
+ preset.screenSize = obj["screenSize"].toString();
+ preset.useQtVirtualKeyboard = obj["useQtVirtualKeyboard"].toBool();
+ preset.qtVersion = obj["qtVersion"].toString();
+ preset.styleName = obj["styleName"].toString();
if (preset.isValid())
- result.push_back(std::move(preset));
+ result.push_back(preset);
}
- m_settings->endArray();
return result;
}
-
-QString UserPresetsStore::fullFilePath() const
-{
- return Core::ICore::userResourcePath("UserPresets.ini").toString();
-}
diff --git a/src/plugins/studiowelcome/userpresets.h b/src/plugins/studiowelcome/userpresets.h
index 4f6053ef26..3f614f4ea0 100644
--- a/src/plugins/studiowelcome/userpresets.h
+++ b/src/plugins/studiowelcome/userpresets.h
@@ -25,8 +25,10 @@
#pragma once
+#include <memory>
#include <vector>
-#include <QSettings>
+#include <QFile>
+#include <QDebug>
namespace StudioWelcome {
@@ -68,24 +70,59 @@ inline bool operator==(const UserPresetData &lhs, const UserPresetData &rhs)
return lhs.categoryId == rhs.categoryId && lhs.wizardName == rhs.wizardName
&& lhs.name == rhs.name && lhs.screenSize == rhs.screenSize
&& lhs.useQtVirtualKeyboard == rhs.useQtVirtualKeyboard && lhs.qtVersion == rhs.qtVersion
- && lhs.styleName == rhs.styleName;
+ && lhs.styleName == rhs.styleName;;
}
+enum class StorePolicy {UniqueNames, UniqueValues};
+
+class StoreIo
+{
+public:
+ virtual ~StoreIo() {}
+
+ virtual QByteArray read() const = 0;
+ virtual void write(const QByteArray &bytes) = 0;
+};
+
+class FileStoreIo : public StoreIo
+{
+public:
+ explicit FileStoreIo(const QString &fileName);
+ FileStoreIo(FileStoreIo &&other): m_file{std::move(other.m_file)} {}
+ FileStoreIo& operator=(FileStoreIo &&other) { m_file = std::move(other.m_file); return *this; }
+
+ QByteArray read() const override;
+ void write(const QByteArray &data) override;
+
+private:
+ QString fullFilePath(const QString &fileName) const;
+
+ std::unique_ptr<QFile> m_file;
+
+ Q_DISABLE_COPY(FileStoreIo)
+};
+
class UserPresetsStore
{
public:
- UserPresetsStore();
- UserPresetsStore(std::unique_ptr<QSettings> &&settings);
+ UserPresetsStore(const QString &fileName, StorePolicy policy);
+ UserPresetsStore(std::unique_ptr<StoreIo> &&store, StorePolicy policy);
bool save(const UserPresetData &preset);
- void remove(const QString &category, const QString &name);
std::vector<UserPresetData> fetchAll() const;
+ void remove(const QString &category, const QString &name);
+ std::vector<UserPresetData> remove(const UserPresetData &preset);
+
+ void setMaximum(int maximum) { m_maximum = maximum; }
+ void setReverseOrder() { m_reverse = true; }
private:
- QString fullFilePath() const;
void savePresets(const std::vector<UserPresetData> &presets);
- std::unique_ptr<QSettings> m_settings;
+ std::unique_ptr<StoreIo> m_store;
+ StorePolicy m_policy = StorePolicy::UniqueNames;
+ bool m_reverse = false;
+ int m_maximum = -1;
};
} // namespace StudioWelcome
diff --git a/tests/auto/qml/qmldesigner/wizard/CMakeLists.txt b/tests/auto/qml/qmldesigner/wizard/CMakeLists.txt
index c5040112ef..a2965b22f1 100644
--- a/tests/auto/qml/qmldesigner/wizard/CMakeLists.txt
+++ b/tests/auto/qml/qmldesigner/wizard/CMakeLists.txt
@@ -17,14 +17,12 @@ add_qtc_test(tst_qml_wizard
SOURCES
wizardfactories-test.cpp
stylemodel-test.cpp
- recentpresets-test.cpp
userpresets-test.cpp
presetmodel-test.cpp
test-utilities.h
test-main.cpp
"${StudioWelcomeDir}/wizardfactories.cpp"
"${StudioWelcomeDir}/stylemodel.cpp"
- "${StudioWelcomeDir}/recentpresets.cpp"
"${StudioWelcomeDir}/userpresets.cpp"
"${StudioWelcomeDir}/presetmodel.cpp"
)
diff --git a/tests/auto/qml/qmldesigner/wizard/presetmodel-test.cpp b/tests/auto/qml/qmldesigner/wizard/presetmodel-test.cpp
index 3c16688458..8452ca8222 100644
--- a/tests/auto/qml/qmldesigner/wizard/presetmodel-test.cpp
+++ b/tests/auto/qml/qmldesigner/wizard/presetmodel-test.cpp
@@ -100,6 +100,14 @@ UserPresetData aUserPreset(const QString &categId, const QString &wizardName, co
return preset;
}
+UserPresetData aRecentPreset(const QString &categId, const QString &wizardName, const QString &screenSizeName)
+{
+ UserPresetData preset = aUserPreset(categId, wizardName, wizardName);
+ preset.screenSize = screenSizeName;
+
+ return preset;
+}
+
MATCHER_P2(PresetIs, category, name, PrintToString(PresetItem{name, category}))
{
return arg->categoryId == category && arg->wizardName == name;
@@ -139,6 +147,7 @@ TEST(QdsPresetModel, haveSameArraySizeForPresetsAndCategories)
PresetData data;
data.setData(
+ /*wizard presets*/
{
aCategory("A.categ", "A", {"item a", "item b"}),
aCategory("B.categ", "B", {"item c", "item d"}),
@@ -157,6 +166,7 @@ TEST(QdsPresetModel, haveWizardPresetsNoRecents)
// When
data.setData(
+ /*wizard presets*/
{
aCategory("A.categ", "A", {"item a", "item b"}),
aCategory("B.categ", "B", {"item c", "item d"}),
@@ -179,6 +189,7 @@ TEST(QdsPresetModel, whenHaveUserPresetsButNoWizardPresetsReturnEmpty)
// When
data.setData({/*builtin presets*/},
+ /*user presets*/
{
aUserPreset("A.Mobile", "Scroll", "iPhone5"),
aUserPreset("B.Desktop", "Launcher", "MacBook"),
@@ -196,9 +207,10 @@ TEST(QdsPresetModel, haveRecentsNoWizardPresets)
data.setData({/*wizardPresets*/},
{/*user presets*/},
+ /*recent presets*/
{
- {"A.categ", "Desktop", "640 x 480"},
- {"B.categ", "Mobile", "800 x 600"},
+ aRecentPreset("A.categ", "Desktop", "640 x 480"),
+ aRecentPreset("B.categ", "Mobile", "800 x 600"),
});
ASSERT_THAT(data.categories(), IsEmpty());
@@ -220,8 +232,8 @@ TEST(QdsPresetModel, recentsAddedWithWizardPresets)
{/*user presets*/},
/*recents*/
{
- {"A.categ", "Desktop", "800 x 600"},
- {"B.categ", "Mobile", "640 x 480"},
+ aRecentPreset("A.categ", "Desktop", "800 x 600"),
+ aRecentPreset("B.categ", "Mobile", "640 x 480"),
});
// Then
@@ -247,6 +259,7 @@ TEST(QdsPresetModel, userPresetsAddedWithWizardPresets)
aCategory("A.categ", "A", {"Desktop", "item b"}),
aCategory("B.categ", "B", {"Mobile"}),
},
+ /*user presets*/
{
aUserPreset("A.categ", "Desktop", "Windows10"),
},
@@ -272,6 +285,7 @@ TEST(QdsPresetModel, doesNotAddUserPresetsOfNonExistingCategory)
{
aCategory("A.categ", "A", {"Desktop"}), // Only category "A.categ" exists
},
+ /*user presets*/
{
aUserPreset("Bad.Categ", "Desktop", "Windows8"), // Bad.Categ does not exist
},
@@ -293,6 +307,7 @@ TEST(QdsPresetModel, doesNotAddUserPresetIfWizardPresetItRefersToDoesNotExist)
{
aCategory("A.categ", "A", {"Desktop"}),
},
+ /*user presets*/
{
aUserPreset("B.categ", "BadWizard", "Tablet"), // BadWizard referenced does not exist
},
@@ -314,6 +329,7 @@ TEST(QdsPresetModel, userPresetWithSameNameAsWizardPreset)
{
aCategory("A.categ", "A", {"Desktop"}),
},
+ /*user presets*/
{
aUserPreset("A.categ", "Desktop", "Desktop"),
},
@@ -326,34 +342,7 @@ TEST(QdsPresetModel, userPresetWithSameNameAsWizardPreset)
ElementsAre(UserPresetIs("A.categ", "Desktop", "Desktop"))));
}
-TEST(QdsPresetModel, recentOfUserPresetReferringToExistingWizardPreset)
-{
- // Given
- PresetData data;
-
- // When
- data.setData(
- /*wizard presets*/
- {
- aCategory("A.categ", "A", {"Desktop"}),
- },
- {
- aUserPreset("A.categ", "Desktop", "Windows 7"),
- },
- /*recents*/
- {
- {"A.categ", "Windows 7", "200 x 300", /*is user*/true}
- });
-
- // Then
- ASSERT_THAT(data.categories(), ElementsAre("Recents", "A", "Custom"));
- ASSERT_THAT(data.presets(),
- ElementsAre(ElementsAre(UserPresetIs("A.categ", "Desktop", "Windows 7")),
- ElementsAre(PresetIs("A.categ", "Desktop")),
- ElementsAre(UserPresetIs("A.categ", "Desktop", "Windows 7"))));
-}
-
-TEST(QdsPresetModel, recentOfUserPresetReferringToNonexistingWizardPreset)
+TEST(QdsPresetModel, recentOfNonExistentWizardPreset)
{
// Given
PresetData data;
@@ -364,12 +353,10 @@ TEST(QdsPresetModel, recentOfUserPresetReferringToNonexistingWizardPreset)
{
aCategory("A.categ", "A", {"Desktop"}),
},
- {
- aUserPreset("A.categ", "Not-Desktop", "Windows 7"), // Non-existing Wizard Preset
- },
+ {/*user presets*/},
/*recents*/
{
- {"A.categ", "Windows 7", "200 x 300", /*is user*/true}
+ aRecentPreset("A.categ", "Windows 7", "200 x 300")
});
// Then
@@ -377,7 +364,7 @@ TEST(QdsPresetModel, recentOfUserPresetReferringToNonexistingWizardPreset)
ASSERT_THAT(data.presets(), ElementsAre(ElementsAre(PresetIs("A.categ", "Desktop"))));
}
-TEST(QdsPresetModel, recentOfNonExistentUserPreset)
+TEST(QdsPresetModel, recentsShouldNotBeSorted)
{
// Given
PresetData data;
@@ -386,20 +373,26 @@ TEST(QdsPresetModel, recentOfNonExistentUserPreset)
data.setData(
/*wizard presets*/
{
- aCategory("A.categ", "A", {"Desktop"}),
+ aCategory("A.categ", "A", {"Desktop", "item b"}),
+ aCategory("B.categ", "B", {"item c", "Mobile"}),
+ aCategory("Z.categ", "Z", {"Z.desktop"}),
},
{/*user presets*/},
/*recents*/
{
- {"A.categ", "Windows 7", "200 x 300", /*is user*/true}
+ aRecentPreset("Z.categ", "Z.desktop", "200 x 300"),
+ aRecentPreset("B.categ", "Mobile", "200 x 300"),
+ aRecentPreset("A.categ", "Desktop", "200 x 300"),
});
// Then
- ASSERT_THAT(data.categories(), ElementsAre("A"));
- ASSERT_THAT(data.presets(), ElementsAre(ElementsAre(PresetIs("A.categ", "Desktop"))));
+ ASSERT_THAT(data.presets()[0],
+ ElementsAre(PresetIs("Z.categ", "Z.desktop"),
+ PresetIs("B.categ", "Mobile"),
+ PresetIs("A.categ", "Desktop")));
}
-TEST(QdsPresetModel, recentsShouldNotBeSorted)
+TEST(QdsPresetModel, recentsOfSameWizardProjectButDifferentSizesAreRecognizedAsDifferentPresets)
{
// Given
PresetData data;
@@ -408,26 +401,25 @@ TEST(QdsPresetModel, recentsShouldNotBeSorted)
data.setData(
/*wizard presets*/
{
- aCategory("A.categ", "A", {"Desktop", "item b"}),
- aCategory("B.categ", "B", {"item c", "Mobile"}),
- aCategory("Z.categ", "Z", {"Z.desktop"}),
+ aCategory("A.categ", "A", {"Desktop"}),
+ aCategory("B.categ", "B", {"Mobile"}),
},
{/*user presets*/},
/*recents*/
{
- {"Z.categ", "Z.desktop", "200 x 300"},
- {"B.categ", "Mobile", "200 x 300"},
- {"A.categ", "Desktop", "200 x 300"},
+ aRecentPreset("B.categ", "Mobile", "400 x 400"),
+ aRecentPreset("B.categ", "Mobile", "200 x 300"),
+ aRecentPreset("A.categ", "Desktop", "640 x 480"),
});
// Then
ASSERT_THAT(data.presets()[0],
- ElementsAre(PresetIs("Z.categ", "Z.desktop"),
- PresetIs("B.categ", "Mobile"),
- PresetIs("A.categ", "Desktop")));
+ ElementsAre(PresetIs("B.categ", "Mobile", "400 x 400"),
+ PresetIs("B.categ", "Mobile", "200 x 300"),
+ PresetIs("A.categ", "Desktop", "640 x 480")));
}
-TEST(QdsPresetModel, recentsOfSameWizardProjectButDifferentSizesAreRecognizedAsDifferentPresets)
+TEST(QdsPresetModel, allowRecentsWithTheSameName)
{
// Given
PresetData data;
@@ -437,21 +429,23 @@ TEST(QdsPresetModel, recentsOfSameWizardProjectButDifferentSizesAreRecognizedAsD
/*wizard presets*/
{
aCategory("A.categ", "A", {"Desktop"}),
- aCategory("B.categ", "B", {"Mobile"}),
},
{/*user presets*/},
/*recents*/
{
- {"B.categ", "Mobile", "400 x 400"},
- {"B.categ", "Mobile", "200 x 300"},
- {"A.categ", "Desktop", "640 x 480"},
+ /* NOTE: it is assumed recents with the same name and size have other fields that
+ * distinguishes them from one another. It is the responsibility of the caller, who
+ * calls data.setData() to make sure that the recents do not contain duplicates. */
+ aRecentPreset("A.categ", "Desktop", "200 x 300"),
+ aRecentPreset("A.categ", "Desktop", "200 x 300"),
+ aRecentPreset("A.categ", "Desktop", "200 x 300"),
});
// 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")));
+ ElementsAre(PresetIs("A.categ", "Desktop"),
+ PresetIs("A.categ", "Desktop"),
+ PresetIs("A.categ", "Desktop")));
}
TEST(QdsPresetModel, outdatedRecentsAreNotShown)
@@ -469,8 +463,8 @@ TEST(QdsPresetModel, outdatedRecentsAreNotShown)
{/*user presets*/},
/*recents*/
{
- {"B.categ", "NoLongerExists", "400 x 400"},
- {"A.categ", "Desktop", "640 x 480"},
+ aRecentPreset("B.categ", "NoLongerExists", "400 x 400"),
+ aRecentPreset("A.categ", "Desktop", "640 x 480"),
});
// Then
diff --git a/tests/auto/qml/qmldesigner/wizard/recentpresets-test.cpp b/tests/auto/qml/qmldesigner/wizard/recentpresets-test.cpp
deleted file mode 100644
index 745462f5c1..0000000000
--- a/tests/auto/qml/qmldesigner/wizard/recentpresets-test.cpp
+++ /dev/null
@@ -1,303 +0,0 @@
-/****************************************************************************
-**
-** 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";
-
-namespace StudioWelcome {
-void PrintTo(const RecentPresetData &recent, std::ostream *os)
-{
- *os << "{categId: " << recent.category << ", name: " << recent.presetName
- << ", size: " << recent.sizeName << ", isUser: " << recent.isUserPreset;
-
- *os << "}";
-}
-} // namespace StudioWelcome
-
-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<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents, IsEmpty());
-}
-
-TEST_F(QdsRecentPresets, readEmptyRecentPresets)
-{
- RecentPresetsStore store = aStoreWithOne("");
-
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents, IsEmpty());
-}
-
-TEST_F(QdsRecentPresets, readOneRecentPresetAsList)
-{
- RecentPresetsStore store = aStoreWithRecents({"category/preset:640 x 480"});
-
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents, ElementsAre(RecentPresetData("category", "preset", "640 x 480")));
-}
-
-TEST_F(QdsRecentPresets, readOneRecentPresetAsString)
-{
- RecentPresetsStore store = aStoreWithOne("category/preset:200 x 300");
-
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents, ElementsAre(RecentPresetData("category", "preset", "200 x 300")));
-}
-
-TEST_F(QdsRecentPresets, readOneRecentUserPresetAsString)
-{
- RecentPresetsStore store = aStoreWithOne("category/[U]preset:200 x 300");
-
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents, ElementsAre(RecentPresetData("category", "preset", "200 x 300", true)));
-}
-
-TEST_F(QdsRecentPresets, readBadRecentPresetAsString)
-{
- RecentPresetsStore store = aStoreWithOne("no_category_only_preset");
-
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents, IsEmpty());
-}
-
-TEST_F(QdsRecentPresets, readBadRecentPresetAsInt)
-{
- RecentPresetsStore store = aStoreWithOne(32);
-
- std::vector<RecentPresetData> 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
- "categ/[U]user:300 x 200", // good
- "categ/[u]user2:300 x 200", // small cap "U"
- "categ/[x]user3:300 x 200", // must be letter "U"
- "categ/[U] user4:300 x 200", // space
- });
-
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents,
- ElementsAre(RecentPresetData("categ", "name", "800 x 600", false),
- RecentPresetData("categ", "user", "300 x 200", true)));
-}
-
-TEST_F(QdsRecentPresets, readTwoRecentPresets)
-{
- RecentPresetsStore store = aStoreWithRecents(
- {"category_1/preset 1:640 x 480", "category_2/preset 2:320 x 200"});
-
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents,
- ElementsAre(RecentPresetData("category_1", "preset 1", "640 x 480"),
- RecentPresetData("category_2", "preset 2", "320 x 200")));
-}
-
-TEST_F(QdsRecentPresets, readRecentsToDifferentKindsOfPresets)
-{
- RecentPresetsStore store = aStoreWithRecents(
- {"category_1/preset 1:640 x 480", "category_2/[U]preset 2:320 x 200"});
-
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents,
- ElementsAre(RecentPresetData("category_1", "preset 1", "640 x 480", false),
- RecentPresetData("category_2", "preset 2", "320 x 200", true)));
-}
-
-TEST_F(QdsRecentPresets, addFirstRecentPreset)
-{
- RecentPresetsStore store{&settings};
-
- store.add("A.Category", "Normal Application", "400 x 600");
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents, ElementsAre(RecentPresetData("A.Category", "Normal Application", "400 x 600")));
-}
-
-TEST_F(QdsRecentPresets, addFirstRecentUserPreset)
-{
- RecentPresetsStore store{&settings};
-
- store.add("A.Category", "Normal Application", "400 x 600", /*user preset*/ true);
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents,
- ElementsAre(RecentPresetData("A.Category", "Normal Application", "400 x 600", true)));
-}
-
-TEST_F(QdsRecentPresets, addExistingFirstRecentPreset)
-{
- RecentPresetsStore store = aStoreWithRecents({"category/preset:200 x 300"});
- ASSERT_THAT(store.fetchAll(), ElementsAre(RecentPresetData("category", "preset", "200 x 300")));
-
- store.add("category", "preset", "200 x 300");
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents, ElementsAre(RecentPresetData("category", "preset", "200 x 300")));
-}
-
-TEST_F(QdsRecentPresets, addRecentUserPresetWithSameNameAsExistingRecentNormalPreset)
-{
- RecentPresetsStore store = aStoreWithRecents({"category/preset:200 x 300"});
- ASSERT_THAT(store.fetchAll(), ElementsAre(RecentPresetData("category", "preset", "200 x 300")));
-
- store.add("category", "preset", "200 x 300", /*user preset*/ true);
- std::vector<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents,
- ElementsAre(RecentPresetData("category", "preset", "200 x 300", true),
- RecentPresetData("category", "preset", "200 x 300", false)));
-}
-
-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<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents,
- ElementsAre(RecentPresetData("A.Category", "Preset 2", "640 x 480"),
- RecentPresetData("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<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents,
- ElementsAre(RecentPresetData("A.Category", "Preset", "640 x 480"),
- RecentPresetData("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<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents,
- ElementsAre(RecentPresetData("A.Category", "Preset 3", "800 x 600"),
- RecentPresetData("A.Category", "Preset 2", "640 x 480"),
- RecentPresetData("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<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents,
- ElementsAre(RecentPresetData("A.Category", "Preset 3", "640 x 480"),
- RecentPresetData("A.Category", "Preset 1", "200 x 300"),
- RecentPresetData("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<RecentPresetData> recents = store.fetchAll();
-
- ASSERT_THAT(recents,
- ElementsAre(RecentPresetData("A.Category", "Preset 3", "200 x 300"),
- RecentPresetData("A.Category", "Preset 2", "200 x 300")));
-}
diff --git a/tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp b/tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp
index 8fa73402c0..6ed562621b 100644
--- a/tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp
+++ b/tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp
@@ -29,6 +29,10 @@
#include <utils/filepath.h>
#include <utils/temporarydirectory.h>
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QJsonDocument>
+
namespace StudioWelcome {
void PrintTo(const UserPresetData &preset, std::ostream *os)
@@ -64,69 +68,85 @@ using namespace StudioWelcome;
constexpr char ARRAY_NAME[] = "UserPresets";
+class FakeStoreIo : public StoreIo
+{
+public:
+ QByteArray read() const override
+ {
+ return data.toUtf8();
+ }
+
+ void write(const QByteArray &bytes) override
+ {
+ data = bytes;
+ }
+
+ QString data;
+};
+
class QdsUserPresets : public ::testing::Test
{
protected:
void SetUp()
{
- settings = std::make_unique<QSettings>(tempDir.filePath("test").toString(),
- QSettings::IniFormat);
+ storeIo = std::make_unique<FakeStoreIo>();
}
- UserPresetsStore anEmptyStore() { return UserPresetsStore{std::move(settings)}; }
+ UserPresetsStore anEmptyStore()
+ {
+ return UserPresetsStore{std::move(storeIo), StorePolicy::UniqueNames};
+ }
UserPresetsStore aStoreWithZeroItems()
{
- settings->beginWriteArray(ARRAY_NAME, 0);
- settings->endArray();
+ storeIo->data = "[]";
- return UserPresetsStore{std::move(settings)};
+ return UserPresetsStore{std::move(storeIo), StorePolicy::UniqueNames};
}
- UserPresetsStore aStoreWithOne(const UserPresetData &preset)
+ UserPresetsStore aStoreWithOne(const UserPresetData &preset,
+ StorePolicy policy = StorePolicy::UniqueNames)
{
- settings->beginWriteArray(ARRAY_NAME, 1);
- settings->setArrayIndex(0);
-
- settings->setValue("categoryId", preset.categoryId);
- settings->setValue("wizardName", preset.wizardName);
- settings->setValue("name", preset.name);
- settings->setValue("screenSize", preset.screenSize);
- settings->setValue("useQtVirtualKeyboard", preset.useQtVirtualKeyboard);
- settings->setValue("qtVersion", preset.qtVersion);
- settings->setValue("styleName", preset.styleName);
-
- settings->endArray();
-
- return UserPresetsStore{std::move(settings)};
+ QJsonArray array({QJsonObject{{"categoryId", preset.categoryId},
+ {"wizardName", preset.wizardName},
+ {"name", preset.name},
+ {"screenSize", preset.screenSize},
+ {"useQtVirtualKeyboard", preset.useQtVirtualKeyboard},
+ {"qtVersion", preset.qtVersion},
+ {"styleName", preset.styleName}}});
+ QJsonDocument doc{array};
+ storeIo->data = doc.toJson();
+
+ return UserPresetsStore{std::move(storeIo), policy};
}
- UserPresetsStore aStoreWithPresets(const std::vector<UserPresetData> &presets)
+ UserPresetsStore aStoreWithPresets(const std::vector<UserPresetData> &presetItems)
{
- settings->beginWriteArray(ARRAY_NAME, presets.size());
-
- for (size_t i = 0; i < presets.size(); ++i) {
- settings->setArrayIndex(i);
- const auto &preset = presets[i];
-
- settings->setValue("categoryId", preset.categoryId);
- settings->setValue("wizardName", preset.wizardName);
- settings->setValue("name", preset.name);
- settings->setValue("screenSize", preset.screenSize);
- settings->setValue("useQtVirtualKeyboard", preset.useQtVirtualKeyboard);
- settings->setValue("qtVersion", preset.qtVersion);
- settings->setValue("styleName", preset.styleName);
+ QJsonArray array;
+
+ for (const auto &preset : presetItems) {
+ QJsonObject obj({{"categoryId", preset.categoryId},
+ {"wizardName", preset.wizardName},
+ {"name", preset.name},
+ {"screenSize", preset.screenSize},
+ {"useQtVirtualKeyboard", preset.useQtVirtualKeyboard},
+ {"qtVersion", preset.qtVersion},
+ {"styleName", preset.styleName}});
+
+ array.append(QJsonValue{obj});
}
- settings->endArray();
- return UserPresetsStore{std::move(settings)};
+ QJsonDocument doc{array};
+ storeIo->data = doc.toJson();
+
+ return UserPresetsStore{std::move(storeIo), StorePolicy::UniqueNames};
}
Utils::TemporaryDirectory tempDir{"userpresets-XXXXXX"};
- std::unique_ptr<QSettings> settings;
+ std::unique_ptr<FakeStoreIo> storeIo;
private:
- QString settingsPath;
+ QString storeIoPath;
};
/******************* TESTS *******************/
@@ -234,10 +254,10 @@ TEST_F(QdsUserPresets, saveIncompletePreset)
ASSERT_THAT(presets, ElementsAre(preset));
}
-TEST_F(QdsUserPresets, cannotSavePresetWithSameName)
+TEST_F(QdsUserPresets, cannotSavePresetWithSameNameForUniqueNamesPolicy)
{
UserPresetData existing{"B.categ", "3D App", "Same Name", "400 x 20", true, "Qt 5", "Material Dark"};
- auto store = aStoreWithOne(existing);
+ auto store = aStoreWithOne(existing, StorePolicy::UniqueNames);
UserPresetData newPreset{"C.categ", "Empty", "Same Name", "100 x 30", false, "Qt 6", "Fusion"};
bool saved = store.save(newPreset);
@@ -246,6 +266,30 @@ TEST_F(QdsUserPresets, cannotSavePresetWithSameName)
ASSERT_THAT(store.fetchAll(), ElementsAreArray({existing}));
}
+TEST_F(QdsUserPresets, canSavePresetWithSameNameForUniqueValuesPolicy)
+{
+ UserPresetData existing{"B.categ", "3D App", "Same Name", "400 x 20", true, "Qt 5", "Material Dark"};
+ auto store = aStoreWithOne(existing, StorePolicy::UniqueValues);
+
+ // NOTE: only Style is different
+ UserPresetData newPreset{"B.categ", "3D App", "Same Name", "400 x 20", true, "Qt 5", "Fusion"};
+ bool saved = store.save(newPreset);
+
+ ASSERT_TRUE(saved);
+ ASSERT_THAT(store.fetchAll(), ElementsAreArray({existing, newPreset}));
+}
+
+TEST_F(QdsUserPresets, cannotSaveExactCopyForUniqueValuesPolicy)
+{
+ UserPresetData existing{"B.categ", "3D App", "Same Name", "400 x 20", true, "Qt 5", "Material Dark"};
+ auto store = aStoreWithOne(existing, StorePolicy::UniqueNames);
+
+ bool saved = store.save(existing);
+
+ ASSERT_FALSE(saved);
+ ASSERT_THAT(store.fetchAll(), ElementsAreArray({existing}));
+}
+
TEST_F(QdsUserPresets, saveNewPreset)
{
UserPresetData existing{"A.categ", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"};
@@ -258,6 +302,54 @@ TEST_F(QdsUserPresets, saveNewPreset)
ASSERT_THAT(presets, ElementsAre(existing, newPreset));
}
+TEST_F(QdsUserPresets, canLimitPresetsToAMaximum)
+{
+ std::vector<UserPresetData> existing{
+ {"A.categ", "AppA", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"},
+ {"B.categ", "AppB", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"},
+ {"C.categ", "AppC", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"},
+ };
+ auto store = aStoreWithPresets(existing);
+ store.setMaximum(3);
+
+ UserPresetData newPreset{"D.categ", "AppD", "Huawei", "100 x 30", true, "Qt 6", "Fusion"};
+ store.save(newPreset);
+
+ auto presets = store.fetchAll();
+ ASSERT_THAT(presets, ElementsAre(existing[1], existing[2], newPreset));
+}
+
+TEST_F(QdsUserPresets, canLimitPresetsToAMaximumForReverseOrder)
+{
+ std::vector<UserPresetData> existing{
+ {"A.categ", "AppA", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"},
+ {"B.categ", "AppB", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"},
+ {"C.categ", "AppC", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"},
+ };
+ auto store = aStoreWithPresets(existing);
+ store.setMaximum(3);
+ store.setReverseOrder();
+
+ UserPresetData newPreset{"D.categ", "AppD", "Huawei", "100 x 30", true, "Qt 6", "Fusion"};
+ store.save(newPreset);
+
+ auto presets = store.fetchAll();
+ ASSERT_THAT(presets, ElementsAre(newPreset, existing[0], existing[1]));
+}
+
+TEST_F(QdsUserPresets, canSavePresetsInReverseOrder)
+{
+ UserPresetData existing{"A.categ", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"};
+ auto store = aStoreWithOne(existing, StorePolicy::UniqueNames);
+ store.setReverseOrder();
+
+ UserPresetData newPreset{"A.categ", "Empty", "Huawei", "100 x 30", true, "Qt 6", "Fusion"};
+ store.save(newPreset);
+
+ auto presets = store.fetchAll();
+ ASSERT_THAT(presets, ElementsAre(newPreset, existing));
+}
+
TEST_F(QdsUserPresets, removeUserPresetFromEmptyStore)
{
UserPresetData preset{"C.categ", "2D App", "Android", "", false, "", ""};