From 5b0b48177b49b8318576864673e9b0999ff3005c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Tue, 22 Mar 2022 21:17:19 +0100 Subject: [PATCH 01/24] Squish: Port tst_WELP03 to Python3 Change-Id: I997fb8cc44fab986b5f4e0aa6e6f1f92fccfd54a Reviewed-by: Reviewed-by: Christian Stenger --- tests/system/suite_WELP/tst_WELP03/test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/system/suite_WELP/tst_WELP03/test.py b/tests/system/suite_WELP/tst_WELP03/test.py index bc16fd0e597..58e8e6dd760 100644 --- a/tests/system/suite_WELP/tst_WELP03/test.py +++ b/tests/system/suite_WELP/tst_WELP03/test.py @@ -1,6 +1,6 @@ ############################################################################ # -# Copyright (C) 2016 The Qt Company Ltd. +# Copyright (C) 2022 The Qt Company Ltd. # Contact: https://www.qt.io/licensing/ # # This file is part of Qt Creator. @@ -93,8 +93,8 @@ def main(): example = findExampleOrTutorial(listView, ".*", True) test.verify(example is None, "Verifying: No example is shown.") - proFiles = map(lambda p: os.path.join(p, "opengl", "2dpainting", "2dpainting.pro"), - Qt5Path.getPaths(Qt5Path.EXAMPLES)) + proFiles = [os.path.join(p, "opengl", "2dpainting", "2dpainting.pro") + for p in Qt5Path.getPaths(Qt5Path.EXAMPLES)] cleanUpUserFiles(proFiles) for p in proFiles: removePackagingDirectory(os.path.dirname(p)) @@ -115,8 +115,8 @@ def main(): # go to "Welcome" page and choose another example switchViewTo(ViewConstants.WELCOME) - proFiles = map(lambda p: os.path.join(p, "widgets", "itemviews", "addressbook", "addressbook.pro"), - Qt5Path.getPaths(Qt5Path.EXAMPLES)) + proFiles = [os.path.join(p, "widgets", "itemviews", "addressbook", "addressbook.pro") + for p in Qt5Path.getPaths(Qt5Path.EXAMPLES)] cleanUpUserFiles(proFiles) for p in proFiles: removePackagingDirectory(os.path.dirname(p)) From e7f314e02ae61eb6bf82ced693516ce1f468bcb7 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 23 Mar 2022 11:06:36 +0100 Subject: [PATCH 02/24] Squish: Fix local git test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test was written for a qmake based project. Explicitly use one as default build system has changed. Change-Id: Iccf5761915708dd66ba73aeda25ca6b61a8c0b99 Reviewed-by: Robert Löhning --- tests/system/suite_tools/tst_git_local/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/suite_tools/tst_git_local/test.py b/tests/system/suite_tools/tst_git_local/test.py index 8208cf5e0ab..611f7c69538 100644 --- a/tests/system/suite_tools/tst_git_local/test.py +++ b/tests/system/suite_tools/tst_git_local/test.py @@ -165,7 +165,7 @@ def main(): startQC() if not startedWithoutPluginError(): return - createProject_Qt_GUI(srcPath, projectName, addToVersionControl = "Git") + createProject_Qt_GUI(srcPath, projectName, addToVersionControl = "Git", buildSystem = "qmake") openVcsLog() vcsLog = waitForObject("{type='Core::OutputWindow' unnamed='1' visible='1' " "window=':Qt Creator_Core::Internal::MainWindow'}").plainText From a889dea4d785afe91288ee51894d0e22779cb5fa Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Wed, 23 Mar 2022 17:02:32 +0100 Subject: [PATCH 03/24] QmlProjectRunConfiguration: Do not enable DebugTranslation by default Do not enable this service by default since it breaks the profiler and debugger and it is not required. This does not break language switching. Task-number: QDS-6523 Change-Id: Ib55179674c59034cdf1ece135a3f9a2e8cf08e86 Reviewed-by: Tim Jenssen Reviewed-by: --- src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index f4363afe93b..1309230e8cc 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -236,8 +236,6 @@ QString QmlProjectRunConfiguration::commandLineArguments() const if (!main.isEmpty()) ProcessArgs::addArg(&args, main, osType); - if (m_multiLanguageAspect && m_multiLanguageAspect->value()) - ProcessArgs::addArg(&args, "-qmljsdebugger=file:unused_if_debugger_arguments_added,services:DebugTranslation", osType); return args; } From fcddfe5a8cb33f9e3ac8a7a0f22a49cbef487166 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 16 Mar 2022 17:16:33 +0100 Subject: [PATCH 04/24] Squish: Fix project wizard test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Iabf4463ac535cab025db3ee808e61f5fe7054095 Reviewed-by: Robert Löhning --- tests/system/shared/project.py | 17 +++++++++++------ tests/system/shared/utils.py | 3 +++ .../tst_create_proj_wizard/test.py | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/tests/system/shared/project.py b/tests/system/shared/project.py index 16570424b03..4774bca9a5b 100644 --- a/tests/system/shared/project.py +++ b/tests/system/shared/project.py @@ -207,7 +207,7 @@ def __verifyFileCreation__(path, expectedFiles): def __modifyAvailableTargets__(available, requiredQt, asStrings=False): versionFinder = re.compile("^Desktop (\\d{1}\.\\d{1,2}\.\\d{1,2}).*$") tmp = list(available) # we need a deep copy - if Qt5Path.toVersionTuple(requiredQt) > (4,8,7): + if Qt5Path.toVersionTuple(requiredQt) > (4,8,7) and qt4Available: toBeRemoved = Targets.EMBEDDED_LINUX if asStrings: toBeRemoved = Targets.getStringForTarget(toBeRemoved) @@ -221,6 +221,8 @@ def __modifyAvailableTargets__(available, requiredQt, asStrings=False): if found: if Qt5Path.toVersionTuple(found.group(1)) < Qt5Path.toVersionTuple(requiredQt): available.discard(currentItem) + elif currentItem.endswith(" (invalid)"): + available.discard(currentItem) def __getProjectFileName__(projectName, buildSystem): if buildSystem is None or buildSystem == "CMake": @@ -523,7 +525,9 @@ def __closeSubprocessByPushingStop__(isQtQuickUI): # configured Qt versions and Toolchains and cannot be looked up the same way # if you set getAsStrings to True this function returns a list of strings instead # of the constants defined in Targets -def __getSupportedPlatforms__(text, templateName, getAsStrings=False): +# ignoreValidity if true kits will be considered available even if they are configured +# to use an invalid Qt +def __getSupportedPlatforms__(text, templateName, getAsStrings=False, ignoreValidity=False): reqPattern = re.compile("requires qt (?P\d+\.\d+(\.\d+)?)", re.IGNORECASE) res = reqPattern.search(text) if res: @@ -536,11 +540,12 @@ def __getSupportedPlatforms__(text, templateName, getAsStrings=False): supports = text[text.find('Supported Platforms'):].split(":")[1].strip().split("\n") result = set() if 'Desktop' in supports: - if (version == None or version < "5.0") and not templateName.startswith("Qt Quick 2"): - if qt4Available: + if (version == None or version < "5.0") and not templateName.startswith("Qt Quick"): + neverIgnoreValidity = templateName in ("Qt Custom Designer Widget", "Code Snippet", "Subdirs Project") + if qt4Available or ignoreValidity and not neverIgnoreValidity: result.add(Targets.DESKTOP_4_8_7_DEFAULT) - if platform.system() in ("Linux", "Darwin"): - result.add(Targets.EMBEDDED_LINUX) + if platform.system() in ("Linux", "Darwin"): + result.add(Targets.EMBEDDED_LINUX) result = result.union(set([Targets.DESKTOP_5_10_1_DEFAULT, Targets.DESKTOP_5_14_1_DEFAULT])) if platform.system() != 'Darwin': result.add(Targets.DESKTOP_5_4_1_GCC) diff --git a/tests/system/shared/utils.py b/tests/system/shared/utils.py index c67cbc57517..1135351829b 100644 --- a/tests/system/shared/utils.py +++ b/tests/system/shared/utils.py @@ -366,6 +366,9 @@ def getConfiguredKits(): def __setQtVersionForKit__(kit, kitName, kitsQtVersionName): mouseClick(waitForObjectItem(":BuildAndRun_QTreeView", kit)) qtVersionStr = str(waitForObjectExists(":Kits_QtVersion_QComboBox").currentText) + invalid = qtVersionStr.endswith(" (invalid)") + if invalid: + qtVersionStr = qtVersionStr[:-10] kitsQtVersionName[kitName] = qtVersionStr # end of internal function for iterate kits diff --git a/tests/system/suite_general/tst_create_proj_wizard/test.py b/tests/system/suite_general/tst_create_proj_wizard/test.py index 1a9d4d9be2a..8d33ba62c48 100644 --- a/tests/system/suite_general/tst_create_proj_wizard/test.py +++ b/tests/system/suite_general/tst_create_proj_wizard/test.py @@ -156,7 +156,7 @@ def __createProject__(category, template): origTxt = safeGetTextBrowserText() mouseClick(waitForObjectItem(templatesView, template)) waitFor("origTxt != safeGetTextBrowserText() != ''", 2000) - displayedPlatforms = __getSupportedPlatforms__(safeGetTextBrowserText(), template, True)[0] + displayedPlatforms = __getSupportedPlatforms__(safeGetTextBrowserText(), template, True, True)[0] safeClickButton("Choose...") # don't check because project could exist __createProjectSetNameAndPath__(os.path.expanduser("~"), 'untitled', False) From c1b585933bcacee610079f3067ff35efeae1e721 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 23 Mar 2022 13:28:27 +0100 Subject: [PATCH 05/24] Squish: Fix remove kits test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cmake is automatically running when re-opening a project and creates a temporary kit which was different when using qmake. Switch back to qmake and enhance later to support cmake as well. Change-Id: Id3e1dc23c828a6e2af5454863e1349d9866dfa38 Reviewed-by: Robert Löhning --- tests/system/shared/classes.py | 6 +++--- tests/system/suite_general/tst_remove_kits/test.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/system/shared/classes.py b/tests/system/shared/classes.py index d43c15a63a3..efe60daa701 100644 --- a/tests/system/shared/classes.py +++ b/tests/system/shared/classes.py @@ -46,11 +46,11 @@ class Targets: "Desktop 5.14.1 default"])) @staticmethod - def availableTargetClasses(): + def availableTargetClasses(ignoreValidity=False): availableTargets = set(Targets.ALL_TARGETS) - if not qt4Available: + if not qt4Available and not ignoreValidity: availableTargets.remove(Targets.DESKTOP_4_8_7_DEFAULT) - if not qt4Available or platform.system() in ('Windows', 'Microsoft'): + if not (qt4Available or ignoreValidity) or platform.system() in ('Windows', 'Microsoft'): availableTargets.remove(Targets.EMBEDDED_LINUX) elif platform.system() == 'Darwin': availableTargets.remove(Targets.DESKTOP_5_4_1_GCC) diff --git a/tests/system/suite_general/tst_remove_kits/test.py b/tests/system/suite_general/tst_remove_kits/test.py index b3376d71601..8549d285cad 100644 --- a/tests/system/suite_general/tst_remove_kits/test.py +++ b/tests/system/suite_general/tst_remove_kits/test.py @@ -54,9 +54,9 @@ def main(): startQC() if not startedWithoutPluginError(): return - createProject_Qt_Console(tempDir(), "SquishProject") + createProject_Qt_Console(tempDir(), "SquishProject", buildSystem = "qmake") switchViewTo(ViewConstants.PROJECTS) - verifyProjectsMode(Targets.getTargetsAsStrings(Targets.availableTargetClasses())) + verifyProjectsMode(Targets.getTargetsAsStrings(Targets.availableTargetClasses(True))) iterateKits(True, False, __removeKit__) clickButton(waitForObject(":Options.OK_QPushButton")) verifyProjectsMode([]) From bd607909a514ac17a5d0e41ee664bf61494783b5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 18 Mar 2022 10:19:07 +0200 Subject: [PATCH 06/24] QmlDesigner: Reset puppet when ParticleShape3D is reparented This is a workaround to QtQuick3D bug that only updates the parent node of the shape at componentComplete. Fixes: QDS-6473 Change-Id: Ice8afdc81b35eb40c07889bb1eebcdb70e68c17d Reviewed-by: Thomas Hartmann Reviewed-by: Qt CI Bot --- .../designercore/instances/nodeinstanceview.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index a6061234320..670a6a5c4ac 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -565,9 +565,11 @@ void NodeInstanceView::nodeReparented(const ModelNode &node, const NodeAbstractP // Reset puppet when particle emitter/affector is reparented to work around issue in // autodetecting the particle system it belongs to. QTBUG-101157 - if ((node.isSubclassOf("QtQuick.Particles3D.ParticleEmitter3D") - || node.isSubclassOf("QtQuick.Particles3D.Affector3D")) - && node.property("system").toBindingProperty().expression().isEmpty()) { + // Reset is also needed when particle shapes are reparented. QTBUG-101882 + if (((node.isSubclassOf("QtQuick.Particles3D.ParticleEmitter3D") + || node.isSubclassOf("QtQuick.Particles3D.Affector3D")) + && node.property("system").toBindingProperty().expression().isEmpty()) + || node.isSubclassOf("QQuick3DParticleAbstractShape")) { resetPuppet(); } } From e40041978e7b42e17bc5264def255f71090160b4 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 21 Mar 2022 14:06:39 +0200 Subject: [PATCH 07/24] QmlDesigner: Fix font preview tooltip image The preview tooltip was recently reduced in size, but font previews were still being rendered the old size and scaled down, degrading image quality. The new default size is bit too small to render the sample text of fonts nicely, so added an option to set unscaled image to the tooltip, allowing us to use twice as wide preview images for fonts. Fixes: QDS-6486 Change-Id: Ieaabfbea11e47509de7cd6aed93464d8595ea541 Reviewed-by: Samuel Ghinet Reviewed-by: Thomas Hartmann Reviewed-by: Mahmoud Badri Reviewed-by: Qt CI Bot --- .../assetslibrary/assetslibrarymodel.cpp | 5 +-- .../assetslibrary/assetslibrarymodel.h | 5 +-- .../assetslibrary/assetslibrarywidget.cpp | 6 ++- .../previewtooltip/previewimagetooltip.cpp | 14 +++++-- .../previewtooltip/previewimagetooltip.h | 2 +- .../previewtooltip/previewtooltipbackend.cpp | 39 +++++++++++++------ .../previewtooltip/previewtooltipbackend.h | 5 +++ .../imagecache/imagecachefontcollector.cpp | 2 +- 8 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 9fe3524537d..6dd1504f95d 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -253,11 +253,8 @@ const QStringList &AssetsLibraryModel::supportedTexture3DSuffixes() return retList; } -AssetsLibraryModel::AssetsLibraryModel(SynchronousImageCache &fontImageCache, - Utils::FileSystemWatcher *fileSystemWatcher, - QObject *parent) +AssetsLibraryModel::AssetsLibraryModel(Utils::FileSystemWatcher *fileSystemWatcher, QObject *parent) : QAbstractListModel(parent) - , m_fontImageCache(fontImageCache) , m_fileSystemWatcher(fileSystemWatcher) { // add role names diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h index 2b87359e07d..e964766f952 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h @@ -47,9 +47,7 @@ class AssetsLibraryModel : public QAbstractListModel Q_PROPERTY(bool isEmpty READ isEmpty WRITE setIsEmpty NOTIFY isEmptyChanged) public: - AssetsLibraryModel(QmlDesigner::SynchronousImageCache &fontImageCache, - Utils::FileSystemWatcher *fileSystemWatcher, - QObject *parent = nullptr); + AssetsLibraryModel(Utils::FileSystemWatcher *fileSystemWatcher, QObject *parent = nullptr); QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; @@ -97,7 +95,6 @@ private: void setIsEmpty(bool empty); - SynchronousImageCache &m_fontImageCache; QHash> m_iconCache; QString m_searchText; diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 34be8ab46d9..1b6c4f64fb9 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -117,7 +117,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &imageCache, , m_fontImageCache(synchronousFontImageCache) , m_assetsIconProvider(new AssetsLibraryIconProvider(synchronousFontImageCache)) , m_fileSystemWatcher(new Utils::FileSystemWatcher(this)) - , m_assetsModel(new AssetsLibraryModel(synchronousFontImageCache, m_fileSystemWatcher, this)) + , m_assetsModel(new AssetsLibraryModel(m_fileSystemWatcher, this)) , m_assetsWidget(new QQuickWidget(this)) , m_imageCache{imageCache} { @@ -130,11 +130,13 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &imageCache, m_assetsWidget->installEventFilter(this); m_fontPreviewTooltipBackend = std::make_unique(asynchronousFontImageCache); + // We want font images to have custom size, so don't scale them in the tooltip + m_fontPreviewTooltipBackend->setScaleImage(false); // Note: Though the text specified here appears in UI, it shouldn't be translated, as it's // a commonly used sentence to preview the font glyphs in latin fonts. // For fonts that do not have latin glyphs, the font family name will have to suffice for preview. m_fontPreviewTooltipBackend->setAuxiliaryData( - ImageCache::FontCollectorSizeAuxiliaryData{QSize{300, 300}, + ImageCache::FontCollectorSizeAuxiliaryData{QSize{300, 150}, Theme::getColor(Theme::DStextColor).name(), QStringLiteral("The quick brown fox jumps\n" "over the lazy dog\n" diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp index 048e33f9aa5..3b70d79fd11 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp +++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp @@ -62,10 +62,16 @@ void PreviewImageTooltip::setInfo(const QString &info) m_ui->infoLabel->setText(info); } -void PreviewImageTooltip::setImage(const QImage &image) +void PreviewImageTooltip::setImage(const QImage &image, bool scale) { - m_ui->imageLabel->setPixmap(QPixmap::fromImage({image}).scaled(m_ui->imageLabel->width(), - m_ui->imageLabel->height(), - Qt::KeepAspectRatio)); + QPixmap pm = QPixmap::fromImage({image}); + if (scale) { + m_ui->imageLabel->setPixmap(pm.scaled(m_ui->imageLabel->width(), + m_ui->imageLabel->height(), + Qt::KeepAspectRatio)); + } else { + m_ui->imageLabel->setPixmap(pm); + } } + } diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h index 09ca27fa2e3..2bebb316a0c 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h +++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h @@ -46,7 +46,7 @@ public: void setName(const QString &name); void setPath(const QString &path); void setInfo(const QString &info); - void setImage(const QImage &pixmap); + void setImage(const QImage &image, bool scale = true); private: std::unique_ptr m_ui; diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp index 475e4ac17d0..17a3b227bdf 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp +++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp @@ -55,10 +55,10 @@ void PreviewTooltipBackend::showTooltip() m_cache.requestImage( m_path, - [tooltip = QPointer(m_tooltip.get())](const QImage &image) { - QMetaObject::invokeMethod(tooltip, [tooltip, image] { + [tooltip = QPointer(m_tooltip.get()), scaleImage = m_scaleImage](const QImage &image) { + QMetaObject::invokeMethod(tooltip, [tooltip, image, scaleImage] { if (tooltip) { - tooltip->setImage(image); + tooltip->setImage(image, scaleImage); tooltip->show(); } }); @@ -126,9 +126,10 @@ QString PreviewTooltipBackend::name() const void PreviewTooltipBackend::setName(const QString &name) { - m_name = name; - if (m_name != name) + if (m_name != name) { + m_name = name; emit nameChanged(); + } } QString PreviewTooltipBackend::path() const @@ -138,9 +139,10 @@ QString PreviewTooltipBackend::path() const void PreviewTooltipBackend::setPath(const QString &path) { - m_path = path; - if (m_path != path) + if (m_path != path) { + m_path = path; emit pathChanged(); + } } QString PreviewTooltipBackend::info() const @@ -150,9 +152,10 @@ QString PreviewTooltipBackend::info() const void PreviewTooltipBackend::setInfo(const QString &info) { - m_info = info; - if (m_info != info) + if (m_info != info) { + m_info = info; emit infoChanged(); + } } QString PreviewTooltipBackend::extraId() const @@ -163,9 +166,23 @@ QString PreviewTooltipBackend::extraId() const // Sets the imageCache extraId hint. Valid content depends on image cache collector used. void PreviewTooltipBackend::setExtraId(const QString &extraId) { - m_extraId = extraId; - if (m_extraId != extraId) + if (m_extraId != extraId) { + m_extraId = extraId; emit extraIdChanged(); + } +} + +bool PreviewTooltipBackend::scaleImage() const +{ + return m_scaleImage; +} + +void PreviewTooltipBackend::setScaleImage(bool scale) +{ + if (m_scaleImage != scale) { + m_scaleImage = scale; + emit scaleImageChanged(); + } } } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h index 168ae72495e..bc7255a9797 100644 --- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h +++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h @@ -45,6 +45,7 @@ class PreviewTooltipBackend : public QObject Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged) Q_PROPERTY(QString info READ info WRITE setInfo NOTIFY infoChanged) Q_PROPERTY(QString extraId READ extraId WRITE setExtraId NOTIFY extraIdChanged) + Q_PROPERTY(bool scaleImage READ scaleImage WRITE setScaleImage NOTIFY scaleImageChanged) public: PreviewTooltipBackend(AsynchronousImageCache &cache); @@ -62,6 +63,8 @@ public: void setInfo(const QString &info); QString extraId() const; void setExtraId(const QString &extraId); + bool scaleImage() const; + void setScaleImage(bool scale); bool isVisible() const; @@ -75,12 +78,14 @@ signals: void pathChanged(); void infoChanged(); void extraIdChanged(); + void scaleImageChanged(); private: QString m_name; QString m_path; QString m_info; QString m_extraId; + bool m_scaleImage = true; std::unique_ptr m_tooltip; ImageCache::AuxiliaryData m_auxiliaryData; AsynchronousImageCache &m_cache; diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp index d74b28b0e38..3069600fd01 100644 --- a/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp +++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachefontcollector.cpp @@ -131,7 +131,7 @@ void ImageCacheFontCollector::start(Utils::SmallStringView name, auto &&auxiliaryData = Utils::get(auxiliaryDataValue); QColor textColor = auxiliaryData.colorName; QSize size = auxiliaryData.size; - QString text = font.family() + "\n\n" + auxiliaryData.text; + QString text = font.family() + "\n" + auxiliaryData.text; QImage image = createFontImage(text, textColor, font, size); From 9a9cd6d62f85f8ead41ac607b87a14bd27ae4f4a Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 18 Mar 2022 15:14:15 +0200 Subject: [PATCH 08/24] QmlDesigner: Prevent showing preview tooltip when button is pressed On some platforms, drag will stop deliving mouse events to MouseArea. Disallowed showing the tooltip when a mouse button is pressed. Fixes: QDS-6481 Change-Id: I8777d57be1bfef8470571027d9257d3a10eb5a7a Reviewed-by: Mahmoud Badri Reviewed-by: Reviewed-by: Samuel Ghinet Reviewed-by: Thomas Hartmann --- .../qmldesigner/itemLibraryQmlSources/Assets.qml | 14 +++++++++++--- .../itemLibraryQmlSources/ItemDelegate.qml | 2 ++ .../HelperWidgets/ImagePreviewTooltipArea.qml | 11 +++++++++-- .../components/navigator/navigatortreeview.cpp | 9 ++++++++- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml index 8173457f73b..66760b1a24d 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml @@ -665,15 +665,23 @@ Item { MouseArea { id: mouseArea + property bool allowTooltip: true + anchors.fill: parent hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton onExited: tooltipBackend.hideTooltip() - onCanceled: tooltipBackend.hideTooltip() + onEntered: allowTooltip = true + onCanceled: { + tooltipBackend.hideTooltip() + allowTooltip = true + } onPositionChanged: tooltipBackend.reposition() onPressed: (mouse)=> { forceActiveFocus() + allowTooltip = false + tooltipBackend.hideTooltip() var ctrlDown = mouse.modifiers & Qt.ControlModifier if (mouse.button === Qt.LeftButton) { if (!root.selectedAssets[filePath] && !ctrlDown) @@ -698,12 +706,12 @@ Item { root.contextDir = model.fileDir root.isDirContextMenu = false - tooltipBackend.hideTooltip() contextMenu.popup() } } onReleased: (mouse)=> { + allowTooltip = true if (mouse.button === Qt.LeftButton) { if (!(mouse.modifiers & Qt.ControlModifier)) root.selectedAssets = {} @@ -720,7 +728,7 @@ Item { Timer { interval: 1000 - running: mouseArea.containsMouse + running: mouseArea.containsMouse && mouseArea.allowTooltip onTriggered: { if (suffix === ".ttf" || suffix === ".otf") { tooltipBackend.name = fileName diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml index 03e536676d2..130ec2aff68 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml @@ -88,6 +88,8 @@ Item { onShowContextMenu: delegateRoot.showContextMenu() onPressed: (mouse)=> { + allowTooltip = false + hide() if (mouse.button === Qt.LeftButton) rootView.startDragAndDrop(itemLibraryEntry, mapToGlobal(mouse.x, mouse.y)) } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml index e354f5a6981..11d6d8445ef 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml @@ -30,6 +30,8 @@ import HelperWidgets 2.0 MouseArea { id: mouseArea + property bool allowTooltip: true + signal showContextMenu() function hide() @@ -38,7 +40,12 @@ MouseArea { } onExited: tooltipBackend.hideTooltip() - onCanceled: tooltipBackend.hideTooltip() + onEntered: allowTooltip = true + onCanceled: { + tooltipBackend.hideTooltip() + allowTooltip = true + } + onReleased: allowTooltip = true onPositionChanged: tooltipBackend.reposition() onClicked: function(mouse) { forceActiveFocus() @@ -51,7 +58,7 @@ MouseArea { Timer { interval: 1000 - running: mouseArea.containsMouse + running: mouseArea.containsMouse && mouseArea.allowTooltip onTriggered: { tooltipBackend.name = itemName tooltipBackend.path = componentPath diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp index d679e6017d4..f243341938f 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp @@ -293,8 +293,15 @@ void NavigatorTreeView::mousePressEvent(QMouseEvent *event) void NavigatorTreeView::startDrag(Qt::DropActions supportedActions) { - if (m_dragAllowed) + if (m_dragAllowed) { + if (m_previewToolTip) { + // Workaround to ensure tooltip doesn't linger during drag, as drag grabs all mouse + // events on some platforms (e.g. mac) + m_previewToolTip->hide(); + m_previewToolTipNodeId = -1; + } QTreeView::startDrag(supportedActions); + } } } From cd9a5b1c8dc6dc46243d1fe361b99dd23d174003 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 24 Mar 2022 10:19:28 +0100 Subject: [PATCH 09/24] Bump version to 7.0.1 Change-Id: I73f23e53629e7c8ed39ba06a72b5baff1714e784 Reviewed-by: Eike Ziller --- cmake/QtCreatorIDEBranding.cmake | 4 ++-- qbs/modules/qtc/qtc.qbs | 4 ++-- qtcreator_ide_branding.pri | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmake/QtCreatorIDEBranding.cmake b/cmake/QtCreatorIDEBranding.cmake index ade8b73ac23..ad9c58146ab 100644 --- a/cmake/QtCreatorIDEBranding.cmake +++ b/cmake/QtCreatorIDEBranding.cmake @@ -1,6 +1,6 @@ -set(IDE_VERSION "7.0.0") # The IDE version. +set(IDE_VERSION "7.0.1") # The IDE version. set(IDE_VERSION_COMPAT "7.0.0") # The IDE Compatibility version. -set(IDE_VERSION_DISPLAY "7.0.0") # The IDE display version. +set(IDE_VERSION_DISPLAY "7.0.1") # The IDE display version. set(IDE_COPYRIGHT_YEAR "2022") # The IDE current copyright year. set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. diff --git a/qbs/modules/qtc/qtc.qbs b/qbs/modules/qtc/qtc.qbs index f801369421a..e967c702622 100644 --- a/qbs/modules/qtc/qtc.qbs +++ b/qbs/modules/qtc/qtc.qbs @@ -3,10 +3,10 @@ import qbs.Environment import qbs.FileInfo Module { - property string qtcreator_display_version: '7.0.0' + property string qtcreator_display_version: '7.0.1' property string ide_version_major: '7' property string ide_version_minor: '0' - property string ide_version_release: '0' + property string ide_version_release: '1' property string qtcreator_version: ide_version_major + '.' + ide_version_minor + '.' + ide_version_release diff --git a/qtcreator_ide_branding.pri b/qtcreator_ide_branding.pri index 7cbfec223da..fc868587333 100644 --- a/qtcreator_ide_branding.pri +++ b/qtcreator_ide_branding.pri @@ -1,6 +1,6 @@ -QTCREATOR_VERSION = 7.0.0 +QTCREATOR_VERSION = 7.0.1 QTCREATOR_COMPAT_VERSION = 7.0.0 -QTCREATOR_DISPLAY_VERSION = 7.0.0 +QTCREATOR_DISPLAY_VERSION = 7.0.1 QTCREATOR_COPYRIGHT_YEAR = 2022 IDE_DISPLAY_NAME = Qt Creator From 4b96545a23486687aa04b91199beb302e4b68791 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 24 Mar 2022 11:15:39 +0200 Subject: [PATCH 10/24] QmlDesigner: Fix add new state plus button centering on all sizes Take the font size into account when centering the plus sign so it centers well at all button heights. Fixes: QDS-6529 Change-Id: Ie221fd2adb0fa19ad6fee1120ea33e702240960c Reviewed-by: Mahmoud Badri --- .../qtcreator/qmldesigner/statesEditorQmlSources/StatesList.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/statesEditorQmlSources/StatesList.qml b/share/qtcreator/qmldesigner/statesEditorQmlSources/StatesList.qml index 142aaa5c838..ec5dabb23b4 100644 --- a/share/qtcreator/qmldesigner/statesEditorQmlSources/StatesList.qml +++ b/share/qtcreator/qmldesigner/statesEditorQmlSources/StatesList.qml @@ -161,7 +161,7 @@ FocusScope { Text { text: "+" anchors.centerIn: parent - anchors.verticalCenterOffset: -16 + anchors.verticalCenterOffset: -(5 + (font.pixelSize - 35) / 9) font.pixelSize: parent.height * .5 color: Qt.lighter(StudioTheme.Values.themeControlBackgroundInteraction, addState.containsMouse ? 1.5 : 1) } From 3935c671dd3adcdb0fad7e8b57735868c81082c5 Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Mon, 21 Mar 2022 23:18:59 +0200 Subject: [PATCH 11/24] 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 Reviewed-by: Qt CI Bot Reviewed-by: Miikka Heikkinen --- src/plugins/studiowelcome/CMakeLists.txt | 1 - src/plugins/studiowelcome/algorithm.h | 15 +- src/plugins/studiowelcome/presetmodel.cpp | 45 +-- src/plugins/studiowelcome/presetmodel.h | 12 +- src/plugins/studiowelcome/qdsnewdialog.cpp | 67 ++-- src/plugins/studiowelcome/qdsnewdialog.h | 4 +- src/plugins/studiowelcome/recentpresets.cpp | 152 --------- src/plugins/studiowelcome/recentpresets.h | 101 ------ src/plugins/studiowelcome/studiowelcome.qbs | 2 - src/plugins/studiowelcome/userpresets.cpp | 155 ++++++--- src/plugins/studiowelcome/userpresets.h | 51 ++- .../qml/qmldesigner/wizard/CMakeLists.txt | 2 - .../qmldesigner/wizard/presetmodel-test.cpp | 124 ++++--- .../qmldesigner/wizard/recentpresets-test.cpp | 303 ------------------ .../qmldesigner/wizard/userpresets-test.cpp | 168 +++++++--- 15 files changed, 398 insertions(+), 804 deletions(-) delete mode 100644 src/plugins/studiowelcome/recentpresets.cpp delete mode 100644 src/plugins/studiowelcome/recentpresets.h delete mode 100644 tests/auto/qml/qmldesigner/wizard/recentpresets-test.cpp 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, "", ""}; From 38462c8f133f709f4b08f244fc7a5f3b6a44b541 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Thu, 24 Mar 2022 12:43:47 +0200 Subject: [PATCH 12/24] Debugger: Fix MSVC warning signed/unsigned comparison. Change-Id: I5fab0ccadbeb8c034410169bb6a3b4439e3f7516 Reviewed-by: hjk --- src/plugins/debugger/watchdata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/debugger/watchdata.cpp b/src/plugins/debugger/watchdata.cpp index b5036f34ed2..7b72fbf6b2f 100644 --- a/src/plugins/debugger/watchdata.cpp +++ b/src/plugins/debugger/watchdata.cpp @@ -253,7 +253,7 @@ public: { const QByteArray ba = QByteArray::fromHex(rawData.toUtf8()); const auto p = (const T*)ba.data(); - for (int i = 0, n = ba.size() / sizeof(T); i < n; ++i) { + for (int i = 0, n = int(ba.size() / sizeof(T)); i < n; ++i) { auto child = new WatchItem; child->arrayIndex = i; child->value = decodeItemHelper(p[i]); From d3dae6b7ddbc1edc15e30b2ac5cd8625d0f333f5 Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Thu, 24 Mar 2022 12:48:25 +0200 Subject: [PATCH 13/24] QmlDesigner: Don't generate new id for node if already set Source template may already set the id for the generated node, in which case we don't want to override it. Fixes: QDS-6530 Change-Id: I22b86e6bb744372ad07924f440a6ecd0dad54095 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp index f8044fd6da1..cc7ab776152 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlvisualnode.cpp @@ -335,7 +335,8 @@ QmlObjectNode QmlVisualNode::createQmlObjectNode(AbstractView *view, if (!newQmlObjectNode.isValid()) return; - newQmlObjectNode.modelNode().setIdWithoutRefactoring(view->model()->generateNewId(itemLibraryEntry.name())); + if (newQmlObjectNode.id().isEmpty()) + newQmlObjectNode.modelNode().setIdWithoutRefactoring(view->model()->generateNewId(itemLibraryEntry.name())); for (const auto &propertyBindingEntry : propertyBindingList) newQmlObjectNode.modelNode().bindingProperty(propertyBindingEntry.first).setExpression(propertyBindingEntry.second); From 3b5c56bcfad99dd702a319ca56a07d845ec9680f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 24 Mar 2022 12:35:54 +0100 Subject: [PATCH 14/24] QmlDesigner: Add invalid ids to QmlJSCheck Also improving document message for exceptions. Change-Id: I7878987ce73f5d4891ced3c702c7804b25b07eb3 Reviewed-by: Miikka Heikkinen --- src/libs/qmljs/qmljscheck.cpp | 3 ++- .../qmldesigner/designercore/model/documentmessage.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 7fe9885bdd6..d9fe7df36c6 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -570,7 +570,8 @@ public: "x", "y", "opacity", "parent", "item", "flow", "color", "margin", "padding", "print", "border", "font", "text", "source", "state", "visible", "focus", "data", - "clip", "layer", "scale", "enabled", "anchors"}) + "clip", "layer", "scale", "enabled", "anchors", + "texture", "shaderInfo", "sprite", "spriteSequence", "baseState"}) {} }; diff --git a/src/plugins/qmldesigner/designercore/model/documentmessage.cpp b/src/plugins/qmldesigner/designercore/model/documentmessage.cpp index 7039caca6c8..00782087f2f 100644 --- a/src/plugins/qmldesigner/designercore/model/documentmessage.cpp +++ b/src/plugins/qmldesigner/designercore/model/documentmessage.cpp @@ -42,7 +42,7 @@ DocumentMessage::DocumentMessage(Exception *exception): m_line(exception->line()), m_column(-1), m_description(exception->description()), - m_url(exception->file()) + m_url(QUrl::fromLocalFile(exception->file())) { } @@ -84,14 +84,14 @@ QString DocumentMessage::toString() const if (line() != -1) { if (!str.isEmpty()) str += QLatin1Char(' '); - str += ::QmlDesigner::DocumentMessage::tr("line %1").arg(line()); + str += ::QmlDesigner::DocumentMessage::tr("line %1\n").arg(line()); } if (column() != -1) { if (!str.isEmpty()) str += QLatin1Char(' '); - str += ::QmlDesigner::DocumentMessage::tr("column %1").arg(column()); + str += ::QmlDesigner::DocumentMessage::tr("column %1\n").arg(column()); } if (!str.isEmpty()) From aabff7bd35595309c34251d6fccb1484a9078d86 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 24 Mar 2022 17:43:23 +0100 Subject: [PATCH 15/24] QmlDesigner: Fix puppet crash with DelegateModel Change-Id: Ibe42b87b6364c78157c54504f00354bff8bb4b23 Reviewed-by: Tim Jenssen --- .../qmlprivategate/qmlprivategate_56.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/share/qtcreator/qml/qmlpuppet/qmlprivategate/qmlprivategate_56.cpp b/share/qtcreator/qml/qmlpuppet/qmlprivategate/qmlprivategate_56.cpp index 3213d507862..c9f2820b748 100644 --- a/share/qtcreator/qml/qmlpuppet/qmlprivategate/qmlprivategate_56.cpp +++ b/share/qtcreator/qml/qmlpuppet/qmlprivategate/qmlprivategate_56.cpp @@ -185,13 +185,13 @@ void registerNodeInstanceMetaObject(QObject *object, QQmlEngine *engine) QQuickDesignerSupportProperties::registerNodeInstanceMetaObject(object, engine); } -static bool isQuickStyleItemMetaObject(const QMetaObject *metaObject) +static bool isMetaObjectofType(const QMetaObject *metaObject, const QByteArray &type) { if (metaObject) { - if (metaObject->className() == QByteArrayLiteral("QQuickStyleItem")) + if (metaObject->className() == type) return true; - return isQuickStyleItemMetaObject(metaObject->superClass()); + return isMetaObjectofType(metaObject->superClass(), type); } return false; @@ -200,7 +200,15 @@ static bool isQuickStyleItemMetaObject(const QMetaObject *metaObject) static bool isQuickStyleItem(QObject *object) { if (object) - return isQuickStyleItemMetaObject(object->metaObject()); + return isMetaObjectofType(object->metaObject(), "QQuickStyleItem"); + + return false; +} + +static bool isDelegateModel(QObject *object) +{ + if (object) + return isMetaObjectofType(object->metaObject(), "QQmlDelegateModel"); return false; } @@ -400,7 +408,7 @@ void doComponentCompleteRecursive(QObject *object, NodeInstanceServer *nodeInsta doComponentCompleteRecursive(child, nodeInstanceServer); } - if (!isQuickStyleItem(item)) { + if (!isQuickStyleItem(object) && !isDelegateModel(object)) { if (item) { static_cast(item)->componentComplete(); } else { From ff8d672fa252362223c02f644a59bfa67c6c76ab Mon Sep 17 00:00:00 2001 From: Aleksei German Date: Thu, 24 Mar 2022 17:55:58 +0100 Subject: [PATCH 16/24] QmlDesigner: Update MCUs metadata for MCUs 2.1 Task-number: QDS-6535 Change-Id: Ibe9914ddfc48823c8fc4c2b3bc48a06cf6e936bc Reviewed-by: Reviewed-by: Thomas Hartmann --- .../qtcreator/qmldesigner/qt4mcu/metadata.qml | 8 +- share/qtcreator/qmldesigner/qt4mcu/qul-21.qml | 224 ++++++++++++++++++ 2 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 share/qtcreator/qmldesigner/qt4mcu/qul-21.qml diff --git a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml index ee889214065..5e5c515199d 100644 --- a/share/qtcreator/qmldesigner/qt4mcu/metadata.qml +++ b/share/qtcreator/qmldesigner/qt4mcu/metadata.qml @@ -27,7 +27,7 @@ Metadata { id: metadataFile - defaultVersion: v20 + defaultVersion: v21 VersionData { id: v14 @@ -58,4 +58,10 @@ Metadata { name: "Qt for MCUs 2.0" path: "qul-20.qml" } + + VersionData { + id: v21 + name: "Qt for MCUs 2.1" + path: "qul-21.qml" + } } diff --git a/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml b/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml new file mode 100644 index 00000000000..43a480e61dc --- /dev/null +++ b/share/qtcreator/qmldesigner/qt4mcu/qul-21.qml @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +//differences from 2.0: +//text.elide is introduced in QUL + +VersionData { + name: "Qt for MCUs 2.1" + + bannedItems: ["QtQuick.AnimatedImage", + "QtQuick.FocusScope", + "QtQuick.TextInput", + "QtQuick.TextEdit", + "QtQuick.Flow", + "QtQuick.Grid", + "QtQuick.GridView", + "QtQuick.PathView", + "QtQuick.Loader", + "QtQuick.Controls", + "QtQuick.Controls.BusyIndicator", + "QtQuick.Controls.ButtonGroup", + "QtQuick.Controls.CheckDelegate", + "QtQuick.Controls.Container", + "QtQuick.Controls.ComboBox", + "QtQuick.Controls.DelayButton", + "QtQuick.Controls.Frame", + "QtQuick.Controls.GroupBox", + "QtQuick.Controls.ItemDelegate", + "QtQuick.Controls.Label", + "QtQuick.Controls.Page", + "QtQuick.Controls.PageIndicator", + "QtQuick.Controls.Pane", + "QtQuick.Controls.RadioDelegate", + "QtQuick.Controls.RangeSlider", + "QtQuick.Controls.RoundButton", + "QtQuick.Controls.ScrollView", + "QtQuick.Controls.SpinBox", + "QtQuick.Controls.StackView", + "QtQuick.Controls.SwipeDelegate", + "QtQuick.Controls.SwitchDelegate", + "QtQuick.Controls.ToolBar", + "QtQuick.Controls.ToolButton", + "QtQuick.Controls.TabBar", + "QtQuick.Controls.TabButton", + "QtQuick.Controls.TextArea", + "QtQuick.Controls.TextField", + "QtQuick.Controls.ToolSeparator", + "QtQuick.Controls.Tumbler", + "QtQuick.Shapes.ConicalGradient", + "QtQuick.Shapes.LinearGradient", + "QtQuick.Shapes.RadialGradient", + "QtQuick.Shapes.ShapeGradient"] + + allowedImports: ["QtQuick", + "QtQuick.Shapes", + "QtQuick.Controls", + "QtQuick.Timeline", + "QtQuickUltralite.Extras", + "QtQuickUltralite.Layers"] + + bannedImports: ["FlowView"] + + //ComplexProperty is not a type, it's just a way to handle bigger props + ComplexProperty { + prefix: "font" + bannedProperties: ["wordSpacing", "letterSpacing", "hintingPreference", + "kerning", "preferShaping", "capitalization", + "strikeout", "underline", "styleName"] + } + + QtQuick.Item { + bannedProperties: ["layer", "opacity", "smooth", "antialiasing", + "baselineOffset", "focus", "activeFocusOnTab", + "rotation", "scale", "transformOrigin"] + } + + QtQuick.Rectangle { + bannedProperties: ["gradient", "border"] + } + + QtQuick.Flickable { + bannedProperties: ["boundsBehavior", "boundsMovement", "flickDeceleration", + "flickableDirection", "leftMargin", "rightMargin", "bottomMargin", "topMargin", + "originX", "originY", "pixelAligned", "pressDelay", "synchronousDrag"] + } + + QtQuick.MouseArea { + bannedProperties: ["propagateComposedEvents", "preventStealing", "cursorShape", + "scrollGestureEnabled", "drag", "acceptedButtons", "hoverEnabled"] + } + + QtQuick.Image { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["mirror", "mipmap", "cache", "autoTransform", "asynchronous", + "sourceSize", "smooth"] + } + + QtQuick.BorderImage { + bannedProperties: ["asynchronous", "cache", "currentFrame", "frameCount", + "horizontalTileMode", "mirror", "progress", "smooth", "sourceSize", + "status", "verticalTileMode"] + } + + QtQuick.Text { + allowChildren: false + allowedProperties: ["rotation", "scale", "transformOrigin"] + bannedProperties: ["lineHeight", "lineHeightMode", "wrapMode", "style", + "styleColor", "minimumPointSize", "minimumPixelSize", + "fontSizeMode", "renderType", "renderTypeQuality", "textFormat", "maximumLineCount"] + } + + //Padding is not an actual item, but rather set of properties in Text + Padding { + bannedProperties: ["bottomPadding", "topPadding", "leftPadding", "rightPadding"] + } + + QtQuick.Column { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding"] + } + + QtQuick.Row { + bannedProperties: ["bottomPadding", "leftPadding", "rightPadding", "topPadding", + "effectiveLayoutDirection", "layoutDirection"] + } + + QtQuick.ListView { + bannedProperties: ["cacheBuffer", "highlightRangeMode", "highlightMoveDuration", + "highlightResizeDuration", "preferredHighlightBegin", "layoutDirection", + "preferredHighlightEnd", "highlightFollowsCurrentItem", "keyNavigationWraps", + "snapMode", "highlightMoveVelocity", "highlightResizeVelocity"] + } + + QtQuick.Animation { + bannedProperties: ["paused"] + } + + //Quick Controls2 Items and properties: + + QtQuick.Controls.Control { + bannedProperties: ["focusPolicy", "hoverEnabled", "wheelEnabled"] + } + + QtQuick.Controls.AbstractButton { + bannedProperties: ["display", "autoExclusive"] + } + + QtQuick.Controls.ProgressBar { + bannedProperties: ["indeterminate"] + } + + QtQuick.Controls.Slider { + bannedProperties: ["live", "snapMode", "touchDragThreshold"] + } + + //Path and Shapes related: + + QtQuick.Path { + bannedProperties: ["scale", "pathElements"] + } + + QtQuick.PathArc { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathLine { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathMove { + bannedProperties: ["relativeX", "relativeY"] + } + + QtQuick.PathQuad { + bannedProperties: ["relativeX", "relativeY", + "relativeControlX", "relativeControlY"] + } + + QtQuick.PathCubic { + bannedProperties: ["relativeX", "relativeY", + "relativeControl1X", "relativeControl1Y", + "relativeControl2X", "relativeControl2Y"] + } + + QtQuick.PathElement { + //nothing + } + + QtQuick.PathSvg { + //nothing + } + + QtQuick.Shapes.Shape { + bannedProperties: ["asynchronous", "containsMode", "data", + "renderType", "status", "vendorExtensionsEnabled"] + } + + QtQuick.Shapes.ShapePath { + bannedProperties: ["dashOffset", "dashPattern", + "fillGradient", "strokeStyle"] + } +} From a3e0b139de16e08dd72464f50d14ffc5ce4f0b34 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 21 Mar 2022 14:08:13 +0100 Subject: [PATCH 17/24] TextEditor: Make sure cursor is visible after refactoring Fixes: QTCREATORBUG-27210 Change-Id: I612f6e91188730d6abdfef3f2fe8a286fcec4831 Reviewed-by: David Schulz Reviewed-by: --- src/plugins/texteditor/refactoringchanges.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp index 6872c18c60a..506290732f7 100644 --- a/src/plugins/texteditor/refactoringchanges.cpp +++ b/src/plugins/texteditor/refactoringchanges.cpp @@ -332,10 +332,13 @@ bool RefactoringFile::apply() } // open / activate / goto position + bool ensureCursorVisible = false; if (m_openEditor && !m_filePath.isEmpty()) { int line = -1, column = -1; - if (m_editorCursorPosition != -1) + if (m_editorCursorPosition != -1) { lineAndColumn(m_editorCursorPosition, &line, &column); + ensureCursorVisible = true; + } m_editor = RefactoringChanges::openEditor(m_filePath, m_activateEditor, line, column); m_openEditor = false; m_activateEditor = false; @@ -396,6 +399,9 @@ bool RefactoringFile::apply() } } + if (m_editor && ensureCursorVisible) + m_editor->ensureCursorVisible(); + m_appliedOnce = true; return result; } From b97c9494af2d4d6e53bcc87b588f21a4f445ef6f Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 22 Mar 2022 18:06:07 +0100 Subject: [PATCH 18/24] ClangFormat: Adapt to LLVM 15 API change Task-number: QTCREATORBUG-27170 Change-Id: I3676792da351f52199b1bf303c596e581469d7a5 Reviewed-by: Artem Sokolovskii Reviewed-by: Reviewed-by: Christian Stenger --- src/plugins/clangformat/clangformatutils.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp index 83aa58c1674..cf32bff3545 100644 --- a/src/plugins/clangformat/clangformatutils.cpp +++ b/src/plugins/clangformat/clangformatutils.cpp @@ -53,7 +53,10 @@ clang::format::FormatStyle qtcStyle() style.Language = FormatStyle::LK_Cpp; style.AccessModifierOffset = -4; style.AlignAfterOpenBracket = FormatStyle::BAS_Align; -#if LLVM_VERSION_MAJOR >= 12 +#if LLVM_VERSION_MAJOR >= 15 + style.AlignConsecutiveAssignments = {false}; + style.AlignConsecutiveDeclarations = {false}; +#elif LLVM_VERSION_MAJOR >= 12 style.AlignConsecutiveAssignments = FormatStyle::ACS_None; style.AlignConsecutiveDeclarations = FormatStyle::ACS_None; #else From d8d9a381acbbf3629236fb3fe26996f97c8bbf41 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 23 Mar 2022 15:56:38 +0100 Subject: [PATCH 19/24] Add missing changelog item Change-Id: I790e2482a8b1637ebcc2f13a1cdeca9e5925814e Reviewed-by: Assam Boudjelthia Reviewed-by: Leena Miettinen Reviewed-by: --- dist/changes-7.0.0.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dist/changes-7.0.0.md b/dist/changes-7.0.0.md index 991e52926a6..f49c1db0243 100644 --- a/dist/changes-7.0.0.md +++ b/dist/changes-7.0.0.md @@ -190,6 +190,8 @@ Platforms ### Android +* Improved monitoring for connected devices by using `track-devices` command and + file watching instead of polling (QTCREATORBUG-23991) * Added option for default NDK (QTCREATORBUG-21755, QTCREATORBUG-22389, QTCREATORBUG-24248, QTCREATORBUG-26281) * Fixed that `Include prebuilt OpenSSL libraries` could add it to the wrong From 6271b7ad850d3b92fb487a717e239b4795a92903 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 25 Mar 2022 14:46:37 +0100 Subject: [PATCH 20/24] QmlDesigner: Fix TransitionEditor for cpp types Task-number: QDS-6537 Change-Id: I800dbba7e5ac67908ae9ff7b1717910472cf4c81 Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- .../components/transitioneditor/transitioneditorview.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp index ec715624f34..49490886527 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp @@ -227,6 +227,8 @@ ModelNode TransitionEditorView::addNewTransition() const QString targetId = target.id(); for (const VariantProperty &property : change.modelNode().variantProperties()) { TypeName typeName = target.metaInfo().propertyTypeName(property.name()); + if (typeName.startsWith(".")) + typeName.remove(0, 6); if (validProperties.contains(typeName)) locList.append(QString::fromUtf8(property.name())); From dc6dbbf082802816cd6f7d086b477494caac92db Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 25 Mar 2022 19:32:11 +0100 Subject: [PATCH 21/24] QmlDesigner: Add support for object properties property QtObject: myObject: QtObject {} Change-Id: I1f1428bdb383c31fd2976bd7ac69087da54d1044 Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp index 1497c5da22c..d966d4fda1b 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp @@ -97,6 +97,9 @@ static TypeName resolveTypeName(const ASTPropertyReference *ref, const ContextPt if (const CppComponentValue * componentObjectValue = value->asCppComponentValue()) { type = componentObjectValue->className().toUtf8(); dotProperties = getObjectTypes(componentObjectValue, context); + } else if (const ObjectValue * objectValue = value->asObjectValue()) { + type = objectValue->className().toUtf8(); + dotProperties = getObjectTypes(objectValue, context); } if (type == "alias") { From 90ad0c9500580d245e73ad263489bc540599c5df Mon Sep 17 00:00:00 2001 From: Samuel Ghinet Date: Mon, 28 Mar 2022 10:04:00 +0300 Subject: [PATCH 22/24] New Project dialog fix: wrong preset is deleted The preset to be removed was not selected upon clicking on the "x" (i.e. delete) button. Task-number: QDS-6555 Change-Id: I3e7dac0ae78e67fc6236d6b4a9814fae59e40bad Reviewed-by: Miikka Heikkinen --- .../newprojectdialog/imports/NewProjectDialog/PresetView.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PresetView.qml b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PresetView.qml index d0544c2f67a..a246784c7fd 100644 --- a/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PresetView.qml +++ b/share/qtcreator/qmldesigner/newprojectdialog/imports/NewProjectDialog/PresetView.qml @@ -222,6 +222,8 @@ ScrollView { : Qt.ArrowCursor onClicked: { + delegate.GridView.view.currentIndex = index + removePresetDialog.presetName = presetName.text removePresetDialog.open() } From f0fc3926cba269096669eff9ccab50e33851b7ce Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Mon, 28 Mar 2022 10:38:16 +0300 Subject: [PATCH 23/24] QmlDesigner: Remove obsolete label from asset import dialog This label is obscured by other controls in the current layout, so it can be removed. On some platforms it also incorrectly draws on top of the controls that should be obscuring it. Fixes: QDS-6554 Change-Id: I3deed0779ea79ded2753b1437b168ef0f45654ab Reviewed-by: Reviewed-by: Thomas Hartmann --- .../qmldesigner/components/componentcore/addimagesdialog.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/componentcore/addimagesdialog.cpp b/src/plugins/qmldesigner/components/componentcore/addimagesdialog.cpp index b271717ccae..c9d0c9b93ce 100644 --- a/src/plugins/qmldesigner/components/componentcore/addimagesdialog.cpp +++ b/src/plugins/qmldesigner/components/componentcore/addimagesdialog.cpp @@ -126,7 +126,6 @@ QString AddImagesDialog::getDirectory(const QStringList &fileNames, const QStrin setDirectoryForComboBox(newDir); }); - mainLayout->addWidget(new QLabel(QCoreApplication::translate("AddImageToResources", "In directory:")), 1, 0); mainLayout->addWidget(directoryComboBox, 1, 0, 1, 3); mainLayout->addWidget(browseButton, 1, 3, 1 , 1); From f58add023aedcfa2b9011af512b082285eb78dfe Mon Sep 17 00:00:00 2001 From: Tapani Mattila Date: Tue, 22 Mar 2022 16:45:43 +0200 Subject: [PATCH 24/24] Navigator: Add search/filter to navigator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QDS-6063 Change-Id: I922a04a46f673befe8811a62342cf2cdd9c61583 Reviewed-by: Reviewed-by: Henning Gründl Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 1 + .../navigator/navigatormodelinterface.h | 1 + .../navigator/navigatorsearchwidget.cpp | 66 +++++++++++++++++++ .../navigator/navigatorsearchwidget.h | 49 ++++++++++++++ .../navigator/navigatortreemodel.cpp | 44 ++++++++++++- .../components/navigator/navigatortreemodel.h | 3 + .../components/navigator/navigatorview.cpp | 10 +++ .../components/navigator/navigatorview.h | 2 + .../components/navigator/navigatorwidget.cpp | 10 +++ .../components/navigator/navigatorwidget.h | 5 ++ src/plugins/qmldesigner/qmldesignerplugin.qbs | 2 + 11 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp create mode 100644 src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index bee619026ad..2a2c2314ba8 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -276,6 +276,7 @@ extend_qtc_plugin(QmlDesigner nameitemdelegate.cpp nameitemdelegate.h navigator.qrc navigatormodelinterface.h + navigatorsearchwidget.cpp navigatorsearchwidget.h navigatortreemodel.cpp navigatortreemodel.h navigatortreeview.cpp navigatortreeview.h navigatorview.cpp navigatorview.h diff --git a/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h b/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h index 8c914b50e45..52bfcfe6130 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h +++ b/src/plugins/qmldesigner/components/navigator/navigatormodelinterface.h @@ -45,6 +45,7 @@ public: virtual void notifyModelNodesMoved(const QList &modelNodes) = 0; virtual void notifyIconsChanged() = 0; virtual void setFilter(bool showObjects) = 0; + virtual void setNameFilter(const QString &filter) = 0; virtual void setOrder(bool reverse) = 0; virtual void resetModel() = 0; }; diff --git a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp new file mode 100644 index 00000000000..77199017c14 --- /dev/null +++ b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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 "navigatorsearchwidget.h" + +#include +#include + +#include +#include +#include + +namespace QmlDesigner { + +NavigatorSearchWidget::NavigatorSearchWidget(QWidget *parent) + : QWidget(parent) +{ + auto layout = new QBoxLayout(QBoxLayout::LeftToRight); + setLayout(layout); + + const QString fontName = "qtds_propertyIconFont.ttf"; + const int iconSize = 15; + const QColor iconColor(QmlDesigner::Theme::getColor(QmlDesigner::Theme::IconsBaseColor)); + const QIcon searchIcon = Utils::StyleHelper::getIconFromIconFont( + fontName, QmlDesigner::Theme::getIconUnicode(QmlDesigner::Theme::Icon::search), + iconSize, iconSize, iconColor); + + m_textField = new QLineEdit; + m_textField->setPlaceholderText(tr("Filter")); + m_textField->setFrame(false); + m_textField->setClearButtonEnabled(true); + m_textField->addAction(searchIcon, QLineEdit::LeadingPosition); + + connect(m_textField, &QLineEdit::textChanged, this, &NavigatorSearchWidget::textChanged); + + layout->addWidget(m_textField); +} + +void NavigatorSearchWidget::clear() +{ + m_textField->clear(); +} + +} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.h b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.h new file mode 100644 index 00000000000..d1c4e0269b6 --- /dev/null +++ b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** 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 + +namespace QmlDesigner { + +class NavigatorSearchWidget : public QWidget +{ + Q_OBJECT + +public: + NavigatorSearchWidget(QWidget *parent = nullptr); + + void clear(); + +signals: + void textChanged(const QString &text); + +private: + + QLineEdit *m_textField; +}; + +} //QmlDesigner diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 735df196afb..5f4ae027441 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -324,14 +324,25 @@ QList NavigatorTreeModel::filteredList(const NodeListProperty &proper return it.value(); QList list; + QList propertyNodes = property.toModelNodeList(); + QList nameFilteredList; + + if (m_nameFilter.isEmpty()) { + nameFilteredList = propertyNodes; + } else { + nameFilteredList.append(Utils::filtered(propertyNodes, [&] (const ModelNode &arg){ + const bool value = m_nameFilteredList.contains(arg); + return value; + })); + } if (filter) { - list.append(Utils::filtered(property.toModelNodeList(), [] (const ModelNode &arg) { + list.append(Utils::filtered(nameFilteredList, [] (const ModelNode &arg) { const bool value = QmlItemNode::isValidQmlItemNode(arg) || NodeHints::fromModelNode(arg).visibleInNavigator(); return value; })); } else { - list = property.toModelNodeList(); + list = nameFilteredList; } appendForcedNodes(property, list); @@ -1222,6 +1233,35 @@ void NavigatorTreeModel::setFilter(bool showOnlyVisibleItems) resetModel(); } +void NavigatorTreeModel::setNameFilter(const QString &filter) +{ + m_nameFilter = filter; + m_rowCache.clear(); + + ModelNode rootNode = m_view->rootModelNode(); + QList allNodes = rootNode.allSubModelNodes(); + m_nameFilteredList.clear(); + + if (filter.isEmpty()) { + m_nameFilteredList = allNodes; + } else { + for (ModelNode &node : rootNode.allSubModelNodes()) { + if (node.displayName().contains(filter, Qt::CaseSensitivity::CaseInsensitive)) { + m_nameFilteredList.append(node); + ModelNode n = node; + while (n.hasParentProperty()) { + n = n.parentProperty().parentModelNode(); + if (n.isRootNode() || m_nameFilteredList.contains(n)) + break; + m_nameFilteredList.append(n); + } + } + } + } + + resetModel(); +} + void NavigatorTreeModel::setOrder(bool reverseItemOrder) { m_reverseItemOrder = reverseItemOrder; diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h index c7b3f9b75f1..96529816076 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h @@ -101,6 +101,7 @@ public: void notifyModelNodesMoved(const QList &modelNodes) override; void notifyIconsChanged() override; void setFilter(bool showOnlyVisibleItems) override; + void setNameFilter(const QString &filter) override; void setOrder(bool reverseItemOrder) override; void resetModel() override; @@ -140,6 +141,8 @@ private: bool m_showOnlyVisibleItems = true; bool m_reverseItemOrder = false; DesignerActionManager *m_actionManager = nullptr; + QString m_nameFilter; + QList m_nameFilteredList; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index b1b12da2e0c..d9d713b5eb0 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -152,6 +152,8 @@ void NavigatorView::modelAttached(Model *model) treeView->setIndentation(20); m_currentModelInterface->setFilter(false); + m_currentModelInterface->setNameFilter(""); + m_widget->clearSearch(); QTimer::singleShot(0, this, [this, treeView]() { m_currentModelInterface->setFilter( @@ -576,6 +578,12 @@ void NavigatorView::reverseOrderToggled(bool flag) DesignerSettings::setValue(DesignerSettingsKey::NAVIGATOR_REVERSE_ITEM_ORDER, flag); } +void NavigatorView::textFilterChanged(const QString &text) +{ + m_treeModel->setNameFilter(text); + treeWidget()->expandAll(); +} + void NavigatorView::changeSelection(const QItemSelection & /*newSelection*/, const QItemSelection &/*deselected*/) { if (m_blockSelectionChangedSignal) @@ -704,6 +712,8 @@ void NavigatorView::setupWidget() connect(m_widget.data(), &NavigatorWidget::filterToggled, this, &NavigatorView::filterToggled); connect(m_widget.data(), &NavigatorWidget::reverseOrderToggled, this, &NavigatorView::reverseOrderToggled); + connect(m_widget.data(), &NavigatorWidget::textFilterChanged, this, &NavigatorView::textFilterChanged); + #ifndef QMLDESIGNER_TEST const QString fontName = "qtds_propertyIconFont.ttf"; const QSize size = QSize(28, 28); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index c779522baa7..a8a87c00828 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -118,6 +118,8 @@ private: void filterToggled(bool); void reverseOrderToggled(bool); + void textFilterChanged(const QString &text); + protected: //functions QTreeView *treeWidget() const; NavigatorTreeModel *treeModel(); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp index 00cd4758885..4929c75bd91 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp @@ -23,6 +23,7 @@ ** ****************************************************************************/ +#include "navigatorsearchwidget.h" #include "navigatorwidget.h" #include "navigatorview.h" @@ -160,6 +161,10 @@ QToolBar *NavigatorWidget::createToolBar() for (auto toolButton : buttons) toolBar->addWidget(toolButton); + m_searchWidget = new NavigatorSearchWidget(); + connect(m_searchWidget, &NavigatorSearchWidget::textChanged, this, &NavigatorWidget::textFilterChanged); + toolBar->addWidget(m_searchWidget); + return toolBar; } @@ -211,4 +216,9 @@ QByteArray NavigatorWidget::dragType() const return m_dragType; } +void NavigatorWidget::clearSearch() +{ + m_searchWidget->clear(); +} + } diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.h b/src/plugins/qmldesigner/components/navigator/navigatorwidget.h index 785c3978f30..adbd5085dfd 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorwidget.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.h @@ -39,6 +39,7 @@ QT_FORWARD_DECLARE_CLASS(QAbstractItemModel) namespace QmlDesigner { class NavigatorView; +class NavigatorSearchWidget; class NavigatorWidget: public QFrame { @@ -59,6 +60,8 @@ public: void setDragType(const QByteArray &type); QByteArray dragType() const; + void clearSearch(); + signals: void leftButtonClicked(); void rightButtonClicked(); @@ -66,6 +69,7 @@ signals: void downButtonClicked(); void filterToggled(bool); void reverseOrderToggled(bool); + void textFilterChanged(const QString &name); protected: void dragEnterEvent(QDragEnterEvent *dragEnterEvent) override; @@ -77,6 +81,7 @@ private: NavigatorTreeView *m_treeView; QPointer m_navigatorView; QByteArray m_dragType; + NavigatorSearchWidget *m_searchWidget; }; } diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 09690f9d2f4..a0427d37809 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -680,6 +680,8 @@ Project { "navigator/nameitemdelegate.cpp", "navigator/nameitemdelegate.h", "navigator/navigator.qrc", + "navigator/navigatorsearchwidget.cpp", + "navigator/navigatorsearchwidget.h", "navigator/navigatortreemodel.cpp", "navigator/navigatortreemodel.h", "navigator/navigatortreeview.cpp",