aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmldesigner/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/qmldesigner/components')
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp3
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h3
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp82
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h27
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp61
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h5
-rw-r--r--src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp1
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp87
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h34
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp1011
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h152
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp621
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h102
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp157
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h57
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h19
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp340
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h46
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp521
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h79
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp469
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionview.h120
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp296
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h71
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp511
-rw-r--r--src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h63
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractaction.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractaction.h5
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp10
-rw-r--r--src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h5
-rw-r--r--src/plugins/qmldesigner/components/componentcore/componentcore_constants.h3
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp30
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.h2
-rw-r--r--src/plugins/qmldesigner/components/componentcore/dialogutils.cpp32
-rw-r--r--src/plugins/qmldesigner/components/componentcore/dialogutils.h17
-rw-r--r--src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h49
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp123
-rw-r--r--src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h1
-rw-r--r--src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp4
-rw-r--r--src/plugins/qmldesigner/components/componentcore/theme.cpp7
-rw-r--r--src/plugins/qmldesigner/components/componentcore/theme.h1
-rw-r--r--src/plugins/qmldesigner/components/componentcore/viewmanager.cpp41
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp28
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h3
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp3
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp31
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h3
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp161
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h38
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp78
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp10
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h12
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp156
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h35
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp76
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h (renamed from src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h)18
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp7
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h14
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp192
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h47
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp54
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h28
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp10
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h2
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp34
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h4
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp686
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h135
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp646
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h35
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp282
-rw-r--r--src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h53
-rw-r--r--src/plugins/qmldesigner/components/createtexture.cpp3
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp2
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp2
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp2
-rw-r--r--src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp10
-rw-r--r--src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h1
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp48
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h3
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.cpp220
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dview.h15
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp72
-rw-r--r--src/plugins/qmldesigner/components/edit3d/edit3dwidget.h1
-rw-r--r--src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp10
-rw-r--r--src/plugins/qmldesigner/components/edit3d/snapconfiguration.h1
-rw-r--r--src/plugins/qmldesigner/components/formeditor/dragtool.cpp23
-rw-r--r--src/plugins/qmldesigner/components/formeditor/dragtool.h1
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp12
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp9
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h2
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp22
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp2
-rw-r--r--src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp4
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.cpp32
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.h14
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocumentview.cpp10
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp82
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h37
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp47
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h28
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp330
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h33
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui76
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp117
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h36
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp82
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h2
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp4
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp16
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h11
-rw-r--r--src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp4
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp11
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h5
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp2
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp5
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp20
-rw-r--r--src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h5
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp4
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp19
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h11
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp101
-rw-r--r--src/plugins/qmldesigner/components/materialeditor/materialeditorview.h2
-rw-r--r--src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp6
-rw-r--r--src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp5
-rw-r--r--src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp6
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp4
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp8
-rw-r--r--src/plugins/qmldesigner/components/navigator/previewtooltip.cpp3
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp3
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp13
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp8
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp7
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp58
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h31
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp326
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h45
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp208
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h10
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp236
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h35
-rw-r--r--src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp3
-rw-r--r--src/plugins/qmldesigner/components/texteditor/texteditorview.cpp3
-rw-r--r--src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp4
-rw-r--r--src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp24
-rw-r--r--src/plugins/qmldesigner/components/texttool/textedititemwidget.h7
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp4
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp38
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h15
-rw-r--r--src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp4
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp9
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp10
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp8
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp7
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp2
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp7
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp4
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp23
-rw-r--r--src/plugins/qmldesigner/components/toolbar/toolbarbackend.h4
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp7
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp4
165 files changed, 4420 insertions, 6559 deletions
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp
index dc5a1c9741..b821cc6595 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.cpp
@@ -2,9 +2,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "assetslibraryiconprovider.h"
-#include "asset.h"
-#include "modelnodeoperations.h"
+#include <modelnodeoperations.h>
#include <theme.h>
#include <utils/hdrimage.h>
#include <utils/ktximage.h>
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h
index fb38605ea6..d52779232f 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibraryiconprovider.h
@@ -3,12 +3,11 @@
#pragma once
+#include <asset.h>
#include <synchronousimagecache.h>
#include <QQuickImageProvider>
-#include "asset.h"
-
namespace QmlDesigner {
struct Thumbnail
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
index c2359409eb..7f488bd615 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp
@@ -1,21 +1,22 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include <QCheckBox>
-#include <QFileInfo>
-#include <QFileSystemModel>
-#include <QMessageBox>
-#include <QSortFilterProxyModel>
-
-#include "asset.h"
#include "assetslibrarymodel.h"
#include <modelnodeoperations.h>
#include <qmldesignerplugin.h>
+#include <uniquename.h>
#include <coreplugin/icore.h>
+
#include <utils/algorithm.h>
-#include <utils/qtcassert.h>
+#include <utils/asset.h>
+#include <utils/filepath.h>
+#include <utils/filesystemwatcher.h>
+
+#include <QFileInfo>
+#include <QFileSystemModel>
+#include <QMessageBox>
namespace QmlDesigner {
@@ -38,7 +39,7 @@ void AssetsLibraryModel::createBackendModel()
QObject::connect(m_sourceFsModel, &QFileSystemModel::directoryLoaded, this,
[this]([[maybe_unused]] const QString &dir) {
- syncHaveFiles();
+ syncHasFiles();
});
m_fileWatcher = new Utils::FileSystemWatcher(parent());
@@ -153,16 +154,15 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString &
QString AssetsLibraryModel::addNewFolder(const QString &folderPath)
{
- QString iterPath = folderPath;
- QDir dir{folderPath};
-
- while (dir.exists()) {
- iterPath = getUniqueName(iterPath);
+ Utils::FilePath uniqueDirPath = Utils::FilePath::fromString(UniqueName::generatePath(folderPath));
- dir.setPath(iterPath);
+ auto res = uniqueDirPath.ensureWritableDir();
+ if (!res.has_value()) {
+ qWarning() << __FUNCTION__ << res.error();
+ return {};
}
- return dir.mkpath(iterPath) ? iterPath : "";
+ return uniqueDirPath.path();
}
bool AssetsLibraryModel::urlPathExistsInModel(const QUrl &url) const
@@ -207,7 +207,7 @@ bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sour
}
}
-bool AssetsLibraryModel::checkHaveFiles(const QModelIndex &parentIdx) const
+bool AssetsLibraryModel::checkHasFiles(const QModelIndex &parentIdx) const
{
if (!parentIdx.isValid())
return false;
@@ -218,60 +218,30 @@ bool AssetsLibraryModel::checkHaveFiles(const QModelIndex &parentIdx) const
if (!isDirectory(newIdx))
return true;
- if (checkHaveFiles(newIdx))
+ if (checkHasFiles(newIdx))
return true;
}
return false;
}
-void AssetsLibraryModel::setHaveFiles(bool value)
+void AssetsLibraryModel::setHasFiles(bool value)
{
- if (m_haveFiles != value) {
- m_haveFiles = value;
- emit haveFilesChanged();
+ if (m_hasFiles != value) {
+ m_hasFiles = value;
+ emit hasFilesChanged();
}
}
-bool AssetsLibraryModel::checkHaveFiles() const
+bool AssetsLibraryModel::checkHasFiles() const
{
auto rootIdx = indexForPath(m_rootPath);
- return checkHaveFiles(rootIdx);
+ return checkHasFiles(rootIdx);
}
-void AssetsLibraryModel::syncHaveFiles()
+void AssetsLibraryModel::syncHasFiles()
{
- setHaveFiles(checkHaveFiles());
-}
-
-QString AssetsLibraryModel::getUniqueName(const QString &oldName) {
- static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
-
- QString uniqueName = oldName;
- // if the folder name ends with a number, increment it
- QRegularExpressionMatch match = rgx.match(uniqueName);
- if (match.hasMatch()) { // ends with a number
- QString numStr = match.captured(0);
- int num = match.captured(0).toInt();
-
- // get number of padding zeros, ex: for "005" = 2
- int nPaddingZeros = 0;
- for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros);
-
- ++num;
-
- // if the incremented number's digits increased, decrease the padding zeros
- if (std::fmod(std::log10(num), 1.0) == 0)
- --nPaddingZeros;
-
- uniqueName = oldName.mid(0, match.capturedStart())
- + QString('0').repeated(nPaddingZeros)
- + QString::number(num);
- } else {
- uniqueName = oldName + '1';
- }
-
- return uniqueName;
+ setHasFiles(checkHasFiles());
}
void AssetsLibraryModel::setRootPath(const QString &newPath)
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
index 9334e86e9b..f08578651a 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.h
@@ -3,12 +3,13 @@
#pragma once
-#include <QFileInfo>
-#include <QFileSystemModel>
#include <QSortFilterProxyModel>
-#include <utils/filesystemwatcher.h>
-#include <utils/qtcassert.h>
+namespace Utils {
+class FileSystemWatcher;
+}
+
+QT_FORWARD_DECLARE_CLASS(QFileSystemModel)
namespace QmlDesigner {
@@ -22,7 +23,7 @@ public:
void setRootPath(const QString &newPath);
void setSearchText(const QString &searchText);
- Q_PROPERTY(bool haveFiles READ haveFiles NOTIFY haveFilesChanged);
+ Q_PROPERTY(bool hasFiles READ hasFiles NOTIFY hasFilesChanged)
Q_INVOKABLE QString rootPath() const;
Q_INVOKABLE QString filePath(const QModelIndex &index) const;
@@ -35,7 +36,7 @@ public:
Q_INVOKABLE QModelIndex parentDirIndex(const QString &path) const;
Q_INVOKABLE QModelIndex parentDirIndex(const QModelIndex &index) const;
Q_INVOKABLE QString parentDirPath(const QString &path) const;
- Q_INVOKABLE void syncHaveFiles();
+ Q_INVOKABLE void syncHasFiles();
Q_INVOKABLE QList<QModelIndex> parentIndices(const QModelIndex &index) const;
Q_INVOKABLE bool indexIsValid(const QModelIndex &index) const;
@@ -55,30 +56,28 @@ public:
return std::min(result, 1);
}
- bool haveFiles() const { return m_haveFiles; }
-
- QString getUniqueName(const QString &oldName);
+ bool hasFiles() const { return m_hasFiles; }
signals:
void directoryLoaded(const QString &path);
void rootPathChanged();
- void haveFilesChanged();
+ void hasFilesChanged();
void fileChanged(const QString &path);
void effectsDeleted(const QStringList &effectNames);
private:
- void setHaveFiles(bool value);
+ void setHasFiles(bool value);
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
void resetModel();
void createBackendModel();
void destroyBackendModel();
- bool checkHaveFiles(const QModelIndex &parentIdx) const;
- bool checkHaveFiles() const;
+ bool checkHasFiles(const QModelIndex &parentIdx) const;
+ bool checkHasFiles() const;
QString m_searchText;
QString m_rootPath;
QFileSystemModel *m_sourceFsModel = nullptr;
- bool m_haveFiles = false;
+ bool m_hasFiles = false;
Utils::FileSystemWatcher *m_fileWatcher = nullptr;
};
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
index 3b98eb6baf..5e2211ce0d 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp
@@ -3,26 +3,29 @@
#include "assetslibrarywidget.h"
-#include "asset.h"
#include "assetslibraryiconprovider.h"
#include "assetslibrarymodel.h"
#include "assetslibraryview.h"
-#include "designeractionmanager.h"
-#include "import.h"
-#include "modelnodeoperations.h"
-#include "nodemetainfo.h"
-#include "qmldesignerconstants.h"
-#include "qmldesignerplugin.h"
-#include "theme.h"
-#include <utils3d.h>
+#include <designeractionmanager.h>
+#include <designerpaths.h>
+#include <hdrimage.h>
+#include <import.h>
+#include <modelnodeoperations.h>
+#include <nodemetainfo.h>
+#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
#include <studioquickwidget.h>
+#include <theme.h>
+#include <uniquename.h>
+#include <utils3d.h>
#include <coreplugin/fileutils.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagebox.h>
#include <utils/algorithm.h>
+#include <utils/asset.h>
#include <utils/environment.h>
#include <utils/filepath.h>
#include <utils/qtcassert.h>
@@ -92,7 +95,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon
, m_assetsModel{new AssetsLibraryModel(this)}
, m_assetsView{view}
, m_createTextures{view}
- , m_assetsWidget{new StudioQuickWidget(this)}
+ , m_assetsWidget{Utils::makeUniqueObjectPtr<StudioQuickWidget>(this)}
{
setWindowTitle(tr("Assets Library", "Title of assets library widget"));
setMinimumWidth(250);
@@ -128,7 +131,7 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
layout->setSpacing(0);
- layout->addWidget(m_assetsWidget.data());
+ layout->addWidget(m_assetsWidget.get());
updateSearch();
@@ -172,23 +175,10 @@ void AssetsLibraryWidget::deleteSelectedAssets()
QString AssetsLibraryWidget::getUniqueEffectPath(const QString &parentFolder, const QString &effectName)
{
- auto genEffectPath = [&parentFolder](const QString &name) {
- QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder);
- return QLatin1String("%1/%2.qep").arg(effectsDir, name);
- };
-
- QString uniqueName = effectName;
- QString path = genEffectPath(uniqueName);
- QFileInfo file{path};
+ QString effectsDir = ModelNodeOperations::getEffectsDefaultDirectory(parentFolder);
+ QString effectPath = QLatin1String("%1/%2.qep").arg(effectsDir, effectName);
- while (file.exists()) {
- uniqueName = m_assetsModel->getUniqueName(uniqueName);
-
- path = genEffectPath(uniqueName);
- file.setFile(path);
- }
-
- return path;
+ return UniqueName::generatePath(effectPath);
}
bool AssetsLibraryWidget::createNewEffect(const QString &effectPath, bool openInEffectComposer)
@@ -287,14 +277,16 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList
// Remove usages of deleted effects from the current document
m_assetsView->executeInTransaction(__FUNCTION__, [&]() {
QList<ModelNode> allNodes = m_assetsView->allModelNodes();
- const QString typeTemplate = "Effects.%1.%1";
- const QString importUrlTemplate = "Effects.%1";
+ const QString typeTemplate = "%1.%2.%2";
+ const QString importUrlTemplate = "%1.%2";
const Imports imports = m_assetsView->model()->imports();
Imports removedImports;
+ const QString typePrefix = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().composedEffectsTypePrefix();
for (const QString &effectName : effectNames) {
if (effectName.isEmpty())
continue;
- const TypeName type = typeTemplate.arg(effectName).toUtf8();
+ const TypeName type = typeTemplate.arg(typePrefix, effectName).toUtf8();
for (ModelNode &node : allNodes) {
if (node.metaInfo().typeName() == type) {
clearStacks = true;
@@ -302,7 +294,7 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList
}
}
- const QString importPath = importUrlTemplate.arg(effectName);
+ const QString importPath = importUrlTemplate.arg(typePrefix, effectName);
Import removedImport = Utils::findOrDefault(imports, [&importPath](const Import &import) {
return import.url() == importPath;
});
@@ -374,7 +366,7 @@ QList<QToolButton *> AssetsLibraryWidget::createToolBarWidgets()
void AssetsLibraryWidget::handleSearchFilterChanged(const QString &filterText)
{
- if (filterText == m_filterText || (!m_assetsModel->haveFiles()
+ if (filterText == m_filterText || (!m_assetsModel->hasFiles()
&& filterText.contains(m_filterText, Qt::CaseInsensitive)))
return;
@@ -643,4 +635,9 @@ void AssetsLibraryWidget::addResources(const QStringList &files, bool showDialog
}
}
+void AssetsLibraryWidget::addAssetsToContentLibrary(const QStringList &assetPaths)
+{
+ m_assetsView->emitCustomNotification("add_assets_to_content_lib", {}, {assetPaths});
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
index ed987d14de..f2d476c842 100644
--- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
+++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h
@@ -8,6 +8,8 @@
#include <coreplugin/icontext.h>
+#include <utils/uniqueobjectptr.h>
+
#include <QFrame>
#include <QQmlPropertyMap>
#include <QQuickWidget>
@@ -98,6 +100,7 @@ public:
Q_INVOKABLE void showInGraphicalShell(const QString &path);
Q_INVOKABLE QString showInGraphicalShellMsg() const;
+ Q_INVOKABLE void addAssetsToContentLibrary(const QStringList &assetPaths);
signals:
void itemActivated(const QString &itemName);
@@ -135,7 +138,7 @@ private:
AssetsLibraryView *m_assetsView = nullptr;
CreateTextures m_createTextures = nullptr;
- QScopedPointer<StudioQuickWidget> m_assetsWidget;
+ Utils::UniqueObjectPtr<StudioQuickWidget> m_assetsWidget;
std::unique_ptr<PreviewTooltipBackend> m_fontPreviewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut = nullptr;
diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp
index ff2361aa36..b067dc8d46 100644
--- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditorwidget.cpp
@@ -147,7 +147,6 @@ BindingEditorFactory::BindingEditorFactory()
{
setId(BINDINGEDITOR_CONTEXT_ID);
setDisplayName(::Core::Tr::tr("Binding Editor"));
- setEditorActionHandlers(0);
addMimeType(BINDINGEDITOR_CONTEXT_ID);
addMimeType(Utils::Constants::QML_MIMETYPE);
addMimeType(Utils::Constants::QMLTYPES_MIMETYPE);
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp
deleted file mode 100644
index cb306c58cb..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "collectiondatatypemodel.h"
-
-#include <QHash>
-#include <QtQml/QmlTypeAndRevisionsRegistration>
-
-namespace QmlDesigner {
-
-struct CollectionDataTypeModel::Details
-{
- CollectionDetails::DataType type;
- QString name;
- QString description;
-};
-
-const QList<CollectionDataTypeModel::Details> CollectionDataTypeModel::m_orderedDetails{
- {DataType::String, "String", "Text"},
- {DataType::Integer, "Integer", "Whole number that can be positive, negative, or zero"},
- {DataType::Real, "Real", "Number with a decimal"},
- {DataType::Image, "Image", "Image resource"},
- {DataType::Color, "Color", "HEX value"},
- {DataType::Url, "Url", "Resource locator"},
- {DataType::Boolean, "Boolean", "True/false"},
- {DataType::Unknown, "Unknown", "Unknown data type"},
-};
-
-CollectionDataTypeModel::CollectionDataTypeModel(QObject *parent)
- : QAbstractListModel(parent)
-{
-}
-
-int CollectionDataTypeModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
-{
- return m_orderedDetails.size();
-}
-
-QVariant CollectionDataTypeModel::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return {};
-
- if (role == Qt::DisplayRole)
- return m_orderedDetails.at(index.row()).name;
- if (role == Qt::ToolTipRole)
- return m_orderedDetails.at(index.row()).description;
-
- return {};
-}
-
-QString CollectionDataTypeModel::dataTypeToString(DataType dataType)
-{
- static const QHash<DataType, QString> dataTypeHash = []() -> QHash<DataType, QString> {
- QHash<DataType, QString> result;
- for (const Details &details : m_orderedDetails)
- result.insert(details.type, details.name);
- return result;
- }();
-
- if (dataTypeHash.contains(dataType))
- return dataTypeHash.value(dataType);
-
- return "Unknown";
-}
-
-CollectionDetails::DataType CollectionDataTypeModel::dataTypeFromString(const QString &dataType)
-{
- static const QHash<QString, DataType> stringTypeHash = []() -> QHash<QString, DataType> {
- QHash<QString, DataType> result;
- for (const Details &details : m_orderedDetails)
- result.insert(details.name, details.type);
- return result;
- }();
-
- if (stringTypeHash.contains(dataType))
- return stringTypeHash.value(dataType);
-
- return DataType::Unknown;
-}
-
-void CollectionDataTypeModel::registerDeclarativeType()
-{
- qmlRegisterType<CollectionDataTypeModel>("CollectionDetails", 1, 0, "CollectionDataTypeModel");
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h
deleted file mode 100644
index 1f91aecbff..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondatatypemodel.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2024 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "collectiondetails.h"
-
-#include <QAbstractListModel>
-#include <QList>
-
-namespace QmlDesigner {
-
-class CollectionDataTypeModel : public QAbstractListModel
-{
- Q_OBJECT
-
-public:
- using DataType = CollectionDetails::DataType;
- CollectionDataTypeModel(QObject *parent = nullptr);
-
- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
-
- static Q_INVOKABLE QString dataTypeToString(DataType dataType);
- static Q_INVOKABLE DataType dataTypeFromString(const QString &dataType);
-
- static void registerDeclarativeType();
-
-private:
- struct Details;
- static const QList<Details> m_orderedDetails;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
deleted file mode 100644
index ddfb82746c..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.cpp
+++ /dev/null
@@ -1,1011 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "collectiondetails.h"
-
-#include "collectiondatatypemodel.h"
-#include "collectioneditorutils.h"
-
-#include <utils/span.h>
-#include <qmljs/parser/qmljsast_p.h>
-#include <qmljs/parser/qmljsastvisitor_p.h>
-#include <qmljs/qmljsdocument.h>
-#include <qqml.h>
-
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QRegularExpression>
-#include <QTextStream>
-#include <QUrl>
-#include <QVariant>
-
-namespace QmlDesigner {
-#define COLLERR_OK QT_TRANSLATE_NOOP("CollectioParseError", "no error occurred")
-#define COLLERR_MAINOBJECT QT_TRANSLATE_NOOP("CollectioParseError", "Document object not found")
-#define COLLERR_COLLECTIONNAME QT_TRANSLATE_NOOP("CollectioParseError", "Model name not found")
-#define COLLERR_COLLECTIONOBJ QT_TRANSLATE_NOOP("CollectioParseError", "Model is not an object")
-#define COLLERR_COLUMNARRAY QT_TRANSLATE_NOOP("CollectioParseError", "Column is not an array")
-#define COLLERR_UNKNOWN QT_TRANSLATE_NOOP("CollectioParseError", "Unknown error")
-
-struct CollectionProperty
-{
- using DataType = CollectionDetails::DataType;
-
- QString name;
- DataType type;
-};
-
-const QMap<DataTypeWarning::Warning, QString> DataTypeWarning::dataTypeWarnings = {
- {DataTypeWarning::CellDataTypeMismatch, "Cell and column data types do not match."}
-};
-
-class CollectionDetails::Private
-{
-public:
- QList<CollectionProperty> properties;
- QList<QJsonArray> dataRecords;
- CollectionReference reference;
- bool isChanged = false;
-
- bool isValidColumnId(int column) const { return column > -1 && column < properties.size(); }
-
- bool isValidRowId(int row) const { return row > -1 && row < dataRecords.size(); }
-};
-
-inline static bool isValidColorName(const QString &colorName)
-{
- return QColor::isValidColorName(colorName);
-}
-
-/**
- * @brief getCustomUrl
- * MimeType = <MainType/SubType>
- * Address = <Url|LocalFile>
- *
- * @param value The input value to be evaluated
- * @param dataType if the value is a valid url or image, the data type
- * will be stored to this parameter, otherwise, it will be Unknown
- * @param urlResult if the value is a valid url or image, the address
- * will be stored in this parameter, otherwise it will be empty.
- * @param subType if the value is a valid image, the image subtype
- * will be stored in this parameter, otherwise it will be empty.
- * @return true if the result is either url or image
- */
-static bool getCustomUrl(const QString &value,
- CollectionDetails::DataType &dataType,
- QUrl *urlResult = nullptr,
- QString *subType = nullptr)
-{
- static const QRegularExpression urlRegex{
- "^(?<MimeType>"
- "(?<MainType>image)\\/"
- "(?<SubType>apng|avif|gif|jpeg|png|(?:svg\\+xml)|webp|xyz)\\:)?" // end of MimeType
- "(?<Address>"
- "(?<Url>https?:\\/\\/"
- "(?:www\\.|(?!www))[A-z0-9][A-z0-9-]+[A-z0-9]\\.[^\\s]{2,}|www\\.[A-z0-9][A-z0-9-]+"
- "[A-z0-9]\\.[^\\s]{2,}|https?:\\/\\/"
- "(?:www\\.|(?!www))[A-z0-9]+\\.[^\\s]{2,}|www\\.[A-z0-9]+\\.[^\\s]{2,})|" // end of Url
- "(?<LocalFile>("
- "?:(?:[A-z]:)|(?:(?:\\\\|\\/){1,2}\\w+)\\$?)(?:(?:\\\\|\\/)(?:\\w[\\w ]*.*))+)" // end of LocalFile
- "){1}$" // end of Address
- };
-
- const QRegularExpressionMatch match = urlRegex.match(value.trimmed());
- if (match.hasMatch()) {
- if (match.hasCaptured("Address")) {
- if (match.hasCaptured("MimeType") && match.captured("MainType") == "image")
- dataType = CollectionDetails::DataType::Image;
- else
- dataType = CollectionDetails::DataType::Url;
-
- if (urlResult)
- urlResult->setUrl(match.captured("Address"));
-
- if (subType)
- *subType = match.captured("SubType");
-
- return true;
- }
- }
-
- if (urlResult)
- urlResult->clear();
-
- if (subType)
- subType->clear();
-
- dataType = CollectionDetails::DataType::Unknown;
- return false;
-}
-
-/**
- * @brief dataTypeFromString
- * @param value The string value to be evaluated
- * @return Unknown if the string is empty, But returns Bool, Color, Integer,
- * Real, Url, Image if these types are detected within the non-empty string,
- * Otherwise it returns String.
- * If the value is integer, but it's out of the int range, it will be
- * considered as a Real.
- */
-static CollectionDetails::DataType dataTypeFromString(const QString &value)
-{
- using DataType = CollectionDetails::DataType;
- static const QRegularExpression validator{
- "(?<boolean>^(?:true|false)$)|"
- "(?<color>^(?:#(?:(?:[0-9a-fA-F]{2}){3,4}|(?:[0-9a-fA-F]){3,4}))$)|"
- "(?<integer>^\\d+$)|"
- "(?<real>^(?:-?(?:0|[1-9]\\d*)?(?:\\.\\d*)?(?<=\\d|\\.)"
- "(?:e-?(?:0|[1-9]\\d*))?|0x[0-9a-f]+)$)"};
- static const int boolIndex = validator.namedCaptureGroups().indexOf("boolean");
- static const int colorIndex = validator.namedCaptureGroups().indexOf("color");
- static const int integerIndex = validator.namedCaptureGroups().indexOf("integer");
- static const int realIndex = validator.namedCaptureGroups().indexOf("real");
-
- [[maybe_unused]] static const bool allIndexesFound =
- [](const std::initializer_list<int> &captureIndexes) {
- QTC_ASSERT(Utils::allOf(captureIndexes, [](int val) { return val > -1; }), return false);
- return true;
- }({boolIndex, colorIndex, integerIndex, realIndex});
-
- if (value.isEmpty())
- return DataType::Unknown;
-
- const QString trimmedValue = value.trimmed();
- QRegularExpressionMatch match = validator.match(trimmedValue);
-
- if (match.hasCaptured(boolIndex))
- return DataType::Boolean;
- if (match.hasCaptured(colorIndex))
- return DataType::Color;
- if (match.hasCaptured(integerIndex)) {
- bool isInt = false;
- trimmedValue.toInt(&isInt);
- return isInt ? DataType::Integer : DataType::Real;
- }
- if (match.hasCaptured(realIndex))
- return DataType::Real;
-
- DataType urlType;
- if (getCustomUrl(trimmedValue, urlType))
- return urlType;
-
- return DataType::String;
-}
-
-static CollectionProperty::DataType dataTypeFromJsonValue(const QJsonValue &value)
-{
- using DataType = CollectionDetails::DataType;
- using JsonType = QJsonValue::Type;
-
- switch (value.type()) {
- case JsonType::Null:
- case JsonType::Undefined:
- return DataType::Unknown;
- case JsonType::Bool:
- return DataType::Boolean;
- case JsonType::Double: {
- if (qFuzzyIsNull(std::remainder(value.toDouble(), 1)))
- return DataType::Integer;
- return DataType::Real;
- }
- case JsonType::String:
- return dataTypeFromString(value.toString());
- default:
- return DataType::Unknown;
- }
-}
-
-static QList<CollectionProperty> getColumnsFromImportedJsonArray(const QJsonArray &importedArray)
-{
- using DataType = CollectionDetails::DataType;
-
- QHash<QString, int> resultSet;
- QList<CollectionProperty> result;
-
- for (const QJsonValue &value : importedArray) {
- if (value.isObject()) {
- const QJsonObject object = value.toObject();
- QJsonObject::ConstIterator element = object.constBegin();
- const QJsonObject::ConstIterator stopItem = object.constEnd();
-
- while (element != stopItem) {
- const QString propertyName = element.key();
- if (resultSet.contains(propertyName)) {
- CollectionProperty &property = result[resultSet.value(propertyName)];
- if (property.type == DataType::Unknown) {
- property.type = dataTypeFromJsonValue(element.value());
- } else if (property.type == DataType::Integer) {
- const DataType currentCellDataType = dataTypeFromJsonValue(element.value());
- if (currentCellDataType == DataType::Real)
- property.type = currentCellDataType;
- }
- } else {
- result.append({propertyName, dataTypeFromJsonValue(element.value())});
- resultSet.insert(propertyName, resultSet.size());
- }
- ++element;
- }
- }
- }
-
- return result;
-}
-
-static QVariant valueToVariant(const QJsonValue &value, CollectionDetails::DataType type)
-{
- using DataType = CollectionDetails::DataType;
- QVariant variantValue = value.toVariant();
-
- switch (type) {
- case DataType::String:
- return variantValue.toString();
- case DataType::Integer:
- return variantValue.toInt();
- case DataType::Real:
- return variantValue.toDouble();
- case DataType::Boolean:
- return variantValue.toBool();
- case DataType::Color:
- return variantValue.value<QColor>();
- case DataType::Image: {
- DataType type;
- QUrl url;
- if (getCustomUrl(variantValue.toString(), type, &url))
- return url;
- return variantValue.toString();
- }
- case DataType::Url:
- return variantValue.value<QUrl>();
- default:
- return variantValue;
- }
-}
-
-static QJsonValue variantToJsonValue(
- const QVariant &variant, CollectionDetails::DataType type = CollectionDetails::DataType::Unknown)
-{
- using VariantType = QVariant::Type;
- using DataType = CollectionDetails::DataType;
-
- if (type == CollectionDetails::DataType::Unknown) {
- static const QHash<VariantType, DataType> typeMap = {{VariantType::Bool, DataType::Boolean},
- {VariantType::Double, DataType::Real},
- {VariantType::Int, DataType::Integer},
- {VariantType::String, DataType::String},
- {VariantType::Color, DataType::Color},
- {VariantType::Url, DataType::Url}};
- type = typeMap.value(variant.type(), DataType::Unknown);
- }
-
- switch (type) {
- case DataType::Boolean:
- return variant.toBool();
- case DataType::Real:
- return variant.toDouble();
- case DataType::Integer:
- return variant.toInt();
- case DataType::Image: {
- const QUrl url(variant.toUrl());
- if (url.isValid())
- return QString("image/xyz:%1").arg(url.toString());
- return {};
- }
- case DataType::String:
- case DataType::Color:
- case DataType::Url:
- default:
- return variant.toString();
- }
-}
-
-inline static bool isEmptyJsonValue(const QJsonValue &value)
-{
- return value.isNull() || value.isUndefined() || (value.isString() && value.toString().isEmpty());
-}
-
-QStringList csvReadLine(const QString &line)
-{
- static const QRegularExpression lineRegex{
- "(?:,\\\"|^\\\")(?<value>\\\"\\\"|[\\w\\W]*?)(?=\\\",|\\\"$)"
- "|(?:,(?!\\\")|^(?!\\\"))(?<quote>[^,]*?)(?=$|,)|(\\\\r\\\\n|\\\\n)"};
- static const int valueIndex = lineRegex.namedCaptureGroups().indexOf("value");
- static const int quoteIndex = lineRegex.namedCaptureGroups().indexOf("quote");
- Q_ASSERT(valueIndex > 0 && quoteIndex > 0);
-
- QStringList result;
- QRegularExpressionMatchIterator iterator = lineRegex.globalMatch(line, 0);
- while (iterator.hasNext()) {
- const QRegularExpressionMatch match = iterator.next();
-
- if (match.hasCaptured(valueIndex))
- result.append(match.captured(valueIndex));
- else if (match.hasCaptured(quoteIndex))
- result.append(match.captured(quoteIndex));
- }
- return result;
-}
-
-class PropertyOrderFinder : public QmlJS::AST::Visitor
-{
-public:
- static QStringList parse(const QString &jsonContent)
- {
- PropertyOrderFinder finder;
- QmlJS::Document::MutablePtr jsonDoc = QmlJS::Document::create(Utils::FilePath::fromString(
- "<expression>"),
- QmlJS::Dialect::Json);
-
- jsonDoc->setSource(jsonContent);
- jsonDoc->parseJavaScript();
-
- if (!jsonDoc->isParsedCorrectly())
- return {};
-
- jsonDoc->ast()->accept(&finder);
- return finder.m_orderedList;
- }
-
-protected:
- bool visit(QmlJS::AST::PatternProperty *patternProperty) override
- {
- const QString propertyName = patternProperty->name->asString();
- if (!m_propertySet.contains(propertyName)) {
- m_propertySet.insert(propertyName);
- m_orderedList.append(propertyName);
- }
- return true;
- }
-
- void throwRecursionDepthError() override
- {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Recursion depth error";
- };
-
-private:
- QSet<QString> m_propertySet;
- QStringList m_orderedList;
-};
-
-QString CollectionParseError::errorString() const
-{
- switch (errorNo) {
- case NoError:
- return COLLERR_OK;
- case MainObjectMissing:
- return COLLERR_MAINOBJECT;
- case CollectionNameNotFound:
- return COLLERR_COLLECTIONNAME;
- case CollectionIsNotObject:
- return COLLERR_COLLECTIONOBJ;
- case ColumnsBlockIsNotArray:
- return COLLERR_COLUMNARRAY;
- case UnknownError:
- default:
- return COLLERR_UNKNOWN;
- }
-}
-
-CollectionDetails::CollectionDetails()
- : d(new Private())
-{}
-
-CollectionDetails::CollectionDetails(const CollectionReference &reference)
- : CollectionDetails()
-{
- d->reference = reference;
-}
-
-void CollectionDetails::resetData(const QJsonDocument &localDocument,
- const QString &collectionToImport,
- CollectionParseError *error)
-{
- CollectionDetails importedCollection = fromLocalJson(localDocument, collectionToImport, error);
- d->properties.swap(importedCollection.d->properties);
- d->dataRecords.swap(importedCollection.d->dataRecords);
-}
-
-CollectionDetails::CollectionDetails(const CollectionDetails &other) = default;
-
-CollectionDetails::~CollectionDetails() = default;
-
-void CollectionDetails::insertColumn(const QString &propertyName,
- int colIdx,
- const QVariant &defaultValue,
- DataType type)
-{
- if (containsPropertyName(propertyName))
- return;
-
- CollectionProperty property = {propertyName, type};
- if (d->isValidColumnId(colIdx)) {
- d->properties.insert(colIdx, property);
- } else {
- colIdx = d->properties.size();
- d->properties.append(property);
- }
-
- const QJsonValue defaultJsonValue = QJsonValue::fromVariant(defaultValue);
- for (QJsonArray &record : d->dataRecords)
- record.insert(colIdx, defaultJsonValue);
-
- markChanged();
-}
-
-bool CollectionDetails::removeColumns(int colIdx, int count)
-{
- if (!d->isValidColumnId(colIdx))
- return false;
-
- int maxCount = d->properties.count() - colIdx;
- count = std::min(maxCount, count);
-
- if (count < 1)
- return false;
-
- d->properties.remove(colIdx, count);
-
- for (QJsonArray &record : d->dataRecords) {
- QJsonArray newElement;
-
- auto elementItr = record.constBegin();
- auto elementEnd = elementItr + colIdx;
- while (elementItr != elementEnd)
- newElement.append(*(elementItr++));
-
- elementItr += count;
- elementEnd = record.constEnd();
-
- while (elementItr != elementEnd)
- newElement.append(*(elementItr++));
-
- record = newElement;
- }
-
- markChanged();
-
- return true;
-}
-
-void CollectionDetails::insertEmptyRows(int row, int count)
-{
- if (count < 1)
- return;
-
- row = qBound(0, row, rows());
-
- insertRecords({}, row, count);
-
- markChanged();
-}
-
-bool CollectionDetails::removeRows(int row, int count)
-{
- if (!d->isValidRowId(row))
- return false;
-
- int maxCount = d->dataRecords.count() - row;
- count = std::min(maxCount, count);
-
- if (count < 1)
- return false;
-
- d->dataRecords.remove(row, count);
- markChanged();
- return true;
-}
-
-bool CollectionDetails::setPropertyValue(int row, int column, const QVariant &value)
-{
- if (!d->isValidRowId(row) || !d->isValidColumnId(column))
- return false;
-
- QVariant currentValue = data(row, column);
- if (value == currentValue)
- return false;
-
- QJsonArray &record = d->dataRecords[row];
- record.replace(column, variantToJsonValue(value, typeAt(column)));
- markChanged();
- return true;
-}
-
-bool CollectionDetails::setPropertyName(int column, const QString &value)
-{
- if (!d->isValidColumnId(column))
- return false;
-
- const CollectionProperty &oldProperty = d->properties.at(column);
- if (oldProperty.name == value)
- return false;
-
- d->properties.replace(column, {value, oldProperty.type});
-
- markChanged();
- return true;
-}
-
-bool CollectionDetails::setPropertyType(int column, DataType type)
-{
- if (!d->isValidColumnId(column))
- return false;
-
- bool changed = false;
- CollectionProperty &property = d->properties[column];
- if (property.type != type)
- changed = true;
-
- const DataType formerType = property.type;
- property.type = type;
-
- for (QJsonArray &rowData : d->dataRecords) {
- if (column < rowData.size()) {
- const QJsonValue value = rowData.at(column);
- const QVariant properTypedValue = valueToVariant(value, formerType);
- const QJsonValue properTypedJsonValue = variantToJsonValue(properTypedValue, type);
- rowData.replace(column, properTypedJsonValue);
- changed = true;
- }
- }
-
- if (changed)
- markChanged();
-
- return changed;
-}
-
-CollectionReference CollectionDetails::reference() const
-{
- return d->reference;
-}
-
-QVariant CollectionDetails::data(int row, int column) const
-{
- if (!d->isValidRowId(row))
- return {};
-
- if (!d->isValidColumnId(column))
- return {};
-
- const QJsonValue cellValue = d->dataRecords.at(row).at(column);
-
- if (typeAt(column) == DataType::Image) {
- const QUrl imageUrl = valueToVariant(cellValue, DataType::Image).toUrl();
-
- if (imageUrl.isValid())
- return imageUrl;
- }
-
- return cellValue.toVariant();
-}
-
-QString CollectionDetails::propertyAt(int column) const
-{
- if (!d->isValidColumnId(column))
- return {};
-
- return d->properties.at(column).name;
-}
-
-CollectionDetails::DataType CollectionDetails::typeAt(int column) const
-{
- if (!d->isValidColumnId(column))
- return {};
-
- return d->properties.at(column).type;
-}
-
-CollectionDetails::DataType CollectionDetails::typeAt(int row, int column) const
-{
- if (!d->isValidRowId(row) || !d->isValidColumnId(column))
- return {};
-
- const QJsonValue cellData = d->dataRecords.at(row).at(column);
- return dataTypeFromJsonValue(cellData);
-}
-
-DataTypeWarning::Warning CollectionDetails::cellWarningCheck(int row, int column) const
-{
- const QJsonValue cellValue = d->dataRecords.at(row).at(column);
-
- const DataType columnType = typeAt(column);
- const DataType cellType = typeAt(row, column);
-
- if (columnType == DataType::Unknown || isEmptyJsonValue(cellValue))
- return DataTypeWarning::Warning::None;
-
- if (columnType == DataType::Real && cellType == DataType::Integer)
- return DataTypeWarning::Warning::None;
-
- if (columnType != cellType)
- return DataTypeWarning::Warning::CellDataTypeMismatch;
-
- return DataTypeWarning::Warning::None;
-}
-
-bool CollectionDetails::containsPropertyName(const QString &propertyName) const
-{
- return Utils::anyOf(d->properties, [&propertyName](const CollectionProperty &property) {
- return property.name == propertyName;
- });
-}
-
-bool CollectionDetails::hasValidReference() const
-{
- return d->reference.node.isValid() && d->reference.name.size();
-}
-
-bool CollectionDetails::isChanged() const
-{
- return d->isChanged;
-}
-
-int CollectionDetails::columns() const
-{
- return d->properties.size();
-}
-
-int CollectionDetails::rows() const
-{
- return d->dataRecords.size();
-}
-
-bool CollectionDetails::markSaved()
-{
- if (d->isChanged) {
- d->isChanged = false;
- return true;
- }
- return false;
-}
-
-void CollectionDetails::swap(CollectionDetails &other)
-{
- d.swap(other.d);
-}
-
-void CollectionDetails::resetReference(const CollectionReference &reference)
-{
- if (d->reference != reference) {
- d->reference = reference;
- markChanged();
- }
-}
-
-QString CollectionDetails::toJson() const
-{
- QJsonArray exportedArray;
- const int propertyCount = d->properties.count();
-
- for (const QJsonArray &record : std::as_const(d->dataRecords)) {
- const int valueCount = std::min(int(record.count()), propertyCount);
-
- QJsonObject exportedElement;
- for (int i = 0; i < valueCount; ++i) {
- const QJsonValue &value = record.at(i);
- if (isEmptyJsonValue(value))
- exportedElement.insert(d->properties.at(i).name, QJsonValue::Null);
- else
- exportedElement.insert(d->properties.at(i).name, value);
- }
-
- exportedArray.append(exportedElement);
- }
-
- return QString::fromUtf8(QJsonDocument(exportedArray).toJson());
-}
-
-QString CollectionDetails::toCsv() const
-{
- QString content;
-
- auto gotoNextLine = [&content]() {
- if (content.size() && content.back() == ',')
- content.back() = '\n';
- else
- content += "\n";
- };
-
- const int propertyCount = d->properties.count();
- if (propertyCount <= 0)
- return "";
-
- for (const CollectionProperty &property : std::as_const(d->properties))
- content += property.name + ',';
-
- gotoNextLine();
-
- for (const QJsonArray &record : std::as_const(d->dataRecords)) {
- const int valueCount = std::min(int(record.count()), propertyCount);
- int i = 0;
- for (; i < valueCount; ++i) {
- const QJsonValue &value = record.at(i);
-
- if (value.isDouble())
- content += QString::number(value.toDouble()) + ',';
- else if (value.isBool())
- content += value.toBool() ? QString("true,") : QString("false,");
- else
- content += value.toString() + ',';
- }
-
- for (; i < propertyCount; ++i)
- content += ',';
-
- gotoNextLine();
- }
-
- return content;
-}
-
-QJsonObject CollectionDetails::toLocalJson() const
-{
- QJsonObject collectionObject;
- QJsonArray columnsArray;
- QJsonArray dataArray;
-
- for (const CollectionProperty &property : std::as_const(d->properties)) {
- QJsonObject columnObject;
- columnObject.insert("name", property.name);
- columnObject.insert("type", CollectionDataTypeModel::dataTypeToString(property.type));
- columnsArray.append(columnObject);
- }
-
- for (const QJsonArray &record : std::as_const(d->dataRecords))
- dataArray.append(record);
-
- collectionObject.insert("columns", columnsArray);
- collectionObject.insert("data", dataArray);
-
- return collectionObject;
-}
-
-void CollectionDetails::registerDeclarativeType()
-{
- typedef CollectionDetails::DataType DataType;
- qRegisterMetaType<DataType>("DataType");
- qmlRegisterUncreatableType<CollectionDetails>("CollectionDetails", 1, 0, "DataType", "Enum type");
-
- qRegisterMetaType<DataTypeWarning::Warning>("Warning");
- qmlRegisterUncreatableType<DataTypeWarning>("CollectionDetails", 1, 0, "Warning", "Enum type");
-}
-
-CollectionDetails CollectionDetails::fromImportedCsv(const QByteArray &document,
- const bool &firstRowIsHeader)
-{
- QStringList headers;
- QJsonArray importedArray;
-
- QTextStream stream(document);
- stream.setEncoding(QStringConverter::Latin1);
-
- if (firstRowIsHeader && !stream.atEnd()) {
- headers = Utils::transform(csvReadLine(stream.readLine()),
- [](const QString &value) -> QString { return value.trimmed(); });
- }
-
- while (!stream.atEnd()) {
- const QStringList recordDataList = csvReadLine(stream.readLine());
- int column = -1;
- QJsonObject recordData;
- for (const QString &cellData : recordDataList) {
- if (++column == headers.size()) {
- QString proposalName;
- int proposalId = column;
- do
- proposalName = QString("Column %1").arg(++proposalId);
- while (headers.contains(proposalName));
- headers.append(proposalName);
- }
- recordData.insert(headers.at(column), cellData);
- }
- importedArray.append(recordData);
- }
-
- return fromImportedJson(importedArray, headers);
-}
-
-CollectionDetails CollectionDetails::fromImportedJson(const QByteArray &json, QJsonParseError *error)
-{
- QJsonArray importedCollection;
- auto refineJsonArray = [](const QJsonArray &array) -> QJsonArray {
- QJsonArray resultArray;
- for (const QJsonValue &collectionData : array) {
- if (collectionData.isObject()) {
- QJsonObject rowObject = collectionData.toObject();
- const QStringList rowKeys = rowObject.keys();
- for (const QString &key : rowKeys) {
- const QJsonValue cellValue = rowObject.value(key);
- if (cellValue.isArray())
- rowObject.remove(key);
- }
- resultArray.push_back(rowObject);
- }
- }
- return resultArray;
- };
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(json, &parseError);
- if (error)
- *error = parseError;
-
- if (parseError.error != QJsonParseError::NoError)
- return CollectionDetails{};
-
- if (document.isArray()) {
- importedCollection = refineJsonArray(document.array());
- } else if (document.isObject()) {
- QJsonObject documentObject = document.object();
- const QStringList mainKeys = documentObject.keys();
-
- bool arrayFound = false;
- for (const QString &key : mainKeys) {
- const QJsonValue value = documentObject.value(key);
- if (value.isArray()) {
- arrayFound = true;
- importedCollection = refineJsonArray(value.toArray());
- break;
- }
- }
-
- if (!arrayFound) {
- QJsonObject singleObject;
- for (const QString &key : mainKeys) {
- const QJsonValue value = documentObject.value(key);
-
- if (!value.isObject())
- singleObject.insert(key, value);
- }
- importedCollection.push_back(singleObject);
- }
- }
-
- return fromImportedJson(importedCollection, PropertyOrderFinder::parse(QLatin1String(json)));
-}
-
-CollectionDetails CollectionDetails::fromLocalJson(const QJsonDocument &document,
- const QString &collectionName,
- CollectionParseError *error)
-{
- auto setError = [&error](CollectionParseError::ParseError parseError) {
- if (error)
- error->errorNo = parseError;
- };
-
- setError(CollectionParseError::NoError);
-
- if (document.isObject()) {
- QJsonObject collectionMap = document.object();
- if (collectionMap.contains(collectionName)) {
- QJsonValue collectionValue = collectionMap.value(collectionName);
- if (collectionValue.isObject())
- return fromLocalCollection(collectionValue.toObject());
- else
- setError(CollectionParseError::CollectionIsNotObject);
- } else {
- setError(CollectionParseError::CollectionNameNotFound);
- }
- } else {
- setError(CollectionParseError::MainObjectMissing);
- }
-
- return CollectionDetails{};
-}
-
-CollectionDetails &CollectionDetails::operator=(const CollectionDetails &other)
-{
- CollectionDetails value(other);
- swap(value);
- return *this;
-}
-
-void CollectionDetails::markChanged()
-{
- d->isChanged = true;
-}
-
-void CollectionDetails::insertRecords(const QJsonArray &record, int idx, int count)
-{
- if (count < 1)
- return;
-
- QJsonArray localRecord;
- const int columnsCount = columns();
- for (int i = 0; i < columnsCount; i++) {
- const QJsonValue originalCellData = record.at(i);
- if (originalCellData.isArray())
- localRecord.append({});
- else
- localRecord.append(originalCellData);
- }
-
- if (idx > d->dataRecords.size() || idx < 0)
- idx = d->dataRecords.size();
-
- d->dataRecords.insert(idx, count, localRecord);
-}
-
-CollectionDetails CollectionDetails::fromImportedJson(const QJsonArray &importedArray,
- const QStringList &propertyPriority)
-{
- QList<CollectionProperty> columnData = getColumnsFromImportedJsonArray(importedArray);
- if (!propertyPriority.isEmpty()) {
- QMap<QString, int> priorityMap;
- for (const QString &propertyName : propertyPriority) {
- if (!priorityMap.contains(propertyName))
- priorityMap.insert(propertyName, priorityMap.size());
- }
- const int lowestPriority = priorityMap.size();
-
- Utils::sort(columnData, [&](const CollectionProperty &a, const CollectionProperty &b) {
- return priorityMap.value(a.name, lowestPriority)
- < priorityMap.value(b.name, lowestPriority);
- });
- }
-
- QList<QJsonArray> localJsonArray;
- for (const QJsonValue &importedRowValue : importedArray) {
- QJsonObject importedRowObject = importedRowValue.toObject();
- QJsonArray localRow;
- for (const CollectionProperty &property : columnData)
- localRow.append(importedRowObject.value(property.name));
- localJsonArray.append(localRow);
- }
- CollectionDetails result;
- result.d->properties = columnData;
- result.d->dataRecords = localJsonArray;
- result.markSaved();
-
- return result;
-}
-
-CollectionDetails CollectionDetails::fromLocalCollection(const QJsonObject &localCollection,
- CollectionParseError *error)
-{
- auto setError = [&error](CollectionParseError::ParseError parseError) {
- if (error)
- error->errorNo = parseError;
- };
-
- CollectionDetails result;
- setError(CollectionParseError::NoError);
-
- if (localCollection.contains("columns")) {
- const QJsonValue columnsValue = localCollection.value("columns");
- if (columnsValue.isArray()) {
- const QJsonArray columns = columnsValue.toArray();
- for (const QJsonValue &columnValue : columns) {
- if (columnValue.isObject()) {
- const QJsonObject column = columnValue.toObject();
- const QString columnName = column.value("name").toString();
- if (!columnName.isEmpty()) {
- result.insertColumn(columnName,
- -1,
- {},
- CollectionDataTypeModel::dataTypeFromString(
- column.value("type").toString()));
- }
- }
- }
-
- if (int columnsCount = result.columns()) {
- const QJsonArray dataRecords = localCollection.value("data").toArray();
- for (const QJsonValue &dataRecordValue : dataRecords) {
- QJsonArray dataRecord = dataRecordValue.toArray();
- while (dataRecord.count() > columnsCount)
- dataRecord.removeLast();
-
- result.insertRecords(dataRecord);
- }
- }
- } else {
- setError(CollectionParseError::ColumnsBlockIsNotArray);
- return result;
- }
- }
-
- return result;
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h
deleted file mode 100644
index b84c214570..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetails.h
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "modelnode.h"
-
-#include <QSharedPointer>
-
-QT_BEGIN_NAMESPACE
-class QJsonObject;
-struct QJsonParseError;
-class QVariant;
-QT_END_NAMESPACE
-
-namespace QmlDesigner {
-
-struct CollectionReference
-{
- ModelNode node;
- QString name;
-
- friend auto qHash(const CollectionReference &collection)
- {
- return qHash(collection.node) ^ ::qHash(collection.name);
- }
-
- bool operator==(const CollectionReference &other) const
- {
- return node == other.node && name == other.name;
- }
-
- bool operator!=(const CollectionReference &other) const { return !(*this == other); }
-};
-
-struct CollectionProperty;
-
-struct DataTypeWarning {
-public:
- enum Warning { None, CellDataTypeMismatch };
- Q_ENUM(Warning)
-
- Warning warning = None;
- DataTypeWarning(Warning warning)
- : warning(warning)
- {}
-
- static QString getDataTypeWarningString(Warning warning)
- {
- return dataTypeWarnings.value(warning);
- }
-
-private:
- Q_GADGET
- static const QMap<Warning, QString> dataTypeWarnings;
-};
-
-struct CollectionParseError
-{
- enum ParseError {
- NoError,
- MainObjectMissing,
- CollectionNameNotFound,
- CollectionIsNotObject,
- ColumnsBlockIsNotArray,
- UnknownError
- };
-
- ParseError errorNo = ParseError::NoError;
- QString errorString() const;
-};
-
-class CollectionDetails
-{
- Q_GADGET
-
-public:
- enum class DataType { Unknown, String, Url, Integer, Real, Boolean, Image, Color };
- Q_ENUM(DataType)
-
- explicit CollectionDetails();
- CollectionDetails(const CollectionReference &reference);
- CollectionDetails(const CollectionDetails &other);
- ~CollectionDetails();
-
- void resetData(const QJsonDocument &localDocument,
- const QString &collectionToImport,
- CollectionParseError *error = nullptr);
-
- void insertColumn(const QString &propertyName,
- int colIdx = -1,
- const QVariant &defaultValue = {},
- DataType type = DataType::Unknown);
- bool removeColumns(int colIdx, int count = 1);
-
- void insertEmptyRows(int row = 0, int count = 1);
- bool removeRows(int row, int count = 1);
- bool setPropertyValue(int row, int column, const QVariant &value);
-
- bool setPropertyName(int column, const QString &value);
- bool setPropertyType(int column, DataType type);
-
- CollectionReference reference() const;
- QVariant data(int row, int column) const;
- QString propertyAt(int column) const;
- DataType typeAt(int column) const;
- DataType typeAt(int row, int column) const;
- DataTypeWarning::Warning cellWarningCheck(int row, int column) const;
- bool containsPropertyName(const QString &propertyName) const;
-
- bool hasValidReference() const;
- bool isChanged() const;
-
- int columns() const;
- int rows() const;
-
- bool markSaved();
-
- void swap(CollectionDetails &other);
- void resetReference(const CollectionReference &reference);
-
- QString toJson() const;
- QString toCsv() const;
- QJsonObject toLocalJson() const;
-
- static void registerDeclarativeType();
-
- static CollectionDetails fromImportedCsv(const QByteArray &document,
- const bool &firstRowIsHeader = true);
- static CollectionDetails fromImportedJson(const QByteArray &json,
- QJsonParseError *error = nullptr);
- static CollectionDetails fromLocalJson(const QJsonDocument &document,
- const QString &collectionName,
- CollectionParseError *error = nullptr);
-
- CollectionDetails &operator=(const CollectionDetails &other);
-
-private:
- void markChanged();
- void insertRecords(const QJsonArray &record, int idx = -1, int count = 1);
-
- static CollectionDetails fromImportedJson(const QJsonArray &importedArray,
- const QStringList &propertyPriority = {});
- static CollectionDetails fromLocalCollection(const QJsonObject &localCollection,
- CollectionParseError *error = nullptr);
-
- // The private data is supposed to be shared between the copies
- class Private;
- QSharedPointer<Private> d;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
deleted file mode 100644
index b26b1a845e..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.cpp
+++ /dev/null
@@ -1,621 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "collectiondetailsmodel.h"
-
-#include "collectiondatatypemodel.h"
-#include "collectioneditorutils.h"
-#include "modelnode.h"
-
-#include <coreplugin/editormanager/editormanager.h>
-
-#include <utils/fileutils.h>
-#include <utils/qtcassert.h>
-#include <utils/textfileformat.h>
-
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonParseError>
-
-namespace QmlDesigner {
-
-CollectionDetailsModel::CollectionDetailsModel(QObject *parent)
- : QAbstractTableModel(parent)
-{
- connect(this, &CollectionDetailsModel::modelReset, this, &CollectionDetailsModel::updateEmpty);
- connect(this, &CollectionDetailsModel::rowsInserted, this, &CollectionDetailsModel::updateEmpty);
- connect(this, &CollectionDetailsModel::rowsRemoved, this, &CollectionDetailsModel::updateEmpty);
-}
-
-QHash<int, QByteArray> CollectionDetailsModel::roleNames() const
-{
- static QHash<int, QByteArray> roles;
- if (roles.isEmpty()) {
- roles.insert(QAbstractTableModel::roleNames());
- roles.insert(SelectedRole, "itemSelected");
- roles.insert(DataTypeRole, "dataType");
- roles.insert(ColumnDataTypeRole, "columnType");
- roles.insert(DataTypeWarningRole, "dataTypeWarning");
- }
- return roles;
-}
-
-int CollectionDetailsModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
-{
- return m_currentCollection.rows();
-}
-
-int CollectionDetailsModel::columnCount([[maybe_unused]] const QModelIndex &parent) const
-{
- return m_currentCollection.columns();
-}
-
-QVariant CollectionDetailsModel::data(const QModelIndex &index, int role) const
-{
- if (!index.isValid())
- return {};
-
- QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
-
- if (role == SelectedRole)
- return (index.column() == m_selectedColumn || index.row() == m_selectedRow);
-
- if (role == DataTypeRole)
- return QVariant::fromValue(m_currentCollection.typeAt(index.row(), index.column()));
-
- if (role == ColumnDataTypeRole)
- return QVariant::fromValue(m_currentCollection.typeAt(index.column()));
-
- if (role == Qt::EditRole)
- return m_currentCollection.data(index.row(), index.column());
-
- if (role == DataTypeWarningRole )
- return QVariant::fromValue(m_currentCollection.cellWarningCheck(index.row(), index.column()));
-
- return m_currentCollection.data(index.row(), index.column()).toString();
-}
-
-bool CollectionDetailsModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (!index.isValid())
- return {};
-
- if (role == Qt::EditRole) {
- DataTypeWarning::Warning prevWarning = m_currentCollection.cellWarningCheck(index.row(), index.column());
- bool changed = m_currentCollection.setPropertyValue(index.row(), index.column(), value);
-
- if (changed) {
- QList<int> roles = {Qt::DisplayRole, Qt::EditRole};
-
- if (prevWarning != m_currentCollection.cellWarningCheck(index.row(), index.column()))
- roles << DataTypeWarningRole;
-
- emit dataChanged(index, index, roles);
- }
-
- return true;
- }
-
- return false;
-}
-
-bool CollectionDetailsModel::setHeaderData(int section,
- Qt::Orientation orientation,
- const QVariant &value,
- [[maybe_unused]] int role)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (orientation == Qt::Vertical)
- return false;
-
- bool headerChanged = m_currentCollection.setPropertyName(section, value.toString());
- if (headerChanged)
- emit this->headerDataChanged(orientation, section, section);
-
- return headerChanged;
-}
-
-bool CollectionDetailsModel::insertRows(int row, int count, [[maybe_unused]] const QModelIndex &parent)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (count < 1)
- return false;
-
- row = qBound(0, row, rowCount());
-
- beginResetModel();
- m_currentCollection.insertEmptyRows(row, count);
- endResetModel();
-
- selectRow(row);
- return true;
-}
-
-bool CollectionDetailsModel::removeColumns(int column, int count, const QModelIndex &parent)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (column < 0 || column >= columnCount(parent) || count < 1)
- return false;
-
- count = std::min(count, columnCount(parent) - column);
- beginRemoveColumns(parent, column, column + count - 1);
- bool columnsRemoved = m_currentCollection.removeColumns(column, count);
- endRemoveColumns();
-
- if (!columnCount(parent))
- removeRows(0, rowCount(parent), parent);
-
- int nextColumn = column - 1;
- if (nextColumn < 0 && columnCount(parent) > 0)
- nextColumn = 0;
-
- selectColumn(nextColumn);
-
- ensureSingleCell();
- return columnsRemoved;
-}
-
-bool CollectionDetailsModel::removeRows(int row, int count, const QModelIndex &parent)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (row < 0 || row >= rowCount(parent) || count < 1)
- return false;
-
- count = std::min(count, rowCount(parent) - row);
- beginRemoveRows(parent, row, row + count - 1);
- bool rowsRemoved = m_currentCollection.removeRows(row, count);
- endRemoveRows();
-
- ensureSingleCell();
- return rowsRemoved;
-}
-
-Qt::ItemFlags CollectionDetailsModel::flags(const QModelIndex &index) const
-{
- if (!index.isValid())
- return {};
-
- return {Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable};
-}
-
-QVariant CollectionDetailsModel::headerData(int section, Qt::Orientation orientation, int role) const
-{
- if (orientation == Qt::Horizontal) {
- if (role == DataTypeRole)
- return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(section));
- else
- return m_currentCollection.propertyAt(section);
- }
-
- if (orientation == Qt::Vertical)
- return section + 1;
-
- return {};
-}
-
-CollectionDetails::DataType CollectionDetailsModel::propertyDataType(int column) const
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return CollectionDetails::DataType::Unknown);
-
- return m_currentCollection.typeAt(column);
-}
-
-int CollectionDetailsModel::selectedColumn() const
-{
- return m_selectedColumn;
-}
-
-int CollectionDetailsModel::selectedRow() const
-{
- return m_selectedRow;
-}
-
-QString CollectionDetailsModel::propertyName(int column) const
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
-
- return m_currentCollection.propertyAt(column);
-}
-
-QString CollectionDetailsModel::propertyType(int column) const
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return {});
-
- return CollectionDataTypeModel::dataTypeToString(m_currentCollection.typeAt(column));
-}
-
-bool CollectionDetailsModel::isPropertyAvailable(const QString &name)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- return m_currentCollection.containsPropertyName(name);
-}
-
-bool CollectionDetailsModel::addColumn(int column, const QString &name, const QString &propertyType)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- if (m_currentCollection.containsPropertyName(name))
- return false;
-
- if (column < 0 || column > columnCount())
- column = columnCount();
-
- beginInsertColumns({}, column, column);
- m_currentCollection.insertColumn(name,
- column,
- {},
- CollectionDataTypeModel::dataTypeFromString(propertyType));
- endInsertColumns();
- return m_currentCollection.containsPropertyName(name);
-}
-
-bool CollectionDetailsModel::selectColumn(int section)
-{
- if (m_selectedColumn == section)
- return false;
-
- const int columns = columnCount();
-
- if (section >= columns)
- section = columns - 1;
-
- selectRow(-1);
-
- const int rows = rowCount();
- const int previousColumn = m_selectedColumn;
-
- m_selectedColumn = section;
- emit this->selectedColumnChanged(m_selectedColumn);
-
- auto notifySelectedDataChanged = [this, columns, rows](int notifyingColumn) {
- if (notifyingColumn > -1 && notifyingColumn < columns && rows) {
- emit dataChanged(index(0, notifyingColumn),
- index(rows - 1, notifyingColumn),
- {SelectedRole});
- }
- };
-
- notifySelectedDataChanged(previousColumn);
- notifySelectedDataChanged(m_selectedColumn);
-
- return true;
-}
-
-bool CollectionDetailsModel::renameColumn(int section, const QString &newValue)
-{
- return setHeaderData(section, Qt::Horizontal, newValue);
-}
-
-bool CollectionDetailsModel::setPropertyType(int column, const QString &newValue)
-{
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- bool changed = m_currentCollection.setPropertyType(column,
- CollectionDataTypeModel::dataTypeFromString(
- newValue));
- if (changed) {
- emit headerDataChanged(Qt::Horizontal, column, column);
- emit dataChanged(
- index(0, column),
- index(rowCount() - 1, column),
- {Qt::DisplayRole, Qt::EditRole, DataTypeRole, DataTypeWarningRole, ColumnDataTypeRole});
- }
-
- return changed;
-}
-
-bool CollectionDetailsModel::selectRow(int row)
-{
- if (m_selectedRow == row)
- return false;
-
- const int rows = rowCount();
-
- if (row >= rows)
- row = rows - 1;
-
- selectColumn(-1);
-
- const int columns = columnCount();
- const int previousRow = m_selectedRow;
-
- m_selectedRow = row;
- emit this->selectedRowChanged(m_selectedRow);
-
- auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) {
- if (notifyingRow > -1 && notifyingRow < rows && columns)
- emit dataChanged(index(notifyingRow, 0), index(notifyingRow, columns - 1), {SelectedRole});
- };
-
- notifySelectedDataChanged(previousRow);
- notifySelectedDataChanged(m_selectedRow);
-
- return true;
-}
-
-void CollectionDetailsModel::deselectAll()
-{
- selectColumn(-1);
- selectRow(-1);
-}
-
-void CollectionDetailsModel::loadCollection(const ModelNode &sourceNode, const QString &collection)
-{
- QString fileName = CollectionEditorUtils::getSourceCollectionPath(sourceNode);
-
- CollectionReference newReference{sourceNode, collection};
- bool alreadyOpen = m_openedCollections.contains(newReference);
-
- if (alreadyOpen) {
- if (m_currentCollection.reference() != newReference) {
- deselectAll();
- beginResetModel();
- switchToCollection(newReference);
- ensureSingleCell();
- endResetModel();
- }
- } else {
- deselectAll();
- switchToCollection(newReference);
- loadJsonCollection(fileName, collection);
- }
-}
-
-void CollectionDetailsModel::removeCollection(const ModelNode &sourceNode, const QString &collection)
-{
- CollectionReference collectionRef{sourceNode, collection};
- if (!m_openedCollections.contains(collectionRef))
- return;
-
- if (m_currentCollection.reference() == collectionRef)
- loadCollection({}, {});
-
- m_openedCollections.remove(collectionRef);
-}
-
-void CollectionDetailsModel::removeAllCollections()
-{
- loadCollection({}, {});
- m_openedCollections.clear();
-}
-
-void CollectionDetailsModel::renameCollection(const ModelNode &sourceNode,
- const QString &oldName,
- const QString &newName)
-{
- CollectionReference oldRef{sourceNode, oldName};
- if (!m_openedCollections.contains(oldRef))
- return;
-
- CollectionReference newReference{sourceNode, newName};
- bool collectionIsSelected = m_currentCollection.reference() == oldRef;
- CollectionDetails collection = m_openedCollections.take(oldRef);
- collection.resetReference(newReference);
- m_openedCollections.insert(newReference, collection);
-
- if (collectionIsSelected)
- setCollectionName(newName);
-}
-
-bool CollectionDetailsModel::saveDataStoreCollections()
-{
- const ModelNode node = m_currentCollection.reference().node;
- const Utils::FilePath path = CollectionEditorUtils::dataStoreJsonFilePath();
- Utils::FileReader fileData;
-
- if (!fileData.fetch(path)) {
- qWarning() << Q_FUNC_INFO << "Cannot read the json file:" << fileData.errorString();
- return false;
- }
-
- QJsonParseError jpe;
- QJsonDocument document = QJsonDocument::fromJson(fileData.data(), &jpe);
-
- if (jpe.error == QJsonParseError::NoError) {
- QJsonObject obj = document.object();
-
- QList<CollectionDetails> collectionsToBeSaved;
- for (CollectionDetails &openedCollection : m_openedCollections) {
- const CollectionReference reference = openedCollection.reference();
- if (reference.node == node) {
- obj.insert(reference.name, openedCollection.toLocalJson());
- collectionsToBeSaved << openedCollection;
- }
- }
-
- document.setObject(obj);
-
- if (CollectionEditorUtils::writeToJsonDocument(path, document)) {
- const CollectionReference currentReference = m_currentCollection.reference();
- for (CollectionDetails &collection : collectionsToBeSaved) {
- collection.markSaved();
- const CollectionReference reference = collection.reference();
- if (reference != currentReference)
- closeCollectionIfSaved(reference);
- }
- return true;
- }
- }
- return false;
-}
-
-bool CollectionDetailsModel::exportCollection(const QUrl &url)
-{
- using Core::EditorManager;
- using Utils::FilePath;
- using Utils::TextFileFormat;
-
- QTC_ASSERT(m_currentCollection.hasValidReference(), return false);
-
- bool saved = false;
- const FilePath filePath = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
- : url.toString());
- const QString saveFormat = filePath.toFileInfo().suffix().toLower();
- const QString content = saveFormat == "csv" ? m_currentCollection.toCsv()
- : m_currentCollection.toJson();
-
- TextFileFormat textFileFormat;
- textFileFormat.codec = EditorManager::defaultTextCodec();
- textFileFormat.lineTerminationMode = EditorManager::defaultLineEnding();
- QString errorMessage;
- saved = textFileFormat.writeFile(filePath, content, &errorMessage);
-
- if (!saved)
- qWarning() << Q_FUNC_INFO << "Unable to write file" << errorMessage;
-
- return saved;
-}
-
-const CollectionDetails CollectionDetailsModel::upToDateConstCollection(
- const CollectionReference &reference) const
-{
- using Utils::FilePath;
- using Utils::FileReader;
- CollectionDetails collection;
-
- if (m_openedCollections.contains(reference)) {
- collection = m_openedCollections.value(reference);
- } else {
- QUrl url = CollectionEditorUtils::getSourceCollectionPath(reference.node);
- FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
- : url.toString());
- FileReader file;
-
- if (!file.fetch(path))
- return collection;
-
- QJsonParseError jpe;
- QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe);
-
- if (jpe.error != QJsonParseError::NoError)
- return collection;
-
- collection = CollectionDetails::fromLocalJson(document, reference.name);
- collection.resetReference(reference);
- }
- return collection;
-}
-
-bool CollectionDetailsModel::collectionHasColumn(const CollectionReference &reference,
- const QString &columnName) const
-{
- const CollectionDetails collection = upToDateConstCollection(reference);
- return collection.containsPropertyName(columnName);
-}
-
-QString CollectionDetailsModel::getFirstColumnName(const CollectionReference &reference) const
-{
- const CollectionDetails collection = upToDateConstCollection(reference);
- return collection.propertyAt(0);
-}
-
-void CollectionDetailsModel::updateEmpty()
-{
- bool isEmptyNow = rowCount() == 0;
- if (m_isEmpty != isEmptyNow) {
- m_isEmpty = isEmptyNow;
- emit isEmptyChanged(m_isEmpty);
- }
-}
-
-void CollectionDetailsModel::switchToCollection(const CollectionReference &collection)
-{
- if (m_currentCollection.reference() == collection)
- return;
-
- closeCurrentCollectionIfSaved();
-
- if (!m_openedCollections.contains(collection))
- m_openedCollections.insert(collection, CollectionDetails(collection));
-
- m_currentCollection = m_openedCollections.value(collection);
-
- setCollectionName(collection.name);
-}
-
-void CollectionDetailsModel::closeCollectionIfSaved(const CollectionReference &collection)
-{
- if (!m_openedCollections.contains(collection))
- return;
-
- const CollectionDetails &collectionDetails = m_openedCollections.value(collection);
-
- if (!collectionDetails.isChanged())
- m_openedCollections.remove(collection);
-}
-
-void CollectionDetailsModel::closeCurrentCollectionIfSaved()
-{
- if (m_currentCollection.hasValidReference()) {
- closeCollectionIfSaved(m_currentCollection.reference());
- m_currentCollection = CollectionDetails{};
- }
-}
-
-void CollectionDetailsModel::loadJsonCollection(const QString &filePath, const QString &collection)
-{
- QJsonDocument document = readJsonFile(filePath);
-
- beginResetModel();
- m_currentCollection.resetData(document, collection);
- ensureSingleCell();
- endResetModel();
-}
-
-void CollectionDetailsModel::ensureSingleCell()
-{
- if (!m_currentCollection.hasValidReference())
- return;
-
- if (!columnCount())
- addColumn(0, "Column 1", "String");
-
- if (!rowCount())
- insertRow(0);
-
- updateEmpty();
-}
-
-QJsonDocument CollectionDetailsModel::readJsonFile(const QUrl &url)
-{
- using Utils::FilePath;
- using Utils::FileReader;
- FilePath path = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile() : url.toString());
- FileReader file;
-
- if (!file.fetch(path)) {
- emit warning(tr("File reading problem"), file.errorString());
- return {};
- }
-
- QJsonParseError jpe;
- QJsonDocument document = QJsonDocument::fromJson(file.data(), &jpe);
-
- if (jpe.error != QJsonParseError::NoError)
- emit warning(tr("Json parse error"), jpe.errorString());
-
- return document;
-}
-
-void CollectionDetailsModel::setCollectionName(const QString &newCollectionName)
-{
- if (m_collectionName != newCollectionName) {
- m_collectionName = newCollectionName;
- emit this->collectionNameChanged(m_collectionName);
- }
-}
-
-QString CollectionDetailsModel::warningToString(DataTypeWarning::Warning warning) const
-{
- return DataTypeWarning::getDataTypeWarningString(warning);
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
deleted file mode 100644
index 24a040cce6..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailsmodel.h
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "collectiondetails.h"
-
-#include <QAbstractTableModel>
-#include <QHash>
-
-namespace QmlDesigner {
-
-class ModelNode;
-
-class CollectionDetailsModel : public QAbstractTableModel
-{
- Q_OBJECT
-
- Q_PROPERTY(QString collectionName MEMBER m_collectionName NOTIFY collectionNameChanged)
- Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged)
- Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged)
- Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
-
-public:
- enum DataRoles { SelectedRole = Qt::UserRole + 1, DataTypeRole, ColumnDataTypeRole, DataTypeWarningRole };
- explicit CollectionDetailsModel(QObject *parent = nullptr);
-
- QHash<int, QByteArray> roleNames() const override;
- int rowCount(const QModelIndex &parent = {}) const override;
- int columnCount(const QModelIndex &parent = {}) const override;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
- bool setHeaderData(int section,
- Qt::Orientation orientation,
- const QVariant &value,
- int role = Qt::EditRole) override;
- bool insertRows(int row, int count, const QModelIndex &parent = {}) override;
- bool removeColumns(int column, int count, const QModelIndex &parent = {}) override;
- bool removeRows(int row, int count, const QModelIndex &parent = {}) override;
-
- Qt::ItemFlags flags(const QModelIndex &index) const override;
- QVariant headerData(int section,
- Qt::Orientation orientation,
- int role = Qt::DisplayRole) const override;
-
- CollectionDetails::DataType propertyDataType(int column) const;
-
- int selectedColumn() const;
- int selectedRow() const;
- Q_INVOKABLE QString propertyName(int column) const;
- Q_INVOKABLE QString propertyType(int column) const;
-
- Q_INVOKABLE bool isPropertyAvailable(const QString &name);
- Q_INVOKABLE bool addColumn(int column, const QString &name, const QString &propertyType = {});
- Q_INVOKABLE bool selectColumn(int section);
- Q_INVOKABLE bool renameColumn(int section, const QString &newValue);
- Q_INVOKABLE bool setPropertyType(int column, const QString &newValue);
- Q_INVOKABLE bool selectRow(int row);
- Q_INVOKABLE void deselectAll();
- Q_INVOKABLE QString warningToString(DataTypeWarning::Warning warning) const;
-
- void loadCollection(const ModelNode &sourceNode, const QString &collection);
- void removeCollection(const ModelNode &sourceNode, const QString &collection);
- void removeAllCollections();
- void renameCollection(const ModelNode &sourceNode, const QString &oldName, const QString &newName);
-
- Q_INVOKABLE bool saveDataStoreCollections();
- Q_INVOKABLE bool exportCollection(const QUrl &url);
-
- const CollectionDetails upToDateConstCollection(const CollectionReference &reference) const;
- bool collectionHasColumn(const CollectionReference &reference, const QString &columnName) const;
- QString getFirstColumnName(const CollectionReference &reference) const;
-
-signals:
- void collectionNameChanged(const QString &collectionName);
- void selectedColumnChanged(int);
- void selectedRowChanged(int);
- void isEmptyChanged(bool);
- void warning(const QString &title, const QString &body);
-
-private slots:
- void updateEmpty();
-
-private:
- void switchToCollection(const CollectionReference &collection);
- void closeCollectionIfSaved(const CollectionReference &collection);
- void closeCurrentCollectionIfSaved();
- void setCollectionName(const QString &newCollectionName);
- void loadJsonCollection(const QString &filePath, const QString &collection);
- void ensureSingleCell();
- QJsonDocument readJsonFile(const QUrl &url);
-
- QHash<CollectionReference, CollectionDetails> m_openedCollections;
- CollectionDetails m_currentCollection;
- bool m_isEmpty = true;
- int m_selectedColumn = -1;
- int m_selectedRow = -1;
-
- QString m_collectionName;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp
deleted file mode 100644
index f56bb36e88..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "collectiondetailssortfiltermodel.h"
-
-#include "collectiondetailsmodel.h"
-#include "collectioneditorutils.h"
-
-#include <utils/qtcassert.h>
-
-namespace QmlDesigner {
-
-CollectionDetailsSortFilterModel::CollectionDetailsSortFilterModel(QObject *parent)
- : QSortFilterProxyModel(parent)
-{
- connect(this, &CollectionDetailsSortFilterModel::rowsInserted,
- this, &CollectionDetailsSortFilterModel::updateRowCountChanges);
- connect(this, &CollectionDetailsSortFilterModel::rowsRemoved,
- this, &CollectionDetailsSortFilterModel::updateRowCountChanges);
- connect(this, &CollectionDetailsSortFilterModel::modelReset,
- this, &CollectionDetailsSortFilterModel::updateRowCountChanges);
-
- setDynamicSortFilter(true);
-}
-
-void CollectionDetailsSortFilterModel::setSourceModel(CollectionDetailsModel *model)
-{
- m_source = model;
- Super::setSourceModel(model);
- connect(m_source, &CollectionDetailsModel::selectedColumnChanged,
- this, &CollectionDetailsSortFilterModel::updateSelectedColumn);
-
- connect(m_source, &CollectionDetailsModel::selectedRowChanged,
- this, &CollectionDetailsSortFilterModel::updateSelectedRow);
-}
-
-int CollectionDetailsSortFilterModel::selectedRow() const
-{
- QTC_ASSERT(m_source, return -1);
-
- return mapFromSource(m_source->index(m_source->selectedRow(), 0)).row();
-}
-
-int CollectionDetailsSortFilterModel::selectedColumn() const
-{
- QTC_ASSERT(m_source, return -1);
-
- return mapFromSource(m_source->index(0, m_source->selectedColumn())).column();
-}
-
-bool CollectionDetailsSortFilterModel::selectRow(int row)
-{
- QTC_ASSERT(m_source, return false);
-
- return m_source->selectRow(mapToSource(index(row, 0)).row());
-}
-
-bool CollectionDetailsSortFilterModel::selectColumn(int column)
-{
- QTC_ASSERT(m_source, return false);
-
- return m_source->selectColumn(mapToSource(index(0, column)).column());
-}
-
-CollectionDetailsSortFilterModel::~CollectionDetailsSortFilterModel() = default;
-
-bool CollectionDetailsSortFilterModel::filterAcceptsRow(int sourceRow,
- const QModelIndex &sourceParent) const
-{
- QTC_ASSERT(m_source, return false);
- QModelIndex sourceIndex(m_source->index(sourceRow, 0, sourceParent));
- return sourceIndex.isValid();
-}
-
-bool CollectionDetailsSortFilterModel::lessThan(const QModelIndex &sourceleft,
- const QModelIndex &sourceRight) const
-{
- QTC_ASSERT(m_source, return false);
-
- if (sourceleft.column() == sourceRight.column()) {
- int column = sourceleft.column();
- CollectionDetails::DataType columnType = m_source->propertyDataType(column);
- return CollectionEditorUtils::variantIslessThan(sourceleft.data(),
- sourceRight.data(),
- columnType);
- }
-
- return false;
-}
-
-void CollectionDetailsSortFilterModel::updateEmpty()
-{
- bool newValue = rowCount() == 0;
- if (m_isEmpty != newValue) {
- m_isEmpty = newValue;
- emit isEmptyChanged(m_isEmpty);
- }
-}
-
-void CollectionDetailsSortFilterModel::updateSelectedRow()
-{
- const int upToDateSelectedRow = selectedRow();
- if (m_selectedRow == upToDateSelectedRow)
- return;
-
- const int rows = rowCount();
- const int columns = columnCount();
- const int previousRow = m_selectedRow;
-
- m_selectedRow = upToDateSelectedRow;
- emit this->selectedRowChanged(m_selectedRow);
-
- auto notifySelectedDataChanged = [this, rows, columns](int notifyingRow) {
- if (notifyingRow > -1 && notifyingRow < rows && columns) {
- emit dataChanged(index(notifyingRow, 0),
- index(notifyingRow, columns - 1),
- {CollectionDetailsModel::SelectedRole});
- }
- };
-
- notifySelectedDataChanged(previousRow);
- notifySelectedDataChanged(m_selectedRow);
-}
-
-void CollectionDetailsSortFilterModel::updateSelectedColumn()
-{
- const int upToDateSelectedColumn = selectedColumn();
- if (m_selectedColumn == upToDateSelectedColumn)
- return;
-
- const int rows = rowCount();
- const int columns = columnCount();
- const int previousColumn = m_selectedColumn;
-
- m_selectedColumn = upToDateSelectedColumn;
- emit this->selectedColumnChanged(m_selectedColumn);
-
- auto notifySelectedDataChanged = [this, rows, columns](int notifyingCol) {
- if (notifyingCol > -1 && notifyingCol < columns && rows) {
- emit dataChanged(index(0, notifyingCol),
- index(rows - 1, notifyingCol),
- {CollectionDetailsModel::SelectedRole});
- }
- };
-
- notifySelectedDataChanged(previousColumn);
- notifySelectedDataChanged(m_selectedColumn);
-}
-
-void CollectionDetailsSortFilterModel::updateRowCountChanges()
-{
- updateEmpty();
- updateSelectedRow();
- invalidate();
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h
deleted file mode 100644
index 93305f3ca2..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectiondetailssortfiltermodel.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QPointer>
-#include <QSortFilterProxyModel>
-
-namespace QmlDesigner {
-
-class CollectionDetailsModel;
-
-class CollectionDetailsSortFilterModel : public QSortFilterProxyModel
-{
- Q_OBJECT
-
- Q_PROPERTY(int selectedColumn READ selectedColumn WRITE selectColumn NOTIFY selectedColumnChanged)
- Q_PROPERTY(int selectedRow READ selectedRow WRITE selectRow NOTIFY selectedRowChanged)
- Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
-
- using Super = QSortFilterProxyModel;
-
-public:
- explicit CollectionDetailsSortFilterModel(QObject *parent = nullptr);
- virtual ~CollectionDetailsSortFilterModel();
-
- void setSourceModel(CollectionDetailsModel *model);
-
- int selectedRow() const;
- int selectedColumn() const;
-
- Q_INVOKABLE bool selectRow(int row);
- Q_INVOKABLE bool selectColumn(int column);
-
-signals:
- void selectedColumnChanged(int);
- void selectedRowChanged(int);
- void isEmptyChanged(bool);
-
-protected:
- using Super::setSourceModel;
- bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
- bool lessThan(const QModelIndex &sourceleft, const QModelIndex &sourceRight) const override;
-
-private:
- void updateEmpty();
- void updateSelectedRow();
- void updateSelectedColumn();
- void updateRowCountChanges();
-
- QPointer<CollectionDetailsModel> m_source;
- int m_selectedColumn = -1;
- int m_selectedRow = -1;
- bool m_isEmpty = true;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h
deleted file mode 100644
index 76524762ed..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorconstants.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-namespace QmlDesigner::CollectionEditorConstants {
-
-enum class SourceFormat { Unknown, Json };
-
-inline constexpr char SOURCEFILE_PROPERTY[] = "source";
-inline constexpr char ALLMODELS_PROPERTY[] = "allModels";
-inline constexpr char JSONCHILDMODELNAME_PROPERTY[] = "modelName";
-
-inline constexpr char COLLECTIONMODEL_IMPORT[] = "QtQuick.Studio.Utils";
-inline constexpr char JSONCOLLECTIONMODEL_TYPENAME[] = "QtQuick.Studio.Utils.JsonListModel";
-inline constexpr char JSONCOLLECTIONCHILDMODEL_TYPENAME[] = "QtQuick.Studio.Utils.ChildListModel";
-inline constexpr char JSONBACKEND_TYPENAME[] = "JsonData";
-
-} // namespace QmlDesigner::CollectionEditorConstants
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
deleted file mode 100644
index 4725987f12..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.cpp
+++ /dev/null
@@ -1,340 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "collectioneditorutils.h"
-
-#include "model.h"
-#include "nodemetainfo.h"
-#include "propertymetainfo.h"
-
-#include <coreplugin/documentmanager.h>
-#include <coreplugin/icore.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/projectmanager.h>
-#include <qmljs/qmljsmodelmanagerinterface.h>
-#include <utils/qtcassert.h>
-
-#include <variant>
-
-#include <QColor>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonParseError>
-#include <QJsonValue>
-#include <QUrl>
-
-using DataType = QmlDesigner::CollectionDetails::DataType;
-
-namespace {
-
-using CollectionDataVariant = std::variant<QString, bool, double, int, QUrl, QColor>;
-
-inline bool operator<(const QColor &a, const QColor &b)
-{
- return a.name(QColor::HexArgb) < b.name(QColor::HexArgb);
-}
-
-inline CollectionDataVariant valueToVariant(const QVariant &value, DataType type)
-{
- switch (type) {
- case DataType::String:
- return value.toString();
- case DataType::Real:
- return value.toDouble();
- case DataType::Integer:
- return value.toInt();
- case DataType::Boolean:
- return value.toBool();
- case DataType::Color:
- return value.value<QColor>();
- case DataType::Image:
- case DataType::Url:
- return value.value<QUrl>();
- default:
- return false;
- }
-}
-
-struct LessThanVisitor
-{
- template<typename T1, typename T2>
- bool operator()(const T1 &a, const T2 &b) const
- {
- return CollectionDataVariant(a).index() < CollectionDataVariant(b).index();
- }
-
- template<typename T>
- bool operator()(const T &a, const T &b) const
- {
- return a < b;
- }
-};
-
-Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName)
-{
- QDirIterator it(path.toString(), QDirIterator::Subdirectories);
-
- while (it.hasNext()) {
- QFileInfo file(it.next());
- if (file.isDir())
- continue;
-
- if (file.fileName() == fileName)
- return Utils::FilePath::fromFileInfo(file);
- }
- return {};
-}
-
-Utils::FilePath dataStoreDir()
-{
- using Utils::FilePath;
- ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::startupProject();
-
- if (!currentProject)
- return {};
-
- return currentProject->projectDirectory().pathAppended("/imports/"
- + currentProject->displayName());
-}
-
-inline Utils::FilePath collectionPath(const QString &filePath)
-{
- return dataStoreDir().pathAppended("/" + filePath);
-}
-
-inline Utils::FilePath qmlDirFilePath()
-{
- return collectionPath("qmldir");
-}
-
-} // namespace
-
-namespace QmlDesigner::CollectionEditorUtils {
-
-bool variantIslessThan(const QVariant &a, const QVariant &b, DataType type)
-{
- return std::visit(LessThanVisitor{}, valueToVariant(a, type), valueToVariant(b, type));
-}
-
-QString getSourceCollectionType(const ModelNode &node)
-{
- using namespace QmlDesigner;
- if (node.type() == CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
- return "json";
-
- return {};
-}
-
-Utils::FilePath dataStoreJsonFilePath()
-{
- return collectionPath("models.json");
-}
-
-Utils::FilePath dataStoreQmlFilePath()
-{
- return collectionPath("DataStore.qml");
-}
-
-bool canAcceptCollectionAsModel(const ModelNode &node)
-{
- const NodeMetaInfo nodeMetaInfo = node.metaInfo();
- if (!nodeMetaInfo.isValid())
- return false;
-
- const PropertyMetaInfo modelProperty = nodeMetaInfo.property("model");
- if (!modelProperty.isValid())
- return false;
-
- return modelProperty.isWritable() && !modelProperty.isPrivate()
- && modelProperty.propertyType().isVariant();
-}
-
-bool hasTextRoleProperty(const ModelNode &node)
-{
- const NodeMetaInfo nodeMetaInfo = node.metaInfo();
- if (!nodeMetaInfo.isValid())
- return false;
-
- const PropertyMetaInfo textRoleProperty = nodeMetaInfo.property("textRole");
- if (!textRoleProperty.isValid())
- return false;
-
- return textRoleProperty.isWritable() && !textRoleProperty.isPrivate()
- && textRoleProperty.propertyType().isString();
-}
-
-QString getSourceCollectionPath(const ModelNode &dataStoreNode)
-{
- using Utils::FilePath;
- if (!dataStoreNode.isValid())
- return {};
-
- const FilePath expectedFile = dataStoreJsonFilePath();
-
- if (expectedFile.exists())
- return expectedFile.toFSPathString();
-
- return {};
-}
-
-bool isDataStoreNode(const ModelNode &dataStoreNode)
-{
- using Utils::FilePath;
-
- if (!dataStoreNode.isValid())
- return false;
-
- const FilePath expectedFile = dataStoreQmlFilePath();
-
- if (!expectedFile.exists())
- return false;
-
- FilePath modelPath = FilePath::fromUserInput(dataStoreNode.model()->fileUrl().toLocalFile());
-
- return modelPath.isSameFile(expectedFile);
-}
-
-bool ensureDataStoreExists(bool &justCreated)
-{
- using Utils::FilePath;
- using Utils::FileReader;
- using Utils::FileSaver;
-
- FilePath qmlDestinationPath = dataStoreQmlFilePath();
- justCreated = false;
-
- auto extractDependency = [&justCreated](const FilePath &filePath) -> bool {
- if (filePath.exists())
- return true;
-
- const QString templateFileName = filePath.fileName() + u".tpl";
- const FilePath templatePath = findFile(Core::ICore::resourcePath(), templateFileName);
- if (!templatePath.exists()) {
- qWarning() << Q_FUNC_INFO << __LINE__ << templateFileName << "does not exist";
- return false;
- }
-
- if (!filePath.parentDir().ensureWritableDir()) {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot create directory"
- << filePath.parentDir();
- return false;
- }
-
- if (templatePath.copyFile(filePath)) {
- justCreated = true;
- return true;
- }
-
- qWarning() << Q_FUNC_INFO << __LINE__ << "Cannot copy" << templateFileName << "to" << filePath;
- return false;
- };
-
- if (!extractDependency(dataStoreJsonFilePath()))
- return false;
-
- if (!extractDependency(collectionPath("data.json")))
- return false;
-
- if (!extractDependency(collectionPath("JsonData.qml")))
- return false;
-
- if (!qmlDestinationPath.exists()) {
- if (qmlDestinationPath.ensureExistingFile()) {
- justCreated = true;
- } else {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Can't create DataStore Qml File";
- return false;
- }
- }
-
- FilePath qmlDirPath = qmlDirFilePath();
- qmlDirPath.ensureExistingFile();
-
- FileReader qmlDirReader;
- if (!qmlDirReader.fetch(qmlDirPath)) {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the qmldir";
- return false;
- }
-
- QByteArray qmlDirContent = qmlDirReader.data();
- const QList<QByteArray> qmlDirLines = qmlDirContent.split('\n');
- for (const QByteArray &line : qmlDirLines) {
- if (line.startsWith("singleton DataStore "))
- return true;
- }
-
- if (!qmlDirContent.isEmpty() && qmlDirContent.back() != '\n')
- qmlDirContent.append("\n");
- qmlDirContent.append("singleton DataStore 1.0 DataStore.qml\n");
-
- FileSaver qmlDirSaver(qmlDirPath);
- qmlDirSaver.write(qmlDirContent);
-
- if (qmlDirSaver.finalize()) {
- justCreated = true;
- return true;
- }
-
- qWarning() << Q_FUNC_INFO << __LINE__ << "Can't write to the qmldir file";
- return false;
-}
-
-QJsonObject defaultCollection()
-{
- QJsonObject collectionObject;
-
- QJsonArray columns;
- QJsonObject defaultColumn;
- defaultColumn.insert("name", "Column 1");
- defaultColumn.insert("type", "string");
- columns.append(defaultColumn);
-
- QJsonArray collectionData;
- QJsonArray cellData;
- cellData.append(QString{});
- collectionData.append(cellData);
-
- collectionObject.insert("columns", columns);
- collectionObject.insert("data", collectionData);
-
- return collectionObject;
-}
-
-QJsonObject defaultColorCollection()
-{
- using Utils::FilePath;
- using Utils::FileReader;
- const FilePath templatePath = findFile(Core::ICore::resourcePath(), "Colors.json.tpl");
-
- FileReader fileReader;
- if (!fileReader.fetch(templatePath)) {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Can't read the content of the file" << templatePath;
- return {};
- }
-
- QJsonParseError parseError;
- const CollectionDetails collection = CollectionDetails::fromImportedJson(fileReader.data(),
- &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- qWarning() << Q_FUNC_INFO << __LINE__ << "Error in template file" << parseError.errorString();
- return {};
- }
-
- return collection.toLocalJson();
-}
-
-bool writeToJsonDocument(const Utils::FilePath &path, const QJsonDocument &document, QString *errorString)
-{
- Core::FileChangeBlocker fileBlocker(path);
- Utils::FileSaver jsonFile(path);
- if (jsonFile.write(document.toJson()))
- jsonFile.finalize();
- if (errorString)
- *errorString = jsonFile.errorString();
-
- return !jsonFile.hasError();
-}
-
-} // namespace QmlDesigner::CollectionEditorUtils
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h b/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h
deleted file mode 100644
index 355addf59b..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectioneditorutils.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "collectiondetails.h"
-#include "collectioneditorconstants.h"
-
-QT_BEGIN_NAMESPACE
-class QJsonArray;
-class QJsonObject;
-QT_END_NAMESPACE
-
-namespace Utils {
-class FilePath;
-}
-
-namespace QmlDesigner::CollectionEditorUtils {
-
-bool variantIslessThan(const QVariant &a, const QVariant &b, CollectionDetails::DataType type);
-
-QString getSourceCollectionType(const QmlDesigner::ModelNode &node);
-
-QString getSourceCollectionPath(const QmlDesigner::ModelNode &dataStoreNode);
-
-Utils::FilePath dataStoreJsonFilePath();
-
-Utils::FilePath dataStoreQmlFilePath();
-
-bool writeToJsonDocument(const Utils::FilePath &path,
- const QJsonDocument &document,
- QString *errorString = nullptr);
-
-bool isDataStoreNode(const ModelNode &dataStoreNode);
-
-bool ensureDataStoreExists(bool &justCreated);
-
-bool canAcceptCollectionAsModel(const ModelNode &node);
-
-bool hasTextRoleProperty(const ModelNode &node);
-
-QJsonObject defaultCollection();
-
-QJsonObject defaultColorCollection();
-
-} // namespace QmlDesigner::CollectionEditorUtils
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp
deleted file mode 100644
index d27a077d2a..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.cpp
+++ /dev/null
@@ -1,521 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "collectionlistmodel.h"
-
-#include "collectioneditorutils.h"
-
-#include <utils/algorithm.h>
-#include <utils/fileutils.h>
-#include <utils/qtcassert.h>
-
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonParseError>
-
-namespace {
-
-template<typename ValueType>
-bool containsItem(const std::initializer_list<ValueType> &container, const ValueType &value)
-{
- auto begin = std::cbegin(container);
- auto end = std::cend(container);
-
- auto it = std::find(begin, end, value);
- return it != end;
-}
-
-bool sameCollectionNames(QStringList a, QStringList b)
-{
- if (a.size() != b.size())
- return false;
-
- a.sort(Qt::CaseSensitive);
- b.sort(Qt::CaseSensitive);
-
- return a == b;
-}
-
-} // namespace
-
-namespace QmlDesigner {
-
-CollectionListModel::CollectionListModel()
- : QAbstractListModel()
-{
- connect(this, &CollectionListModel::modelReset, this, &CollectionListModel::updateEmpty);
- connect(this, &CollectionListModel::rowsRemoved, this, &CollectionListModel::updateEmpty);
- connect(this, &CollectionListModel::rowsInserted, this, &CollectionListModel::updateEmpty);
-}
-
-QHash<int, QByteArray> CollectionListModel::roleNames() const
-{
- static QHash<int, QByteArray> roles;
- if (roles.isEmpty()) {
- roles.insert(Super::roleNames());
- roles.insert({
- {IdRole, "collectionId"},
- {NameRole, "collectionName"},
- {SelectedRole, "collectionIsSelected"},
- });
- }
- return roles;
-}
-
-int CollectionListModel::rowCount([[maybe_unused]] const QModelIndex &parent) const
-{
- return m_data.count();
-}
-
-bool CollectionListModel::setData(const QModelIndex &index, const QVariant &value, int role)
-{
- if (!index.isValid())
- return false;
-
- if (containsItem<int>({Qt::EditRole, Qt::DisplayRole, NameRole}, role)) {
- if (collectionExists(value.toString()))
- return false;
-
- QString oldName = collectionNameAt(index.row());
- bool nameChanged = value != data(index);
- if (nameChanged) {
- QString newName = value.toString();
- QString errorString;
- if (renameCollectionInDataStore(oldName, newName, errorString)) {
- m_data.replace(index.row(), newName);
- emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole, NameRole});
- emit this->collectionNameChanged(oldName, newName);
- if (m_selectedCollectionName == oldName)
- updateSelectedCollectionName();
- return true;
- } else {
- emit warning("Rename Model", errorString);
- return false;
- }
- }
- } else if (role == SelectedRole) {
- if (value.toBool() != index.data(SelectedRole).toBool()) {
- setSelectedIndex(value.toBool() ? index.row() : -1);
- return true;
- }
- }
- return false;
-}
-
-bool CollectionListModel::removeRows(int row, int count, const QModelIndex &parent)
-{
- const int rows = rowCount(parent);
- if (row >= rows)
- return false;
-
- row = qBound(0, row, rows - 1);
- count = qBound(0, count, rows - row);
-
- if (count < 1)
- return false;
-
- QString errorString;
- QStringList removedCollections = m_data.mid(row, count);
- if (removeCollectionsFromDataStore(removedCollections, errorString)) {
- beginRemoveRows(parent, row, row + count - 1);
- m_data.remove(row, count);
- endRemoveRows();
-
- emit collectionsRemoved(removedCollections);
- if (m_selectedIndex >= row) {
- int preferredIndex = m_selectedIndex - count;
- if (preferredIndex < 0) // If the selected item is deleted, reset selection
- selectCollectionIndex(-1);
- selectCollectionIndex(preferredIndex, true);
- }
-
- updateSelectedCollectionName();
- return true;
- } else {
- emit warning("Remove Model", errorString);
- return false;
- }
-}
-
-QVariant CollectionListModel::data(const QModelIndex &index, int role) const
-{
- QTC_ASSERT(index.isValid(), return {});
-
- switch (role) {
- case IdRole:
- return index.row();
- case SelectedRole:
- return index.row() == m_selectedIndex;
- case NameRole:
- default:
- return m_data.at(index.row());
- }
-}
-
-void CollectionListModel::setDataStoreNode(const ModelNode &dataStoreNode)
-{
- m_dataStoreNode = dataStoreNode;
- update();
-}
-
-int CollectionListModel::selectedIndex() const
-{
- return m_selectedIndex;
-}
-
-ModelNode CollectionListModel::sourceNode() const
-{
- return m_dataStoreNode;
-}
-
-bool CollectionListModel::collectionExists(const QString &collectionName) const
-{
- return m_data.contains(collectionName);
-}
-
-QStringList CollectionListModel::collections() const
-{
- return m_data;
-}
-
-QString CollectionListModel::getUniqueCollectionName(const QString &baseName) const
-{
- QString name = baseName.isEmpty() ? "Model" : baseName;
- QString nameTemplate = name + "%1";
-
- int num = 0;
-
- while (collectionExists(name))
- name = nameTemplate.arg(++num, 2, 10, QChar('0'));
-
- return name;
-}
-
-void CollectionListModel::selectCollectionIndex(int idx, bool selectAtLeastOne)
-{
- int collectionCount = m_data.size();
- int preferredIndex = -1;
- if (collectionCount) {
- if (selectAtLeastOne)
- preferredIndex = std::max(0, std::min(idx, collectionCount - 1));
- else if (idx > -1 && idx < collectionCount)
- preferredIndex = idx;
- }
-
- setSelectedIndex(preferredIndex);
-}
-
-void CollectionListModel::selectCollectionName(QString collectionName, bool selectAtLeastOne)
-{
- int idx = m_data.indexOf(collectionName);
- if (idx > -1)
- selectCollectionIndex(idx);
- else
- selectCollectionIndex(selectedIndex(), selectAtLeastOne);
-
- collectionName = collectionNameAt(selectedIndex());
- if (m_selectedCollectionName == collectionName)
- return;
-
- m_selectedCollectionName = collectionName;
- emit selectedCollectionNameChanged(m_selectedCollectionName);
-}
-
-QString CollectionListModel::collectionNameAt(int idx) const
-{
- return index(idx).data(NameRole).toString();
-}
-
-QString CollectionListModel::selectedCollectionName() const
-{
- return m_selectedCollectionName;
-}
-
-void CollectionListModel::update()
-{
- using Utils::FilePath;
- using Utils::FileReader;
-
- FileReader sourceFile;
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
- FilePath path = FilePath::fromUserInput(sourceFileAddress);
- bool fileRead = false;
- if (path.exists()) {
- fileRead = sourceFile.fetch(path);
- if (!fileRead)
- emit this->warning(tr("Model Editor"),
- tr("Cannot read the dataStore file\n%1").arg(sourceFile.errorString()));
- }
-
- QStringList collectionNames;
- if (fileRead) {
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(sourceFile.data(), &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- emit this->warning(tr("Model Editor"),
- tr("There is an error in the JSON file.\n%1")
- .arg(parseError.errorString()));
- } else {
- if (document.isObject())
- collectionNames = document.object().toVariantMap().keys();
- else
- emit this->warning(tr("Model Editor"), tr("The JSON document be an object."));
- }
- }
-
- if (!sameCollectionNames(m_data, collectionNames)) {
- QString prevSelectedCollection = selectedIndex() > -1 ? m_data.at(selectedIndex())
- : QString();
- beginResetModel();
- m_data = collectionNames;
- endResetModel();
- emit this->collectionNamesChanged(collections());
- selectCollectionName(prevSelectedCollection, true);
- }
-}
-
-bool CollectionListModel::addCollection(const QString &collectionName,
- const QJsonObject &localCollection)
-{
- if (collectionExists(collectionName)) {
- emit warning(tr("Add Model"), tr("Model \"%1\" already exists.").arg(collectionName));
- return false;
- }
-
- QString errorMessage;
- if (addCollectionToDataStore(collectionName, localCollection, errorMessage)) {
- int row = rowCount();
- beginInsertRows({}, row, row);
- m_data.append(collectionName);
- endInsertRows();
-
- selectCollectionName(collectionName);
- emit collectionAdded(collectionName);
- return true;
- } else {
- emit warning(tr("Add Collection"), errorMessage);
- }
- return false;
-}
-
-void CollectionListModel::setSelectedIndex(int idx)
-{
- idx = (idx > -1 && idx < rowCount()) ? idx : -1;
-
- if (m_selectedIndex != idx) {
- QModelIndex previousIndex = index(m_selectedIndex);
- QModelIndex newIndex = index(idx);
-
- m_selectedIndex = idx;
-
- if (previousIndex.isValid())
- emit dataChanged(previousIndex, previousIndex, {SelectedRole});
-
- if (newIndex.isValid())
- emit dataChanged(newIndex, newIndex, {SelectedRole});
-
- emit selectedIndexChanged(idx);
- updateSelectedCollectionName();
- }
-}
-
-bool CollectionListModel::removeCollectionsFromDataStore(const QStringList &removedCollections,
- QString &error) const
-{
- using Utils::FilePath;
- using Utils::FileReader;
- auto setErrorAndReturn = [&error](const QString &msg) -> bool {
- error = msg;
- return false;
- };
-
- if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
- return setErrorAndReturn(tr("Invalid node type"));
-
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
-
- QFileInfo sourceFileInfo(sourceFileAddress);
- if (!sourceFileInfo.isFile())
- return setErrorAndReturn(tr("The selected node has an invalid source address"));
-
- FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
- FileReader jsonFile;
- if (!jsonFile.fetch(jsonPath)) {
- return setErrorAndReturn(tr("Can't read file \"%1\".\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
- }
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
- }
-
- if (document.isObject()) {
- QJsonObject rootObject = document.object();
-
- for (const QString &collectionName : removedCollections) {
- bool sourceContainsCollection = rootObject.contains(collectionName);
- if (sourceContainsCollection) {
- rootObject.remove(collectionName);
- } else {
- setErrorAndReturn(tr("The model group doesn't contain the model name (%1).")
- .arg(sourceContainsCollection));
- }
- }
-
- document.setObject(rootObject);
-
- if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) {
- error.clear();
- return true;
- } else {
- return setErrorAndReturn(
- tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
- }
- } else {
- return setErrorAndReturn(tr("Local Json Document should be an object"));
- }
-
- return false;
-}
-
-bool CollectionListModel::renameCollectionInDataStore(const QString &oldName,
- const QString &newName,
- QString &error)
-{
- using Utils::FilePath;
- using Utils::FileReader;
- using Utils::FileSaver;
-
- auto setErrorAndReturn = [&error](const QString &msg) -> bool {
- error = msg;
- return false;
- };
-
- if (m_dataStoreNode.type() != CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME)
- return setErrorAndReturn(tr("Invalid node type"));
-
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
-
- QFileInfo sourceFileInfo(sourceFileAddress);
- if (!sourceFileInfo.isFile())
- return setErrorAndReturn(tr("Selected node must have a valid source file address"));
-
- FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
- FileReader jsonFile;
- if (!jsonFile.fetch(jsonPath)) {
- return setErrorAndReturn(
- tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
- }
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- return setErrorAndReturn(tr("\"%1\" is corrupted.\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
- }
-
- if (document.isObject()) {
- QJsonObject rootObject = document.object();
-
- bool collectionContainsOldName = rootObject.contains(oldName);
- bool collectionContainsNewName = rootObject.contains(newName);
-
- if (!collectionContainsOldName) {
- return setErrorAndReturn(
- tr("The model group doesn't contain the old model name (%1).").arg(oldName));
- }
-
- if (collectionContainsNewName) {
- return setErrorAndReturn(
- tr("The model name \"%1\" already exists in the model group.").arg(newName));
- }
-
- QJsonValue oldValue = rootObject.value(oldName);
- rootObject.insert(newName, oldValue);
- rootObject.remove(oldName);
-
- document.setObject(rootObject);
-
- if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document)) {
- error.clear();
- return true;
- } else {
- return setErrorAndReturn(
- tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
- }
- } else {
- return setErrorAndReturn(tr("Local Json Document should be an object"));
- }
- return false;
-}
-
-bool CollectionListModel::addCollectionToDataStore(const QString &collectionName,
- const QJsonObject &localCollection,
- QString &errorString) const
-{
- using Utils::FilePath;
- using Utils::FileReader;
- auto returnError = [&errorString](const QString &msg) -> bool {
- errorString = msg;
- return false;
- };
-
- if (collectionExists(collectionName))
- return returnError(tr("A model with the identical name already exists."));
-
- QString sourceFileAddress = CollectionEditorUtils::getSourceCollectionPath(m_dataStoreNode);
-
- QFileInfo sourceFileInfo(sourceFileAddress);
- if (!sourceFileInfo.isFile())
- return returnError(tr("Selected node must have a valid source file address"));
-
- FilePath jsonPath = FilePath::fromUserInput(sourceFileAddress);
- FileReader jsonFile;
- if (!jsonFile.fetch(jsonPath)) {
- return returnError(
- tr("Can't read \"%1\".\n%2").arg(sourceFileInfo.absoluteFilePath(), jsonFile.errorString()));
- }
-
- QJsonParseError parseError;
- QJsonDocument document = QJsonDocument::fromJson(jsonFile.data(), &parseError);
- if (parseError.error != QJsonParseError::NoError)
- return returnError(tr("\"%1\" is corrupted.\n%2")
- .arg(sourceFileInfo.absoluteFilePath(), parseError.errorString()));
-
- if (document.isObject()) {
- QJsonObject sourceObject = document.object();
- sourceObject.insert(collectionName, localCollection);
- document.setObject(sourceObject);
-
- if (CollectionEditorUtils::writeToJsonDocument(jsonPath, document))
- return true;
- else
- return returnError(tr("Can't write to \"%1\".").arg(sourceFileInfo.absoluteFilePath()));
- } else {
- return returnError(tr("JSON document type should be an object containing models."));
- }
-}
-
-void CollectionListModel::updateEmpty()
-{
- bool isEmptyNow = m_data.isEmpty();
- if (m_isEmpty != isEmptyNow) {
- m_isEmpty = isEmptyNow;
- emit isEmptyChanged(m_isEmpty);
-
- if (m_isEmpty)
- setSelectedIndex(-1);
- }
-}
-
-void CollectionListModel::updateSelectedCollectionName()
-{
- QString selectedCollectionByIndex = collectionNameAt(selectedIndex());
- if (selectedCollectionByIndex != selectedCollectionName())
- selectCollectionName(selectedCollectionByIndex);
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h b/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h
deleted file mode 100644
index 7902fd5909..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionlistmodel.h
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QAbstractListModel>
-#include <QHash>
-
-#include "modelnode.h"
-
-namespace QmlDesigner {
-
-class CollectionListModel : public QAbstractListModel
-{
- Q_OBJECT
-
- Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
- Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
- Q_PROPERTY(QString selectedCollectionName
- READ selectedCollectionName
- WRITE selectCollectionName
- NOTIFY selectedCollectionNameChanged)
-
-public:
- enum Roles { IdRole = Qt::UserRole + 1, NameRole, SelectedRole };
-
- explicit CollectionListModel();
- QHash<int, QByteArray> roleNames() const override;
-
- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
- bool setData(const QModelIndex &index, const QVariant &value, int role) override;
- bool removeRows(int row, int count, const QModelIndex &parent = {}) override;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
- void setDataStoreNode(const ModelNode &dataStoreNode = {});
-
- Q_INVOKABLE int selectedIndex() const;
- Q_INVOKABLE ModelNode sourceNode() const;
- Q_INVOKABLE bool collectionExists(const QString &collectionName) const;
- Q_INVOKABLE QStringList collections() const;
- Q_INVOKABLE QString getUniqueCollectionName(const QString &baseName = {}) const;
-
- void selectCollectionIndex(int idx, bool selectAtLeastOne = false);
- void selectCollectionName(QString collectionName, bool selectAtLeastOne = false);
- QString collectionNameAt(int idx) const;
- QString selectedCollectionName() const;
-
- void update();
- bool addCollection(const QString &collectionName, const QJsonObject &localCollection);
-
-signals:
- void selectedIndexChanged(int idx);
- void isEmptyChanged(bool);
- void collectionNameChanged(const QString &oldName, const QString &newName);
- void collectionNamesChanged(const QStringList &collectionNames);
- void collectionsRemoved(const QStringList &names);
- void collectionAdded(const QString &name);
- void selectedCollectionNameChanged(const QString &selectedCollectionName);
- void warning(const QString &title, const QString &body);
-
-private:
- void setSelectedIndex(int idx);
- bool removeCollectionsFromDataStore(const QStringList &removedCollections, QString &error) const;
- bool renameCollectionInDataStore(const QString &oldName, const QString &newName, QString &error);
- bool addCollectionToDataStore(const QString &collectionName,
- const QJsonObject &localCollection,
- QString &errorString) const;
-
- void updateEmpty();
- void updateSelectedCollectionName();
-
- using Super = QAbstractListModel;
- int m_selectedIndex = -1;
- bool m_isEmpty = false;
- ModelNode m_dataStoreNode;
- QString m_selectedCollectionName;
- QStringList m_data;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
deleted file mode 100644
index f6ec821fde..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.cpp
+++ /dev/null
@@ -1,469 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "collectionview.h"
-
-#include "collectiondatatypemodel.h"
-#include "collectiondetailsmodel.h"
-#include "collectioneditorconstants.h"
-#include "collectioneditorutils.h"
-#include "collectionlistmodel.h"
-#include "collectionwidget.h"
-#include "datastoremodelnode.h"
-#include "designmodecontext.h"
-#include "nodeabstractproperty.h"
-#include "nodemetainfo.h"
-#include "nodeproperty.h"
-#include "qmldesignerplugin.h"
-#include "variantproperty.h"
-
-#include <projectexplorer/project.h>
-#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/projectmanager.h>
-#include <qmljs/qmljsmodelmanagerinterface.h>
-
-#include <coreplugin/icore.h>
-#include <utils/algorithm.h>
-#include <utils/qtcassert.h>
-
-#include <QTimer>
-
-namespace {
-
-bool isStudioCollectionModel(const QmlDesigner::ModelNode &node)
-{
- return node.metaInfo().isQtQuickStudioUtilsJsonListModel();
-}
-
-inline void setVariantPropertyValue(const QmlDesigner::ModelNode &node,
- const QmlDesigner::PropertyName &propertyName,
- const QVariant &value)
-{
- QmlDesigner::VariantProperty property = node.variantProperty(propertyName);
- property.setValue(value);
-}
-
-inline void setBindingPropertyExpression(const QmlDesigner::ModelNode &node,
- const QmlDesigner::PropertyName &propertyName,
- const QString &expression)
-{
- QmlDesigner::BindingProperty property = node.bindingProperty(propertyName);
- property.setExpression(expression);
-}
-
-} // namespace
-
-namespace QmlDesigner {
-
-CollectionView::CollectionView(ExternalDependenciesInterface &externalDependencies)
- : AbstractView(externalDependencies)
- , m_dataStore(std::make_unique<DataStoreModelNode>())
-
-{
- connect(ProjectExplorer::ProjectManager::instance(),
- &ProjectExplorer::ProjectManager::startupProjectChanged, this, [this] {
- resetDataStoreNode();
- if (m_widget.get())
- m_widget->collectionDetailsModel()->removeAllCollections();
- });
-}
-
-bool CollectionView::hasWidget() const
-{
- return true;
-}
-
-QmlDesigner::WidgetInfo CollectionView::widgetInfo()
-{
- if (m_widget.isNull()) {
- m_widget = new CollectionWidget(this);
- m_widget->setMinimumSize(m_widget->minimumSizeHint());
-
- auto collectionEditorContext = new Internal::CollectionEditorContext(m_widget.data());
- Core::ICore::addContextObject(collectionEditorContext);
- CollectionListModel *listModel = m_widget->listModel().data();
-
- connect(listModel,
- &CollectionListModel::selectedCollectionNameChanged,
- this,
- [this](const QString &collection) {
- m_widget->collectionDetailsModel()->loadCollection(dataStoreNode(), collection);
- });
-
- connect(listModel, &CollectionListModel::isEmptyChanged, this, [this](bool isEmpty) {
- if (isEmpty)
- m_widget->collectionDetailsModel()->loadCollection({}, {});
- });
-
- connect(listModel, &CollectionListModel::modelReset, this, [this] {
- CollectionListModel *listModel = m_widget->listModel().data();
- if (listModel->sourceNode() == m_dataStore->modelNode())
- m_dataStore->setCollectionNames(listModel->collections());
- });
-
- connect(listModel,
- &CollectionListModel::collectionAdded,
- this,
- [this](const QString &collectionName) { m_dataStore->addCollection(collectionName); });
-
- connect(listModel,
- &CollectionListModel::collectionNameChanged,
- this,
- [this](const QString &oldName, const QString &newName) {
- m_dataStore->renameCollection(oldName, newName);
- m_widget->collectionDetailsModel()->renameCollection(dataStoreNode(),
- oldName,
- newName);
- });
-
- connect(listModel,
- &CollectionListModel::collectionsRemoved,
- this,
- [this](const QStringList &collectionNames) {
- m_dataStore->removeCollections(collectionNames);
- for (const QString &collectionName : collectionNames) {
- m_widget->collectionDetailsModel()->removeCollection(dataStoreNode(),
- collectionName);
- }
- });
- }
-
- return createWidgetInfo(m_widget.data(),
- "CollectionEditor",
- WidgetInfo::LeftPane,
- 0,
- tr("Model Editor [beta]"),
- tr("Model Editor view"));
-}
-
-void CollectionView::modelAttached(Model *model)
-{
- AbstractView::modelAttached(model);
- resetDataStoreNode();
-}
-
-void CollectionView::modelAboutToBeDetached([[maybe_unused]] Model *model)
-{
- m_libraryInfoIsUpdated = false;
- m_reloadCounter = 0;
- m_rewriterAmended = false;
- m_dataStoreTypeFound = false;
- disconnect(m_documentUpdateConnection);
- QTC_ASSERT(m_delayedTasks.isEmpty(), m_delayedTasks.clear());
- m_widget->listModel()->setDataStoreNode();
-}
-
-void CollectionView::selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
- [[maybe_unused]] const QList<ModelNode> &lastSelectedNodeList)
-{
- QList<ModelNode> selectedCollectionNodes = Utils::filtered(selectedNodeList,
- &isStudioCollectionModel);
-
- bool singleNonCollectionNodeSelected = selectedNodeList.size() == 1
- && selectedCollectionNodes.isEmpty();
-
- bool singleSelectedHasModelProperty = false;
- if (singleNonCollectionNodeSelected) {
- const ModelNode selectedNode = selectedNodeList.first();
- singleSelectedHasModelProperty = CollectionEditorUtils::canAcceptCollectionAsModel(
- selectedNode);
- }
-
- m_widget->setTargetNodeSelected(singleSelectedHasModelProperty);
-
- // More than one model is selected. So ignore them
- if (selectedCollectionNodes.size() > 1)
- return;
-}
-
-void CollectionView::customNotification(const AbstractView *,
- const QString &identifier,
- const QList<ModelNode> &nodeList,
- const QList<QVariant> &data)
-{
- if (identifier == QLatin1String("item_library_created_by_drop") && !nodeList.isEmpty())
- onItemLibraryNodeCreated(nodeList.first());
- else if (identifier == QLatin1String("open_collection_by_id") && !data.isEmpty())
- m_widget->openCollection(collectionNameFromDataStoreChildren(data.first().toByteArray()));
- else if (identifier == "delete_selected_collection")
- m_widget->deleteSelectedCollection();
-}
-
-void CollectionView::addResource(const QUrl &url, const QString &name)
-{
- executeInTransaction(Q_FUNC_INFO, [this, &url, &name]() {
- ensureStudioModelImport();
- QString sourceAddress;
- if (url.isLocalFile()) {
- Utils::FilePath fp = QmlDesignerPlugin::instance()->currentDesignDocument()->fileName().parentDir();
- sourceAddress = Utils::FilePath::calcRelativePath(url.toLocalFile(),
- fp.absoluteFilePath().toString());
- } else {
- sourceAddress = url.toString();
- }
-#ifdef QDS_USE_PROJECTSTORAGE
- ModelNode resourceNode = createModelNode("JsonListModel");
-#else
- const NodeMetaInfo resourceMetaInfo = jsonCollectionMetaInfo();
- ModelNode resourceNode = createModelNode(resourceMetaInfo.typeName(),
- resourceMetaInfo.majorVersion(),
- resourceMetaInfo.minorVersion());
-#endif
- VariantProperty sourceProperty = resourceNode.variantProperty(
- CollectionEditorConstants::SOURCEFILE_PROPERTY);
- VariantProperty nameProperty = resourceNode.variantProperty("objectName");
- sourceProperty.setValue(sourceAddress);
- nameProperty.setValue(name);
- resourceNode.setIdWithoutRefactoring(model()->generateIdFromName(name, "model"));
- rootModelNode().defaultNodeAbstractProperty().reparentHere(resourceNode);
- });
-}
-
-void CollectionView::assignCollectionToNode(const QString &collectionName, const ModelNode &node)
-{
- using DataType = CollectionDetails::DataType;
- executeInTransaction("CollectionView::assignCollectionToNode", [&]() {
- m_dataStore->assignCollectionToNode(
- this,
- node,
- collectionName,
- [&](const QString &collectionName, const QString &columnName) -> bool {
- const CollectionReference reference{dataStoreNode(), collectionName};
- return m_widget->collectionDetailsModel()->collectionHasColumn(reference, columnName);
- },
- [&](const QString &collectionName) -> QString {
- const CollectionReference reference{dataStoreNode(), collectionName};
- return m_widget->collectionDetailsModel()->getFirstColumnName(reference);
- });
-
- // Create and assign a delegate to the list view item
- if (node.metaInfo().isQtQuickListView()) {
- CollectionDetails collection = m_widget->collectionDetailsModel()->upToDateConstCollection(
- {dataStoreNode(), collectionName});
-
- ModelNode rowItem(createModelNode("QtQuick.Row"));
- ::setVariantPropertyValue(rowItem, "spacing", 5);
-
- const int columnsCount = collection.columns();
- for (int column = 0; column < columnsCount; ++column) {
- const DataType dataType = collection.typeAt(column);
- const QString columnName = collection.propertyAt(column);
- ModelNode cellItem;
- if (dataType == DataType::Color) {
- cellItem = createModelNode("QtQuick.Rectangle");
- ::setBindingPropertyExpression(cellItem, "color", columnName);
- ::setVariantPropertyValue(cellItem, "height", 20);
- } else {
- cellItem = createModelNode("QtQuick.Text");
- ::setBindingPropertyExpression(cellItem, "text", columnName);
- }
- ::setVariantPropertyValue(cellItem, "width", 100);
- rowItem.defaultNodeAbstractProperty().reparentHere(cellItem);
- }
-
- NodeProperty delegateProperty = node.nodeProperty("delegate");
- // Remove the old model node if is available
- if (delegateProperty.modelNode())
- delegateProperty.modelNode().destroy();
-
- delegateProperty.setModelNode(rowItem);
- }
- });
-}
-
-void CollectionView::assignCollectionToSelectedNode(const QString &collectionName)
-{
- QTC_ASSERT(dataStoreNode() && hasSingleSelectedModelNode(), return);
- assignCollectionToNode(collectionName, singleSelectedModelNode());
-}
-
-void CollectionView::addNewCollection(const QString &collectionName, const QJsonObject &localCollection)
-{
- addTask(QSharedPointer<CollectionTask>(
- new AddCollectionTask(this, m_widget->listModel(), localCollection, collectionName)));
-}
-
-void CollectionView::openCollection(const QString &collectionName)
-{
- m_widget->openCollection(collectionName);
-}
-
-void CollectionView::registerDeclarativeType()
-{
- CollectionDetails::registerDeclarativeType();
- CollectionDataTypeModel::registerDeclarativeType();
-}
-
-void CollectionView::resetDataStoreNode()
-{
- m_dataStore->reloadModel();
-
- ModelNode dataStore = m_dataStore->modelNode();
- if (!dataStore || m_widget->listModel()->sourceNode() == dataStore)
- return;
-
- bool dataStoreSingletonFound = m_dataStoreTypeFound;
- if (!dataStoreSingletonFound && rewriterView() && rewriterView()->isAttached()) {
- const QList<QmlTypeData> types = rewriterView()->getQMLTypes();
- for (const QmlTypeData &cppTypeData : types) {
- if (cppTypeData.isSingleton && cppTypeData.typeName == "DataStore") {
- dataStoreSingletonFound = true;
- break;
- }
- }
- if (!dataStoreSingletonFound && !m_rewriterAmended) {
- rewriterView()->forceAmend();
- m_rewriterAmended = true;
- }
- }
-
- if (dataStoreSingletonFound) {
- m_widget->listModel()->setDataStoreNode(dataStore);
- m_dataStoreTypeFound = true;
-
- while (!m_delayedTasks.isEmpty())
- m_delayedTasks.takeFirst()->process();
- } else if (++m_reloadCounter < 50) {
- QTimer::singleShot(200, this, &CollectionView::resetDataStoreNode);
- } else {
- QTC_ASSERT(false, m_delayedTasks.clear());
- }
-}
-
-ModelNode CollectionView::dataStoreNode() const
-{
- return m_dataStore->modelNode();
-}
-
-void CollectionView::ensureDataStoreExists()
-{
- bool filesJustCreated = false;
- bool filesExist = CollectionEditorUtils::ensureDataStoreExists(filesJustCreated);
- if (filesExist) {
- if (filesJustCreated) {
- // Force code model reset to notice changes to existing module
- auto modelManager = QmlJS::ModelManagerInterface::instance();
- if (modelManager) {
- m_libraryInfoIsUpdated = false;
-
- m_expectedDocumentUpdates.clear();
- m_expectedDocumentUpdates << CollectionEditorUtils::dataStoreQmlFilePath()
- << CollectionEditorUtils::dataStoreJsonFilePath();
-
- m_documentUpdateConnection = connect(modelManager,
- &QmlJS::ModelManagerInterface::documentUpdated,
- this,
- &CollectionView::onDocumentUpdated);
-
- modelManager->resetCodeModel();
- }
- resetDataStoreNode();
- } else {
- m_libraryInfoIsUpdated = true;
- }
- }
-}
-
-QString CollectionView::collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const
-{
- return dataStoreNode()
- .nodeProperty(childPropertyName)
- .modelNode()
- .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)
- .toVariantProperty()
- .value()
- .toString();
-}
-
-NodeMetaInfo CollectionView::jsonCollectionMetaInfo() const
-{
- return model()->metaInfo(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME);
-}
-
-void CollectionView::ensureStudioModelImport()
-{
- executeInTransaction(__FUNCTION__, [&] {
- Import import = Import::createLibraryImport(CollectionEditorConstants::COLLECTIONMODEL_IMPORT);
- try {
- if (!model()->hasImport(import, true, true))
- model()->changeImports({import}, {});
- } catch (const Exception &) {
- QTC_ASSERT(false, return);
- }
- });
-}
-
-void CollectionView::onItemLibraryNodeCreated(const ModelNode &node)
-{
- if (node.metaInfo().isQtQuickListView()) {
- addTask(QSharedPointer<CollectionTask>(
- new DropListViewTask(this, m_widget->listModel(), node)));
- }
-}
-
-void CollectionView::onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc)
-{
- if (m_expectedDocumentUpdates.contains(doc->fileName()))
- m_expectedDocumentUpdates.remove(doc->fileName());
-
- if (m_expectedDocumentUpdates.isEmpty()) {
- disconnect(m_documentUpdateConnection);
- m_libraryInfoIsUpdated = true;
- }
-}
-
-void CollectionView::addTask(QSharedPointer<CollectionTask> task)
-{
- ensureDataStoreExists();
- if (m_dataStoreTypeFound)
- task->process();
- else if (m_dataStore->modelNode())
- m_delayedTasks << task;
-}
-
-CollectionTask::CollectionTask(CollectionView *view, CollectionListModel *listModel)
- : m_collectionView(view)
- , m_listModel(listModel)
-{}
-
-DropListViewTask::DropListViewTask(CollectionView *view,
- CollectionListModel *listModel,
- const ModelNode &node)
- : CollectionTask(view, listModel)
- , m_node(node)
-{}
-
-void DropListViewTask::process()
-{
- AbstractView *view = m_node.view();
- if (!m_node || !m_collectionView || !m_listModel || !view)
- return;
-
- const QString newCollectionName = m_listModel->getUniqueCollectionName("ListModel");
- m_listModel->addCollection(newCollectionName, CollectionEditorUtils::defaultColorCollection());
- m_collectionView->openCollection(newCollectionName);
- m_collectionView->assignCollectionToNode(newCollectionName, m_node);
-}
-
-AddCollectionTask::AddCollectionTask(CollectionView *view,
- CollectionListModel *listModel,
- const QJsonObject &localJsonObject,
- const QString &collectionName)
- : CollectionTask(view, listModel)
- , m_localJsonObject(localJsonObject)
- , m_name(collectionName)
-{}
-
-void AddCollectionTask::process()
-{
- if (!m_listModel)
- return;
-
- const QString newCollectionName = m_listModel->collectionExists(m_name)
- ? m_listModel->getUniqueCollectionName(m_name)
- : m_name;
-
- m_listModel->addCollection(newCollectionName, m_localJsonObject);
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h b/src/plugins/qmldesigner/components/collectioneditor/collectionview.h
deleted file mode 100644
index a4b16c4c27..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionview.h
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "abstractview.h"
-#include "datastoremodelnode.h"
-#include "modelnode.h"
-
-#include <QJsonObject>
-
-namespace QmlJS {
-class Document;
-}
-
-namespace QmlDesigner {
-
-class CollectionDetails;
-class CollectionListModel;
-class CollectionTask;
-class CollectionWidget;
-class DataStoreModelNode;
-
-class CollectionView : public AbstractView
-{
- Q_OBJECT
-
-public:
- explicit CollectionView(ExternalDependenciesInterface &externalDependencies);
-
- bool hasWidget() const override;
- WidgetInfo widgetInfo() override;
-
- void modelAttached(Model *model) override;
- void modelAboutToBeDetached(Model *model) override;
-
- void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
- const QList<ModelNode> &lastSelectedNodeList) override;
-
- void customNotification(const AbstractView *view,
- const QString &identifier,
- const QList<ModelNode> &nodeList,
- const QList<QVariant> &data) override;
-
- void addResource(const QUrl &url, const QString &name);
-
- void assignCollectionToNode(const QString &collectionName, const ModelNode &node);
- void assignCollectionToSelectedNode(const QString &collectionName);
- void addNewCollection(const QString &collectionName, const QJsonObject &localCollection);
-
- void openCollection(const QString &collectionName);
-
- static void registerDeclarativeType();
-
- void resetDataStoreNode();
- ModelNode dataStoreNode() const;
- void ensureDataStoreExists();
- QString collectionNameFromDataStoreChildren(const PropertyName &childPropertyName) const;
-
-private:
- friend class CollectionTask;
-
- NodeMetaInfo jsonCollectionMetaInfo() const;
- void ensureStudioModelImport();
- void onItemLibraryNodeCreated(const ModelNode &node);
- void onDocumentUpdated(const QSharedPointer<const QmlJS::Document> &doc);
- void addTask(QSharedPointer<CollectionTask> task);
-
- QPointer<CollectionWidget> m_widget;
- std::unique_ptr<DataStoreModelNode> m_dataStore;
- QSet<Utils::FilePath> m_expectedDocumentUpdates;
- QList<QSharedPointer<CollectionTask>> m_delayedTasks;
- QMetaObject::Connection m_documentUpdateConnection;
- bool m_libraryInfoIsUpdated = false;
- bool m_dataStoreTypeFound = false;
- bool m_rewriterAmended = false;
- int m_reloadCounter = 0;
-};
-
-class CollectionTask
-{
-public:
- CollectionTask(CollectionView *view, CollectionListModel *listModel);
- CollectionTask() = delete;
- virtual ~CollectionTask() = default;
-
- virtual void process() = 0;
-
-protected:
- QPointer<CollectionView> m_collectionView;
- QPointer<CollectionListModel> m_listModel;
-};
-
-class DropListViewTask : public CollectionTask
-{
-public:
- DropListViewTask(CollectionView *view, CollectionListModel *listModel, const ModelNode &node);
-
- void process() override;
-
-private:
- ModelNode m_node;
-};
-
-class AddCollectionTask : public CollectionTask
-{
-public:
- AddCollectionTask(CollectionView *view,
- CollectionListModel *listModel,
- const QJsonObject &localJsonObject,
- const QString &collectionName);
-
- void process() override;
-
-private:
- QJsonObject m_localJsonObject;
- QString m_name;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
deleted file mode 100644
index 093729dc67..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.cpp
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "collectionwidget.h"
-
-#include "collectiondetails.h"
-#include "collectiondetailsmodel.h"
-#include "collectiondetailssortfiltermodel.h"
-#include "collectioneditorutils.h"
-#include "collectionlistmodel.h"
-#include "collectionview.h"
-#include "designmodewidget.h"
-#include "qmldesignerconstants.h"
-#include "qmldesignerplugin.h"
-#include "theme.h"
-
-#include <coreplugin/icore.h>
-#include <coreplugin/messagebox.h>
-#include <studioquickwidget.h>
-
-#include <QFileInfo>
-#include <QJsonArray>
-#include <QJsonDocument>
-#include <QJsonObject>
-#include <QJsonParseError>
-#include <QMetaObject>
-#include <QQmlEngine>
-#include <QQuickItem>
-#include <QShortcut>
-#include <QVBoxLayout>
-
-namespace {
-
-QString collectionViewResourcesPath()
-{
-#ifdef SHARE_QML_PATH
- if (qEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
- return QLatin1String(SHARE_QML_PATH) + "/collectionEditorQmlSource";
-#endif
- return Core::ICore::resourcePath("qmldesigner/collectionEditorQmlSource").toString();
-}
-
-QString getPreferredCollectionName(const QUrl &url, const QString &collectionName)
-{
- if (collectionName.isEmpty()) {
- QFileInfo fileInfo(url.isLocalFile() ? url.toLocalFile() : url.toString());
- return fileInfo.completeBaseName();
- }
-
- return collectionName;
-}
-
-} // namespace
-
-namespace QmlDesigner {
-CollectionWidget::CollectionWidget(CollectionView *view)
- : QFrame()
- , m_view(view)
- , m_listModel(new CollectionListModel)
- , m_collectionDetailsModel(new CollectionDetailsModel)
- , m_collectionDetailsSortFilterModel(std::make_unique<CollectionDetailsSortFilterModel>())
- , m_quickWidget(new StudioQuickWidget(this))
-{
- setWindowTitle(tr("Model Editor", "Title of model editor widget"));
-
- Core::IContext *icontext = nullptr;
- Core::Context context(Constants::C_QMLCOLLECTIONEDITOR);
- icontext = new Core::IContext(this);
- icontext->setContext(context);
- icontext->setWidget(this);
-
- connect(m_listModel, &CollectionListModel::warning, this, &CollectionWidget::warn);
-
- m_collectionDetailsSortFilterModel->setSourceModel(m_collectionDetailsModel);
-
- m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_COLLECTION_EDITOR);
- m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
- m_quickWidget->engine()->addImportPath(collectionViewResourcesPath() + "/imports");
- m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground));
-
- Theme::setupTheme(m_quickWidget->engine());
- m_quickWidget->quickWidget()->installEventFilter(this);
-
- auto layout = new QVBoxLayout(this);
- layout->setContentsMargins({});
- layout->setSpacing(0);
- layout->addWidget(m_quickWidget.data());
-
- qmlRegisterAnonymousType<CollectionWidget>("CollectionEditorBackend", 1);
- auto map = m_quickWidget->registerPropertyMap("CollectionEditorBackend");
- map->setProperties({
- {"rootView", QVariant::fromValue(this)},
- {"model", QVariant::fromValue(m_listModel.data())},
- {"collectionDetailsModel", QVariant::fromValue(m_collectionDetailsModel.data())},
- {"collectionDetailsSortFilterModel",
- QVariant::fromValue(m_collectionDetailsSortFilterModel.get())},
- });
-
- auto hotReloadShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F4), this);
- connect(hotReloadShortcut, &QShortcut::activated, this, &CollectionWidget::reloadQmlSource);
-
- reloadQmlSource();
-
- QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_MODELEDITOR_TIME);
-}
-
-void CollectionWidget::contextHelp(const Core::IContext::HelpCallback &callback) const
-{
- if (m_view)
- QmlDesignerPlugin::contextHelp(callback, m_view->contextHelpId());
- else
- callback({});
-}
-
-QPointer<CollectionListModel> CollectionWidget::listModel() const
-{
- return m_listModel;
-}
-
-QPointer<CollectionDetailsModel> CollectionWidget::collectionDetailsModel() const
-{
- return m_collectionDetailsModel;
-}
-
-void CollectionWidget::reloadQmlSource()
-{
- const QString collectionViewQmlPath = collectionViewResourcesPath() + "/CollectionView.qml";
-
- QTC_ASSERT(QFileInfo::exists(collectionViewQmlPath), return);
-
- m_quickWidget->setSource(QUrl::fromLocalFile(collectionViewQmlPath));
-
- if (!m_quickWidget->rootObject()) {
- QString errorString;
- const auto errors = m_quickWidget->errors();
- for (const QQmlError &error : errors)
- errorString.append("\n" + error.toString());
-
- Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"),
- tr("StatesEditorWidget: %1 cannot be created.%2")
- .arg(collectionViewQmlPath, errorString));
- return;
- }
-}
-
-QSize CollectionWidget::minimumSizeHint() const
-{
- return {300, 300};
-}
-
-bool CollectionWidget::loadJsonFile(const QUrl &url, const QString &collectionName)
-{
- if (!isJsonFile(url))
- return false;
-
- m_view->addResource(url, getPreferredCollectionName(url, collectionName));
-
- return true;
-}
-
-bool CollectionWidget::loadCsvFile(const QUrl &url, const QString &collectionName)
-{
- m_view->addResource(url, getPreferredCollectionName(url, collectionName));
-
- return true;
-}
-
-bool CollectionWidget::isJsonFile(const QUrl &url) const
-{
- Utils::FilePath filePath = Utils::FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
- : url.toString());
- Utils::FileReader file;
- if (!file.fetch(filePath))
- return false;
-
- QJsonParseError error;
- QJsonDocument::fromJson(file.data(), &error);
- if (error.error)
- return false;
-
- return true;
-}
-
-bool CollectionWidget::isCsvFile(const QUrl &url) const
-{
- QString filePath = url.isLocalFile() ? url.toLocalFile() : url.toString();
- QFileInfo fileInfo(filePath);
- return fileInfo.exists() && !fileInfo.suffix().compare("csv", Qt::CaseInsensitive);
-}
-
-bool CollectionWidget::isValidUrlToImport(const QUrl &url) const
-{
- using Utils::FilePath;
- FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
- : url.toString());
- if (fileInfo.suffix() == "json")
- return isJsonFile(url);
-
- if (fileInfo.suffix() == "csv")
- return isCsvFile(url);
-
- return false;
-}
-
-bool CollectionWidget::importFile(const QString &collectionName,
- const QUrl &url,
- const bool &firstRowIsHeader)
-{
- using Utils::FilePath;
-
- FilePath fileInfo = FilePath::fromUserInput(url.isLocalFile() ? url.toLocalFile()
- : url.toString());
- CollectionDetails loadedCollection;
- QByteArray fileContent;
-
- auto loadUrlContent = [&]() -> bool {
- Utils::FileReader file;
- if (file.fetch(fileInfo)) {
- fileContent = file.data();
- return true;
- }
-
- warn(tr("Import from file"), tr("Cannot import from file \"%1\"").arg(fileInfo.fileName()));
- return false;
- };
-
- if (fileInfo.suffix() == "json") {
- if (!loadUrlContent())
- return false;
-
- QJsonParseError parseError;
- loadedCollection = CollectionDetails::fromImportedJson(fileContent, &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- warn(tr("Json file Import error"),
- tr("Cannot parse json content\n%1").arg(parseError.errorString()));
- }
- } else if (fileInfo.suffix() == "csv") {
- if (!loadUrlContent())
- return false;
- loadedCollection = CollectionDetails::fromImportedCsv(fileContent, firstRowIsHeader);
- }
-
- if (loadedCollection.columns()) {
- m_view->addNewCollection(collectionName, loadedCollection.toLocalJson());
- return true;
- } else {
- warn(tr("Can not add a model to the JSON file"),
- tr("The imported model is empty or is not supported."));
- }
- return false;
-}
-
-void CollectionWidget::addCollectionToDataStore(const QString &collectionName)
-{
- m_view->addNewCollection(collectionName, CollectionEditorUtils::defaultCollection());
-}
-
-void CollectionWidget::assignCollectionToSelectedNode(const QString collectionName)
-{
- m_view->assignCollectionToSelectedNode(collectionName);
-}
-
-void CollectionWidget::openCollection(const QString &collectionName)
-{
- m_listModel->selectCollectionName(collectionName);
- QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("CollectionEditor", true);
-}
-
-ModelNode CollectionWidget::dataStoreNode() const
-{
- return m_view->dataStoreNode();
-}
-
-void CollectionWidget::warn(const QString &title, const QString &body)
-{
- QMetaObject::invokeMethod(m_quickWidget->rootObject(),
- "showWarning",
- Q_ARG(QVariant, title),
- Q_ARG(QVariant, body));
-}
-
-void CollectionWidget::setTargetNodeSelected(bool selected)
-{
- if (m_targetNodeSelected == selected)
- return;
-
- m_targetNodeSelected = selected;
- emit targetNodeSelectedChanged(m_targetNodeSelected);
-}
-
-void CollectionWidget::deleteSelectedCollection()
-{
- QMetaObject::invokeMethod(m_quickWidget->quickWidget()->rootObject(), "deleteSelectedCollection");
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h b/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
deleted file mode 100644
index 0957bd81e0..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/collectionwidget.h
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <QFrame>
-
-#include <coreplugin/icontext.h>
-
-class StudioQuickWidget;
-
-namespace QmlDesigner {
-
-class CollectionDetailsModel;
-class CollectionDetailsSortFilterModel;
-class CollectionListModel;
-class CollectionView;
-class ModelNode;
-
-class CollectionWidget : public QFrame
-{
- Q_OBJECT
-
- Q_PROPERTY(bool targetNodeSelected MEMBER m_targetNodeSelected NOTIFY targetNodeSelectedChanged)
-
-public:
- CollectionWidget(CollectionView *view);
- void contextHelp(const Core::IContext::HelpCallback &callback) const;
-
- QPointer<CollectionListModel> listModel() const;
- QPointer<CollectionDetailsModel> collectionDetailsModel() const;
-
- void reloadQmlSource();
-
- virtual QSize minimumSizeHint() const;
-
- Q_INVOKABLE bool loadJsonFile(const QUrl &url, const QString &collectionName = {});
- Q_INVOKABLE bool loadCsvFile(const QUrl &url, const QString &collectionName = {});
- Q_INVOKABLE bool isJsonFile(const QUrl &url) const;
- Q_INVOKABLE bool isCsvFile(const QUrl &url) const;
- Q_INVOKABLE bool isValidUrlToImport(const QUrl &url) const;
-
- Q_INVOKABLE bool importFile(const QString &collectionName,
- const QUrl &url,
- const bool &firstRowIsHeader = true);
-
- Q_INVOKABLE void addCollectionToDataStore(const QString &collectionName);
- Q_INVOKABLE void assignCollectionToSelectedNode(const QString collectionName);
- Q_INVOKABLE void openCollection(const QString &collectionName);
- Q_INVOKABLE ModelNode dataStoreNode() const;
-
- void warn(const QString &title, const QString &body);
- void setTargetNodeSelected(bool selected);
-
- void deleteSelectedCollection();
-
-signals:
- void targetNodeSelectedChanged(bool);
-
-private:
- QString generateUniqueCollectionName(const ModelNode &node, const QString &name);
-
- QPointer<CollectionView> m_view;
- QPointer<CollectionListModel> m_listModel;
- QPointer<CollectionDetailsModel> m_collectionDetailsModel;
- std::unique_ptr<CollectionDetailsSortFilterModel> m_collectionDetailsSortFilterModel;
- QScopedPointer<StudioQuickWidget> m_quickWidget;
- bool m_targetNodeSelected = false;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp
deleted file mode 100644
index 5be9c20f9e..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.cpp
+++ /dev/null
@@ -1,511 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "datastoremodelnode.h"
-
-#include "abstractview.h"
-#include "collectioneditorconstants.h"
-#include "collectioneditorutils.h"
-#include "model/qmltextgenerator.h"
-#include "plaintexteditmodifier.h"
-#include "qmldesignerbase/qmldesignerbaseplugin.h"
-#include "qmldesignerexternaldependencies.h"
-#include "rewriterview.h"
-
-#include <model.h>
-#include <nodemetainfo.h>
-#include <nodeproperty.h>
-#include <variantproperty.h>
-
-#include <qmljstools/qmljscodestylepreferences.h>
-#include <qmljstools/qmljstoolssettings.h>
-
-#include <coreplugin/documentmanager.h>
-#include <projectexplorer/project.h>
-#include <projectexplorer/projectexplorer.h>
-#include <projectexplorer/projectmanager.h>
-
-#include <utils/fileutils.h>
-#include <utils/qtcassert.h>
-
-#include <QPlainTextEdit>
-#include <QRegularExpression>
-#include <QRegularExpressionMatch>
-#include <QScopedPointer>
-
-namespace {
-
-inline constexpr char CHILDLISTMODEL_TYPENAME[] = "ChildListModel";
-
-QmlDesigner::PropertyNameList createNameList(const QmlDesigner::ModelNode &node)
-{
- using QmlDesigner::AbstractProperty;
- using QmlDesigner::PropertyName;
- using QmlDesigner::PropertyNameList;
- static PropertyNameList defaultsNodeProps = {
- "id",
- QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY,
- QmlDesigner::CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY,
- "backend"};
- PropertyNameList dynamicPropertyNames = Utils::transform(
- node.dynamicProperties(),
- [](const AbstractProperty &property) -> PropertyName { return property.name(); });
-
- Utils::sort(dynamicPropertyNames);
-
- return defaultsNodeProps + dynamicPropertyNames;
-}
-
-bool isValidCollectionPropertyName(const QString &collectionId)
-{
- static const QmlDesigner::PropertyNameList reservedKeywords = {
- QmlDesigner::CollectionEditorConstants::SOURCEFILE_PROPERTY,
- QmlDesigner::CollectionEditorConstants::JSONBACKEND_TYPENAME,
- "backend",
- "models",
- };
-
- return QmlDesigner::ModelNode::isValidId(collectionId)
- && !reservedKeywords.contains(collectionId.toLatin1());
-}
-
-QMap<QString, QmlDesigner::PropertyName> getModelIdMap(const QmlDesigner::ModelNode &rootNode)
-{
- using namespace QmlDesigner;
- QMap<QString, PropertyName> modelNameForId;
-
- const QList<AbstractProperty> propertyNames = rootNode.dynamicProperties();
-
- for (const AbstractProperty &property : std::as_const(propertyNames)) {
- if (!property.isNodeProperty())
- continue;
-
- NodeProperty nodeProperty = property.toNodeProperty();
- if (!nodeProperty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME))
- continue;
-
- ModelNode childNode = nodeProperty.modelNode();
- if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) {
- QString modelName = childNode
- .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)
- .toVariantProperty()
- .value()
- .toString();
-
- if (!modelName.isEmpty())
- modelNameForId.insert(modelName, property.name());
- }
- }
- return modelNameForId;
-}
-
-void setQmlContextToModel(QmlDesigner::Model *model, const QString &qmlContext)
-{
- using namespace QmlDesigner;
- Q_ASSERT(model);
-
- QScopedPointer<QPlainTextEdit> textEdit(new QPlainTextEdit);
- QScopedPointer<NotIndentingTextEditModifier> modifier(
- new NotIndentingTextEditModifier(textEdit.data()));
- textEdit->hide();
- textEdit->setPlainText(qmlContext);
- QmlDesigner::ExternalDependencies externalDependencies{QmlDesignerBasePlugin::settings()};
- QScopedPointer<RewriterView> rewriter(
- new RewriterView(externalDependencies, QmlDesigner::RewriterView::Validate));
-
- rewriter->setParent(model);
- rewriter->setTextModifier(modifier.get());
- rewriter->setCheckSemanticErrors(false);
-
- model->attachView(rewriter.get());
- model->detachView(rewriter.get());
-}
-
-} // namespace
-
-namespace QmlDesigner {
-
-DataStoreModelNode::DataStoreModelNode()
-{
- reloadModel();
-}
-
-void DataStoreModelNode::reloadModel()
-{
- using Utils::FilePath;
- if (!ProjectExplorer::ProjectManager::startupProject()) {
- reset();
- return;
- }
- bool forceUpdate = false;
-
- const FilePath dataStoreQmlPath = CollectionEditorUtils::dataStoreQmlFilePath();
- const FilePath dataStoreJsonPath = CollectionEditorUtils::dataStoreJsonFilePath();
- QUrl dataStoreQmlUrl = dataStoreQmlPath.toUrl();
-
- if (dataStoreQmlPath.exists() && dataStoreJsonPath.exists()) {
- if (!m_model.get() || m_model->fileUrl() != dataStoreQmlUrl) {
-#ifdef QDS_USE_PROJECTSTORAGE
- m_model = model()->createModel("JsonListModel");
- forceUpdate = true;
- Import import = Import::createLibraryImport("QtQuick.Studio.Utils");
- m_model->changeImports({import}, {});
-#else
- m_model = Model::create(CollectionEditorConstants::JSONCOLLECTIONMODEL_TYPENAME, 1, 1);
- forceUpdate = true;
- Import import = Import::createLibraryImport(
- CollectionEditorConstants::COLLECTIONMODEL_IMPORT);
- try {
- if (!m_model->hasImport(import, true, true))
- m_model->changeImports({import}, {});
- } catch (const Exception &) {
- QTC_ASSERT(false, return);
- }
-#endif
- }
- } else {
- reset();
- }
-
- if (!m_model.get())
- return;
-
- if (forceUpdate) {
- m_model->setFileUrl(dataStoreQmlUrl);
- m_dataRelativePath = dataStoreJsonPath.relativePathFrom(dataStoreQmlPath).toFSPathString();
- preloadFile();
- update();
- }
-}
-
-QStringList DataStoreModelNode::collectionNames() const
-{
- return m_collectionPropertyNames.keys();
-}
-
-Model *DataStoreModelNode::model() const
-{
- return m_model.get();
-}
-
-ModelNode DataStoreModelNode::modelNode() const
-{
- if (!m_model.get())
- return {};
- return m_model->rootModelNode();
-}
-
-QString DataStoreModelNode::getModelQmlText()
-{
- ModelNode node = modelNode();
- QTC_ASSERT(node, return {});
-
- Internal::QmlTextGenerator textGen(createNameList(node),
- QmlJSTools::QmlJSToolsSettings::globalCodeStyle()->tabSettings());
-
- QString genText = textGen(node);
- return genText;
-}
-
-void DataStoreModelNode::reset()
-{
- if (m_model)
- m_model.reset();
-
- m_dataRelativePath.clear();
- setCollectionNames({});
-}
-
-void DataStoreModelNode::preloadFile()
-{
- using Utils::FilePath;
- using Utils::FileReader;
-
- if (!m_model)
- return;
-
- const FilePath dataStoreQmlPath = dataStoreQmlFilePath();
- FileReader dataStoreQmlFile;
- QString sourceQmlContext;
-
- if (dataStoreQmlFile.fetch(dataStoreQmlPath))
- sourceQmlContext = QString::fromLatin1(dataStoreQmlFile.data());
-
- setQmlContextToModel(m_model.get(), sourceQmlContext);
- m_collectionPropertyNames = getModelIdMap(m_model->rootModelNode());
-}
-
-void DataStoreModelNode::updateDataStoreProperties()
-{
- QTC_ASSERT(model(), return);
-
- ModelNode rootNode = modelNode();
- QTC_ASSERT(rootNode.isValid(), return);
-
- QSet<QString> collectionNamesToBeAdded;
- const QStringList allCollectionNames = m_collectionPropertyNames.keys();
- for (const QString &collectionName : allCollectionNames)
- collectionNamesToBeAdded << collectionName;
-
- const QList<AbstractProperty> formerPropertyNames = rootNode.dynamicProperties();
-
- // Remove invalid collection names from the properties
- for (const AbstractProperty &property : formerPropertyNames) {
- if (!property.isNodeProperty())
- continue;
-
- NodeProperty nodeProprty = property.toNodeProperty();
- if (!nodeProprty.hasDynamicTypeName(CHILDLISTMODEL_TYPENAME))
- continue;
-
- ModelNode childNode = nodeProprty.modelNode();
- if (childNode.hasProperty(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)) {
- QString modelName = childNode
- .property(CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY)
- .toVariantProperty()
- .value()
- .toString();
- if (collectionNamesToBeAdded.contains(modelName)) {
- m_collectionPropertyNames.insert(modelName, property.name());
- collectionNamesToBeAdded.remove(modelName);
- } else {
- rootNode.removeProperty(property.name());
- }
- } else {
- rootNode.removeProperty(property.name());
- }
- }
-
- rootNode.setIdWithoutRefactoring("models");
-
- QStringList collectionNamesLeft = collectionNamesToBeAdded.values();
- Utils::sort(collectionNamesLeft);
- for (const QString &collectionName : std::as_const(collectionNamesLeft))
- addCollectionNameToTheModel(collectionName, getUniquePropertyName(collectionName));
-
- // Backend Property
- ModelNode backendNode = model()->createModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME);
- NodeProperty backendProperty = rootNode.nodeProperty("backend");
- backendProperty.setDynamicTypeNameAndsetModelNode(CollectionEditorConstants::JSONBACKEND_TYPENAME,
- backendNode);
- // Source Property
- VariantProperty sourceProp = rootNode.variantProperty(
- CollectionEditorConstants::SOURCEFILE_PROPERTY);
- sourceProp.setValue(m_dataRelativePath);
-}
-
-void DataStoreModelNode::updateSingletonFile()
-{
- using Utils::FilePath;
- using Utils::FileSaver;
- QTC_ASSERT(m_model.get(), return);
-
- const QString pragmaSingleTone = "pragma Singleton\n";
- QString imports;
-
- for (const Import &import : m_model->imports())
- imports += QStringLiteral("import %1\n").arg(import.toString(true));
-
- QString content = pragmaSingleTone + imports + getModelQmlText();
- Core::DocumentManager::expectFileChange(dataStoreQmlFilePath());
- FileSaver file(dataStoreQmlFilePath());
- file.write(content.toLatin1());
- file.finalize();
-}
-
-void DataStoreModelNode::update()
-{
- if (!m_model.get())
- return;
-
- updateDataStoreProperties();
- updateSingletonFile();
-}
-
-void DataStoreModelNode::addCollectionNameToTheModel(const QString &collectionName,
- const PropertyName &dataStorePropertyName)
-{
- ModelNode rootNode = modelNode();
- QTC_ASSERT(rootNode.isValid(), return);
-
- if (dataStorePropertyName.isEmpty()) {
- qWarning() << __FUNCTION__ << __LINE__
- << QString("The property name cannot be generated from \"%1\"").arg(collectionName);
- return;
- }
-
- ModelNode collectionNode = model()->createModelNode(CHILDLISTMODEL_TYPENAME);
- VariantProperty modelNameProperty = collectionNode.variantProperty(
- CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY);
- modelNameProperty.setValue(collectionName);
-
- NodeProperty nodeProp = rootNode.nodeProperty(dataStorePropertyName);
- nodeProp.setDynamicTypeNameAndsetModelNode(CHILDLISTMODEL_TYPENAME, collectionNode);
-
- m_collectionPropertyNames.insert(collectionName, dataStorePropertyName);
-}
-
-Utils::FilePath DataStoreModelNode::dataStoreQmlFilePath() const
-{
- QUrl modelUrl = m_model->fileUrl();
- return Utils::FilePath::fromUserInput(modelUrl.isLocalFile() ? modelUrl.toLocalFile()
- : modelUrl.toString());
-}
-
-PropertyName DataStoreModelNode::getUniquePropertyName(const QString &collectionName)
-{
- ModelNode dataStoreNode = modelNode();
- QTC_ASSERT(!collectionName.isEmpty() && dataStoreNode.isValid(), return {});
-
- QString newProperty;
-
- // convert to camel case
- QStringList nameWords = collectionName.split(' ');
- nameWords[0] = nameWords[0].at(0).toLower() + nameWords[0].mid(1);
- for (int i = 1; i < nameWords.size(); ++i)
- nameWords[i] = nameWords[i].at(0).toUpper() + nameWords[i].mid(1);
- newProperty = nameWords.join("");
-
- // if id starts with a number prepend an underscore
- if (newProperty.at(0).isDigit())
- newProperty.prepend('_');
-
- // If the new id is not valid (e.g. qml keyword match), prepend an underscore
- if (!isValidCollectionPropertyName(newProperty))
- newProperty.prepend('_');
-
- static const QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
- while (dataStoreNode.hasProperty(newProperty.toLatin1())) { // id exists
- QRegularExpressionMatch match = rgx.match(newProperty);
- if (match.hasMatch()) { // ends with a number, increment it
- QString numStr = match.captured();
- int num = numStr.toInt() + 1;
- newProperty = newProperty.mid(0, match.capturedStart()) + QString::number(num);
- } else {
- newProperty.append('1');
- }
- }
-
- return newProperty.toLatin1();
-}
-
-void DataStoreModelNode::setCollectionNames(const QStringList &newCollectionNames)
-{
- m_collectionPropertyNames.clear();
- for (const QString &collectionName : newCollectionNames)
- m_collectionPropertyNames.insert(collectionName, {});
- update();
-}
-
-void DataStoreModelNode::addCollection(const QString &collectionName)
-{
- if (!m_collectionPropertyNames.contains(collectionName)) {
- m_collectionPropertyNames.insert(collectionName, {});
- update();
- }
-}
-
-void DataStoreModelNode::renameCollection(const QString &oldName, const QString &newName)
-{
- ModelNode dataStoreNode = modelNode();
- QTC_ASSERT(dataStoreNode.isValid(), return);
-
- if (m_collectionPropertyNames.contains(oldName)) {
- const PropertyName oldPropertyName = m_collectionPropertyNames.value(oldName);
- if (!oldPropertyName.isEmpty() && dataStoreNode.hasProperty(oldPropertyName)) {
- NodeProperty collectionNode = dataStoreNode.property(oldPropertyName).toNodeProperty();
- if (collectionNode.isValid()) {
- VariantProperty modelNameProperty = collectionNode.modelNode().variantProperty(
- CollectionEditorConstants::JSONCHILDMODELNAME_PROPERTY);
- modelNameProperty.setValue(newName);
- m_collectionPropertyNames.remove(oldName);
- m_collectionPropertyNames.insert(newName, collectionNode.name());
- update();
- return;
- }
- qWarning() << __FUNCTION__ << __LINE__
- << "There is no valid node for the old collection name";
- return;
- }
- qWarning() << __FUNCTION__ << __LINE__ << QString("Invalid old property name")
- << oldPropertyName;
- return;
- }
- qWarning() << __FUNCTION__ << __LINE__
- << QString("There is no old collection name registered with this name \"%1\"").arg(oldName);
-}
-
-void DataStoreModelNode::removeCollections(const QStringList &collectionNames)
-{
- bool updateRequired = false;
- for (const QString &collectionName : collectionNames) {
- if (m_collectionPropertyNames.contains(collectionName)) {
- m_collectionPropertyNames.remove(collectionName);
- updateRequired = true;
- }
- }
-
- if (updateRequired)
- update();
-}
-
-void DataStoreModelNode::assignCollectionToNode(AbstractView *view,
- const ModelNode &targetNode,
- const QString &collectionName,
- CollectionColumnFinder collectionHasColumn,
- FirstColumnProvider firstColumnProvider)
-{
- QTC_ASSERT(targetNode.isValid(), return);
-
- if (!CollectionEditorUtils::canAcceptCollectionAsModel(targetNode))
- return;
-
- if (!m_collectionPropertyNames.contains(collectionName)) {
- qWarning() << __FUNCTION__ << __LINE__ << "Collection doesn't exist in the DataStore"
- << collectionName;
- return;
- }
-
- PropertyName propertyName = m_collectionPropertyNames.value(collectionName);
-
- const ModelNode dataStore = modelNode();
- VariantProperty sourceProperty = dataStore.variantProperty(propertyName);
- if (!sourceProperty.exists()) {
- qWarning() << __FUNCTION__ << __LINE__
- << "The source property doesn't exist in the DataStore.";
- return;
- }
-
- view->executeInTransaction("assignCollectionToNode", [&]() {
- QString identifier = QString("DataStore.%1").arg(QString::fromLatin1(sourceProperty.name()));
-
- // Remove the old model node property if exists
- NodeProperty modelNodeProperty = targetNode.nodeProperty("model");
- if (modelNodeProperty.modelNode())
- modelNodeProperty.modelNode().destroy();
-
- // Assign the collection to the node
- BindingProperty modelProperty = targetNode.bindingProperty("model");
- modelProperty.setExpression(identifier);
-
- if (CollectionEditorUtils::hasTextRoleProperty(targetNode)) {
- VariantProperty textRoleProperty = targetNode.variantProperty("textRole");
- const QVariant currentTextRoleValue = textRoleProperty.value();
-
- if (currentTextRoleValue.isValid() && !currentTextRoleValue.isNull()) {
- if (currentTextRoleValue.type() == QVariant::String) {
- const QString currentTextRole = currentTextRoleValue.toString();
- if (collectionHasColumn(collectionName, currentTextRole))
- return;
- } else {
- return;
- }
- }
-
- QString textRoleValue = firstColumnProvider(collectionName);
- textRoleProperty.setValue(textRoleValue);
- }
- });
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h b/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h
deleted file mode 100644
index 6cd969edbe..0000000000
--- a/src/plugins/qmldesigner/components/collectioneditor/datastoremodelnode.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <modelnode.h>
-
-#include <QMap>
-
-namespace Utils {
-class FilePath;
-}
-
-namespace QmlDesigner {
-
-class Model;
-
-class DataStoreModelNode
-{
-public:
- using CollectionColumnFinder = std::function<bool(const QString &collectionName,
- const QString &columnName)>;
- using FirstColumnProvider = std::function<QString(const QString &collectionName)>;
-
- DataStoreModelNode();
-
- void reloadModel();
- QStringList collectionNames() const;
-
- Model *model() const;
- ModelNode modelNode() const;
-
- void setCollectionNames(const QStringList &newCollectionNames);
- void addCollection(const QString &collectionName);
- void renameCollection(const QString &oldName, const QString &newName);
- void removeCollections(const QStringList &collectionNames);
-
- void assignCollectionToNode(AbstractView *view,
- const ModelNode &targetNode,
- const QString &collectionName,
- CollectionColumnFinder collectionHasColumn,
- FirstColumnProvider firstColumnProvider);
-
-private:
- QString getModelQmlText();
-
- void reset();
- void preloadFile();
- void updateDataStoreProperties();
- void updateSingletonFile();
- void update();
- void addCollectionNameToTheModel(const QString &collectionName,
- const PropertyName &dataStorePropertyName);
- Utils::FilePath dataStoreQmlFilePath() const;
-
- PropertyName getUniquePropertyName(const QString &collectionName);
-
- ModelPointer m_model;
- QMap<QString, PropertyName> m_collectionPropertyNames;
- QString m_dataRelativePath;
-};
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp
index 559e8ea69c..3379d99834 100644
--- a/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.cpp
@@ -8,7 +8,7 @@
namespace QmlDesigner {
AbstractAction::AbstractAction(const QString &description)
- : m_pureAction(new DefaultAction(description))
+ : m_pureAction(std::make_unique<DefaultAction>(description))
{
const Utils::Icon defaultIcon({
{":/utils/images/select.png", Utils::Theme::QmlDesigner_FormEditorForegroundColor}}, Utils::Icon::MenuTintedStyle);
@@ -56,7 +56,7 @@ void AbstractAction::setCheckable(bool checkable)
PureActionInterface *AbstractAction::pureAction() const
{
- return m_pureAction.data();
+ return m_pureAction.get();
}
SelectionContext AbstractAction::selectionContext() const
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractaction.h b/src/plugins/qmldesigner/components/componentcore/abstractaction.h
index ca4cc582ce..53b540cc7a 100644
--- a/src/plugins/qmldesigner/components/componentcore/abstractaction.h
+++ b/src/plugins/qmldesigner/components/componentcore/abstractaction.h
@@ -6,7 +6,8 @@
#include "actioninterface.h"
#include <QAction>
-#include <QScopedPointer>
+
+#include <memory>
namespace QmlDesigner {
@@ -58,7 +59,7 @@ protected:
SelectionContext selectionContext() const;
private:
- QScopedPointer<PureActionInterface> m_pureAction;
+ std::unique_ptr<PureActionInterface> m_pureAction;
SelectionContext m_selectionContext;
};
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp
index 288b8e409d..5b340343e7 100644
--- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.cpp
@@ -8,14 +8,14 @@
namespace QmlDesigner {
-AbstractActionGroup::AbstractActionGroup(const QString &displayName) :
- m_displayName(displayName),
- m_menu(new QmlEditorMenu)
+AbstractActionGroup::AbstractActionGroup(const QString &displayName)
+ : m_displayName(displayName)
+ , m_menu(Utils::makeUniqueObjectPtr<QmlEditorMenu>())
{
m_menu->setTitle(displayName);
m_action = m_menu->menuAction();
- QmlEditorMenu *qmlEditorMenu = qobject_cast<QmlEditorMenu *>(m_menu.data());
+ QmlEditorMenu *qmlEditorMenu = qobject_cast<QmlEditorMenu *>(m_menu.get());
if (qmlEditorMenu)
qmlEditorMenu->setIconsVisible(false);
}
@@ -32,7 +32,7 @@ QAction *AbstractActionGroup::action() const
QMenu *AbstractActionGroup::menu() const
{
- return m_menu.data();
+ return m_menu.get();
}
SelectionContext AbstractActionGroup::selectionContext() const
diff --git a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h
index dd89849ecf..f239eeab3d 100644
--- a/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h
+++ b/src/plugins/qmldesigner/components/componentcore/abstractactiongroup.h
@@ -5,9 +5,10 @@
#include "actioninterface.h"
+#include <utils/uniqueobjectptr.h>
+
#include <QAction>
#include <QMenu>
-#include <QScopedPointer>
namespace QmlDesigner {
@@ -29,7 +30,7 @@ public:
private:
const QString m_displayName;
SelectionContext m_selectionContext;
- QScopedPointer<QMenu> m_menu;
+ Utils::UniqueObjectPtr<QMenu> m_menu;
QAction *m_action;
};
diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
index d992a6a5bf..da7c5bf72e 100644
--- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
+++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h
@@ -69,7 +69,6 @@ const char mergeTemplateCommandId[] = "MergeTemplate";
const char goToImplementationCommandId[] = "GoToImplementation";
const char makeComponentCommandId[] = "MakeComponent";
const char editMaterialCommandId[] = "EditMaterial";
-const char editCollectionCommandId[] = "EditCollection";
const char addItemToStackedContainerCommandId[] = "AddItemToStackedContainer";
const char addTabBarToStackedContainerCommandId[] = "AddTabBarToStackedContainer";
const char increaseIndexOfStackedContainerCommandId[] = "IncreaseIndexOfStackedContainer";
@@ -128,7 +127,6 @@ const char mergeTemplateDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMen
const char goToImplementationDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Go to Implementation");
const char makeComponentDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Create Component");
const char editMaterialDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Material");
-const char editCollectionDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Model");
const char editAnnotationsDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit Annotations");
const char addMouseAreaFillDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Add Mouse Area");
const char editIn3dViewDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Edit in 3D View");
@@ -214,7 +212,6 @@ enum PrioritiesEnum : int {
ArrangeCategory,
EditCategory,
EditListModel,
- EditCollection,
/******** Section *****************************/
PositionSection = 2000,
SnappingCategory,
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
index 4e32237ee9..bbe64935f6 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
@@ -61,11 +61,6 @@ inline static QString captionForModelNode(const ModelNode &modelNode)
return modelNode.id();
}
-inline static bool contains(const QmlItemNode &node, const QPointF &position)
-{
- return node.isValid() && node.instanceSceneTransform().mapRect(node.instanceBoundingRect()).contains(position);
-}
-
DesignerActionManagerView *DesignerActionManager::view()
{
return m_designerActionManagerView;
@@ -118,7 +113,6 @@ void DesignerActionManager::polishActions() const
Core::Context qmlDesignerNavigatorContext(Constants::C_QMLNAVIGATOR);
Core::Context qmlDesignerMaterialBrowserContext(Constants::C_QMLMATERIALBROWSER);
Core::Context qmlDesignerAssetsLibraryContext(Constants::C_QMLASSETSLIBRARY);
- Core::Context qmlDesignerCollectionEditorContext(Constants::C_QMLCOLLECTIONEDITOR);
Core::Context qmlDesignerUIContext;
qmlDesignerUIContext.add(qmlDesignerFormEditorContext);
@@ -126,7 +120,6 @@ void DesignerActionManager::polishActions() const
qmlDesignerUIContext.add(qmlDesignerNavigatorContext);
qmlDesignerUIContext.add(qmlDesignerMaterialBrowserContext);
qmlDesignerUIContext.add(qmlDesignerAssetsLibraryContext);
- qmlDesignerUIContext.add(qmlDesignerCollectionEditorContext);
for (auto *action : actions) {
if (!action->menuId().isEmpty()) {
@@ -438,8 +431,8 @@ public:
}
for (const ModelNode &node : selectionContext().view()->allModelNodes()) {
if (node != selectionContext().currentSingleSelectedNode() && node != parentNode
- && contains(node, selectionContext().scenePosition()) && !node.isRootNode()
- && !ModelUtils::isThisOrAncestorLocked(node)) {
+ && SelectionContextHelpers::contains(node, selectionContext().scenePosition())
+ && !node.isRootNode() && !ModelUtils::isThisOrAncestorLocked(node)) {
selectionContext().setTargetNode(node);
QString what = QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select: %1")).arg(captionForModelNode(node));
ActionTemplate *selectionAction = new ActionTemplate("SELECT", what, &ModelNodeOperations::select);
@@ -1971,7 +1964,7 @@ void DesignerActionManager::createDefaultDesignerActions()
QKeySequence(),
Priorities::ComponentActions + 1,
&editIn3dView,
- &singleSelectionView3D,
+ &SelectionContextFunctors::always, // If action is visible, it is usable
&singleSelectionView3D));
addDesignerAction(new ModelNodeContextMenuAction(
@@ -1993,8 +1986,8 @@ void DesignerActionManager::createDefaultDesignerActions()
QKeySequence(),
44,
&editMaterial,
- &modelHasMaterial,
- &isModel));
+ &hasEditableMaterial,
+ &isModelOrMaterial));
addDesignerAction(new ModelNodeContextMenuAction(
mergeTemplateCommandId,
@@ -2016,16 +2009,6 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new EditListModelAction);
- addDesignerAction(new ModelNodeContextMenuAction(editCollectionCommandId,
- editCollectionDisplayName,
- contextIcon(DesignerIcons::EditIcon),
- rootCategory,
- QKeySequence("Alt+e"),
- ComponentCoreConstants::Priorities::EditCollection,
- &editCollection,
- &hasCollectionAsModel,
- &hasCollectionAsModel));
-
addDesignerAction(new ModelNodeContextMenuAction(openSignalDialogCommandId,
openSignalDialogDisplayName,
{},
@@ -2198,7 +2181,8 @@ void DesignerActionManager::addCustomTransitionEffectAction()
void DesignerActionManager::setupIcons()
{
- m_designerIcons.reset(new DesignerIcons("qtds_propertyIconFont.ttf", designerIconResourcesPath()));
+ m_designerIcons = std::make_unique<DesignerIcons>("qtds_propertyIconFont.ttf",
+ designerIconResourcesPath());
}
QString DesignerActionManager::designerIconResourcesPath() const
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
index 16d6219cd6..89505fcbe8 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.h
@@ -138,7 +138,7 @@ private:
QList<AddResourceHandler> m_addResourceHandler;
QList<ModelNodePreviewImageHandler> m_modelNodePreviewImageHandlers;
ExternalDependenciesInterface &m_externalDependencies;
- QScopedPointer<DesignerIcons> m_designerIcons;
+ std::unique_ptr<DesignerIcons> m_designerIcons;
QList<ActionAddedInterface> m_callBacks;
};
diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp
new file mode 100644
index 0000000000..f882ae528d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/dialogutils.cpp
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <model.h>
+
+#include <coreplugin/messagebox.h>
+
+namespace QmlDesigner {
+
+namespace DialogUtils {
+
+void showWarningForInvalidId(const QString &id)
+{
+ constexpr char text[] = R"(
+The ID <b>'%1'</b> is invalid.
+
+Make sure the ID is:
+<ul>
+<li>Unique within the QML file.</li>
+<li>Beginning with a lowercase letter.</li>
+<li>Without any blank space or symbol.</li>
+<li>Not a reserved QML keyword. </li>
+</ul>
+)";
+
+ Core::AsynchronousMessageBox::warning(Model::tr("Invalid Id"),
+ Model::tr(text).arg(id));
+}
+
+} // namespace DialogUtils
+
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/dialogutils.h b/src/plugins/qmldesigner/components/componentcore/dialogutils.h
new file mode 100644
index 0000000000..3ca98016dd
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/dialogutils.h
@@ -0,0 +1,17 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <qmldesignercomponents_global.h>
+
+#include <QString>
+
+namespace QmlDesigner {
+
+namespace DialogUtils {
+
+QMLDESIGNERCOMPONENTS_EXPORT void showWarningForInvalidId(const QString &id);
+
+} // namespace DialogUtils
+} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
index 8d3412e0e8..89b50c4d1a 100644
--- a/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/layoutingridlayout.cpp
@@ -452,7 +452,9 @@ void LayoutInGridLayout::removeSpacersBySpanning(QList<ModelNode> &nodes)
{
for (const ModelNode &node : std::as_const(m_spacerNodes)) {
if (int index = nodes.indexOf(node)) {
- ModelNode before = nodes.at(index -1);
+ ModelNode before;
+ if (index > 0)
+ before = nodes.at(index - 1);
if (m_spacerNodes.contains(before)) {
m_spacerNodes.removeAll(node);
m_layoutedNodes.removeAll(node);
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp
index f6e18458b2..4cbebd738d 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.cpp
@@ -105,8 +105,10 @@ bool selectionIsImported3DAsset(const SelectionContext &selectionState)
// Node is not a file component, so we have to check if the current doc itself is
fileName = node.model()->fileUrl().toLocalFile();
}
- if (fileName.contains(Constants::QUICK_3D_ASSETS_FOLDER))
+ if (QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().isImport3dPath(fileName)) {
return true;
+ }
}
return false;
}
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
index eb4915b1d0..6734bac568 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodecontextmenu_helper.h
@@ -25,6 +25,16 @@ namespace QmlDesigner {
using SelectionContextPredicate = std::function<bool (const SelectionContext&)>;
using SelectionContextOperation = std::function<void (const SelectionContext&)>;
+namespace SelectionContextHelpers {
+
+inline bool contains(const QmlItemNode &node, const QPointF &position)
+{
+ return node.isValid()
+ && node.instanceSceneTransform().mapRect(node.instanceBoundingRect()).contains(position);
+}
+
+} // namespace SelectionContextHelpers
+
namespace SelectionContextFunctors {
inline bool always(const SelectionContext &)
@@ -54,33 +64,24 @@ inline bool addMouseAreaFillCheck(const SelectionContext &selectionContext)
return false;
}
-inline bool isModel(const SelectionContext &selectionState)
+inline bool isModelOrMaterial(const SelectionContext &selectionState)
{
ModelNode node = selectionState.currentSingleSelectedNode();
- return node.metaInfo().isQtQuick3DModel();
+ return node.metaInfo().isQtQuick3DModel() || node.metaInfo().isQtQuick3DMaterial();
}
-inline bool modelHasMaterial(const SelectionContext &selectionState)
+inline bool hasEditableMaterial(const SelectionContext &selectionState)
{
ModelNode node = selectionState.currentSingleSelectedNode();
+ if (node.metaInfo().isQtQuick3DMaterial())
+ return true;
+
BindingProperty prop = node.bindingProperty("materials");
return prop.exists() && (!prop.expression().isEmpty() || !prop.resolveToModelNodeList().empty());
}
-inline bool hasCollectionAsModel(const SelectionContext &selectionState)
-{
- if (!selectionState.isInBaseState() || !selectionState.singleNodeIsSelected())
- return false;
-
- const ModelNode singleSelectedNode = selectionState.currentSingleSelectedNode();
-
- return singleSelectedNode.metaInfo().isQtQuickListView()
- && singleSelectedNode.property("model").toBindingProperty().expression().startsWith(
- "DataStore.");
-}
-
inline bool selectionEnabled(const SelectionContext &selectionState)
{
return selectionState.showSelectionTools();
@@ -99,8 +100,22 @@ inline bool singleSelectionNotRoot(const SelectionContext &selectionState)
inline bool singleSelectionView3D(const SelectionContext &selectionState)
{
- return selectionState.singleNodeIsSelected()
- && selectionState.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D();
+ if (selectionState.singleNodeIsSelected()
+ && selectionState.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D()) {
+ return true;
+ }
+
+ // If currently selected node is not View3D, check if there is a View3D under the cursor.
+ if (!selectionState.scenePosition().isNull()) {
+ // Assumption is that last match in allModelNodes() list is the topmost one.
+ const QList<ModelNode> allNodes = selectionState.view()->allModelNodes();
+ for (int i = allNodes.size() - 1; i >= 0; --i) {
+ if (SelectionContextHelpers::contains(allNodes[i], selectionState.scenePosition()))
+ return allNodes[i].metaInfo().isQtQuick3DView3D();
+ }
+ }
+
+ return false;
}
inline bool selectionHasProperty(const SelectionContext &selectionState, const char *property)
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
index cebe7d7c53..bf8e78a2c7 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp
@@ -817,21 +817,25 @@ void editMaterial(const SelectionContext &selectionContext)
QTC_ASSERT(modelNode.isValid(), return);
- BindingProperty prop = modelNode.bindingProperty("materials");
- if (!prop.exists())
- return;
-
AbstractView *view = selectionContext.view();
ModelNode material;
- if (view->hasId(prop.expression())) {
- material = view->modelNodeForId(prop.expression());
+ if (modelNode.metaInfo().isQtQuick3DMaterial()) {
+ material = modelNode;
} else {
- QList<ModelNode> materials = prop.resolveToModelNodeList();
+ BindingProperty prop = modelNode.bindingProperty("materials");
+ if (!prop.exists())
+ return;
+
+ if (view->hasId(prop.expression())) {
+ material = view->modelNodeForId(prop.expression());
+ } else {
+ QList<ModelNode> materials = prop.resolveToModelNodeList();
- if (materials.size() > 0)
- material = materials.first();
+ if (materials.size() > 0)
+ material = materials.first();
+ }
}
if (material.isValid()) {
@@ -842,30 +846,6 @@ void editMaterial(const SelectionContext &selectionContext)
}
}
-// Open a collection in the collection editor
-void editCollection(const SelectionContext &selectionContext)
-{
- ModelNode modelNode = selectionContext.targetNode();
-
- if (!modelNode)
- modelNode = selectionContext.currentSingleSelectedNode();
-
- if (!modelNode)
- return;
-
- const QString dataStoreExpression = "DataStore.";
-
- BindingProperty prop = modelNode.bindingProperty("model");
- if (!prop.exists() || !prop.expression().startsWith(dataStoreExpression))
- return;
-
- AbstractView *view = selectionContext.view();
- const QString collectionId = prop.expression().mid(dataStoreExpression.size());
-
- // to CollectionEditor...
- view->emitCustomNotification("open_collection_by_id", {}, {collectionId});
-}
-
void addItemToStackedContainer(const SelectionContext &selectionContext)
{
AbstractView *view = selectionContext.view();
@@ -1138,18 +1118,12 @@ static QString getAssetDefaultDirectory(const QString &assetDir, const QString &
{
QString adjustedDefaultDirectory = defaultDirectory;
- Utils::FilePath contentPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
-
- if (contentPath.pathAppended("content").exists())
- contentPath = contentPath.pathAppended("content");
+ Utils::FilePath contentPath = QmlDesignerPlugin::instance()->documentManager().currentResourcePath();
Utils::FilePath assetPath = contentPath.pathAppended(assetDir);
- if (!assetPath.exists()) {
- // Create the default asset type directory if it doesn't exist
- QDir dir(contentPath.toString());
- dir.mkpath(assetDir);
- }
+ if (!assetPath.exists())
+ assetPath.createDir();
if (assetPath.exists() && assetPath.isDir())
adjustedDefaultDirectory = assetPath.toString();
@@ -1691,16 +1665,44 @@ void updateImported3DAsset(const SelectionContext &selectionContext)
void editIn3dView(const SelectionContext &selectionContext)
{
- if (selectionContext.view() && selectionContext.hasSingleSelectedModelNode()
+ if (!selectionContext.view())
+ return;
+
+ ModelNode targetNode;
+
+ if (selectionContext.hasSingleSelectedModelNode()
&& selectionContext.currentSingleSelectedNode().metaInfo().isQtQuick3DView3D()) {
+ targetNode = selectionContext.currentSingleSelectedNode();
+ }
+
+ const QPointF scenePos = selectionContext.scenePosition();
+ if (!targetNode.isValid() && !scenePos.isNull()) {
+ // If currently selected node is not View3D, check if there is a View3D under the cursor.
+ // Assumption is that last match in allModelNodes() list is the topmost one.
+ const QList<ModelNode> allNodes = selectionContext.view()->allModelNodes();
+ for (int i = allNodes.size() - 1; i >= 0; --i) {
+ if (SelectionContextHelpers::contains(allNodes[i], selectionContext.scenePosition())) {
+ if (allNodes[i].metaInfo().isQtQuick3DView3D())
+ targetNode = allNodes[i];
+ break;
+ }
+ }
+ }
+
+ if (targetNode.isValid()) {
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("Editor3D", true);
- selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true);
+ if (scenePos.isNull()) {
+ selectionContext.view()->emitView3DAction(View3DActionType::AlignViewToCamera, true);
+ } else {
+ selectionContext.view()->emitCustomNotification("pick_3d_node_from_2d_scene",
+ {targetNode}, {scenePos});
+ }
}
}
bool isEffectComposerActivated()
{
- const QVector<ExtensionSystem::PluginSpec *> specs = ExtensionSystem::PluginManager::plugins();
+ const ExtensionSystem::PluginSpecs specs = ExtensionSystem::PluginManager::plugins();
return std::find_if(specs.begin(), specs.end(),
[](ExtensionSystem::PluginSpec *spec) {
return spec->name() == "EffectComposer" && spec->isEffectivelyEnabled();
@@ -1727,13 +1729,12 @@ void openOldEffectMaker(const QString &filePath)
return;
}
- Utils::FilePath projectPath = target->project()->projectDirectory();
- QString effectName = QFileInfo(filePath).baseName();
- QString effectResDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER)
- + "/" + effectName;
- Utils::FilePath effectResPath = projectPath.pathAppended(effectResDir);
+ Utils::FilePath effectResPath = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().composedEffectsBasePath()
+ .pathAppended(QFileInfo(filePath).baseName());
+
if (!effectResPath.exists())
- QDir().mkpath(effectResPath.toString());
+ effectResPath.createDir();
const QtSupport::QtVersion *baseQtVersion = QtSupport::QtKitAspect::qtVersion(target->kit());
if (baseQtVersion) {
@@ -1769,14 +1770,11 @@ void openOldEffectMaker(const QString &filePath)
Utils::FilePath getEffectsImportDirectory()
{
- QString defaultDir = QLatin1String(Constants::DEFAULT_EFFECTS_IMPORT_FOLDER);
- Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
- Utils::FilePath effectsPath = projectPath.pathAppended(defaultDir);
+ Utils::FilePath effectsPath = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().composedEffectsBasePath();
- if (!effectsPath.exists()) {
- QDir dir(projectPath.toString());
- dir.mkpath(effectsPath.toString());
- }
+ if (!effectsPath.exists())
+ effectsPath.createDir();
return effectsPath;
}
@@ -1794,12 +1792,9 @@ QString getEffectsDefaultDirectory(const QString &defaultDir)
QString getEffectIcon(const QString &effectPath)
{
- Utils::FilePath projectPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
- QString effectName = QFileInfo(effectPath).baseName();
- QString effectResDir = "asset_imports/Effects/" + effectName;
- Utils::FilePath effectResPath = projectPath.resolvePath(effectResDir + "/" + effectName + ".qml");
-
- return effectResPath.exists() ? QString("effectExported") : QString("effectClass");
+ Utils::FilePath effectFile = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().composedEffectPath(effectPath);
+ return effectFile.exists() ? QString("effectExported") : QString("effectClass");
}
bool useLayerEffect()
diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
index a67cef4942..26562f429a 100644
--- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
+++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h
@@ -92,7 +92,6 @@ void layoutGridLayout(const SelectionContext &selectionState);
void goImplementation(const SelectionContext &selectionState);
void addNewSignalHandler(const SelectionContext &selectionState);
void editMaterial(const SelectionContext &selectionContext);
-void editCollection(const SelectionContext &selectionContext);
void addSignalHandlerOrGotoImplementation(const SelectionContext &selectionState, bool addAlwaysNewSlot);
void removeLayout(const SelectionContext &selectionContext);
void removePositioner(const SelectionContext &selectionContext);
diff --git a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp
index 8cc84058d2..58326dc77a 100644
--- a/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/propertycomponentgenerator.cpp
@@ -37,7 +37,7 @@ Type getProperty(const QmlJS::SimpleReaderNode *node, const QString &name)
{
if (auto property = node->property(name)) {
const auto &value = property.value;
- if (value.type() == QVariant::List) {
+ if (value.typeId() == QMetaType::QVariantList) {
auto list = value.toList();
if (list.size())
return list.front().value<Type>();
@@ -179,7 +179,7 @@ std::optional<PropertyComponentGenerator::Entry> createEntry(QmlJS::SimpleReader
if (moduleName.isEmpty())
return {};
- auto module = model->module(moduleName);
+ auto module = model->module(moduleName, Storage::ModuleKind::QmlLibrary);
auto typeName = getProperty<QByteArray>(node, "typeNames");
diff --git a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp
index 4a229564c6..24047f650f 100644
--- a/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/resourcegenerator.cpp
@@ -222,6 +222,10 @@ bool createQmlrcFile(const FilePath &qmlrcFilePath)
rccProcess.setWorkingDirectory(project->projectDirectory());
const QStringList arguments = {"--binary",
+ "--compress",
+ "9",
+ "--threshold",
+ "30",
"--output",
qmlrcFilePath.toString(),
tempQrcFile.toString()};
diff --git a/src/plugins/qmldesigner/components/componentcore/theme.cpp b/src/plugins/qmldesigner/components/componentcore/theme.cpp
index af495fd3b5..ef618447e7 100644
--- a/src/plugins/qmldesigner/components/componentcore/theme.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/theme.cpp
@@ -10,6 +10,7 @@
#include <utils/stylehelper.h>
+#include <qqml.h>
#include <QApplication>
#include <QMainWindow>
#include <QPointer>
@@ -18,7 +19,7 @@
#include <QQmlProperty>
#include <QRegularExpression>
#include <QScreen>
-#include <qqml.h>
+#include <QWindow>
static Q_LOGGING_CATEGORY(themeLog, "qtc.qmldesigner.theme", QtWarningMsg)
@@ -140,7 +141,9 @@ bool Theme::highPixelDensity() const
QWindow *Theme::mainWindowHandle() const
{
- return Core::ICore::mainWindow()->windowHandle();
+ QWindow *handle = Core::ICore::mainWindow()->windowHandle();
+ QQmlEngine::setObjectOwnership(handle, QJSEngine::CppOwnership);
+ return handle;
}
QPixmap Theme::getPixmap(const QString &id)
diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h
index 73184d391c..392f6c94f6 100644
--- a/src/plugins/qmldesigner/components/componentcore/theme.h
+++ b/src/plugins/qmldesigner/components/componentcore/theme.h
@@ -83,6 +83,7 @@ public:
binding_medium,
bounds_small,
branch_medium,
+ cameraSpeed_medium,
camera_medium,
camera_small,
centerHorizontal,
diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
index 05d6f5fdf0..a56735862f 100644
--- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp
@@ -7,7 +7,6 @@
#include <abstractview.h>
#include <assetslibraryview.h>
#include <capturingconnectionmanager.h>
-#include <collectionview.h>
#include <componentaction.h>
#include <componentview.h>
#include <contentlibraryview.h>
@@ -42,14 +41,6 @@
namespace QmlDesigner {
-static bool enableModelEditor()
-{
- Utils::QtcSettings *settings = Core::ICore::settings();
- const Utils::Key enableModelManagerKey = "QML/Designer/UseExperimentalFeatures44";
-
- return settings->value(enableModelManagerKey, false).toBool();
-}
-
static Q_LOGGING_CATEGORY(viewBenchmark, "qtc.viewmanager.attach", QtWarningMsg)
class ViewManagerData
@@ -64,19 +55,22 @@ public:
: connectionManager,
externalDependencies,
true)
- , collectionView{externalDependencies}
- , contentLibraryView{externalDependencies}
+ , contentLibraryView{imageCache, externalDependencies}
, componentView{externalDependencies}
+#ifndef QTC_USE_QML_DESIGNER_LITE
, edit3DView{externalDependencies}
+#endif
, formEditorView{externalDependencies}
, textEditorView{externalDependencies}
, assetsLibraryView{externalDependencies}
, itemLibraryView(imageCache, externalDependencies)
, navigatorView{externalDependencies}
, propertyEditorView(imageCache, externalDependencies)
+#ifndef QTC_USE_QML_DESIGNER_LITE
, materialEditorView{externalDependencies}
, materialBrowserView{imageCache, externalDependencies}
, textureEditorView{imageCache, externalDependencies}
+#endif
, statesEditorView{externalDependencies}
{}
@@ -86,19 +80,22 @@ public:
Internal::DebugView debugView;
DesignerActionManagerView designerActionManagerView;
NodeInstanceView nodeInstanceView;
- CollectionView collectionView;
ContentLibraryView contentLibraryView;
ComponentView componentView;
+#ifndef QTC_USE_QML_DESIGNER_LITE
Edit3DView edit3DView;
+#endif
FormEditorView formEditorView;
TextEditorView textEditorView;
AssetsLibraryView assetsLibraryView;
ItemLibraryView itemLibraryView;
NavigatorView navigatorView;
PropertyEditorView propertyEditorView;
+#ifndef QTC_USE_QML_DESIGNER_LITE
MaterialEditorView materialEditorView;
MaterialBrowserView materialBrowserView;
TextureEditorView textureEditorView;
+#endif
StatesEditorView statesEditorView;
std::vector<std::unique_ptr<AbstractView>> additionalViews;
@@ -203,6 +200,7 @@ QList<AbstractView *> ViewManager::views() const
QList<AbstractView *> ViewManager::standardViews() const
{
+#ifndef QTC_USE_QML_DESIGNER_LITE
QList<AbstractView *> list = {&d->edit3DView,
&d->formEditorView,
&d->textEditorView,
@@ -215,9 +213,16 @@ QList<AbstractView *> ViewManager::standardViews() const
&d->textureEditorView,
&d->statesEditorView,
&d->designerActionManagerView};
-
- if (enableModelEditor())
- list.append(&d->collectionView);
+#else
+ QList<AbstractView *> list = {&d->formEditorView,
+ &d->textEditorView,
+ &d->assetsLibraryView,
+ &d->itemLibraryView,
+ &d->navigatorView,
+ &d->propertyEditorView,
+ &d->statesEditorView,
+ &d->designerActionManagerView};
+#endif
if (QmlDesignerPlugin::instance()
->settings()
@@ -384,19 +389,21 @@ QList<WidgetInfo> ViewManager::widgetInfos() const
{
QList<WidgetInfo> widgetInfoList;
+#ifndef QTC_USE_QML_DESIGNER_LITE
widgetInfoList.append(d->edit3DView.widgetInfo());
+#endif
widgetInfoList.append(d->formEditorView.widgetInfo());
widgetInfoList.append(d->textEditorView.widgetInfo());
widgetInfoList.append(d->assetsLibraryView.widgetInfo());
widgetInfoList.append(d->itemLibraryView.widgetInfo());
widgetInfoList.append(d->navigatorView.widgetInfo());
widgetInfoList.append(d->propertyEditorView.widgetInfo());
+#ifndef QTC_USE_QML_DESIGNER_LITE
widgetInfoList.append(d->materialEditorView.widgetInfo());
widgetInfoList.append(d->materialBrowserView.widgetInfo());
widgetInfoList.append(d->textureEditorView.widgetInfo());
+#endif
widgetInfoList.append(d->statesEditorView.widgetInfo());
- if (enableModelEditor())
- widgetInfoList.append(d->collectionView.widgetInfo());
if (checkEnterpriseLicense())
widgetInfoList.append(d->contentLibraryView.widgetInfo());
diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
index f871bae84b..2cee7b0f97 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.cpp
@@ -22,6 +22,7 @@ namespace QmlDesigner {
BindingModel::BindingModel(ConnectionView *view)
: m_connectionView(view)
+ , m_delegate(*this)
{
setHorizontalHeaderLabels(BindingModelItem::headerLabels());
}
@@ -246,11 +247,8 @@ void BindingModel::addModelNode(const ModelNode &node)
appendRow(new BindingModelItem(property));
}
-BindingModelBackendDelegate::BindingModelBackendDelegate()
- : m_targetNode()
- , m_property()
- , m_sourceNode()
- , m_sourceNodeProperty()
+BindingModelBackendDelegate::BindingModelBackendDelegate(BindingModel &model)
+ : m_model{model}
{
connect(&m_sourceNode, &StudioQmlComboBoxBackend::activated, this, [this] {
sourceNodeChanged();
@@ -322,17 +320,14 @@ StudioQmlComboBoxBackend *BindingModelBackendDelegate::sourceProperty()
void BindingModelBackendDelegate::sourceNodeChanged()
{
- BindingModel *model = qobject_cast<BindingModel *>(parent());
- QTC_ASSERT(model, return);
-
- ConnectionView *view = model->connectionView();
+ ConnectionView *view = m_model.connectionView();
QTC_ASSERT(view, return);
QTC_ASSERT(view->isAttached(), return );
const QString sourceNode = m_sourceNode.currentText();
const QString sourceProperty = m_sourceNodeProperty.currentText();
- BindingProperty targetProperty = model->currentProperty();
+ BindingProperty targetProperty = m_model.currentProperty();
QStringList properties = availableSourceProperties(sourceNode, targetProperty, view);
if (!properties.contains(sourceProperty)) {
@@ -351,9 +346,6 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const
return;
auto commit = [this, sourceProperty]() {
- BindingModel *model = qobject_cast<BindingModel *>(parent());
- QTC_ASSERT(model, return);
-
const QString sourceNode = m_sourceNode.currentText();
QString expression;
if (sourceProperty.isEmpty())
@@ -361,8 +353,8 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const
else
expression = sourceNode + QLatin1String(".") + sourceProperty;
- int row = model->currentIndex();
- model->commitExpression(row, expression);
+ int row = m_model.currentIndex();
+ m_model.commitExpression(row, expression);
};
callLater(commit);
@@ -371,11 +363,9 @@ void BindingModelBackendDelegate::sourcePropertyNameChanged() const
void BindingModelBackendDelegate::targetPropertyNameChanged() const
{
auto commit = [this] {
- BindingModel *model = qobject_cast<BindingModel *>(parent());
- QTC_ASSERT(model, return);
const PropertyName propertyName = m_property.currentText().toUtf8();
- int row = model->currentIndex();
- model->commitPropertyName(row, propertyName);
+ int row = m_model.currentIndex();
+ m_model.commitPropertyName(row, propertyName);
};
callLater(commit);
diff --git a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
index b57cc5c958..69b137e78a 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/bindingmodel.h
@@ -30,7 +30,7 @@ signals:
void targetNodeChanged();
public:
- BindingModelBackendDelegate();
+ BindingModelBackendDelegate(class BindingModel &model);
void update(const BindingProperty &property, AbstractView *view);
@@ -44,6 +44,7 @@ private:
StudioQmlComboBoxBackend *sourceNode();
StudioQmlComboBoxBackend *sourceProperty();
+ BindingModel &m_model;
QString m_targetNode;
StudioQmlComboBoxBackend m_property;
StudioQmlComboBoxBackend m_sourceNode;
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp
index 57ca619a70..3cbfb8c038 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectioneditorutils.cpp
@@ -212,7 +212,8 @@ bool isDynamicVariantPropertyType(const TypeName &type)
{
// "variant" is considered value type as it is initialized as one.
// This may need to change if we provide any kind of proper editor for it.
- static const QSet<TypeName> valueTypes{"int", "real", "color", "string", "bool", "url", "var", "variant"};
+ static const QSet<TypeName> valueTypes{
+ "int", "real", "double", "color", "string", "bool", "url", "var", "variant"};
return valueTypes.contains(type);
}
diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
index 9fdd3daec3..fa29c6c8a1 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp
@@ -25,7 +25,7 @@ namespace QmlDesigner {
DynamicPropertiesModel::DynamicPropertiesModel(bool exSelection, AbstractView *view)
: m_view(view)
- , m_delegate(std::make_unique<DynamicPropertiesModelBackendDelegate>())
+ , m_delegate(std::make_unique<DynamicPropertiesModelBackendDelegate>(*this))
, m_explicitSelection(exSelection)
{
setHorizontalHeaderLabels(DynamicPropertiesItem::headerLabels());
@@ -382,8 +382,8 @@ void DynamicPropertiesModel::setSelectedNode(const ModelNode &node)
reset();
}
-DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate()
- : m_internalNodeId(std::nullopt)
+DynamicPropertiesModelBackendDelegate::DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel &model)
+ : m_model(model)
{
m_type.setModel({"int", "bool", "var", "real", "string", "url", "color"});
connect(&m_type, &StudioQmlComboBoxBackend::activated, this, [this] { handleTypeChanged(); });
@@ -411,32 +411,26 @@ void DynamicPropertiesModelBackendDelegate::update(const AbstractProperty &prope
void DynamicPropertiesModelBackendDelegate::handleTypeChanged()
{
- DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
- QTC_ASSERT(model, return);
-
const PropertyName name = m_name.text().toUtf8();
- int current = model->currentIndex();
+ int current = m_model.currentIndex();
const TypeName type = m_type.currentText().toUtf8();
- model->commitPropertyType(current, type);
+ m_model.commitPropertyType(current, type);
// The order might have changed!
- model->setCurrent(m_internalNodeId.value_or(-1), name);
+ m_model.setCurrent(m_internalNodeId.value_or(-1), name);
}
void DynamicPropertiesModelBackendDelegate::handleNameChanged()
{
- DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
- QTC_ASSERT(model, return);
-
const PropertyName name = m_name.text().toUtf8();
QTC_ASSERT(!name.isEmpty(), return);
- int current = model->currentIndex();
- model->commitPropertyName(current, name);
+ int current = m_model.currentIndex();
+ m_model.commitPropertyName(current, name);
// The order might have changed!
- model->setCurrent(m_internalNodeId.value_or(-1), name);
+ m_model.setCurrent(m_internalNodeId.value_or(-1), name);
}
// TODO: Maybe replace with utils typeConvertVariant?
@@ -456,12 +450,9 @@ QVariant valueFromText(const QString &value, const QString &type)
void DynamicPropertiesModelBackendDelegate::handleValueChanged()
{
- DynamicPropertiesModel *model = qobject_cast<DynamicPropertiesModel *>(parent());
- QTC_ASSERT(model, return);
-
- int current = model->currentIndex();
+ int current = m_model.currentIndex();
QVariant value = valueFromText(m_value.text(), m_type.currentText());
- model->commitPropertyValue(current, value);
+ m_model.commitPropertyValue(current, value);
}
QString DynamicPropertiesModelBackendDelegate::targetNode() const
diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
index c2beed8730..071c72bef8 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.h
@@ -97,7 +97,7 @@ class DynamicPropertiesModelBackendDelegate : public QObject
Q_PROPERTY(StudioQmlTextBackend *value READ value CONSTANT)
public:
- DynamicPropertiesModelBackendDelegate();
+ DynamicPropertiesModelBackendDelegate(DynamicPropertiesModel &model);
void update(const AbstractProperty &property);
@@ -116,6 +116,7 @@ private:
StudioQmlTextBackend *value();
QString targetNode() const;
+ DynamicPropertiesModel &m_model;
std::optional<int> m_internalNodeId;
StudioQmlComboBoxBackend m_type;
StudioQmlTextBackend m_name;
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp
index 9e6bdd03b9..6388c93fa9 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.cpp
@@ -3,83 +3,67 @@
#include "contentlibrarybundleimporter.h"
-#include "documentmanager.h"
-#include "import.h"
-#include "model.h"
-#include "qmldesignerconstants.h"
-#include "qmldesignerplugin.h"
-#include "rewritingexception.h"
+#include <documentmanager.h>
+#include <import.h>
+#include <model.h>
+#include <nodemetainfo.h>
+#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
+#include <rewritingexception.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
-#include <QFileInfo>
#include <QJsonDocument>
#include <QJsonObject>
#include <QStringList>
using namespace Utils;
-namespace QmlDesigner::Internal {
+namespace QmlDesigner {
-ContentLibraryBundleImporter::ContentLibraryBundleImporter(const QString &bundleDir,
- const QString &bundleId,
- const QStringList &sharedFiles,
- QObject *parent)
+ContentLibraryBundleImporter::ContentLibraryBundleImporter(QObject *parent)
: QObject(parent)
- , m_bundleDir(FilePath::fromString(bundleDir))
- , m_bundleId(bundleId)
- , m_sharedFiles(sharedFiles)
{
m_importTimer.setInterval(200);
connect(&m_importTimer, &QTimer::timeout, this, &ContentLibraryBundleImporter::handleImportTimer);
- m_moduleName = QStringLiteral("%1.%2").arg(
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER),
- m_bundleId).mid(1); // Chop leading slash
}
// Returns empty string on success or an error message on failure.
// Note that there is also an asynchronous portion to the import, which will only
// be done if this method returns success. Once the asynchronous portion of the
// import is completed, importFinished signal will be emitted.
-QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile,
+QString ContentLibraryBundleImporter::importComponent(const QString &bundleDir,
+ const TypeName &type,
+ const QString &qmlFile,
const QStringList &files)
{
- FilePath bundleImportPath = resolveBundleImportPath();
+ QString module = QString::fromLatin1(type.left(type.lastIndexOf('.')));
+ m_bundleId = module.mid(module.lastIndexOf('.') + 1);
+
+ FilePath bundleDirPath = FilePath::fromString(bundleDir); // source dir
+ FilePath bundleImportPath = resolveBundleImportPath(m_bundleId); // target dir
+
if (bundleImportPath.isEmpty())
return "Failed to resolve bundle import folder";
- bool bundleImportPathExists = bundleImportPath.exists();
-
- if (!bundleImportPathExists && !bundleImportPath.createDir())
+ if (!bundleImportPath.exists() && !bundleImportPath.createDir())
return QStringLiteral("Failed to create bundle import folder: '%1'").arg(bundleImportPath.toString());
- for (const QString &file : std::as_const(m_sharedFiles)) {
- FilePath target = bundleImportPath.resolvePath(file);
- if (!target.exists()) {
- FilePath parentDir = target.parentDir();
- if (!parentDir.exists() && !parentDir.createDir())
- return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString());
- FilePath source = m_bundleDir.resolvePath(file);
- if (!source.copyFile(target))
- return QStringLiteral("Failed to copy shared file: '%1'").arg(source.toString());
- }
- }
-
- FilePath qmldirPath = bundleImportPath.resolvePath(QStringLiteral("qmldir"));
+ FilePath qmldirPath = bundleImportPath.pathAppended("qmldir");
QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray()));
if (qmldirContent.isEmpty()) {
qmldirContent.append("module ");
- qmldirContent.append(m_moduleName);
+ qmldirContent.append(module);
qmldirContent.append('\n');
}
- FilePath qmlSourceFile = bundleImportPath.resolvePath(FilePath::fromString(qmlFile));
+ FilePath qmlSourceFile = bundleImportPath.pathAppended(qmlFile);
const bool qmlFileExists = qmlSourceFile.exists();
const QString qmlType = qmlSourceFile.baseName();
- const QString fullTypeName = QStringLiteral("%1.%2.%3")
- .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType);
- if (m_pendingTypes.contains(fullTypeName) && !m_pendingTypes[fullTypeName])
- return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(fullTypeName);
+
+ if (m_pendingTypes.contains(type) && !m_pendingTypes.value(type))
+ return QStringLiteral("Unable to import while unimporting the same type: '%1'").arg(QLatin1String(type));
+
if (!qmldirContent.contains(qmlFile)) {
qmldirContent.append(qmlType);
qmldirContent.append(" 1.0 ");
@@ -92,12 +76,12 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile,
allFiles.append(files);
allFiles.append(qmlFile);
for (const QString &file : std::as_const(allFiles)) {
- FilePath target = bundleImportPath.resolvePath(file);
+ FilePath target = bundleImportPath.pathAppended(file);
FilePath parentDir = target.parentDir();
if (!parentDir.exists() && !parentDir.createDir())
return QStringLiteral("Failed to create folder for: '%1'").arg(target.toString());
- FilePath source = m_bundleDir.resolvePath(file);
+ FilePath source = bundleDirPath.pathAppended(file);
if (target.exists()) {
if (source.lastModified() == target.lastModified())
continue;
@@ -126,23 +110,23 @@ QString ContentLibraryBundleImporter::importComponent(const QString &qmlFile,
if (!model)
return "Model not available, cannot add import statement or update code model";
- Import import = Import::createLibraryImport(m_moduleName, "1.0");
+ Import import = Import::createLibraryImport(module, "1.0");
if (!model->hasImport(import)) {
if (model->possibleImports().contains(import)) {
- m_importAddPending = false;
+ m_pendingImport.clear();
try {
model->changeImports({import}, {});
} catch (const RewritingException &) {
// No point in trying to add import asynchronously either, so just fail out
- return QStringLiteral("Failed to add import statement for: '%1'").arg(m_moduleName);
+ return QStringLiteral("Failed to add import statement for: '%1'").arg(module);
}
} else {
// If import is not yet possible, import statement needs to be added asynchronously to
// avoid errors, as code model update takes a while.
- m_importAddPending = true;
+ m_pendingImport = module;
}
}
- m_pendingTypes.insert(fullTypeName, true);
+ m_pendingTypes.insert(type, true);
m_importTimerCount = 0;
m_importTimer.start();
@@ -154,17 +138,19 @@ void ContentLibraryBundleImporter::handleImportTimer()
auto handleFailure = [this] {
m_importTimer.stop();
m_fullReset = false;
- m_importAddPending = false;
+ m_pendingImport.clear();
m_importTimerCount = 0;
// Emit dummy finished signals for all pending types
- const QStringList pendingTypes = m_pendingTypes.keys();
- for (const QString &pendingType : pendingTypes) {
+ const QList<TypeName> pendingTypes = m_pendingTypes.keys();
+ for (const TypeName &pendingType : pendingTypes) {
m_pendingTypes.remove(pendingType);
- if (m_pendingTypes[pendingType])
- emit importFinished({});
+ if (m_pendingTypes.value(pendingType))
+ emit importFinished({}, m_bundleId);
else
- emit unimportFinished({});
+ emit unimportFinished({}, m_bundleId);
+
+ m_bundleId.clear();
}
};
@@ -186,12 +172,12 @@ void ContentLibraryBundleImporter::handleImportTimer()
QmlDesignerPlugin::instance()->documentManager().resetPossibleImports();
- if (m_importAddPending) {
+ if (!m_pendingImport.isEmpty()) {
try {
- Import import = Import::createLibraryImport(m_moduleName, "1.0");
+ Import import = Import::createLibraryImport(m_pendingImport, "1.0");
if (model->possibleImports().contains(import)) {
model->changeImports({import}, {});
- m_importAddPending = false;
+ m_pendingImport.clear();
}
} catch (const RewritingException &) {
// Import adding is unlikely to succeed later, either, so just bail out
@@ -201,21 +187,23 @@ void ContentLibraryBundleImporter::handleImportTimer()
}
// Detect when the code model has the new material(s) fully available
- const QStringList pendingTypes = m_pendingTypes.keys();
- for (const QString &pendingType : pendingTypes) {
- NodeMetaInfo metaInfo = model->metaInfo(pendingType.toUtf8());
- const bool isImport = m_pendingTypes[pendingType];
+ const QList<TypeName> pendingTypes = m_pendingTypes.keys();
+ for (const TypeName &pendingType : pendingTypes) {
+ NodeMetaInfo metaInfo = model->metaInfo(pendingType);
+ const bool isImport = m_pendingTypes.value(pendingType);
const bool typeComplete = metaInfo.isValid() && !metaInfo.prototypes().empty();
if (isImport == typeComplete) {
m_pendingTypes.remove(pendingType);
if (isImport)
#ifdef QDS_USE_PROJECTSTORAGE
- emit importFinished(pendingType.toUtf8());
+ emit importFinished(pendingType, m_bundleId);
#else
- emit importFinished(metaInfo);
+ emit importFinished(metaInfo, m_bundleId);
#endif
else
- emit unimportFinished(metaInfo);
+ emit unimportFinished(metaInfo, m_bundleId);
+
+ m_bundleId.clear();
}
}
@@ -225,10 +213,10 @@ void ContentLibraryBundleImporter::handleImportTimer()
}
}
-QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath &bundlePath)
+QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const FilePath &bundlePath)
{
FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE));
- const Utils::expected_str<QByteArray> content = assetRefPath.fileContents();
+ const expected_str<QByteArray> content = assetRefPath.fileContents();
if (content) {
QJsonParseError error;
QJsonDocument bundleDataJsonDoc = QJsonDocument::fromJson(*content, &error);
@@ -242,7 +230,7 @@ QVariantHash ContentLibraryBundleImporter::loadAssetRefMap(const Utils::FilePath
return {};
}
-void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundlePath,
+void ContentLibraryBundleImporter::writeAssetRefMap(const FilePath &bundlePath,
const QVariantHash &assetRefMap)
{
FilePath assetRefPath = bundlePath.resolvePath(QLatin1String(Constants::COMPONENT_BUNDLES_ASSET_REF_FILE));
@@ -253,9 +241,14 @@ void ContentLibraryBundleImporter::writeAssetRefMap(const Utils::FilePath &bundl
}
}
-QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
+QString ContentLibraryBundleImporter::unimportComponent(const TypeName &type, const QString &qmlFile)
{
- FilePath bundleImportPath = resolveBundleImportPath();
+ QString module = QString::fromLatin1(type.left(type.lastIndexOf('.')));
+ m_bundleId = module.mid(module.lastIndexOf('.') + 1);
+
+ emit aboutToUnimport(type, m_bundleId);
+
+ FilePath bundleImportPath = resolveBundleImportPath(m_bundleId);
if (bundleImportPath.isEmpty())
return QStringLiteral("Failed to resolve bundle import folder for: '%1'").arg(qmlFile);
@@ -274,10 +267,10 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
QByteArray newContent;
QString qmlType = qmlFilePath.baseName();
- const QString fullTypeName = QStringLiteral("%1.%2.%3")
- .arg(QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1), m_bundleId, qmlType);
- if (m_pendingTypes.contains(fullTypeName) && m_pendingTypes[fullTypeName])
- return QStringLiteral("Unable to unimport while importing the same type: '%1'").arg(fullTypeName);
+ if (m_pendingTypes.contains(type) && m_pendingTypes.value(type)) {
+ return QStringLiteral("Unable to unimport while importing the same type: '%1'")
+ .arg(QString::fromLatin1(type));
+ }
if (qmldirContent) {
int typeIndex = qmldirContent->indexOf(qmlType.toUtf8());
@@ -293,7 +286,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
}
}
- m_pendingTypes.insert(fullTypeName, false);
+ m_pendingTypes.insert(type, false);
QVariantHash assetRefMap = loadAssetRefMap(bundleImportPath);
bool writeAssetRefs = false;
@@ -327,7 +320,7 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
Model *model = doc ? doc->currentModel() : nullptr;
if (model) {
- Import import = Import::createLibraryImport(m_moduleName, "1.0");
+ Import import = Import::createLibraryImport(module, "1.0");
if (model->imports().contains(import))
model->changeImports({}, {import});
}
@@ -340,18 +333,14 @@ QString ContentLibraryBundleImporter::unimportComponent(const QString &qmlFile)
return {};
}
-FilePath ContentLibraryBundleImporter::resolveBundleImportPath()
+FilePath ContentLibraryBundleImporter::resolveBundleImportPath(const QString &bundleId)
{
- FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath();
+ FilePath bundleImportPath = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().componentBundlesBasePath();
if (bundleImportPath.isEmpty())
- return bundleImportPath;
-
- const QString projectBundlePath = QStringLiteral("%1%2/%3").arg(
- QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER),
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER),
- m_bundleId).mid(1); // Chop leading slash
+ return {};
- return bundleImportPath.resolvePath(projectBundlePath);
+ return bundleImportPath.resolvePath(bundleId);
}
-} // namespace QmlDesigner::Internal
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h
index 3aff09fe34..8155311e3e 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarybundleimporter.h
@@ -3,59 +3,53 @@
#pragma once
-#include <utils/filepath.h>
+#include <modelfwd.h>
-#include "nodemetainfo.h"
+#include <utils/filepath.h>
#include <QTimer>
#include <QVariantHash>
-QT_BEGIN_NAMESPACE
-QT_END_NAMESPACE
+namespace QmlDesigner {
-namespace QmlDesigner::Internal {
+class NodeMetaInfo;
class ContentLibraryBundleImporter : public QObject
{
Q_OBJECT
public:
- ContentLibraryBundleImporter(const QString &bundleDir,
- const QString &bundleId,
- const QStringList &sharedFiles,
- QObject *parent = nullptr);
+ ContentLibraryBundleImporter(QObject *parent = nullptr);
~ContentLibraryBundleImporter() = default;
- QString importComponent(const QString &qmlFile,
+ QString importComponent(const QString &bundleDir, const TypeName &type, const QString &qmlFile,
const QStringList &files);
- QString unimportComponent(const QString &qmlFile);
- Utils::FilePath resolveBundleImportPath();
+ QString unimportComponent(const TypeName &type, const QString &qmlFile);
+ Utils::FilePath resolveBundleImportPath(const QString &bundleId);
signals:
// The metaInfo parameter will be invalid if an error was encountered during
// asynchronous part of the import. In this case all remaining pending imports have been
// terminated, and will not receive separate importFinished notifications.
#ifdef QDS_USE_PROJECTSTORAGE
- void importFinished(const QmlDesigner::TypeName &typeName);
+ void importFinished(const QmlDesigner::TypeName &typeName, const QString &bundleId);
#else
- void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo);
+ void importFinished(const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId);
#endif
- void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo);
+ void unimportFinished(const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId);
+ void aboutToUnimport(const TypeName &type, const QString &bundleId);
private:
void handleImportTimer();
QVariantHash loadAssetRefMap(const Utils::FilePath &bundlePath);
void writeAssetRefMap(const Utils::FilePath &bundlePath, const QVariantHash &assetRefMap);
- Utils::FilePath m_bundleDir;
- QString m_bundleId;
- QString m_moduleName;
- QStringList m_sharedFiles;
QTimer m_importTimer;
int m_importTimerCount = 0;
- bool m_importAddPending = false;
+ QString m_pendingImport;
+ QString m_bundleId;
bool m_fullReset = false;
- QHash<QString, bool> m_pendingTypes; // <type, isImport>
+ QHash<TypeName, bool> m_pendingTypes; // <type, isImport>
};
-} // namespace QmlDesigner::Internal
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp
deleted file mode 100644
index f572fbe65f..0000000000
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (C) 2023 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#include "contentlibraryeffect.h"
-
-#include <QFileInfo>
-
-namespace QmlDesigner {
-
-ContentLibraryEffect::ContentLibraryEffect(QObject *parent,
- const QString &name,
- const QString &qml,
- const TypeName &type,
- const QUrl &icon,
- const QStringList &files)
- : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files)
-{
- m_allFiles = m_files;
- m_allFiles.push_back(m_qml);
-}
-
-bool ContentLibraryEffect::filter(const QString &searchText)
-{
- if (m_visible != m_name.contains(searchText, Qt::CaseInsensitive)) {
- m_visible = !m_visible;
- emit itemVisibleChanged();
- }
-
- return m_visible;
-}
-
-QUrl ContentLibraryEffect::icon() const
-{
- return m_icon;
-}
-
-QString ContentLibraryEffect::qml() const
-{
- return m_qml;
-}
-
-TypeName ContentLibraryEffect::type() const
-{
- return m_type;
-}
-
-QStringList ContentLibraryEffect::files() const
-{
- return m_files;
-}
-
-bool ContentLibraryEffect::visible() const
-{
- return m_visible;
-}
-
-bool ContentLibraryEffect::setImported(bool imported)
-{
- if (m_imported != imported) {
- m_imported = imported;
- emit itemImportedChanged();
- return true;
- }
-
- return false;
-}
-
-bool ContentLibraryEffect::imported() const
-{
- return m_imported;
-}
-
-QStringList ContentLibraryEffect::allFiles() const
-{
- return m_allFiles;
-}
-
-} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp
index 38e6eed3da..f904775d52 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.cpp
@@ -3,14 +3,12 @@
#include "contentlibraryeffectscategory.h"
-#include "contentlibraryeffect.h"
-
namespace QmlDesigner {
ContentLibraryEffectsCategory::ContentLibraryEffectsCategory(QObject *parent, const QString &name)
: QObject(parent), m_name(name) {}
-void ContentLibraryEffectsCategory::addBundleItem(ContentLibraryEffect *bundleItem)
+void ContentLibraryEffectsCategory::addBundleItem(ContentLibraryItem *bundleItem)
{
m_categoryItems.append(bundleItem);
}
@@ -19,7 +17,7 @@ bool ContentLibraryEffectsCategory::updateImportedState(const QStringList &impor
{
bool changed = false;
- for (ContentLibraryEffect *item : std::as_const(m_categoryItems))
+ for (ContentLibraryItem *item : std::as_const(m_categoryItems))
changed |= item->setImported(importedItems.contains(item->qml().chopped(4)));
return changed;
@@ -28,7 +26,7 @@ bool ContentLibraryEffectsCategory::updateImportedState(const QStringList &impor
bool ContentLibraryEffectsCategory::filter(const QString &searchText)
{
bool visible = false;
- for (ContentLibraryEffect *item : std::as_const(m_categoryItems))
+ for (ContentLibraryItem *item : std::as_const(m_categoryItems))
visible |= item->filter(searchText);
if (visible != m_visible) {
@@ -55,7 +53,7 @@ bool ContentLibraryEffectsCategory::expanded() const
return m_expanded;
}
-QList<ContentLibraryEffect *> ContentLibraryEffectsCategory::categoryItems() const
+QList<ContentLibraryItem *> ContentLibraryEffectsCategory::categoryItems() const
{
return m_categoryItems;
}
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h
index 79737c1ec2..9f56de3c6b 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectscategory.h
@@ -3,12 +3,12 @@
#pragma once
+#include "contentlibraryitem.h"
+
#include <QObject>
namespace QmlDesigner {
-class ContentLibraryEffect;
-
class ContentLibraryEffectsCategory : public QObject
{
Q_OBJECT
@@ -16,20 +16,20 @@ class ContentLibraryEffectsCategory : public QObject
Q_PROPERTY(QString bundleCategoryName MEMBER m_name CONSTANT)
Q_PROPERTY(bool bundleCategoryVisible MEMBER m_visible NOTIFY categoryVisibleChanged)
Q_PROPERTY(bool bundleCategoryExpanded MEMBER m_expanded NOTIFY categoryExpandChanged)
- Q_PROPERTY(QList<ContentLibraryEffect *> bundleCategoryItems MEMBER m_categoryItems
+ Q_PROPERTY(QList<ContentLibraryItem *> bundleCategoryItems MEMBER m_categoryItems
NOTIFY categoryItemsChanged)
public:
ContentLibraryEffectsCategory(QObject *parent, const QString &name);
- void addBundleItem(ContentLibraryEffect *bundleItem);
+ void addBundleItem(ContentLibraryItem *bundleItem);
bool updateImportedState(const QStringList &importedMats);
bool filter(const QString &searchText);
QString name() const;
bool visible() const;
bool expanded() const;
- QList<ContentLibraryEffect *> categoryItems() const;
+ QList<ContentLibraryItem *> categoryItems() const;
signals:
void categoryVisibleChanged();
@@ -41,7 +41,7 @@ private:
bool m_visible = true;
bool m_expanded = true;
- QList<ContentLibraryEffect *> m_categoryItems;
+ QList<ContentLibraryItem *> m_categoryItems;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp
index 6b1de2d2a7..e0fe2b6c54 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.cpp
@@ -4,11 +4,11 @@
#include "contentlibraryeffectsmodel.h"
#include "contentlibrarybundleimporter.h"
-#include "contentlibraryeffect.h"
#include "contentlibraryeffectscategory.h"
+#include "contentlibraryitem.h"
#include "contentlibrarywidget.h"
-#include "qmldesignerconstants.h"
+#include <qmldesignerplugin.h>
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <utils/hostosinfo.h>
@@ -63,6 +63,11 @@ bool ContentLibraryEffectsModel::isValidIndex(int idx) const
return idx > -1 && idx < rowCount();
}
+QString ContentLibraryEffectsModel::bundleId() const
+{
+ return m_bundleId;
+}
+
void ContentLibraryEffectsModel::updateIsEmpty()
{
bool anyCatVisible = Utils::anyOf(m_bundleCategories, [&](ContentLibraryEffectsCategory *cat) {
@@ -88,49 +93,23 @@ QHash<int, QByteArray> ContentLibraryEffectsModel::roleNames() const
return roles;
}
-void ContentLibraryEffectsModel::createImporter(const QString &bundlePath, const QString &bundleId,
- const QStringList &sharedFiles)
-{
- m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles);
-#ifdef QDS_USE_PROJECTSTORAGE
- connect(m_importer,
- &Internal::ContentLibraryBundleImporter::importFinished,
- this,
- [&](const QmlDesigner::TypeName &typeName) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (typeName.size())
- emit bundleItemImported(typeName);
- });
-#else
- connect(m_importer,
- &Internal::ContentLibraryBundleImporter::importFinished,
- this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (metaInfo.isValid())
- emit bundleItemImported(metaInfo);
- });
-#endif
-
- connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- Q_UNUSED(metaInfo)
- m_importerRunning = false;
- emit importerRunningChanged();
- emit bundleItemUnimported(metaInfo);
- });
-
- resetModel();
- updateIsEmpty();
-}
-
void ContentLibraryEffectsModel::loadBundle()
{
- if (m_bundleExists || m_probeBundleDir)
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ if (m_probeBundleDir || (m_bundleExists && m_bundleId == compUtils.effectsBundleId()))
return;
+ // clean up
+ qDeleteAll(m_bundleCategories);
+ m_bundleCategories.clear();
+ m_bundleExists = false;
+ m_isEmpty = true;
+ m_probeBundleDir = false;
+ m_bundleObj = {};
+ m_bundleId.clear();
+ m_bundlePath.clear();
+
QDir bundleDir;
if (!qEnvironmentVariable("EFFECT_BUNDLE_PATH").isEmpty())
@@ -145,30 +124,32 @@ void ContentLibraryEffectsModel::loadBundle()
while (!bundleDir.cd("effect_bundle") && bundleDir.cdUp())
; // do nothing
- if (bundleDir.dirName() != "effect_bundle") // bundlePathDir not found
+ if (bundleDir.dirName() != "effect_bundle") { // bundlePathDir not found
+ resetModel();
return;
+ }
}
QString bundlePath = bundleDir.filePath("effect_bundle.json");
- if (m_bundleObj.isEmpty()) {
- QFile propsFile(bundlePath);
-
- if (!propsFile.open(QIODevice::ReadOnly)) {
- qWarning("Couldn't open effect_bundle.json");
- return;
- }
+ QFile bundleFile(bundlePath);
+ if (!bundleFile.open(QIODevice::ReadOnly)) {
+ qWarning("Couldn't open effect_bundle.json");
+ resetModel();
+ return;
+ }
- QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(propsFile.readAll());
- if (bundleJsonDoc.isNull()) {
- qWarning("Invalid effect_bundle.json file");
- return;
- } else {
- m_bundleObj = bundleJsonDoc.object();
- }
+ QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll());
+ if (bundleJsonDoc.isNull()) {
+ qWarning("Invalid effect_bundle.json file");
+ resetModel();
+ return;
}
- QString bundleId = m_bundleObj.value("id").toString();
+ m_bundleObj = bundleJsonDoc.object();
+
+ QString bundleType = compUtils.effectsBundleType();
+ m_bundleId = compUtils.effectsBundleId();
const QJsonObject catsObj = m_bundleObj.value("categories").toObject();
const QStringList categories = catsObj.keys();
@@ -176,38 +157,36 @@ void ContentLibraryEffectsModel::loadBundle()
auto category = new ContentLibraryEffectsCategory(this, cat);
const QJsonObject itemsObj = catsObj.value(cat).toObject();
- const QStringList items = itemsObj.keys();
- for (const QString &item : items) {
- const QJsonObject itemObj = itemsObj.value(item).toObject();
+ const QStringList itemsNames = itemsObj.keys();
+ for (const QString &itemName : itemsNames) {
+ const QJsonObject itemObj = itemsObj.value(itemName).toObject();
QStringList files;
const QJsonArray assetsArr = itemObj.value("files").toArray();
- for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr)
+ for (const QJsonValueConstRef &asset : assetsArr)
files.append(asset.toString());
QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(itemObj.value("icon").toString()));
QString qml = itemObj.value("qml").toString();
- TypeName type = QLatin1String("%1.%2.%3").arg(
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1),
- bundleId,
- qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
+ TypeName type = QLatin1String("%1.%2")
+ .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
- auto bundleItem = new ContentLibraryEffect(category, item, qml, type, icon, files);
+ auto bundleItem = new ContentLibraryItem(category, itemName, qml, type, icon, files);
category->addBundleItem(bundleItem);
}
m_bundleCategories.append(category);
}
- QStringList sharedFiles;
+ m_bundleSharedFiles.clear();
const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray();
- for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr)
- sharedFiles.append(file.toString());
-
- createImporter(bundleDir.path(), bundleId, sharedFiles);
+ for (const QJsonValueConstRef &file : sharedFilesArr)
+ m_bundleSharedFiles.append(file.toString());
+ m_bundlePath = bundleDir.path();
m_bundleExists = true;
- emit bundleExistsChanged();
+ updateIsEmpty();
+ resetModel();
}
bool ContentLibraryEffectsModel::hasRequiredQuick3DImport() const
@@ -220,11 +199,6 @@ bool ContentLibraryEffectsModel::bundleExists() const
return m_bundleExists;
}
-Internal::ContentLibraryBundleImporter *ContentLibraryEffectsModel::bundleImporter() const
-{
- return m_importer;
-}
-
void ContentLibraryEffectsModel::setSearchText(const QString &searchText)
{
QString lowerSearchText = searchText.toLower();
@@ -277,30 +251,26 @@ void ContentLibraryEffectsModel::resetModel()
endResetModel();
}
-void ContentLibraryEffectsModel::addInstance(ContentLibraryEffect *bundleItem)
+void ContentLibraryEffectsModel::addInstance(ContentLibraryItem *bundleItem)
{
- QString err = m_importer->importComponent(bundleItem->qml(), bundleItem->files());
+ QString err = m_widget->importer()->importComponent(m_bundlePath, bundleItem->type(),
+ bundleItem->qml(),
+ bundleItem->files() + m_bundleSharedFiles);
- if (err.isEmpty()) {
- m_importerRunning = true;
- emit importerRunningChanged();
- } else {
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
qWarning() << __FUNCTION__ << err;
- }
}
-void ContentLibraryEffectsModel::removeFromProject(ContentLibraryEffect *bundleItem)
+void ContentLibraryEffectsModel::removeFromProject(ContentLibraryItem *bundleItem)
{
- emit bundleItemAboutToUnimport(bundleItem->type());
+ QString err = m_widget->importer()->unimportComponent(bundleItem->type(), bundleItem->qml());
- QString err = m_importer->unimportComponent(bundleItem->qml());
-
- if (err.isEmpty()) {
- m_importerRunning = true;
- emit importerRunningChanged();
- } else {
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
qWarning() << __FUNCTION__ << err;
- }
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h
index 5d67ac3da8..5bcb857fca 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffectsmodel.h
@@ -3,22 +3,15 @@
#pragma once
-#include "nodemetainfo.h"
-
#include <QAbstractListModel>
-#include <QDir>
#include <QJsonObject>
namespace QmlDesigner {
-class ContentLibraryEffect;
+class ContentLibraryItem;
class ContentLibraryEffectsCategory;
class ContentLibraryWidget;
-namespace Internal {
-class ContentLibraryBundleImporter;
-}
-
class ContentLibraryEffectsModel : public QAbstractListModel
{
Q_OBJECT
@@ -26,7 +19,6 @@ class ContentLibraryEffectsModel : public QAbstractListModel
Q_PROPERTY(bool bundleExists READ bundleExists NOTIFY bundleExistsChanged)
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged)
- Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged)
public:
ContentLibraryEffectsModel(ContentLibraryWidget *parent = nullptr);
@@ -49,46 +41,33 @@ public:
void resetModel();
void updateIsEmpty();
- Internal::ContentLibraryBundleImporter *bundleImporter() const;
+ Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryItem *bundleItem);
+ Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryItem *bundleItem);
- Q_INVOKABLE void addInstance(QmlDesigner::ContentLibraryEffect *bundleItem);
- Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryEffect *bundleItem);
+ QString bundleId() const;
signals:
void isEmptyChanged();
void hasRequiredQuick3DImportChanged();
-#ifdef QDS_USE_PROJECTSTORAGE
- void bundleItemImported(const QmlDesigner::TypeName &typeName);
-#else
- void bundleItemImported(const QmlDesigner::NodeMetaInfo &metaInfo);
-#endif
- void bundleItemAboutToUnimport(const QmlDesigner::TypeName &type);
- void bundleItemUnimported(const QmlDesigner::NodeMetaInfo &metaInfo);
- void importerRunningChanged();
void bundleExistsChanged();
private:
bool isValidIndex(int idx) const;
- void createImporter(const QString &bundlePath, const QString &bundleId,
- const QStringList &sharedFiles);
ContentLibraryWidget *m_widget = nullptr;
QString m_searchText;
+ QString m_bundlePath;
+ QString m_bundleId;
+ QStringList m_bundleSharedFiles;
QList<ContentLibraryEffectsCategory *> m_bundleCategories;
QJsonObject m_bundleObj;
- Internal::ContentLibraryBundleImporter *m_importer = nullptr;
bool m_isEmpty = true;
bool m_bundleExists = false;
bool m_probeBundleDir = false;
- bool m_importerRunning = false;
int m_quick3dMajorVersion = -1;
int m_quick3dMinorVersion = -1;
-
- QString m_importerBundlePath;
- QString m_importerBundleId;
- QStringList m_importerSharedFiles;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp
new file mode 100644
index 0000000000..38fb69abbc
--- /dev/null
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.cpp
@@ -0,0 +1,76 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "contentlibraryitem.h"
+
+namespace QmlDesigner {
+
+ContentLibraryItem::ContentLibraryItem(QObject *parent,
+ const QString &name,
+ const QString &qml,
+ const TypeName &type,
+ const QUrl &icon,
+ const QStringList &files)
+ : QObject(parent), m_name(name), m_qml(qml), m_type(type), m_icon(icon), m_files(files)
+{
+ m_allFiles = m_files;
+ m_allFiles.push_back(m_qml);
+}
+
+bool ContentLibraryItem::filter(const QString &searchText)
+{
+ if (m_visible != m_name.contains(searchText, Qt::CaseInsensitive)) {
+ m_visible = !m_visible;
+ emit itemVisibleChanged();
+ }
+
+ return m_visible;
+}
+
+QUrl ContentLibraryItem::icon() const
+{
+ return m_icon;
+}
+
+QString ContentLibraryItem::qml() const
+{
+ return m_qml;
+}
+
+TypeName ContentLibraryItem::type() const
+{
+ return m_type;
+}
+
+QStringList ContentLibraryItem::files() const
+{
+ return m_files;
+}
+
+bool ContentLibraryItem::visible() const
+{
+ return m_visible;
+}
+
+bool ContentLibraryItem::setImported(bool imported)
+{
+ if (m_imported != imported) {
+ m_imported = imported;
+ emit itemImportedChanged();
+ return true;
+ }
+
+ return false;
+}
+
+bool ContentLibraryItem::imported() const
+{
+ return m_imported;
+}
+
+QStringList ContentLibraryItem::allFiles() const
+{
+ return m_allFiles;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h
index fdb302b613..bdf8736728 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryeffect.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryitem.h
@@ -10,7 +10,7 @@
namespace QmlDesigner {
-class ContentLibraryEffect : public QObject
+class ContentLibraryItem : public QObject
{
Q_OBJECT
@@ -19,14 +19,15 @@ class ContentLibraryEffect : public QObject
Q_PROPERTY(QStringList bundleItemFiles READ allFiles CONSTANT)
Q_PROPERTY(bool bundleItemVisible MEMBER m_visible NOTIFY itemVisibleChanged)
Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY itemImportedChanged)
+ Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT)
public:
- ContentLibraryEffect(QObject *parent,
- const QString &name,
- const QString &qml,
- const TypeName &type,
- const QUrl &icon,
- const QStringList &files);
+ ContentLibraryItem(QObject *parent,
+ const QString &name,
+ const QString &qml,
+ const TypeName &type,
+ const QUrl &icon,
+ const QStringList &files);
bool filter(const QString &searchText);
@@ -35,11 +36,9 @@ public:
TypeName type() const;
QStringList files() const;
bool visible() const;
- QString qmlFilePath() const;
bool setImported(bool imported);
bool imported() const;
- QString parentDirPath() const;
QStringList allFiles() const;
signals:
@@ -57,6 +56,7 @@ private:
bool m_imported = false;
QStringList m_allFiles;
+ const QString m_itemType = "item";
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp
index 834bc8aa30..25d1523199 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.cpp
@@ -32,6 +32,11 @@ bool ContentLibraryMaterial::filter(const QString &searchText)
return m_visible;
}
+QString ContentLibraryMaterial::name() const
+{
+ return m_name;
+}
+
QUrl ContentLibraryMaterial::icon() const
{
return m_icon;
@@ -84,7 +89,7 @@ QString ContentLibraryMaterial::qmlFilePath() const
return m_downloadPath + "/" + m_qml;
}
-QString ContentLibraryMaterial::parentDirPath() const
+QString ContentLibraryMaterial::dirPath() const
{
return m_downloadPath;
}
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
index f546ea98cd..aaaf9517f9 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterial.h
@@ -3,9 +3,8 @@
#pragma once
-#include "qmldesignercorelib_global.h"
+#include "nodeinstanceglobal.h"
-#include <QDataStream>
#include <QObject>
#include <QUrl>
@@ -18,10 +17,11 @@ class ContentLibraryMaterial : public QObject
Q_PROPERTY(QString bundleMaterialName MEMBER m_name CONSTANT)
Q_PROPERTY(QUrl bundleMaterialIcon MEMBER m_icon CONSTANT)
Q_PROPERTY(bool bundleMaterialVisible MEMBER m_visible NOTIFY materialVisibleChanged)
- Q_PROPERTY(bool bundleMaterialImported READ imported WRITE setImported NOTIFY materialImportedChanged)
+ Q_PROPERTY(bool bundleItemImported READ imported WRITE setImported NOTIFY materialImportedChanged)
Q_PROPERTY(QString bundleMaterialBaseWebUrl MEMBER m_baseWebUrl CONSTANT)
- Q_PROPERTY(QString bundleMaterialParentPath READ parentDirPath CONSTANT)
+ Q_PROPERTY(QString bundleMaterialDirPath READ dirPath CONSTANT)
Q_PROPERTY(QStringList bundleMaterialFiles READ allFiles CONSTANT)
+ Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT)
public:
ContentLibraryMaterial(QObject *parent,
@@ -31,12 +31,13 @@ public:
const QUrl &icon,
const QStringList &files,
const QString &downloadPath,
- const QString &baseWebUrl);
+ const QString &baseWebUrl = {});
bool filter(const QString &searchText);
Q_INVOKABLE bool isDownloaded() const;
+ QString name() const;
QUrl icon() const;
QString qml() const;
TypeName type() const;
@@ -46,7 +47,7 @@ public:
bool setImported(bool imported);
bool imported() const;
- QString parentDirPath() const;
+ QString dirPath() const;
QStringList allFiles() const;
signals:
@@ -66,6 +67,7 @@ private:
QString m_downloadPath;
QString m_baseWebUrl;
QStringList m_allFiles;
+ const QString m_itemType = "material";
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
index 7594c691b5..adb47108a9 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.cpp
@@ -8,18 +8,19 @@
#include "contentlibrarymaterialscategory.h"
#include "contentlibrarywidget.h"
-#include <designerpaths.h>
+#include "designerpaths.h"
#include "filedownloader.h"
#include "fileextractor.h"
#include "multifiledownloader.h"
-#include "qmldesignerconstants.h"
-#include "qmldesignerplugin.h"
+
+#include <qmldesignerplugin.h>
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <QCoreApplication>
+#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
#include <QQmlEngine>
@@ -40,7 +41,10 @@ ContentLibraryMaterialsModel::ContentLibraryMaterialsModel(ContentLibraryWidget
qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader");
qmlRegisterType<QmlDesigner::MultiFileDownloader>("WebFetcher", 1, 0, "MultiFileDownloader");
+}
+void ContentLibraryMaterialsModel::loadBundle()
+{
QDir bundleDir{m_downloadPath};
if (fetchBundleMetadata(bundleDir) && fetchBundleIcons(bundleDir))
loadMaterialBundle(bundleDir);
@@ -192,11 +196,9 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co
extractor->setAlwaysCreateDir(false);
extractor->setClearTargetPathContents(false);
- QObject::connect(extractor, &FileExtractor::finishedChanged, this, [this, downloader, extractor]() {
+ QObject::connect(extractor, &FileExtractor::finishedChanged, this, [downloader, extractor]() {
downloader->deleteLater();
extractor->deleteLater();
-
- createImporter(m_importerBundlePath, m_importerBundleId, m_importerSharedFiles);
});
extractor->extract();
@@ -205,93 +207,68 @@ void ContentLibraryMaterialsModel::downloadSharedFiles(const QDir &targetDir, co
downloader->start();
}
-void ContentLibraryMaterialsModel::createImporter(const QString &bundlePath, const QString &bundleId,
- const QStringList &sharedFiles)
+QString ContentLibraryMaterialsModel::bundleId() const
{
- m_importer = new Internal::ContentLibraryBundleImporter(bundlePath, bundleId, sharedFiles);
-#ifdef QDS_USE_PROJECTSTORAGE
- connect(m_importer,
- &Internal::ContentLibraryBundleImporter::importFinished,
- this,
- [&](const QmlDesigner::TypeName &typeName) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (typeName.size())
- emit bundleMaterialImported(typeName);
- });
-#else
- connect(m_importer,
- &Internal::ContentLibraryBundleImporter::importFinished,
- this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- m_importerRunning = false;
- emit importerRunningChanged();
- if (metaInfo.isValid())
- emit bundleMaterialImported(metaInfo);
- });
-#endif
-
- connect(m_importer, &Internal::ContentLibraryBundleImporter::unimportFinished, this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- Q_UNUSED(metaInfo)
- m_importerRunning = false;
- emit importerRunningChanged();
- emit bundleMaterialUnimported(metaInfo);
- });
-
- resetModel();
- updateIsEmpty();
+ return m_bundleId;
}
-void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir)
+void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &bundleDir)
{
- if (m_matBundleExists)
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ if (m_bundleExists && m_bundleId == compUtils.materialsBundleId())
return;
- QString matBundlePath = matBundleDir.filePath("material_bundle.json");
+ // clean up
+ qDeleteAll(m_bundleCategories);
+ m_bundleCategories.clear();
+ m_bundleExists = false;
+ m_isEmpty = true;
+ m_bundleObj = {};
+ m_bundleId.clear();
- if (m_matBundleObj.isEmpty()) {
- QFile matPropsFile(matBundlePath);
+ QString bundlePath = bundleDir.filePath("material_bundle.json");
- if (!matPropsFile.open(QIODevice::ReadOnly)) {
- qWarning("Couldn't open material_bundle.json");
- return;
- }
+ QFile bundleFile(bundlePath);
+ if (!bundleFile.open(QIODevice::ReadOnly)) {
+ qWarning("Couldn't open material_bundle.json");
+ resetModel();
+ return;
+ }
- QJsonDocument matBundleJsonDoc = QJsonDocument::fromJson(matPropsFile.readAll());
- if (matBundleJsonDoc.isNull()) {
- qWarning("Invalid material_bundle.json file");
- return;
- } else {
- m_matBundleObj = matBundleJsonDoc.object();
- }
+ QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(bundleFile.readAll());
+ if (bundleJsonDoc.isNull()) {
+ qWarning("Invalid material_bundle.json file");
+ resetModel();
+ return;
}
- QString bundleId = m_matBundleObj.value("id").toString();
+ m_bundleObj = bundleJsonDoc.object();
- const QJsonObject catsObj = m_matBundleObj.value("categories").toObject();
+ QString bundleType = compUtils.materialsBundleType();
+ m_bundleId = compUtils.materialsBundleId();
+
+ const QJsonObject catsObj = m_bundleObj.value("categories").toObject();
const QStringList categories = catsObj.keys();
for (const QString &cat : categories) {
auto category = new ContentLibraryMaterialsCategory(this, cat);
const QJsonObject matsObj = catsObj.value(cat).toObject();
- const QStringList mats = matsObj.keys();
- for (const QString &mat : mats) {
- const QJsonObject matObj = matsObj.value(mat).toObject();
+ const QStringList matsNames = matsObj.keys();
+ for (const QString &matName : matsNames) {
+ const QJsonObject matObj = matsObj.value(matName).toObject();
QStringList files;
const QJsonArray assetsArr = matObj.value("files").toArray();
- for (const auto /*QJson{Const,}ValueRef*/ &asset : assetsArr)
+ for (const QJsonValueConstRef &asset : assetsArr)
files.append(asset.toString());
- QUrl icon = QUrl::fromLocalFile(matBundleDir.filePath(matObj.value("icon").toString()));
+ QUrl icon = QUrl::fromLocalFile(bundleDir.filePath(matObj.value("icon").toString()));
QString qml = matObj.value("qml").toString();
- TypeName type = QLatin1String("%1.%2.%3").arg(
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1),
- bundleId,
- qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
+ TypeName type = QLatin1String("%1.%2")
+ .arg(bundleType, qml.chopped(4)).toLatin1(); // chopped(4): remove .qml
- auto bundleMat = new ContentLibraryMaterial(category, mat, qml, type, icon, files,
+ auto bundleMat = new ContentLibraryMaterial(category, matName, qml, type, icon, files,
m_downloadPath, m_baseUrl);
category->addBundleMaterial(bundleMat);
@@ -299,30 +276,23 @@ void ContentLibraryMaterialsModel::loadMaterialBundle(const QDir &matBundleDir)
m_bundleCategories.append(category);
}
- QStringList sharedFiles;
- const QJsonArray sharedFilesArr = m_matBundleObj.value("sharedFiles").toArray();
- for (const auto /*QJson{Const,}ValueRef*/ &file : sharedFilesArr)
- sharedFiles.append(file.toString());
+ m_bundleSharedFiles.clear();
+ const QJsonArray sharedFilesArr = m_bundleObj.value("sharedFiles").toArray();
+ for (const QJsonValueConstRef &file : sharedFilesArr)
+ m_bundleSharedFiles.append(file.toString());
QStringList missingSharedFiles;
- for (const QString &s : std::as_const(sharedFiles)) {
- const QString fullSharedFilePath = matBundleDir.filePath(s);
-
- if (!QFileInfo::exists(fullSharedFilePath))
+ for (const QString &s : std::as_const(m_bundleSharedFiles)) {
+ if (!QFileInfo::exists(bundleDir.filePath(s)))
missingSharedFiles.push_back(s);
}
- if (missingSharedFiles.length() > 0) {
- m_importerBundlePath = matBundleDir.path();
- m_importerBundleId = bundleId;
- m_importerSharedFiles = sharedFiles;
- downloadSharedFiles(matBundleDir, missingSharedFiles);
- } else {
- createImporter(matBundleDir.path(), bundleId, sharedFiles);
- }
+ if (missingSharedFiles.length() > 0)
+ downloadSharedFiles(bundleDir, missingSharedFiles);
- m_matBundleExists = true;
- emit matBundleExistsChanged();
+ m_bundleExists = true;
+ updateIsEmpty();
+ resetModel();
}
bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const
@@ -332,12 +302,7 @@ bool ContentLibraryMaterialsModel::hasRequiredQuick3DImport() const
bool ContentLibraryMaterialsModel::matBundleExists() const
{
- return m_matBundleExists;
-}
-
-Internal::ContentLibraryBundleImporter *ContentLibraryMaterialsModel::bundleImporter() const
-{
- return m_importer;
+ return m_bundleExists;
}
void ContentLibraryMaterialsModel::setSearchText(const QString &searchText)
@@ -359,11 +324,11 @@ void ContentLibraryMaterialsModel::setSearchText(const QString &searchText)
updateIsEmpty();
}
-void ContentLibraryMaterialsModel::updateImportedState(const QStringList &importedMats)
+void ContentLibraryMaterialsModel::updateImportedState(const QStringList &importedItems)
{
bool changed = false;
for (ContentLibraryMaterialsCategory *cat : std::as_const(m_bundleCategories))
- changed |= cat->updateImportedState(importedMats);
+ changed |= cat->updateImportedState(importedItems);
if (changed)
resetModel();
@@ -399,42 +364,23 @@ void ContentLibraryMaterialsModel::applyToSelected(ContentLibraryMaterial *mat,
void ContentLibraryMaterialsModel::addToProject(ContentLibraryMaterial *mat)
{
- QString err = m_importer->importComponent(mat->qml(), mat->files());
+ QString err = m_widget->importer()->importComponent(mat->dirPath(), mat->type(), mat->qml(),
+ mat->files() + m_bundleSharedFiles);
- if (err.isEmpty()) {
- m_importerRunning = true;
- emit importerRunningChanged();
- } else {
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
qWarning() << __FUNCTION__ << err;
- }
}
void ContentLibraryMaterialsModel::removeFromProject(ContentLibraryMaterial *mat)
{
- emit bundleMaterialAboutToUnimport(mat->type());
+ QString err = m_widget->importer()->unimportComponent(mat->type(), mat->qml());
- QString err = m_importer->unimportComponent(mat->qml());
-
- if (err.isEmpty()) {
- m_importerRunning = true;
- emit importerRunningChanged();
- } else {
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
qWarning() << __FUNCTION__ << err;
- }
-}
-
-bool ContentLibraryMaterialsModel::hasModelSelection() const
-{
- return m_hasModelSelection;
-}
-
-void ContentLibraryMaterialsModel::setHasModelSelection(bool b)
-{
- if (b == m_hasModelSelection)
- return;
-
- m_hasModelSelection = b;
- emit hasModelSelectionChanged();
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h
index 21bd374137..c0840dc3cc 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarymaterialsmodel.h
@@ -3,22 +3,17 @@
#pragma once
-#include "nodemetainfo.h"
-
#include <QAbstractListModel>
-#include <QDir>
#include <QJsonObject>
+QT_FORWARD_DECLARE_CLASS(QDir)
+
namespace QmlDesigner {
class ContentLibraryMaterial;
class ContentLibraryMaterialsCategory;
class ContentLibraryWidget;
-namespace Internal {
-class ContentLibraryBundleImporter;
-}
-
class ContentLibraryMaterialsModel : public QAbstractListModel
{
Q_OBJECT
@@ -26,8 +21,6 @@ class ContentLibraryMaterialsModel : public QAbstractListModel
Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged)
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged)
- Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged)
- Q_PROPERTY(bool importerRunning MEMBER m_importerRunning NOTIFY importerRunningChanged)
public:
ContentLibraryMaterialsModel(ContentLibraryWidget *parent = nullptr);
@@ -38,40 +31,27 @@ public:
QHash<int, QByteArray> roleNames() const override;
void setSearchText(const QString &searchText);
- void updateImportedState(const QStringList &importedMats);
-
+ void updateImportedState(const QStringList &importedItems);
void setQuick3DImportVersion(int major, int minor);
bool hasRequiredQuick3DImport() const;
-
bool matBundleExists() const;
- bool hasModelSelection() const;
- void setHasModelSelection(bool b);
-
void resetModel();
void updateIsEmpty();
-
- Internal::ContentLibraryBundleImporter *bundleImporter() const;
+ void loadBundle();
Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
Q_INVOKABLE void addToProject(QmlDesigner::ContentLibraryMaterial *mat);
Q_INVOKABLE void removeFromProject(QmlDesigner::ContentLibraryMaterial *mat);
+ QString bundleId() const;
+
signals:
void isEmptyChanged();
void hasRequiredQuick3DImportChanged();
- void hasModelSelectionChanged();
void materialVisibleChanged();
void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
-#ifdef QDS_USE_PROJECTSTORAGE
- void bundleMaterialImported(const QmlDesigner::TypeName &typeName);
-#else
- void bundleMaterialImported(const QmlDesigner::NodeMetaInfo &metaInfo);
-#endif
- void bundleMaterialAboutToUnimport(const QmlDesigner::TypeName &type);
- void bundleMaterialUnimported(const QmlDesigner::NodeMetaInfo &metaInfo);
- void importerRunningChanged();
void matBundleExistsChanged();
private:
@@ -80,29 +60,22 @@ private:
bool fetchBundleMetadata(const QDir &bundleDir);
bool isValidIndex(int idx) const;
void downloadSharedFiles(const QDir &targetDir, const QStringList &files);
- void createImporter(const QString &bundlePath, const QString &bundleId,
- const QStringList &sharedFiles);
ContentLibraryWidget *m_widget = nullptr;
QString m_searchText;
+ QString m_bundleId;
+ QStringList m_bundleSharedFiles;
QList<ContentLibraryMaterialsCategory *> m_bundleCategories;
- QJsonObject m_matBundleObj;
- Internal::ContentLibraryBundleImporter *m_importer = nullptr;
+ QJsonObject m_bundleObj;
bool m_isEmpty = true;
- bool m_matBundleExists = false;
- bool m_hasModelSelection = false;
- bool m_importerRunning = false;
+ bool m_bundleExists = false;
int m_quick3dMajorVersion = -1;
int m_quick3dMinorVersion = -1;
QString m_downloadPath;
QString m_baseUrl;
-
- QString m_importerBundlePath;
- QString m_importerBundleId;
- QStringList m_importerSharedFiles;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
index 7ab239aab4..d2c2df2baa 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.cpp
@@ -12,20 +12,19 @@
namespace QmlDesigner {
ContentLibraryTexture::ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo,
- const QString &downloadPath, const QUrl &icon,
- const QString &key, const QString &webTextureUrl,
- const QString &webIconUrl, const QString &fileExt,
+ const QString &dirPath, const QString &suffix,
const QSize &dimensions, const qint64 sizeInBytes,
- bool hasUpdate, bool isNew)
+ const QString &key, const QString &textureUrl,
+ const QString &iconUrl, bool hasUpdate, bool isNew)
: QObject(parent)
, m_iconPath(iconFileInfo.filePath())
- , m_downloadPath(downloadPath)
- , m_webTextureUrl(webTextureUrl)
- , m_webIconUrl(webIconUrl)
+ , m_dirPath(dirPath)
+ , m_textureUrl(textureUrl)
+ , m_iconUrl(iconUrl)
, m_baseName{iconFileInfo.baseName()}
- , m_fileExt(fileExt)
+ , m_suffix(suffix)
, m_textureKey(key)
- , m_icon(icon)
+ , m_icon(QUrl::fromLocalFile(iconFileInfo.absoluteFilePath()))
, m_dimensions(dimensions)
, m_sizeInBytes(sizeInBytes)
, m_hasUpdate(hasUpdate)
@@ -54,9 +53,9 @@ QString ContentLibraryTexture::iconPath() const
return m_iconPath;
}
-QString ContentLibraryTexture::resolveFileExt()
+QString ContentLibraryTexture::resolveSuffix()
{
- const QFileInfoList files = QDir(m_downloadPath).entryInfoList(QDir::Files);
+ const QFileInfoList files = QDir(m_dirPath).entryInfoList(QDir::Files);
const QFileInfoList textureFiles = Utils::filtered(files, [this](const QFileInfo &fi) {
return fi.baseName() == m_baseName;
});
@@ -76,22 +75,20 @@ QString ContentLibraryTexture::resolveFileExt()
QString ContentLibraryTexture::resolveToolTipText()
{
- if (m_fileExt.isEmpty()) {
- // No supplied or resolved extension means we have just the icon and no other data
- return m_baseName;
- }
+ if (m_suffix.isEmpty())
+ return m_baseName; // empty suffix means we have just the icon and no other data
- QString fileName = m_baseName + m_fileExt;
+ QString fileName = m_baseName + m_suffix;
QString imageInfo;
if (!m_isDownloaded && m_sizeInBytes > 0 && !m_dimensions.isNull()) {
- imageInfo = ImageUtils::imageInfo(m_dimensions, m_sizeInBytes);
+ imageInfo = ImageUtils::imageInfoString(m_dimensions, m_sizeInBytes);
} else {
- QString fullDownloadPath = m_downloadPath + '/' + fileName;
- imageInfo = ImageUtils::imageInfo(fullDownloadPath);
+ QString fullDownloadPath = m_dirPath + '/' + fileName;
+ imageInfo = ImageUtils::imageInfoString(fullDownloadPath);
}
- return QStringLiteral("%1\n%2").arg(fileName, imageInfo);
+ return QString("%1\n%2").arg(fileName, imageInfo);
}
bool ContentLibraryTexture::isDownloaded() const
@@ -99,9 +96,9 @@ bool ContentLibraryTexture::isDownloaded() const
return m_isDownloaded;
}
-QString ContentLibraryTexture::downloadedTexturePath() const
+QString ContentLibraryTexture::texturePath() const
{
- return m_downloadPath + '/' + m_baseName + m_fileExt;
+ return m_dirPath + '/' + m_baseName + m_suffix;
}
void ContentLibraryTexture::setDownloaded()
@@ -116,16 +113,21 @@ void ContentLibraryTexture::setDownloaded()
void ContentLibraryTexture::doSetDownloaded()
{
- if (m_fileExt.isEmpty())
- m_fileExt = resolveFileExt();
+ if (m_suffix.isEmpty())
+ m_suffix = resolveSuffix();
- m_isDownloaded = QFileInfo::exists(downloadedTexturePath());
+ m_isDownloaded = QFileInfo::exists(texturePath());
m_toolTip = resolveToolTipText();
}
+bool ContentLibraryTexture::visible() const
+{
+ return m_visible;
+}
+
QString ContentLibraryTexture::parentDirPath() const
{
- return m_downloadPath;
+ return m_dirPath;
}
QString ContentLibraryTexture::textureKey() const
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
index 9f5b46630f..7f5db6d7d6 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexture.h
@@ -19,17 +19,18 @@ class ContentLibraryTexture : public QObject
Q_PROPERTY(QString textureToolTip MEMBER m_toolTip NOTIFY textureToolTipChanged)
Q_PROPERTY(QUrl textureIcon MEMBER m_icon CONSTANT)
Q_PROPERTY(bool textureVisible MEMBER m_visible NOTIFY textureVisibleChanged)
- Q_PROPERTY(QString textureWebUrl MEMBER m_webTextureUrl CONSTANT)
- Q_PROPERTY(QString textureWebIconUrl MEMBER m_webIconUrl CONSTANT)
+ Q_PROPERTY(QString textureUrl MEMBER m_textureUrl CONSTANT)
+ Q_PROPERTY(QString textureIconUrl MEMBER m_iconUrl CONSTANT)
Q_PROPERTY(bool textureHasUpdate WRITE setHasUpdate READ hasUpdate NOTIFY hasUpdateChanged)
Q_PROPERTY(bool textureIsNew MEMBER m_isNew CONSTANT)
Q_PROPERTY(QString textureKey MEMBER m_textureKey CONSTANT)
+ Q_PROPERTY(QString itemType MEMBER m_itemType CONSTANT)
public:
- ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, const QString &downloadPath,
- const QUrl &icon, const QString &key, const QString &webTextureUrl,
- const QString &webIconUrl, const QString &fileExt, const QSize &dimensions,
- const qint64 sizeInBytes, bool hasUpdate, bool isNew);
+ ContentLibraryTexture(QObject *parent, const QFileInfo &iconFileInfo, const QString &dirPath,
+ const QString &suffix, const QSize &dimensions, const qint64 sizeInBytes,
+ const QString &key = {}, const QString &textureUrl = {},
+ const QString &iconUrl = {}, bool hasUpdate = false, bool isNew = false);
Q_INVOKABLE bool isDownloaded() const;
Q_INVOKABLE void setDownloaded();
@@ -38,30 +39,32 @@ public:
QUrl icon() const;
QString iconPath() const;
- QString downloadedTexturePath() const;
+ QString texturePath() const;
QString parentDirPath() const;
QString textureKey() const;
void setHasUpdate(bool value);
bool hasUpdate() const;
+ bool visible() const;
+
signals:
void textureVisibleChanged();
void textureToolTipChanged();
void hasUpdateChanged();
private:
- QString resolveFileExt();
+ QString resolveSuffix();
QString resolveToolTipText();
void doSetDownloaded();
QString m_iconPath;
- QString m_downloadPath;
- QString m_webTextureUrl;
- QString m_webIconUrl;
+ QString m_dirPath;
+ QString m_textureUrl;
+ QString m_iconUrl;
QString m_toolTip;
QString m_baseName;
- QString m_fileExt;
+ QString m_suffix;
QString m_textureKey;
QUrl m_icon;
QSize m_dimensions;
@@ -71,6 +74,7 @@ private:
bool m_visible = true;
bool m_hasUpdate = false;
bool m_isNew = false;
+ const QString m_itemType = "texture";
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp
index 77519ad88f..0cafe8d138 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.cpp
@@ -14,17 +14,15 @@ namespace QmlDesigner {
ContentLibraryTexturesCategory::ContentLibraryTexturesCategory(QObject *parent, const QString &name)
: QObject(parent), m_name(name) {}
-void ContentLibraryTexturesCategory::addTexture(const QFileInfo &tex, const QString &downloadPath,
+void ContentLibraryTexturesCategory::addTexture(const QFileInfo &texIcon, const QString &downloadPath,
const QString &key, const QString &webTextureUrl,
- const QString &webIconUrl, const QString &fileExt,
+ const QString &iconUrl, const QString &suffix,
const QSize &dimensions, const qint64 sizeInBytes,
bool hasUpdate, bool isNew)
{
- QUrl icon = QUrl::fromLocalFile(tex.absoluteFilePath());
-
m_categoryTextures.append(new ContentLibraryTexture(
- this, tex, downloadPath, icon, key, webTextureUrl, webIconUrl,
- fileExt, dimensions, sizeInBytes, hasUpdate, isNew));
+ this, texIcon, downloadPath, suffix, dimensions, sizeInBytes,
+ key, webTextureUrl, iconUrl, hasUpdate, isNew));
}
bool ContentLibraryTexturesCategory::filter(const QString &searchText)
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h
index 166528f05a..857346df06 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturescategory.h
@@ -28,7 +28,7 @@ public:
ContentLibraryTexturesCategory(QObject *parent, const QString &name);
void addTexture(const QFileInfo &tex, const QString &subPath, const QString &key,
- const QString &webTextureUrl, const QString &webIconUrl, const QString &fileExt,
+ const QString &webTextureUrl, const QString &iconUrl, const QString &suffix,
const QSize &dimensions, const qint64 sizeInBytes, bool hasUpdate, bool isNew);
bool filter(const QString &searchText);
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp
index 319ca2686f..b575b6b9b2 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.cpp
@@ -95,37 +95,37 @@ QHash<int, QByteArray> ContentLibraryTexturesModel::roleNames() const
/**
* @brief Load the bundle categorized icons. Actual textures are downloaded on demand
*
- * @param bundlePath local path to the bundle folder and icons
- * @param metaData bundle textures metadata
+ * @param textureBundleUrl remote url to the texture bundle
+ * @param bundleIconPath local path to the texture bundle icons folder
+ * @param jsonData bundle textures information from the bundle json
*/
-void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, const QString &iconsUrl,
+void ContentLibraryTexturesModel::loadTextureBundle(const QString &textureBundleUrl,
const QString &bundleIconPath,
- const QVariantMap &metaData)
+ const QVariantMap &jsonData)
{
if (!m_bundleCategories.isEmpty())
return;
QDir bundleDir = QString("%1/%2").arg(bundleIconPath, m_category);
- if (!bundleDir.exists()) {
- qWarning() << __FUNCTION__ << "textures bundle folder doesn't exist." << bundleDir.absolutePath();
- return;
- }
+ QTC_ASSERT(bundleDir.exists(), return);
- const QVariantMap imageItems = metaData.value("image_items").toMap();
+ const QVariantMap imageItems = jsonData.value("image_items").toMap();
const QFileInfoList dirs = bundleDir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &dir : dirs) {
auto category = new ContentLibraryTexturesCategory(this, dir.fileName());
- const QFileInfoList texFiles = QDir(dir.filePath()).entryInfoList(QDir::Files);
- for (const QFileInfo &tex : texFiles) {
- QString textureUrl = QString("%1/%2/%3.zip").arg(remoteUrl, dir.fileName(), tex.baseName());
- QString iconUrl = QString("%1/%2/%3.png").arg(iconsUrl, dir.fileName(), tex.baseName());
-
- QString localDownloadPath = QString("%1/%2/%3")
+ const QFileInfoList texIconFiles = QDir(dir.filePath()).entryInfoList(QDir::Files);
+ for (const QFileInfo &texIcon : texIconFiles) {
+ QString textureUrl = QString("%1/%2/%3/%4.zip").arg(textureBundleUrl, m_category,
+ dir.fileName(), texIcon.baseName());
+ QString iconUrl = QString("%1/icons/%2/%3/%4.png").arg(textureBundleUrl, m_category,
+ dir.fileName(), texIcon.baseName());
+
+ QString texturePath = QString("%1/%2/%3")
.arg(Paths::bundlesPathSetting(),
m_category,
dir.fileName());
- QString key = QString("%1/%2/%3").arg(m_category, dir.fileName(), tex.baseName());
+ QString key = QString("%1/%2/%3").arg(m_category, dir.fileName(), texIcon.baseName());
QString fileExt;
QSize dimensions;
qint64 sizeInBytes = -1;
@@ -141,7 +141,7 @@ void ContentLibraryTexturesModel::loadTextureBundle(const QString &remoteUrl, co
isNew = m_newFiles.contains(key);
}
- category->addTexture(tex, localDownloadPath, key, textureUrl, iconUrl, fileExt,
+ category->addTexture(texIcon, texturePath, key, textureUrl, iconUrl, fileExt,
dimensions, sizeInBytes, hasUpdate, isNew);
}
m_bundleCategories.append(category);
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h
index 92db4151a8..94e223a251 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarytexturesmodel.h
@@ -37,8 +37,8 @@ public:
void setHasSceneEnv(bool b);
void resetModel();
- void loadTextureBundle(const QString &remoteUrl, const QString &iconsUrl,
- const QString &bundlePath, const QVariantMap &metaData);
+ void loadTextureBundle(const QString &textureBundleUrl, const QString &bundlePath,
+ const QVariantMap &metaData);
signals:
void isEmptyChanged();
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp
new file mode 100644
index 0000000000..8dcf4575f7
--- /dev/null
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.cpp
@@ -0,0 +1,686 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "contentlibraryusermodel.h"
+
+#include "contentlibrarybundleimporter.h"
+#include "contentlibraryitem.h"
+#include "contentlibrarymaterial.h"
+#include "contentlibrarymaterialscategory.h"
+#include "contentlibrarytexture.h"
+#include "contentlibrarywidget.h"
+
+#include <designerpaths.h>
+#include <imageutils.h>
+#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
+#include <uniquename.h>
+
+#include <utils/algorithm.h>
+#include <utils/qtcassert.h>
+
+#include <QFileInfo>
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QUrl>
+
+namespace QmlDesigner {
+
+ContentLibraryUserModel::ContentLibraryUserModel(ContentLibraryWidget *parent)
+ : QAbstractListModel(parent)
+ , m_widget(parent)
+{
+ m_userCategories = {tr("Materials"), tr("Textures"), tr("3D"), /*tr("Effects"), tr("2D components")*/}; // TODO
+}
+
+int ContentLibraryUserModel::rowCount(const QModelIndex &) const
+{
+ return m_userCategories.size();
+}
+
+QVariant ContentLibraryUserModel::data(const QModelIndex &index, int role) const
+{
+ QTC_ASSERT(index.isValid() && index.row() < m_userCategories.size(), return {});
+ QTC_ASSERT(roleNames().contains(role), return {});
+
+ if (role == NameRole)
+ return m_userCategories.at(index.row());
+
+ if (role == ItemsRole) {
+ if (index.row() == MaterialsSectionIdx)
+ return QVariant::fromValue(m_userMaterials);
+ if (index.row() == TexturesSectionIdx)
+ return QVariant::fromValue(m_userTextures);
+ if (index.row() == Items3DSectionIdx)
+ return QVariant::fromValue(m_user3DItems);
+ if (index.row() == EffectsSectionIdx)
+ return QVariant::fromValue(m_userEffects);
+ }
+
+ if (role == NoMatchRole) {
+ if (index.row() == MaterialsSectionIdx)
+ return m_noMatchMaterials;
+ if (index.row() == TexturesSectionIdx)
+ return m_noMatchTextures;
+ if (index.row() == Items3DSectionIdx)
+ return m_noMatch3D;
+ if (index.row() == EffectsSectionIdx)
+ return m_noMatchEffects;
+ }
+
+ if (role == VisibleRole) {
+ if (index.row() == MaterialsSectionIdx)
+ return !m_userMaterials.isEmpty();
+ if (index.row() == TexturesSectionIdx)
+ return !m_userTextures.isEmpty();
+ if (index.row() == Items3DSectionIdx)
+ return !m_user3DItems.isEmpty();
+ if (index.row() == EffectsSectionIdx)
+ return !m_userEffects.isEmpty();
+ }
+
+ return {};
+}
+
+bool ContentLibraryUserModel::isValidIndex(int idx) const
+{
+ return idx > -1 && idx < rowCount();
+}
+
+void ContentLibraryUserModel::updateNoMatchMaterials()
+{
+ m_noMatchMaterials = Utils::allOf(m_userMaterials, [&](ContentLibraryMaterial *item) {
+ return !item->visible();
+ });
+}
+
+void ContentLibraryUserModel::updateNoMatchTextures()
+{
+ m_noMatchTextures = Utils::allOf(m_userTextures, [&](ContentLibraryTexture *item) {
+ return !item->visible();
+ });
+}
+
+void ContentLibraryUserModel::updateNoMatch3D()
+{
+ m_noMatch3D = Utils::allOf(m_user3DItems, [&](ContentLibraryItem *item) {
+ return !item->visible();
+ });
+}
+
+void ContentLibraryUserModel::addMaterial(const QString &name, const QString &qml,
+ const QUrl &icon, const QStringList &files)
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ QString typePrefix = compUtils.userMaterialsBundleType();
+ TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
+
+ auto libMat = new ContentLibraryMaterial(this, name, qml, type, icon, files,
+ Paths::bundlesPathSetting().append("/User/materials"));
+ m_userMaterials.append(libMat);
+
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+}
+
+void ContentLibraryUserModel::add3DItem(const QString &name, const QString &qml,
+ const QUrl &icon, const QStringList &files)
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ QString typePrefix = compUtils.user3DBundleType();
+ TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
+
+ m_user3DItems.append(new ContentLibraryItem(this, name, qml, type, icon, files));
+}
+
+void ContentLibraryUserModel::refresh3DSection()
+{
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
+}
+
+void ContentLibraryUserModel::addTextures(const QStringList &paths)
+{
+ QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"};
+ bundleDir.mkpath(".");
+ bundleDir.mkdir("icons");
+
+ for (const QString &path : paths) {
+ QFileInfo fileInfo(path);
+ QString suffix = '.' + fileInfo.suffix();
+ auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png"));
+ QPair<QSize, qint64> info = ImageUtils::imageInfo(path);
+ QString dirPath = fileInfo.path();
+ QSize imgDims = info.first;
+ qint64 imgFileSize = info.second;
+
+ auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize);
+ m_userTextures.append(tex);
+ }
+
+ emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
+}
+
+void ContentLibraryUserModel::add3DInstance(ContentLibraryItem *bundleItem)
+{
+ QString err = m_widget->importer()->importComponent(m_bundlePath3D.path(), bundleItem->type(),
+ bundleItem->qml(),
+ bundleItem->files() + m_bundle3DSharedFiles);
+
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
+ qWarning() << __FUNCTION__ << err;
+}
+
+void ContentLibraryUserModel::removeTexture(ContentLibraryTexture *tex)
+{
+ // remove resources
+ Utils::FilePath::fromString(tex->texturePath()).removeFile();
+ Utils::FilePath::fromString(tex->iconPath()).removeFile();
+
+ // remove from model
+ m_userTextures.removeOne(tex);
+ tex->deleteLater();
+
+ // update model
+ emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
+}
+
+void ContentLibraryUserModel::removeFromContentLib(QObject *item)
+{
+ if (auto mat = qobject_cast<ContentLibraryMaterial *>(item))
+ removeMaterialFromContentLib(mat);
+ else if (auto itm = qobject_cast<ContentLibraryItem *>(item))
+ remove3DFromContentLib(itm);
+}
+
+void ContentLibraryUserModel::removeMaterialFromContentLib(ContentLibraryMaterial *item)
+{
+ auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/");
+
+ QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray();
+
+ // remove qml and icon files
+ Utils::FilePath::fromString(item->qmlFilePath()).removeFile();
+ Utils::FilePath::fromUrl(item->icon()).removeFile();
+
+ // remove from the bundle json file
+ for (int i = 0; i < itemsArr.size(); ++i) {
+ if (itemsArr.at(i).toObject().value("qml") == item->qml()) {
+ itemsArr.removeAt(i);
+ break;
+ }
+ }
+ m_bundleObjMaterial.insert("items", itemsArr);
+
+ auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME)
+ .writeFileContents(QJsonDocument(m_bundleObjMaterial).toJson());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ // delete dependency files if they are only used by the deleted material
+ QStringList allFiles;
+ for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr))
+ allFiles.append(itemRef.toObject().value("files").toVariant().toStringList());
+
+ const QStringList itemFiles = item->files();
+ for (const QString &file : itemFiles) {
+ if (allFiles.count(file) == 0) // only used by the deleted item
+ bundlePath.pathAppended(file).removeFile();
+ }
+
+ // remove from model
+ m_userMaterials.removeOne(item);
+ item->deleteLater();
+
+ // update model
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+}
+
+void ContentLibraryUserModel::remove3DFromContentLibByName(const QString &qmlFileName)
+{
+ ContentLibraryItem *itemToRemove = Utils::findOr(m_user3DItems, nullptr,
+ [&qmlFileName](ContentLibraryItem *item) {
+ return item->qml() == qmlFileName;
+ });
+
+ if (itemToRemove)
+ remove3DFromContentLib(itemToRemove);
+}
+
+void ContentLibraryUserModel::remove3DFromContentLib(ContentLibraryItem *item)
+{
+ QJsonArray itemsArr = m_bundleObj3D.value("items").toArray();
+
+ // remove qml and icon files
+ m_bundlePath3D.pathAppended(item->qml()).removeFile();
+ Utils::FilePath::fromUrl(item->icon()).removeFile();
+
+ // remove from the bundle json file
+ for (int i = 0; i < itemsArr.size(); ++i) {
+ if (itemsArr.at(i).toObject().value("qml") == item->qml()) {
+ itemsArr.removeAt(i);
+ break;
+ }
+ }
+ m_bundleObj3D.insert("items", itemsArr);
+
+ auto result = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME)
+ .writeFileContents(QJsonDocument(m_bundleObj3D).toJson());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ // delete dependency files if they are only used by the deleted item
+ QStringList allFiles;
+ for (const QJsonValueConstRef &itemRef : std::as_const(itemsArr))
+ allFiles.append(itemRef.toObject().value("files").toVariant().toStringList());
+
+ const QStringList itemFiles = item->files();
+ for (const QString &file : itemFiles) {
+ if (allFiles.count(file) == 0) // only used by the deleted item
+ m_bundlePath3D.pathAppended(file).removeFile();
+ }
+
+ // remove from model
+ m_user3DItems.removeOne(item);
+ item->deleteLater();
+
+ // update model
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
+}
+
+/**
+ * @brief Gets unique Qml component and icon file material names from a given name
+ * @param defaultName input name
+ * @return <Qml, icon> file names
+ */
+QPair<QString, QString> ContentLibraryUserModel::getUniqueLibMaterialNames(const QString &defaultName) const
+{
+ return getUniqueLibItemNames(defaultName, m_bundleObjMaterial);
+}
+
+/**
+ * @brief Gets unique Qml component and icon file 3d item names from a given name
+ * @param defaultName input name
+ * @return <Qml, icon> file names
+ */
+QPair<QString, QString> ContentLibraryUserModel::getUniqueLib3DNames(const QString &defaultName) const
+{
+ return getUniqueLibItemNames(defaultName, m_bundleObj3D);
+}
+
+QPair<QString, QString> ContentLibraryUserModel::getUniqueLibItemNames(const QString &defaultName,
+ const QJsonObject &bundleObj) const
+{
+ QTC_ASSERT(!bundleObj.isEmpty(), return {});
+
+ const QJsonArray itemsArr = bundleObj.value("items").toArray();
+
+ QStringList itemQmls, itemIcons;
+ for (const QJsonValueConstRef &itemRef : itemsArr) {
+ const QJsonObject &obj = itemRef.toObject();
+ itemQmls.append(obj.value("qml").toString().chopped(4)); // remove .qml
+ itemIcons.append(QFileInfo(obj.value("icon").toString()).baseName());
+ }
+
+ QString baseQml = UniqueName::generateId(defaultName);
+ baseQml[0] = baseQml.at(0).toUpper();
+ baseQml.prepend("My");
+
+ QString uniqueQml = UniqueName::generate(baseQml, [&] (const QString &name) {
+ return itemQmls.contains(name);
+ });
+
+ QString uniqueIcon = UniqueName::generate(defaultName, [&] (const QString &name) {
+ return itemIcons.contains(name);
+ });
+
+ return {uniqueQml + ".qml", uniqueIcon + ".png"};
+}
+
+QHash<int, QByteArray> ContentLibraryUserModel::roleNames() const
+{
+ static const QHash<int, QByteArray> roles {
+ {NameRole, "categoryName"},
+ {VisibleRole, "categoryVisible"},
+ {ItemsRole, "categoryItems"},
+ {NoMatchRole, "categoryNoMatch"}
+ };
+ return roles;
+}
+
+QJsonObject &ContentLibraryUserModel::bundleJsonMaterialObjectRef()
+{
+ return m_bundleObjMaterial;
+}
+
+QJsonObject &ContentLibraryUserModel::bundleJson3DObjectRef()
+{
+ return m_bundleObj3D;
+}
+
+void ContentLibraryUserModel::loadBundles()
+{
+ loadMaterialBundle();
+ load3DBundle();
+ loadTextureBundle();
+}
+
+void ContentLibraryUserModel::loadMaterialBundle()
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ if (m_matBundleExists && m_bundleIdMaterial == compUtils.userMaterialsBundleId())
+ return;
+
+ // clean up
+ qDeleteAll(m_userMaterials);
+ m_userMaterials.clear();
+ m_matBundleExists = false;
+ m_noMatchMaterials = true;
+ m_bundleObjMaterial = {};
+ m_bundleIdMaterial.clear();
+
+ m_bundlePathMaterial = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials");
+ m_bundlePathMaterial.ensureWritableDir();
+ m_bundlePathMaterial.pathAppended("icons").ensureWritableDir();
+
+ auto jsonFilePath = m_bundlePathMaterial.pathAppended(Constants::BUNDLE_JSON_FILENAME);
+ if (!jsonFilePath.exists()) {
+ QString jsonContent = "{\n";
+ jsonContent += " \"id\": \"UserMaterials\",\n";
+ jsonContent += " \"items\": []\n";
+ jsonContent += "}";
+ Utils::expected_str<qint64> res = jsonFilePath.writeFileContents(jsonContent.toLatin1());
+ if (!res.has_value()) {
+ qWarning() << __FUNCTION__ << res.error();
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+ return;
+ }
+ }
+
+ Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents();
+ if (!jsonContents.has_value()) {
+ qWarning() << __FUNCTION__ << jsonContents.error();
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+ return;
+ }
+
+ QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value());
+ if (bundleJsonDoc.isNull()) {
+ qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath;
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+ return;
+ }
+
+ m_bundleIdMaterial = compUtils.userMaterialsBundleId();
+ m_bundleObjMaterial = bundleJsonDoc.object();
+ m_bundleObjMaterial["id"] = m_bundleIdMaterial;
+
+ // parse items
+ QString typePrefix = compUtils.userMaterialsBundleType();
+ const QJsonArray itemsArr = m_bundleObjMaterial.value("items").toArray();
+ for (const QJsonValueConstRef &itemRef : itemsArr) {
+ const QJsonObject itemObj = itemRef.toObject();
+
+ QString name = itemObj.value("name").toString();
+ QString qml = itemObj.value("qml").toString();
+ TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
+ QUrl icon = m_bundlePathMaterial.pathAppended(itemObj.value("icon").toString()).toUrl();
+ QStringList files;
+ const QJsonArray assetsArr = itemObj.value("files").toArray();
+ for (const QJsonValueConstRef &asset : assetsArr)
+ files.append(asset.toString());
+
+ m_userMaterials.append(new ContentLibraryMaterial(this, name, qml, type, icon, files,
+ m_bundlePathMaterial.path(), ""));
+ }
+
+ m_bundleMaterialSharedFiles.clear();
+ const QJsonArray sharedFilesArr = m_bundleObjMaterial.value("sharedFiles").toArray();
+ for (const QJsonValueConstRef &file : sharedFilesArr)
+ m_bundleMaterialSharedFiles.append(file.toString());
+
+ m_matBundleExists = true;
+ updateNoMatchMaterials();
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+}
+
+void ContentLibraryUserModel::load3DBundle()
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ if (m_bundle3DExists && m_bundleId3D == compUtils.user3DBundleId())
+ return;
+
+ // clean up
+ qDeleteAll(m_user3DItems);
+ m_user3DItems.clear();
+ m_bundle3DExists = false;
+ m_noMatch3D = true;
+ m_bundleObj3D = {};
+ m_bundleId3D.clear();
+
+ m_bundlePath3D = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d");
+ m_bundlePath3D.ensureWritableDir();
+ m_bundlePath3D.pathAppended("icons").ensureWritableDir();
+
+ auto jsonFilePath = m_bundlePath3D.pathAppended(Constants::BUNDLE_JSON_FILENAME);
+ if (!jsonFilePath.exists()) {
+ QByteArray jsonContent = "{\n";
+ jsonContent += " \"id\": \"User3D\",\n";
+ jsonContent += " \"items\": []\n";
+ jsonContent += "}";
+ Utils::expected_str<qint64> res = jsonFilePath.writeFileContents(jsonContent);
+ if (!res.has_value()) {
+ qWarning() << __FUNCTION__ << res.error();
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
+ return;
+ }
+ }
+
+ Utils::expected_str<QByteArray> jsonContents = jsonFilePath.fileContents();
+ if (!jsonContents.has_value()) {
+ qWarning() << __FUNCTION__ << jsonContents.error();
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
+ return;
+ }
+
+ QJsonDocument bundleJsonDoc = QJsonDocument::fromJson(jsonContents.value());
+ if (bundleJsonDoc.isNull()) {
+ qWarning() << __FUNCTION__ << "Invalid json file" << jsonFilePath;
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
+ return;
+ }
+
+ m_bundleId3D = compUtils.user3DBundleId();
+ m_bundleObj3D = bundleJsonDoc.object();
+ m_bundleObj3D["id"] = m_bundleId3D;
+
+ // parse items
+ QString typePrefix = compUtils.user3DBundleType();
+ const QJsonArray itemsArr = m_bundleObj3D.value("items").toArray();
+ for (const QJsonValueConstRef &itemRef : itemsArr) {
+ const QJsonObject itemObj = itemRef.toObject();
+
+ QString name = itemObj.value("name").toString();
+ QString qml = itemObj.value("qml").toString();
+ TypeName type = QLatin1String("%1.%2").arg(typePrefix, qml.chopped(4)).toLatin1();
+ QUrl icon = m_bundlePath3D.pathAppended(itemObj.value("icon").toString()).toUrl();
+ QStringList files;
+ const QJsonArray assetsArr = itemObj.value("files").toArray();
+ for (const QJsonValueConstRef &asset : assetsArr)
+ files.append(asset.toString());
+
+ m_user3DItems.append(new ContentLibraryItem(nullptr, name, qml, type, icon, files));
+ }
+
+ m_bundle3DSharedFiles.clear();
+ const QJsonArray sharedFilesArr = m_bundleObj3D.value("sharedFiles").toArray();
+ for (const QJsonValueConstRef &file : sharedFilesArr)
+ m_bundle3DSharedFiles.append(file.toString());
+
+ m_bundle3DExists = true;
+ updateNoMatch3D();
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
+}
+
+void ContentLibraryUserModel::loadTextureBundle()
+{
+ if (!m_userTextures.isEmpty())
+ return;
+
+ QDir bundleDir{Paths::bundlesPathSetting() + "/User/textures"};
+ bundleDir.mkpath(".");
+ bundleDir.mkdir("icons");
+
+ const QFileInfoList fileInfos = bundleDir.entryInfoList(QDir::Files);
+ for (const QFileInfo &fileInfo : fileInfos) {
+ QString suffix = '.' + fileInfo.suffix();
+ auto iconFileInfo = QFileInfo(fileInfo.path().append("/icons/").append(fileInfo.baseName() + ".png"));
+ QPair<QSize, qint64> info = ImageUtils::imageInfo(fileInfo.path());
+ QString dirPath = fileInfo.path();
+ QSize imgDims = info.first;
+ qint64 imgFileSize = info.second;
+
+ auto tex = new ContentLibraryTexture(this, iconFileInfo, dirPath, suffix, imgDims, imgFileSize);
+ m_userTextures.append(tex);
+ }
+
+ updateNoMatchTextures();
+ emit dataChanged(index(TexturesSectionIdx), index(TexturesSectionIdx));
+}
+
+bool ContentLibraryUserModel::hasRequiredQuick3DImport() const
+{
+ return m_widget->hasQuick3DImport() && m_quick3dMajorVersion == 6 && m_quick3dMinorVersion >= 3;
+}
+
+bool ContentLibraryUserModel::matBundleExists() const
+{
+ return m_matBundleExists;
+}
+
+void ContentLibraryUserModel::setSearchText(const QString &searchText)
+{
+ QString lowerSearchText = searchText.toLower();
+
+ if (m_searchText == lowerSearchText)
+ return;
+
+ m_searchText = lowerSearchText;
+
+ for (ContentLibraryMaterial *item : std::as_const(m_userMaterials))
+ item->filter(m_searchText);
+
+ for (ContentLibraryTexture *item : std::as_const(m_userTextures))
+ item->filter(m_searchText);
+
+ for (ContentLibraryItem *item : std::as_const(m_user3DItems))
+ item->filter(m_searchText);
+
+ updateNoMatchMaterials();
+ updateNoMatchTextures();
+ updateNoMatch3D();
+ resetModel();
+}
+
+void ContentLibraryUserModel::updateMaterialsImportedState(const QStringList &importedItems)
+{
+ bool changed = false;
+ for (ContentLibraryMaterial *mat : std::as_const(m_userMaterials))
+ changed |= mat->setImported(importedItems.contains(mat->qml().chopped(4)));
+
+ if (changed)
+ emit dataChanged(index(MaterialsSectionIdx), index(MaterialsSectionIdx));
+}
+
+void ContentLibraryUserModel::update3DImportedState(const QStringList &importedItems)
+{
+ bool changed = false;
+ for (ContentLibraryItem *item : std::as_const(m_user3DItems))
+ changed |= item->setImported(importedItems.contains(item->qml().chopped(4)));
+
+ if (changed)
+ emit dataChanged(index(Items3DSectionIdx), index(Items3DSectionIdx));
+}
+
+void ContentLibraryUserModel::setQuick3DImportVersion(int major, int minor)
+{
+ bool oldRequiredImport = hasRequiredQuick3DImport();
+
+ m_quick3dMajorVersion = major;
+ m_quick3dMinorVersion = minor;
+
+ bool newRequiredImport = hasRequiredQuick3DImport();
+
+ if (oldRequiredImport == newRequiredImport)
+ return;
+
+ emit hasRequiredQuick3DImportChanged();
+}
+
+void ContentLibraryUserModel::resetModel()
+{
+ beginResetModel();
+ endResetModel();
+}
+
+void ContentLibraryUserModel::applyToSelected(ContentLibraryMaterial *mat, bool add)
+{
+ emit applyToSelectedTriggered(mat, add);
+}
+
+void ContentLibraryUserModel::addToProject(QObject *item)
+{
+ QString bundleDir;
+ TypeName type;
+ QString qmlFile;
+ QStringList files;
+ if (auto mat = qobject_cast<ContentLibraryMaterial *>(item)) {
+ bundleDir = mat->dirPath();
+ type = mat->type();
+ qmlFile = mat->qml();
+ files = mat->files() + m_bundleMaterialSharedFiles;
+ } else if (auto itm = qobject_cast<ContentLibraryItem *>(item)) {
+ bundleDir = m_bundlePath3D.toString();
+ type = itm->type();
+ qmlFile = itm->qml();
+ files = itm->files() + m_bundle3DSharedFiles;
+ } else {
+ qWarning() << __FUNCTION__ << "Unsupported Item";
+ return;
+ }
+
+ QString err = m_widget->importer()->importComponent(bundleDir, type, qmlFile, files);
+
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
+ qWarning() << __FUNCTION__ << err;
+}
+
+void ContentLibraryUserModel::removeFromProject(QObject *item)
+{
+ TypeName type;
+ QString qml;
+ if (auto mat = qobject_cast<ContentLibraryMaterial *>(item)) {
+ type = mat->type();
+ qml = mat->qml();
+ } else if (auto itm = qobject_cast<ContentLibraryItem *>(item)) {
+ type = itm->type();
+ qml = itm->qml();
+ } else {
+ qWarning() << __FUNCTION__ << "Unsupported Item";
+ return;
+ }
+
+ QString err = m_widget->importer()->unimportComponent(type, qml);
+
+ if (err.isEmpty())
+ m_widget->setImporterRunning(true);
+ else
+ qWarning() << __FUNCTION__ << err;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h
new file mode 100644
index 0000000000..2a7f9a66f3
--- /dev/null
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryusermodel.h
@@ -0,0 +1,135 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <utils/filepath.h>
+
+#include <QAbstractListModel>
+#include <QJsonObject>
+
+QT_FORWARD_DECLARE_CLASS(QUrl)
+
+namespace QmlDesigner {
+
+class ContentLibraryItem;
+class ContentLibraryMaterial;
+class ContentLibraryTexture;
+class ContentLibraryWidget;
+class NodeMetaInfo;
+
+class ContentLibraryUserModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool matBundleExists READ matBundleExists NOTIFY matBundleExistsChanged)
+ Q_PROPERTY(bool bundle3DExists MEMBER m_bundle3DExists NOTIFY bundle3DExistsChanged)
+ Q_PROPERTY(bool hasRequiredQuick3DImport READ hasRequiredQuick3DImport NOTIFY hasRequiredQuick3DImportChanged)
+ Q_PROPERTY(QList<ContentLibraryMaterial *> userMaterials MEMBER m_userMaterials NOTIFY userMaterialsChanged)
+ Q_PROPERTY(QList<ContentLibraryTexture *> userTextures MEMBER m_userTextures NOTIFY userTexturesChanged)
+ Q_PROPERTY(QList<ContentLibraryItem *> user3DItems MEMBER m_user3DItems NOTIFY user3DItemsChanged)
+ Q_PROPERTY(QList<ContentLibraryItem *> userEffects MEMBER m_userEffects NOTIFY userEffectsChanged)
+
+public:
+ ContentLibraryUserModel(ContentLibraryWidget *parent = nullptr);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
+ QHash<int, QByteArray> roleNames() const override;
+
+ void setSearchText(const QString &searchText);
+ void updateMaterialsImportedState(const QStringList &importedItems);
+ void update3DImportedState(const QStringList &importedItems);
+
+ QPair<QString, QString> getUniqueLibMaterialNames(const QString &defaultName = "Material") const;
+ QPair<QString, QString> getUniqueLib3DNames(const QString &defaultName = "Item") const;
+
+ void setQuick3DImportVersion(int major, int minor);
+
+ bool hasRequiredQuick3DImport() const;
+
+ bool matBundleExists() const;
+
+ void resetModel();
+ void updateNoMatchMaterials();
+ void updateNoMatchTextures();
+ void updateNoMatch3D();
+
+ void addMaterial(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files);
+ void add3DItem(const QString &name, const QString &qml, const QUrl &icon, const QStringList &files);
+ void refresh3DSection();
+ void addTextures(const QStringList &paths);
+
+ void add3DInstance(ContentLibraryItem *bundleItem);
+
+ void remove3DFromContentLibByName(const QString &qmlFileName);
+
+ void setBundleObj(const QJsonObject &newBundleObj);
+ QJsonObject &bundleJsonMaterialObjectRef();
+ QJsonObject &bundleJson3DObjectRef();
+
+ void loadBundles();
+
+ Q_INVOKABLE void applyToSelected(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
+ Q_INVOKABLE void addToProject(QObject *item);
+ Q_INVOKABLE void removeFromProject(QObject *item);
+ Q_INVOKABLE void removeTexture(QmlDesigner::ContentLibraryTexture *tex);
+ Q_INVOKABLE void removeFromContentLib(QObject *item);
+
+signals:
+ void hasRequiredQuick3DImportChanged();
+ void userMaterialsChanged();
+ void userTexturesChanged();
+ void user3DItemsChanged();
+ void userEffectsChanged();
+ void applyToSelectedTriggered(QmlDesigner::ContentLibraryMaterial *mat, bool add = false);
+ void matBundleExistsChanged();
+ void bundle3DExistsChanged();
+
+private:
+ enum SectionIndex { MaterialsSectionIdx = 0,
+ TexturesSectionIdx,
+ Items3DSectionIdx,
+ EffectsSectionIdx };
+
+ void loadMaterialBundle();
+ void load3DBundle();
+ void loadTextureBundle();
+ bool isValidIndex(int idx) const;
+ void removeMaterialFromContentLib(ContentLibraryMaterial *mat);
+ void remove3DFromContentLib(ContentLibraryItem *item);
+ QPair<QString, QString> getUniqueLibItemNames(const QString &defaultName,
+ const QJsonObject &bundleObj) const;
+
+ ContentLibraryWidget *m_widget = nullptr;
+ QString m_searchText;
+ QString m_bundleIdMaterial;
+ QString m_bundleId3D;
+ QStringList m_bundleMaterialSharedFiles;
+ QStringList m_bundle3DSharedFiles;
+ Utils::FilePath m_bundlePathMaterial;
+ Utils::FilePath m_bundlePath3D;
+
+ QList<ContentLibraryMaterial *> m_userMaterials;
+ QList<ContentLibraryTexture *> m_userTextures;
+ QList<ContentLibraryItem *> m_userEffects;
+ QList<ContentLibraryItem *> m_user3DItems;
+ QStringList m_userCategories;
+
+ QJsonObject m_bundleObjMaterial;
+ QJsonObject m_bundleObj3D;
+
+ bool m_noMatchMaterials = true;
+ bool m_noMatchTextures = true;
+ bool m_noMatch3D = true;
+ bool m_noMatchEffects = true;
+ bool m_matBundleExists = false;
+ bool m_bundle3DExists = false;
+
+ int m_quick3dMajorVersion = -1;
+ int m_quick3dMinorVersion = -1;
+
+ enum Roles { NameRole = Qt::UserRole + 1, VisibleRole, ItemsRole, NoMatchRole };
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
index 61ae078ea8..9258faaf33 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.cpp
@@ -4,22 +4,29 @@
#include "contentlibraryview.h"
#include "contentlibrarybundleimporter.h"
-#include "contentlibraryeffect.h"
+#include "contentlibraryitem.h"
#include "contentlibraryeffectsmodel.h"
#include "contentlibrarymaterial.h"
#include "contentlibrarymaterialsmodel.h"
#include "contentlibrarytexture.h"
#include "contentlibrarytexturesmodel.h"
+#include "contentlibraryusermodel.h"
#include "contentlibrarywidget.h"
-#include "externaldependenciesinterface.h"
-#include "nodelistproperty.h"
-#include "qmldesignerconstants.h"
-#include "qmlobjectnode.h"
-#include "variantproperty.h"
-#include <utils3d.h>
-#include <coreplugin/messagebox.h>
+#include <asset.h>
+#include <bindingproperty.h>
+#include <designerpaths.h>
+#include <documentmanager.h>
#include <enumeration.h>
+#include <externaldependenciesinterface.h>
+#include <nodelistproperty.h>
+#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
+#include <qmlobjectnode.h>
+#include <uniquename.h>
+#include <utils3d.h>
+#include <variantproperty.h>
+
#include <utils/algorithm.h>
#ifndef QMLDESIGNER_TEST
@@ -30,12 +37,19 @@
#include <qtsupport/qtkitaspect.h>
#endif
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QMessageBox>
+#include <QPixmap>
#include <QVector3D>
namespace QmlDesigner {
-ContentLibraryView::ContentLibraryView(ExternalDependenciesInterface &externalDependencies)
+ContentLibraryView::ContentLibraryView(AsynchronousImageCache &imageCache,
+ ExternalDependenciesInterface &externalDependencies)
: AbstractView(externalDependencies)
+ , m_imageCache(imageCache)
, m_createTexture(this)
{}
@@ -60,9 +74,9 @@ WidgetInfo ContentLibraryView::widgetInfo()
[&] (QmlDesigner::ContentLibraryTexture *tex) {
m_draggedBundleTexture = tex;
});
- connect(m_widget, &ContentLibraryWidget::bundleEffectDragStarted, this,
- [&] (QmlDesigner::ContentLibraryEffect *eff) {
- m_draggedBundleEffect = eff;
+ connect(m_widget, &ContentLibraryWidget::bundleItemDragStarted, this,
+ [&] (QmlDesigner::ContentLibraryItem *item) {
+ m_draggedBundleItem = item;
});
connect(m_widget, &ContentLibraryWidget::addTextureRequested, this,
@@ -79,138 +93,150 @@ WidgetInfo ContentLibraryView::widgetInfo()
m_widget->environmentsModel()->setHasSceneEnv(sceneEnvExists);
});
- ContentLibraryMaterialsModel *materialsModel = m_widget->materialsModel().data();
-
- connect(materialsModel,
+ connect(m_widget->materialsModel(),
&ContentLibraryMaterialsModel::applyToSelectedTriggered,
this,
[&](ContentLibraryMaterial *bundleMat, bool add) {
- if (m_selectedModels.isEmpty())
- return;
+ if (m_selectedModels.isEmpty())
+ return;
- m_bundleMaterialTargets = m_selectedModels;
- m_bundleMaterialAddToSelected = add;
+ m_bundleMaterialTargets = m_selectedModels;
+ m_bundleMaterialAddToSelected = add;
- ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type());
- if (defaultMat.isValid())
- applyBundleMaterialToDropTarget(defaultMat);
- else
- m_widget->materialsModel()->addToProject(bundleMat);
- });
+ ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type());
+ if (defaultMat.isValid())
+ applyBundleMaterialToDropTarget(defaultMat);
+ else
+ m_widget->materialsModel()->addToProject(bundleMat);
+ });
-#ifdef QDS_USE_PROJECTSTORAGE
- connect(materialsModel,
- &ContentLibraryMaterialsModel::bundleMaterialImported,
- this,
- [&](const QmlDesigner::TypeName &typeName) {
- applyBundleMaterialToDropTarget({}, typeName);
- updateBundleMaterialsImportedState();
- });
-#else
- connect(materialsModel,
- &ContentLibraryMaterialsModel::bundleMaterialImported,
+ connect(m_widget->userModel(),
+ &ContentLibraryUserModel::applyToSelectedTriggered,
this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- applyBundleMaterialToDropTarget({}, metaInfo);
- updateBundleMaterialsImportedState();
- });
-#endif
+ [&](ContentLibraryMaterial *bundleMat, bool add) {
+ if (m_selectedModels.isEmpty())
+ return;
- connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialAboutToUnimport, this,
- [&] (const QmlDesigner::TypeName &type) {
- // delete instances of the bundle material that is about to be unimported
- executeInTransaction("ContentLibraryView::widgetInfo", [&] {
- ModelNode matLib = Utils3D::materialLibraryNode(this);
- if (!matLib.isValid())
- return;
+ m_bundleMaterialTargets = m_selectedModels;
+ m_bundleMaterialAddToSelected = add;
- Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) {
- if (mat.isValid() && mat.type() == type)
- QmlObjectNode(mat).destroy();
- });
- });
+ ModelNode defaultMat = getBundleMaterialDefaultInstance(bundleMat->type());
+ if (defaultMat.isValid())
+ applyBundleMaterialToDropTarget(defaultMat);
+ else
+ m_widget->userModel()->addToProject(bundleMat);
});
- connect(materialsModel, &ContentLibraryMaterialsModel::bundleMaterialUnimported, this,
- &ContentLibraryView::updateBundleMaterialsImportedState);
+ connectImporter();
+ }
- ContentLibraryEffectsModel *effectsModel = m_widget->effectsModel().data();
+ return createWidgetInfo(m_widget.data(),
+ "ContentLibrary",
+ WidgetInfo::LeftPane,
+ 0,
+ tr("Content Library"));
+}
+void ContentLibraryView::connectImporter()
+{
#ifdef QDS_USE_PROJECTSTORAGE
- connect(effectsModel,
- &ContentLibraryEffectsModel::bundleItemImported,
- this,
- [&](const QmlDesigner::TypeName &typeName) {
- QTC_ASSERT(typeName.size(), return);
-
- if (!m_bundleEffectTarget)
- m_bundleEffectTarget = Utils3D::active3DSceneNode(this);
+ connect(m_widget->importer(),
+ &ContentLibraryBundleImporter::importFinished,
+ this,
+ [&](const QmlDesigner::TypeName &typeName, const QString &bundleId) {
+ QTC_ASSERT(typeName.size(), return);
+ if (isMaterialBundle(bundleId)) {
+ applyBundleMaterialToDropTarget({}, typeName);
+ } else if (isItemBundle(bundleId)) {
+ if (!m_bundleItemTarget)
+ m_bundleItemTarget = Utils3D::active3DSceneNode(this);
- QTC_ASSERT(m_bundleEffectTarget, return);
+ QTC_ASSERT(m_bundleItemTarget, return);
executeInTransaction("ContentLibraryView::widgetInfo", [&] {
- QVector3D pos = m_bundleEffectPos.value<QVector3D>();
- ModelNode newEffNode = createModelNode(
+ QVector3D pos = m_bundleItemPos.value<QVector3D>();
+ ModelNode newNode = createModelNode(
typeName, -1, -1, {{"x", pos.x()}, {"y", pos.y()}, {"z", pos.z()}});
- m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode);
+ m_bundleItemTarget.defaultNodeListProperty().reparentHere(newNode);
clearSelectedModelNodes();
- selectModelNode(newEffNode);
+ selectModelNode(newNode);
});
- updateBundleEffectsImportedState();
- m_bundleEffectTarget = {};
- m_bundleEffectPos = {};
- });
+ m_bundleItemTarget = {};
+ m_bundleItemPos = {};
+ }
+ });
#else
- connect(effectsModel,
- &ContentLibraryEffectsModel::bundleItemImported,
- this,
- [&](const QmlDesigner::NodeMetaInfo &metaInfo) {
- QTC_ASSERT(metaInfo.isValid(), return);
-
- if (!m_bundleEffectTarget)
- m_bundleEffectTarget = Utils3D::active3DSceneNode(this);
+ connect(m_widget->importer(),
+ &ContentLibraryBundleImporter::importFinished,
+ this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) {
+ QTC_ASSERT(metaInfo.isValid(), return);
+ if (isMaterialBundle(bundleId)) {
+ applyBundleMaterialToDropTarget({}, metaInfo);
+ } else if (isItemBundle(bundleId)) {
+ if (!m_bundleItemTarget)
+ m_bundleItemTarget = Utils3D::active3DSceneNode(this);
- QTC_ASSERT(m_bundleEffectTarget, return);
+ QTC_ASSERT(m_bundleItemTarget, return);
- executeInTransaction("ContentLibraryView::widgetInfo", [&] {
- QVector3D pos = m_bundleEffectPos.value<QVector3D>();
- ModelNode newEffNode = createModelNode(metaInfo.typeName(),
+ executeInTransaction("ContentLibraryView::connectImporter", [&] {
+ QVector3D pos = m_bundleItemPos.value<QVector3D>();
+ ModelNode newNode = createModelNode(metaInfo.typeName(),
metaInfo.majorVersion(),
metaInfo.minorVersion(),
{{"x", pos.x()},
{"y", pos.y()},
{"z", pos.z()}});
- m_bundleEffectTarget.defaultNodeListProperty().reparentHere(newEffNode);
+ m_bundleItemTarget.defaultNodeListProperty().reparentHere(newNode);
clearSelectedModelNodes();
- selectModelNode(newEffNode);
+ selectModelNode(newNode);
});
- updateBundleEffectsImportedState();
- m_bundleEffectTarget = {};
- m_bundleEffectPos = {};
- });
+ m_bundleItemTarget = {};
+ m_bundleItemPos = {};
+ }
+ });
#endif
- connect(effectsModel, &ContentLibraryEffectsModel::bundleItemAboutToUnimport, this,
- [&] (const QmlDesigner::TypeName &type) {
- // delete instances of the bundle effect that is about to be unimported
- executeInTransaction("ContentLibraryView::widgetInfo", [&] {
- NodeMetaInfo metaInfo = model()->metaInfo(type);
- QList<ModelNode> effects = allModelNodesOfType(metaInfo);
- for (ModelNode &eff : effects)
- eff.destroy();
- });
+
+ connect(m_widget->importer(), &ContentLibraryBundleImporter::aboutToUnimport, this,
+ [&] (const QmlDesigner::TypeName &type, const QString &bundleId) {
+ if (isMaterialBundle(bundleId)) {
+ // delete instances of the bundle material that is about to be unimported
+ executeInTransaction("ContentLibraryView::connectImporter", [&] {
+ ModelNode matLib = Utils3D::materialLibraryNode(this);
+ if (!matLib.isValid())
+ return;
+
+ Utils::reverseForeach(matLib.directSubModelNodes(), [&](const ModelNode &mat) {
+ if (mat.isValid() && mat.type() == type)
+ QmlObjectNode(mat).destroy();
});
+ });
+ } else if (isItemBundle(bundleId)) {
+ // delete instances of the bundle item that is about to be unimported
+ executeInTransaction("ContentLibraryView::connectImporter", [&] {
+ NodeMetaInfo metaInfo = model()->metaInfo(type);
+ QList<ModelNode> nodes = allModelNodesOfType(metaInfo);
+ for (ModelNode &node : nodes)
+ node.destroy();
+ });
+ }
+ });
+}
- connect(effectsModel, &ContentLibraryEffectsModel::bundleItemUnimported, this,
- &ContentLibraryView::updateBundleEffectsImportedState);
- }
+bool ContentLibraryView::isMaterialBundle(const QString &bundleId) const
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ return bundleId == compUtils.materialsBundleId() || bundleId == compUtils.userMaterialsBundleId();
+}
- return createWidgetInfo(m_widget.data(),
- "ContentLibrary",
- WidgetInfo::LeftPane,
- 0,
- tr("Content Library"));
+// item bundle includes effects and 3D components
+bool ContentLibraryView::isItemBundle(const QString &bundleId) const
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ return bundleId == compUtils.effectsBundleId() || bundleId == compUtils.userEffectsBundleId()
+ || bundleId == compUtils.user3DBundleId();
}
void ContentLibraryView::modelAttached(Model *model)
@@ -220,7 +246,6 @@ void ContentLibraryView::modelAttached(Model *model)
m_hasQuick3DImport = model->hasImport("QtQuick3D");
updateBundlesQuick3DVersion();
- updateBundleMaterialsImportedState();
const bool hasLibrary = Utils3D::materialLibraryNode(this).isValid();
m_widget->setHasMaterialLibrary(hasLibrary);
@@ -232,8 +257,17 @@ void ContentLibraryView::modelAttached(Model *model)
m_widget->setHasActive3DScene(m_sceneId != -1);
m_widget->clearSearchFilter();
+ // bundles loading has to happen here, otherwise project path is not ready which will
+ // cause bundle items types to resolve incorrectly
+ m_widget->materialsModel()->loadBundle();
m_widget->effectsModel()->loadBundle();
- updateBundleEffectsImportedState();
+ m_widget->userModel()->loadBundles();
+
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ m_widget->updateImportedState(compUtils.materialsBundleId());
+ m_widget->updateImportedState(compUtils.effectsBundleId());
+ m_widget->updateImportedState(compUtils.userMaterialsBundleId());
+ m_widget->updateImportedState(compUtils.user3DBundleId());
}
void ContentLibraryView::modelAboutToBeDetached(Model *model)
@@ -275,7 +309,7 @@ void ContentLibraryView::selectedNodesChanged(const QList<ModelNode> &selectedNo
return node.metaInfo().isQtQuick3DModel();
});
- m_widget->materialsModel()->setHasModelSelection(!m_selectedModels.isEmpty());
+ m_widget->setHasModelSelection(!m_selectedModels.isEmpty());
}
void ContentLibraryView::customNotification(const AbstractView *view,
@@ -283,8 +317,6 @@ void ContentLibraryView::customNotification(const AbstractView *view,
const QList<ModelNode> &nodeList,
const QList<QVariant> &data)
{
- Q_UNUSED(data)
-
if (view == this)
return;
@@ -318,12 +350,29 @@ void ContentLibraryView::customNotification(const AbstractView *view,
m_widget->addTexture(m_draggedBundleTexture);
m_draggedBundleTexture = nullptr;
- } else if (identifier == "drop_bundle_effect") {
+ } else if (identifier == "drop_bundle_item") {
QTC_ASSERT(nodeList.size() == 1, return);
- m_bundleEffectPos = data.size() == 1 ? data.first() : QVariant();
- m_widget->effectsModel()->addInstance(m_draggedBundleEffect);
- m_bundleEffectTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this);
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ bool is3D = m_draggedBundleItem->type().startsWith(compUtils.user3DBundleType().toLatin1());
+
+ m_bundleItemPos = data.size() == 1 ? data.first() : QVariant();
+ if (is3D)
+ m_widget->userModel()->add3DInstance(m_draggedBundleItem);
+ else
+ m_widget->effectsModel()->addInstance(m_draggedBundleItem);
+ m_bundleItemTarget = nodeList.first() ? nodeList.first() : Utils3D::active3DSceneNode(this);
+ } else if (identifier == "add_material_to_content_lib") {
+ QTC_ASSERT(nodeList.size() == 1 && data.size() == 1, return);
+
+ addLibMaterial(nodeList.first(), data.first().value<QPixmap>());
+ } else if (identifier == "add_assets_to_content_lib") {
+ addLibAssets(data.first().toStringList());
+ } else if (identifier == "add_3d_to_content_lib") {
+ if (nodeList.first().isComponent())
+ addLib3DComponent(nodeList.first());
+ else
+ addLib3DItem(nodeList.first());
}
}
@@ -452,6 +501,325 @@ void ContentLibraryView::applyBundleMaterialToDropTarget(const ModelNode &bundle
}
#endif
+// Add a project material to Content Library's user tab
+void ContentLibraryView::addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap)
+{
+ auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/materials/");
+
+ QString name = node.variantProperty("objectName").value().toString();
+ auto [qml, icon] = m_widget->userModel()->getUniqueLibMaterialNames(node.id());
+
+ QString iconPath = QLatin1String("icons/%1").arg(icon);
+ QString fullIconPath = bundlePath.pathAppended(iconPath).toString();
+
+ // save icon
+ bool iconSaved = iconPixmap.save(fullIconPath);
+ if (!iconSaved)
+ qWarning() << __FUNCTION__ << "icon save failed";
+
+ // generate and save material Qml file
+ const QStringList depAssets = writeLibItemQml(node, qml);
+
+ // add the material to the bundle json
+ QJsonObject &jsonRef = m_widget->userModel()->bundleJsonMaterialObjectRef();
+ QJsonArray itemsArr = jsonRef.value("items").toArray();
+ QJsonObject itemObj;
+ itemObj.insert("name", name);
+ itemObj.insert("qml", qml);
+ itemObj.insert("icon", iconPath);
+ QJsonArray filesArr;
+ for (const QString &assetPath : depAssets)
+ filesArr.append(assetPath);
+ itemObj.insert("files", filesArr);
+
+ itemsArr.append(itemObj);
+ jsonRef["items"] = itemsArr;
+
+ auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME)
+ .writeFileContents(QJsonDocument(jsonRef).toJson());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ // copy material assets to bundle folder
+ for (const QString &assetPath : depAssets) {
+ Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath);
+ Utils::FilePath assetPathTarget = bundlePath.pathAppended(assetPath);
+ assetPathTarget.parentDir().ensureWritableDir();
+
+ auto result = assetPathSource.copyFile(assetPathTarget);
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+ }
+
+ m_widget->userModel()->addMaterial(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets);
+}
+
+QStringList ContentLibraryView::writeLibItemQml(const ModelNode &node, const QString &qml)
+{
+ QStringList depListIds;
+ auto [qmlString, assets] = modelNodeToQmlString(node, depListIds);
+
+ qmlString.prepend("import QtQuick\nimport QtQuick3D\n\n");
+
+ QString itemType = QLatin1String(node.metaInfo().isQtQuick3DMaterial() ? "materials" : "3d");
+ auto qmlPath = Utils::FilePath::fromString(QLatin1String("%1/User/%2/%3")
+ .arg(Paths::bundlesPathSetting(), itemType, qml));
+ auto result = qmlPath.writeFileContents(qmlString.toUtf8());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ return assets.values();
+}
+
+QPair<QString, QSet<QString>> ContentLibraryView::modelNodeToQmlString(const ModelNode &node,
+ QStringList &depListIds,
+ int depth)
+{
+ QString qml;
+ QSet<QString> assets;
+
+ QString indent = QString(" ").repeated(depth * 4);
+
+ qml += indent + node.simplifiedTypeName() + " {\n";
+
+ indent = QString(" ").repeated((depth + 1) * 4);
+
+ qml += indent + "id: " + (depth == 0 ? "root" : node.id()) + " \n\n";
+
+ const QList<PropertyName> excludedProps = {"x", "y", "z", "eulerRotation.x", "eulerRotation.y",
+ "eulerRotation.z", "scale.x", "scale.y", "scale.z",
+ "pivot.x", "pivot.y", "pivot.z"};
+ const QList<AbstractProperty> matProps = node.properties();
+ for (const AbstractProperty &p : matProps) {
+ if (excludedProps.contains(p.name()))
+ continue;
+
+ if (p.isVariantProperty()) {
+ QVariant pValue = p.toVariantProperty().value();
+ QString val;
+ if (strcmp(pValue.typeName(), "QString") == 0 || strcmp(pValue.typeName(), "QColor") == 0) {
+ val = QLatin1String("\"%1\"").arg(pValue.toString());
+ } else if (strcmp(pValue.typeName(), "QUrl") == 0) {
+ QString pValueStr = pValue.toString();
+ val = QLatin1String("\"%1\"").arg(pValueStr);
+ if (!pValueStr.startsWith("#"))
+ assets.insert(pValue.toString());
+ } else if (strcmp(pValue.typeName(), "QmlDesigner::Enumeration") == 0) {
+ val = pValue.value<QmlDesigner::Enumeration>().toString();
+ } else {
+ val = pValue.toString();
+ }
+
+ qml += indent + p.name() + ": " + val + "\n";
+ } else if (p.isBindingProperty()) {
+ qml += indent + p.name() + ": " + p.toBindingProperty().expression() + "\n";
+
+ ModelNode depNode = modelNodeForId(p.toBindingProperty().expression());
+
+ if (depNode && !depListIds.contains(depNode.id())) {
+ depListIds.append(depNode.id());
+ auto [depQml, depAssets] = modelNodeToQmlString(depNode, depListIds, depth + 1);
+ qml += "\n" + depQml + "\n";
+ assets.unite(depAssets);
+ }
+ }
+ }
+
+ indent = QString(" ").repeated(depth * 4);
+
+ qml += indent + "}\n";
+
+ return {qml, assets};
+}
+
+void ContentLibraryView::addLibAssets(const QStringList &paths)
+{
+ auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/textures");
+ QStringList pathsInBundle;
+
+ const QStringList existingTextures = Utils::transform(bundlePath.dirEntries(QDir::Files),
+ [](const Utils::FilePath &path) {
+ return path.fileName();
+ });
+
+ for (const QString &path : paths) {
+ auto assetFilePath = Utils::FilePath::fromString(path);
+ if (existingTextures.contains(assetFilePath.fileName()))
+ continue;
+
+ Asset asset(path);
+
+ // save icon
+ QString iconSavePath = bundlePath.pathAppended("icons/" + assetFilePath.baseName() + ".png")
+ .toString();
+ QPixmap icon = asset.pixmap({120, 120});
+ bool iconSaved = icon.save(iconSavePath);
+ if (!iconSaved)
+ qWarning() << __FUNCTION__ << "icon save failed";
+
+ // save asset
+ auto result = assetFilePath.copyFile(bundlePath.pathAppended(asset.fileName()));
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ pathsInBundle.append(bundlePath.pathAppended(asset.fileName()).toString());
+ }
+
+ m_widget->userModel()->addTextures(pathsInBundle);
+}
+
+void ContentLibraryView::addLib3DComponent(const ModelNode &node)
+{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
+ QString compBaseName = node.simplifiedTypeName();
+ QString compFileName = compBaseName + ".qml";
+
+ Utils::FilePath compDir = DocumentManager::currentProjectDirPath()
+ .pathAppended(compUtils.import3dTypePath() + '/' + compBaseName);
+
+ auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/");
+
+ // confirm overwrite if an item with same name exists
+ if (bundlePath.pathAppended(compFileName).exists()) {
+ // Show a QML confirmation dialog before proceeding
+ QMessageBox::StandardButton reply = QMessageBox::question(m_widget, tr("3D Item Exists"),
+ tr("A 3D item with the same name '%1' already exists in the Content Library, are you sure you want to overwrite it?")
+ .arg(compFileName), QMessageBox::Yes | QMessageBox::No);
+ if (reply == QMessageBox::No)
+ return;
+
+ // before overwriting remove old item (to avoid partial items and dangling assets)
+ m_widget->userModel()->remove3DFromContentLibByName(compFileName);
+ }
+
+ // generate and save icon
+ QString iconPath = QLatin1String("icons/%1").arg(UniqueName::generateId(compBaseName) + ".png");
+ QString fullIconPath = bundlePath.pathAppended(iconPath).toString();
+ genAndSaveIcon(compDir.pathAppended(compFileName).path(), fullIconPath);
+
+ const Utils::FilePaths sourceFiles = compDir.dirEntries({{}, QDir::Files, QDirIterator::Subdirectories});
+ const QStringList ignoreList {"_importdata.json", "qmldir", compBaseName + ".hints"};
+ QStringList filesList; // 3D component's assets (dependencies)
+
+ for (const Utils::FilePath &sourcePath : sourceFiles) {
+ Utils::FilePath relativePath = sourcePath.relativePathFrom(compDir);
+ if (ignoreList.contains(sourcePath.fileName()) || relativePath.startsWith("source scene"))
+ continue;
+
+ Utils::FilePath targetPath = bundlePath.pathAppended(relativePath.path());
+ targetPath.parentDir().ensureWritableDir();
+
+ // copy item from project to user bundle
+ auto result = sourcePath.copyFile(targetPath);
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ if (sourcePath.fileName() != compFileName) // skip component file (only collect dependencies)
+ filesList.append(relativePath.path());
+ }
+
+ // add the item to the bundle json
+ QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef();
+ QJsonArray itemsArr = jsonRef.value("items").toArray();
+ itemsArr.append(QJsonObject {
+ {"name", node.simplifiedTypeName()},
+ {"qml", compFileName},
+ {"icon", iconPath},
+ {"files", QJsonArray::fromStringList(filesList)}
+ });
+
+ jsonRef["items"] = itemsArr;
+
+ auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME)
+ .writeFileContents(QJsonDocument(jsonRef).toJson());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ m_widget->userModel()->add3DItem(compBaseName, compFileName, QUrl::fromLocalFile(fullIconPath),
+ filesList);
+}
+
+void ContentLibraryView::addLib3DItem(const ModelNode &node)
+{
+ auto bundlePath = Utils::FilePath::fromString(Paths::bundlesPathSetting() + "/User/3d/");
+
+ QString name = node.variantProperty("objectName").value().toString();
+ auto [qml, icon] = m_widget->userModel()->getUniqueLib3DNames(node.id());
+ QString iconPath = QLatin1String("icons/%1").arg(icon);
+
+ if (name.isEmpty())
+ name = node.id();
+
+ // generate and save item Qml file
+ const QStringList depAssets = writeLibItemQml(node, qml);
+
+ // generate and save icon
+ QString qmlPath = QLatin1String("%1/User/3d/%2").arg(Paths::bundlesPathSetting(), qml);
+ QString fullIconPath = bundlePath.pathAppended(iconPath).toString();
+ genAndSaveIcon(qmlPath, fullIconPath);
+
+ // add the item to the bundle json
+ QJsonObject &jsonRef = m_widget->userModel()->bundleJson3DObjectRef();
+ QJsonArray itemsArr = jsonRef.value("items").toArray();
+ itemsArr.append(QJsonObject {
+ {"name", name},
+ {"qml", qml},
+ {"icon", iconPath},
+ {"files", QJsonArray::fromStringList(depAssets)}
+ });
+
+ jsonRef["items"] = itemsArr;
+
+ auto result = bundlePath.pathAppended(Constants::BUNDLE_JSON_FILENAME)
+ .writeFileContents(QJsonDocument(jsonRef).toJson());
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+
+ // copy item's assets to bundle folder
+ for (const QString &assetPath : depAssets) {
+ Utils::FilePath assetPathSource = DocumentManager::currentResourcePath().pathAppended(assetPath);
+ Utils::FilePath assetPathTarget = bundlePath.pathAppended(assetPath);
+ assetPathTarget.parentDir().ensureWritableDir();
+
+ auto result = assetPathSource.copyFile(assetPathTarget);
+ if (!result)
+ qWarning() << __FUNCTION__ << result.error();
+ }
+
+ m_widget->userModel()->add3DItem(name, qml, QUrl::fromLocalFile(fullIconPath), depAssets);
+}
+
+/**
+ * @brief Generates an icon image from a qml component
+ * @param qmlPath path to the qml component file to be rendered
+ * @param iconPath output save path of the generated icon
+ */
+void ContentLibraryView::genAndSaveIcon(const QString &qmlPath, const QString &iconPath)
+{
+ m_imageCache.requestSmallImage(
+ Utils::PathString{qmlPath},
+ [&, qmlPath, iconPath](const QImage &image) {
+ bool iconSaved = image.save(iconPath);
+ if (iconSaved)
+ m_widget->userModel()->refresh3DSection();
+ else
+ qWarning() << "ContentLibraryView::genAndSaveIcon(): icon save failed";
+ },
+ [&](ImageCache::AbortReason abortReason) {
+ if (abortReason == ImageCache::AbortReason::Abort) {
+ qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation "
+ "failed for path %1, reason: Abort").arg(qmlPath);
+ } else if (abortReason == ImageCache::AbortReason::Failed) {
+ qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation "
+ "failed for path %1, reason: Failed").arg(qmlPath);
+ } else if (abortReason == ImageCache::AbortReason::NoEntry) {
+ qWarning() << QLatin1String("ContentLibraryView::genAndSaveIcon(): icon generation "
+ "failed for path %1, reason: NoEntry").arg(qmlPath);
+ }
+ });
+}
+
ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &type)
{
ModelNode matLib = Utils3D::materialLibraryNode(this);
@@ -477,6 +845,7 @@ ModelNode ContentLibraryView::getBundleMaterialDefaultInstance(const TypeName &t
return {};
}
+
#ifdef QDS_USE_PROJECTSTORAGE
ModelNode ContentLibraryView::createMaterial(const TypeName &typeName)
{
@@ -491,7 +860,7 @@ ModelNode ContentLibraryView::createMaterial(const TypeName &typeName)
QString newName = QString::fromUtf8(typeName).replace(rgx, " \\1\\2").trimmed();
if (newName.endsWith(" Material"))
newName.chop(9); // remove trailing " Material"
- QString newId = model()->generateIdFromName(newName, "material");
+ QString newId = model()->generateNewId(newName, "material");
newMatNode.setIdWithRefactoring(newId);
VariantProperty objNameProp = newMatNode.variantProperty("objectName");
@@ -517,7 +886,7 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo)
QString newName = QString::fromLatin1(metaInfo.simplifiedTypeName()).replace(rgx, " \\1\\2").trimmed();
if (newName.endsWith(" Material"))
newName.chop(9); // remove trailing " Material"
- QString newId = model()->generateIdFromName(newName, "material");
+ QString newId = model()->generateNewId(newName, "material");
newMatNode.setIdWithRefactoring(newId);
VariantProperty objNameProp = newMatNode.variantProperty("objectName");
@@ -529,44 +898,6 @@ ModelNode ContentLibraryView::createMaterial(const NodeMetaInfo &metaInfo)
}
#endif
-void ContentLibraryView::updateBundleMaterialsImportedState()
-{
- using namespace Utils;
-
- if (!m_widget->materialsModel()->bundleImporter())
- return;
-
- QStringList importedBundleMats;
-
- FilePath materialBundlePath = m_widget->materialsModel()->bundleImporter()->resolveBundleImportPath();
-
- if (materialBundlePath.exists()) {
- importedBundleMats = transform(materialBundlePath.dirEntries({{"*.qml"}, QDir::Files}),
- [](const FilePath &f) { return f.fileName().chopped(4); });
- }
-
- m_widget->materialsModel()->updateImportedState(importedBundleMats);
-}
-
-void ContentLibraryView::updateBundleEffectsImportedState()
-{
- using namespace Utils;
-
- if (!m_widget->effectsModel()->bundleImporter())
- return;
-
- QStringList importedBundleEffs;
-
- FilePath bundlePath = m_widget->effectsModel()->bundleImporter()->resolveBundleImportPath();
-
- if (bundlePath.exists()) {
- importedBundleEffs = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}),
- [](const FilePath &f) { return f.fileName().chopped(4); });
- }
-
- m_widget->effectsModel()->updateImportedState(importedBundleEffs);
-}
-
void ContentLibraryView::updateBundlesQuick3DVersion()
{
bool hasImport = false;
@@ -601,6 +932,7 @@ void ContentLibraryView::updateBundlesQuick3DVersion()
#endif
m_widget->materialsModel()->setQuick3DImportVersion(major, minor);
m_widget->effectsModel()->setQuick3DImportVersion(major, minor);
+ m_widget->userModel()->setQuick3DImportVersion(major, minor);
}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
index 3b57b7a4ab..914a8b8ea0 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibraryview.h
@@ -3,16 +3,19 @@
#pragma once
-#include "abstractview.h"
-#include "createtexture.h"
-#include "nodemetainfo.h"
+#include <asynchronousimagecache.h>
+#include <abstractview.h>
+#include <createtexture.h>
+#include <nodemetainfo.h>
#include <QObject>
#include <QPointer>
+QT_FORWARD_DECLARE_CLASS(QPixmap)
+
namespace QmlDesigner {
-class ContentLibraryEffect;
+class ContentLibraryItem;
class ContentLibraryMaterial;
class ContentLibraryTexture;
class ContentLibraryWidget;
@@ -23,7 +26,8 @@ class ContentLibraryView : public AbstractView
Q_OBJECT
public:
- ContentLibraryView(ExternalDependenciesInterface &externalDependencies);
+ ContentLibraryView(AsynchronousImageCache &imageCache,
+ ExternalDependenciesInterface &externalDependencies);
~ContentLibraryView() override;
bool hasWidget() const override;
@@ -46,10 +50,20 @@ public:
const QVariant &data) override;
private:
+ void connectImporter();
+ bool isMaterialBundle(const QString &bundleId) const;
+ bool isItemBundle(const QString &bundleId) const;
void active3DSceneChanged(qint32 sceneId);
- void updateBundleMaterialsImportedState();
- void updateBundleEffectsImportedState();
void updateBundlesQuick3DVersion();
+ void addLibMaterial(const ModelNode &node, const QPixmap &iconPixmap);
+ void addLibAssets(const QStringList &paths);
+ void addLib3DComponent(const ModelNode &node);
+ void addLib3DItem(const ModelNode &node);
+ void genAndSaveIcon(const QString &qmlPath, const QString &iconPath);
+ QStringList writeLibItemQml(const ModelNode &node, const QString &qml);
+ QPair<QString, QSet<QString>> modelNodeToQmlString(const ModelNode &node, QStringList &depListIds,
+ int depth = 0);
+
#ifdef QDS_USE_PROJECTSTORAGE
void applyBundleMaterialToDropTarget(const ModelNode &bundleMat, const TypeName &typeName = {});
#else
@@ -64,12 +78,13 @@ private:
#endif
QPointer<ContentLibraryWidget> m_widget;
QList<ModelNode> m_bundleMaterialTargets;
- ModelNode m_bundleEffectTarget; // target of the dropped bundle effect
- QVariant m_bundleEffectPos; // pos of the dropped bundle effect
+ ModelNode m_bundleItemTarget; // target of the dropped bundle item
+ QVariant m_bundleItemPos; // pos of the dropped bundle item
QList<ModelNode> m_selectedModels; // selected 3D model nodes
ContentLibraryMaterial *m_draggedBundleMaterial = nullptr;
ContentLibraryTexture *m_draggedBundleTexture = nullptr;
- ContentLibraryEffect *m_draggedBundleEffect = nullptr;
+ ContentLibraryItem *m_draggedBundleItem = nullptr;
+ AsynchronousImageCache &m_imageCache;
bool m_bundleMaterialAddToSelected = false;
bool m_hasQuick3DImport = false;
qint32 m_sceneId = -1;
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
index c885a76ba7..72bece4c98 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.cpp
@@ -3,13 +3,15 @@
#include "contentlibrarywidget.h"
-#include "contentlibraryeffect.h"
+#include "contentlibrarybundleimporter.h"
#include "contentlibraryeffectsmodel.h"
+#include "contentlibraryitem.h"
#include "contentlibrarymaterial.h"
#include "contentlibrarymaterialsmodel.h"
#include "contentlibrarytexture.h"
#include "contentlibrarytexturesmodel.h"
#include "contentlibraryiconprovider.h"
+#include "contentlibraryusermodel.h"
#include "utils/filedownloader.h"
#include "utils/fileextractor.h"
@@ -17,6 +19,7 @@
#include <coreplugin/icore.h>
#include <designerpaths.h>
+#include <nodemetainfo.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
@@ -39,7 +42,6 @@
#include <QQuickWidget>
#include <QRegularExpression>
#include <QShortcut>
-#include <QStandardPaths>
#include <QVBoxLayout>
namespace QmlDesigner {
@@ -66,18 +68,18 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
Model *model = document->currentModel();
QTC_ASSERT(model, return false);
- if (m_effectToDrag) {
+ if (m_itemToDrag) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
if ((me->globalPos() - m_dragStartPoint).manhattanLength() > 20) {
QByteArray data;
QMimeData *mimeData = new QMimeData;
QDataStream stream(&data, QIODevice::WriteOnly);
- stream << m_effectToDrag->type();
- mimeData->setData(Constants::MIME_TYPE_BUNDLE_EFFECT, data);
+ stream << m_itemToDrag->type();
+ mimeData->setData(Constants::MIME_TYPE_BUNDLE_ITEM, data);
- emit bundleEffectDragStarted(m_effectToDrag);
- model->startDrag(mimeData, m_effectToDrag->icon().toLocalFile());
- m_effectToDrag = nullptr;
+ emit bundleItemDragStarted(m_itemToDrag);
+ model->startDrag(mimeData, m_itemToDrag->icon().toLocalFile());
+ m_itemToDrag = nullptr;
}
} else if (m_materialToDrag) {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
@@ -100,10 +102,10 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
&& m_textureToDrag->isDownloaded()) {
QMimeData *mimeData = new QMimeData;
mimeData->setData(Constants::MIME_TYPE_BUNDLE_TEXTURE,
- {m_textureToDrag->downloadedTexturePath().toUtf8()});
+ {m_textureToDrag->texturePath().toUtf8()});
// Allows standard file drag-n-drop. As of now needed to drop on Assets view
- mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->downloadedTexturePath())});
+ mimeData->setUrls({QUrl::fromLocalFile(m_textureToDrag->texturePath())});
emit bundleTextureDragStarted(m_textureToDrag);
model->startDrag(mimeData, m_textureToDrag->icon().toLocalFile());
@@ -111,7 +113,7 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
}
}
} else if (event->type() == QMouseEvent::MouseButtonRelease) {
- m_effectToDrag = nullptr;
+ m_itemToDrag = nullptr;
m_materialToDrag = nullptr;
m_textureToDrag = nullptr;
setIsDragging(false);
@@ -121,11 +123,12 @@ bool ContentLibraryWidget::eventFilter(QObject *obj, QEvent *event)
}
ContentLibraryWidget::ContentLibraryWidget()
- : m_quickWidget(new StudioQuickWidget(this))
+ : m_quickWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>(this))
, m_materialsModel(new ContentLibraryMaterialsModel(this))
, m_texturesModel(new ContentLibraryTexturesModel("Textures", this))
, m_environmentsModel(new ContentLibraryTexturesModel("Environments", this))
, m_effectsModel(new ContentLibraryEffectsModel(this))
+ , m_userModel(new ContentLibraryUserModel(this))
{
qmlRegisterType<QmlDesigner::FileDownloader>("WebFetcher", 1, 0, "FileDownloader");
qmlRegisterType<QmlDesigner::FileExtractor>("WebFetcher", 1, 0, "FileExtractor");
@@ -140,18 +143,12 @@ ContentLibraryWidget::ContentLibraryWidget()
m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_quickWidget->setClearColor(Theme::getColor(Theme::Color::DSpanelBackground));
- m_baseUrl = QmlDesignerPlugin::settings()
- .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString()
- + "/textures";
+ m_textureBundleUrl = QmlDesignerPlugin::settings()
+ .value(DesignerSettingsKey::DOWNLOADABLE_BUNDLES_URL).toString() + "/textures";
- m_texturesUrl = m_baseUrl + "/Textures";
- m_textureIconsUrl = m_baseUrl + "/icons/Textures";
- m_environmentIconsUrl = m_baseUrl + "/icons/Environments";
- m_environmentsUrl = m_baseUrl + "/Environments";
+ m_bundlePath = Paths::bundlesPathSetting();
- m_downloadPath = Paths::bundlesPathSetting();
-
- loadTextureBundle();
+ loadTextureBundles();
Theme::setupTheme(m_quickWidget->engine());
m_quickWidget->quickWidget()->installEventFilter(this);
@@ -159,7 +156,7 @@ ContentLibraryWidget::ContentLibraryWidget()
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
layout->setSpacing(0);
- layout->addWidget(m_quickWidget.data());
+ layout->addWidget(m_quickWidget.get());
updateSearch();
@@ -177,38 +174,95 @@ ContentLibraryWidget::ContentLibraryWidget()
{"materialsModel", QVariant::fromValue(m_materialsModel.data())},
{"texturesModel", QVariant::fromValue(m_texturesModel.data())},
{"environmentsModel", QVariant::fromValue(m_environmentsModel.data())},
- {"effectsModel", QVariant::fromValue(m_effectsModel.data())}});
+ {"effectsModel", QVariant::fromValue(m_effectsModel.data())},
+ {"userModel", QVariant::fromValue(m_userModel.data())}});
reloadQmlSource();
+ createImporter();
+}
+
+void ContentLibraryWidget::createImporter()
+{
+ m_importer = new ContentLibraryBundleImporter();
+#ifdef QDS_USE_PROJECTSTORAGE
+ connect(m_importer,
+ &ContentLibraryBundleImporter::importFinished,
+ this,
+ [&](const QmlDesigner::TypeName &typeName, const QString &bundleId) {
+ setImporterRunning(false);
+ if (typeName.size())
+ updateImportedState(bundleId);
+ });
+#else
+ connect(m_importer,
+ &ContentLibraryBundleImporter::importFinished,
+ this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) {
+ setImporterRunning(false);
+ if (metaInfo.isValid())
+ updateImportedState(bundleId);
+ });
+#endif
+
+ connect(m_importer, &ContentLibraryBundleImporter::unimportFinished, this,
+ [&](const QmlDesigner::NodeMetaInfo &metaInfo, const QString &bundleId) {
+ Q_UNUSED(metaInfo)
+ setImporterRunning(false);
+ updateImportedState(bundleId);
+ });
+}
+
+void ContentLibraryWidget::updateImportedState(const QString &bundleId)
+{
+ if (!m_importer)
+ return;
+
+ Utils::FilePath bundlePath = m_importer->resolveBundleImportPath(bundleId);
+
+ QStringList importedItems;
+ if (bundlePath.exists()) {
+ importedItems = transform(bundlePath.dirEntries({{"*.qml"}, QDir::Files}),
+ [](const Utils::FilePath &f) { return f.baseName(); });
+ }
+
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+ if (bundleId == compUtils.materialsBundleId())
+ m_materialsModel->updateImportedState(importedItems);
+ else if (bundleId == compUtils.effectsBundleId())
+ m_effectsModel->updateImportedState(importedItems);
+ else if (bundleId == compUtils.userMaterialsBundleId())
+ m_userModel->updateMaterialsImportedState(importedItems);
+ else if (bundleId == compUtils.user3DBundleId())
+ m_userModel->update3DImportedState(importedItems);
+}
+
+ContentLibraryBundleImporter *ContentLibraryWidget::importer() const
+{
+ return m_importer;
}
-QVariantMap ContentLibraryWidget::readBundleMetadata()
+QVariantMap ContentLibraryWidget::readTextureBundleJson()
{
- QVariantMap metaData;
- QFile jsonFile(m_downloadPath + "/texture_bundle.json");
+ QVariantMap jsonData;
+ QFile jsonFile(m_bundlePath + "/texture_bundle.json");
if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text))
- metaData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap();
+ jsonData = QJsonDocument::fromJson(jsonFile.readAll()).toVariant().toMap();
- int version = metaData["version"].toInt();
+ int version = jsonData["version"].toInt();
if (version > TextureBundleMetadataVersion) {
qWarning() << "Unrecognized texture metadata file version: " << version;
return {};
}
- return metaData;
+ return jsonData;
}
-void ContentLibraryWidget::loadTextureBundle()
+void ContentLibraryWidget::loadTextureBundles()
{
- QDir bundleDir{m_downloadPath};
+ QDir bundleDir{m_bundlePath};
- if (fetchTextureBundleMetadata(bundleDir) && fetchTextureBundleIcons(bundleDir)) {
- QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
- QVariantMap metaData = readBundleMetadata();
- m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath, metaData);
- m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl,
- bundleIconPath, metaData);
- }
+ if (fetchTextureBundleJson(bundleDir) && fetchTextureBundleIcons(bundleDir))
+ populateTextureBundleModels();
}
std::tuple<QVariantMap, QVariantMap, QVariantMap> ContentLibraryWidget::compareTextureMetaFiles(
@@ -272,9 +326,9 @@ void ContentLibraryWidget::fetchNewTextureIcons(const QVariantMap &existingFiles
});
auto multidownloader = new MultiFileDownloader(this);
- multidownloader->setBaseUrl(QString(m_baseUrl + "/icons"));
+ multidownloader->setBaseUrl(QString(m_textureBundleUrl + "/icons"));
multidownloader->setFiles(fileList);
- multidownloader->setTargetDirPath(m_downloadPath + "/TextureBundleIcons");
+ multidownloader->setTargetDirPath(m_bundlePath + "/TextureBundleIcons");
auto downloader = new FileDownloader(this);
downloader->setDownloadEnabled(true);
@@ -314,15 +368,8 @@ void ContentLibraryWidget::fetchNewTextureIcons(const QVariantMap &existingFiles
existingFile.flush();
}
- if (fetchTextureBundleIcons(bundleDir)) {
- QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
- QVariantMap metaData = readBundleMetadata();
- m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath,
- metaData);
- m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl,
- bundleIconPath, metaData);
- }
-
+ if (fetchTextureBundleIcons(bundleDir))
+ populateTextureBundleModels();
});
multidownloader->start();
@@ -433,50 +480,45 @@ QStringList ContentLibraryWidget::saveNewTextures(const QDir &bundleDir, const Q
}
}
-bool ContentLibraryWidget::fetchTextureBundleMetadata(const QDir &bundleDir)
+bool ContentLibraryWidget::fetchTextureBundleJson(const QDir &bundleDir)
{
QString filePath = bundleDir.filePath("texture_bundle.json");
QFileInfo fi(filePath);
- bool metaFileExists = fi.exists() && fi.size() > 0;
+ bool jsonFileExists = fi.exists() && fi.size() > 0;
- QString metaFileUrl = m_baseUrl + "/texture_bundle.zip";
+ QString bundleZipUrl = m_textureBundleUrl + "/texture_bundle.zip";
FileDownloader *downloader = new FileDownloader(this);
- downloader->setUrl(metaFileUrl);
+ downloader->setUrl(bundleZipUrl);
downloader->setProbeUrl(false);
downloader->setDownloadEnabled(true);
+ downloader->start();
QObject::connect(downloader, &FileDownloader::downloadFailed, this,
- [this, metaFileExists, bundleDir] {
- if (metaFileExists) {
- if (fetchTextureBundleIcons(bundleDir)) {
- QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
- QVariantMap metaData = readBundleMetadata();
- m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath,
- metaData);
- m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl,
- bundleIconPath, metaData);
- }
+ [this, jsonFileExists, bundleDir] {
+ if (jsonFileExists) {
+ if (fetchTextureBundleIcons(bundleDir))
+ populateTextureBundleModels();
}
});
QObject::connect(downloader, &FileDownloader::finishedChanged, this,
- [this, downloader, bundleDir, metaFileExists, filePath] {
+ [this, downloader, bundleDir, jsonFileExists, filePath] {
FileExtractor *extractor = new FileExtractor(this);
extractor->setArchiveName(downloader->completeBaseName());
extractor->setSourceFile(downloader->outputFile());
- if (!metaFileExists)
+ if (!jsonFileExists)
extractor->setTargetPath(bundleDir.absolutePath());
extractor->setAlwaysCreateDir(false);
extractor->setClearTargetPathContents(false);
QObject::connect(extractor, &FileExtractor::finishedChanged, this,
- [this, downloader, bundleDir, extractor, metaFileExists, filePath] {
+ [this, downloader, bundleDir, extractor, jsonFileExists, filePath] {
downloader->deleteLater();
extractor->deleteLater();
- if (metaFileExists) {
+ if (jsonFileExists) {
QVariantMap newFiles, existing;
QVariantMap modifiedFilesEntries;
@@ -498,32 +540,35 @@ bool ContentLibraryWidget::fetchTextureBundleMetadata(const QDir &bundleDir)
}
}
- if (fetchTextureBundleIcons(bundleDir)) {
- QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
- QVariantMap metaData = readBundleMetadata();
- m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath,
- metaData);
- m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl,
- bundleIconPath, metaData);
- }
+ if (fetchTextureBundleIcons(bundleDir))
+ populateTextureBundleModels();
});
extractor->extract();
});
- downloader->start();
return false;
}
+void ContentLibraryWidget::populateTextureBundleModels()
+{
+ QVariantMap jsonData = readTextureBundleJson();
+
+ QString bundleIconPath = m_bundlePath + "/TextureBundleIcons";
+
+ m_texturesModel->loadTextureBundle(m_textureBundleUrl, bundleIconPath, jsonData);
+ m_environmentsModel->loadTextureBundle(m_textureBundleUrl, bundleIconPath, jsonData);
+}
+
bool ContentLibraryWidget::fetchTextureBundleIcons(const QDir &bundleDir)
{
QString iconsPath = bundleDir.filePath("TextureBundleIcons");
QDir iconsDir(iconsPath);
- if (iconsDir.exists() && iconsDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot).length() > 0)
+ if (iconsDir.exists() && !iconsDir.isEmpty())
return true;
- QString zipFileUrl = m_baseUrl + "/icons.zip";
+ QString zipFileUrl = m_textureBundleUrl + "/icons.zip";
FileDownloader *downloader = new FileDownloader(this);
downloader->setUrl(zipFileUrl);
@@ -543,13 +588,7 @@ bool ContentLibraryWidget::fetchTextureBundleIcons(const QDir &bundleDir)
[this, downloader, extractor] {
downloader->deleteLater();
extractor->deleteLater();
-
- QString bundleIconPath = m_downloadPath + "/TextureBundleIcons";
- QVariantMap metaData = readBundleMetadata();
- m_texturesModel->loadTextureBundle(m_texturesUrl, m_textureIconsUrl, bundleIconPath,
- metaData);
- m_environmentsModel->loadTextureBundle(m_environmentsUrl, m_environmentIconsUrl,
- bundleIconPath, metaData);
+ populateTextureBundleModels();
});
extractor->extract();
@@ -572,7 +611,7 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey)
checksumOnServer = m_environmentsModel->removeModifiedFileEntry(textureKey);
QJsonObject metaDataObj;
- QFile jsonFile(m_downloadPath + "/texture_bundle.json");
+ QFile jsonFile(m_bundlePath + "/texture_bundle.json");
if (jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
metaDataObj = QJsonDocument::fromJson(jsonFile.readAll()).object();
jsonFile.close();
@@ -589,7 +628,7 @@ void ContentLibraryWidget::markTextureUpdated(const QString &textureKey)
QJsonDocument outDoc(metaDataObj);
QByteArray data = outDoc.toJson();
- QFile outFile(m_downloadPath + "/texture_bundle.json");
+ QFile outFile(m_bundlePath + "/texture_bundle.json");
if (outFile.open(QIODeviceBase::WriteOnly | QIODeviceBase::Text)) {
outFile.write(data);
outFile.flush();
@@ -700,6 +739,20 @@ void ContentLibraryWidget::setIsQt6Project(bool b)
emit isQt6ProjectChanged();
}
+bool ContentLibraryWidget::importerRunning() const
+{
+ return m_importerRunning;
+}
+
+void ContentLibraryWidget::setImporterRunning(bool b)
+{
+ if (m_importerRunning == b)
+ return;
+
+ m_importerRunning = b;
+ emit importerRunningChanged();
+}
+
void ContentLibraryWidget::reloadQmlSource()
{
const QString materialBrowserQmlPath = qmlSourcesPath() + "/ContentLibrary.qml";
@@ -715,6 +768,7 @@ void ContentLibraryWidget::updateSearch()
m_effectsModel->setSearchText(m_filterText);
m_texturesModel->setSearchText(m_filterText);
m_environmentsModel->setSearchText(m_filterText);
+ m_userModel->setSearchText(m_filterText);
m_quickWidget->update();
}
@@ -726,32 +780,9 @@ void ContentLibraryWidget::setIsDragging(bool val)
}
}
-QString ContentLibraryWidget::findTextureBundlePath()
-{
- QDir texBundleDir;
-
- if (!qEnvironmentVariable("TEXTURE_BUNDLE_PATH").isEmpty())
- texBundleDir.setPath(qEnvironmentVariable("TEXTURE_BUNDLE_PATH"));
- else if (Utils::HostOsInfo::isMacHost())
- texBundleDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/texture_bundle");
-
- // search for matBundleDir from exec dir and up
- if (texBundleDir.dirName() == ".") {
- texBundleDir.setPath(QCoreApplication::applicationDirPath());
- while (!texBundleDir.cd("texture_bundle") && texBundleDir.cdUp())
- ; // do nothing
-
- if (texBundleDir.dirName() != "texture_bundle") // bundlePathDir not found
- return {};
- }
-
- return texBundleDir.path();
-}
-
-void ContentLibraryWidget::startDragEffect(QmlDesigner::ContentLibraryEffect *eff,
- const QPointF &mousePos)
+void ContentLibraryWidget::startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos)
{
- m_effectToDrag = eff;
+ m_itemToDrag = item;
m_dragStartPoint = mousePos.toPoint();
setIsDragging(true);
}
@@ -777,7 +808,7 @@ void ContentLibraryWidget::addImage(ContentLibraryTexture *tex)
if (!tex->isDownloaded())
return;
- emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Image);
+ emit addTextureRequested(tex->texturePath(), AddTextureMode::Image);
}
void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex)
@@ -785,7 +816,7 @@ void ContentLibraryWidget::addTexture(ContentLibraryTexture *tex)
if (!tex->isDownloaded())
return;
- emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::Texture);
+ emit addTextureRequested(tex->texturePath(), AddTextureMode::Texture);
}
void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex)
@@ -793,7 +824,7 @@ void ContentLibraryWidget::addLightProbe(ContentLibraryTexture *tex)
if (!tex->isDownloaded())
return;
- emit addTextureRequested(tex->downloadedTexturePath(), AddTextureMode::LightProbe);
+ emit addTextureRequested(tex->texturePath(), AddTextureMode::LightProbe);
}
void ContentLibraryWidget::updateSceneEnvState()
@@ -821,4 +852,23 @@ QPointer<ContentLibraryEffectsModel> ContentLibraryWidget::effectsModel() const
return m_effectsModel;
}
+QPointer<ContentLibraryUserModel> ContentLibraryWidget::userModel() const
+{
+ return m_userModel;
+}
+
+bool ContentLibraryWidget::hasModelSelection() const
+{
+ return m_hasModelSelection;
+}
+
+void ContentLibraryWidget::setHasModelSelection(bool b)
+{
+ if (b == m_hasModelSelection)
+ return;
+
+ m_hasModelSelection = b;
+ emit hasModelSelectionChanged();
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
index ab71a3dc79..8e96d9d2f3 100644
--- a/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
+++ b/src/plugins/qmldesigner/components/contentlibrary/contentlibrarywidget.h
@@ -5,6 +5,10 @@
#include "createtexture.h"
+#include <modelfwd.h>
+
+#include <utils/uniqueobjectptr.h>
+
#include <QFrame>
#include <QPointer>
@@ -18,12 +22,15 @@ class StudioQuickWidget;
namespace QmlDesigner {
-class ContentLibraryEffect;
+class ContentLibraryBundleImporter;
class ContentLibraryEffectsModel;
+class ContentLibraryItem;
class ContentLibraryMaterial;
class ContentLibraryMaterialsModel;
class ContentLibraryTexture;
class ContentLibraryTexturesModel;
+class ContentLibraryUserModel;
+class NodeMetaInfo;
class ContentLibraryWidget : public QFrame
{
@@ -33,6 +40,8 @@ class ContentLibraryWidget : public QFrame
Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary NOTIFY hasMaterialLibraryChanged)
Q_PROPERTY(bool hasActive3DScene READ hasActive3DScene WRITE setHasActive3DScene NOTIFY hasActive3DSceneChanged)
Q_PROPERTY(bool isQt6Project READ isQt6Project NOTIFY isQt6ProjectChanged)
+ Q_PROPERTY(bool importerRunning READ importerRunning WRITE setImporterRunning NOTIFY importerRunningChanged)
+ Q_PROPERTY(bool hasModelSelection READ hasModelSelection NOTIFY hasModelSelectionChanged)
// Needed for a workaround for a bug where after drag-n-dropping an item, the ScrollView scrolls to a random position
Q_PROPERTY(bool isDragging MEMBER m_isDragging NOTIFY isDraggingChanged)
@@ -57,16 +66,23 @@ public:
bool isQt6Project() const;
void setIsQt6Project(bool b);
- Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText);
+ bool importerRunning() const;
+ void setImporterRunning(bool b);
+
+ bool hasModelSelection() const;
+ void setHasModelSelection(bool b);
void setMaterialsModel(QPointer<ContentLibraryMaterialsModel> newMaterialsModel);
+ void updateImportedState(const QString &bundleId);
QPointer<ContentLibraryMaterialsModel> materialsModel() const;
QPointer<ContentLibraryTexturesModel> texturesModel() const;
QPointer<ContentLibraryTexturesModel> environmentsModel() const;
QPointer<ContentLibraryEffectsModel> effectsModel() const;
+ QPointer<ContentLibraryUserModel> userModel() const;
- Q_INVOKABLE void startDragEffect(QmlDesigner::ContentLibraryEffect *eff, const QPointF &mousePos);
+ Q_INVOKABLE void handleSearchFilterChanged(const QString &filterText);
+ Q_INVOKABLE void startDragItem(QmlDesigner::ContentLibraryItem *item, const QPointF &mousePos);
Q_INVOKABLE void startDragMaterial(QmlDesigner::ContentLibraryMaterial *mat, const QPointF &mousePos);
Q_INVOKABLE void startDragTexture(QmlDesigner::ContentLibraryTexture *tex, const QPointF &mousePos);
Q_INVOKABLE void addImage(QmlDesigner::ContentLibraryTexture *tex);
@@ -77,8 +93,10 @@ public:
QSize sizeHint() const override;
+ ContentLibraryBundleImporter *importer() const;
+
signals:
- void bundleEffectDragStarted(QmlDesigner::ContentLibraryEffect *bundleEff);
+ void bundleItemDragStarted(QmlDesigner::ContentLibraryItem *item);
void bundleMaterialDragStarted(QmlDesigner::ContentLibraryMaterial *bundleMat);
void bundleTextureDragStarted(QmlDesigner::ContentLibraryTexture *bundleTex);
void addTextureRequested(const QString texPath, QmlDesigner::AddTextureMode mode);
@@ -88,6 +106,8 @@ signals:
void hasActive3DSceneChanged();
void isDraggingChanged();
void isQt6ProjectChanged();
+ void importerRunningChanged();
+ void hasModelSelectionChanged();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -96,28 +116,31 @@ private:
void reloadQmlSource();
void updateSearch();
void setIsDragging(bool val);
- QString findTextureBundlePath();
- void loadTextureBundle();
- QVariantMap readBundleMetadata();
- bool fetchTextureBundleMetadata(const QDir &bundleDir);
+ void loadTextureBundles();
+ QVariantMap readTextureBundleJson();
+ bool fetchTextureBundleJson(const QDir &bundleDir);
bool fetchTextureBundleIcons(const QDir &bundleDir);
void fetchNewTextureIcons(const QVariantMap &existingFiles, const QVariantMap &newFiles,
const QString &existingMetaFilePath, const QDir &bundleDir);
std::tuple<QVariantMap, QVariantMap, QVariantMap> compareTextureMetaFiles(
const QString &existingMetaFile, const QString downloadedMetaFile);
QStringList saveNewTextures(const QDir &bundleDir, const QStringList &newFiles);
+ void populateTextureBundleModels();
+ void createImporter();
- QScopedPointer<StudioQuickWidget> m_quickWidget;
+ Utils::UniqueObjectPtr<StudioQuickWidget> m_quickWidget;
QPointer<ContentLibraryMaterialsModel> m_materialsModel;
QPointer<ContentLibraryTexturesModel> m_texturesModel;
QPointer<ContentLibraryTexturesModel> m_environmentsModel;
QPointer<ContentLibraryEffectsModel> m_effectsModel;
+ QPointer<ContentLibraryUserModel> m_userModel;
+ ContentLibraryBundleImporter *m_importer = nullptr;
QShortcut *m_qmlSourceUpdateShortcut = nullptr;
QString m_filterText;
- ContentLibraryEffect *m_effectToDrag = nullptr;
+ ContentLibraryItem *m_itemToDrag = nullptr;
ContentLibraryMaterial *m_materialToDrag = nullptr;
ContentLibraryTexture *m_textureToDrag = nullptr;
QPoint m_dragStartPoint;
@@ -127,12 +150,10 @@ private:
bool m_hasQuick3DImport = false;
bool m_isDragging = false;
bool m_isQt6Project = false;
- QString m_baseUrl;
- QString m_texturesUrl;
- QString m_textureIconsUrl;
- QString m_environmentIconsUrl;
- QString m_environmentsUrl;
- QString m_downloadPath;
+ bool m_importerRunning = false;
+ bool m_hasModelSelection = false;
+ QString m_textureBundleUrl;
+ QString m_bundlePath;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/createtexture.cpp b/src/plugins/qmldesigner/components/createtexture.cpp
index b6e99ae972..56056e3fd2 100644
--- a/src/plugins/qmldesigner/components/createtexture.cpp
+++ b/src/plugins/qmldesigner/components/createtexture.cpp
@@ -17,6 +17,7 @@
#include <coreplugin/messagebox.h>
#include <QTimer>
+#include <QUrl>
namespace QmlDesigner {
@@ -94,7 +95,7 @@ ModelNode CreateTexture::createTextureFromImage(const Utils::FilePath &assetPat
newTexNode.setIdWithoutRefactoring(m_view->model()->generateNewId(assetPath.baseName()));
VariantProperty sourceProp = newTexNode.variantProperty("source");
- sourceProp.setValue(textureSource);
+ sourceProp.setValue(QUrl(textureSource));
matLib.defaultNodeListProperty().reparentHere(newTexNode);
}
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
index e84623e26c..36ce3373e5 100644
--- a/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditor.cpp
@@ -77,8 +77,8 @@ CurveEditor::CurveEditor(CurveEditorModel *model, QWidget *parent)
connect(m_toolbar, &CurveEditorToolBar::currentFrameChanged, [this, model](int frame) {
model->setCurrentFrame(frame);
+ m_view->setCurrentFrame(frame, false);
updateStatusLine();
- m_view->viewport()->update();
});
connect(m_toolbar, &CurveEditorToolBar::zoomChanged, [this](double zoom) {
diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
index 72410ffb07..8da87ce119 100644
--- a/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/curveeditortoolbar.cpp
@@ -129,6 +129,8 @@ CurveEditorToolBar::CurveEditorToolBar(CurveEditorModel *model, QWidget* parent)
m_currentSpin->setFrame(false);
connect(m_currentSpin, &QSpinBox::valueChanged, this, &CurveEditorToolBar::currentFrameChanged);
+ connect(model, &CurveEditorModel::commitCurrentFrame,
+ this, [this](int frame) { m_currentSpin->setValue(frame); });
addSpacer();
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp
index a1c229f57e..159e7c31ee 100644
--- a/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/keyframeitem.cpp
@@ -422,7 +422,7 @@ QVariant KeyframeItem::itemChange(QGraphicsItem::GraphicsItemChange change, cons
rseg.moveLeftTo(position);
if (legalLeft() && legalRight()) {
- if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos.has_value()) {
+ if (qApp->keyboardModifiers().testFlag(Qt::ShiftModifier) && m_validPos) {
if (m_firstPos) {
auto firstToNow = QLineF(*m_firstPos, position);
if (std::abs(firstToNow.dx()) > std::abs(firstToNow.dy()))
diff --git a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp
index 95a260c26f..a1dfcc8f98 100644
--- a/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/bakelightsdatamodel.cpp
@@ -15,6 +15,8 @@
#include "qmlobjectnode.h"
#include "variantproperty.h"
+#include <model/modelutils.h>
+
#include <utils3d.h>
#include <utils/expected.h>
@@ -292,7 +294,7 @@ bool BakeLightsDataModel::reset()
if (!hasExposedProps && node.metaInfo().isFileComponent()
&& node.metaInfo().isQtQuick3DNode()) {
- const QString compFile = node.metaInfo().componentFileName();
+ const QString compFile = ModelUtils::componentFilePath(node);
const QString projPath = m_view->externalDependencies().currentProjectDirPath();
if (compFile.startsWith(projPath)) {
// Quick and dirty scan of the component source to check if it potentially has
diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp
index 76560ac192..f5a74ee864 100644
--- a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.cpp
@@ -66,9 +66,11 @@ void CameraSpeedConfiguration::resetDefaults()
void CameraSpeedConfiguration::hideCursor()
{
- if (QGuiApplication::overrideCursor())
+ if (m_cursorHidden)
return;
+ m_cursorHidden = true;
+
QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
if (QWindow *w = QGuiApplication::focusWindow())
@@ -77,9 +79,11 @@ void CameraSpeedConfiguration::hideCursor()
void CameraSpeedConfiguration::restoreCursor()
{
- if (!QGuiApplication::overrideCursor())
+ if (!m_cursorHidden)
return;
+ m_cursorHidden = false;
+
QGuiApplication::restoreOverrideCursor();
if (QWindow *w = QGuiApplication::focusWindow())
@@ -88,7 +92,7 @@ void CameraSpeedConfiguration::restoreCursor()
void CameraSpeedConfiguration::holdCursorInPlace()
{
- if (!QGuiApplication::overrideCursor())
+ if (!m_cursorHidden)
return;
if (QWindow *w = QGuiApplication::focusWindow())
diff --git a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h
index 55256890cb..fef06efc49 100644
--- a/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h
+++ b/src/plugins/qmldesigner/components/edit3d/cameraspeedconfiguration.h
@@ -71,6 +71,7 @@ private:
double m_multiplier = 0.;
bool m_changes = false;
QPoint m_lastPos;
+ bool m_cursorHidden = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
index 2e8ef8304f..63d5e958b1 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp
@@ -51,6 +51,8 @@ Edit3DCanvas::Edit3DCanvas(Edit3DWidget *parent)
setAcceptDrops(true);
setFocusPolicy(Qt::ClickFocus);
m_busyIndicator->show();
+
+ installEventFilter(this);
}
void Edit3DCanvas::updateRenderImage(const QImage &img)
@@ -79,11 +81,20 @@ QWidget *Edit3DCanvas::busyIndicator() const
return m_busyIndicator;
}
+#ifdef Q_OS_MACOS
+extern "C" bool AXIsProcessTrusted();
+#endif
+
void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos)
{
if (m_flyMode == enabled)
return;
+#ifdef Q_OS_MACOS
+ if (!AXIsProcessTrusted())
+ m_isTrusted = false;
+#endif
+
m_flyMode = enabled;
if (enabled) {
@@ -132,6 +143,23 @@ void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos)
m_parent->view()->setFlyMode(enabled);
}
+bool Edit3DCanvas::eventFilter(QObject *obj, QEvent *event)
+{
+ if (m_flyMode && event->type() == QEvent::ShortcutOverride) {
+ // Suppress shortcuts that conflict with fly mode keys
+ const QList<int> controlKeys = { Qt::Key_W, Qt::Key_A, Qt::Key_S,
+ Qt::Key_D, Qt::Key_Q, Qt::Key_E,
+ Qt::Key_Up, Qt::Key_Down, Qt::Key_Left,
+ Qt::Key_Right, Qt::Key_PageDown, Qt::Key_PageUp,
+ Qt::Key_Alt, Qt::Key_Shift };
+ auto ke = static_cast<QKeyEvent *>(event);
+ if (controlKeys.contains(ke->key()))
+ event->accept();
+ }
+
+ return QObject::eventFilter(obj, event);
+}
+
void Edit3DCanvas::mousePressEvent(QMouseEvent *e)
{
m_contextMenuPending = false;
@@ -171,7 +199,8 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e)
// We notify explicit camera rotation need for puppet rather than rely in mouse events,
// as mouse isn't grabbed on puppet side and can't handle fast movements that go out of
// edit camera mouse area. This also simplifies split view handling.
- QPointF diff = m_hiddenCursorPos - e->globalPos();
+ QPointF diff = m_isTrusted ? (m_hiddenCursorPos - e->globalPos()) : (m_lastCursorPos - e->globalPos());
+
if (e->buttons() == (Qt::LeftButton | Qt::RightButton)) {
m_parent->view()->emitView3DAction(View3DActionType::EditCameraMove,
QVector3D{float(-diff.x()), float(-diff.y()), 0.f});
@@ -182,13 +211,26 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e)
// Skip first move to avoid undesirable jump occasionally when initiating flight mode
m_flyModeFirstUpdate = false;
}
- QCursor::setPos(m_hiddenCursorPos);
+
+ if (m_isTrusted)
+ QCursor::setPos(m_hiddenCursorPos);
+ else
+ m_lastCursorPos = e->globalPos();
}
}
void Edit3DCanvas::wheelEvent(QWheelEvent *e)
{
- m_parent->view()->sendInputEvent(e);
+ if (m_flyMode) {
+ // In fly mode, wheel controls the camera speed slider (value range 1-100)
+ double speed;
+ double mult;
+ m_parent->view()->getCameraSpeedAuxData(speed, mult);
+ speed = qMin(100., qMax(1., speed + double(e->angleDelta().y()) / 40.));
+ m_parent->view()->setCameraSpeedAuxData(speed, mult);
+ } else {
+ m_parent->view()->sendInputEvent(e);
+ }
QWidget::wheelEvent(e);
}
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h
index 39207554a7..16c1063dd6 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.h
@@ -30,6 +30,7 @@ public:
bool isFlyMode() const { return m_flyMode; }
protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
void mouseDoubleClickEvent(QMouseEvent *e) override;
@@ -52,10 +53,12 @@ private:
qint32 m_activeScene = -1;
QElapsedTimer m_usageTimer;
qreal m_opacity = 1.0;
+ bool m_isTrusted = true;
QWidget *m_busyIndicator = nullptr;
bool m_flyMode = false;
QPoint m_flyModeStartCursorPos;
QPoint m_hiddenCursorPos;
+ QPoint m_lastCursorPos;
qint64 m_flyModeStartTime = 0;
bool m_flyModeFirstUpdate = false;
bool m_contextMenuPending = false;
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
index 911ee1fb15..bb7404f252 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp
@@ -20,9 +20,11 @@
#include "nodeinstanceview.h"
#include "qmldesignerconstants.h"
#include "qmldesignerplugin.h"
+#include "qmlitemnode.h"
#include "qmlvisualnode.h"
#include "seekerslider.h"
#include "snapconfiguration.h"
+#include "variantproperty.h"
#include <auxiliarydataproperties.h>
#include <model/modelutils.h>
@@ -133,6 +135,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
const QString orientationKey = QStringLiteral("globalOrientation");
const QString editLightKey = QStringLiteral("showEditLight");
const QString gridKey = QStringLiteral("showGrid");
+ const QString showLookAtKey = QStringLiteral("showLookAt");
const QString selectionBoxKey = QStringLiteral("showSelectionBox");
const QString iconGizmoKey = QStringLiteral("showIconGizmo");
const QString cameraFrustumKey = QStringLiteral("showCameraFrustum");
@@ -187,6 +190,11 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
else
m_showGridAction->action()->setChecked(false);
+ if (sceneState.contains(showLookAtKey))
+ m_showLookAtAction->action()->setChecked(sceneState[showLookAtKey].toBool());
+ else
+ m_showLookAtAction->action()->setChecked(false);
+
if (sceneState.contains(selectionBoxKey))
m_showSelectionBoxAction->action()->setChecked(sceneState[selectionBoxKey].toBool());
else
@@ -235,36 +243,10 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
state.showWireframe = false;
}
- // Syncing background color only makes sense for children of View3D instances
- bool syncValue = false;
- bool syncEnabled = false;
- bool desiredSyncValue = false;
if (sceneState.contains(syncEnvBgKey))
- desiredSyncValue = sceneState[syncEnvBgKey].toBool();
- ModelNode checkNode = Utils3D::active3DSceneNode(this);
- const bool activeSceneValid = checkNode.isValid();
-
- while (checkNode.isValid()) {
- if (checkNode.metaInfo().isQtQuick3DView3D()) {
- syncValue = desiredSyncValue;
- syncEnabled = true;
- break;
- }
- if (checkNode.hasParentProperty())
- checkNode = checkNode.parentProperty().parentModelNode();
- else
- break;
- }
-
- if (activeSceneValid && syncValue != desiredSyncValue) {
- // Update actual toolstate as well if we overrode it.
- QTimer::singleShot(0, this, [this, syncValue]() {
- emitView3DAction(View3DActionType::SyncEnvBackground, syncValue);
- });
- }
-
- m_syncEnvBackgroundAction->action()->setChecked(syncValue);
- m_syncEnvBackgroundAction->action()->setEnabled(syncEnabled);
+ m_syncEnvBackgroundAction->action()->setChecked(sceneState[syncEnvBgKey].toBool());
+ else
+ m_syncEnvBackgroundAction->action()->setChecked(false);
// Selection context change updates visible and enabled states
SelectionContext selectionContext(this);
@@ -273,12 +255,22 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
m_bakeLightsAction->currentContextChanged(selectionContext);
syncCameraSpeedToNewView();
+
+ storeCurrentSceneEnvironment();
}
void Edit3DView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
+ QString currProjectPath = QmlDesigner::DocumentManager::currentProjectDirPath().toString();
+ if (m_currProjectPath != currProjectPath) {
+ // Opening a new project -> reset camera speeds
+ m_currProjectPath = currProjectPath;
+ m_previousCameraSpeed = -1.;
+ m_previousCameraMultiplier = -1.;
+ }
+
syncSnapAuxPropsToSettings();
rootModelNode().setAuxiliaryData(edit3dGridColorProperty,
@@ -342,11 +334,10 @@ void Edit3DView::handleEntriesChanged()
{EK_importedModels, {tr("Imported Models"), contextIcon(DesignerIcons::ImportedModelsIcon)}}};
#ifdef QDS_USE_PROJECTSTORAGE
- const auto &projectStorage = *model()->projectStorage();
auto append = [&](const NodeMetaInfo &metaInfo, ItemLibraryEntryKeys key) {
auto entries = metaInfo.itemLibrariesEntries();
if (entries.size())
- entriesMap[key].entryList.append(toItemLibraryEntries(entries, projectStorage));
+ entriesMap[key].entryList.append(toItemLibraryEntries(entries));
};
append(model()->qtQuick3DModelMetaInfo(), EK_primitives);
@@ -356,7 +347,12 @@ void Edit3DView::handleEntriesChanged()
append(model()->qtQuick3DOrthographicCameraMetaInfo(), EK_cameras);
append(model()->qtQuick3DPerspectiveCameraMetaInfo(), EK_cameras);
- auto assetsModule = model()->module("Quick3DAssets");
+ Utils::PathString import3dTypePrefix = QmlDesignerPlugin::instance()
+ ->documentManager()
+ .generatedComponentUtils()
+ .import3dTypePrefix();
+
+ auto assetsModule = model()->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary);
for (const auto &metaInfo : model()->metaInfosForModule(assetsModule))
append(metaInfo, EK_importedModels);
@@ -373,8 +369,12 @@ void Edit3DView::handleEntriesChanged()
} else if (entry.typeName() == "QtQuick3D.OrthographicCamera"
|| entry.typeName() == "QtQuick3D.PerspectiveCamera") {
entryKey = EK_cameras;
- } else if (entry.typeName().startsWith("Quick3DAssets.")
- && NodeHints::fromItemLibraryEntry(entry).canBeDroppedInView3D()) {
+ } else if (entry.typeName().startsWith(QmlDesignerPlugin::instance()
+ ->documentManager()
+ .generatedComponentUtils()
+ .import3dTypePrefix()
+ .toUtf8())
+ && NodeHints::fromItemLibraryEntry(entry, model()).canBeDroppedInView3D()) {
entryKey = EK_importedModels;
} else {
continue;
@@ -448,6 +448,7 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view,
self->emitView3DAction(View3DActionType::GetNodeAtMainScenePos,
QVariantList{data[0], nodeList[0].internalId()});
self->m_nodeAtPosReqType = NodeAtPosReqType::MainScenePick;
+ self->m_pickView3dNode = nodeList[0];
});
}
}
@@ -490,7 +491,7 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos
} else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleMaterialDrop) {
emitCustomNotification("drop_bundle_material", {modelNode}); // To ContentLibraryView
} else if (m_nodeAtPosReqType == NodeAtPosReqType::BundleEffectDrop) {
- emitCustomNotification("drop_bundle_effect", {modelNode}, {pos3d}); // To ContentLibraryView
+ emitCustomNotification("drop_bundle_item", {modelNode}, {pos3d}); // To ContentLibraryView
} else if (m_nodeAtPosReqType == NodeAtPosReqType::TextureDrop) {
emitCustomNotification("apply_texture_to_model3D", {modelNode, m_droppedModelNode});
} else if (m_nodeAtPosReqType == NodeAtPosReqType::AssetDrop) {
@@ -500,6 +501,8 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos
} else if (m_nodeAtPosReqType == NodeAtPosReqType::MainScenePick) {
if (modelNode.isValid())
setSelectedModelNode(modelNode);
+ else if (m_pickView3dNode.isValid() && !m_pickView3dNode.isSelected())
+ setSelectedModelNode(m_pickView3dNode);
emitView3DAction(View3DActionType::AlignViewToCamera, true);
}
@@ -523,6 +526,21 @@ void Edit3DView::nodeRemoved(const ModelNode &,
updateAlignActionStates();
}
+void Edit3DView::propertiesRemoved(const QList<AbstractProperty> &propertyList)
+{
+ maybeStoreCurrentSceneEnvironment(propertyList);
+}
+
+void Edit3DView::bindingPropertiesChanged(const QList<BindingProperty> &propertyList, PropertyChangeFlags)
+{
+ maybeStoreCurrentSceneEnvironment(propertyList);
+}
+
+void Edit3DView::variantPropertiesChanged(const QList<VariantProperty> &propertyList, PropertyChangeFlags)
+{
+ maybeStoreCurrentSceneEnvironment(propertyList);
+}
+
void Edit3DView::sendInputEvent(QEvent *e) const
{
if (nodeInstanceView())
@@ -699,6 +717,30 @@ QPoint Edit3DView::resolveToolbarPopupPos(Edit3DAction *action) const
return pos;
}
+template<typename T, typename>
+void Edit3DView::maybeStoreCurrentSceneEnvironment(const QList<T> &propertyList)
+{
+ QSet<qint32> handledNodes;
+ QmlObjectNode sceneEnv;
+ for (const AbstractProperty &prop : propertyList) {
+ ModelNode node = prop.parentModelNode();
+ const qint32 id = node.internalId();
+ if (handledNodes.contains(id))
+ continue;
+
+ handledNodes.insert(id);
+ if (!node.metaInfo().isQtQuick3DSceneEnvironment())
+ continue;
+
+ if (!sceneEnv.isValid())
+ sceneEnv = currentSceneEnv();
+ if (sceneEnv == node) {
+ storeCurrentSceneEnvironment();
+ break;
+ }
+ }
+}
+
void Edit3DView::showContextMenu()
{
// If request for context menu is still pending, skip for now
@@ -719,32 +761,6 @@ void Edit3DView::showContextMenu()
void Edit3DView::setFlyMode(bool enabled)
{
emitView3DAction(View3DActionType::FlyModeToggle, enabled);
-
- // Disable any actions with conflicting hotkeys
- if (enabled) {
- m_flyModeDisabledActions.clear();
- const QList<QKeySequence> controlKeys = { Qt::Key_W, Qt::Key_A, Qt::Key_S,
- Qt::Key_D, Qt::Key_Q, Qt::Key_E,
- Qt::Key_Up, Qt::Key_Down, Qt::Key_Left,
- Qt::Key_Right, Qt::Key_PageDown, Qt::Key_PageUp};
- for (auto i = m_edit3DActions.cbegin(), end = m_edit3DActions.cend(); i != end; ++i) {
- for (const QKeySequence &controlKey : controlKeys) {
- if (Core::Command *cmd = m_edit3DWidget->actionToCommandHash().value(i.value()->action())) {
- if (cmd->keySequence().matches(controlKey) == QKeySequence::ExactMatch) {
- if (i.value()->action()->isEnabled()) {
- m_flyModeDisabledActions.append(i.value());
- i.value()->action()->setEnabled(false);
- }
- break;
- }
- }
- }
- }
- } else {
- for (Edit3DAction *action : std::as_const(m_flyModeDisabledActions))
- action->action()->setEnabled(true);
- m_flyModeDisabledActions.clear();
- }
}
void Edit3DView::syncSnapAuxPropsToSettings()
@@ -814,6 +830,75 @@ void Edit3DView::syncCameraSpeedToNewView()
setCameraSpeedAuxData(speed, multiplier);
}
+QmlObjectNode Edit3DView::currentSceneEnv()
+{
+ PropertyName envProp{"environment"};
+ ModelNode checkNode = Utils3D::active3DSceneNode(this);
+ while (checkNode.isValid()) {
+ if (checkNode.metaInfo().isQtQuick3DView3D()) {
+ QmlObjectNode sceneEnvNode = QmlItemNode(checkNode).bindingProperty(envProp)
+ .resolveToModelNode();
+ if (sceneEnvNode.isValid())
+ return sceneEnvNode;
+ break;
+ }
+ if (checkNode.hasParentProperty())
+ checkNode = checkNode.parentProperty().parentModelNode();
+ else
+ break;
+ }
+ return {};
+}
+
+void Edit3DView::storeCurrentSceneEnvironment()
+{
+ // If current active scene has scene environment, store relevant properties
+ QmlObjectNode sceneEnvNode = currentSceneEnv();
+ if (sceneEnvNode.isValid()) {
+ QVariantMap lastSceneEnvData;
+
+ auto insertPropValue = [](const PropertyName prop, const QmlObjectNode &node,
+ QVariantMap &map) {
+ if (!node.hasProperty(prop))
+ return;
+
+ map.insert(QString::fromUtf8(prop), node.modelValue(prop));
+ };
+
+ auto insertTextureProps = [&](const PropertyName prop) {
+ // For now we just grab the absolute path of texture source for simplicity
+ if (!sceneEnvNode.hasProperty(prop))
+ return;
+
+ QmlObjectNode bindNode = QmlItemNode(sceneEnvNode).bindingProperty(prop)
+ .resolveToModelNode();
+ if (bindNode.isValid()) {
+ QVariantMap props;
+ const PropertyName sourceProp = "source";
+ if (bindNode.hasProperty(sourceProp)) {
+ Utils::FilePath qmlPath = Utils::FilePath::fromUrl(
+ model()->fileUrl()).absolutePath();
+ Utils::FilePath sourcePath = Utils::FilePath::fromUrl(
+ bindNode.modelValue(sourceProp).toUrl());
+
+ sourcePath = qmlPath.resolvePath(sourcePath);
+
+ props.insert(QString::fromUtf8(sourceProp),
+ sourcePath.absoluteFilePath().toUrl());
+ }
+ lastSceneEnvData.insert(QString::fromUtf8(prop), props);
+ }
+ };
+
+ insertPropValue("backgroundMode", sceneEnvNode, lastSceneEnvData);
+ insertPropValue("clearColor", sceneEnvNode, lastSceneEnvData);
+ insertTextureProps("lightProbe");
+ insertTextureProps("skyBoxCubeMap");
+
+ emitView3DAction(View3DActionType::SetLastSceneEnvData, lastSceneEnvData);
+ }
+}
+
const QList<Edit3DView::SplitToolState> &Edit3DView::splitToolStates() const
{
return m_splitToolStates;
@@ -961,6 +1046,18 @@ void Edit3DView::createEdit3DActions()
nullptr,
QCoreApplication::translate("ShowGridAction", "Toggle the visibility of the helper grid."));
+ m_showLookAtAction = std::make_unique<Edit3DAction>(
+ QmlDesigner::Constants::EDIT3D_EDIT_SHOW_LOOKAT,
+ View3DActionType::ShowLookAt,
+ QCoreApplication::translate("ShowLookAtAction", "Show Look-at"),
+ QKeySequence(Qt::Key_L),
+ true,
+ true,
+ QIcon(),
+ this,
+ nullptr,
+ QCoreApplication::translate("ShowLookAtAction", "Toggle the visibility of the edit camera look-at indicator."));
+
m_showSelectionBoxAction = std::make_unique<Edit3DAction>(
QmlDesigner::Constants::EDIT3D_EDIT_SHOW_SELECTION_BOX,
View3DActionType::ShowSelectionBox,
@@ -1264,6 +1361,7 @@ void Edit3DView::createEdit3DActions()
m_rightActions << m_resetAction.get();
m_visibilityToggleActions << m_showGridAction.get();
+ m_visibilityToggleActions << m_showLookAtAction.get();
m_visibilityToggleActions << m_showSelectionBoxAction.get();
m_visibilityToggleActions << m_showIconGizmoAction.get();
m_visibilityToggleActions << m_showCameraFrustumAction.get();
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.h b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
index 781b26d8d8..755efc0ae3 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dview.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.h
@@ -8,6 +8,7 @@
#include <abstractview.h>
#include <modelcache.h>
+#include <qmlobjectnode.h>
#include <QImage>
#include <QPointer>
@@ -59,6 +60,11 @@ public:
PropertyChangeFlags propertyChange) override;
void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty,
PropertyChangeFlags propertyChange) override;
+ void propertiesRemoved(const QList<AbstractProperty> &propertyList) override;
+ void bindingPropertiesChanged(const QList<BindingProperty> &propertyList,
+ PropertyChangeFlags propertyChange) override;
+ void variantPropertiesChanged(const QList<VariantProperty> &propertyList,
+ PropertyChangeFlags propertyChange) override;
void sendInputEvent(QEvent *e) const;
void edit3DViewResized(const QSize &size) const;
@@ -127,9 +133,14 @@ private:
void createSyncEnvBackgroundAction();
void createSeekerSliderAction();
void syncCameraSpeedToNewView();
+ QmlObjectNode currentSceneEnv();
+ void storeCurrentSceneEnvironment();
QPoint resolveToolbarPopupPos(Edit3DAction *action) const;
+ template<typename T, typename = typename std::enable_if<std::is_base_of<AbstractProperty , T>::value>::type>
+ void maybeStoreCurrentSceneEnvironment(const QList<T> &propertyList);
+
QPointer<Edit3DWidget> m_edit3DWidget;
QVector<Edit3DAction *> m_leftActions;
QVector<Edit3DAction *> m_rightActions;
@@ -148,6 +159,7 @@ private:
std::unique_ptr<Edit3DAction> m_orientationModeAction;
std::unique_ptr<Edit3DAction> m_editLightAction;
std::unique_ptr<Edit3DAction> m_showGridAction;
+ std::unique_ptr<Edit3DAction> m_showLookAtAction;
std::unique_ptr<Edit3DAction> m_showSelectionBoxAction;
std::unique_ptr<Edit3DAction> m_showIconGizmoAction;
std::unique_ptr<Edit3DAction> m_showCameraFrustumAction;
@@ -187,11 +199,12 @@ private:
int m_activeSplit = 0;
QList<SplitToolState> m_splitToolStates;
- QList<Edit3DAction *> m_flyModeDisabledActions;
ModelNode m_contextMenuPendingNode;
+ ModelNode m_pickView3dNode;
double m_previousCameraSpeed = -1.;
double m_previousCameraMultiplier = -1.;
+ QString m_currProjectPath;
friend class Edit3DAction;
};
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
index 07102ae893..f6bbf8d794 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.cpp
@@ -2,37 +2,41 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "edit3dwidget.h"
-#include "designdocument.h"
-#include "designericons.h"
+
#include "edit3dactions.h"
#include "edit3dcanvas.h"
#include "edit3dtoolbarmenu.h"
#include "edit3dview.h"
-#include "externaldependenciesinterface.h"
-#include "materialutils.h"
-#include "metainfo.h"
-#include "modelnodeoperations.h"
-#include "nodeabstractproperty.h"
-#include "nodehints.h"
-#include "qmldesignerconstants.h"
-#include "qmldesignerplugin.h"
-#include "qmleditormenu.h"
-#include "qmlvisualnode.h"
-#include "viewmanager.h"
-#include <utils3d.h>
#include <auxiliarydataproperties.h>
#include <designeractionmanager.h>
+#include <designdocument.h>
+#include <designericons.h>
#include <designermcumanager.h>
+#include <externaldependenciesinterface.h>
+#include <generatedcomponentutils.h>
#include <import.h>
-#include <model/modelutils.h>
+#include <materialutils.h>
+#include <metainfo.h>
+#include <modelnodeoperations.h>
+#include <nodeabstractproperty.h>
+#include <nodehints.h>
#include <nodeinstanceview.h>
+#include <qmldesignerconstants.h>
+#include <qmldesignerplugin.h>
+#include <qmleditormenu.h>
+#include <qmlvisualnode.h>
#include <seekerslider.h>
+#include <toolbox.h>
+#include <viewmanager.h>
+#include <utils3d.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/icore.h>
-#include <toolbox.h>
+
+#include <model/modelutils.h>
+
#include <utils/asset.h>
#include <utils/qtcassert.h>
#include <utils/utilsicons.h>
@@ -359,6 +363,14 @@ void Edit3DWidget::createContextMenu()
resetAction->setToolTip(tr("Reset all shading options for all viewports."));
m_contextMenu->addSeparator();
+
+ m_addToContentLibAction = m_contextMenu->addAction(
+ contextIcon(DesignerIcons::CreateIcon), // TODO: placeholder icon
+ tr("Add to Content Library"), [&] {
+ view()->emitCustomNotification("add_3d_to_content_lib", {m_contextMenuTarget});
+ });
+
+ m_contextMenu->addSeparator();
}
bool Edit3DWidget::isPasteAvailable() const
@@ -402,7 +414,7 @@ void Edit3DWidget::showOnboardingLabel()
" in the"
" <b>Assets</b>"
" view.");
- text = labelText.arg(Utils::creatorTheme()->color(Utils::Theme::TextColorLink).name());
+ text = labelText.arg(Utils::creatorColor(Utils::Theme::TextColorLink).name());
} else {
text = tr("3D view is not supported in Qt5 projects.");
}
@@ -608,14 +620,18 @@ void Edit3DWidget::showBackgroundColorMenu(bool show, const QPoint &pos)
void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d)
{
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
m_contextMenuTarget = modelNode;
m_contextMenuPos3d = pos3d;
const bool isModel = modelNode.metaInfo().isQtQuick3DModel();
+ const bool isNode = modelNode.metaInfo().isQtQuick3DNode();
const bool allowAlign = view()->edit3DAction(View3DActionType::AlignCamerasToView)->action()->isEnabled();
const bool isSingleComponent = view()->hasSingleSelectedModelNode() && modelNode.isComponent();
const bool anyNodeSelected = view()->hasSelectedModelNodes();
const bool selectionExcludingRoot = anyNodeSelected && !view()->rootModelNode().isSelected();
+ const bool isInBundle = modelNode.type().startsWith(compUtils.componentBundlesTypePrefix().toLatin1());
if (m_createSubMenu)
m_createSubMenu->setEnabled(!isSceneLocked());
@@ -633,6 +649,7 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode
m_toggleGroupAction->setEnabled(true);
m_bakeLightsAction->setVisible(view()->bakeLightsAction()->action()->isVisible());
m_bakeLightsAction->setEnabled(view()->bakeLightsAction()->action()->isEnabled());
+ m_addToContentLibAction->setEnabled(isNode && !isInBundle);
if (m_view) {
int idx = m_view->activeSplit();
@@ -685,7 +702,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
} else if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData())
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL)
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)
- || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)
+ || dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_TEXTURE)) {
if (Utils3D::active3DSceneNode(m_view).isValid())
dragEnterEvent->acceptProposedAction();
@@ -694,7 +711,7 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
if (!data.isEmpty()) {
QDataStream stream(data);
stream >> m_draggedEntry;
- if (NodeHints::fromItemLibraryEntry(m_draggedEntry).canBeDroppedInView3D())
+ if (NodeHints::fromItemLibraryEntry(m_draggedEntry, view()->model()).canBeDroppedInView3D())
dragEnterEvent->acceptProposedAction();
}
}
@@ -730,8 +747,8 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
return;
}
- // handle dropping bundle effects
- if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)) {
+ // handle dropping bundle items
+ if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) {
m_view->dropBundleEffect(pos);
m_view->model()->endDrag();
return;
@@ -766,9 +783,14 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
QString fileName = QFileInfo(assetPath).baseName();
fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter
auto model = m_view->model();
- auto metaInfo = model->metaInfo(model->module("Quick3DAssets"), fileName.toUtf8());
+ Utils::PathString import3dTypePrefix = QmlDesignerPlugin::instance()
+ ->documentManager()
+ .generatedComponentUtils()
+ .import3dTypePrefix();
+ auto moduleId = model->module(import3dTypePrefix, Storage::ModuleKind::QmlLibrary);
+ auto metaInfo = model->metaInfo(moduleId, fileName.toUtf8());
if (auto entries = metaInfo.itemLibrariesEntries(); entries.size()) {
- auto entry = ItemLibraryEntry{entries.front(), *model->projectStorage()};
+ auto entry = ItemLibraryEntry{entries.front()};
QmlVisualNode::createQml3DNode(view(), entry, m_canvas->activeScene(), {}, false);
}
}
@@ -780,7 +802,9 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
for (const QString &assetPath : added3DAssets) {
QString fileName = QFileInfo(assetPath).baseName();
fileName = fileName.at(0).toUpper() + fileName.mid(1); // capitalize first letter
- QString type = QString("Quick3DAssets.%1.%1").arg(fileName);
+ QString type = QString("%1.%2.%2").arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().import3dTypePrefix(),
+ fileName);
QList<ItemLibraryEntry> entriesForType = itemLibInfo->entriesForType(type.toUtf8());
if (!entriesForType.isEmpty()) { // should always be true, but just in case
QmlVisualNode::createQml3DNode(view(),
diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
index 211b044d41..97c0469668 100644
--- a/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
+++ b/src/plugins/qmldesigner/components/edit3d/edit3dwidget.h
@@ -100,6 +100,7 @@ private:
QPointer<QAction> m_selectParentAction;
QPointer<QAction> m_toggleGroupAction;
QPointer<QAction> m_wireFrameAction;
+ QPointer<QAction> m_addToContentLibAction;
QHash<int, QPointer<QAction>> m_matOverrideActions;
QPointer<QMenu> m_createSubMenu;
ModelNode m_contextMenuTarget;
diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp
index 26c5fe0eb2..8890ea8964 100644
--- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp
+++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.cpp
@@ -87,9 +87,11 @@ void SnapConfiguration::resetDefaults()
void SnapConfiguration::hideCursor()
{
- if (QGuiApplication::overrideCursor())
+ if (m_cursorHidden)
return;
+ m_cursorHidden = true;
+
QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
if (QWindow *w = QGuiApplication::focusWindow())
@@ -98,9 +100,11 @@ void SnapConfiguration::hideCursor()
void SnapConfiguration::restoreCursor()
{
- if (!QGuiApplication::overrideCursor())
+ if (!m_cursorHidden)
return;
+ m_cursorHidden = false;
+
QGuiApplication::restoreOverrideCursor();
if (QWindow *w = QGuiApplication::focusWindow())
@@ -109,7 +113,7 @@ void SnapConfiguration::restoreCursor()
void SnapConfiguration::holdCursorInPlace()
{
- if (!QGuiApplication::overrideCursor())
+ if (!m_cursorHidden)
return;
if (QWindow *w = QGuiApplication::focusWindow())
diff --git a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h
index 729e6ce7d1..ae401f60f9 100644
--- a/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h
+++ b/src/plugins/qmldesigner/components/edit3d/snapconfiguration.h
@@ -103,6 +103,7 @@ private:
double m_scaleInterval = 0.;
bool m_changes = false;
QPoint m_lastPos;
+ bool m_cursorHidden = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
index 0b7d199b50..a6494811b6 100644
--- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
@@ -206,9 +206,16 @@ static ItemLibraryEntry itemLibraryEntryFromMimeData(const QMimeData *mimeData)
return itemLibraryEntry;
}
-static bool canBeDropped(const QMimeData *mimeData)
+static bool canBeDropped(const QMimeData *mimeData, Model *model)
{
- return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData)).canBeDroppedInFormEditor();
+#ifdef QDS_USE_PROJECTSTORAGE
+ auto itemLibraryEntry = itemLibraryEntryFromMimeData(mimeData);
+ NodeMetaInfo metaInfo{itemLibraryEntry.typeId(), model->projectStorage()};
+ return metaInfo.canBeDroppedInFormEditor() == FlagIs::True;
+#else
+ return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData), model)
+ .canBeDroppedInFormEditor();
+#endif
}
static bool hasItemLibraryInfo(const QMimeData *mimeData)
@@ -218,7 +225,7 @@ static bool hasItemLibraryInfo(const QMimeData *mimeData)
void DragTool::dropEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSceneDragDropEvent *event)
{
- if (canBeDropped(event->mimeData())) {
+ if (canBeDropped(event->mimeData(), view()->model())) {
event->accept();
end(generateUseSnapping(event->modifiers()));
@@ -290,7 +297,7 @@ void DragTool::dropEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSceneD
void DragTool::dragEnterEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSceneDragDropEvent *event)
{
- if (canBeDropped(event->mimeData())) {
+ if (canBeDropped(event->mimeData(), view()->model())) {
m_blockMove = false;
if (hasItemLibraryInfo(event->mimeData())) {
@@ -306,7 +313,7 @@ void DragTool::dragEnterEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraph
void DragTool::dragLeaveEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSceneDragDropEvent *event)
{
- if (canBeDropped(event->mimeData())) {
+ if (canBeDropped(event->mimeData(), view()->model())) {
event->accept();
m_moveManipulator.end();
@@ -363,10 +370,8 @@ void DragTool::dragMoveEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSc
->data(Constants::MIME_TYPE_ASSETS)).split(',');
QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPaths[0]).first;
- if (!m_blockMove
- && !m_isAborted
- && canBeDropped(event->mimeData())
- && assetType != Constants::MIME_TYPE_ASSET_EFFECT) {
+ if (!m_blockMove && !m_isAborted && canBeDropped(event->mimeData(), view()->model())
+ && assetType != Constants::MIME_TYPE_ASSET_EFFECT) {
event->accept();
if (!m_dragNodes.isEmpty()) {
if (targetContainerItem) {
diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.h b/src/plugins/qmldesigner/components/formeditor/dragtool.h
index 1cd2c9f487..c1d5626d28 100644
--- a/src/plugins/qmldesigner/components/formeditor/dragtool.h
+++ b/src/plugins/qmldesigner/components/formeditor/dragtool.h
@@ -7,7 +7,6 @@
#include "selectionindicator.h"
#include <QObject>
-#include <QScopedPointer>
#include <QPointer>
namespace QmlDesigner {
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp
index 35a48a6b6d..dd239dd966 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorannotationicon.cpp
@@ -302,9 +302,9 @@ QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(QRectF rect, const
const QString &author, const QString &text,
const QString &date, QGraphicsItem *parent)
{
- static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::DStextColor);
- static QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColorDarker);
- static QColor frameColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor);
+ static QColor textColor = Utils::creatorColor(Utils::Theme::DStextColor);
+ static QColor backgroundColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColorDarker);
+ static QColor frameColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColor);
QFont font;
font.setBold(true);
@@ -405,9 +405,9 @@ QGraphicsItem *FormEditorAnnotationIcon::createCommentBubble(QRectF rect, const
QGraphicsItem *FormEditorAnnotationIcon::createTitleBubble(const QRectF &rect, const QString &text, QGraphicsItem *parent)
{
- static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::DStextColor);
- static QColor backgroundColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColorDarker);
- static QColor frameColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_BackgroundColor);
+ static QColor textColor = Utils::creatorColor(Utils::Theme::DStextColor);
+ static QColor backgroundColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColorDarker);
+ static QColor frameColor = Utils::creatorColor(Utils::Theme::QmlDesigner_BackgroundColor);
QFont font;
font.setBold(true);
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp
index 9549ce9dd4..d835274a97 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp
@@ -3,7 +3,6 @@
#include "formeditorgraphicsview.h"
#include "backgroundaction.h"
-#include "formeditoritem.h"
#include "formeditorwidget.h"
#include "navigation2d.h"
@@ -76,11 +75,11 @@ bool FormEditorGraphicsView::eventFilter(QObject *watched, QEvent *event)
auto mouseEvent = static_cast<QMouseEvent *>(event);
if (!m_panningStartPosition.isNull()) {
horizontalScrollBar()->setValue(horizontalScrollBar()->value() -
- (mouseEvent->x() - m_panningStartPosition.x()));
+ (mouseEvent->position().x() - m_panningStartPosition.x()));
verticalScrollBar()->setValue(verticalScrollBar()->value() -
- (mouseEvent->y() - m_panningStartPosition.y()));
+ (mouseEvent->position().y() - m_panningStartPosition.y()));
}
- m_panningStartPosition = mouseEvent->pos();
+ m_panningStartPosition = mouseEvent->position();
event->accept();
return true;
}
@@ -215,7 +214,7 @@ void FormEditorGraphicsView::drawBackground(QPainter *painter, const QRectF &rec
painter->fillRect(rectangle.intersected(rootItemRect()), backgroundBrush());
}
- QPen pen(Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor));
+ QPen pen(Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor));
pen.setStyle(Qt::DotLine);
pen.setWidth(1);
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h
index 60e02582cd..e1e61a8f1e 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h
+++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.h
@@ -46,7 +46,7 @@ private:
void stopPanning(QEvent *event);
Panning m_isPanning = Panning::NotStarted;
- QPoint m_panningStartPosition;
+ QPointF m_panningStartPosition;
QRectF m_rootItemRect;
QImage m_backgroundImage;
};
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
index 12da85e2c4..b3df6b8fe7 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
@@ -48,23 +48,19 @@ void drawIcon(QPainter *painter,
int iconSize,
const QColor &penColor)
{
- static QFontDatabase a;
-
const QString fontName = "qtds_propertyIconFont.ttf";
- Q_ASSERT(a.hasFamily(fontName));
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), return);
- if (a.hasFamily(fontName)) {
- QFont font(fontName);
- font.setPixelSize(fontSize);
+ QFont font(fontName);
+ font.setPixelSize(fontSize);
- painter->save();
- painter->setPen(penColor);
- painter->setFont(font);
- painter->drawText(QRectF(x, y, iconSize, iconSize), iconSymbol);
+ painter->save();
+ painter->setPen(penColor);
+ painter->setFont(font);
+ painter->drawText(QRectF(x, y, iconSize, iconSize), iconSymbol);
- painter->restore();
- }
+ painter->restore();
}
FormEditorScene *FormEditorItem::scene() const {
@@ -309,7 +305,7 @@ void FormEditorItem::paintBoundingRect(QPainter *painter) const
pen.setJoinStyle(Qt::MiterJoin);
const QColor frameColor(0xaa, 0xaa, 0xaa);
- static const QColor selectionColor = Utils::creatorTheme()->color(
+ static const QColor selectionColor = Utils::creatorColor(
Utils::Theme::QmlDesigner_FormEditorSelectionColor);
if (scene()->showBoundingRects()) {
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp b/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp
index 555d0d90e3..f3aa96ada4 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditortoolbutton.cpp
@@ -42,7 +42,7 @@ void FormEditorToolButton::paint(QPainter *painter, const QStyleOptionGraphicsIt
QRectF adjustedRect(size().width() - toolButtonSize, size().height() - toolButtonSize, toolButtonSize, toolButtonSize);
painter->setPen(Qt::NoPen);
- static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
+ static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
if (m_state == Hovered)
painter->setBrush(selectionColor.lighter(110));
diff --git a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp
index 0f5b5f4438..45d26f831e 100644
--- a/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/selectionindicator.cpp
@@ -78,7 +78,7 @@ void SelectionIndicator::setItems(const QList<FormEditorItem*> &itemList)
{
clear();
- static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
+ static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
for (FormEditorItem *item : itemList) {
if (!item->qmlItemNode().isValid())
@@ -119,7 +119,7 @@ void SelectionIndicator::setItems(const QList<FormEditorItem*> &itemList)
m_annotationItem = nullptr;
}
- static QColor textColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorForegroundColor);
+ static QColor textColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorForegroundColor);
textItem->setDefaultTextColor(textColor);
QPolygonF labelPolygon = boundingRectInLayerItemSpaceForItem(selectedItem, m_layerItem.data());
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp
index c0bebbb82b..aa2dfd3b28 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp
@@ -64,13 +64,14 @@ namespace QmlDesigner {
DesignDocument acts as a facade to a model representing a qml document,
and the different views/widgets accessing it.
*/
-DesignDocument::DesignDocument(ProjectStorageDependencies projectStorageDependencies,
+DesignDocument::DesignDocument([[maybe_unused]] const QUrl &filePath,
+ ProjectStorageDependencies projectStorageDependencies,
ExternalDependenciesInterface &externalDependencies)
#ifdef QDS_USE_PROJECTSTORAGE
: m_documentModel(Model::create(projectStorageDependencies,
"Item",
{Import::createLibraryImport("QtQuick")},
- {},
+ filePath,
std::make_unique<ModelResourceManagement>()))
#else
: m_documentModel(
@@ -149,7 +150,10 @@ bool DesignDocument::loadInFileComponent(const ModelNode &componentNode)
if (!componentNode.isRootNode()) {
//change to subcomponent model
- changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.data(), rewriterView(), componentText, componentNode));
+ changeToInFileComponentModel(createComponentTextModifier(m_documentTextModifier.get(),
+ rewriterView(),
+ componentText,
+ componentNode));
}
return true;
@@ -280,11 +284,10 @@ void DesignDocument::moveNodesToPosition(const QList<ModelNode> &nodes, const st
parentProperty.reparentHere(pastedNode);
QmlVisualNode visualNode(pastedNode);
- if (!firstVisualNode.has_value() && visualNode.isValid()){
+ if (!firstVisualNode && visualNode) {
firstVisualNode = visualNode;
- translationVect = (position.has_value() && firstVisualNode.has_value())
- ? position.value() - firstVisualNode->position()
- : QVector3D();
+ translationVect = (position && firstVisualNode) ? *position - firstVisualNode->position()
+ : QVector3D();
}
visualNode.translate(translationVect);
}
@@ -376,9 +379,12 @@ void DesignDocument::loadDocument(QPlainTextEdit *edit)
m_documentTextModifier.reset(new BaseTextEditModifier(qobject_cast<TextEditor::TextEditorWidget *>(plainTextEdit())));
- connect(m_documentTextModifier.data(), &TextModifier::textChanged, this, &DesignDocument::updateQrcFiles);
+ connect(m_documentTextModifier.get(),
+ &TextModifier::textChanged,
+ this,
+ &DesignDocument::updateQrcFiles);
- m_rewriterView->setTextModifier(m_documentTextModifier.data());
+ m_rewriterView->setTextModifier(m_documentTextModifier.get());
m_inFileComponentTextModifier.reset();
@@ -398,7 +404,7 @@ void DesignDocument::changeToDocumentModel()
if (edit)
edit->document()->clearUndoRedoStacks();
- m_rewriterView->setTextModifier(m_documentTextModifier.data());
+ m_rewriterView->setTextModifier(m_documentTextModifier.get());
m_inFileComponentModel.reset();
m_inFileComponentTextModifier.reset();
@@ -431,7 +437,7 @@ bool DesignDocument::hasProject() const
void DesignDocument::setModified()
{
- if (!m_documentTextModifier.isNull())
+ if (m_documentTextModifier)
m_documentTextModifier->textDocument()->setModified(true);
}
@@ -447,7 +453,7 @@ void DesignDocument::changeToInFileComponentModel(ComponentTextModifier *textMod
m_inFileComponentModel = createInFileComponentModel();
- m_rewriterView->setTextModifier(m_inFileComponentTextModifier.data());
+ m_rewriterView->setTextModifier(m_inFileComponentTextModifier.get());
viewManager().attachRewriterView();
viewManager().attachViewsExceptRewriterAndComponetView();
@@ -674,7 +680,7 @@ void DesignDocument::selectAll()
RewriterView *DesignDocument::rewriterView() const
{
- return m_rewriterView.data();
+ return m_rewriterView.get();
}
void DesignDocument::setEditor(Core::IEditor *editor)
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h
index 0d75141205..1f67ff4b30 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.h
+++ b/src/plugins/qmldesigner/components/integration/designdocument.h
@@ -16,9 +16,10 @@
#include <QObject>
#include <QString>
-
#include <QStackedWidget>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QPlainTextEdit;
QT_END_NAMESPACE
@@ -41,7 +42,8 @@ class QMLDESIGNERCOMPONENTS_EXPORT DesignDocument : public QObject
Q_OBJECT
public:
- DesignDocument(ProjectStorageDependencies projectStorageDependencies,
+ DesignDocument(const QUrl &filePath,
+ ProjectStorageDependencies projectStorageDependencies,
ExternalDependenciesInterface &externalDependencies);
~DesignDocument() override;
@@ -142,12 +144,12 @@ private: // variables
ModelPointer m_documentModel;
ModelPointer m_inFileComponentModel;
QPointer<Core::IEditor> m_textEditor;
- QScopedPointer<BaseTextEditModifier> m_documentTextModifier;
- QScopedPointer<ComponentTextModifier> m_inFileComponentTextModifier;
+ std::unique_ptr<BaseTextEditModifier> m_documentTextModifier;
+ std::unique_ptr<ComponentTextModifier> m_inFileComponentTextModifier;
#ifndef QDS_USE_PROJECTSTORAGE
- QScopedPointer<SubComponentManager> m_subComponentManager;
+ std::unique_ptr<SubComponentManager> m_subComponentManager;
#endif
- QScopedPointer<RewriterView> m_rewriterView;
+ std::unique_ptr<RewriterView> m_rewriterView;
bool m_documentLoaded;
ProjectExplorer::Target *m_currentTarget;
ProjectStorageDependencies m_projectStorageDependencies;
diff --git a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
index 6ef95bf4c4..d97b9ff06f 100644
--- a/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocumentview.cpp
@@ -23,6 +23,8 @@
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
+#include <memory>
+
namespace QmlDesigner {
DesignDocumentView::DesignDocumentView(ExternalDependenciesInterface &externalDependencies)
@@ -116,14 +118,14 @@ QString DesignDocumentView::toText() const
textEdit.setPlainText(imports + QStringLiteral("Item {\n}\n"));
NotIndentingTextEditModifier modifier(&textEdit);
- QScopedPointer<RewriterView> rewriterView(
- new RewriterView(externalDependencies(), RewriterView::Amend));
+ std::unique_ptr<RewriterView> rewriterView = std::make_unique<RewriterView>(externalDependencies(),
+ RewriterView::Amend);
rewriterView->setCheckSemanticErrors(false);
rewriterView->setPossibleImportsEnabled(false);
rewriterView->setTextModifier(&modifier);
- outputModel->setRewriterView(rewriterView.data());
+ outputModel->setRewriterView(rewriterView.get());
- ModelMerger merger(rewriterView.data());
+ ModelMerger merger(rewriterView.get());
merger.replaceModel(rootModelNode());
diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp
new file mode 100644
index 0000000000..608bf42eb3
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.cpp
@@ -0,0 +1,82 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "import3dcanvas.h"
+
+#include <QImage>
+#include <QLinearGradient>
+#include <QMouseEvent>
+#include <QPainter>
+
+namespace QmlDesigner {
+
+static QImage createGradientImage(int width, int height) {
+ QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
+
+ QLinearGradient gradient(0, 0, 0, height);
+ gradient.setColorAt(0, QColor(0x999999));
+ gradient.setColorAt(1, QColor(0x222222));
+
+ QPainter painter(&image);
+ painter.fillRect(0, 0, width, height, gradient);
+
+ return image;
+}
+
+Import3dCanvas::Import3dCanvas(QWidget *parent)
+ : QWidget(parent)
+{
+}
+
+void Import3dCanvas::updateRenderImage(const QImage &img)
+{
+ m_image = img;
+ update();
+}
+
+void Import3dCanvas::paintEvent([[maybe_unused]] QPaintEvent *e)
+{
+ QWidget::paintEvent(e);
+
+ QPainter painter(this);
+
+ if (m_image.isNull()) {
+ QImage image = createGradientImage(width(), height());
+ painter.drawImage(rect(), image, QRect(0, 0, image.width(), image.height()));
+ } else {
+ painter.drawImage(rect(), m_image, QRect(0, 0, m_image.width(), m_image.height()));
+ }
+}
+
+void Import3dCanvas::resizeEvent(QResizeEvent *)
+{
+ emit requestImageUpdate();
+}
+
+void Import3dCanvas::mousePressEvent(QMouseEvent *e)
+{
+ if (e->buttons() == Qt::LeftButton)
+ m_dragPos = e->position();
+ else
+ m_dragPos = {};
+}
+
+void Import3dCanvas::mouseReleaseEvent(QMouseEvent *)
+{
+ m_dragPos = {};
+}
+
+void Import3dCanvas::mouseMoveEvent(QMouseEvent *e)
+{
+ if (m_dragPos.isNull())
+ return;
+
+ const QPointF curPos = e->position();
+ const QPointF delta = curPos - m_dragPos;
+
+ m_dragPos = curPos;
+
+ emit requestRotation(delta);
+}
+
+}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h
new file mode 100644
index 0000000000..72fb19acff
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/import3dcanvas.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+#pragma once
+
+#include <QEvent>
+#include <QImage>
+#include <QPointF>
+#include <QWidget>
+
+namespace QmlDesigner {
+
+class Import3dCanvas : public QWidget
+{
+ Q_OBJECT
+
+public:
+ Import3dCanvas(QWidget *parent);
+
+ void updateRenderImage(const QImage &img);
+
+signals:
+ void requestImageUpdate();
+ void requestRotation(const QPointF &delta);
+
+protected:
+ void paintEvent(QPaintEvent *e) override;
+ void resizeEvent(QResizeEvent *e) override;
+ void mousePressEvent(QMouseEvent *e) override;
+ void mouseReleaseEvent(QMouseEvent *e) override;
+ void mouseMoveEvent(QMouseEvent *e) override;
+
+private:
+ QImage m_image;
+ QPointF m_dragPos;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp
new file mode 100644
index 0000000000..4c455e3c6d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "import3dconnectionmanager.h"
+
+#include <imagecontainer.h>
+#include <puppettocreatorcommand.h>
+
+#include <QImage>
+
+namespace QmlDesigner {
+
+Import3dConnectionManager::Import3dConnectionManager()
+{
+ connections().clear(); // Remove default interactive puppets
+ connections().emplace_back("Import 3D", "import3dmode");
+}
+
+void Import3dConnectionManager::setPreviewImageCallback(ImageCallback callback)
+{
+ m_previewImageCallback = std::move(callback);
+}
+
+void Import3dConnectionManager::dispatchCommand(const QVariant &command,
+ ConnectionManagerInterface::Connection &connection)
+{
+ static const int commandType = QMetaType::type("PuppetToCreatorCommand");
+
+ if (command.typeId() == commandType) {
+ auto cmd = command.value<PuppetToCreatorCommand>();
+ switch (cmd.type()) {
+ case PuppetToCreatorCommand::Import3DPreviewImage: {
+ ImageContainer container = qvariant_cast<ImageContainer>(cmd.data());
+ QImage image = container.image();
+ if (!image.isNull())
+ m_previewImageCallback(image);
+ break;
+ }
+ default:
+ break;
+ }
+ } else {
+ InteractiveConnectionManager::dispatchCommand(command, connection);
+ }
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h
new file mode 100644
index 0000000000..458d612ad2
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/import3dconnectionmanager.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "interactiveconnectionmanager.h"
+
+QT_FORWARD_DECLARE_CLASS(QImage)
+
+namespace QmlDesigner {
+
+class Import3dConnectionManager : public InteractiveConnectionManager
+{
+public:
+ using ImageCallback = std::function<void(const QImage &)>;
+
+ Import3dConnectionManager();
+
+ void setPreviewImageCallback(ImageCallback callback);
+
+protected:
+ void dispatchCommand(const QVariant &command, Connection &connection) override;
+
+private:
+ ImageCallback m_previewImageCallback;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
index 29fff4b359..271410233b 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.cpp
@@ -4,11 +4,16 @@
#include "itemlibraryassetimportdialog.h"
#include "ui_itemlibraryassetimportdialog.h"
+#include "import3dcanvas.h"
+#include "import3dconnectionmanager.h"
+
#include <model.h>
#include <model/modelutils.h>
+#include <nodeinstanceview.h>
#include <nodemetainfo.h>
#include <qmldesignerconstants.h>
#include <qmldesignerplugin.h>
+#include <rewriterview.h>
#include <variantproperty.h>
#include <theme.h>
@@ -73,9 +78,10 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
const QStringList &importFiles, const QString &defaulTargetDirectory,
const QVariantMap &supportedExts, const QVariantMap &supportedOpts,
const QJsonObject &defaultOpts, const QSet<QString> &preselectedFilesForOverwrite,
- QWidget *parent)
+ AbstractView *view, QWidget *parent)
: QDialog(parent)
, ui(new Ui::ItemLibraryAssetImportDialog)
+ , m_view(view)
, m_importer(this)
, m_preselectedFilesForOverwrite(preselectedFilesForOverwrite)
{
@@ -106,17 +112,15 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
if (m_quick3DFiles.size() != importFiles.size())
addWarning("Cannot import 3D and other assets simultaneously. Skipping non-3D assets.");
- ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Import"));
- connect(ui->buttonBox->button(QDialogButtonBox::Ok), &QPushButton::clicked,
- this, &ItemLibraryAssetImportDialog::onImport);
+ connect(ui->importButton, &QPushButton::clicked, this, &ItemLibraryAssetImportDialog::onImport);
- ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
+ ui->importButton->setDefault(true);
ui->advancedSettingsButton->setStyleSheet(
"QPushButton#advancedSettingsButton {background-color: transparent}");
ui->advancedSettingsButton->setStyleSheet(
QString("QPushButton { border: none; color :%1 }").arg(
- Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_HighlightColor).name()));
+ Utils::creatorColor(Utils::Theme::QmlDesigner_HighlightColor).name()));
QStringList importPaths;
auto doc = QmlDesignerPlugin::instance()->currentDesignDocument();
@@ -130,43 +134,8 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
if (targetDir.isEmpty())
targetDir = defaulTargetDirectory;
- // Import is always done under known folder. The order of preference for folder is:
- // 1) An existing QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path
- // 2) An existing QUICK_3D_ASSETS_FOLDER under any project import path
- // 3) New QUICK_3D_ASSETS_FOLDER under DEFAULT_ASSET_IMPORT_FOLDER project import path
- // 4) New QUICK_3D_ASSETS_FOLDER under any project import path
- // 5) New QUICK_3D_ASSETS_FOLDER under new DEFAULT_ASSET_IMPORT_FOLDER under project
- const QString defaultAssetFolder = QLatin1String(Constants::DEFAULT_ASSET_IMPORT_FOLDER);
- const QString quick3DFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER);
- QString candidatePath = targetDir + defaultAssetFolder + quick3DFolder;
- int candidatePriority = 5;
-
- for (const auto &importPath : std::as_const(importPaths)) {
- if (importPath.startsWith(targetDir)) {
- const bool isDefaultFolder = importPath.endsWith(defaultAssetFolder);
- const QString assetFolder = importPath + quick3DFolder;
- const bool exists = QFileInfo::exists(assetFolder);
- if (exists) {
- if (isDefaultFolder) {
- // Priority one location, stop looking
- candidatePath = assetFolder;
- break;
- } else if (candidatePriority > 2) {
- candidatePriority = 2;
- candidatePath = assetFolder;
- }
- } else {
- if (candidatePriority > 3 && isDefaultFolder) {
- candidatePriority = 3;
- candidatePath = assetFolder;
- } else if (candidatePriority > 4) {
- candidatePriority = 4;
- candidatePath = assetFolder;
- }
- }
- }
- }
- m_quick3DImportPath = candidatePath;
+ m_quick3DImportPath = QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().import3dBasePath().toString();
if (!m_quick3DFiles.isEmpty()) {
QVector<QJsonObject> groups;
@@ -244,10 +213,14 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
ui->tabWidget->setCurrentIndex(0);
}
- connect(ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked,
+ connect(ui->closeButton, &QPushButton::clicked,
this, &ItemLibraryAssetImportDialog::onClose);
connect(ui->tabWidget, &QTabWidget::currentChanged,
this, &ItemLibraryAssetImportDialog::updateUi);
+ connect(canvas(), &Import3dCanvas::requestImageUpdate,
+ this, &ItemLibraryAssetImportDialog::onRequestImageUpdate);
+ connect(canvas(), &Import3dCanvas::requestRotation,
+ this, &ItemLibraryAssetImportDialog::onRequestRotation);
connect(&m_importer, &ItemLibraryAssetImporter::errorReported,
this, &ItemLibraryAssetImportDialog::addError);
@@ -261,23 +234,35 @@ ItemLibraryAssetImportDialog::ItemLibraryAssetImportDialog(
this, &ItemLibraryAssetImportDialog::onImportFinished);
connect(&m_importer, &ItemLibraryAssetImporter::progressChanged,
this, &ItemLibraryAssetImportDialog::setImportProgress);
-
- addInfo(tr("Select import options and press \"Import\" to import the following files:"));
- for (const auto &file : std::as_const(m_quick3DFiles))
- addInfo(file);
+ connect(&m_importer, &ItemLibraryAssetImporter::importReadyForPreview,
+ this, &ItemLibraryAssetImportDialog::onImportReadyForPreview);
connect(ui->advancedSettingsButton, &QPushButton::clicked,
this, &ItemLibraryAssetImportDialog::toggleAdvanced);
QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::updateUi);
+
+ if (m_quick3DFiles.size() != 1) {
+ addInfo(tr("Select import options and press \"Import\" to import the following files:"));
+ } else {
+ addInfo(tr("Importing:"));
+ QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::onImport);
+ }
+
+ for (const auto &file : std::as_const(m_quick3DFiles))
+ addInfo(file);
+
+ updateImportButtonState();
}
ItemLibraryAssetImportDialog::~ItemLibraryAssetImportDialog()
{
+ cleanupPreviewPuppet();
delete ui;
}
-void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode,
+void ItemLibraryAssetImportDialog::updateImport(AbstractView *view,
+ const ModelNode &updateNode,
const QVariantMap &supportedExts,
const QVariantMap &supportedOpts)
{
@@ -294,11 +279,14 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode,
QFileInfo compFileInfo{compFileName};
// Find to top asset folder
- const QString assetFolder = QLatin1String(Constants::QUICK_3D_ASSETS_FOLDER).mid(1);
+ const QString oldAssetFolder = QLatin1String(Constants::OLD_QUICK_3D_ASSETS_FOLDER);
+ QString assetFolder = QLatin1String(Constants::QUICK_3D_COMPONENTS_FOLDER);
const QStringList parts = compFileName.split('/');
int i = parts.size() - 1;
int previousSize = 0;
for (; i >= 0; --i) {
+ if (parts[i] == oldAssetFolder)
+ assetFolder = oldAssetFolder;
if (parts[i] == assetFolder)
break;
previousSize = parts[i].size();
@@ -363,7 +351,8 @@ void ItemLibraryAssetImportDialog::updateImport(const ModelNode &updateNode,
{sourceInfo.absoluteFilePath()},
node.model()->fileUrl().toLocalFile(),
supportedExts, supportedOpts, options,
- preselectedFiles, Core::ICore::dialogParent());
+ preselectedFiles, view,
+ Core::ICore::dialogParent());
importDlg->show();
} else {
@@ -512,6 +501,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
QJsonValue value(optCheck->isChecked());
optObj.insert("value", value);
m_importOptions[optionsIndex].insert(optKey, optObj);
+ updateImportButtonState();
});
} else {
// Simple options also exist in advanced, so don't connect simple controls directly
@@ -519,13 +509,17 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
auto *advCheck = qobject_cast<QCheckBox *>(
m_labelToControlWidgetMaps[optionsIndex].value(optKey));
if (advCheck) {
- QObject::connect(optCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() {
- if (advCheck->isChecked() != optCheck->isChecked())
+ QObject::connect(optCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() {
+ if (advCheck->isChecked() != optCheck->isChecked()) {
advCheck->setChecked(optCheck->isChecked());
+ updateImportButtonState();
+ }
});
- QObject::connect(advCheck, &QCheckBox::toggled, this, [optCheck, advCheck]() {
- if (advCheck->isChecked() != optCheck->isChecked())
+ QObject::connect(advCheck, &QCheckBox::toggled, this, [this, optCheck, advCheck]() {
+ if (advCheck->isChecked() != optCheck->isChecked()) {
optCheck->setChecked(advCheck->isChecked());
+ updateImportButtonState();
+ }
});
}
}
@@ -561,6 +555,7 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
QJsonValue value(optSpin->value());
optObj.insert("value", value);
m_importOptions[optionsIndex].insert(optKey, optObj);
+ updateImportButtonState();
});
} else {
auto *advSpin = qobject_cast<QDoubleSpinBox *>(
@@ -568,14 +563,18 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
if (advSpin) {
// Connect corresponding advanced control
QObject::connect(optSpin, &QDoubleSpinBox::valueChanged,
- this, [optSpin, advSpin] {
- if (advSpin->value() != optSpin->value())
+ this, [this, optSpin, advSpin] {
+ if (advSpin->value() != optSpin->value()) {
advSpin->setValue(optSpin->value());
+ updateImportButtonState();
+ }
});
QObject::connect(advSpin, &QDoubleSpinBox::valueChanged,
- this, [optSpin, advSpin] {
- if (advSpin->value() != optSpin->value())
+ this, [this, optSpin, advSpin] {
+ if (advSpin->value() != optSpin->value()) {
optSpin->setValue(advSpin->value());
+ updateImportButtonState();
+ }
});
}
}
@@ -733,8 +732,10 @@ QGridLayout *ItemLibraryAssetImportDialog::createOptionsGrid(
// and move the remaining member to ungrouped options
// Note: <= 2 instead of < 2 because each group has group label member
if (i != 0 && groupWidgets.size() <= 2) {
- widgets[0].prepend(groupWidgets[1]);
- groupWidgets[0].first->hide(); // hide group label
+ if (groupWidgets.size() == 2)
+ widgets[0].prepend(groupWidgets[1]);
+ if (groupWidgets.size() >= 1)
+ groupWidgets[0].first->hide(); // hide group label
groupWidgets.clear();
}
}
@@ -858,6 +859,145 @@ bool ItemLibraryAssetImportDialog::isHiddenOption(const QString &id)
return hiddenOptions.contains(id);
}
+void ItemLibraryAssetImportDialog::startPreview()
+{
+ cleanupPreviewPuppet();
+
+ // Preview is done via custom QML file added into the temporary folder of the preview
+ QString previewQml =
+R"(
+import QtQuick
+import QtQuick3D
+
+Rectangle {
+ id: root
+ width: %1
+ height: %2
+
+ property alias sceneNode: sceneNode
+ property alias view3d: view3d
+ property string extents
+ property string sceneModelName: "%3"
+
+ gradient: Gradient {
+ GradientStop { position: 1.0; color: "#222222" }
+ GradientStop { position: 0.0; color: "#999999" }
+ }
+
+ View3D {
+ id: view3d
+ anchors.fill: parent
+ camera: viewCamera
+
+ environment: SceneEnvironment {
+ antialiasingMode: SceneEnvironment.MSAA
+ antialiasingQuality: SceneEnvironment.VeryHigh
+ }
+
+ PerspectiveCamera {
+ id: viewCamera
+ x: 600
+ y: 600
+ z: 600
+ eulerRotation.x: -45
+ eulerRotation.y: -45
+ clipFar: 100000
+ clipNear: 10
+ }
+
+ DirectionalLight {
+ rotation: viewCamera.rotation
+ }
+
+ Node {
+ id: sceneNode
+ }
+ }
+
+ Text {
+ anchors.bottom: parent.bottom
+ anchors.left: parent.left
+ color: "white"
+ text: root.extents
+ font.pixelSize: 14
+ }
+}
+)";
+
+ QSize size = canvas()->size();
+ previewQml = previewQml.arg(size.width()).arg(size.height()).arg(m_previewCompName);
+
+ m_previewFile.writeFileContents(previewQml.toUtf8());
+
+ if (!m_previewFile.exists()) {
+ addWarning("Failed to write preview file.");
+ return;
+ }
+
+ m_connectionManager = new Import3dConnectionManager;
+ m_rewriterView = new RewriterView{m_view->externalDependencies(), RewriterView::Amend};
+ m_nodeInstanceView = new NodeInstanceView{*m_connectionManager, m_view->externalDependencies()};
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ m_model = m_view->model()->createModel("Item");
+#else
+ m_model = QmlDesigner::Model::create("QtQuick/Item", 2, 1);
+ m_model->setFileUrl(m_previewFile.toUrl());
+#endif
+
+ auto textDocument = std::make_unique<QTextDocument>(previewQml);
+ auto modifier = std::make_unique<NotIndentingTextEditModifier>(textDocument.get(),
+ QTextCursor{textDocument.get()});
+ m_rewriterView->setTextModifier(modifier.get());
+ m_model->setRewriterView(m_rewriterView);
+
+ if (!m_rewriterView->errors().isEmpty()) {
+ addWarning("Preview scene creation failed.");
+ cleanupPreviewPuppet();
+ return;
+ }
+
+ m_nodeInstanceView->setTarget(m_view->nodeInstanceView()->target());
+
+ auto previewImageCallback = [this](const QImage &image) {
+ canvas()->updateRenderImage(image);
+ };
+
+ auto crashCallback = [&] {
+ addWarning("Preview process crashed.");
+ cleanupPreviewPuppet();
+ };
+
+ m_connectionManager->setPreviewImageCallback(std::move(previewImageCallback));
+ m_nodeInstanceView->setCrashCallback(std::move(crashCallback));
+
+ m_model->setNodeInstanceView(m_nodeInstanceView);
+}
+
+void ItemLibraryAssetImportDialog::cleanupPreviewPuppet()
+{
+ if (m_model) {
+ m_model->setNodeInstanceView({});
+ m_model->setRewriterView({});
+ m_model.reset();
+ }
+
+ if (m_nodeInstanceView)
+ m_nodeInstanceView->setCrashCallback({});
+
+ if (m_connectionManager)
+ m_connectionManager->setPreviewImageCallback({});
+
+ delete m_rewriterView;
+ delete m_nodeInstanceView;
+ delete m_connectionManager;
+}
+
+Import3dCanvas *ItemLibraryAssetImportDialog::canvas()
+{
+ return ui->import3dcanvas;
+}
+
void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event)
{
m_dialogHeight = event->size().height();
@@ -866,8 +1006,13 @@ void ItemLibraryAssetImportDialog::resizeEvent(QResizeEvent *event)
void ItemLibraryAssetImportDialog::setCloseButtonState(bool importing)
{
- ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(true);
- ui->buttonBox->button(QDialogButtonBox::Close)->setText(importing ? tr("Cancel") : tr("Close"));
+ ui->closeButton->setEnabled(true);
+ ui->closeButton->setText(importing ? tr("Cancel") : tr("Close"));
+}
+
+void ItemLibraryAssetImportDialog::updateImportButtonState()
+{
+ ui->importButton->setText(m_previewOptions == m_importOptions ? tr("Accept") : tr("Import"));
}
void ItemLibraryAssetImportDialog::addError(const QString &error, const QString &srcPath)
@@ -889,14 +1034,25 @@ void ItemLibraryAssetImportDialog::addInfo(const QString &info, const QString &s
void ItemLibraryAssetImportDialog::onImport()
{
- ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+ ui->importButton->setEnabled(false);
+
+ if (!m_previewCompName.isEmpty() && m_previewOptions == m_importOptions) {
+ cleanupPreviewPuppet();
+ m_importer.finalizeQuick3DImport();
+ return;
+ }
+
setCloseButtonState(true);
ui->progressBar->setValue(0);
if (!m_quick3DFiles.isEmpty()) {
- m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath,
- m_importOptions, m_extToImportOptionsMap,
- m_preselectedFilesForOverwrite);
+ if (!m_previewCompName.isEmpty()) {
+ m_importer.reImportQuick3D(m_previewCompName, m_importOptions);
+ } else {
+ m_importer.importQuick3D(m_quick3DFiles, m_quick3DImportPath,
+ m_importOptions, m_extToImportOptionsMap,
+ m_preselectedFilesForOverwrite);
+ }
}
}
@@ -910,10 +1066,37 @@ void ItemLibraryAssetImportDialog::setImportProgress(int value, const QString &t
ui->progressBar->setValue(value);
}
+void ItemLibraryAssetImportDialog::onImportReadyForPreview(const QString &path, const QString &compName)
+{
+ addInfo(tr("Import is ready for preview."));
+ if (m_previewCompName.isEmpty())
+ addInfo(tr("Click \"Accept\" to finish the import or adjust options and click \"Import\" to import again."));
+
+ m_previewFile = Utils::FilePath::fromString(path).pathAppended(m_importer.previewFileName());
+ m_previewCompName = compName;
+ m_previewOptions = m_importOptions;
+ QTimer::singleShot(0, this, &ItemLibraryAssetImportDialog::startPreview);
+
+ ui->importButton->setEnabled(true);
+ updateImportButtonState();
+}
+
+void ItemLibraryAssetImportDialog::onRequestImageUpdate()
+{
+ if (m_nodeInstanceView)
+ m_nodeInstanceView->view3DAction(View3DActionType::Import3dUpdatePreviewImage, canvas()->size());
+}
+
+void ItemLibraryAssetImportDialog::onRequestRotation(const QPointF &delta)
+{
+ if (m_nodeInstanceView)
+ m_nodeInstanceView->view3DAction(View3DActionType::Import3dRotatePreviewModel, delta);
+}
+
void ItemLibraryAssetImportDialog::onImportNearlyFinished()
{
// Canceling import is no longer doable
- ui->buttonBox->button(QDialogButtonBox::Close)->setEnabled(false);
+ ui->closeButton->setEnabled(false);
}
void ItemLibraryAssetImportDialog::onImportFinished()
@@ -923,19 +1106,28 @@ void ItemLibraryAssetImportDialog::onImportFinished()
QString interruptStr = tr("Import interrupted.");
addError(interruptStr);
setImportProgress(0, interruptStr);
+ if (m_explicitClose)
+ QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::doClose);
} else {
QString doneStr = tr("Import done.");
addInfo(doneStr);
setImportProgress(100, doneStr);
if (m_closeOnFinish) {
// Add small delay to allow user to visually confirm import finishing
- QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::onClose);
+ QTimer::singleShot(1000, this, &ItemLibraryAssetImportDialog::doClose);
}
}
}
void ItemLibraryAssetImportDialog::onClose()
{
+ ui->importButton->setEnabled(false);
+ m_explicitClose = true;
+ doClose();
+}
+
+void ItemLibraryAssetImportDialog::doClose()
+{
if (m_importer.isImporting()) {
addInfo(tr("Canceling import."));
m_importer.cancelImport();
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h
index c5da478232..e7c4933056 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.h
@@ -3,14 +3,19 @@
#pragma once
#include "itemlibraryassetimporter.h"
-#include "modelnode.h"
+
+#include <modelnode.h>
+
+#include <utils/filepath.h>
#include <QDialog>
#include <QJsonObject>
+#include <QPointer>
#include <QSet>
QT_BEGIN_NAMESPACE
class QGridLayout;
+class QPushButton;
QT_END_NAMESPACE
namespace Utils {
@@ -19,6 +24,10 @@ class OutputFormatter;
namespace QmlDesigner {
class ItemLibraryAssetImporter;
+class Import3dCanvas;
+class Import3dConnectionManager;
+class NodeInstanceView;
+class RewriterView;
namespace Ui {
class ItemLibraryAssetImportDialog;
@@ -35,10 +44,12 @@ public:
const QVariantMap &supportedOpts,
const QJsonObject &defaultOpts,
const QSet<QString> &preselectedFilesForOverwrite,
+ AbstractView *view,
QWidget *parent = nullptr);
~ItemLibraryAssetImportDialog();
- static void updateImport(const ModelNode &updateNode,
+ static void updateImport(AbstractView *view,
+ const ModelNode &updateNode,
const QVariantMap &supportedExts,
const QVariantMap &supportedOpts);
@@ -52,12 +63,17 @@ private slots:
private:
void setCloseButtonState(bool importing);
+ void updateImportButtonState();
void onImport();
void setImportProgress(int value, const QString &text);
+ void onImportReadyForPreview(const QString &path, const QString &compName);
+ void onRequestImageUpdate();
+ void onRequestRotation(const QPointF &delta);
void onImportNearlyFinished();
void onImportFinished();
void onClose();
+ void doClose();
void toggleAdvanced();
void createTab(const QString &tabLabel, int optionsIndex, const QJsonObject &groups);
@@ -69,8 +85,19 @@ private:
bool isSimpleOption(const QString &id);
bool isHiddenOption(const QString &id);
+ void startPreview();
+ void cleanupPreviewPuppet();
+ Import3dCanvas *canvas();
+
Ui::ItemLibraryAssetImportDialog *ui = nullptr;
Utils::OutputFormatter *m_outputFormatter = nullptr;
+ QPointer<Import3dConnectionManager> m_connectionManager;
+ QPointer<NodeInstanceView> m_nodeInstanceView;
+ QPointer<RewriterView> m_rewriterView;
+ QPointer<AbstractView> m_view;
+ ModelPointer m_model;
+ Utils::FilePath m_previewFile;
+ QString m_previewCompName;
struct OptionsData
{
@@ -83,6 +110,7 @@ private:
QString m_quick3DImportPath;
ItemLibraryAssetImporter m_importer;
QVector<QJsonObject> m_importOptions;
+ QVector<QJsonObject> m_previewOptions;
QHash<QString, int> m_extToImportOptionsMap;
QSet<QString> m_preselectedFilesForOverwrite;
bool m_closeOnFinish = true;
@@ -91,5 +119,6 @@ private:
OptionsData m_advancedData;
bool m_advancedMode = false;
int m_dialogHeight = 350;
+ bool m_explicitClose = false;
};
}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui
index 081bc36a3d..e0b9d925fc 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimportdialog.ui
@@ -6,15 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
- <width>630</width>
+ <width>1100</width>
<height>350</height>
</rect>
</property>
<property name="windowTitle">
<string>Asset Import</string>
</property>
- <layout class="QFormLayout" name="formLayout">
- <item row="0" column="0" colspan="2">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
@@ -24,6 +24,12 @@
<verstretch>2</verstretch>
</sizepolicy>
</property>
+ <property name="minimumSize">
+ <size>
+ <width>550</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="currentIndex">
<number>0</number>
</property>
@@ -98,6 +104,12 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="text">
<string/>
</property>
@@ -111,16 +123,64 @@
</widget>
</item>
<item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Close|QDialogButtonBox::Ok</set>
- </property>
- </widget>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeButton">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="importButton">
+ <property name="text">
+ <string>Import</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</item>
+ <item>
+ <widget class="Import3dCanvas" name="import3dcanvas" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>300</width>
+ <height>300</height>
+ </size>
+ </property>
+ </widget>
+ </item>
</layout>
</widget>
+ <customwidgets>
+ <customwidget>
+ <class>Import3dCanvas</class>
+ <extends>QWidget</extends>
+ <header>import3dcanvas.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
index 48958ceec9..010d00a970 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
@@ -1,6 +1,7 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "itemlibraryassetimporter.h"
+
#include "assetimportupdatedialog.h"
#include "qmldesignerplugin.h"
#include "qmldesignerconstants.h"
@@ -20,6 +21,7 @@
#include <utils/algorithm.h>
#include <utils/async.h>
+#include <utils/filepath.h>
#include <utils/qtcassert.h>
#include <QApplication>
@@ -57,8 +59,6 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles,
const QHash<QString, int> &extToImportOptionsMap,
const QSet<QString> &preselectedFilesForOverwrite)
{
- if (m_isImporting)
- cancelImport();
reset();
m_isImporting = true;
@@ -91,6 +91,53 @@ void ItemLibraryAssetImporter::importQuick3D(const QStringList &inputFiles,
}
}
+void ItemLibraryAssetImporter::reImportQuick3D(const QString &assetName,
+ const QVector<QJsonObject> &options)
+{
+ if (!assetName.isEmpty() && !m_parseData.contains(assetName)) {
+ addError(tr("Attempted to reimport non-existing asset: %1").arg(assetName));
+ return;
+ }
+
+ ParseData &pd = m_parseData[assetName];
+ // Change outDir just in case reimport generates different files
+ QDir oldDir = pd.outDir;
+ QString assetFolder = generateAssetFolderName(pd.assetName);
+ pd.outDir.cdUp();
+ pd.outDir.mkpath(assetFolder);
+
+ if (!pd.outDir.cd(assetFolder)) {
+ addError(tr("Could not access temporary asset directory: \"%1\".")
+ .arg(pd.outDir.filePath(assetFolder)));
+ return;
+ }
+
+ if (oldDir.absolutePath().contains(tempDirNameBase()))
+ oldDir.removeRecursively();
+
+ m_isImporting = false;
+ m_cancelled = false;
+
+ m_puppetProcess.reset();
+ m_requiredImports.clear();
+ m_currentImportId = 0;
+ m_puppetQueue.clear();
+
+ for (ParseData &pd : m_parseData)
+ pd.importId = -1;
+
+ pd.options = options[pd.optionsIndex];
+ pd.importId = 1;
+
+ m_importFiles.remove(assetName);
+
+ m_importIdToAssetNameMap.clear();
+ m_importIdToAssetNameMap[pd.importId] = assetName;
+
+ m_puppetQueue.append(pd.importId);
+ startNextImportProcess();
+}
+
bool ItemLibraryAssetImporter::isImporting() const
{
return m_isImporting;
@@ -103,19 +150,19 @@ void ItemLibraryAssetImporter::cancelImport()
notifyFinished();
}
-void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath) const
+void ItemLibraryAssetImporter::addError(const QString &errMsg, const QString &srcPath)
{
qCDebug(importerLog) << "Error: "<< errMsg << srcPath;
emit errorReported(errMsg, srcPath);
}
-void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath) const
+void ItemLibraryAssetImporter::addWarning(const QString &warningMsg, const QString &srcPath)
{
qCDebug(importerLog) << "Warning: " << warningMsg << srcPath;
emit warningReported(warningMsg, srcPath);
}
-void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath) const
+void ItemLibraryAssetImporter::addInfo(const QString &infoMsg, const QString &srcPath)
{
qCDebug(importerLog) << "Info: " << infoMsg << srcPath;
emit infoReported(infoMsg, srcPath);
@@ -126,8 +173,8 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo
{
m_puppetProcess.reset();
- if (m_parseData.contains(m_currentImportId)) {
- const ParseData &pd = m_parseData[m_currentImportId];
+ if (m_importIdToAssetNameMap.contains(m_currentImportId)) {
+ const ParseData &pd = m_parseData[m_importIdToAssetNameMap[m_currentImportId]];
QString errStr;
if (exitStatus == QProcess::ExitStatus::CrashExit) {
errStr = tr("Import process crashed.");
@@ -150,11 +197,12 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo
addError(tr("Asset import process failed: \"%1\".")
.arg(pd.sourceInfo.absoluteFilePath()));
addError(errStr);
- m_parseData.remove(m_currentImportId);
+ m_parseData.remove(m_importIdToAssetNameMap[m_currentImportId]);
+ m_importIdToAssetNameMap.remove(m_currentImportId);
}
}
- int finishedCount = m_parseData.size() - m_puppetQueue.size();
+ int finishedCount = m_importIdToAssetNameMap.size() - m_puppetQueue.size();
if (!m_puppetQueue.isEmpty())
startNextImportProcess();
@@ -162,7 +210,7 @@ void ItemLibraryAssetImporter::importProcessFinished([[maybe_unused]] int exitCo
notifyProgress(100);
QTimer::singleShot(0, this, &ItemLibraryAssetImporter::postImport);
} else {
- notifyProgress(int(100. * (double(finishedCount) / double(m_parseData.size()))));
+ notifyProgress(int(100. * (double(finishedCount) / double(m_importIdToAssetNameMap.size()))));
}
}
@@ -178,7 +226,7 @@ void ItemLibraryAssetImporter::reset()
m_cancelled = false;
delete m_tempDir;
- m_tempDir = new QTemporaryDir;
+ m_tempDir = new QTemporaryDir(QDir::tempPath() + tempDirNameBase());
m_importFiles.clear();
m_overwrittenImports.clear();
m_puppetProcess.reset();
@@ -186,6 +234,7 @@ void ItemLibraryAssetImporter::reset()
m_requiredImports.clear();
m_currentImportId = 0;
m_puppetQueue.clear();
+ m_importIdToAssetNameMap.clear();
}
void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths,
@@ -207,9 +256,11 @@ void ItemLibraryAssetImporter::parseFiles(const QStringList &filePaths,
int index = extToImportOptionsMap.value(QFileInfo(file).suffix());
ParseData pd;
pd.options = options[index];
+ pd.optionsIndex = index;
if (preParseQuick3DAsset(file, pd, preselectedFilesForOverwrite)) {
pd.importId = ++m_importIdCounter;
- m_parseData.insert(pd.importId, pd);
+ m_importIdToAssetNameMap[pd.importId] = pd.assetName;
+ m_parseData.insert(pd.assetName, pd);
}
notifyProgress(qRound(++count * quota), progressTitle);
}
@@ -238,7 +289,7 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa
pd.targetDirPath = pd.targetDir.filePath(pd.assetName);
- if (pd.outDir.exists(pd.assetName)) {
+ if (m_parseData.contains(pd.assetName)) {
addWarning(tr("Skipped import of duplicate asset: \"%1\".").arg(pd.assetName));
return false;
}
@@ -287,11 +338,12 @@ bool ItemLibraryAssetImporter::preParseQuick3DAsset(const QString &file, ParseDa
}
}
- pd.outDir.mkpath(pd.assetName);
+ QString assetFolder = generateAssetFolderName(pd.assetName);
+ pd.outDir.mkpath(assetFolder);
- if (!pd.outDir.cd(pd.assetName)) {
+ if (!pd.outDir.cd(assetFolder)) {
addError(tr("Could not access temporary asset directory: \"%1\".")
- .arg(pd.outDir.filePath(pd.assetName)));
+ .arg(pd.outDir.filePath(assetFolder)));
return false;
}
return true;
@@ -329,12 +381,15 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd)
if (qmldirFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
QString qmlInfo;
qmlInfo.append("module ");
- qmlInfo.append(m_importPath.split('/').last());
+ qmlInfo.append(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().import3dTypePrefix());
qmlInfo.append(".");
qmlInfo.append(pd.assetName);
qmlInfo.append('\n');
m_requiredImports.append(
- QStringLiteral("%1.%2").arg(pd.targetDir.dirName(), pd.assetName));
+ QStringLiteral("%1.%2").arg(QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().import3dTypePrefix(),
+ pd.assetName));
while (qmlIt.hasNext()) {
qmlIt.next();
QFileInfo fi = QFileInfo(qmlIt.filePath());
@@ -421,7 +476,7 @@ void ItemLibraryAssetImporter::postParseQuick3DAsset(ParseData &pd)
// Copy the original asset into a subdirectory
assetFiles.insert(sourcePath, sourceSceneTargetFilePath(pd));
- m_importFiles.insert(assetFiles);
+ m_importFiles.insert(pd.assetName, assetFiles);
}
void ItemLibraryAssetImporter::copyImportedFiles()
@@ -496,6 +551,12 @@ void ItemLibraryAssetImporter::keepUiAlive() const
QApplication::processEvents();
}
+QString ItemLibraryAssetImporter::generateAssetFolderName(const QString &assetName) const
+{
+ static int counter = 0;
+ return assetName + "_QDS_" + QString::number(counter++);
+}
+
ItemLibraryAssetImporter::OverwriteResult ItemLibraryAssetImporter::confirmAssetOverwrite(const QString &assetName)
{
const QString title = tr("Overwrite Existing Asset?");
@@ -530,7 +591,8 @@ void ItemLibraryAssetImporter::startNextImportProcess()
if (model && view) {
bool done = false;
while (!m_puppetQueue.isEmpty() && !done) {
- const ParseData pd = m_parseData.value(m_puppetQueue.takeLast());
+ const ParseData pd = m_parseData.value(
+ m_importIdToAssetNameMap.value(m_puppetQueue.takeLast()));
QStringList puppetArgs;
QJsonDocument optDoc(pd.options);
@@ -553,7 +615,8 @@ void ItemLibraryAssetImporter::startNextImportProcess()
} else {
addError(tr("Failed to start import 3D asset process."),
pd.sourceInfo.absoluteFilePath());
- m_parseData.remove(pd.importId);
+ const QString assetName = m_importIdToAssetNameMap.take(pd.importId);
+ m_parseData.remove(assetName);
m_puppetProcess.reset();
}
}
@@ -569,8 +632,16 @@ void ItemLibraryAssetImporter::postImport()
postParseQuick3DAsset(pd);
}
- if (!isCancelled())
- finalizeQuick3DImport();
+ if (!isCancelled()) {
+ // TODO: Currently we only support import preview for single imports
+ if (m_parseData.size() != 1) {
+ finalizeQuick3DImport();
+ } else {
+ const ParseData &pd = m_parseData[m_parseData.keys().first()];
+ const QString importedComponentName = pd.assetName;
+ emit importReadyForPreview(pd.outDir.absolutePath(), importedComponentName);
+ }
+ }
}
void ItemLibraryAssetImporter::finalizeQuick3DImport()
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h
index 8ad7b5a2de..abe9690951 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.h
@@ -2,8 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
-#include "import.h"
-
#include <qprocessuniqueptr.h>
#include <QSet>
@@ -35,20 +33,28 @@ public:
const QHash<QString, int> &extToImportOptionsMap,
const QSet<QString> &preselectedFilesForOverwrite);
+ void reImportQuick3D(const QString &assetName, const QVector<QJsonObject> &options);
+
bool isImporting() const;
void cancelImport();
bool isCancelled() const;
- void addError(const QString &errMsg, const QString &srcPath = {}) const;
- void addWarning(const QString &warningMsg, const QString &srcPath = {}) const;
- void addInfo(const QString &infoMsg, const QString &srcPath = {}) const;
+ void addError(const QString &errMsg, const QString &srcPath = {});
+ void addWarning(const QString &warningMsg, const QString &srcPath = {});
+ void addInfo(const QString &infoMsg, const QString &srcPath = {});
+
+ QString previewFileName() const { return "QDSImport3dPreviewScene.qml"; }
+ QString tempDirNameBase() const { return "/qds3dimport"; }
+
+ void finalizeQuick3DImport();
signals:
- void errorReported(const QString &, const QString &) const;
- void warningReported(const QString &, const QString &) const;
- void infoReported(const QString &, const QString &) const;
- void progressChanged(int value, const QString &text) const;
- void importNearlyFinished() const;
+ void errorReported(const QString &, const QString &);
+ void warningReported(const QString &, const QString &);
+ void infoReported(const QString &, const QString &);
+ void progressChanged(int value, const QString &text);
+ void importReadyForPreview(const QString &path, const QString &compName);
+ void importNearlyFinished();
void importFinished();
private slots:
@@ -63,7 +69,8 @@ private:
QFileInfo sourceInfo;
QString assetName;
QString originalAssetName;
- int importId;
+ int importId = -1;
+ int optionsIndex = -1;
};
void notifyFinished();
@@ -79,6 +86,7 @@ private:
void notifyProgress(int value, const QString &text);
void notifyProgress(int value);
void keepUiAlive() const;
+ QString generateAssetFolderName(const QString &assetName) const;
enum class OverwriteResult {
Skip,
@@ -89,10 +97,9 @@ private:
OverwriteResult confirmAssetOverwrite(const QString &assetName);
void startNextImportProcess();
void postImport();
- void finalizeQuick3DImport();
QString sourceSceneTargetFilePath(const ParseData &pd);
- QSet<QHash<QString, QString>> m_importFiles;
+ QHash<QString, QHash<QString, QString>> m_importFiles; // Key: asset name
QHash<QString, QStringList> m_overwrittenImports;
bool m_isImporting = false;
bool m_cancelled = false;
@@ -101,7 +108,8 @@ private:
QProcessUniquePointer m_puppetProcess;
int m_importIdCounter = 0;
int m_currentImportId = 0;
- QHash<int, ParseData> m_parseData;
+ QHash<int, QString> m_importIdToAssetNameMap;
+ QHash<QString, ParseData> m_parseData; // Key: asset name
QString m_progressTitle;
QStringList m_requiredImports;
QList<int> m_puppetQueue;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
index dbeacc7595..8ef9512e26 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
@@ -296,15 +296,16 @@ void ItemLibraryModel::setSearchText(const QString &searchText)
Import ItemLibraryModel::entryToImport(const ItemLibraryEntry &entry)
{
+#ifndef QDS_USE_PROJECTSTORAGE
if (entry.majorVersion() == -1 && entry.minorVersion() == -1)
return Import::createFileImport(entry.requiredImport());
-
+#endif
return Import::createLibraryImport(entry.requiredImport(), QString::number(entry.majorVersion()) + QLatin1Char('.') +
QString::number(entry.minorVersion()));
}
-void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo, Model *model)
+void ItemLibraryModel::update(Model *model)
{
if (!model)
return;
@@ -312,46 +313,54 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo,
beginResetModel();
clearSections();
+ const QString projectName = DocumentManager::currentProjectName();
+
+ auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
+
QStringList excludedImports {
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1) + ".MaterialBundle",
- QLatin1String(Constants::COMPONENT_BUNDLES_FOLDER).mid(1) + ".EffectBundle"
+ projectName,
+ compUtils.materialsBundleType(),
+ compUtils.effectsBundleType(),
+ compUtils.userMaterialsBundleType(),
+ compUtils.user3DBundleType(),
+ compUtils.userEffectsBundleType()
};
// create import sections
- const QString projectName = DocumentManager::currentProjectName();
const Imports usedImports = model->usedImports();
QHash<QString, ItemLibraryImport *> importHash;
for (const Import &import : model->imports()) {
- if (import.url() != projectName) {
- if (excludedImports.contains(import.url()) || import.url().startsWith("Effects."))
- continue;
- bool addNew = true;
- bool isQuick3DAsset = import.url().startsWith("Quick3DAssets.");
- QString importUrl = import.url();
- if (isQuick3DAsset)
- importUrl = ItemLibraryImport::quick3DAssetsTitle();
- else if (import.isFileImport())
- importUrl = import.toString(true, true).remove("\"");
-
- ItemLibraryImport *oldImport = importHash.value(importUrl);
- if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets
- && isQuick3DAsset) {
- addNew = false; // add only 1 Quick3DAssets import section
- } else if (oldImport && oldImport->importEntry().url() == import.url()) {
- // Retain the higher version if multiples exist
- if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion())
- addNew = false;
- else
- delete oldImport;
- }
+ if (excludedImports.contains(import.url())
+ || import.url().startsWith(compUtils.composedEffectsTypePrefix())) {
+ continue;
+ }
- if (addNew) {
- auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets
- : ItemLibraryImport::SectionType::Default;
- ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType);
- itemLibImport->setImportUsed(usedImports.contains(import));
- importHash.insert(importUrl, itemLibImport);
- }
+ bool addNew = true;
+ bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix());
+ QString importUrl = import.url();
+ if (isQuick3DAsset)
+ importUrl = ItemLibraryImport::quick3DAssetsTitle();
+ else if (import.isFileImport())
+ importUrl = import.toString(true, true).remove("\"");
+
+ ItemLibraryImport *oldImport = importHash.value(importUrl);
+ if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets
+ && isQuick3DAsset) {
+ addNew = false; // add only 1 Quick3DAssets import section
+ } else if (oldImport && oldImport->importEntry().url() == import.url()) {
+ // Retain the higher version if multiples exist
+ if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion())
+ addNew = false;
+ else
+ delete oldImport;
+ }
+
+ if (addNew) {
+ auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets
+ : ItemLibraryImport::SectionType::Default;
+ ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType);
+ itemLibImport->setImportUsed(usedImports.contains(import));
+ importHash.insert(importUrl, itemLibImport);
}
}
@@ -367,7 +376,7 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo,
NodeMetaInfo metaInfo;
if constexpr (useProjectStorage())
- metaInfo = entry.metaInfo();
+ metaInfo = NodeMetaInfo{entry.typeId(), model->projectStorage()};
else
metaInfo = model->metaInfo(entry.typeName());
@@ -379,7 +388,8 @@ void ItemLibraryModel::update([[maybe_unused]] ItemLibraryInfo *itemLibraryInfo,
|| metaInfo.majorVersion() < 0);
#endif
bool isItem = valid && metaInfo.isQtQuickItem();
- bool forceVisibility = valid && NodeHints::fromItemLibraryEntry(entry).visibleInLibrary();
+ bool forceVisibility = valid
+ && NodeHints::fromItemLibraryEntry(entry, model).visibleInLibrary();
if (m_flowMode) {
isItem = metaInfo.isFlowViewItem();
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
index 212ddf8e04..b04ea492d2 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.h
@@ -37,7 +37,7 @@ public:
QString searchText() const;
ItemLibraryImport *importByUrl(const QString &importName) const;
- void update(ItemLibraryInfo *itemLibraryInfo, Model *model);
+ void update(Model *model);
void updateUsedImports(const Imports &usedImports);
QMimeData *getMimeData(const ItemLibraryEntry &itemLibraryEntry);
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
index feff523b9c..e4f4ffcd9c 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
@@ -164,7 +164,7 @@ void ItemLibraryView::updateImport3DSupport(const QVariantMap &supportMap)
auto importDlg = new ItemLibraryAssetImportDialog(fileNames, defaultDir,
m_importableExtensions3DMap,
m_importOptions3DMap, {}, {},
- Core::ICore::dialogParent());
+ this, Core::ICore::dialogParent());
int result = importDlg->exec();
return result == QDialog::Accepted ? AddFilesResult::succeeded() : AddFilesResult::cancelled();
@@ -198,7 +198,7 @@ void ItemLibraryView::customNotification(const AbstractView *view, const QString
const QList<ModelNode> &nodeList, const QList<QVariant> &data)
{
if (identifier == "UpdateImported3DAsset" && nodeList.size() > 0) {
- ItemLibraryAssetImportDialog::updateImport(nodeList[0],
+ ItemLibraryAssetImportDialog::updateImport(this, nodeList[0],
m_importableExtensions3DMap,
m_importOptions3DMap);
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
index b710a8226f..ffefc43178 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
@@ -118,9 +118,9 @@ void ItemLibraryWidget::resizeEvent(QResizeEvent *event)
ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache)
: m_itemIconSize(24, 24)
- , m_itemLibraryModel(new ItemLibraryModel(this))
- , m_addModuleModel(new ItemLibraryAddImportModel(this))
- , m_itemsWidget(new StudioQuickWidget(this))
+ , m_itemLibraryModel(std::make_unique<ItemLibraryModel>())
+ , m_addModuleModel(std::make_unique<ItemLibraryAddImportModel>())
+ , m_itemsWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>())
, m_imageCache{imageCache}
{
m_compressionTimer.setInterval(1000);
@@ -146,7 +146,7 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache)
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
layout->setSpacing(0);
- layout->addWidget(m_itemsWidget.data());
+ layout->addWidget(m_itemsWidget.get());
updateSearch();
@@ -167,8 +167,8 @@ ItemLibraryWidget::ItemLibraryWidget(AsynchronousImageCache &imageCache)
auto map = m_itemsWidget->registerPropertyMap("ItemLibraryBackend");
- map->setProperties({{"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.data())},
- {"addModuleModel", QVariant::fromValue(m_addModuleModel.data())},
+ map->setProperties({{"itemLibraryModel", QVariant::fromValue(m_itemLibraryModel.get())},
+ {"addModuleModel", QVariant::fromValue(m_addModuleModel.get())},
{"itemLibraryIconWidth", m_itemIconSize.width()},
{"itemLibraryIconHeight", m_itemIconSize.height()},
{"rootView", QVariant::fromValue(this)},
@@ -333,9 +333,7 @@ void ItemLibraryWidget::updateModel()
m_compressionTimer.stop();
}
-#ifndef QDS_USE_PROJECTSTORAGE
- m_itemLibraryModel->update(m_itemLibraryInfo.data(), m_model.data());
-#endif
+ m_itemLibraryModel->update(m_model.data());
if (m_itemLibraryModel->rowCount() == 0 && !m_updateRetry) {
m_updateRetry = true; // Only retry once to avoid endless loops
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
index b56532b218..2940db7a73 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
@@ -10,9 +10,10 @@
#include <studioquickwidget.h>
-#include <utils/fancylineedit.h>
-#include <utils/dropsupport.h>
#include <previewtooltip/previewtooltipbackend.h>
+#include <utils/dropsupport.h>
+#include <utils/fancylineedit.h>
+#include <utils/uniqueobjectptr.h>
#include <QFileIconProvider>
#include <QFrame>
@@ -104,10 +105,10 @@ private:
#ifndef QDS_USE_PROJECTSTORAGE
QPointer<ItemLibraryInfo> m_itemLibraryInfo;
#endif
- QPointer<ItemLibraryModel> m_itemLibraryModel;
- QPointer<ItemLibraryAddImportModel> m_addModuleModel;
+ std::unique_ptr<ItemLibraryModel> m_itemLibraryModel;
+ std::unique_ptr<ItemLibraryAddImportModel> m_addModuleModel;
- QScopedPointer<StudioQuickWidget> m_itemsWidget;
+ Utils::UniqueObjectPtr<StudioQuickWidget> m_itemsWidget;
std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut;
diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp
index b6009edc77..f97b6f74ac 100644
--- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp
+++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp
@@ -29,10 +29,10 @@ public:
QVariant maybeConvertToNumber(const QVariant &value)
{
- if (value.typeId() == QVariant::Bool)
+ if (value.typeId() == QMetaType::Bool)
return value;
- if (value.typeId() == QVariant::String) {
+ if (value.typeId() == QMetaType::QString) {
const QString text = value.toString();
if (text == "true")
return QVariant(true);
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp
index d36e78512b..b74f310741 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.cpp
@@ -366,6 +366,9 @@ void MaterialBrowserModel::selectMaterial(int idx, bool force)
if (idx != m_selectedIndex || force) {
m_selectedIndex = idx;
emit selectedIndexChanged(idx);
+
+ m_selectedMaterialIsComponent = selectedMaterial().isComponent();
+ emit selectedMaterialIsComponentChanged();
}
}
@@ -434,22 +437,20 @@ void MaterialBrowserModel::copyMaterialProperties(int idx, const QString &sectio
QJsonObject propsSpecObj = m_propertyGroupsObj.value(m_copiedMaterialType).toObject();
if (propsSpecObj.contains(section)) { // should always be true
const QJsonArray propNames = propsSpecObj.value(section).toArray();
- // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before
- for (const auto &propName : propNames)
+ for (const QJsonValueConstRef &propName : propNames)
copiedProps.append(propName.toString().toLatin1());
if (section == "Base") { // add QtQuick3D.Material base props as well
QJsonObject propsMatObj = m_propertyGroupsObj.value("Material").toObject();
const QJsonArray propNames = propsMatObj.value("Base").toArray();
- // auto == QJsonValueConstRef after 04dc959d49e5e3 / Qt 6.4, QJsonValueRef before
- for (const auto &propName : propNames)
+ for (const QJsonValueConstRef &propName : propNames)
copiedProps.append(propName.toString().toLatin1());
}
}
}
m_copiedMaterialProps.clear();
- for (const auto &propName : copiedProps) {
+ for (const PropertyName &propName : copiedProps) {
PropertyCopyData data;
data.name = propName;
data.isValid = m_allPropsCopied || validProps.contains(propName);
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h
index 337dce0550..3b6b64ec86 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsermodel.h
@@ -3,7 +3,7 @@
#pragma once
-#include "modelnode.h"
+#include <modelnode.h>
#include <QAbstractListModel>
#include <QJsonObject>
@@ -20,6 +20,7 @@ class MaterialBrowserModel : public QAbstractListModel
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
+ Q_PROPERTY(bool selectedMaterialIsComponent MEMBER m_selectedMaterialIsComponent NOTIFY selectedMaterialIsComponentChanged)
Q_PROPERTY(bool hasQuick3DImport READ hasQuick3DImport WRITE setHasQuick3DImport NOTIFY hasQuick3DImportChanged)
Q_PROPERTY(bool hasModelSelection READ hasModelSelection WRITE setHasModelSelection NOTIFY hasModelSelectionChanged)
Q_PROPERTY(bool hasMaterialLibrary READ hasMaterialLibrary WRITE setHasMaterialLibrary NOTIFY hasMaterialLibraryChanged)
@@ -110,6 +111,7 @@ signals:
const QList<QmlDesigner::MaterialBrowserModel::PropertyCopyData> &props,
bool all);
void isQt6ProjectChanged();
+ void selectedMaterialIsComponentChanged();
private:
bool isValidIndex(int idx) const;
@@ -132,6 +134,7 @@ private:
bool m_hasMaterialLibrary = false;
bool m_allPropsCopied = true;
bool m_isQt6Project = false;
+ bool m_selectedMaterialIsComponent = false;
QString m_copiedMaterialType;
QPointer<MaterialBrowserView> m_view;
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp
index ec95f3e5d3..918956a04c 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowsertexturesmodel.cpp
@@ -63,7 +63,7 @@ QVariant MaterialBrowserTexturesModel::data(const QModelIndex &index, int role)
return tr("Texture has no source image.");
ModelNode texNode = m_textureList.at(index.row());
- QString info = ImageUtils::imageInfo(source);
+ QString info = ImageUtils::imageInfoString(source);
if (info.isEmpty())
return tr("Texture has no data.");
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
index 8bd5761728..7d90dffffc 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserview.cpp
@@ -446,8 +446,13 @@ void QmlDesigner::MaterialBrowserView::loadPropertyGroups()
if (!m_hasQuick3DImport || m_propertyGroupsLoaded || !model())
return;
+#ifdef QDS_USE_PROJECTSTORAGE
+ // TODO
+ QString matPropsPath;
+#else
QString matPropsPath = model()->metaInfo("QtQuick3D.Material").importDirectoryPath()
+ "/designer/propertyGroups.json";
+#endif
m_propertyGroupsLoaded = m_widget->materialBrowserModel()->loadPropertyGroups(matPropsPath);
}
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
index 47a1e8d293..8723611be0 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.cpp
@@ -56,6 +56,13 @@ public:
m_pixmaps.insert(node.internalId(), pixmap);
}
+ QPixmap getPixmap(const ModelNode &node)
+ {
+ QTC_ASSERT(node, return {});
+
+ return m_pixmaps.value(node.internalId());
+ }
+
void clearPixmapCache()
{
m_pixmaps.clear();
@@ -143,7 +150,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache,
: m_materialBrowserView(view)
, m_materialBrowserModel(new MaterialBrowserModel(view, this))
, m_materialBrowserTexturesModel(new MaterialBrowserTexturesModel(view, this))
- , m_quickWidget(new StudioQuickWidget(this))
+ , m_quickWidget(Utils::makeUniqueObjectPtr<StudioQuickWidget>(this))
, m_previewImageProvider(new PreviewImageProvider())
{
QImage defaultImage;
@@ -172,7 +179,7 @@ MaterialBrowserWidget::MaterialBrowserWidget(AsynchronousImageCache &imageCache,
auto layout = new QVBoxLayout(this);
layout->setContentsMargins({});
layout->setSpacing(0);
- layout->addWidget(m_quickWidget.data());
+ layout->addWidget(m_quickWidget.get());
updateSearch();
@@ -357,6 +364,13 @@ void MaterialBrowserWidget::focusMaterialSection(bool focusMatSec)
}
}
+void MaterialBrowserWidget::addMaterialToContentLibrary()
+{
+ ModelNode mat = m_materialBrowserModel->selectedMaterial();
+ m_materialBrowserView->emitCustomNotification("add_material_to_content_lib", {mat},
+ {m_previewImageProvider->getPixmap(mat)});
+}
+
QString MaterialBrowserWidget::qmlSourcesPath()
{
#ifdef SHARE_QML_PATH
@@ -397,7 +411,7 @@ void MaterialBrowserWidget::setIsDragging(bool val)
StudioQuickWidget *MaterialBrowserWidget::quickWidget() const
{
- return m_quickWidget.data();
+ return m_quickWidget.get();
}
void MaterialBrowserWidget::clearPreviewCache()
diff --git a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
index bfe7ace34d..6506283f85 100644
--- a/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
+++ b/src/plugins/qmldesigner/components/materialbrowser/materialbrowserwidget.h
@@ -7,6 +7,8 @@
#include <coreplugin/icontext.h>
+#include <utils/uniqueobjectptr.h>
+
#include <QFrame>
QT_BEGIN_NAMESPACE
@@ -60,6 +62,7 @@ public:
Q_INVOKABLE void acceptAssetsDropOnMaterial(int matIndex, const QList<QUrl> &urls);
Q_INVOKABLE void acceptTextureDropOnMaterial(int matIndex, const QString &texId);
Q_INVOKABLE void focusMaterialSection(bool focusMatSec);
+ Q_INVOKABLE void addMaterialToContentLibrary();
StudioQuickWidget *quickWidget() const;
@@ -83,7 +86,7 @@ private:
QPointer<MaterialBrowserView> m_materialBrowserView;
QPointer<MaterialBrowserModel> m_materialBrowserModel;
QPointer<MaterialBrowserTexturesModel> m_materialBrowserTexturesModel;
- QScopedPointer<StudioQuickWidget> m_quickWidget;
+ Utils::UniqueObjectPtr<StudioQuickWidget> m_quickWidget;
QShortcut *m_qmlSourceUpdateShortcut = nullptr;
PreviewImageProvider *m_previewImageProvider = nullptr;
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp
index 664006fb7a..f583498db7 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorcontextobject.cpp
@@ -46,9 +46,9 @@ QString MaterialEditorContextObject::convertColorToString(const QVariant &color)
{
QString colorString;
QColor theColor;
- if (color.canConvert(QVariant::Color)) {
+ if (color.canConvert(QMetaType(QMetaType::QColor))) {
theColor = color.value<QColor>();
- } else if (color.canConvert(QVariant::Vector3D)) {
+ } else if (color.canConvert(QMetaType(QMetaType::QVector3D))) {
auto vec = color.value<QVector3D>();
theColor = QColor::fromRgbF(vec.x(), vec.y(), vec.z());
}
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
index ecc460ae51..0e508f8f36 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.cpp
@@ -80,8 +80,8 @@ public:
MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialEditor)
: m_quickWidget(Utils::makeUniqueObjectPtr<QQuickWidget>())
- , m_materialEditorTransaction(new MaterialEditorTransaction(materialEditor))
- , m_contextObject(new MaterialEditorContextObject(m_quickWidget.get()))
+ , m_materialEditorTransaction(std::make_unique<MaterialEditorTransaction>(materialEditor))
+ , m_contextObject(std::make_unique<MaterialEditorContextObject>(m_quickWidget.get()))
, m_materialEditorImageProvider(new MaterialEditorImageProvider())
{
m_quickWidget->setObjectName(Constants::OBJECT_NAME_MATERIAL_EDITOR);
@@ -90,7 +90,7 @@ MaterialEditorQmlBackend::MaterialEditorQmlBackend(MaterialEditorView *materialE
m_quickWidget->engine()->addImageProvider("materialEditor", m_materialEditorImageProvider);
m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
m_contextObject->setModel(materialEditor->model());
- context()->setContextObject(m_contextObject.data());
+ context()->setContextObject(m_contextObject.get());
QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged,
materialEditor, &MaterialEditorView::changeValue);
@@ -193,7 +193,7 @@ QQmlContext *MaterialEditorQmlBackend::context() const
MaterialEditorContextObject *MaterialEditorQmlBackend::contextObject() const
{
- return m_contextObject.data();
+ return m_contextObject.get();
}
QQuickWidget *MaterialEditorQmlBackend::widget() const
@@ -224,7 +224,7 @@ DesignerPropertyMap &MaterialEditorQmlBackend::backendValuesPropertyMap()
MaterialEditorTransaction *MaterialEditorQmlBackend::materialEditorTransaction() const
{
- return m_materialEditorTransaction.data();
+ return m_materialEditorTransaction.get();
}
PropertyEditorValue *MaterialEditorQmlBackend::propertyValueForName(const QString &propertyName)
@@ -267,12 +267,9 @@ void MaterialEditorQmlBackend::setup(const QmlObjectNode &selectedMaterialNode,
// anchors
m_backendAnchorBinding.setup(selectedMaterialNode.modelNode());
- context()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
- {{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.data())}
- }
- );
+ context()->setContextProperties(QVector<QQmlContext::PropertyPair>{
+ {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
+ {{"transaction"}, QVariant::fromValue(m_materialEditorTransaction.get())}});
contextObject()->setSpecificsUrl(qmlSpecificsFile);
contextObject()->setStateName(stateName);
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h
index 6f9d6014e9..9fd5fc2399 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorqmlbackend.h
@@ -11,6 +11,8 @@
#include <nodemetainfo.h>
+#include <memory>
+
class PropertyEditorValue;
QT_BEGIN_NAMESPACE
@@ -60,12 +62,15 @@ private:
MaterialEditorView *materialEditor);
PropertyName auxNamePostFix(const PropertyName &propertyName);
+ // to avoid a crash while destructing DesignerPropertyMap in the QQmlData
+ // this needs be destructed after m_quickWidget->engine() is destructed
+ DesignerPropertyMap m_backendValuesPropertyMap;
+
Utils::UniqueObjectPtr<QQuickWidget> m_quickWidget = nullptr;
QmlAnchorBindingProxy m_backendAnchorBinding;
QmlModelNodeProxy m_backendModelNode;
- DesignerPropertyMap m_backendValuesPropertyMap;
- QScopedPointer<MaterialEditorTransaction> m_materialEditorTransaction;
- QScopedPointer<MaterialEditorContextObject> m_contextObject;
+ std::unique_ptr<MaterialEditorTransaction> m_materialEditorTransaction;
+ std::unique_ptr<MaterialEditorContextObject> m_contextObject;
QPointer<MaterialEditorImageProvider> m_materialEditorImageProvider;
};
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
index e083310cdb..21114267cb 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.cpp
@@ -24,7 +24,6 @@
#include "qmldesignerplugin.h"
#include "qmltimeline.h"
#include "variantproperty.h"
-#include <itemlibraryentry.h>
#include <utils3d.h>
#include <coreplugin/icore.h>
@@ -63,10 +62,6 @@ MaterialEditorView::MaterialEditorView(ExternalDependenciesInterface &externalDe
}
});
- m_typeUpdateTimer.setSingleShot(true);
- m_typeUpdateTimer.setInterval(500);
- connect(&m_typeUpdateTimer, &QTimer::timeout, this, &MaterialEditorView::updatePossibleTypes);
-
QmlDesignerPlugin::trackWidgetFocusTime(m_stackedWidget, Constants::EVENT_MATERIALEDITOR_TIME);
MaterialEditorDynamicPropertiesProxyModel::registerDeclarativeType();
@@ -333,8 +328,10 @@ void MaterialEditorView::resetView()
setupQmlBackend();
- if (m_qmlBackEnd)
+ if (m_qmlBackEnd) {
m_qmlBackEnd->emitSelectionChanged();
+ updatePossibleTypes();
+ }
QTimer::singleShot(0, this, &MaterialEditorView::requestPreviewRender);
@@ -605,7 +602,6 @@ void MaterialEditorView::setupQmlBackend()
else
m_dynamicPropertiesModel->reset();
- delayedTypeUpdate();
initPreviewData();
m_stackedWidget->setCurrentWidget(m_qmlBackEnd->widget());
@@ -690,21 +686,6 @@ void MaterialEditorView::initPreviewData()
}
}
-void MaterialEditorView::delayedTypeUpdate()
-{
- m_typeUpdateTimer.start();
-}
-
-[[maybe_unused]] static Import entryToImport(const ItemLibraryEntry &entry)
-{
- if (entry.majorVersion() == -1 && entry.minorVersion() == -1)
- return Import::createFileImport(entry.requiredImport());
-
- return Import::createLibraryImport(entry.requiredImport(),
- QString::number(entry.majorVersion()) + QLatin1Char('.') +
- QString::number(entry.minorVersion()));
-}
-
void MaterialEditorView::updatePossibleTypes()
{
QTC_ASSERT(model(), return);
@@ -712,49 +693,21 @@ void MaterialEditorView::updatePossibleTypes()
if (!m_qmlBackEnd)
return;
-#ifdef QDS_USE_PROJECTSTORAGE
- auto heirs = model()->qtQuick3DMaterialMetaInfo().heirs();
- heirs.push_back(model()->qtQuick3DMaterialMetaInfo());
- auto entries = Utils::transform<ItemLibraryEntries>(heirs, [&](const auto &heir) {
- return toItemLibraryEntries(heir.itemLibrariesEntries(), *model()->projectStorage());
- });
+ static const QStringList basicTypes {
+ "CustomMaterial",
+ "DefaultMaterial",
+ "PrincipledMaterial",
+ "SpecularGlossyMaterial"
+ };
- // I am unsure about the code intention here
-#else // Ensure basic types are always first
- QStringList nonQuick3dTypes;
- QStringList allTypes;
-
- const QList<ItemLibraryEntry> itemLibEntries = m_itemLibraryInfo->entries();
- for (const ItemLibraryEntry &entry : itemLibEntries) {
- NodeMetaInfo metaInfo = model()->metaInfo(entry.typeName());
- bool valid = metaInfo.isValid()
- && (metaInfo.majorVersion() >= entry.majorVersion()
- || metaInfo.majorVersion() < 0);
- if (valid && metaInfo.isQtQuick3DMaterial()) {
- bool addImport = entry.requiredImport().isEmpty();
- if (!addImport) {
- Import import = entryToImport(entry);
- addImport = model()->hasImport(import, true, true);
- }
- if (addImport) {
- const QList<QByteArray> typeSplit = entry.typeName().split('.');
- const QString typeName = QString::fromLatin1(typeSplit.last());
- if (typeSplit.size() == 2 && typeSplit.first() == "QtQuick3D") {
- if (!allTypes.contains(typeName))
- allTypes.append(typeName);
- } else if (!nonQuick3dTypes.contains(typeName)) {
- nonQuick3dTypes.append(typeName);
- }
- }
- }
- }
+ const QString matType = m_selectedMaterial.simplifiedTypeName();
- allTypes.sort();
- nonQuick3dTypes.sort();
- allTypes.append(nonQuick3dTypes);
+ if (basicTypes.contains(matType)) {
+ m_qmlBackEnd->contextObject()->setPossibleTypes(basicTypes);
+ return;
+ }
- m_qmlBackEnd->contextObject()->setPossibleTypes(allTypes);
-#endif
+ m_qmlBackEnd->contextObject()->setPossibleTypes({matType});
}
void MaterialEditorView::modelAttached(Model *model)
@@ -774,20 +727,6 @@ void MaterialEditorView::modelAttached(Model *model)
m_ensureMatLibTimer.start(500);
}
-#ifndef QDS_USE_PROJECTSTORAGE
- if (m_itemLibraryInfo.data() != model->metaInfo().itemLibraryInfo()) {
- if (m_itemLibraryInfo) {
- disconnect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
- this, &MaterialEditorView::delayedTypeUpdate);
- }
- m_itemLibraryInfo = model->metaInfo().itemLibraryInfo();
- if (m_itemLibraryInfo) {
- connect(m_itemLibraryInfo.data(), &ItemLibraryInfo::entriesChanged,
- this, &MaterialEditorView::delayedTypeUpdate);
- }
- }
-#endif
-
if (!m_setupCompleted) {
reloadQml();
m_setupCompleted = true;
@@ -801,7 +740,8 @@ void MaterialEditorView::modelAboutToBeDetached(Model *model)
{
AbstractView::modelAboutToBeDetached(model);
m_dynamicPropertiesModel->reset();
- m_qmlBackEnd->materialEditorTransaction()->end();
+ if (auto transaction = m_qmlBackEnd->materialEditorTransaction())
+ transaction->end();
m_qmlBackEnd->contextObject()->setHasMaterialLibrary(false);
m_selectedMaterial = {};
}
@@ -938,7 +878,8 @@ void MaterialEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNo
m_selectedModels.append(node);
}
- m_qmlBackEnd->contextObject()->setHasModelSelection(!m_selectedModels.isEmpty());
+ if (m_qmlBackEnd)
+ m_qmlBackEnd->contextObject()->setHasModelSelection(!m_selectedModels.isEmpty());
}
void MaterialEditorView::currentStateChanged(const ModelNode &node)
@@ -1020,7 +961,7 @@ void MaterialEditorView::renameMaterial(ModelNode &material, const QString &newN
return;
executeInTransaction(__FUNCTION__, [&] {
- material.setIdWithRefactoring(model()->generateIdFromName(newName, "material"));
+ material.setIdWithRefactoring(model()->generateNewId(newName, "material"));
VariantProperty objNameProp = material.variantProperty("objectName");
objNameProp.setValue(newName);
@@ -1057,7 +998,7 @@ void MaterialEditorView::duplicateMaterial(const ModelNode &material)
QString newName = sourceMat.modelNode().variantProperty("objectName").value().toString() + " copy";
VariantProperty objNameProp = duplicateMatNode.variantProperty("objectName");
objNameProp.setValue(newName);
- duplicateMatNode.setIdWithoutRefactoring(model()->generateIdFromName(newName, "material"));
+ duplicateMatNode.setIdWithoutRefactoring(model()->generateNewId(newName, "material"));
// sync properties. Only the base state is duplicated.
const QList<AbstractProperty> props = material.properties();
diff --git a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h
index c201742bd5..11bea46063 100644
--- a/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h
+++ b/src/plugins/qmldesigner/components/materialeditor/materialeditorview.h
@@ -109,12 +109,10 @@ private:
bool noValidSelection() const;
void initPreviewData();
- void delayedTypeUpdate();
void updatePossibleTypes();
ModelNode m_selectedMaterial;
QTimer m_ensureMatLibTimer;
- QTimer m_typeUpdateTimer;
QShortcut *m_updateShortcut = nullptr;
int m_timerId = 0;
QStackedWidget *m_stackedWidget = nullptr;
diff --git a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
index 58b4c42749..fee3218af0 100644
--- a/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
+++ b/src/plugins/qmldesigner/components/navigator/choosefrompropertylistdialog.cpp
@@ -5,6 +5,8 @@
#include "nodemetainfo.h"
#include "ui_choosefrompropertylistdialog.h"
+#include <qmldesignerplugin.h>
+
namespace QmlDesigner {
// This will filter and return possible properties that the given type can be bound to
@@ -100,7 +102,9 @@ ChooseFromPropertyListFilter::ChooseFromPropertyListFilter(const NodeMetaInfo &i
#ifdef QDS_USE_PROJECTSTORAGE
// TODO add the types here or use the module
#else
- } else if (insertInfo.typeName().startsWith("ComponentBundles.MaterialBundle")) {
+ } else if (insertInfo.typeName().startsWith(
+ QmlDesignerPlugin::instance()->documentManager()
+ .generatedComponentUtils().materialsBundleType().toUtf8())) {
if (parentInfo.isQtQuick3DModel())
propertyList.append("materials");
#endif
diff --git a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp
index 5b36bee7f9..09cf5945e8 100644
--- a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp
+++ b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp
@@ -79,15 +79,12 @@ void IconCheckboxItemDelegate::paint(QPainter *painter,
if (rowIsPropertyRole(modelIndex.model(), modelIndex) || getModelNode(modelIndex).isRootNode())
return; // Do not paint icons for property rows or root node
- QWindow *window = dynamic_cast<QWidget*>(painter->device())->window()->windowHandle();
- QTC_ASSERT(window, return);
-
const QSize iconSize(16, 16);
QPoint iconPosition(styleOption.rect.left() + (styleOption.rect.width() - iconSize.width()) / 2,
styleOption.rect.top() + 2 + delegateMargin);
const QIcon::State state = isChecked(modelIndex) ? QIcon::State::On : QIcon::State::Off;
- const QPixmap iconPixmap = m_icon.pixmap(window, iconSize, mode, state);
+ const QPixmap iconPixmap = m_icon.pixmap(iconSize, painter->device()->devicePixelRatio(), mode, state);
// Shift the lock icon (last column) slightly to the left due to vertical scrollbar width
if (modelIndex.column() == NavigatorTreeModel::ColumnType::Lock)
diff --git a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
index aacaf6dc0d..223e04fd96 100644
--- a/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
+++ b/src/plugins/qmldesigner/components/navigator/nameitemdelegate.cpp
@@ -9,9 +9,8 @@
#include "navigatortreeview.h"
#include "navigatorwidget.h"
#include "choosefrompropertylistdialog.h"
-#include "qproxystyle.h"
-
#include <model/modelutils.h>
+#include <dialogutils.h>
#include <modelnodecontextmenu.h>
#include <theme.h>
#include <qmldesignerconstants.h>
@@ -169,8 +168,7 @@ static void setId(const QModelIndex &index, const QString &newId)
return;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(NavigatorTreeView::tr("Invalid Id"),
- NavigatorTreeView::tr("%1 is an invalid id.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
} else if (modelNode.view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(NavigatorTreeView::tr("Invalid Id"),
NavigatorTreeView::tr("%1 already exists.").arg(newId));
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp
index 24d4377ef2..e7fe0ebb77 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatorsearchwidget.cpp
@@ -97,10 +97,10 @@ void LineEdit::paintEvent(QPaintEvent *event)
QPalette p(palette());
p.setColor(QPalette::Active,
QPalette::PlaceholderText,
- Utils::creatorTheme()->color(Utils::Theme::DSplaceholderTextColor));
+ Utils::creatorColor(Utils::Theme::DSplaceholderTextColor));
p.setColor(QPalette::Inactive,
QPalette::PlaceholderText,
- Utils::creatorTheme()->color(Utils::Theme::DSplaceholderTextColor));
+ Utils::creatorColor(Utils::Theme::DSplaceholderTextColor));
setPalette(p);
}
QLineEdit::paintEvent(event);
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
index d305753ead..c5fa30fc7d 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
@@ -455,7 +455,7 @@ QStringList NavigatorTreeModel::mimeTypes() const
Constants::MIME_TYPE_MATERIAL,
Constants::MIME_TYPE_BUNDLE_TEXTURE,
Constants::MIME_TYPE_BUNDLE_MATERIAL,
- Constants::MIME_TYPE_BUNDLE_EFFECT,
+ Constants::MIME_TYPE_BUNDLE_ITEM,
Constants::MIME_TYPE_ASSETS});
return types;
@@ -570,9 +570,9 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
} else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_MATERIAL)) {
if (targetNode.isValid())
m_view->emitCustomNotification("drop_bundle_material", {targetNode}); // To ContentLibraryView
- } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_EFFECT)) {
+ } else if (mimeData->hasFormat(Constants::MIME_TYPE_BUNDLE_ITEM)) {
if (targetNode.isValid())
- m_view->emitCustomNotification("drop_bundle_effect", {targetNode}); // To ContentLibraryView
+ m_view->emitCustomNotification("drop_bundle_item", {targetNode}); // To ContentLibraryView
} else if (mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) {
const QStringList assetsPaths = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(',');
NodeAbstractProperty targetProperty;
@@ -705,7 +705,7 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
const ItemLibraryEntry itemLibraryEntry =
createItemLibraryEntryFromMimeData(mimeData->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO));
- const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry);
+ const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry, m_view->model());
const QString targetPropertyName = hints.forceNonDefaultProperty();
diff --git a/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp b/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp
index f99b964f2d..e5e42697e2 100644
--- a/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp
+++ b/src/plugins/qmldesigner/components/navigator/previewtooltip.cpp
@@ -21,7 +21,8 @@ PreviewToolTip::PreviewToolTip(QWidget *parent)
m_ui->idLabel->setElideMode(Qt::ElideLeft);
m_ui->typeLabel->setElideMode(Qt::ElideLeft);
m_ui->infoLabel->setElideMode(Qt::ElideLeft);
- setStyleSheet(QString("QWidget { background-color: %1 }").arg(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal).name()));
+ setStyleSheet(QString("QWidget { background-color: %1 }")
+ .arg(Utils::creatorColor(Utils::Theme::BackgroundColorNormal).name()));
m_ui->imageLabel->setStyleSheet("background-color: rgba(0, 0, 0, 0)");
static QPixmap checkers;
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp
index 248a9ec429..4034254420 100644
--- a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp
@@ -20,7 +20,8 @@ PreviewImageTooltip::PreviewImageTooltip(QWidget *parent)
m_ui->nameLabel->setElideMode(Qt::ElideLeft);
m_ui->pathLabel->setElideMode(Qt::ElideLeft);
m_ui->infoLabel->setElideMode(Qt::ElideLeft);
- setStyleSheet(QString("QWidget { background-color: %1 }").arg(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal).name()));
+ setStyleSheet(QString("QWidget { background-color: %1 }")
+ .arg(Utils::creatorColor(Utils::Theme::BackgroundColorNormal).name()));
}
PreviewImageTooltip::~PreviewImageTooltip() = default;
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp
index 16328ae532..b6d5e2ae47 100644
--- a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp
@@ -42,7 +42,18 @@ void PreviewTooltipBackend::showTooltip()
}
});
},
- [](auto) {},
+ [&](ImageCache::AbortReason abortReason) {
+ if (abortReason == ImageCache::AbortReason::Abort) {
+ qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation "
+ "failed for path %1, reason: Abort").arg(m_path);
+ } else if (abortReason == ImageCache::AbortReason::Failed) {
+ qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation "
+ "failed for path %1, reason: Failed").arg(m_path);
+ } else if (abortReason == ImageCache::AbortReason::NoEntry) {
+ qWarning() << QLatin1String("PreviewTooltipBackend::showTooltip(): preview generation "
+ "failed for path %1, reason: NoEntry").arg(m_path);
+ }
+ },
Utils::PathString{m_extraId},
m_auxiliaryData);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp
index 45f89ae339..4898366619 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/dynamicpropertiesproxymodel.cpp
@@ -143,7 +143,7 @@ QString DynamicPropertiesProxyModel::newPropertyName() const
{
DynamicPropertiesModel *propsModel = dynamicPropertiesModel();
- return QString::fromUtf8(uniquePropertyName("property", propsModel->singleSelectedNode()));
+ return QString::fromUtf8(uniquePropertyName("newName", propsModel->singleSelectedNode()));
}
void DynamicPropertiesProxyModel::createProperty(const QString &name, const QString &type)
@@ -167,6 +167,10 @@ void DynamicPropertiesProxyModel::createProperty(const QString &name, const QStr
QVariant value = defaultValueForType(typeName);
VariantProperty variantProp = modelNode.variantProperty(name.toUtf8());
variantProp.setDynamicTypeNameAndValue(typeName, value);
+ } else if (type == "signal") {
+ SignalDeclarationProperty signalDeclarationProperty
+ = modelNode.signalDeclarationProperty(name.toUtf8());
+ signalDeclarationProperty.setSignature("()");
} else {
QString expression = defaultExpressionForType(typeName);
@@ -270,6 +274,8 @@ PropertyEditorValue *DynamicPropertyRow::createProxyBackendValue()
auto *newValue = new PropertyEditorValue(this);
m_proxyBackendValues.append(newValue);
+ QQmlEngine::setObjectOwnership(newValue, QJSEngine::CppOwnership);
+
return newValue;
}
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
index 591ce5a57f..1a49ce0c39 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp
@@ -87,9 +87,9 @@ QString PropertyEditorContextObject::convertColorToString(const QVariant &color)
{
QString colorString;
QColor theColor;
- if (color.canConvert(QVariant::Color)) {
+ if (color.canConvert(QMetaType(QMetaType::QColor))) {
theColor = color.value<QColor>();
- } else if (color.canConvert(QVariant::Vector3D)) {
+ } else if (color.canConvert(QMetaType(QMetaType::QVector3D))) {
auto vec = color.value<QVector3D>();
theColor = QColor::fromRgbF(vec.x(), vec.y(), vec.z());
}
@@ -586,8 +586,7 @@ int PropertyEditorContextObject::devicePixelRatio()
QStringList PropertyEditorContextObject::styleNamesForFamily(const QString &family)
{
- const QFontDatabase dataBase;
- return dataBase.styles(family);
+ return QFontDatabase::styles(family);
}
QStringList PropertyEditorContextObject::allStatesForId(const QString &id)
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
index 7f1ab00bb9..c397d445b1 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp
@@ -82,17 +82,18 @@ namespace QmlDesigner {
PropertyEditorQmlBackend::PropertyEditorQmlBackend(PropertyEditorView *propertyEditor,
AsynchronousImageCache &imageCache)
- : m_view(new Quick2PropertyEditorView(imageCache))
- , m_propertyEditorTransaction(new PropertyEditorTransaction(propertyEditor))
- , m_dummyPropertyEditorValue(new PropertyEditorValue())
- , m_contextObject(new PropertyEditorContextObject(m_view))
+ : m_view(Utils::makeUniqueObjectPtr<Quick2PropertyEditorView>(imageCache))
+ , m_propertyEditorTransaction(std::make_unique<PropertyEditorTransaction>(propertyEditor))
+ , m_dummyPropertyEditorValue(std::make_unique<PropertyEditorValue>())
+ , m_contextObject(std::make_unique<PropertyEditorContextObject>(m_view.get()))
{
m_view->engine()->setOutputWarningsToStandardError(QmlDesignerPlugin::instance()
->settings().value(DesignerSettingsKey::SHOW_PROPERTYEDITOR_WARNINGS).toBool());
m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_dummyPropertyEditorValue->setValue(QLatin1String("#000000"));
- context()->setContextProperty(QLatin1String("dummyBackendValue"), m_dummyPropertyEditorValue.data());
+ context()->setContextProperty(QLatin1String("dummyBackendValue"),
+ m_dummyPropertyEditorValue.get());
m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
m_contextObject->setModel(propertyEditor->model());
m_contextObject->insertInQmlContext(context());
@@ -284,6 +285,27 @@ void PropertyEditorQmlBackend::setupAuxiliaryProperties(const QmlObjectNode &qml
}
}
+void PropertyEditorQmlBackend::handleInstancePropertyChangedInModelNodeProxy(
+ const ModelNode &modelNode, const PropertyName &propertyName)
+{
+ m_backendModelNode.handleInstancePropertyChanged(modelNode, propertyName);
+}
+
+void PropertyEditorQmlBackend::handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property)
+{
+ m_backendModelNode.handleVariantPropertyChanged(property);
+}
+
+void PropertyEditorQmlBackend::handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property)
+{
+ m_backendModelNode.handleBindingPropertyChanged(property);
+}
+
+void PropertyEditorQmlBackend::handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property)
+{
+ m_backendModelNode.handlePropertiesRemoved(property);
+}
+
void PropertyEditorQmlBackend::createPropertyEditorValue(const QmlObjectNode &qmlObjectNode,
const PropertyName &name,
const QVariant &value,
@@ -381,12 +403,12 @@ QQmlContext *PropertyEditorQmlBackend::context()
PropertyEditorContextObject *PropertyEditorQmlBackend::contextObject()
{
- return m_contextObject.data();
+ return m_contextObject.get();
}
QQuickWidget *PropertyEditorQmlBackend::widget()
{
- return m_view;
+ return m_view.get();
}
void PropertyEditorQmlBackend::setSource(const QUrl &url)
@@ -411,7 +433,7 @@ DesignerPropertyMap &PropertyEditorQmlBackend::backendValuesPropertyMap() {
}
PropertyEditorTransaction *PropertyEditorQmlBackend::propertyEditorTransaction() {
- return m_propertyEditorTransaction.data();
+ return m_propertyEditorTransaction.get();
}
PropertyEditorValue *PropertyEditorQmlBackend::propertyValueForName(const QString &propertyName)
@@ -474,12 +496,9 @@ void PropertyEditorQmlBackend::setup(const QmlObjectNode &qmlObjectNode, const Q
// anchors
m_backendAnchorBinding.setup(qmlObjectNode.modelNode());
- context()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
- {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.data())}
- }
- );
+ context()->setContextProperties(QVector<QQmlContext::PropertyPair>{
+ {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
+ {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}});
contextObject()->setHasMultiSelection(
!qmlObjectNode.view()->singleSelectedModelNode().isValid());
@@ -571,13 +590,10 @@ void PropertyEditorQmlBackend::initialSetup(const TypeName &typeName, const QUrl
QObject::connect(valueObject, &PropertyEditorValue::valueChanged, &backendValuesPropertyMap(), &DesignerPropertyMap::valueChanged);
m_backendValuesPropertyMap.insert(QLatin1String("id"), QVariant::fromValue(valueObject));
- context()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
- {{"modelNodeBackend"}, QVariant::fromValue(&m_backendModelNode)},
- {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.data())}
- }
- );
+ context()->setContextProperties(QVector<QQmlContext::PropertyPair>{
+ {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
+ {{"modelNodeBackend"}, QVariant::fromValue(&m_backendModelNode)},
+ {{"transaction"}, QVariant::fromValue(m_propertyEditorTransaction.get())}});
contextObject()->setSpecificsUrl(qmlSpecificsFile);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
index b677258488..1c9b808ac3 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.h
@@ -3,17 +3,21 @@
#pragma once
-#include "qmlanchorbindingproxy.h"
#include "designerpropertymap.h"
-#include "propertyeditorvalue.h"
#include "propertyeditorcontextobject.h"
+#include "propertyeditorvalue.h"
+#include "qmlanchorbindingproxy.h"
#include "qmlmodelnodeproxy.h"
#include "quick2propertyeditorview.h"
+#include <utils/uniqueobjectptr.h>
+
#include <nodemetainfo.h>
#include <QQmlPropertyMap>
+#include <memory>
+
class PropertyEditorValue;
namespace QmlDesigner {
@@ -71,7 +75,15 @@ public:
PropertyEditorView *propertyEditor);
void setupInsightAttachedProperties(const QmlObjectNode &qmlObjectNode,
PropertyEditorView *propertyEditor);
- void setupAuxiliaryProperties(const QmlObjectNode &qmlObjectNode, PropertyEditorView *propertyEditor);
+ void setupAuxiliaryProperties(const QmlObjectNode &qmlObjectNode,
+ PropertyEditorView *propertyEditor);
+
+ void handleInstancePropertyChangedInModelNodeProxy(const ModelNode &modelNode,
+ const PropertyName &propertyName);
+
+ void handleVariantPropertyChangedInModelNodeProxy(const VariantProperty &property);
+ void handleBindingPropertyChangedInModelNodeProxy(const BindingProperty &property);
+ void handlePropertiesRemovedInModelNodeProxy(const AbstractProperty &property);
static NodeMetaInfo findCommonAncestor(const ModelNode &node);
@@ -92,13 +104,16 @@ private:
static TypeName fixTypeNameForPanes(const TypeName &typeName);
private:
- Quick2PropertyEditorView *m_view;
+ // to avoid a crash while destructing DesignerPropertyMap in the QQmlData
+ // this needs be destructed after m_quickWidget->engine() is destructed
+ DesignerPropertyMap m_backendValuesPropertyMap;
+
+ Utils::UniqueObjectPtr<Quick2PropertyEditorView> m_view = nullptr;
QmlAnchorBindingProxy m_backendAnchorBinding;
QmlModelNodeProxy m_backendModelNode;
- DesignerPropertyMap m_backendValuesPropertyMap;
- QScopedPointer<PropertyEditorTransaction> m_propertyEditorTransaction;
- QScopedPointer<PropertyEditorValue> m_dummyPropertyEditorValue;
- QScopedPointer<PropertyEditorContextObject> m_contextObject;
+ std::unique_ptr<PropertyEditorTransaction> m_propertyEditorTransaction;
+ std::unique_ptr<PropertyEditorValue> m_dummyPropertyEditorValue;
+ std::unique_ptr<PropertyEditorContextObject> m_contextObject;
};
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
index 663ebafb65..27319c15f5 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp
@@ -10,16 +10,18 @@
#include "designmodewidget.h"
#include "nodemetainfo.h"
#include "nodeproperty.h"
+#include "propertyeditorview.h"
+#include "qmldesignerplugin.h"
#include "qmlitemnode.h"
#include "qmlobjectnode.h"
-#include "qmldesignerplugin.h"
+#include "rewritertransaction.h"
+#include "rewritingexception.h"
#include <enumeration.h>
#include <utils/qtcassert.h>
#include <QRegularExpression>
-#include <QScopedPointer>
#include <QUrl>
namespace QmlDesigner {
@@ -57,10 +59,10 @@ static bool cleverColorCompare(const QVariant &value1, const QVariant &value2)
return c1.name() == c2.name() && c1.alpha() == c2.alpha();
}
- if (value1.typeId() == QVariant::String && value2.typeId() == QVariant::Color)
+ if (value1.typeId() == QMetaType::QString && value2.typeId() == QVariant::Color)
return cleverColorCompare(QVariant(QColor(value1.toString())), value2);
- if (value1.typeId() == QVariant::Color && value2.typeId() == QVariant::String)
+ if (value1.typeId() == QVariant::Color && value2.typeId() == QMetaType::QString)
return cleverColorCompare(value1, QVariant(QColor(value2.toString())));
return false;
@@ -153,7 +155,8 @@ void PropertyEditorValue::setExpressionWithEmit(const QString &expression)
if (m_expression != expression) {
setExpression(expression);
m_value.clear();
- emit expressionChanged(nameAsQString()); // Note that we set the name in this case
+ emit expressionChanged(nameAsQString());
+ emit expressionChangedQml();// Note that we set the name in this case
}
}
@@ -180,7 +183,7 @@ bool PropertyEditorValue::isInSubState() const
bool PropertyEditorValue::isBound() const
{
const QmlObjectNode objectNode(modelNode());
- return objectNode.isValid() && objectNode.hasBindingProperty(name());
+ return m_forceBound || (objectNode.isValid() && objectNode.hasBindingProperty(name()));
}
bool PropertyEditorValue::isInModel() const
@@ -334,6 +337,7 @@ void PropertyEditorValue::resetValue()
m_expression = QString();
emit valueChanged(nameAsQString(), QVariant());
emit expressionChanged({});
+ emit expressionChangedQml();
}
}
@@ -425,6 +429,34 @@ QStringList PropertyEditorValue::getExpressionAsList() const
return generateStringList(expression());
}
+QVector<double> PropertyEditorValue::getExpressionAsVector() const
+{
+ const QRegularExpression rx(
+ QRegularExpression::anchoredPattern("Qt.vector(2|3|4)d\\((.*?)\\)"));
+ const QRegularExpressionMatch match = rx.match(expression());
+ if (!match.hasMatch())
+ return {};
+
+ const QStringList floats = match.captured(2).split(',', Qt::SkipEmptyParts);
+
+ bool ok;
+
+ const int num = match.captured(1).toInt();
+
+ if (num != floats.count())
+ return {};
+
+ QVector<double> ret;
+ for (const QString &number : floats) {
+ ret.append(number.toDouble(&ok));
+
+ if (!ok)
+ return {};
+ }
+
+ return ret;
+}
+
bool PropertyEditorValue::idListAdd(const QString &value)
{
const QmlObjectNode objectNode(modelNode());
@@ -507,6 +539,32 @@ void PropertyEditorValue::openMaterialEditor(int idx)
m_modelNode.view()->emitCustomNotification("select_material", {}, {idx});
}
+void PropertyEditorValue::setForceBound(bool b)
+{
+ if (m_forceBound == b)
+ return;
+ m_forceBound = b;
+
+ emit isBoundChanged();
+}
+
+void PropertyEditorValue::insertKeyframe()
+{
+ if (!m_modelNode.isValid())
+ return;
+
+ /*If we add more code here we have to forward the property editor view */
+ AbstractView *view = m_modelNode.view();
+
+ QmlTimeline timeline = view->currentTimeline();
+
+ QTC_ASSERT(timeline.isValid(), return );
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ view->executeInTransaction("PropertyEditorContextObject::insertKeyframe",
+ [&] { timeline.insertKeyframe(m_modelNode, name()); });
+}
+
QStringList PropertyEditorValue::generateStringList(const QString &string) const
{
QString copy = string;
@@ -687,4 +745,260 @@ void PropertyEditorNodeWrapper::update()
emit typeChanged();
}
+QQmlPropertyMap *PropertyEditorSubSelectionWrapper::properties()
+{
+ return &m_valuesPropertyMap;
+}
+
+static QObject *variantToQObject(const QVariant &value)
+{
+ if (value.typeId() == QMetaType::QObjectStar || value.typeId() > QMetaType::User)
+ return *(QObject **)value.constData();
+
+ return nullptr;
+}
+
+void PropertyEditorSubSelectionWrapper::createPropertyEditorValue(const QmlObjectNode &qmlObjectNode,
+ const PropertyName &name,
+ const QVariant &value)
+{
+ PropertyName propertyName(name);
+ propertyName.replace('.', '_');
+ auto valueObject = qobject_cast<PropertyEditorValue*>(variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(propertyName))));
+ if (!valueObject) {
+ valueObject = new PropertyEditorValue(&m_valuesPropertyMap);
+ QObject::connect(valueObject, &PropertyEditorValue::valueChanged, this, &PropertyEditorSubSelectionWrapper::changeValue);
+ QObject::connect(valueObject, &PropertyEditorValue::expressionChanged, this, &PropertyEditorSubSelectionWrapper::changeExpression);
+ QObject::connect(valueObject, &PropertyEditorValue::exportPropertyAsAliasRequested, this, &PropertyEditorSubSelectionWrapper::exportPropertyAsAlias);
+ QObject::connect(valueObject, &PropertyEditorValue::removeAliasExportRequested, this, &PropertyEditorSubSelectionWrapper::removeAliasExport);
+ m_valuesPropertyMap.insert(QString::fromUtf8(propertyName), QVariant::fromValue(valueObject));
+ }
+ valueObject->setName(name);
+ valueObject->setModelNode(qmlObjectNode);
+
+ if (qmlObjectNode.propertyAffectedByCurrentState(name) && !(qmlObjectNode.modelNode().property(name).isBindingProperty()))
+ valueObject->setValue(qmlObjectNode.modelValue(name));
+
+ else
+ valueObject->setValue(value);
+
+ if (propertyName != "id" &&
+ qmlObjectNode.currentState().isBaseState() &&
+ qmlObjectNode.modelNode().property(propertyName).isBindingProperty()) {
+ valueObject->setExpression(qmlObjectNode.modelNode().bindingProperty(propertyName).expression());
+ } else {
+ if (qmlObjectNode.hasBindingProperty(name))
+ valueObject->setExpression(qmlObjectNode.expression(name));
+ else
+ valueObject->setExpression(qmlObjectNode.instanceValue(name).toString());
+ }
+}
+
+void PropertyEditorSubSelectionWrapper::exportPropertyAsAlias(const QString &name)
+{
+ if (name.isNull())
+ return;
+
+ if (locked())
+ return;
+
+ QTC_ASSERT(m_modelNode.isValid(), return);
+
+ view()->executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){
+ PropertyEditorView::generateAliasForProperty(m_modelNode, name);
+ });
+}
+
+void PropertyEditorSubSelectionWrapper::removeAliasExport(const QString &name)
+{
+ if (name.isNull())
+ return;
+
+ if (locked())
+ return;
+
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ view()->executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name]() {
+ PropertyEditorView::removeAliasForProperty(m_modelNode, name);
+ });
+}
+
+bool PropertyEditorSubSelectionWrapper::locked() const
+{
+ return m_locked;
+}
+
+PropertyEditorSubSelectionWrapper::PropertyEditorSubSelectionWrapper(const ModelNode &modelNode)
+ : m_modelNode(modelNode)
+{
+ QmlObjectNode qmlObjectNode(modelNode);
+
+ QTC_ASSERT(qmlObjectNode.isValid(), return );
+
+ for (const auto &property : qmlObjectNode.modelNode().metaInfo().properties()) {
+ auto propertyName = property.name();
+ createPropertyEditorValue(qmlObjectNode,
+ propertyName,
+ qmlObjectNode.instanceValue(propertyName));
+ }
+}
+
+ModelNode PropertyEditorSubSelectionWrapper::modelNode() const
+{
+ return m_modelNode;
+}
+
+void PropertyEditorSubSelectionWrapper::deleteModelNode()
+{
+ QmlObjectNode objectNode(m_modelNode);
+
+ view()->executeInTransaction("PropertyEditorView::changeExpression", [&] {
+ if (objectNode.isValid())
+ objectNode.destroy();
+ });
+}
+
+void PropertyEditorSubSelectionWrapper::changeValue(const QString &name)
+{
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ if (name.isNull())
+ return;
+
+ if (locked())
+ return;
+
+ const QScopeGuard cleanup([&] { m_locked = false; });
+ m_locked = true;
+
+ const NodeMetaInfo metaInfo = m_modelNode.metaInfo();
+ QVariant castedValue;
+ PropertyEditorValue *value = qobject_cast<PropertyEditorValue *>(
+ variantToQObject(m_valuesPropertyMap.value(name)));
+
+ if (auto property = metaInfo.property(name.toUtf8())) {
+ castedValue = property.castedValue(value->value());
+
+ if (castedValue.typeId() == QVariant::Color) {
+ QColor color = castedValue.value<QColor>();
+ QColor newColor = QColor(color.name());
+ newColor.setAlpha(color.alpha());
+ castedValue = QVariant(newColor);
+ }
+
+ if (!value->value().isValid()) { // reset
+ removePropertyFromModel(name.toUtf8());
+ } else {
+ if (castedValue.isValid())
+ commitVariantValueToModel(name.toUtf8(), castedValue);
+ }
+ }
+}
+
+void PropertyEditorSubSelectionWrapper::setValueFromModel(const PropertyName &name,
+ const QVariant &value)
+{
+ m_locked = true;
+
+ QmlObjectNode qmlObjectNode(m_modelNode);
+
+ PropertyName propertyName = name;
+ propertyName.replace('.', '_');
+ auto propertyValue = qobject_cast<PropertyEditorValue *>(
+ variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(propertyName))));
+ if (propertyValue)
+ propertyValue->setValue(value);
+ m_locked = false;
+}
+
+void PropertyEditorSubSelectionWrapper::resetValue(const PropertyName &name)
+{
+ auto propertyValue = qobject_cast<PropertyEditorValue *>(
+ variantToQObject(m_valuesPropertyMap.value(QString::fromUtf8(name))));
+ if (propertyValue)
+ propertyValue->resetValue();
+}
+
+bool PropertyEditorSubSelectionWrapper::isRelevantModelNode(const ModelNode &modelNode) const
+{
+ QmlObjectNode objectNode(m_modelNode);
+ return modelNode == m_modelNode || objectNode.propertyChangeForCurrentState() == modelNode;
+}
+
+void PropertyEditorSubSelectionWrapper::changeExpression(const QString &propertyName)
+{
+ PropertyName name = propertyName.toUtf8();
+
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ if (name.isNull())
+ return;
+
+ if (locked())
+ return;
+
+ const QScopeGuard cleanup([&] { m_locked = false; });
+ m_locked = true;
+
+ view()->executeInTransaction("PropertyEditorView::changeExpression", [this, name, propertyName] {
+ QmlObjectNode qmlObjectNode{m_modelNode};
+ PropertyEditorValue *value = qobject_cast<PropertyEditorValue *>(
+ variantToQObject(m_valuesPropertyMap.value(propertyName)));
+
+ if (!value) {
+ qWarning() << "PropertyEditor::changeExpression no value for " << propertyName;
+ return;
+ }
+
+ if (value->expression().isEmpty()) {
+ value->resetValue();
+ return;
+ }
+ PropertyEditorView::setExpressionOnObjectNode(qmlObjectNode, name, value->expression());
+ }); /* end of transaction */
+}
+
+void PropertyEditorSubSelectionWrapper::removePropertyFromModel(const PropertyName &propertyName)
+{
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ m_locked = true;
+ try {
+ RewriterTransaction transaction = view()->beginRewriterTransaction(
+ "PropertyEditorView::removePropertyFromModel");
+
+ QmlObjectNode(m_modelNode).removeProperty(propertyName);
+
+ transaction.commit();
+ } catch (const RewritingException &e) {
+ e.showException();
+ }
+ m_locked = false;
+}
+
+void PropertyEditorSubSelectionWrapper::commitVariantValueToModel(const PropertyName &propertyName,
+ const QVariant &value)
+{
+ QTC_ASSERT(m_modelNode.isValid(), return );
+
+ try {
+ RewriterTransaction transaction = view()->beginRewriterTransaction(
+ "PropertyEditorView::commitVariantValueToMode");
+
+ QmlObjectNode(m_modelNode).setVariantProperty(propertyName, value);
+
+ transaction.commit();
+ } catch (const RewritingException &e) {
+ e.showException();
+ }
+}
+
+AbstractView *PropertyEditorSubSelectionWrapper::view() const
+{
+ QTC_CHECK(m_modelNode.isValid());
+
+ return m_modelNode.view();
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
index 59236c4fe2..c4b09f6b5a 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h
@@ -14,6 +14,43 @@ namespace QmlDesigner {
class PropertyEditorValue;
+class PropertyEditorSubSelectionWrapper : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QQmlPropertyMap *properties READ properties NOTIFY propertiesChanged)
+
+signals:
+ void propertiesChanged();
+
+public:
+ QQmlPropertyMap *properties();
+ PropertyEditorSubSelectionWrapper(const ModelNode &modelNode);
+ ModelNode modelNode() const;
+
+ Q_INVOKABLE void deleteModelNode();
+
+ void setValueFromModel(const PropertyName &name, const QVariant &value);
+ void resetValue(const PropertyName &name);
+
+ bool isRelevantModelNode(const ModelNode &modelNode) const;
+
+private:
+ void changeValue(const QString &name);
+ void changeExpression(const QString &propertyName);
+ void createPropertyEditorValue(const QmlObjectNode &qmlObjectNode, const PropertyName &name, const QVariant &value);
+ void exportPropertyAsAlias(const QString &name);
+ void removeAliasExport(const QString &name);
+ bool locked() const;
+
+ ModelNode m_modelNode;
+ QQmlPropertyMap m_valuesPropertyMap;
+ bool m_locked = false;
+ void removePropertyFromModel(const PropertyName &propertyName);
+ void commitVariantValueToModel(const PropertyName &propertyName, const QVariant &value);
+ AbstractView *view() const;
+};
+
class PropertyEditorNodeWrapper : public QObject
{
Q_OBJECT
@@ -126,12 +163,17 @@ public:
bool isIdList() const;
Q_INVOKABLE QStringList getExpressionAsList() const;
+ Q_INVOKABLE QVector<double> getExpressionAsVector() const;
Q_INVOKABLE bool idListAdd(const QString &value);
Q_INVOKABLE bool idListRemove(int idx);
Q_INVOKABLE bool idListReplace(int idx, const QString &value);
Q_INVOKABLE void commitDrop(const QString &dropData);
Q_INVOKABLE void openMaterialEditor(int idx);
+ Q_INVOKABLE void setForceBound(bool b);
+
+ Q_INVOKABLE void insertKeyframe();
+
public slots:
void resetValue();
void setEnumeration(const QString &scope, const QString &name);
@@ -143,6 +185,8 @@ signals:
void expressionChanged(const QString &name); // HACK - We use the same notifer for the backend and frontend.
// If name is empty the signal is used for QML.
+ void expressionChangedQml();
+
void exportPropertyAsAliasRequested(const QString &name);
void removeAliasExportRequested(const QString &name);
@@ -168,6 +212,7 @@ private:
bool m_hasActiveDrag = false;
bool m_isValid = false; // if the property value belongs to a non-existing complexProperty it is invalid
PropertyEditorNodeWrapper *m_complexNode;
+ bool m_forceBound = false;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
index 1ff098f4ea..e0d5759617 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp
@@ -38,7 +38,6 @@
#include <QFileSystemWatcher>
#include <QQuickItem>
#include <QScopeGuard>
-#include <QScopedPointer>
#include <QShortcut>
#include <QTimer>
@@ -256,66 +255,19 @@ void PropertyEditorView::changeExpression(const QString &propertyName)
underscoreName.replace('.', '_');
QmlObjectNode qmlObjectNode{m_selectedNode};
- PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName(QString::fromLatin1(underscoreName));
+ PropertyEditorValue *value = m_qmlBackEndForCurrentType->propertyValueForName(
+ QString::fromUtf8(underscoreName));
if (!value) {
qWarning() << "PropertyEditor::changeExpression no value for " << underscoreName;
return;
}
- if (auto property = qmlObjectNode.modelNode().metaInfo().property(name)) {
- const auto &propertType = property.propertyType();
- if (propertType.isColor()) {
- if (QColor(value->expression().remove('"')).isValid()) {
- qmlObjectNode.setVariantProperty(name, QColor(value->expression().remove('"')));
- return;
- }
- } else if (propertType.isBool()) {
- if (isTrueFalseLiteral(value->expression())) {
- if (value->expression().compare("true", Qt::CaseInsensitive) == 0)
- qmlObjectNode.setVariantProperty(name, true);
- else
- qmlObjectNode.setVariantProperty(name, false);
- return;
- }
- } else if (propertType.isInteger()) {
- bool ok;
- int intValue = value->expression().toInt(&ok);
- if (ok) {
- qmlObjectNode.setVariantProperty(name, intValue);
- return;
- }
- } else if (propertType.isFloat()) {
- bool ok;
- qreal realValue = value->expression().toDouble(&ok);
- if (ok) {
- qmlObjectNode.setVariantProperty(name, realValue);
- return;
- }
- } else if (propertType.isVariant()) {
- bool ok;
- qreal realValue = value->expression().toDouble(&ok);
- if (ok) {
- qmlObjectNode.setVariantProperty(name, realValue);
- return;
- } else if (isTrueFalseLiteral(value->expression())) {
- if (value->expression().compare("true", Qt::CaseInsensitive) == 0)
- qmlObjectNode.setVariantProperty(name, true);
- else
- qmlObjectNode.setVariantProperty(name, false);
- return;
- }
- }
- }
-
if (value->expression().isEmpty()) {
value->resetValue();
return;
}
-
- if (qmlObjectNode.expression(name) != value->expression()
- || !qmlObjectNode.propertyAffectedByCurrentState(name))
- qmlObjectNode.setBindingProperty(name, value->expression());
+ setExpressionOnObjectNode(qmlObjectNode, name, value->expression());
}); /* end of transaction */
}
@@ -330,21 +282,8 @@ void PropertyEditorView::exportPropertyAsAlias(const QString &name)
if (noValidSelection())
return;
- executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){
- const QString id = m_selectedNode.validId();
- QString upperCasePropertyName = name;
- upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper());
- QString aliasName = id + upperCasePropertyName;
- aliasName.replace(".", ""); //remove all dots
-
- PropertyName propertyName = aliasName.toUtf8();
- if (rootModelNode().hasProperty(propertyName)) {
- Core::AsynchronousMessageBox::warning(tr("Cannot Export Property as Alias"),
- tr("Property %1 does already exist for root component.").arg(aliasName));
- return;
- }
- rootModelNode().bindingProperty(propertyName).setDynamicTypeNameAndExpression("alias", id + "." + name);
- });
+ executeInTransaction("PropertyEditorView::exportPropertyAsAlias",
+ [this, name]() { generateAliasForProperty(m_selectedNode, name); });
}
void PropertyEditorView::removeAliasExport(const QString &name)
@@ -358,15 +297,8 @@ void PropertyEditorView::removeAliasExport(const QString &name)
if (noValidSelection())
return;
- executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){
- const QString id = m_selectedNode.validId();
-
- for (const BindingProperty &property : rootModelNode().bindingProperties())
- if (property.expression() == (id + "." + name)) {
- rootModelNode().removeProperty(property.name());
- break;
- }
- });
+ executeInTransaction("PropertyEditorView::exportPropertyAsAlias",
+ [this, name]() { removeAliasForProperty(m_selectedNode, name); });
}
bool PropertyEditorView::locked() const
@@ -384,11 +316,113 @@ void PropertyEditorView::refreshMetaInfos(const TypeIds &deletedTypeIds)
m_propertyComponentGenerator.refreshMetaInfos(deletedTypeIds);
}
+void PropertyEditorView::setExpressionOnObjectNode(const QmlObjectNode &constObjectNode,
+ const PropertyName &name,
+ const QString &newExpression)
+{
+ auto qmlObjectNode = constObjectNode;
+ auto expression = newExpression;
+ if (auto property = qmlObjectNode.modelNode().metaInfo().property(name)) {
+ const auto &propertType = property.propertyType();
+ if (propertType.isColor()) {
+ if (QColor(expression.remove('"')).isValid()) {
+ qmlObjectNode.setVariantProperty(name, QColor(expression.remove('"')));
+ return;
+ }
+ } else if (propertType.isBool()) {
+ if (isTrueFalseLiteral(expression)) {
+ if (expression.compare("true", Qt::CaseInsensitive) == 0)
+ qmlObjectNode.setVariantProperty(name, true);
+ else
+ qmlObjectNode.setVariantProperty(name, false);
+ return;
+ }
+ } else if (propertType.isInteger()) {
+ bool ok;
+ int intValue = expression.toInt(&ok);
+ if (ok) {
+ qmlObjectNode.setVariantProperty(name, intValue);
+ return;
+ }
+ } else if (propertType.isFloat()) {
+ bool ok;
+ qreal realValue = expression.toDouble(&ok);
+ if (ok) {
+ qmlObjectNode.setVariantProperty(name, realValue);
+ return;
+ }
+ } else if (propertType.isVariant()) {
+ bool ok;
+ qreal realValue = expression.toDouble(&ok);
+ if (ok) {
+ qmlObjectNode.setVariantProperty(name, realValue);
+ return;
+ } else if (isTrueFalseLiteral(expression)) {
+ if (expression.compare("true", Qt::CaseInsensitive) == 0)
+ qmlObjectNode.setVariantProperty(name, true);
+ else
+ qmlObjectNode.setVariantProperty(name, false);
+ return;
+ }
+ }
+ }
+
+ if (qmlObjectNode.expression(name) != expression
+ || !qmlObjectNode.propertyAffectedByCurrentState(name))
+ qmlObjectNode.setBindingProperty(name, expression);
+}
+
+void PropertyEditorView::generateAliasForProperty(const ModelNode &modelNode, const QString &name)
+{
+ QTC_ASSERT(modelNode.isValid(), return );
+
+ auto view = modelNode.view();
+
+ auto rootNode = view->rootModelNode();
+
+ auto nonConstModelNode = modelNode;
+ const QString id = nonConstModelNode.validId();
+
+ QString upperCasePropertyName = name;
+ upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper());
+ QString aliasName = id + upperCasePropertyName;
+ aliasName.replace(".", ""); //remove all dots
+
+ PropertyName propertyName = aliasName.toUtf8();
+ if (rootNode.hasProperty(propertyName)) {
+ Core::AsynchronousMessageBox::warning(
+ tr("Cannot Export Property as Alias"),
+ tr("Property %1 does already exist for root component.").arg(aliasName));
+ return;
+ }
+ rootNode.bindingProperty(propertyName).setDynamicTypeNameAndExpression("alias", id + "." + name);
+}
+
+void PropertyEditorView::removeAliasForProperty(const ModelNode &modelNode, const QString &propertyName)
+{
+ QTC_ASSERT(modelNode.isValid(), return );
+
+ auto view = modelNode.view();
+
+ auto rootNode = view->rootModelNode();
+
+ auto nonConstModelNode = modelNode;
+
+ const QString id = nonConstModelNode.validId();
+
+ for (const BindingProperty &property : rootNode.bindingProperties()) {
+ if (property.expression() == (id + "." + propertyName)) {
+ rootNode.removeProperty(property.name());
+ break;
+ }
+ }
+}
+
void PropertyEditorView::updateSize()
{
if (!m_qmlBackEndForCurrentType)
return;
- auto frame = m_qmlBackEndForCurrentType->widget()->findChild<QWidget*>("propertyEditorFrame");
+ auto frame = m_qmlBackEndForCurrentType->widget()->findChild<QWidget *>("propertyEditorFrame");
if (frame)
frame->resize(m_stackedWidget->size());
}
@@ -747,7 +781,11 @@ void PropertyEditorView::propertiesRemoved(const QList<AbstractProperty> &proper
if (noValidSelection())
return;
+ QTC_ASSERT(m_qmlBackEndForCurrentType, return );
+
for (const AbstractProperty &property : propertyList) {
+ m_qmlBackEndForCurrentType->handlePropertiesRemovedInModelNodeProxy(property);
+
ModelNode node(property.parentModelNode());
if (node.isRootNode() && !m_selectedNode.isRootNode())
@@ -805,7 +843,11 @@ void PropertyEditorView::variantPropertiesChanged(const QList<VariantProperty>&
if (noValidSelection())
return;
+ QTC_ASSERT(m_qmlBackEndForCurrentType, return );
+
for (const VariantProperty &property : propertyList) {
+ m_qmlBackEndForCurrentType->handleVariantPropertyChangedInModelNodeProxy(property);
+
ModelNode node(property.parentModelNode());
if (propertyIsAttachedLayoutProperty(property.name()))
@@ -830,7 +872,11 @@ void PropertyEditorView::bindingPropertiesChanged(const QList<BindingProperty> &
if (locked() || noValidSelection())
return;
+ QTC_ASSERT(m_qmlBackEndForCurrentType, return );
+
for (const BindingProperty &property : propertyList) {
+ m_qmlBackEndForCurrentType->handleBindingPropertyChangedInModelNodeProxy(property);
+
ModelNode node(property.parentModelNode());
if (property.isAliasExport())
@@ -952,6 +998,9 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
{
if (!m_selectedNode.isValid())
return;
+
+ QTC_ASSERT(m_qmlBackEndForCurrentType, return );
+
m_locked = true;
using ModelNodePropertyPair = QPair<ModelNode, PropertyName>;
@@ -960,7 +1009,11 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
const QmlObjectNode qmlObjectNode(modelNode);
const PropertyName propertyName = propertyPair.second;
- if (qmlObjectNode.isValid() && m_qmlBackEndForCurrentType && modelNode == m_selectedNode && qmlObjectNode.currentState().isValid()) {
+ m_qmlBackEndForCurrentType->handleInstancePropertyChangedInModelNodeProxy(modelNode,
+ propertyName);
+
+ if (qmlObjectNode.isValid() && m_qmlBackEndForCurrentType && modelNode == m_selectedNode
+ && qmlObjectNode.currentState().isValid()) {
const AbstractProperty property = modelNode.property(propertyName);
if (modelNode == m_selectedNode || qmlObjectNode.propertyChangeForCurrentState() == qmlObjectNode) {
if ( !modelNode.hasProperty(propertyName) || modelNode.property(property.name()).isBindingProperty() )
@@ -969,7 +1022,6 @@ void PropertyEditorView::instancePropertyChanged(const QList<QPair<ModelNode, Pr
setValue(modelNode, property.name(), qmlObjectNode.modelValue(property.name()));
}
}
-
}
m_locked = false;
diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h
index cc9b522839..ef2b71f558 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h
@@ -84,6 +84,16 @@ public:
void refreshMetaInfos(const TypeIds &deletedTypeIds) override;
+ static void setExpressionOnObjectNode(const QmlObjectNode &objectNode,
+ const PropertyName &name,
+ const QString &expression);
+
+ static void generateAliasForProperty(const ModelNode &modelNode,
+ const QString &propertyName);
+
+ static void removeAliasForProperty(const ModelNode &modelNode,
+ const QString &propertyName);
+
protected:
void timerEvent(QTimerEvent *event) override;
void setupPane(const TypeName &typeName);
diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp
index 96ec5f92e7..1cff0e55e6 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.cpp
@@ -4,6 +4,15 @@
#include "abstractview.h"
#include "qmlmodelnodeproxy.h"
+#include <nodemetainfo.h>
+
+#include <nodeabstractproperty.h>
+#include <nodelistproperty.h>
+#include <variantproperty.h>
+
+#include <utils/qtcassert.h>
+#include <utils/algorithm.h>
+
#include <QtQml>
namespace QmlDesigner {
@@ -17,6 +26,8 @@ void QmlModelNodeProxy::setup(const QmlObjectNode &objectNode)
{
m_qmlObjectNode = objectNode;
+ m_subselection.clear();
+
emit modelNodeChanged();
}
@@ -75,4 +86,229 @@ QString QmlModelNodeProxy::simplifiedTypeName() const
return m_qmlObjectNode.simplifiedTypeName();
}
+static QList<int> toInternalIdList(const QList<ModelNode> &nodes)
+{
+ return Utils::transform(nodes, [](const ModelNode &node) { return node.internalId(); });
+}
+
+QList<int> QmlModelNodeProxy::allChildren(int internalId) const
+{
+ ModelNode modelNode = m_qmlObjectNode.modelNode();
+
+ QTC_ASSERT(modelNode.isValid(), return {});
+
+ if (internalId >= 0)
+ modelNode = modelNode.view()->modelNodeForInternalId(internalId);
+
+ return allChildren(modelNode);
+}
+
+QList<int> QmlModelNodeProxy::allChildrenOfType(const QString &typeName, int internalId) const
+{
+ ModelNode modelNode = m_qmlObjectNode.modelNode();
+
+ QTC_ASSERT(modelNode.isValid(), return {});
+
+ if (internalId >= 0)
+ modelNode = modelNode.view()->modelNodeForInternalId(internalId);
+
+ return allChildrenOfType(modelNode, typeName);
+}
+
+QString QmlModelNodeProxy::simplifiedTypeName(int internalId) const
+{
+ ModelNode modelNode = m_qmlObjectNode.modelNode();
+
+ QTC_ASSERT(modelNode.isValid(), return {});
+
+ return modelNode.view()->modelNodeForInternalId(internalId).simplifiedTypeName();
+}
+
+PropertyEditorSubSelectionWrapper *QmlModelNodeProxy::findWrapper(int internalId) const
+{
+ for (const auto &item : qAsConst(m_subselection)) {
+ if (item->modelNode().internalId() == internalId)
+ return item.data();
+ }
+
+ return nullptr;
+}
+
+PropertyEditorSubSelectionWrapper *QmlModelNodeProxy::registerSubSelectionWrapper(int internalId)
+{
+ auto result = findWrapper(internalId);
+
+ if (result)
+ return result;
+
+ QTC_ASSERT(m_qmlObjectNode.isValid(), return nullptr);
+
+ ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalId);
+
+ QTC_ASSERT(node.isValid(), return nullptr);
+
+ QSharedPointer<PropertyEditorSubSelectionWrapper> wrapper(
+ new PropertyEditorSubSelectionWrapper(node));
+ m_subselection.append(wrapper);
+
+ QJSEngine::setObjectOwnership(wrapper.data(), QJSEngine::CppOwnership);
+
+ return wrapper.data();
+}
+
+void QmlModelNodeProxy::createModelNode(int internalIdParent,
+ const QString &property,
+ const QString &typeName,
+ const QString &requiredImport)
+{
+ auto parentModelNode = m_qmlObjectNode.modelNode();
+
+ QTC_ASSERT(parentModelNode.isValid(), return );
+
+ AbstractView *view = parentModelNode.view();
+
+ if (internalIdParent >= 0)
+ parentModelNode = view->modelNodeForInternalId(internalIdParent);
+
+ QTC_ASSERT(parentModelNode.isValid(), return );
+
+ Import import;
+ Q_ASSERT(import.isEmpty());
+
+ if (!requiredImport.isEmpty() && !view->model()->hasImport(requiredImport))
+ import = Import::createLibraryImport(requiredImport);
+
+ view->executeInTransaction("QmlModelNodeProxy::createModelNode", [&] {
+ if (!import.isEmpty())
+ view->model()->changeImports({import}, {});
+
+#ifdef QDS_USE_PROJECTSTORAGE
+ ModelNode newNode = view->createModelNode(typeName.toUtf8());
+#else
+ NodeMetaInfo metaInfo = parentModelNode.model()->metaInfo(typeName.toUtf8());
+ ModelNode newNode = view->createModelNode(metaInfo.typeName(),
+ metaInfo.majorVersion(),
+ metaInfo.minorVersion());
+#endif
+ parentModelNode.nodeAbstractProperty(property.toUtf8()).reparentHere(newNode);
+ });
+}
+
+void QmlModelNodeProxy::moveNode(int internalIdParent,
+ const QString &propertyName,
+ int fromIndex,
+ int toIndex)
+{
+ ModelNode modelNode = m_qmlObjectNode.modelNode();
+
+ QTC_ASSERT(modelNode.isValid(), return );
+
+ if (internalIdParent >= 0)
+ modelNode = m_qmlObjectNode.view()->modelNodeForInternalId(internalIdParent);
+
+ QTC_ASSERT(modelNode.isValid(), return );
+ AbstractView *view = m_qmlObjectNode.view();
+ view->executeInTransaction("QmlModelNodeProxy::moveNode", [&] {
+ modelNode.nodeListProperty(propertyName.toUtf8()).slide(fromIndex, toIndex);
+ });
+}
+
+bool QmlModelNodeProxy::isInstanceOf(const QString &typeName, int internalId) const
+{
+ ModelNode modelNode = m_qmlObjectNode.modelNode();
+
+ QTC_ASSERT(modelNode.isValid(), return {});
+
+ if (internalId >= 0)
+ modelNode = modelNode.view()->modelNodeForInternalId(internalId);
+
+ NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8());
+
+ return modelNode.metaInfo().isBasedOn(metaInfo);
+}
+
+void QmlModelNodeProxy::changeType(int internalId, const QString &typeName)
+{
+ QTC_ASSERT(m_qmlObjectNode.isValid(), return );
+
+ ModelNode node = m_qmlObjectNode.view()->modelNodeForInternalId(internalId);
+
+ QTC_ASSERT(node.isValid(), return );
+
+ QTC_ASSERT(!node.isRootNode(), return );
+#ifdef QDS_USE_PROJECTSTORAGE
+ node.changeType(typeName.toUtf8(), -1, -1);
+#else
+ NodeMetaInfo metaInfo = node.model()->metaInfo(typeName.toUtf8());
+ node.changeType(metaInfo.typeName(), metaInfo.majorVersion(), metaInfo.minorVersion());
+#endif
+}
+
+void QmlModelNodeProxy::handleInstancePropertyChanged(const ModelNode &modelNode,
+ const PropertyName &propertyName)
+{
+ const QmlObjectNode qmlObjectNode(modelNode);
+
+ for (const auto &item : qAsConst(m_subselection)) {
+ if (item && item->isRelevantModelNode(modelNode)) {
+ if (!modelNode.hasProperty(propertyName)
+ || modelNode.property(propertyName).isBindingProperty()) {
+ item->setValueFromModel(propertyName, qmlObjectNode.instanceValue(propertyName));
+ } else {
+ item->setValueFromModel(propertyName, qmlObjectNode.modelValue(propertyName));
+ }
+ }
+ }
+}
+
+void QmlModelNodeProxy::handleBindingPropertyChanged(const BindingProperty &property)
+{
+ for (const auto &item : qAsConst(m_subselection)) {
+ if (item && item->isRelevantModelNode(property.parentModelNode())) {
+ QmlObjectNode objectNode(item->modelNode());
+ if (objectNode.modelNode().property(property.name()).isBindingProperty())
+ item->setValueFromModel(property.name(), objectNode.instanceValue(property.name()));
+ else
+ item->setValueFromModel(property.name(), objectNode.modelValue(property.name()));
+ }
+ }
+}
+
+void QmlModelNodeProxy::handleVariantPropertyChanged(const VariantProperty &property)
+{
+ for (const auto &item : qAsConst(m_subselection)) {
+ if (item && item->isRelevantModelNode(property.parentModelNode())) {
+ QmlObjectNode objectNode(item->modelNode());
+ if (objectNode.modelNode().property(property.name()).isBindingProperty())
+ item->setValueFromModel(property.name(), objectNode.instanceValue(property.name()));
+ else
+ item->setValueFromModel(property.name(), objectNode.modelValue(property.name()));
+ }
+ }
+}
+
+void QmlModelNodeProxy::handlePropertiesRemoved(const AbstractProperty &property)
+{
+ for (const auto &item : qAsConst(m_subselection)) {
+ if (item && item->isRelevantModelNode(property.parentModelNode())) {
+ QmlObjectNode objectNode(item->modelNode());
+ item->resetValue(property.name());
+ item->setValueFromModel(property.name(), objectNode.instanceValue(property.name()));
+ }
+ }
+}
+
+QList<int> QmlModelNodeProxy::allChildren(const ModelNode &modelNode) const
+{
+ return toInternalIdList(modelNode.directSubModelNodes());
+}
+
+QList<int> QmlModelNodeProxy::allChildrenOfType(const ModelNode &modelNode, const QString &typeName) const
+{
+ QTC_ASSERT(modelNode.isValid(), return {});
+
+ NodeMetaInfo metaInfo = modelNode.model()->metaInfo(typeName.toUtf8());
+
+ return toInternalIdList(modelNode.directSubModelNodesOfType(metaInfo));
}
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h
index 4740b01fbd..d8a49d7e10 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h
@@ -3,6 +3,8 @@
#pragma once
+#include "propertyeditorvalue.h"
+
#include <qmlitemnode.h>
#include <QObject>
@@ -16,6 +18,7 @@ class QMLDESIGNERCORE_EXPORT QmlModelNodeProxy : public QObject
Q_PROPERTY(QmlDesigner::ModelNode modelNode READ modelNode NOTIFY modelNodeChanged)
Q_PROPERTY(bool multiSelection READ multiSelection NOTIFY modelNodeChanged)
+
public:
explicit QmlModelNodeProxy(QObject *parent = nullptr);
@@ -36,13 +39,45 @@ public:
QString simplifiedTypeName() const;
+ Q_INVOKABLE QList<int> allChildren(int internalId = -1) const;
+ Q_INVOKABLE QList<int> allChildrenOfType(const QString &typeName, int internalId = -1) const;
+
+ Q_INVOKABLE QString simplifiedTypeName(int internalId) const;
+
+ Q_INVOKABLE PropertyEditorSubSelectionWrapper *registerSubSelectionWrapper(int internalId);
+
+ Q_INVOKABLE void createModelNode(int internalIdParent,
+ const QString &property,
+ const QString &typeName,
+ const QString &requiredImport = {});
+
+ Q_INVOKABLE void moveNode(int internalIdParent,
+ const QString &propertyName,
+ int fromIndex,
+ int toIndex);
+
+ Q_INVOKABLE bool isInstanceOf(const QString &typeName, int internalId = -1) const;
+
+ Q_INVOKABLE void changeType(int internalId, const QString &typeName);
+
+ void handleInstancePropertyChanged(const ModelNode &modelNode, const PropertyName &propertyName);
+
+ void handleBindingPropertyChanged(const BindingProperty &property);
+ void handleVariantPropertyChanged(const VariantProperty &property);
+ void handlePropertiesRemoved(const AbstractProperty &property);
+
signals:
void modelNodeChanged();
void selectionToBeChanged();
void selectionChanged();
private:
+ QList<int> allChildren(const ModelNode &modelNode) const;
+ QList<int> allChildrenOfType(const ModelNode &modelNode, const QString &typeName) const;
+ PropertyEditorSubSelectionWrapper *findWrapper(int internalId) const;
+
QmlObjectNode m_qmlObjectNode;
+ QList<QSharedPointer<PropertyEditorSubSelectionWrapper>> m_subselection;
};
} //QmlDesigner
diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp
index a8981ff2f0..3f6f6769fb 100644
--- a/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp
+++ b/src/plugins/qmldesigner/components/texteditor/texteditorstatusbar.cpp
@@ -15,7 +15,8 @@ TextEditorStatusBar::TextEditorStatusBar(QWidget *parent) : QToolBar(parent), m_
addWidget(m_label);
/* We have to set another .css, since the central widget has already a style sheet */
- m_label->setStyleSheet(QString("QLabel { color :%1 }").arg(Utils::creatorTheme()->color(Utils::Theme::TextColorError).name()));
+ m_label->setStyleSheet(QString("QLabel { color :%1 }")
+ .arg(Utils::creatorColor(Utils::Theme::TextColorError).name()));
}
void TextEditorStatusBar::clearText()
diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp
index d400251648..2e52b3358b 100644
--- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp
+++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp
@@ -96,7 +96,8 @@ void TextEditorView::modelAboutToBeDetached(Model *model)
{
AbstractView::modelAboutToBeDetached(model);
- m_widget->setTextEditor(nullptr);
+ if (m_widget)
+ m_widget->setTextEditor(nullptr);
// in case the user closed it explicit we do not want to do anything with the editor
if (Core::ModeManager::currentModeId() == Core::Constants::MODE_DESIGN) {
diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp
index 97ef6c4b68..127780f918 100644
--- a/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/texteditor/texteditorwidget.cpp
@@ -265,7 +265,7 @@ void TextEditorWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
void TextEditorWidget::dragMoveEvent(QDragMoveEvent *dragMoveEvent)
{
- QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dragMoveEvent->pos());
+ QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dragMoveEvent->position().toPoint());
const int cursorPosition = cursor.position();
RewriterView *rewriterView = m_textEditorView->model()->rewriterView();
@@ -279,7 +279,7 @@ void TextEditorWidget::dragMoveEvent(QDragMoveEvent *dragMoveEvent)
void TextEditorWidget::dropEvent(QDropEvent *dropEvent)
{
- QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dropEvent->pos());
+ QTextCursor cursor = m_textEditor->editorWidget()->cursorForPosition(dropEvent->position().toPoint());
const int cursorPosition = cursor.position();
RewriterView *rewriterView = m_textEditorView->model()->rewriterView();
diff --git a/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp b/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp
index b9c5868bd8..dc57cf30c8 100644
--- a/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp
+++ b/src/plugins/qmldesigner/components/texttool/textedititemwidget.cpp
@@ -38,26 +38,26 @@ void TextEditItemWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem
QLineEdit* TextEditItemWidget::lineEdit() const
{
- if (m_lineEdit.isNull()) {
- m_lineEdit.reset(new QLineEdit);
+ if (!m_lineEdit) {
+ m_lineEdit = std::make_unique<QLineEdit>();
m_lineEdit->setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
QPalette palette = m_lineEdit->palette();
- static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
+ static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
palette.setColor(QPalette::Highlight, selectionColor);
palette.setColor(QPalette::HighlightedText, Qt::white);
palette.setColor(QPalette::Base, Qt::white);
palette.setColor(QPalette::Text, Qt::black);
m_lineEdit->setPalette(palette);
}
- return m_lineEdit.data();
+ return m_lineEdit.get();
}
QTextEdit* TextEditItemWidget::textEdit() const
{
- if (m_textEdit.isNull()) {
- m_textEdit.reset(new QTextEdit);
+ if (!m_textEdit) {
+ m_textEdit = std::make_unique<QTextEdit>();
QPalette palette = m_textEdit->palette();
- static QColor selectionColor = Utils::creatorTheme()->color(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
+ static QColor selectionColor = Utils::creatorColor(Utils::Theme::QmlDesigner_FormEditorSelectionColor);
palette.setColor(QPalette::Highlight, selectionColor);
palette.setColor(QPalette::HighlightedText, Qt::white);
palette.setColor(QPalette::Base, Qt::white);
@@ -65,7 +65,7 @@ QTextEdit* TextEditItemWidget::textEdit() const
m_textEdit->setPalette(palette);
}
- return m_textEdit.data();
+ return m_textEdit.get();
}
void TextEditItemWidget::activateTextEdit(const QSize &maximumSize)
@@ -83,19 +83,19 @@ void TextEditItemWidget::activateLineEdit()
QString TextEditItemWidget::text() const
{
- if (widget() == m_lineEdit.data())
+ if (widget() == m_lineEdit.get())
return m_lineEdit->text();
- else if (widget() == m_textEdit.data())
+ else if (widget() == m_textEdit.get())
return m_textEdit->toPlainText();
return QString();
}
void TextEditItemWidget::updateText(const QString &text)
{
- if (widget() == m_lineEdit.data()) {
+ if (widget() == m_lineEdit.get()) {
m_lineEdit->setText(text);
m_lineEdit->selectAll();
- } else if (widget() == m_textEdit.data()) {
+ } else if (widget() == m_textEdit.get()) {
m_textEdit->setText(text);
m_textEdit->selectAll();
}
diff --git a/src/plugins/qmldesigner/components/texttool/textedititemwidget.h b/src/plugins/qmldesigner/components/texttool/textedititemwidget.h
index 8aa6c409f9..f975f1a3da 100644
--- a/src/plugins/qmldesigner/components/texttool/textedititemwidget.h
+++ b/src/plugins/qmldesigner/components/texttool/textedititemwidget.h
@@ -3,7 +3,8 @@
#pragma once
#include <QGraphicsProxyWidget>
-#include <QScopedPointer>
+
+#include <memory>
QT_BEGIN_NAMESPACE
class QTextEdit;
@@ -32,7 +33,7 @@ protected:
QString text() const;
private:
- mutable QScopedPointer<QLineEdit> m_lineEdit;
- mutable QScopedPointer<QTextEdit> m_textEdit;
+ mutable std::unique_ptr<QLineEdit> m_lineEdit;
+ mutable std::unique_ptr<QTextEdit> m_textEdit;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp
index 73e784846b..30f276a48d 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorcontextobject.cpp
@@ -47,9 +47,9 @@ QString TextureEditorContextObject::convertColorToString(const QVariant &color)
{
QString colorString;
QColor theColor;
- if (color.canConvert(QVariant::Color)) {
+ if (color.canConvert(QMetaType(QMetaType::QColor))) {
theColor = color.value<QColor>();
- } else if (color.canConvert(QVariant::Vector3D)) {
+ } else if (color.canConvert(QMetaType(QMetaType::QVector3D))) {
auto vec = color.value<QVector3D>();
theColor = QColor::fromRgbF(vec.x(), vec.y(), vec.z());
}
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
index 80cf5693d2..415d943723 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.cpp
@@ -42,21 +42,22 @@ static QObject *variantToQObject(const QVariant &value)
namespace QmlDesigner {
-TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor, AsynchronousImageCache &imageCache)
- : m_view(new QQuickWidget)
- , m_textureEditorTransaction(new TextureEditorTransaction(textureEditor))
- , m_contextObject(new TextureEditorContextObject(m_view->rootContext()))
+TextureEditorQmlBackend::TextureEditorQmlBackend(TextureEditorView *textureEditor,
+ AsynchronousImageCache &imageCache)
+ : m_quickWidget(Utils::makeUniqueObjectPtr<QQuickWidget>())
+ , m_textureEditorTransaction(std::make_unique<TextureEditorTransaction>(textureEditor))
+ , m_contextObject(std::make_unique<TextureEditorContextObject>(m_quickWidget->rootContext()))
{
QImage defaultImage;
defaultImage.load(Utils::StyleHelper::dpiSpecificImageFile(":/textureeditor/images/texture_default.png"));
m_textureEditorImageProvider = new AssetImageProvider(imageCache, defaultImage);
- m_view->setResizeMode(QQuickWidget::SizeRootObjectToView);
- m_view->setObjectName(Constants::OBJECT_NAME_TEXTURE_EDITOR);
- m_view->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
- m_view->engine()->addImageProvider("qmldesigner_thumbnails", m_textureEditorImageProvider);
+ m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ m_quickWidget->setObjectName(Constants::OBJECT_NAME_TEXTURE_EDITOR);
+ m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
+ m_quickWidget->engine()->addImageProvider("qmldesigner_thumbnails", m_textureEditorImageProvider);
m_contextObject->setBackendValues(&m_backendValuesPropertyMap);
m_contextObject->setModel(textureEditor->model());
- context()->setContextObject(m_contextObject.data());
+ context()->setContextObject(m_contextObject.get());
QObject::connect(&m_backendValuesPropertyMap, &DesignerPropertyMap::valueChanged,
textureEditor, &TextureEditorView::changeValue);
@@ -154,22 +155,22 @@ void TextureEditorQmlBackend::setValue(const QmlObjectNode &, const PropertyName
QQmlContext *TextureEditorQmlBackend::context() const
{
- return m_view->rootContext();
+ return m_quickWidget->rootContext();
}
TextureEditorContextObject *TextureEditorQmlBackend::contextObject() const
{
- return m_contextObject.data();
+ return m_contextObject.get();
}
QQuickWidget *TextureEditorQmlBackend::widget() const
{
- return m_view;
+ return m_quickWidget.get();
}
void TextureEditorQmlBackend::setSource(const QUrl &url)
{
- m_view->setSource(url);
+ m_quickWidget->setSource(url);
}
QmlAnchorBindingProxy &TextureEditorQmlBackend::backendAnchorBinding()
@@ -184,7 +185,7 @@ DesignerPropertyMap &TextureEditorQmlBackend::backendValuesPropertyMap()
TextureEditorTransaction *TextureEditorQmlBackend::textureEditorTransaction() const
{
- return m_textureEditorTransaction.data();
+ return m_textureEditorTransaction.get();
}
PropertyEditorValue *TextureEditorQmlBackend::propertyValueForName(const QString &propertyName)
@@ -227,12 +228,9 @@ void TextureEditorQmlBackend::setup(const QmlObjectNode &selectedTextureNode, co
// anchors
m_backendAnchorBinding.setup(selectedTextureNode.modelNode());
- context()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
- {{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.data())}
- }
- );
+ context()->setContextProperties(QVector<QQmlContext::PropertyPair>{
+ {{"anchorBackend"}, QVariant::fromValue(&m_backendAnchorBinding)},
+ {{"transaction"}, QVariant::fromValue(m_textureEditorTransaction.get())}});
contextObject()->setSpecificsUrl(qmlSpecificsFile);
contextObject()->setStateName(stateName);
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h
index b6d3fddf22..f69c46a569 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorqmlbackend.h
@@ -7,8 +7,12 @@
#include "qmlanchorbindingproxy.h"
#include "qmlmodelnodeproxy.h"
+#include <utils/uniqueobjectptr.h>
+
#include <nodemetainfo.h>
+#include <memory>
+
class PropertyEditorValue;
QT_BEGIN_NAMESPACE
@@ -58,12 +62,15 @@ private:
TextureEditorView *textureEditor);
PropertyName auxNamePostFix(const PropertyName &propertyName);
- QQuickWidget *m_view = nullptr;
+ // to avoid a crash while destructing DesignerPropertyMap in the QQmlData
+ // this needs be destructed after m_quickWidget->engine() is destructed
+ DesignerPropertyMap m_backendValuesPropertyMap;
+
+ Utils::UniqueObjectPtr<QQuickWidget> m_quickWidget;
QmlAnchorBindingProxy m_backendAnchorBinding;
QmlModelNodeProxy m_backendModelNode;
- DesignerPropertyMap m_backendValuesPropertyMap;
- QScopedPointer<TextureEditorTransaction> m_textureEditorTransaction;
- QScopedPointer<TextureEditorContextObject> m_contextObject;
+ std::unique_ptr<TextureEditorTransaction> m_textureEditorTransaction;
+ std::unique_ptr<TextureEditorContextObject> m_contextObject;
AssetImageProvider *m_textureEditorImageProvider = nullptr;
};
diff --git a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
index 5de3730c97..a637431c4d 100644
--- a/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
+++ b/src/plugins/qmldesigner/components/textureeditor/textureeditorview.cpp
@@ -43,7 +43,6 @@
#include <QFileInfo>
#include <QQuickWidget>
#include <QQuickItem>
-#include <QScopedPointer>
#include <QStackedWidget>
#include <QShortcut>
#include <QTimer>
@@ -698,7 +697,8 @@ void TextureEditorView::selectedNodesChanged(const QList<ModelNode> &selectedNod
m_selectedModel = selectedNodeList.at(0);
bool hasValidSelection = QmlObjectNode(m_selectedModel).hasBindingProperty("materials");
- m_qmlBackEnd->contextObject()->setHasSingleModelSelection(hasValidSelection);
+ if (m_qmlBackEnd)
+ m_qmlBackEnd->contextObject()->setHasSingleModelSelection(hasValidSelection);
}
void TextureEditorView::currentStateChanged(const ModelNode &node)
diff --git a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp
index 2d8998404a..f289583cc3 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/setframevaluedialog.cpp
@@ -40,7 +40,7 @@ SetFrameValueDialog::SetFrameValueDialog(qreal frame, const QVariant &value,
valueLabel->setAlignment(Qt::AlignRight);
valueLabel->setFixedWidth(labelWidth);
- m_frameControl->setRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
+ m_frameControl->setRange(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max());
m_frameControl->setValue(static_cast<int>(frame));
m_frameControl->setAlignment(Qt::AlignRight);
@@ -86,7 +86,6 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value)
switch (value.metaType().id())
{
-
case QMetaType::QColor: {
auto* widget = new ColorControl(value.value<QColor>());
m_valueGetter = [widget]() { return widget->value(); };
@@ -102,7 +101,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value)
case QMetaType::Int: {
auto* widget = new QSpinBox;
- widget->setRange(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
+ widget->setRange(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max());
widget->setAlignment(Qt::AlignRight);
widget->setValue(value.toInt());
m_valueGetter = [widget]() { return widget->value(); };
@@ -120,7 +119,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value)
case QMetaType::Float: {
auto* widget = new QDoubleSpinBox;
- widget->setRange(std::numeric_limits<float>::min(), std::numeric_limits<float>::max());
+ widget->setRange(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::max());
widget->setAlignment(Qt::AlignRight);
widget->setValue(value.toFloat());
m_valueGetter = [widget]() { return static_cast<float>(widget->value()); };
@@ -132,7 +131,7 @@ QWidget* SetFrameValueDialog::createValueControl(const QVariant& value)
default: {
auto* widget = new QDoubleSpinBox;
- widget->setRange(std::numeric_limits<double>::min(), std::numeric_limits<double>::max());
+ widget->setRange(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max());
widget->setAlignment(Qt::AlignRight);
widget->setValue(value.toDouble());
m_valueGetter = [widget]() { return widget->value(); };
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
index 83551378ee..56e22dab1b 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineanimationform.cpp
@@ -13,6 +13,7 @@
#include <variantproperty.h>
#include <qmlitemnode.h>
#include <qmlobjectnode.h>
+#include <dialogutils.h>
#include <coreplugin/messagebox.h>
@@ -96,10 +97,10 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent)
using namespace Layouting;
Grid {
Span(4, mainL), br,
- empty(), br,
+ empty, br,
idL, Span(2, m_idLineEdit), Span(2, Row{ runningL, m_running }), br,
- empty(), startFrameL, m_startFrame, endFrameL, m_endFrame, durationL, m_duration, br,
- empty(), continuousL, m_continuous, loopsL, m_loops, pingPongL, m_pingPong, str, br,
+ empty, startFrameL, m_startFrame, endFrameL, m_endFrame, durationL, m_duration, br,
+ empty, continuousL, m_continuous, loopsL, m_loops, pingPongL, m_pingPong, str, br,
tr("Transition to state:"), transitionToStateL, m_transitionToState, br,
}.attachTo(this);
@@ -141,8 +142,7 @@ TimelineAnimationForm::TimelineAnimationForm(QWidget *parent)
bool error = false;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
- tr("%1 is an invalid id.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
error = true;
} else if (animation().view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
index b3d408dc0d..2d1e70cd77 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineform.cpp
@@ -10,6 +10,7 @@
#include <nodemetainfo.h>
#include <rewritertransaction.h>
#include <variantproperty.h>
+#include <dialogutils.h>
#include <coreplugin/messagebox.h>
@@ -77,8 +78,8 @@ TimelineForm::TimelineForm(QWidget *parent)
Grid {
Span(2, mainL), br,
idL, m_idLineEdit, br,
- empty(), Row { startFrameL, m_startFrame, st(), endFrameL, m_endFrame }, str, br,
- empty(), Row { m_expressionBinding, m_animation, st() }, br,
+ empty, Row { startFrameL, m_startFrame, st, endFrameL, m_endFrame }, str, br,
+ empty, Row { m_expressionBinding, m_animation, st }, br,
expressionBindingL, m_expressionBindingLineEdit, br,
}.attachTo(this);
@@ -125,8 +126,7 @@ TimelineForm::TimelineForm(QWidget *parent)
bool error = false;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
- tr("%1 is an invalid id.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
error = true;
} else if (m_timeline.view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid Id"),
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp
index 698ef0f03b..04cc8ebebc 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinepropertyitem.cpp
@@ -35,7 +35,6 @@
#include <QLineEdit>
#include <QMenu>
#include <QPainter>
-#include <QScopedPointer>
#include <algorithm>
@@ -108,14 +107,14 @@ static void editValue(const ModelNode &frameNode, const std::pair<qreal, qreal>
int userType = value.typeId();
QVariant newValue = dialog->value();
- if (newValue.canConvert(userType)) {
+ if (newValue.canConvert(QMetaType(userType))) {
QVariant newValueConverted = newValue;
- bool converted = newValueConverted.convert(userType);
+ bool converted = newValueConverted.convert(QMetaType(userType));
if (!converted) {
// convert() fails for int to double, so we try this combination
newValueConverted = newValue;
- converted = newValueConverted.convert(QMetaType::Double);
+ converted = newValueConverted.convert(QMetaType(QMetaType::Double));
}
if (converted)
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp
index 39c1f01ce6..9b85599ead 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesettingsmodel.cpp
@@ -68,7 +68,7 @@ TimelineEditorDelegate::TimelineEditorDelegate(QWidget *parent)
if (factory == nullptr) {
factory = new QItemEditorFactory;
QItemEditorCreatorBase *creator = new QItemEditorCreator<QComboBox>("currentText");
- factory->registerEditor(QVariant::String, creator);
+ factory->registerEditor(QMetaType::QString, creator);
}
setItemEditorFactory(factory);
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
index 7905df68e9..8288e69316 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
@@ -644,11 +644,12 @@ void TimelineView::registerActions()
TimelineWidget *TimelineView::createWidget()
{
- if (!m_timelineWidget)
+ if (!m_timelineWidget) {
m_timelineWidget = new TimelineWidget(this);
- auto *timelineContext = new TimelineContext(m_timelineWidget);
- Core::ICore::addContextObject(timelineContext);
+ auto *timelineContext = new TimelineContext(m_timelineWidget);
+ Core::ICore::addContextObject(timelineContext);
+ }
return m_timelineWidget;
}
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
index 591926d3f5..592cf0dff9 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
@@ -221,10 +221,10 @@ TimelineWidget::TimelineWidget(TimelineView *view)
{
QPalette timelinePalette;
- timelinePalette.setColor(QPalette::Text, Utils::creatorTheme()->color(
+ timelinePalette.setColor(QPalette::Text, Utils::creatorColor(
Utils::Theme::DStextColor));
timelinePalette.setColor(QPalette::WindowText, timelinePalette.color(QPalette::Text));
- timelinePalette.setColor(QPalette::Window, Utils::creatorTheme()->color(
+ timelinePalette.setColor(QPalette::Window, Utils::creatorColor(
Utils::Theme::QmlDesigner_BackgroundColorDarkAlternate));
onboardingTopLabel->setPalette(timelinePalette);
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
index e9df928c96..cb84183f92 100644
--- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
+++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.cpp
@@ -21,6 +21,9 @@
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/modemanager.h>
+
+#include <texteditor/textdocument.h>
+
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
@@ -302,6 +305,20 @@ ToolBarBackend::ToolBarBackend(QObject *parent)
this,
&ToolBarBackend::documentIndexChanged);
+ connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, this, [this]() {
+ static QMetaObject::Connection *lastConnection = nullptr;
+ delete lastConnection;
+
+ if (auto textDocument = qobject_cast<TextEditor::TextDocument *>(
+ Core::EditorManager::currentDocument())) {
+ connect(textDocument->document(),
+ &QTextDocument::modificationChanged,
+ this,
+ &ToolBarBackend::isDocumentDirtyChanged);
+ emit isDocumentDirtyChanged();
+ }
+ });
+
connect(Core::EditorManager::instance(),
&Core::EditorManager::currentEditorChanged,
this,
@@ -740,6 +757,12 @@ bool ToolBarBackend::isSharingEnabled()
return QmlDesigner::checkEnterpriseLicense();
}
+bool ToolBarBackend::isDocumentDirty() const
+{
+ return Core::EditorManager::currentDocument()
+ && Core::EditorManager::currentDocument()->isModified();
+}
+
void ToolBarBackend::launchGlobalAnnotations()
{
QmlDesignerPlugin::emitUsageStatistics(Constants::EVENT_TOOLBAR_EDIT_GLOBAL_ANNOTATION);
diff --git a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
index 5d0b0e712a..02bdae1717 100644
--- a/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
+++ b/src/plugins/qmldesigner/components/toolbar/toolbarbackend.h
@@ -97,6 +97,7 @@ class ToolBarBackend : public QObject
Q_PROPERTY(bool isMCUs READ isMCUs NOTIFY isMCUsChanged)
Q_PROPERTY(bool projectOpened READ projectOpened NOTIFY projectOpenedChanged)
Q_PROPERTY(bool isSharingEnabled READ isSharingEnabled NOTIFY isSharingEnabledChanged)
+ Q_PROPERTY(bool isDocumentDirty READ isDocumentDirty NOTIFY isDocumentDirtyChanged)
public:
ToolBarBackend(QObject *parent = nullptr);
@@ -147,6 +148,8 @@ public:
bool isSharingEnabled();
+ bool isDocumentDirty() const;
+
static void launchGlobalAnnotations();
signals:
@@ -167,6 +170,7 @@ signals:
void isMCUsChanged();
void projectOpenedChanged();
void isSharingEnabledChanged();
+ void isDocumentDirtyChanged();
private:
void setupWorkspaces();
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
index 104127bd49..9f9e888823 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
@@ -318,11 +318,12 @@ ModelNode TransitionEditorView::addNewTransition()
TransitionEditorWidget *TransitionEditorView::createWidget()
{
- if (!m_transitionEditorWidget)
+ if (!m_transitionEditorWidget) {
m_transitionEditorWidget = new TransitionEditorWidget(this);
- auto *transitionContext = new TransitionContext(m_transitionEditorWidget);
- Core::ICore::addContextObject(transitionContext);
+ auto *transitionContext = new TransitionContext(m_transitionEditorWidget);
+ Core::ICore::addContextObject(transitionContext);
+ }
return m_transitionEditorWidget;
}
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp
index 1770ba63fc..dcbb0b23b3 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitionform.cpp
@@ -13,6 +13,7 @@
#include <rewritertransaction.h>
#include <variantproperty.h>
#include <qmlitemnode.h>
+#include <dialogutils.h>
#include <coreplugin/messagebox.h>
@@ -45,8 +46,7 @@ TransitionForm::TransitionForm(QWidget *parent)
bool error = false;
if (!ModelNode::isValidId(newId)) {
- Core::AsynchronousMessageBox::warning(tr("Invalid ID"),
- tr("%1 is an invalid ID.").arg(newId));
+ DialogUtils::showWarningForInvalidId(newId);
error = true;
} else if (m_transition.view()->hasId(newId)) {
Core::AsynchronousMessageBox::warning(tr("Invalid ID"),