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 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 { 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/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() } 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/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"] + } +} 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) } diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index df4eaf37d77..c5a186bda8d 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/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 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]); 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/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/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); 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/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); + } } } diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 606089f6319..0cf8b01ee98 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -151,6 +151,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( @@ -575,6 +577,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) @@ -703,6 +711,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/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/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp index cf74c86420c..71aa83bfbc3 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())); 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); 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(); } } 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") { 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()) 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); 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", diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 0c2a8f1530f..dc56a3e28da 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -235,8 +235,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; } 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 067211d480f..00000000000 --- a/src/plugins/studiowelcome/recentpresets.h +++ /dev/null @@ -1,103 +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 - -#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 b31859d7cdf..e5e8103228f 100644 --- a/src/plugins/studiowelcome/userpresets.h +++ b/src/plugins/studiowelcome/userpresets.h @@ -25,8 +25,10 @@ #pragma once +#include +#include +#include #include -#include #include @@ -70,24 +72,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/src/plugins/texteditor/refactoringchanges.cpp b/src/plugins/texteditor/refactoringchanges.cpp index 690bd745e27..878c48b4b32 100644 --- a/src/plugins/texteditor/refactoringchanges.cpp +++ b/src/plugins/texteditor/refactoringchanges.cpp @@ -328,10 +328,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; @@ -392,6 +395,9 @@ bool RefactoringFile::apply() } } + if (m_editor && ensureCursorVisible) + m_editor->ensureCursorVisible(); + m_appliedOnce = true; return result; } 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, "", ""}; 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/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 45d7a2631b0..180b06c365c 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_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)) 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) 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([]) 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