diff --git a/src/plugins/studiowelcome/CMakeLists.txt b/src/plugins/studiowelcome/CMakeLists.txt index 209bb80b01c..82e2c78bebb 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 b7e53da5f1d..11d3d1d85d2 100644 --- a/src/plugins/studiowelcome/algorithm.h +++ b/src/plugins/studiowelcome/algorithm.h @@ -42,6 +42,16 @@ template return it == end ? nullopt : make_optional(*it); } +template +[[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 [[nodiscard]] C filterOut(const C &container, const T &value = T()) @@ -67,13 +77,14 @@ void concat(C &out, const SC &container) } template -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 diff --git a/src/plugins/studiowelcome/presetmodel.cpp b/src/plugins/studiowelcome/presetmodel.cpp index cc06bf59f54..6705b6ca650 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 &userPresetsData, - const std::vector &loadedRecentsData) + const std::vector &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 &userPresetsData, - const std::vector &loadedRecentsData) + const std::vector &loadedRecentsData) { m_categories.clear(); m_presets.clear(); @@ -96,11 +93,12 @@ std::shared_ptr PresetData::findPresetItemForUserPreset(const UserPr }); } -PresetItems PresetData::makeUserPresets(const PresetItems &wizardPresets) +PresetItems PresetData::makeUserPresets(const PresetItems &wizardPresets, + const std::vector &data) { PresetItems result; - for (const UserPresetData &userPresetData : m_userPresets) { + for (const UserPresetData &userPresetData : data) { std::shared_ptr foundPreset = findPresetItemForUserPreset(userPresetData, wizardPresets); if (!foundPreset) @@ -128,35 +126,6 @@ PresetItems PresetData::makeUserPresets(const PresetItems &wizardPresets) return result; } -std::shared_ptr PresetData::findPresetItemForRecent(const RecentPresetData &recent, const PresetItems &wizardPresets) -{ - return Utils::findOrDefault(wizardPresets, [&recent](const std::shared_ptr &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 preset = findPresetItemForRecent(recent, wizardPresets); - - if (preset) { - auto clone = std::shared_ptr{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 e4c6712b81b..19150e0fe96 100644 --- a/src/plugins/studiowelcome/presetmodel.h +++ b/src/plugins/studiowelcome/presetmodel.h @@ -33,7 +33,6 @@ #include #include -#include "recentpresets.h" #include "userpresets.h" namespace Utils { @@ -169,10 +168,10 @@ class PresetData { public: void reload(const std::vector &userPresets, - const std::vector &loadedRecents); + const std::vector &loadedRecents); void setData(const PresetsByCategory &presets, const std::vector &userPresets, - const std::vector &recents); + const std::vector &recents); const std::vector &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 &data); std::shared_ptr findPresetItemForUserPreset(const UserPresetData &preset, const PresetItems &wizardPresets); - std::shared_ptr findPresetItemForRecent(const RecentPresetData &recent, const PresetItems &wizardPresets); private: std::vector m_presets; Categories m_categories; - std::vector m_recents; + std::vector m_recents; std::vector m_userPresets; PresetsByCategory m_presetsByCategory; }; diff --git a/src/plugins/studiowelcome/qdsnewdialog.cpp b/src/plugins/studiowelcome/qdsnewdialog.cpp index 943ca0be328..ea0f741e1ab 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 factories_, WizardFactories factories{factories_, m_dialog, platform}; - std::vector recents = m_recentsStore.fetchAll(); + std::vector recents = m_recentsStore.fetchAll(); std::vector userPresets = m_userPresetsStore.fetchAll(); m_presetData.setData(factories.presetsGroupedByCategory(), userPresets, recents); @@ -360,33 +367,13 @@ void QdsNewDialog::setWizardFactories(QList factories_, * sure that all events have occurred before we go ahead and configure the wizard. */ - auto userPreset = m_currentPreset->asUserPreset(); + /* 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_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(); - } - - 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 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 recents = m_recentsStore.fetchAll(); + std::vector recents = m_recentsStore.fetchAll(); std::vector userPresets = m_userPresetsStore.fetchAll(); m_presetData.reload(userPresets, recents); @@ -520,8 +515,8 @@ void QdsNewDialog::removeCurrentPreset() } // remove preset & reload model - std::vector recents = m_recentsStore.remove(m_currentPreset->categoryId, - m_currentPreset->displayName()); + UserPresetData currentPreset = currentUserPresetData(m_qmlPresetName); + std::vector 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 09a425c4996..47779c1b310 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 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 cad2846d757..00000000000 --- 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 - -#include -#include -#include - -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 existing = fetchAll(); - - std::vector recents - = addRecentToExisting(RecentPresetData{categoryId, name, sizeName, isUserPreset}, existing); - - save(recents); -} - -void RecentPresetsStore::save(const std::vector &recents) -{ - QStringList encodedRecents = encodeRecentPresets(recents); - - m_settings->beginGroup(GROUP_NAME); - m_settings->setValue(WIZARDS, encodedRecents); - m_settings->endGroup(); - m_settings->sync(); -} - -std::vector RecentPresetsStore::remove(const QString &categoryId, const QString &presetName) -{ - std::vector 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 RecentPresetsStore::addRecentToExisting( - const RecentPresetData &preset, std::vector &recents) -{ - Utils::erase_one(recents, preset); - Utils::prepend(recents, preset); - - if (int(recents.size()) > m_max) - recents.pop_back(); - - return recents; -} - -std::vector RecentPresetsStore::fetchAll() const -{ - m_settings->beginGroup(GROUP_NAME); - QVariant value = m_settings->value(WIZARDS); - m_settings->endGroup(); - - std::vector 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 &recents) -{ - return Utils::transform(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 RecentPresetsStore::decodeRecentPresets(const QVariantList &values) -{ - return Utils::transform(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 83ab5fb8cc5..00000000000 --- 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 -#include -#include - -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 remove(const QString &categoryId, const QString &presetName); - std::vector fetchAll() const; - -private: - std::vector addRecentToExisting(const RecentPresetData &preset, - std::vector &recents); - static QStringList encodeRecentPresets(const std::vector &recents); - static std::vector decodeRecentPresets(const QVariantList &values); - static RecentPresetData decodeOneRecentPreset(const QString &encoded); - void save(const std::vector &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 9648eda14d3..4c82c8112ec 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 d468c3522eb..e32c010bd63 100644 --- a/src/plugins/studiowelcome/userpresets.cpp +++ b/src/plugins/studiowelcome/userpresets.cpp @@ -24,43 +24,75 @@ ****************************************************************************/ #include "userpresets.h" +#include "algorithm.h" + +#include +#include +#include +#include #include -#include +#include #include using namespace StudioWelcome; -constexpr char PREFIX[] = "UserPresets"; - -UserPresetsStore::UserPresetsStore() -{ - m_settings = std::make_unique(fullFilePath(), QSettings::IniFormat); -} - -UserPresetsStore::UserPresetsStore(std::unique_ptr &&settings) - : m_settings{std::move(settings)} +FileStoreIo::FileStoreIo(const QString &fileName) + : m_file{std::make_unique(fullFilePath(fileName))} {} -void UserPresetsStore::savePresets(const std::vector &presets) +QByteArray FileStoreIo::read() const { - m_settings->beginWriteArray(PREFIX, static_cast(presets.size())); + m_file->open(QFile::ReadOnly | QFile::Text); + QByteArray data = m_file->readAll(); + m_file->close(); - for (size_t i = 0; i < presets.size(); ++i) { - m_settings->setArrayIndex(static_cast(i)); - const auto &preset = presets[i]; + return data; +} - 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); +void FileStoreIo::write(const QByteArray &data) +{ + m_file->open(QFile::WriteOnly | QFile::Text); + m_file->write(data); + m_file->close(); +} + +QString FileStoreIo::fullFilePath(const QString &fileName) const +{ + return Core::ICore::userResourcePath(fileName).toString(); +} + +UserPresetsStore::UserPresetsStore(const QString &fileName, StorePolicy policy) + : m_store{std::make_unique(fileName)} + , m_policy{policy} +{} + +UserPresetsStore::UserPresetsStore(std::unique_ptr &&fileStore, + StorePolicy policy) + : m_store{std::move(fileStore)} + , m_policy{policy} +{} + +void UserPresetsStore::savePresets(const std::vector &presetItems) +{ + 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 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(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 UserPresetsStore::remove(const UserPresetData &preset) +{ + std::vector presetItems = fetchAll(); + bool erased = Utils::erase_one(presetItems, preset); + if (erased) + savePresets(presetItems); + + return presetItems; +} + std::vector UserPresetsStore::fetchAll() const { + QByteArray data = m_store->read(); + + const QJsonDocument doc = QJsonDocument::fromJson(data); + if (!doc.isArray()) + return {}; + std::vector result; - int size = m_settings->beginReadArray(PREFIX); - if (size >= 0) - result.reserve(static_cast(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 4f6053ef260..3f614f4ea07 100644 --- a/src/plugins/studiowelcome/userpresets.h +++ b/src/plugins/studiowelcome/userpresets.h @@ -25,8 +25,10 @@ #pragma once +#include #include -#include +#include +#include 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 m_file; + + Q_DISABLE_COPY(FileStoreIo) +}; + class UserPresetsStore { public: - UserPresetsStore(); - UserPresetsStore(std::unique_ptr &&settings); + UserPresetsStore(const QString &fileName, StorePolicy policy); + UserPresetsStore(std::unique_ptr &&store, StorePolicy policy); bool save(const UserPresetData &preset); - void remove(const QString &category, const QString &name); std::vector fetchAll() const; + void remove(const QString &category, const QString &name); + std::vector 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 &presets); - std::unique_ptr m_settings; + std::unique_ptr 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 c5040112efd..a2965b22f1b 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 3c166884583..8452ca8222c 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,58 +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) -{ - // Given - PresetData data; - - // When - data.setData( - /*wizard presets*/ - { - aCategory("A.categ", "A", {"Desktop"}), - }, - { - aUserPreset("A.categ", "Not-Desktop", "Windows 7"), // Non-existing Wizard Preset - }, - /*recents*/ - { - {"A.categ", "Windows 7", "200 x 300", /*is user*/true} - }); - - // Then - ASSERT_THAT(data.categories(), ElementsAre("A")); - ASSERT_THAT(data.presets(), ElementsAre(ElementsAre(PresetIs("A.categ", "Desktop")))); -} - -TEST(QdsPresetModel, recentOfNonExistentUserPreset) +TEST(QdsPresetModel, recentOfNonExistentWizardPreset) { // Given PresetData data; @@ -391,7 +356,7 @@ TEST(QdsPresetModel, recentOfNonExistentUserPreset) {/*user presets*/}, /*recents*/ { - {"A.categ", "Windows 7", "200 x 300", /*is user*/true} + aRecentPreset("A.categ", "Windows 7", "200 x 300") }); // Then @@ -415,9 +380,9 @@ TEST(QdsPresetModel, recentsShouldNotBeSorted) {/*user presets*/}, /*recents*/ { - {"Z.categ", "Z.desktop", "200 x 300"}, - {"B.categ", "Mobile", "200 x 300"}, - {"A.categ", "Desktop", "200 x 300"}, + aRecentPreset("Z.categ", "Z.desktop", "200 x 300"), + aRecentPreset("B.categ", "Mobile", "200 x 300"), + aRecentPreset("A.categ", "Desktop", "200 x 300"), }); // Then @@ -442,9 +407,9 @@ TEST(QdsPresetModel, recentsOfSameWizardProjectButDifferentSizesAreRecognizedAsD {/*user presets*/}, /*recents*/ { - {"B.categ", "Mobile", "400 x 400"}, - {"B.categ", "Mobile", "200 x 300"}, - {"A.categ", "Desktop", "640 x 480"}, + aRecentPreset("B.categ", "Mobile", "400 x 400"), + aRecentPreset("B.categ", "Mobile", "200 x 300"), + aRecentPreset("A.categ", "Desktop", "640 x 480"), }); // Then @@ -454,6 +419,35 @@ TEST(QdsPresetModel, recentsOfSameWizardProjectButDifferentSizesAreRecognizedAsD PresetIs("A.categ", "Desktop", "640 x 480"))); } +TEST(QdsPresetModel, allowRecentsWithTheSameName) +{ + // Given + PresetData data; + + // When + data.setData( + /*wizard presets*/ + { + aCategory("A.categ", "A", {"Desktop"}), + }, + {/*user presets*/}, + /*recents*/ + { + /* 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("A.categ", "Desktop"), + PresetIs("A.categ", "Desktop"), + PresetIs("A.categ", "Desktop"))); +} + TEST(QdsPresetModel, outdatedRecentsAreNotShown) { // Given @@ -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 745462f5c1b..00000000000 --- 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 -#include -#include - -#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 recents = store.fetchAll(); - - ASSERT_THAT(recents, IsEmpty()); -} - -TEST_F(QdsRecentPresets, readEmptyRecentPresets) -{ - RecentPresetsStore store = aStoreWithOne(""); - - std::vector recents = store.fetchAll(); - - ASSERT_THAT(recents, IsEmpty()); -} - -TEST_F(QdsRecentPresets, readOneRecentPresetAsList) -{ - RecentPresetsStore store = aStoreWithRecents({"category/preset:640 x 480"}); - - std::vector 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 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 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 recents = store.fetchAll(); - - ASSERT_THAT(recents, IsEmpty()); -} - -TEST_F(QdsRecentPresets, readBadRecentPresetAsInt) -{ - RecentPresetsStore store = aStoreWithOne(32); - - std::vector 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 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 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 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 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 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 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 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 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 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 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 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 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 8fa73402c08..6ed562621bf 100644 --- a/tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp +++ b/tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp @@ -29,6 +29,10 @@ #include #include +#include +#include +#include + 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(tempDir.filePath("test").toString(), - QSettings::IniFormat); + storeIo = std::make_unique(); } - 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); + 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(); - 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)}; + return UserPresetsStore{std::move(storeIo), policy}; } - UserPresetsStore aStoreWithPresets(const std::vector &presets) + UserPresetsStore aStoreWithPresets(const std::vector &presetItems) { - settings->beginWriteArray(ARRAY_NAME, presets.size()); + QJsonArray array; - for (size_t i = 0; i < presets.size(); ++i) { - settings->setArrayIndex(i); - const auto &preset = presets[i]; + 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}}); - 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); + 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 settings; + std::unique_ptr 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 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 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, "", ""};