forked from qt-creator/qt-creator
QmlDesigner: Make imported 3D scenes available via assets
A placeholder .q3d file is created under content for imported 3D components found under Generated/QtQuick3D on asset view attach and every time new import is done. .q3d file contains a project root relative path to component's import folder. .q3d files get generated preview as icon in assets view. Imported 3D items are no longer shown in Components view. Removing .q3d file will remove the corresponding module as well as all model nodes created from that asset. Removing last model node of asset will remove the import statement on next document save. Fixes: QDS-12193 Fixes: QDS-14565 Change-Id: If01546ca4c78334bac73b055ed156276f6f8f2a4 Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
@@ -239,6 +239,9 @@ T.TreeViewDelegate {
|
|||||||
+ size.width + " x " + size.height
|
+ size.width + " x " + size.height
|
||||||
+ "\n" + fileSize
|
+ "\n" + fileSize
|
||||||
+ " " + fileExt
|
+ " " + fileExt
|
||||||
|
} else if (rootView.assetIsImported3d(model.filePath)) {
|
||||||
|
return filePath + "\n"
|
||||||
|
+ fileExt
|
||||||
} else {
|
} else {
|
||||||
return filePath + "\n"
|
return filePath + "\n"
|
||||||
+ fileSize
|
+ fileSize
|
||||||
|
@@ -187,50 +187,6 @@ void EffectComposerView::selectedNodesChanged(const QList<QmlDesigner::ModelNode
|
|||||||
m_widget->effectComposerModel()->setHasValidTarget(hasValidTarget);
|
m_widget->effectComposerModel()->setHasValidTarget(hasValidTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectComposerView::nodeAboutToBeRemoved(const QmlDesigner::ModelNode &removedNode)
|
|
||||||
{
|
|
||||||
QList<QmlDesigner::ModelNode> nodes = removedNode.allSubModelNodesAndThisNode();
|
|
||||||
bool effectRemoved = false;
|
|
||||||
for (const QmlDesigner::ModelNode &node : nodes) {
|
|
||||||
QmlDesigner::QmlItemNode qmlNode(node);
|
|
||||||
if (qmlNode.isEffectItem()) {
|
|
||||||
effectRemoved = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (effectRemoved)
|
|
||||||
QTimer::singleShot(0, this, &EffectComposerView::removeUnusedEffectImports);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EffectComposerView::removeUnusedEffectImports()
|
|
||||||
{
|
|
||||||
QTC_ASSERT(model(), return);
|
|
||||||
|
|
||||||
const QString effectPrefix = m_componentUtils.composedEffectsTypePrefix();
|
|
||||||
|
|
||||||
const QmlDesigner::Imports &imports = model()->imports();
|
|
||||||
QHash<QString, QmlDesigner::Import> effectImports;
|
|
||||||
for (const QmlDesigner::Import &import : imports) {
|
|
||||||
if (import.url().startsWith(effectPrefix)) {
|
|
||||||
QString type = import.url().split('.').last();
|
|
||||||
effectImports.insert(type, import);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const QList<QmlDesigner::ModelNode> allNodes = allModelNodes();
|
|
||||||
for (const QmlDesigner::ModelNode &node : allNodes) {
|
|
||||||
if (QmlDesigner::QmlItemNode(node).isEffectItem())
|
|
||||||
effectImports.remove(node.simplifiedTypeName());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!effectImports.isEmpty()) {
|
|
||||||
QmlDesigner::Imports removeImports;
|
|
||||||
for (const QmlDesigner::Import &import : effectImports)
|
|
||||||
removeImports.append(import);
|
|
||||||
model()->changeImports({}, removeImports);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EffectComposerView::highlightSupportedProperties(bool highlight, const QString &suffix)
|
void EffectComposerView::highlightSupportedProperties(bool highlight, const QString &suffix)
|
||||||
{
|
{
|
||||||
QQmlContext *ctxObj = m_widget->quickWidget()->rootContext();
|
QQmlContext *ctxObj = m_widget->quickWidget()->rootContext();
|
||||||
|
@@ -29,7 +29,6 @@ public:
|
|||||||
void modelAboutToBeDetached(QmlDesigner::Model *model) override;
|
void modelAboutToBeDetached(QmlDesigner::Model *model) override;
|
||||||
void selectedNodesChanged(const QList<QmlDesigner::ModelNode> &selectedNodeList,
|
void selectedNodesChanged(const QList<QmlDesigner::ModelNode> &selectedNodeList,
|
||||||
const QList<QmlDesigner::ModelNode> &lastSelectedNodeList) override;
|
const QList<QmlDesigner::ModelNode> &lastSelectedNodeList) override;
|
||||||
void nodeAboutToBeRemoved(const QmlDesigner::ModelNode &removedNode) override;
|
|
||||||
|
|
||||||
void dragStarted(QMimeData *mimeData) override;
|
void dragStarted(QMimeData *mimeData) override;
|
||||||
void dragEnded() override;
|
void dragEnded() override;
|
||||||
@@ -41,7 +40,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
void customNotification(const AbstractView *view, const QString &identifier,
|
void customNotification(const AbstractView *view, const QString &identifier,
|
||||||
const QList<QmlDesigner::ModelNode> &nodeList, const QList<QVariant> &data) override;
|
const QList<QmlDesigner::ModelNode> &nodeList, const QList<QVariant> &data) override;
|
||||||
void removeUnusedEffectImports();
|
|
||||||
|
|
||||||
QPointer<EffectComposerWidget> m_widget;
|
QPointer<EffectComposerWidget> m_widget;
|
||||||
QString m_currProjectPath;
|
QString m_currProjectPath;
|
||||||
|
@@ -24,5 +24,7 @@
|
|||||||
<file>images/asset_ktx_128.png</file>
|
<file>images/asset_ktx_128.png</file>
|
||||||
<file>images/asset_folder.png</file>
|
<file>images/asset_folder.png</file>
|
||||||
<file>images/asset_folder@2x.png</file>
|
<file>images/asset_folder@2x.png</file>
|
||||||
|
<file>images/asset_imported3d.png</file>
|
||||||
|
<file>images/asset_imported3d@2x.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@@ -77,7 +77,7 @@ QPixmap AssetsLibraryIconProvider::generateFontIcons(const QString &filePath, co
|
|||||||
"Abc"}).pixmap(reqSize);
|
"Abc"}).pixmap(reqSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPair<QPixmap, qint64> AssetsLibraryIconProvider::fetchPixmap(const QString &id, const QSize &requestedSize) const
|
QPair<QPixmap, qint64> AssetsLibraryIconProvider::fetchPixmap(const QString &id, const QSize &requestedSize)
|
||||||
{
|
{
|
||||||
Asset asset(id);
|
Asset asset(id);
|
||||||
|
|
||||||
@@ -100,6 +100,18 @@ QPair<QPixmap, qint64> AssetsLibraryIconProvider::fetchPixmap(const QString &id,
|
|||||||
qint64 size = QFileInfo(id).size();
|
qint64 size = QFileInfo(id).size();
|
||||||
QString filePath = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/asset_ktx.png");
|
QString filePath = Utils::StyleHelper::dpiSpecificImageFile(":/AssetsLibrary/images/asset_ktx.png");
|
||||||
return {QPixmap{filePath}, size};
|
return {QPixmap{filePath}, size};
|
||||||
|
} else if (asset.isImported3D()) {
|
||||||
|
static QPixmap defaultPreview = QPixmap::fromImage(QImage(":/AssetsLibrary/images/asset_imported3d.png"));
|
||||||
|
QPixmap pixmap{requestedSize};
|
||||||
|
QString assetId = id.mid(id.lastIndexOf('/') + 1);
|
||||||
|
assetId.chop(asset.suffix().size() - 1); // Remove suffix
|
||||||
|
if (m_pixmaps.contains(assetId)) {
|
||||||
|
pixmap = m_pixmaps.value(assetId);
|
||||||
|
} else {
|
||||||
|
pixmap = defaultPreview;
|
||||||
|
emit asyncAssetPreviewRequested(assetId, id);
|
||||||
|
}
|
||||||
|
return {pixmap, 0};
|
||||||
} else {
|
} else {
|
||||||
QString type;
|
QString type;
|
||||||
if (asset.isShader())
|
if (asset.isShader())
|
||||||
@@ -145,5 +157,18 @@ qint64 AssetsLibraryIconProvider::fileSize(const QString &id)
|
|||||||
return m_thumbnails.contains(id) ? m_thumbnails[id].fileSize : 0;
|
return m_thumbnails.contains(id) ? m_thumbnails[id].fileSize : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString AssetsLibraryIconProvider::setPixmap(const QString &id, const QPixmap &pixmap,
|
||||||
|
const QString &suffix)
|
||||||
|
{
|
||||||
|
m_pixmaps.insert(id, pixmap);
|
||||||
|
const QStringList thumbs = m_thumbnails.keys();
|
||||||
|
const QString checkName = id + "." + suffix;
|
||||||
|
for (const auto &thumb : thumbs) {
|
||||||
|
if (thumb.endsWith(checkName))
|
||||||
|
return thumb;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
|
||||||
|
@@ -20,6 +20,7 @@ struct Thumbnail
|
|||||||
|
|
||||||
class AssetsLibraryIconProvider : public QQuickImageProvider
|
class AssetsLibraryIconProvider : public QQuickImageProvider
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
AssetsLibraryIconProvider(SynchronousImageCache &fontImageCache);
|
AssetsLibraryIconProvider(SynchronousImageCache &fontImageCache);
|
||||||
|
|
||||||
@@ -28,10 +29,14 @@ public:
|
|||||||
void invalidateThumbnail(const QString &id);
|
void invalidateThumbnail(const QString &id);
|
||||||
QSize imageSize(const QString &id);
|
QSize imageSize(const QString &id);
|
||||||
qint64 fileSize(const QString &id);
|
qint64 fileSize(const QString &id);
|
||||||
|
QString setPixmap(const QString &id, const QPixmap &pixmap, const QString &suffix);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void asyncAssetPreviewRequested(const QString &assetId, const QString &assetFile);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QPixmap generateFontIcons(const QString &filePath, const QSize &requestedSize) const;
|
QPixmap generateFontIcons(const QString &filePath, const QSize &requestedSize) const;
|
||||||
QPair<QPixmap, qint64> fetchPixmap(const QString &id, const QSize &requestedSize) const;
|
QPair<QPixmap, qint64> fetchPixmap(const QString &id, const QSize &requestedSize);
|
||||||
Thumbnail createThumbnail(const QString &id, const QSize &requestedSize);
|
Thumbnail createThumbnail(const QString &id, const QSize &requestedSize);
|
||||||
|
|
||||||
SynchronousImageCache &m_fontImageCache;
|
SynchronousImageCache &m_fontImageCache;
|
||||||
@@ -42,6 +47,7 @@ private:
|
|||||||
{96, 96}, // list @2x
|
{96, 96}, // list @2x
|
||||||
{48, 48}}; // list
|
{48, 48}}; // list
|
||||||
QHash<QString, Thumbnail> m_thumbnails;
|
QHash<QString, Thumbnail> m_thumbnails;
|
||||||
|
QHash<QString, QPixmap> m_pixmaps;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -118,19 +118,33 @@ void AssetsLibraryModel::deleteFiles(const QStringList &filePaths, bool dontAskA
|
|||||||
if (dontAskAgain)
|
if (dontAskAgain)
|
||||||
QmlDesignerPlugin::settings().insert(DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET, false);
|
QmlDesignerPlugin::settings().insert(DesignerSettingsKey::ASK_BEFORE_DELETING_ASSET, false);
|
||||||
|
|
||||||
QStringList deletedEffects;
|
QHash<QString, Utils::FilePath> deletedAssets;
|
||||||
|
const GeneratedComponentUtils &compUtils = QmlDesignerPlugin::instance()->documentManager()
|
||||||
|
.generatedComponentUtils();
|
||||||
|
const QString effectTypePrefix = compUtils.composedEffectsTypePrefix();
|
||||||
|
const Utils::FilePath effectBasePath = compUtils.composedEffectsBasePath();
|
||||||
|
|
||||||
for (const QString &filePath : filePaths) {
|
for (const QString &filePath : filePaths) {
|
||||||
QFileInfo fi(filePath);
|
Utils::FilePath fp = Utils::FilePath::fromString(filePath);
|
||||||
if (fi.exists()) {
|
if (fp.exists()) {
|
||||||
if (QFile::remove(filePath)) {
|
// If a generated asset was removed, also remove its module from project
|
||||||
if (Asset(filePath).isEffect()) {
|
Asset asset(filePath);
|
||||||
// If an effect composer effect was removed, also remove effect module from project
|
QString fullType;
|
||||||
QString effectName = fi.baseName();
|
if (asset.isEffect()) {
|
||||||
if (!effectName.isEmpty())
|
QString effectName = fp.baseName();
|
||||||
deletedEffects.append(effectName);
|
fullType = QString("%1.%2.%2").arg(effectTypePrefix, effectName, effectName);
|
||||||
|
deletedAssets.insert(fullType, effectBasePath.resolvePath(effectName));
|
||||||
|
} else if (asset.isImported3D()) {
|
||||||
|
Utils::FilePath qmlFile = compUtils.getImported3dQml(filePath);
|
||||||
|
if (qmlFile.exists()) {
|
||||||
|
QString importName = compUtils.getImported3dImportName(qmlFile);
|
||||||
|
fullType = QString("%1.%2").arg(importName, qmlFile.baseName());
|
||||||
|
deletedAssets.insert(fullType, qmlFile.absolutePath());
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
if (!fp.removeFile()) {
|
||||||
|
deletedAssets.remove(fullType);
|
||||||
QMessageBox::warning(Core::ICore::dialogParent(),
|
QMessageBox::warning(Core::ICore::dialogParent(),
|
||||||
Tr::tr("Failed to Delete File"),
|
Tr::tr("Failed to Delete File"),
|
||||||
Tr::tr("Could not delete \"%1\".").arg(filePath));
|
Tr::tr("Could not delete \"%1\".").arg(filePath));
|
||||||
@@ -138,8 +152,8 @@ void AssetsLibraryModel::deleteFiles(const QStringList &filePaths, bool dontAskA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!deletedEffects.isEmpty())
|
if (!deletedAssets.isEmpty())
|
||||||
emit effectsDeleted(deletedEffects);
|
emit generatedAssetsDeleted(deletedAssets);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString &newName)
|
bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString &newName)
|
||||||
|
@@ -72,7 +72,7 @@ signals:
|
|||||||
void rootPathChanged();
|
void rootPathChanged();
|
||||||
void isEmptyChanged();
|
void isEmptyChanged();
|
||||||
void fileChanged(const QString &path);
|
void fileChanged(const QString &path);
|
||||||
void effectsDeleted(const QStringList &effectNames);
|
void generatedAssetsDeleted(const QHash<QString, Utils::FilePath> &assetData);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setIsEmpty(bool value);
|
void setIsEmpty(bool value);
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#include <imagecachecollectors/imagecachecollector.h>
|
#include <imagecachecollectors/imagecachecollector.h>
|
||||||
#include <imagecachecollectors/imagecacheconnectionmanager.h>
|
#include <imagecachecollectors/imagecacheconnectionmanager.h>
|
||||||
#include <imagecachecollectors/imagecachefontcollector.h>
|
#include <imagecachecollectors/imagecachefontcollector.h>
|
||||||
|
#include <modelnodeoperations.h>
|
||||||
#include <nodelistproperty.h>
|
#include <nodelistproperty.h>
|
||||||
#include <projectexplorer/kit.h>
|
#include <projectexplorer/kit.h>
|
||||||
#include <projectexplorer/project.h>
|
#include <projectexplorer/project.h>
|
||||||
@@ -41,11 +42,18 @@ public:
|
|||||||
TimeStampProvider timeStampProvider;
|
TimeStampProvider timeStampProvider;
|
||||||
AsynchronousImageCache asynchronousFontImageCache{storage, fontGenerator, timeStampProvider};
|
AsynchronousImageCache asynchronousFontImageCache{storage, fontGenerator, timeStampProvider};
|
||||||
SynchronousImageCache synchronousFontImageCache{storage, timeStampProvider, fontCollector};
|
SynchronousImageCache synchronousFontImageCache{storage, timeStampProvider, fontCollector};
|
||||||
|
AsynchronousImageCache *mainImageCache = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
AssetsLibraryView::AssetsLibraryView(ExternalDependenciesInterface &externalDependencies)
|
AssetsLibraryView::AssetsLibraryView(AsynchronousImageCache &imageCache,
|
||||||
|
ExternalDependenciesInterface &externalDependencies)
|
||||||
: AbstractView{externalDependencies}
|
: AbstractView{externalDependencies}
|
||||||
{}
|
{
|
||||||
|
imageCacheData()->mainImageCache = &imageCache;
|
||||||
|
m_3dImportsSyncTimer.callOnTimeout(this, &AssetsLibraryView::sync3dImports);
|
||||||
|
m_3dImportsSyncTimer.setInterval(1000);
|
||||||
|
m_3dImportsSyncTimer.setSingleShot(true);
|
||||||
|
}
|
||||||
|
|
||||||
AssetsLibraryView::~AssetsLibraryView()
|
AssetsLibraryView::~AssetsLibraryView()
|
||||||
{}
|
{}
|
||||||
@@ -59,6 +67,7 @@ WidgetInfo AssetsLibraryView::widgetInfo()
|
|||||||
{
|
{
|
||||||
if (!m_widget) {
|
if (!m_widget) {
|
||||||
m_widget = Utils::makeUniqueObjectPtr<AssetsLibraryWidget>(
|
m_widget = Utils::makeUniqueObjectPtr<AssetsLibraryWidget>(
|
||||||
|
*imageCacheData()->mainImageCache,
|
||||||
imageCacheData()->asynchronousFontImageCache,
|
imageCacheData()->asynchronousFontImageCache,
|
||||||
imageCacheData()->synchronousFontImageCache,
|
imageCacheData()->synchronousFontImageCache,
|
||||||
this);
|
this);
|
||||||
@@ -74,6 +83,8 @@ void AssetsLibraryView::customNotification(const AbstractView * /*view*/,
|
|||||||
{
|
{
|
||||||
if (identifier == "delete_selected_assets")
|
if (identifier == "delete_selected_assets")
|
||||||
m_widget->deleteSelectedAssets();
|
m_widget->deleteSelectedAssets();
|
||||||
|
else if (identifier == "asset_import_finished")
|
||||||
|
m_3dImportsSyncTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsLibraryView::modelAttached(Model *model)
|
void AssetsLibraryView::modelAttached(Model *model)
|
||||||
@@ -82,17 +93,20 @@ void AssetsLibraryView::modelAttached(Model *model)
|
|||||||
|
|
||||||
m_widget->clearSearchFilter();
|
m_widget->clearSearchFilter();
|
||||||
|
|
||||||
setResourcePath(DocumentManager::currentResourcePath().toFileInfo().absoluteFilePath());
|
setResourcePath(DocumentManager::currentResourcePath().toFSPathString());
|
||||||
|
|
||||||
|
m_3dImportsSyncTimer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsLibraryView::modelAboutToBeDetached(Model *model)
|
void AssetsLibraryView::modelAboutToBeDetached(Model *model)
|
||||||
{
|
{
|
||||||
AbstractView::modelAboutToBeDetached(model);
|
AbstractView::modelAboutToBeDetached(model);
|
||||||
|
|
||||||
|
m_3dImportsSyncTimer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsLibraryView::setResourcePath(const QString &resourcePath)
|
void AssetsLibraryView::setResourcePath(const QString &resourcePath)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (resourcePath == m_lastResourcePath)
|
if (resourcePath == m_lastResourcePath)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -100,6 +114,7 @@ void AssetsLibraryView::setResourcePath(const QString &resourcePath)
|
|||||||
|
|
||||||
if (!m_widget) {
|
if (!m_widget) {
|
||||||
m_widget = Utils::makeUniqueObjectPtr<AssetsLibraryWidget>(
|
m_widget = Utils::makeUniqueObjectPtr<AssetsLibraryWidget>(
|
||||||
|
*imageCacheData()->mainImageCache,
|
||||||
imageCacheData()->asynchronousFontImageCache,
|
imageCacheData()->asynchronousFontImageCache,
|
||||||
imageCacheData()->synchronousFontImageCache,
|
imageCacheData()->synchronousFontImageCache,
|
||||||
this);
|
this);
|
||||||
@@ -108,6 +123,68 @@ void AssetsLibraryView::setResourcePath(const QString &resourcePath)
|
|||||||
m_widget->setResourcePath(resourcePath);
|
m_widget->setResourcePath(resourcePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QHash<QString, Utils::FilePath> AssetsLibraryView::collectFiles(const Utils::FilePath &dirPath,
|
||||||
|
const QString &suffix)
|
||||||
|
{
|
||||||
|
if (dirPath.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QHash<QString, Utils::FilePath> files;
|
||||||
|
|
||||||
|
Utils::FilePaths entryList = dirPath.dirEntries(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
for (const Utils::FilePath &entry : entryList) {
|
||||||
|
if (entry.isDir()) {
|
||||||
|
files.insert(collectFiles(entry.absoluteFilePath(), suffix));
|
||||||
|
} else if (entry.suffix() == suffix) {
|
||||||
|
QString baseName = entry.baseName();
|
||||||
|
files.insert(baseName, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetsLibraryView::sync3dImports()
|
||||||
|
{
|
||||||
|
if (!model())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: Once project storage supports notifications for new and removed types,
|
||||||
|
// sync3dImports() should be called in that case as well.
|
||||||
|
// Also, custom notification "asset_import_finished" should not be necessary in that case.
|
||||||
|
|
||||||
|
// Sync generated 3d imports to .q3d files in project content
|
||||||
|
const GeneratedComponentUtils &compUtils
|
||||||
|
= QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
|
||||||
|
auto projPath = Utils::FilePath::fromString(externalDependencies().currentProjectDirPath());
|
||||||
|
|
||||||
|
const QList<Utils::FilePath> qmlFiles = compUtils.imported3dComponents();
|
||||||
|
|
||||||
|
Utils::FilePath resPath = DocumentManager::currentResourcePath();
|
||||||
|
QHash<QString, Utils::FilePath> files = collectFiles(resPath, "q3d");
|
||||||
|
|
||||||
|
const QString pathTemplate("/%1.q3d");
|
||||||
|
|
||||||
|
for (const Utils::FilePath &qmlFile : qmlFiles) {
|
||||||
|
const QString fileStr = qmlFile.baseName();
|
||||||
|
if (files.contains(fileStr)) {
|
||||||
|
files.remove(fileStr);
|
||||||
|
} else {
|
||||||
|
Utils::FilePath targetPath = ModelNodeOperations::getImported3dDefaultDirectory();
|
||||||
|
if (!targetPath.isAbsolutePath() || !targetPath.exists())
|
||||||
|
targetPath = resPath;
|
||||||
|
Utils::FilePath newFile = targetPath.pathAppended(pathTemplate.arg(fileStr));
|
||||||
|
QByteArray data;
|
||||||
|
data.append(qmlFile.relativePathFrom(projPath).toFSPathString().toLatin1());
|
||||||
|
newFile.writeFileContents(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove .q3d files that do not match any imported 3d
|
||||||
|
for (const Utils::FilePath &f : std::as_const(files))
|
||||||
|
f.removeFile();
|
||||||
|
}
|
||||||
|
|
||||||
AssetsLibraryView::ImageCacheData *AssetsLibraryView::imageCacheData()
|
AssetsLibraryView::ImageCacheData *AssetsLibraryView::imageCacheData()
|
||||||
{
|
{
|
||||||
std::call_once(imageCacheFlag,
|
std::call_once(imageCacheFlag,
|
||||||
|
@@ -5,9 +5,11 @@
|
|||||||
|
|
||||||
#include "abstractview.h"
|
#include "abstractview.h"
|
||||||
|
|
||||||
|
#include <utils/filepath.h>
|
||||||
#include <utils/uniqueobjectptr.h>
|
#include <utils/uniqueobjectptr.h>
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
@@ -21,7 +23,8 @@ class AssetsLibraryView : public AbstractView
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AssetsLibraryView(ExternalDependenciesInterface &externalDependencies);
|
AssetsLibraryView(AsynchronousImageCache &imageCache,
|
||||||
|
ExternalDependenciesInterface &externalDependencies);
|
||||||
~AssetsLibraryView() override;
|
~AssetsLibraryView() override;
|
||||||
|
|
||||||
bool hasWidget() const override;
|
bool hasWidget() const override;
|
||||||
@@ -30,7 +33,6 @@ public:
|
|||||||
// AbstractView
|
// AbstractView
|
||||||
void modelAttached(Model *model) override;
|
void modelAttached(Model *model) override;
|
||||||
void modelAboutToBeDetached(Model *model) override;
|
void modelAboutToBeDetached(Model *model) override;
|
||||||
|
|
||||||
void setResourcePath(const QString &resourcePath);
|
void setResourcePath(const QString &resourcePath);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -39,11 +41,15 @@ private:
|
|||||||
|
|
||||||
void customNotification(const AbstractView *view, const QString &identifier,
|
void customNotification(const AbstractView *view, const QString &identifier,
|
||||||
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
|
const QList<ModelNode> &nodeList, const QList<QVariant> &data) override;
|
||||||
|
QHash<QString, Utils::FilePath> collectFiles(const Utils::FilePath &dirPath,
|
||||||
|
const QString &suffix);
|
||||||
|
void sync3dImports();
|
||||||
|
|
||||||
std::once_flag imageCacheFlag;
|
std::once_flag imageCacheFlag;
|
||||||
std::unique_ptr<ImageCacheData> m_imageCacheData;
|
std::unique_ptr<ImageCacheData> m_imageCacheData;
|
||||||
Utils::UniqueObjectPtr<AssetsLibraryWidget> m_widget;
|
Utils::UniqueObjectPtr<AssetsLibraryWidget> m_widget;
|
||||||
QString m_lastResourcePath;
|
QString m_lastResourcePath;
|
||||||
|
QTimer m_3dImportsSyncTimer;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include "assetslibraryview.h"
|
#include "assetslibraryview.h"
|
||||||
#include <qmldesignertr.h>
|
#include <qmldesignertr.h>
|
||||||
|
|
||||||
|
#include <asynchronousimagecache.h>
|
||||||
#include <createtexture.h>
|
#include <createtexture.h>
|
||||||
#include <designeractionmanager.h>
|
#include <designeractionmanager.h>
|
||||||
#include <designermcumanager.h>
|
#include <designermcumanager.h>
|
||||||
@@ -97,10 +98,12 @@ bool AssetsLibraryWidget::eventFilter(QObject *obj, QEvent *event)
|
|||||||
return QObject::eventFilter(obj, event);
|
return QObject::eventFilter(obj, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFontImageCache,
|
AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &mainImageCache,
|
||||||
|
AsynchronousImageCache &asynchronousFontImageCache,
|
||||||
SynchronousImageCache &synchronousFontImageCache,
|
SynchronousImageCache &synchronousFontImageCache,
|
||||||
AssetsLibraryView *view)
|
AssetsLibraryView *view)
|
||||||
: m_itemIconSize{24, 24}
|
: m_itemIconSize{24, 24}
|
||||||
|
, m_mainImageCache{mainImageCache}
|
||||||
, m_fontImageCache{synchronousFontImageCache}
|
, m_fontImageCache{synchronousFontImageCache}
|
||||||
, m_assetsIconProvider{new AssetsLibraryIconProvider(synchronousFontImageCache)}
|
, m_assetsIconProvider{new AssetsLibraryIconProvider(synchronousFontImageCache)}
|
||||||
, m_assetsModel{new AssetsLibraryModel(this)}
|
, m_assetsModel{new AssetsLibraryModel(this)}
|
||||||
@@ -110,6 +113,44 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon
|
|||||||
setWindowTitle(Tr::tr("Assets Library", "Title of assets library widget"));
|
setWindowTitle(Tr::tr("Assets Library", "Title of assets library widget"));
|
||||||
setMinimumWidth(250);
|
setMinimumWidth(250);
|
||||||
|
|
||||||
|
connect(m_assetsIconProvider, &AssetsLibraryIconProvider::asyncAssetPreviewRequested,
|
||||||
|
this, [this](const QString &assetId, const QString &assetFile) {
|
||||||
|
Asset asset{assetFile};
|
||||||
|
if (!asset.isImported3D())
|
||||||
|
return;
|
||||||
|
|
||||||
|
Utils::FilePath fullPath = QmlDesignerPlugin::instance()->documentManager()
|
||||||
|
.generatedComponentUtils().getImported3dQml(assetFile);
|
||||||
|
|
||||||
|
if (!fullPath.exists())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_mainImageCache.requestImage(
|
||||||
|
Utils::PathString{fullPath.toFSPathString()},
|
||||||
|
[this, assetId](const QImage &image) {
|
||||||
|
QMetaObject::invokeMethod(this, [this, assetId, image] {
|
||||||
|
updateAssetPreview(assetId, QPixmap::fromImage(image), "q3d");
|
||||||
|
}, Qt::QueuedConnection);
|
||||||
|
},
|
||||||
|
[assetFile](ImageCache::AbortReason abortReason) {
|
||||||
|
if (abortReason == ImageCache::AbortReason::Abort) {
|
||||||
|
qWarning() << QLatin1String(
|
||||||
|
"AssetsLibraryIconProvider::asyncAssetPreviewRequested(): preview generation "
|
||||||
|
"failed for path %1, reason: Abort").arg(assetFile);
|
||||||
|
} else if (abortReason == ImageCache::AbortReason::Failed) {
|
||||||
|
qWarning() << QLatin1String(
|
||||||
|
"AssetsLibraryIconProvider::asyncAssetPreviewRequested(): preview generation "
|
||||||
|
"failed for path %1, reason: Failed").arg(assetFile);
|
||||||
|
} else if (abortReason == ImageCache::AbortReason::NoEntry) {
|
||||||
|
qWarning() << QLatin1String(
|
||||||
|
"AssetsLibraryIconProvider::asyncAssetPreviewRequested(): preview generation "
|
||||||
|
"failed for path %1, reason: NoEntry").arg(assetFile);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libIcon",
|
||||||
|
ImageCache::LibraryIconAuxiliaryData{true});
|
||||||
|
});
|
||||||
|
|
||||||
m_assetsWidget->quickWidget()->installEventFilter(this);
|
m_assetsWidget->quickWidget()->installEventFilter(this);
|
||||||
|
|
||||||
m_fontPreviewTooltipBackend = std::make_unique<PreviewTooltipBackend>(asynchronousFontImageCache);
|
m_fontPreviewTooltipBackend = std::make_unique<PreviewTooltipBackend>(asynchronousFontImageCache);
|
||||||
@@ -135,8 +176,8 @@ AssetsLibraryWidget::AssetsLibraryWidget(AsynchronousImageCache &asynchronousFon
|
|||||||
connect(m_assetsModel, &AssetsLibraryModel::fileChanged,
|
connect(m_assetsModel, &AssetsLibraryModel::fileChanged,
|
||||||
QmlDesignerPlugin::instance(), &QmlDesignerPlugin::assetChanged);
|
QmlDesignerPlugin::instance(), &QmlDesignerPlugin::assetChanged);
|
||||||
|
|
||||||
connect(m_assetsModel, &AssetsLibraryModel::effectsDeleted,
|
connect(m_assetsModel, &AssetsLibraryModel::generatedAssetsDeleted,
|
||||||
this, &AssetsLibraryWidget::handleDeleteEffects);
|
this, &AssetsLibraryWidget::handleDeletedGeneratedAssets);
|
||||||
|
|
||||||
auto layout = new QVBoxLayout(this);
|
auto layout = new QVBoxLayout(this);
|
||||||
layout->setContentsMargins({});
|
layout->setContentsMargins({});
|
||||||
@@ -292,44 +333,64 @@ void AssetsLibraryWidget::setHasSceneEnv(bool b)
|
|||||||
emit hasSceneEnvChanged();
|
emit hasSceneEnvChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList &effectNames)
|
void AssetsLibraryWidget::handleDeletedGeneratedAssets(const QHash<QString, Utils::FilePath> &assetData)
|
||||||
{
|
{
|
||||||
#ifdef QDS_USE_PROJECTSTORAGE
|
// assetData key: full type name including import, value: import dir
|
||||||
// That code has to rewritten with modules. Seem try to find all effects nodes.
|
|
||||||
#else
|
// This method removes all nodes of the deleted type (assetData.keys())
|
||||||
|
// and removes the import statement for that type
|
||||||
|
|
||||||
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
|
DesignDocument *document = QmlDesignerPlugin::instance()->currentDesignDocument();
|
||||||
if (!document)
|
if (!document)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool clearStacks = false;
|
bool clearStacks = false;
|
||||||
|
|
||||||
// Remove usages of deleted effects from the current document
|
const Imports imports = m_assetsView->model()->imports();
|
||||||
|
const GeneratedComponentUtils &compUtils = QmlDesignerPlugin::instance()->documentManager()
|
||||||
|
.generatedComponentUtils();
|
||||||
|
QString effectPrefix = compUtils.composedEffectsTypePrefix();
|
||||||
|
QStringList effectNames;
|
||||||
|
|
||||||
|
// Remove usages of deleted assets from the current document
|
||||||
m_assetsView->executeInTransaction(__FUNCTION__, [&]() {
|
m_assetsView->executeInTransaction(__FUNCTION__, [&]() {
|
||||||
QList<ModelNode> allNodes = m_assetsView->allModelNodes();
|
QList<ModelNode> allNodes = m_assetsView->allModelNodes();
|
||||||
const QString typeTemplate = "%1.%2.%2";
|
|
||||||
const QString importUrlTemplate = "%1.%2";
|
QList<Import> removedImports;
|
||||||
const Imports imports = m_assetsView->model()->imports();
|
|
||||||
Imports removedImports;
|
const QStringList assetTypes = assetData.keys();
|
||||||
const QString typePrefix = QmlDesignerPlugin::instance()->documentManager()
|
for (const QString &assetType : assetTypes) {
|
||||||
.generatedComponentUtils().composedEffectsTypePrefix();
|
QString removedImportUrl;
|
||||||
for (const QString &effectName : effectNames) {
|
int idx = assetType.lastIndexOf('.');
|
||||||
if (effectName.isEmpty())
|
if (idx >= 0) {
|
||||||
continue;
|
if (assetType.startsWith(effectPrefix))
|
||||||
const TypeName type = typeTemplate.arg(typePrefix, effectName).toUtf8();
|
effectNames.append(assetType.sliced(idx + 1));
|
||||||
|
removedImportUrl = assetType.first(idx);
|
||||||
|
#ifdef QDS_USE_PROJECTSTORAGE
|
||||||
|
auto module = m_assetsView->model()->module(removedImportUrl.toUtf8(),
|
||||||
|
Storage::ModuleKind::QmlLibrary);
|
||||||
|
auto metaInfo = m_assetsView->model()->metaInfo(module, assetType.sliced(idx + 1).toUtf8());
|
||||||
|
for (ModelNode &node : allNodes) {
|
||||||
|
if (node.metaInfo() == metaInfo) {
|
||||||
|
#else
|
||||||
|
TypeName type = assetType.toUtf8();
|
||||||
for (ModelNode &node : allNodes) {
|
for (ModelNode &node : allNodes) {
|
||||||
if (node.metaInfo().typeName() == type) {
|
if (node.metaInfo().typeName() == type) {
|
||||||
|
#endif
|
||||||
clearStacks = true;
|
clearStacks = true;
|
||||||
node.destroy();
|
node.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!removedImportUrl.isEmpty()) {
|
||||||
const QString importPath = importUrlTemplate.arg(typePrefix, effectName);
|
Import removedImport = Utils::findOrDefault(imports,
|
||||||
Import removedImport = Utils::findOrDefault(imports, [&importPath](const Import &import) {
|
[&removedImportUrl](const Import &import) {
|
||||||
return import.url() == importPath;
|
return import.url() == removedImportUrl;
|
||||||
});
|
});
|
||||||
if (!removedImport.isEmpty())
|
if (!removedImport.isEmpty())
|
||||||
removedImports.append(removedImport);
|
removedImports.append(removedImport);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!removedImports.isEmpty()) {
|
if (!removedImports.isEmpty()) {
|
||||||
m_assetsView->model()->changeImports({}, removedImports);
|
m_assetsView->model()->changeImports({}, removedImports);
|
||||||
@@ -338,22 +399,20 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList
|
|||||||
});
|
});
|
||||||
|
|
||||||
// The size check here is to weed out cases where project path somehow resolves
|
// The size check here is to weed out cases where project path somehow resolves
|
||||||
// to just slash. Shortest legal currentProjectDirPath() would be "/a/".
|
// to just slash or drive + slash. (Shortest legal currentProjectDirPath() would be "/a/")
|
||||||
if (m_assetsModel->currentProjectDirPath().size() < 3)
|
if (m_assetsModel->currentProjectDirPath().size() < 4)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Utils::FilePath effectsDir = ModelNodeOperations::getEffectsImportDirectory();
|
// Delete the asset modules
|
||||||
|
for (const Utils::FilePath &dir : assetData) {
|
||||||
// Delete the effect modules
|
if (dir.exists() && dir.toFSPathString().startsWith(m_assetsModel->currentProjectDirPath())) {
|
||||||
for (const QString &effectName : effectNames) {
|
|
||||||
Utils::FilePath eDir = effectsDir.pathAppended(effectName);
|
|
||||||
if (eDir.exists() && eDir.toString().startsWith(m_assetsModel->currentProjectDirPath())) {
|
|
||||||
QString error;
|
QString error;
|
||||||
eDir.removeRecursively(&error);
|
dir.removeRecursively(&error);
|
||||||
|
|
||||||
if (!error.isEmpty()) {
|
if (!error.isEmpty()) {
|
||||||
QMessageBox::warning(Core::ICore::dialogParent(),
|
QMessageBox::warning(Core::ICore::dialogParent(),
|
||||||
Tr::tr("Failed to Delete Effect Resources"),
|
Tr::tr("Failed to Delete Asset Resources"),
|
||||||
Tr::tr("Could not delete \"%1\".").arg(eDir.toString()));
|
Tr::tr("Could not delete \"%1\".").arg(dir.toFSPathString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,7 +423,16 @@ void AssetsLibraryWidget::handleDeleteEffects([[maybe_unused]] const QStringList
|
|||||||
document->clearUndoRedoStacks();
|
document->clearUndoRedoStacks();
|
||||||
|
|
||||||
m_assetsView->emitCustomNotification("effectcomposer_effects_deleted", {}, {effectNames});
|
m_assetsView->emitCustomNotification("effectcomposer_effects_deleted", {}, {effectNames});
|
||||||
#endif
|
m_assetsView->emitCustomNotification("assets_deleted");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetsLibraryWidget::updateAssetPreview(const QString &id, const QPixmap &pixmap,
|
||||||
|
const QString &suffix)
|
||||||
|
{
|
||||||
|
const QString thumb = m_assetsIconProvider->setPixmap(id, pixmap, suffix);
|
||||||
|
|
||||||
|
if (!thumb.isEmpty())
|
||||||
|
emit m_assetsModel->fileChanged(thumb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsLibraryWidget::invalidateThumbnail(const QString &id)
|
void AssetsLibraryWidget::invalidateThumbnail(const QString &id)
|
||||||
@@ -388,6 +456,11 @@ bool AssetsLibraryWidget::assetIsImageOrTexture(const QString &id)
|
|||||||
return Asset(id).isValidTextureSource();
|
return Asset(id).isValidTextureSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AssetsLibraryWidget::assetIsImported3d(const QString &id)
|
||||||
|
{
|
||||||
|
return Asset(id).isImported3D();
|
||||||
|
}
|
||||||
|
|
||||||
// needed to deal with "Object 0xXXXX destroyed while one of its QML signal handlers is in progress..." error which would lead to a crash
|
// needed to deal with "Object 0xXXXX destroyed while one of its QML signal handlers is in progress..." error which would lead to a crash
|
||||||
void AssetsLibraryWidget::invokeAssetsDrop(const QList<QUrl> &urls, const QString &targetDir)
|
void AssetsLibraryWidget::invokeAssetsDrop(const QList<QUrl> &urls, const QString &targetDir)
|
||||||
{
|
{
|
||||||
@@ -615,6 +688,9 @@ QPair<QString, QByteArray> AssetsLibraryWidget::getAssetTypeAndData(const QStrin
|
|||||||
} else if (asset.isEffect()) {
|
} else if (asset.isEffect()) {
|
||||||
// Data: Effect Composer format (suffix)
|
// Data: Effect Composer format (suffix)
|
||||||
return {Constants::MIME_TYPE_ASSET_EFFECT, asset.suffix().toUtf8()};
|
return {Constants::MIME_TYPE_ASSET_EFFECT, asset.suffix().toUtf8()};
|
||||||
|
} else if (asset.isImported3D()) {
|
||||||
|
// Data: Imported 3D component (suffix)
|
||||||
|
return {Constants::MIME_TYPE_ASSET_IMPORTED3D, asset.suffix().toUtf8()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
|
@@ -51,7 +51,8 @@ class AssetsLibraryWidget : public QFrame
|
|||||||
Q_PROPERTY(bool isDragging MEMBER m_isDragging NOTIFY isDraggingChanged)
|
Q_PROPERTY(bool isDragging MEMBER m_isDragging NOTIFY isDraggingChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AssetsLibraryWidget(AsynchronousImageCache &asynchronousFontImageCache,
|
AssetsLibraryWidget(AsynchronousImageCache &mainImageCache,
|
||||||
|
AsynchronousImageCache &asynchronousFontImageCache,
|
||||||
SynchronousImageCache &synchronousFontImageCache, AssetsLibraryView *view);
|
SynchronousImageCache &synchronousFontImageCache, AssetsLibraryView *view);
|
||||||
~AssetsLibraryWidget();
|
~AssetsLibraryWidget();
|
||||||
|
|
||||||
@@ -88,6 +89,7 @@ public:
|
|||||||
Q_INVOKABLE QSize imageSize(const QString &id);
|
Q_INVOKABLE QSize imageSize(const QString &id);
|
||||||
Q_INVOKABLE QString assetFileSize(const QString &id);
|
Q_INVOKABLE QString assetFileSize(const QString &id);
|
||||||
Q_INVOKABLE bool assetIsImageOrTexture(const QString &id);
|
Q_INVOKABLE bool assetIsImageOrTexture(const QString &id);
|
||||||
|
Q_INVOKABLE bool assetIsImported3d(const QString &id);
|
||||||
Q_INVOKABLE void addTextures(const QStringList &filePaths);
|
Q_INVOKABLE void addTextures(const QStringList &filePaths);
|
||||||
Q_INVOKABLE void addLightProbe(const QString &filePaths);
|
Q_INVOKABLE void addLightProbe(const QString &filePaths);
|
||||||
Q_INVOKABLE void updateContextMenuActionsEnableState();
|
Q_INVOKABLE void updateContextMenuActionsEnableState();
|
||||||
@@ -130,10 +132,12 @@ private:
|
|||||||
void setHasSceneEnv(bool b);
|
void setHasSceneEnv(bool b);
|
||||||
void setCanCreateEffects(bool newVal);
|
void setCanCreateEffects(bool newVal);
|
||||||
|
|
||||||
void handleDeleteEffects(const QStringList &effectNames);
|
void handleDeletedGeneratedAssets(const QHash<QString, Utils::FilePath> &assetData);
|
||||||
|
void updateAssetPreview(const QString &id, const QPixmap &pixmap, const QString &suffix);
|
||||||
|
|
||||||
QSize m_itemIconSize;
|
QSize m_itemIconSize;
|
||||||
|
|
||||||
|
AsynchronousImageCache &m_mainImageCache;
|
||||||
SynchronousImageCache &m_fontImageCache;
|
SynchronousImageCache &m_fontImageCache;
|
||||||
|
|
||||||
AssetsLibraryIconProvider *m_assetsIconProvider = nullptr;
|
AssetsLibraryIconProvider *m_assetsIconProvider = nullptr;
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 471 B |
Binary file not shown.
After Width: | Height: | Size: 838 B |
@@ -1879,6 +1879,13 @@ Utils::FilePath getImagesDefaultDirectory()
|
|||||||
QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString()));
|
QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FilePath getImported3dDefaultDirectory()
|
||||||
|
{
|
||||||
|
return Utils::FilePath::fromString(getAssetDefaultDirectory(
|
||||||
|
"3d",
|
||||||
|
QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString()));
|
||||||
|
}
|
||||||
|
|
||||||
void jumpToCode(const ModelNode &modelNode)
|
void jumpToCode(const ModelNode &modelNode)
|
||||||
{
|
{
|
||||||
QmlDesignerPlugin::instance()->viewManager().jumpToCodeInTextEditor(modelNode);
|
QmlDesignerPlugin::instance()->viewManager().jumpToCodeInTextEditor(modelNode);
|
||||||
@@ -2009,6 +2016,36 @@ ModelNode handleItemLibraryEffectDrop(const QString &effectPath, const ModelNode
|
|||||||
return newModelNode;
|
return newModelNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelNode handleImported3dAssetDrop(const QString &assetPath, const ModelNode &targetNode,
|
||||||
|
const QVector3D &position)
|
||||||
|
{
|
||||||
|
AbstractView *view = targetNode.view();
|
||||||
|
QTC_ASSERT(view, return {});
|
||||||
|
QTC_ASSERT(targetNode.isValid(), return {});
|
||||||
|
|
||||||
|
ModelNode newModelNode;
|
||||||
|
|
||||||
|
const GeneratedComponentUtils &compUtils = QmlDesignerPlugin::instance()->documentManager()
|
||||||
|
.generatedComponentUtils();
|
||||||
|
|
||||||
|
Utils::FilePath qmlFile = compUtils.getImported3dQml(assetPath);
|
||||||
|
if (qmlFile.exists()) {
|
||||||
|
TypeName qmlType = qmlFile.baseName().toUtf8();
|
||||||
|
QString importName = compUtils.getImported3dImportName(qmlFile);
|
||||||
|
if (!importName.isEmpty() && !qmlType.isEmpty())
|
||||||
|
newModelNode = QmlVisualNode::createQml3DNode(view, qmlType, targetNode, importName, position);
|
||||||
|
} else {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setText(Tr::tr("Asset %1 is not complete.").arg(qmlFile.baseName()));
|
||||||
|
msgBox.setInformativeText(Tr::tr("Please reimport the asset."));
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setIcon(QMessageBox::Information);
|
||||||
|
msgBox.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
return newModelNode;
|
||||||
|
}
|
||||||
|
|
||||||
void handleTextureDrop(const QMimeData *mimeData, const ModelNode &targetModelNode)
|
void handleTextureDrop(const QMimeData *mimeData, const ModelNode &targetModelNode)
|
||||||
{
|
{
|
||||||
AbstractView *view = targetModelNode.view();
|
AbstractView *view = targetModelNode.view();
|
||||||
|
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include <utils/filepath.h>
|
#include <utils/filepath.h>
|
||||||
|
|
||||||
|
#include <QVector3D>
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
class AddFilesResult
|
class AddFilesResult
|
||||||
@@ -138,10 +140,14 @@ bool validateEffect(const QString &effectPath);
|
|||||||
bool isEffectComposerActivated();
|
bool isEffectComposerActivated();
|
||||||
|
|
||||||
QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getImagesDefaultDirectory();
|
QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getImagesDefaultDirectory();
|
||||||
|
Utils::FilePath getImported3dDefaultDirectory();
|
||||||
|
|
||||||
//Item Library and Assets related drop operations
|
//Item Library and Assets related drop operations
|
||||||
QMLDESIGNERCOMPONENTS_EXPORT ModelNode handleItemLibraryEffectDrop(const QString &effectPath,
|
QMLDESIGNERCOMPONENTS_EXPORT ModelNode handleItemLibraryEffectDrop(const QString &effectPath,
|
||||||
const ModelNode &targetNode);
|
const ModelNode &targetNode);
|
||||||
|
ModelNode handleImported3dAssetDrop(const QString &assetPath,
|
||||||
|
const ModelNode &targetNode,
|
||||||
|
const QVector3D &position = {});
|
||||||
void handleTextureDrop(const QMimeData *mimeData, const ModelNode &targetModelNode);
|
void handleTextureDrop(const QMimeData *mimeData, const ModelNode &targetModelNode);
|
||||||
void handleMaterialDrop(const QMimeData *mimeData, const ModelNode &targetNode);
|
void handleMaterialDrop(const QMimeData *mimeData, const ModelNode &targetNode);
|
||||||
ModelNode handleItemLibraryImageDrop(const QString &imagePath,
|
ModelNode handleItemLibraryImageDrop(const QString &imagePath,
|
||||||
|
@@ -68,7 +68,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
, formEditorView{externalDependencies}
|
, formEditorView{externalDependencies}
|
||||||
, textEditorView{externalDependencies}
|
, textEditorView{externalDependencies}
|
||||||
, assetsLibraryView{externalDependencies}
|
, assetsLibraryView{imageCache, externalDependencies}
|
||||||
, itemLibraryView(imageCache, externalDependencies)
|
, itemLibraryView(imageCache, externalDependencies)
|
||||||
, navigatorView{externalDependencies}
|
, navigatorView{externalDependencies}
|
||||||
, propertyEditorView(imageCache, externalDependencies)
|
, propertyEditorView(imageCache, externalDependencies)
|
||||||
|
@@ -46,5 +46,7 @@
|
|||||||
<file>images/align_view_on@2x.png</file>
|
<file>images/align_view_on@2x.png</file>
|
||||||
<file>images/color_palette.png</file>
|
<file>images/color_palette.png</file>
|
||||||
<file>images/color_palette@2x.png</file>
|
<file>images/color_palette@2x.png</file>
|
||||||
|
<file>images/item-3D_model-icon.png</file>
|
||||||
|
<file>images/item-3D_model-icon@2x.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@@ -35,6 +35,8 @@
|
|||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/messagebox.h>
|
#include <coreplugin/messagebox.h>
|
||||||
|
|
||||||
|
#include <qmldesignerutils/asset.h>
|
||||||
|
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
#include <projectexplorer/kit.h>
|
#include <projectexplorer/kit.h>
|
||||||
|
|
||||||
@@ -305,7 +307,11 @@ void Edit3DView::modelAttached(Model *model)
|
|||||||
if (QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit()))
|
if (QtSupport::QtVersion *qtVer = QtSupport::QtKitAspect::qtVersion(target->kit()))
|
||||||
m_isBakingLightsSupported = qtVer->qtVersion() >= QVersionNumber(6, 5, 0);
|
m_isBakingLightsSupported = qtVer->qtVersion() >= QVersionNumber(6, 5, 0);
|
||||||
}
|
}
|
||||||
#ifndef QDS_USE_PROJECTSTORAGE
|
#ifdef QDS_USE_PROJECTSTORAGE
|
||||||
|
// TODO: Handle actual entries changed signal/notification once it is available.
|
||||||
|
// Until then, we simply get what entries are available at model attach time.
|
||||||
|
onEntriesChanged();
|
||||||
|
#else
|
||||||
connect(model->metaInfo().itemLibraryInfo(),
|
connect(model->metaInfo().itemLibraryInfo(),
|
||||||
&ItemLibraryInfo::entriesChanged,
|
&ItemLibraryInfo::entriesChanged,
|
||||||
this,
|
this,
|
||||||
@@ -333,15 +339,14 @@ void Edit3DView::handleEntriesChanged()
|
|||||||
enum ItemLibraryEntryKeys : int { // used to maintain order
|
enum ItemLibraryEntryKeys : int { // used to maintain order
|
||||||
EK_cameras,
|
EK_cameras,
|
||||||
EK_lights,
|
EK_lights,
|
||||||
EK_primitives,
|
EK_primitives
|
||||||
EK_importedModels
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QMap<ItemLibraryEntryKeys, ItemLibraryDetails> entriesMap{
|
QMap<ItemLibraryEntryKeys, ItemLibraryDetails> entriesMap{
|
||||||
{EK_cameras, {tr("Cameras"), contextIcon(DesignerIcons::CameraIcon)}},
|
{EK_cameras, {tr("Cameras"), contextIcon(DesignerIcons::CameraIcon)}},
|
||||||
{EK_lights, {tr("Lights"), contextIcon(DesignerIcons::LightIcon)}},
|
{EK_lights, {tr("Lights"), contextIcon(DesignerIcons::LightIcon)}},
|
||||||
{EK_primitives, {tr("Primitives"), contextIcon(DesignerIcons::PrimitivesIcon)}},
|
{EK_primitives, {tr("Primitives"), contextIcon(DesignerIcons::PrimitivesIcon)}}
|
||||||
{EK_importedModels, {tr("Imported Models"), contextIcon(DesignerIcons::ImportedModelsIcon)}}};
|
};
|
||||||
|
|
||||||
#ifdef QDS_USE_PROJECTSTORAGE
|
#ifdef QDS_USE_PROJECTSTORAGE
|
||||||
auto append = [&](const NodeMetaInfo &metaInfo, ItemLibraryEntryKeys key) {
|
auto append = [&](const NodeMetaInfo &metaInfo, ItemLibraryEntryKeys key) {
|
||||||
@@ -356,16 +361,6 @@ void Edit3DView::handleEntriesChanged()
|
|||||||
append(model()->qtQuick3DPointLightMetaInfo(), EK_lights);
|
append(model()->qtQuick3DPointLightMetaInfo(), EK_lights);
|
||||||
append(model()->qtQuick3DOrthographicCameraMetaInfo(), EK_cameras);
|
append(model()->qtQuick3DOrthographicCameraMetaInfo(), EK_cameras);
|
||||||
append(model()->qtQuick3DPerspectiveCameraMetaInfo(), EK_cameras);
|
append(model()->qtQuick3DPerspectiveCameraMetaInfo(), EK_cameras);
|
||||||
|
|
||||||
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);
|
|
||||||
#else
|
#else
|
||||||
const QList<ItemLibraryEntry> itemLibEntries = model()->metaInfo().itemLibraryInfo()->entries();
|
const QList<ItemLibraryEntry> itemLibEntries = model()->metaInfo().itemLibraryInfo()->entries();
|
||||||
for (const ItemLibraryEntry &entry : itemLibEntries) {
|
for (const ItemLibraryEntry &entry : itemLibEntries) {
|
||||||
@@ -379,13 +374,6 @@ void Edit3DView::handleEntriesChanged()
|
|||||||
} else if (entry.typeName() == "QtQuick3D.OrthographicCamera"
|
} else if (entry.typeName() == "QtQuick3D.OrthographicCamera"
|
||||||
|| entry.typeName() == "QtQuick3D.PerspectiveCamera") {
|
|| entry.typeName() == "QtQuick3D.PerspectiveCamera") {
|
||||||
entryKey = EK_cameras;
|
entryKey = EK_cameras;
|
||||||
} else if (entry.typeName().startsWith(QmlDesignerPlugin::instance()
|
|
||||||
->documentManager()
|
|
||||||
.generatedComponentUtils()
|
|
||||||
.import3dTypePrefix()
|
|
||||||
.toUtf8())
|
|
||||||
&& NodeHints::fromItemLibraryEntry(entry, model()).canBeDroppedInView3D()) {
|
|
||||||
entryKey = EK_importedModels;
|
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -464,6 +452,8 @@ void Edit3DView::customNotification([[maybe_unused]] const AbstractView *view,
|
|||||||
self->m_nodeAtPosReqType = NodeAtPosReqType::MainScenePick;
|
self->m_nodeAtPosReqType = NodeAtPosReqType::MainScenePick;
|
||||||
self->m_pickView3dNode = self->modelNodeForInternalId(qint32(data[1].toInt()));
|
self->m_pickView3dNode = self->modelNodeForInternalId(qint32(data[1].toInt()));
|
||||||
});
|
});
|
||||||
|
} else if (identifier == "asset_import_finished" || identifier == "assets_deleted") {
|
||||||
|
handleEntriesChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -511,9 +501,22 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos
|
|||||||
emitCustomNotification("apply_texture_to_model3D", {modelNode, m_droppedModelNode});
|
emitCustomNotification("apply_texture_to_model3D", {modelNode, m_droppedModelNode});
|
||||||
} else if (m_nodeAtPosReqType == NodeAtPosReqType::AssetDrop) {
|
} else if (m_nodeAtPosReqType == NodeAtPosReqType::AssetDrop) {
|
||||||
bool isModel = modelNode.metaInfo().isQtQuick3DModel();
|
bool isModel = modelNode.metaInfo().isQtQuick3DModel();
|
||||||
if (!m_droppedFile.isEmpty() && isModel) {
|
if (!m_droppedTexture.isEmpty() && isModel) {
|
||||||
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser");
|
QmlDesignerPlugin::instance()->mainWidget()->showDockWidget("MaterialBrowser");
|
||||||
emitCustomNotification("apply_asset_to_model3D", {modelNode}, {m_droppedFile}); // To MaterialBrowserView
|
emitCustomNotification("apply_asset_to_model3D", {modelNode}, {m_droppedTexture}); // To MaterialBrowserView
|
||||||
|
} else if (!m_dropped3dImports.isEmpty()) {
|
||||||
|
ModelNode sceneNode = Utils3D::active3DSceneNode(this);
|
||||||
|
if (!sceneNode.isValid())
|
||||||
|
sceneNode = rootModelNode();
|
||||||
|
ModelNode createdNode;
|
||||||
|
executeInTransaction(__FUNCTION__, [&] {
|
||||||
|
for (const QString &asset : std::as_const(m_dropped3dImports)) {
|
||||||
|
createdNode = ModelNodeOperations::handleImported3dAssetDrop(
|
||||||
|
asset, sceneNode, pos3d);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (createdNode.isValid())
|
||||||
|
setSelectedModelNode(createdNode);
|
||||||
}
|
}
|
||||||
} else if (m_nodeAtPosReqType == NodeAtPosReqType::MainScenePick) {
|
} else if (m_nodeAtPosReqType == NodeAtPosReqType::MainScenePick) {
|
||||||
if (modelNode.isValid())
|
if (modelNode.isValid())
|
||||||
@@ -524,7 +527,8 @@ void Edit3DView::nodeAtPosReady(const ModelNode &modelNode, const QVector3D &pos
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_droppedModelNode = {};
|
m_droppedModelNode = {};
|
||||||
m_droppedFile.clear();
|
m_dropped3dImports.clear();
|
||||||
|
m_droppedTexture.clear();
|
||||||
m_nodeAtPosReqType = NodeAtPosReqType::None;
|
m_nodeAtPosReqType = NodeAtPosReqType::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1462,10 +1466,22 @@ void Edit3DView::dropComponent(const ItemLibraryEntry &entry, const QPointF &pos
|
|||||||
nodeAtPosReady({}, {}); // No need to actually resolve position for non-node items
|
nodeAtPosReady({}, {}); // No need to actually resolve position for non-node items
|
||||||
}
|
}
|
||||||
|
|
||||||
void Edit3DView::dropAsset(const QString &file, const QPointF &pos)
|
void QmlDesigner::Edit3DView::dropAssets(const QList<QUrl> &urls, const QPointF &pos)
|
||||||
{
|
{
|
||||||
m_nodeAtPosReqType = NodeAtPosReqType::AssetDrop;
|
m_nodeAtPosReqType = NodeAtPosReqType::AssetDrop;
|
||||||
m_droppedFile = file;
|
m_dropped3dImports.clear();
|
||||||
|
|
||||||
|
for (const QUrl &url : urls) {
|
||||||
|
Asset asset(url.toLocalFile());
|
||||||
|
// For textures we only support single drops
|
||||||
|
if (m_dropped3dImports.isEmpty() && asset.isTexture3D()) {
|
||||||
|
m_droppedTexture = asset.fileName();
|
||||||
|
break;
|
||||||
|
} else if (asset.isImported3D()) {
|
||||||
|
m_dropped3dImports.append(asset.id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
emitView3DAction(View3DActionType::GetNodeAtPos, pos);
|
emitView3DAction(View3DActionType::GetNodeAtPos, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -101,7 +101,7 @@ public:
|
|||||||
void dropBundleItem(const QPointF &pos);
|
void dropBundleItem(const QPointF &pos);
|
||||||
void dropTexture(const ModelNode &textureNode, const QPointF &pos);
|
void dropTexture(const ModelNode &textureNode, const QPointF &pos);
|
||||||
void dropComponent(const ItemLibraryEntry &entry, const QPointF &pos);
|
void dropComponent(const ItemLibraryEntry &entry, const QPointF &pos);
|
||||||
void dropAsset(const QString &file, const QPointF &pos);
|
void dropAssets(const QList<QUrl> &urls, const QPointF &pos);
|
||||||
|
|
||||||
bool isBakingLightsSupported() const;
|
bool isBakingLightsSupported() const;
|
||||||
|
|
||||||
@@ -203,7 +203,8 @@ private:
|
|||||||
ModelCache<QImage> m_canvasCache;
|
ModelCache<QImage> m_canvasCache;
|
||||||
ModelNode m_droppedModelNode;
|
ModelNode m_droppedModelNode;
|
||||||
ItemLibraryEntry m_droppedEntry;
|
ItemLibraryEntry m_droppedEntry;
|
||||||
QString m_droppedFile;
|
QStringList m_dropped3dImports;
|
||||||
|
QString m_droppedTexture;
|
||||||
NodeAtPosReqType m_nodeAtPosReqType;
|
NodeAtPosReqType m_nodeAtPosReqType;
|
||||||
QPoint m_contextMenuPosMouse;
|
QPoint m_contextMenuPosMouse;
|
||||||
QVector3D m_contextMenuPos3D;
|
QVector3D m_contextMenuPos3D;
|
||||||
|
@@ -17,10 +17,8 @@
|
|||||||
#include <designmodewidget.h>
|
#include <designmodewidget.h>
|
||||||
#include <externaldependenciesinterface.h>
|
#include <externaldependenciesinterface.h>
|
||||||
#include <generatedcomponentutils.h>
|
#include <generatedcomponentutils.h>
|
||||||
#include <import.h>
|
|
||||||
#include <materialutils.h>
|
#include <materialutils.h>
|
||||||
#include <metainfo.h>
|
#include <metainfo.h>
|
||||||
#include <modelnodeoperations.h>
|
|
||||||
#include <nodeabstractproperty.h>
|
#include <nodeabstractproperty.h>
|
||||||
#include <nodehints.h>
|
#include <nodehints.h>
|
||||||
#include <nodeinstanceview.h>
|
#include <nodeinstanceview.h>
|
||||||
@@ -455,6 +453,7 @@ void Edit3DWidget::updateCreateSubMenu(const QList<ItemLibraryDetails> &entriesL
|
|||||||
m_createSubMenu->deleteLater();
|
m_createSubMenu->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_nameToImport.clear();
|
||||||
m_nameToEntry.clear();
|
m_nameToEntry.clear();
|
||||||
|
|
||||||
m_createSubMenu = new QmlEditorMenu(tr("Create"), m_contextMenu);
|
m_createSubMenu = new QmlEditorMenu(tr("Create"), m_contextMenu);
|
||||||
@@ -493,9 +492,43 @@ void Edit3DWidget::updateCreateSubMenu(const QList<ItemLibraryDetails> &entriesL
|
|||||||
QAction *action = catMenu->addAction(getEntryIcon(entry), entry.name());
|
QAction *action = catMenu->addAction(getEntryIcon(entry), entry.name());
|
||||||
connect(action, &QAction::triggered, this, [this, action] { onCreateAction(action); });
|
connect(action, &QAction::triggered, this, [this, action] { onCreateAction(action); });
|
||||||
action->setData(entry.name());
|
action->setData(entry.name());
|
||||||
|
Import import = Import::createLibraryImport(entry.requiredImport(),
|
||||||
|
QString::number(entry.majorVersion())
|
||||||
|
+ QLatin1Char('.')
|
||||||
|
+ QString::number(entry.minorVersion()));
|
||||||
|
m_nameToImport.insert(entry.name(), import);
|
||||||
m_nameToEntry.insert(entry.name(), entry);
|
m_nameToEntry.insert(entry.name(), entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create menu for imported 3d models, which don't have ItemLibraryEntries
|
||||||
|
const GeneratedComponentUtils &compUtils
|
||||||
|
= QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
|
||||||
|
QList<Utils::FilePath> qmlFiles = compUtils.imported3dComponents();
|
||||||
|
QMenu *catMenu = nullptr;
|
||||||
|
|
||||||
|
if (!qmlFiles.isEmpty()) {
|
||||||
|
catMenu = new QmlEditorMenu(tr("Imported Models"), m_createSubMenu);
|
||||||
|
catMenu->setIcon(contextIcon(DesignerIcons::ImportedModelsIcon));
|
||||||
|
m_createSubMenu->addMenu(catMenu);
|
||||||
|
|
||||||
|
std::ranges::sort(qmlFiles, {}, &Utils::FilePath::baseName);
|
||||||
|
|
||||||
|
const QIcon icon = QIcon(":/edit3d/images/item-3D_model-icon.png");
|
||||||
|
|
||||||
|
for (const Utils::FilePath &qmlFile : std::as_const(qmlFiles)) {
|
||||||
|
QString qmlName = qmlFile.baseName();
|
||||||
|
QAction *action = catMenu->addAction(icon, qmlName);
|
||||||
|
connect(action, &QAction::triggered, this, [this, action] { onCreateAction(action); });
|
||||||
|
action->setData(qmlName);
|
||||||
|
|
||||||
|
QString importName = compUtils.getImported3dImportName(qmlFile);
|
||||||
|
if (!importName.isEmpty()) {
|
||||||
|
Import import = Import::createLibraryImport(importName);
|
||||||
|
m_nameToImport.insert(qmlName, import);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action triggered from the "create" sub-menu
|
// Action triggered from the "create" sub-menu
|
||||||
@@ -505,17 +538,29 @@ void Edit3DWidget::onCreateAction(QAction *action)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_view->executeInTransaction(__FUNCTION__, [&] {
|
m_view->executeInTransaction(__FUNCTION__, [&] {
|
||||||
ItemLibraryEntry entry = m_nameToEntry.value(action->data().toString());
|
const QString actionName = action->data().toString();
|
||||||
Import import = Import::createLibraryImport(entry.requiredImport(),
|
Import import = m_nameToImport.value(actionName);
|
||||||
QString::number(entry.majorVersion())
|
|
||||||
+ QLatin1Char('.')
|
|
||||||
+ QString::number(entry.minorVersion()));
|
|
||||||
if (!m_view->model()->hasImport(import, true, true))
|
if (!m_view->model()->hasImport(import, true, true))
|
||||||
m_view->model()->changeImports({import}, {});
|
m_view->model()->changeImports({import}, {});
|
||||||
|
|
||||||
|
ModelNode modelNode;
|
||||||
|
if (m_nameToEntry.contains(actionName)) {
|
||||||
int activeScene = Utils3D::active3DSceneId(m_view->model());
|
int activeScene = Utils3D::active3DSceneId(m_view->model());
|
||||||
auto modelNode = QmlVisualNode::createQml3DNode(m_view, entry,
|
ItemLibraryEntry entry = m_nameToEntry.value(actionName);
|
||||||
activeScene, m_contextMenuPos3d).modelNode();
|
|
||||||
|
modelNode = QmlVisualNode::createQml3DNode(m_view, entry, activeScene,
|
||||||
|
m_contextMenuPos3d).modelNode();
|
||||||
|
} else {
|
||||||
|
ModelNode sceneNode = Utils3D::active3DSceneNode(m_view);
|
||||||
|
if (!sceneNode.isValid())
|
||||||
|
sceneNode = m_view->rootModelNode();
|
||||||
|
|
||||||
|
modelNode = QmlVisualNode::QmlVisualNode::createQml3DNode(
|
||||||
|
m_view, actionName.toUtf8(), sceneNode, import.url(),
|
||||||
|
m_contextMenuPos3d).modelNode();
|
||||||
|
}
|
||||||
|
|
||||||
QTC_ASSERT(modelNode.isValid(), return);
|
QTC_ASSERT(modelNode.isValid(), return);
|
||||||
m_view->setSelectedModelNode(modelNode);
|
m_view->setSelectedModelNode(modelNode);
|
||||||
|
|
||||||
@@ -717,9 +762,12 @@ void Edit3DWidget::dragEnterEvent(QDragEnterEvent *dragEnterEvent)
|
|||||||
if (dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ASSETS)
|
if (dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ASSETS)
|
||||||
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
|
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
|
||||||
const auto urls = dragEnterEvent->mimeData()->urls();
|
const auto urls = dragEnterEvent->mimeData()->urls();
|
||||||
if (!urls.isEmpty()) {
|
for (const QUrl &url : urls) {
|
||||||
if (Asset(urls.first().toLocalFile()).isValidTextureSource())
|
Asset asset(url.toLocalFile());
|
||||||
|
if (asset.isImported3D() || asset.isTexture3D()) {
|
||||||
dragEnterEvent->acceptProposedAction();
|
dragEnterEvent->acceptProposedAction();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData())
|
} else if (actionManager.externalDragHasSupportedAssets(dragEnterEvent->mimeData())
|
||||||
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL)
|
|| dragEnterEvent->mimeData()->hasFormat(Constants::MIME_TYPE_MATERIAL)
|
||||||
@@ -787,7 +835,7 @@ void Edit3DWidget::dropEvent(QDropEvent *dropEvent)
|
|||||||
// handle dropping image assets
|
// handle dropping image assets
|
||||||
if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ASSETS)
|
if (dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_ASSETS)
|
||||||
|| dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
|
|| dropEvent->mimeData()->hasFormat(Constants::MIME_TYPE_BUNDLE_TEXTURE)) {
|
||||||
m_view->dropAsset(dropEvent->mimeData()->urls().first().toLocalFile(), pos);
|
m_view->dropAssets(dropEvent->mimeData()->urls(), pos);
|
||||||
m_view->model()->endDrag();
|
m_view->model()->endDrag();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <bundlehelper.h>
|
#include <bundlehelper.h>
|
||||||
|
#include <import.h>
|
||||||
#include <itemlibraryentry.h>
|
#include <itemlibraryentry.h>
|
||||||
#include <modelnode.h>
|
#include <modelnode.h>
|
||||||
|
|
||||||
@@ -111,6 +112,7 @@ private:
|
|||||||
QPointer<QMenu> m_createSubMenu;
|
QPointer<QMenu> m_createSubMenu;
|
||||||
ModelNode m_contextMenuTarget;
|
ModelNode m_contextMenuTarget;
|
||||||
QVector3D m_contextMenuPos3d;
|
QVector3D m_contextMenuPos3d;
|
||||||
|
QHash<QString, Import> m_nameToImport;
|
||||||
QHash<QString, ItemLibraryEntry> m_nameToEntry;
|
QHash<QString, ItemLibraryEntry> m_nameToEntry;
|
||||||
ItemLibraryEntry m_draggedEntry;
|
ItemLibraryEntry m_draggedEntry;
|
||||||
QHash<QAction *, Core::Command *> m_actionToCommandHash;
|
QHash<QAction *, Core::Command *> m_actionToCommandHash;
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 407 B |
Binary file not shown.
After Width: | Height: | Size: 733 B |
@@ -398,9 +398,6 @@ void Import3dImporter::postParseQuick3DAsset(ParseData &pd)
|
|||||||
qmlInfo.append(".");
|
qmlInfo.append(".");
|
||||||
qmlInfo.append(pd.assetName);
|
qmlInfo.append(pd.assetName);
|
||||||
qmlInfo.append('\n');
|
qmlInfo.append('\n');
|
||||||
const QString reqImp = generateRequiredImportForAsset(pd.assetName);
|
|
||||||
if (!m_requiredImports.contains(reqImp))
|
|
||||||
m_requiredImports.append(reqImp);
|
|
||||||
while (qmlIt.hasNext()) {
|
while (qmlIt.hasNext()) {
|
||||||
qmlIt.next();
|
qmlIt.next();
|
||||||
QFileInfo fi = QFileInfo(qmlIt.filePath());
|
QFileInfo fi = QFileInfo(qmlIt.filePath());
|
||||||
@@ -454,12 +451,14 @@ void Import3dImporter::postParseQuick3DAsset(ParseData &pd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add quick3D import unless it is already added
|
// Add quick3D import unless it is already added
|
||||||
if (impVersionMajor > 0 && m_requiredImports.first() != "QtQuick3D")
|
if (impVersionMajor > 0 && (m_requiredImports.isEmpty()
|
||||||
|
|| m_requiredImports.first() != "QtQuick3D")) {
|
||||||
m_requiredImports.prepend("QtQuick3D");
|
m_requiredImports.prepend("QtQuick3D");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
qmldirFile.write(qmlInfo.toUtf8());
|
qmldirFile.write(qmlInfo.toUtf8());
|
||||||
qmldirFile.commit();
|
qmldirFile.commit();
|
||||||
} else {
|
} else {
|
||||||
@@ -577,13 +576,6 @@ QString Import3dImporter::generateAssetFolderName(const QString &assetName) cons
|
|||||||
return assetName + "_QDS_" + QString::number(counter++);
|
return assetName + "_QDS_" + QString::number(counter++);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Import3dImporter::generateRequiredImportForAsset(const QString &assetName) const
|
|
||||||
{
|
|
||||||
return QStringLiteral("%1.%2").arg(
|
|
||||||
QmlDesignerPlugin::instance()->documentManager()
|
|
||||||
.generatedComponentUtils().import3dTypePrefix(), assetName);
|
|
||||||
}
|
|
||||||
|
|
||||||
Import3dImporter::OverwriteResult Import3dImporter::confirmAssetOverwrite(const QString &assetName)
|
Import3dImporter::OverwriteResult Import3dImporter::confirmAssetOverwrite(const QString &assetName)
|
||||||
{
|
{
|
||||||
const QString title = tr("Overwrite Existing Asset?");
|
const QString title = tr("Overwrite Existing Asset?");
|
||||||
@@ -744,9 +736,6 @@ void Import3dImporter::finalizeQuick3DImport()
|
|||||||
addError(tr("Failed to insert import statement into qml document."));
|
addError(tr("Failed to insert import statement into qml document."));
|
||||||
transaction.commit();
|
transaction.commit();
|
||||||
#else
|
#else
|
||||||
// TODO: ModelUtils::addImportsWithCheck requires Model::possibleImports()
|
|
||||||
// to return correct list instead of empty list, so until that is
|
|
||||||
// fixed we need to just trust the missing modules are available
|
|
||||||
const Imports &imports = model->imports();
|
const Imports &imports = model->imports();
|
||||||
Imports importsToAdd;
|
Imports importsToAdd;
|
||||||
for (const QString &importName : std::as_const(m_requiredImports)) {
|
for (const QString &importName : std::as_const(m_requiredImports)) {
|
||||||
@@ -775,6 +764,7 @@ void Import3dImporter::finalizeQuick3DImport()
|
|||||||
}
|
}
|
||||||
timer->stop();
|
timer->stop();
|
||||||
notifyFinished();
|
notifyFinished();
|
||||||
|
model->rewriterView()->emitCustomNotification("asset_import_finished");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
timer->stop();
|
timer->stop();
|
||||||
@@ -791,7 +781,6 @@ void Import3dImporter::removeAssetFromImport(const QString &assetName)
|
|||||||
{
|
{
|
||||||
m_parseData.remove(assetName);
|
m_parseData.remove(assetName);
|
||||||
m_importFiles.remove(assetName);
|
m_importFiles.remove(assetName);
|
||||||
m_requiredImports.removeOne(generateRequiredImportForAsset(assetName));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Import3dImporter::sourceSceneTargetFilePath(const ParseData &pd)
|
QString Import3dImporter::sourceSceneTargetFilePath(const ParseData &pd)
|
||||||
|
@@ -685,6 +685,55 @@ RewriterView *DesignDocument::rewriterView() const
|
|||||||
return m_rewriterView.get();
|
return m_rewriterView.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef QDS_USE_PROJECTSTORAGE
|
||||||
|
static void removeUnusedImports(RewriterView *rewriter)
|
||||||
|
{
|
||||||
|
// Remove any import statements for asset based nodes (composed effect or imported3d)
|
||||||
|
// if there is no nodes using them in the scene.
|
||||||
|
QTC_ASSERT(rewriter && rewriter->model(), return);
|
||||||
|
|
||||||
|
GeneratedComponentUtils compUtils{rewriter->externalDependencies()};
|
||||||
|
|
||||||
|
const QString effectPrefix = compUtils.composedEffectsTypePrefix();
|
||||||
|
const QString imported3dPrefix = compUtils.import3dTypePrefix();
|
||||||
|
const QList<Utils::FilePath> qmlFiles = compUtils.imported3dComponents();
|
||||||
|
QHash<QString, QString> m_imported3dTypeMap;
|
||||||
|
for (const Utils::FilePath &qmlFile : qmlFiles) {
|
||||||
|
QString importName = compUtils.getImported3dImportName(qmlFile);
|
||||||
|
QString type = qmlFile.baseName();
|
||||||
|
m_imported3dTypeMap.insert(importName, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Imports &imports = rewriter->model()->imports();
|
||||||
|
QHash<QString, Import> assetImports;
|
||||||
|
for (const Import &import : imports) {
|
||||||
|
if (import.url().startsWith(effectPrefix)) {
|
||||||
|
QString type = import.url().split('.').last();
|
||||||
|
assetImports.insert(type, import);
|
||||||
|
} else if (import.url().startsWith(imported3dPrefix)) {
|
||||||
|
assetImports.insert(m_imported3dTypeMap[import.url()], import);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<ModelNode> allNodes = rewriter->allModelNodes();
|
||||||
|
for (const ModelNode &node : allNodes) {
|
||||||
|
if (QmlItemNode(node).isEffectItem()
|
||||||
|
|| (node.isComponent() && node.metaInfo().isQtQuick3DNode())) {
|
||||||
|
assetImports.remove(node.simplifiedTypeName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!assetImports.isEmpty()) {
|
||||||
|
Imports removeImports;
|
||||||
|
for (const Import &import : assetImports)
|
||||||
|
removeImports.append(import);
|
||||||
|
rewriter->model()->changeImports({}, removeImports);
|
||||||
|
}
|
||||||
|
|
||||||
|
rewriter->forceAmend();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void DesignDocument::setEditor(Core::IEditor *editor)
|
void DesignDocument::setEditor(Core::IEditor *editor)
|
||||||
{
|
{
|
||||||
m_textEditor = editor;
|
m_textEditor = editor;
|
||||||
@@ -694,6 +743,12 @@ void DesignDocument::setEditor(Core::IEditor *editor)
|
|||||||
this, [this](Core::IDocument *document) {
|
this, [this](Core::IDocument *document) {
|
||||||
if (m_textEditor && m_textEditor->document() == document) {
|
if (m_textEditor && m_textEditor->document() == document) {
|
||||||
if (m_documentModel && m_documentModel->rewriterView()) {
|
if (m_documentModel && m_documentModel->rewriterView()) {
|
||||||
|
|
||||||
|
#ifdef QDS_USE_PROJECTSTORAGE
|
||||||
|
// TODO: ProjectStorage should handle this via Model somehow (QDS-14519)
|
||||||
|
#else
|
||||||
|
removeUnusedImports(rewriterView());
|
||||||
|
#endif
|
||||||
m_documentModel->rewriterView()->writeAuxiliaryData();
|
m_documentModel->rewriterView()->writeAuxiliaryData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -19,9 +19,6 @@ QString ItemLibraryImport::importName() const
|
|||||||
if (m_sectionType == SectionType::User)
|
if (m_sectionType == SectionType::User)
|
||||||
return userComponentsTitle();
|
return userComponentsTitle();
|
||||||
|
|
||||||
if (m_sectionType == SectionType::Quick3DAssets)
|
|
||||||
return quick3DAssetsTitle();
|
|
||||||
|
|
||||||
if (m_sectionType == SectionType::Unimported)
|
if (m_sectionType == SectionType::Unimported)
|
||||||
return unimportedComponentsTitle();
|
return unimportedComponentsTitle();
|
||||||
|
|
||||||
@@ -39,9 +36,6 @@ QString ItemLibraryImport::importUrl() const
|
|||||||
if (m_sectionType == SectionType::User)
|
if (m_sectionType == SectionType::User)
|
||||||
return userComponentsTitle();
|
return userComponentsTitle();
|
||||||
|
|
||||||
if (m_sectionType == SectionType::Quick3DAssets)
|
|
||||||
return quick3DAssetsTitle();
|
|
||||||
|
|
||||||
if (m_sectionType == SectionType::Unimported)
|
if (m_sectionType == SectionType::Unimported)
|
||||||
return unimportedComponentsTitle();
|
return unimportedComponentsTitle();
|
||||||
|
|
||||||
@@ -61,9 +55,6 @@ QString ItemLibraryImport::sortingName() const
|
|||||||
if (m_sectionType == SectionType::User)
|
if (m_sectionType == SectionType::User)
|
||||||
return "_"; // user components always come first
|
return "_"; // user components always come first
|
||||||
|
|
||||||
if (m_sectionType == SectionType::Quick3DAssets)
|
|
||||||
return "__"; // Quick3DAssets come second
|
|
||||||
|
|
||||||
if (m_sectionType == SectionType::Unimported)
|
if (m_sectionType == SectionType::Unimported)
|
||||||
return "zzzzzz"; // Unimported components come last
|
return "zzzzzz"; // Unimported components come last
|
||||||
|
|
||||||
@@ -235,12 +226,6 @@ QString ItemLibraryImport::userComponentsTitle()
|
|||||||
return tr("My Components");
|
return tr("My Components");
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
QString ItemLibraryImport::quick3DAssetsTitle()
|
|
||||||
{
|
|
||||||
return tr("My 3D Components");
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
QString ItemLibraryImport::unimportedComponentsTitle()
|
QString ItemLibraryImport::unimportedComponentsTitle()
|
||||||
{
|
{
|
||||||
|
@@ -28,7 +28,6 @@ public:
|
|||||||
enum class SectionType {
|
enum class SectionType {
|
||||||
Default,
|
Default,
|
||||||
User,
|
User,
|
||||||
Quick3DAssets,
|
|
||||||
Unimported
|
Unimported
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -67,7 +66,6 @@ public:
|
|||||||
bool importUnimported() const { return m_sectionType == SectionType::Unimported; }
|
bool importUnimported() const { return m_sectionType == SectionType::Unimported; }
|
||||||
|
|
||||||
static QString userComponentsTitle();
|
static QString userComponentsTitle();
|
||||||
static QString quick3DAssetsTitle();
|
|
||||||
static QString unimportedComponentsTitle();
|
static QString unimportedComponentsTitle();
|
||||||
|
|
||||||
SectionType sectionType() const;
|
SectionType sectionType() const;
|
||||||
|
@@ -319,12 +319,7 @@ void ItemLibraryModel::update(Model *model)
|
|||||||
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
|
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
|
||||||
|
|
||||||
QStringList excludedImports {
|
QStringList excludedImports {
|
||||||
projectName,
|
projectName
|
||||||
compUtils.materialsBundleType(),
|
|
||||||
compUtils.effectsBundleType(),
|
|
||||||
compUtils.userMaterialsBundleType(),
|
|
||||||
compUtils.user3DBundleType(),
|
|
||||||
compUtils.userEffectsBundleType()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// create import sections
|
// create import sections
|
||||||
@@ -332,23 +327,17 @@ void ItemLibraryModel::update(Model *model)
|
|||||||
QHash<QString, ItemLibraryImport *> importHash;
|
QHash<QString, ItemLibraryImport *> importHash;
|
||||||
for (const Import &import : model->imports()) {
|
for (const Import &import : model->imports()) {
|
||||||
if (excludedImports.contains(import.url())
|
if (excludedImports.contains(import.url())
|
||||||
|| import.url().startsWith(compUtils.composedEffectsTypePrefix())) {
|
|| import.url().startsWith(compUtils.generatedComponentTypePrefix())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool addNew = true;
|
bool addNew = true;
|
||||||
bool isQuick3DAsset = import.url().startsWith(compUtils.import3dTypePrefix());
|
|
||||||
QString importUrl = import.url();
|
QString importUrl = import.url();
|
||||||
if (isQuick3DAsset)
|
if (import.isFileImport())
|
||||||
importUrl = ItemLibraryImport::quick3DAssetsTitle();
|
|
||||||
else if (import.isFileImport())
|
|
||||||
importUrl = import.toString(true, true).remove("\"");
|
importUrl = import.toString(true, true).remove("\"");
|
||||||
|
|
||||||
ItemLibraryImport *oldImport = importHash.value(importUrl);
|
ItemLibraryImport *oldImport = importHash.value(importUrl);
|
||||||
if (oldImport && oldImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets
|
if (oldImport && oldImport->importEntry().url() == import.url()) {
|
||||||
&& isQuick3DAsset) {
|
|
||||||
addNew = false; // add only 1 Quick3DAssets import section
|
|
||||||
} else if (oldImport && oldImport->importEntry().url() == import.url()) {
|
|
||||||
// Retain the higher version if multiples exist
|
// Retain the higher version if multiples exist
|
||||||
if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion())
|
if (oldImport->importEntry().toVersion() >= import.toVersion() || import.hasVersion())
|
||||||
addNew = false;
|
addNew = false;
|
||||||
@@ -357,8 +346,7 @@ void ItemLibraryModel::update(Model *model)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (addNew) {
|
if (addNew) {
|
||||||
auto sectionType = isQuick3DAsset ? ItemLibraryImport::SectionType::Quick3DAssets
|
auto sectionType = ItemLibraryImport::SectionType::Default;
|
||||||
: ItemLibraryImport::SectionType::Default;
|
|
||||||
ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType);
|
ItemLibraryImport *itemLibImport = new ItemLibraryImport(import, this, sectionType);
|
||||||
itemLibImport->setImportUsed(usedImports.contains(import));
|
itemLibImport->setImportUsed(usedImports.contains(import));
|
||||||
importHash.insert(importUrl, itemLibImport);
|
importHash.insert(importUrl, itemLibImport);
|
||||||
@@ -445,8 +433,6 @@ void ItemLibraryModel::update(Model *model)
|
|||||||
importSection = importHash[entry.requiredImport()];
|
importSection = importHash[entry.requiredImport()];
|
||||||
|
|
||||||
}
|
}
|
||||||
} else if (catName == ItemLibraryImport::quick3DAssetsTitle()) {
|
|
||||||
importSection = importHash[ItemLibraryImport::quick3DAssetsTitle()];
|
|
||||||
} else {
|
} else {
|
||||||
if (catName.contains("Qt Quick - ")) {
|
if (catName.contains("Qt Quick - ")) {
|
||||||
QString sortingName = catName;
|
QString sortingName = catName;
|
||||||
@@ -549,8 +535,6 @@ ItemLibraryImport *ItemLibraryModel::importByUrl(const QString &importUrl) const
|
|||||||
|| (importUrl.isEmpty() && itemLibraryImport->importUrl() == "QtQuick")
|
|| (importUrl.isEmpty() && itemLibraryImport->importUrl() == "QtQuick")
|
||||||
|| (importUrl == ItemLibraryImport::userComponentsTitle()
|
|| (importUrl == ItemLibraryImport::userComponentsTitle()
|
||||||
&& itemLibraryImport->sectionType() == ItemLibraryImport::SectionType::User)
|
&& itemLibraryImport->sectionType() == ItemLibraryImport::SectionType::User)
|
||||||
|| (importUrl == ItemLibraryImport::quick3DAssetsTitle()
|
|
||||||
&& itemLibraryImport->sectionType() == ItemLibraryImport::SectionType::Quick3DAssets)
|
|
||||||
|| (importUrl == ItemLibraryImport::unimportedComponentsTitle()
|
|| (importUrl == ItemLibraryImport::unimportedComponentsTitle()
|
||||||
&& itemLibraryImport->sectionType() == ItemLibraryImport::SectionType::Unimported)) {
|
&& itemLibraryImport->sectionType() == ItemLibraryImport::SectionType::Unimported)) {
|
||||||
return itemLibraryImport;
|
return itemLibraryImport;
|
||||||
|
@@ -1015,7 +1015,7 @@ void MaterialEditorView::modelNodePreviewPixmapChanged(const ModelNode &node,
|
|||||||
const QPixmap &pixmap,
|
const QPixmap &pixmap,
|
||||||
const QByteArray &requestId)
|
const QByteArray &requestId)
|
||||||
{
|
{
|
||||||
if (node != m_selectedMaterial || requestId != m_previewRequestId)
|
if (!m_qmlBackEnd || node != m_selectedMaterial || requestId != m_previewRequestId)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_qmlBackEnd->updateMaterialPreview(pixmap);
|
m_qmlBackEnd->updateMaterialPreview(pixmap);
|
||||||
|
@@ -239,6 +239,10 @@ void NameItemDelegate::paint(QPainter *painter,
|
|||||||
model->qtQuickBorderImageMetaInfo());
|
model->qtQuickBorderImageMetaInfo());
|
||||||
} else if (dragType == Constants::MIME_TYPE_ASSET_EFFECT) {
|
} else if (dragType == Constants::MIME_TYPE_ASSET_EFFECT) {
|
||||||
validDrop = metaInfo.isBasedOn(node.model()->qtQuickItemMetaInfo());
|
validDrop = metaInfo.isBasedOn(node.model()->qtQuickItemMetaInfo());
|
||||||
|
} else if (dragType == Constants::MIME_TYPE_ASSET_IMPORTED3D) {
|
||||||
|
Model *model = node.model();
|
||||||
|
validDrop = metaInfo.isBasedOn(model->qtQuick3DNodeMetaInfo(),
|
||||||
|
model->qtQuick3DView3DMetaInfo());
|
||||||
} else {
|
} else {
|
||||||
const NodeMetaInfo dragInfo = node.model()->metaInfo(dragType);
|
const NodeMetaInfo dragInfo = node.model()->metaInfo(dragType);
|
||||||
ChooseFromPropertyListFilter *filter = new ChooseFromPropertyListFilter(dragInfo, metaInfo, true);
|
ChooseFromPropertyListFilter *filter = new ChooseFromPropertyListFilter(dragInfo, metaInfo, true);
|
||||||
|
@@ -669,6 +669,10 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData,
|
|||||||
currNode = ModelNodeOperations::handleItemLibraryEffectDrop(
|
currNode = ModelNodeOperations::handleItemLibraryEffectDrop(
|
||||||
assetPath, modelNodeForIndex(rowModelIndex));
|
assetPath, modelNodeForIndex(rowModelIndex));
|
||||||
moveNodesAfter = false;
|
moveNodesAfter = false;
|
||||||
|
} else if (assetType == Constants::MIME_TYPE_ASSET_IMPORTED3D) {
|
||||||
|
currNode = ModelNodeOperations::handleImported3dAssetDrop(
|
||||||
|
assetPath, modelNodeForIndex(rowModelIndex));
|
||||||
|
moveNodesAfter = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currNode.isValid())
|
if (currNode.isValid())
|
||||||
|
@@ -306,6 +306,9 @@ void NavigatorView::dragStarted(QMimeData *mimeData)
|
|||||||
if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) {
|
if (assetType == Constants::MIME_TYPE_ASSET_EFFECT) {
|
||||||
m_widget->setDragType(Constants::MIME_TYPE_ASSET_EFFECT);
|
m_widget->setDragType(Constants::MIME_TYPE_ASSET_EFFECT);
|
||||||
m_widget->update();
|
m_widget->update();
|
||||||
|
} else if (assetType == Constants::MIME_TYPE_ASSET_IMPORTED3D) {
|
||||||
|
m_widget->setDragType(Constants::MIME_TYPE_ASSET_IMPORTED3D);
|
||||||
|
m_widget->update();
|
||||||
} else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) {
|
} else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) {
|
||||||
m_widget->setDragType(Constants::MIME_TYPE_ASSET_TEXTURE3D);
|
m_widget->setDragType(Constants::MIME_TYPE_ASSET_TEXTURE3D);
|
||||||
m_widget->update();
|
m_widget->update();
|
||||||
|
@@ -337,4 +337,61 @@ QString GeneratedComponentUtils::user3DBundleType() const
|
|||||||
return componentBundlesTypePrefix() + '.' + user3DBundleId();
|
return componentBundlesTypePrefix() + '.' + user3DBundleId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<Utils::FilePath> GeneratedComponentUtils::imported3dComponents() const
|
||||||
|
{
|
||||||
|
auto import3dPath = Utils::FilePath::fromString(import3dTypePath());
|
||||||
|
auto projPath = Utils::FilePath::fromString(m_externalDependencies.currentProjectDirPath());
|
||||||
|
auto fullPath = projPath.resolvePath(import3dPath);
|
||||||
|
|
||||||
|
if (fullPath.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return collectFiles(fullPath, "qml");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GeneratedComponentUtils::getImported3dImportName(const Utils::FilePath &qmlFile) const
|
||||||
|
{
|
||||||
|
const QStringList sl = qmlFile.toFSPathString().split('/');
|
||||||
|
int i = sl.size() - 4;
|
||||||
|
if (i >= 0)
|
||||||
|
return QStringView(u"%1.%2.%3").arg(sl[i], sl[i + 1], sl[i + 2]);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::FilePath GeneratedComponentUtils::getImported3dQml(const QString &assetPath) const
|
||||||
|
{
|
||||||
|
Utils::FilePath assetFilePath = Utils::FilePath::fromString(assetPath);
|
||||||
|
const Utils::expected_str<QByteArray> data = assetFilePath.fileContents();
|
||||||
|
|
||||||
|
if (!data)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
Utils::FilePath assetQmlFilePath = Utils::FilePath::fromUtf8(data.value());
|
||||||
|
Utils::FilePath projectPath = Utils::FilePath::fromString(m_externalDependencies.currentProjectDirPath());
|
||||||
|
|
||||||
|
assetQmlFilePath = projectPath.resolvePath(assetQmlFilePath);
|
||||||
|
|
||||||
|
return assetQmlFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively find files of certain suffix in a dir
|
||||||
|
QList<Utils::FilePath> GeneratedComponentUtils::collectFiles(const Utils::FilePath &dirPath,
|
||||||
|
const QString &suffix) const
|
||||||
|
{
|
||||||
|
if (dirPath.isEmpty())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QList<Utils::FilePath> files;
|
||||||
|
|
||||||
|
const Utils::FilePaths entryList = dirPath.dirEntries(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
|
for (const Utils::FilePath &entry : entryList) {
|
||||||
|
if (entry.isDir())
|
||||||
|
files.append(collectFiles(entry.absoluteFilePath(), suffix));
|
||||||
|
else if (entry.suffix() == suffix)
|
||||||
|
files.append(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -50,8 +50,14 @@ public:
|
|||||||
QString userEffectsBundleType() const;
|
QString userEffectsBundleType() const;
|
||||||
QString user3DBundleType() const;
|
QString user3DBundleType() const;
|
||||||
|
|
||||||
|
QList<Utils::FilePath> imported3dComponents() const;
|
||||||
|
QString getImported3dImportName(const Utils::FilePath &qmlFile) const;
|
||||||
|
Utils::FilePath getImported3dQml(const QString &assetPath) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ExternalDependenciesInterface &m_externalDependencies;
|
ExternalDependenciesInterface &m_externalDependencies;
|
||||||
|
|
||||||
|
QList<Utils::FilePath> collectFiles(const Utils::FilePath &dirPath, const QString &suffix) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -182,6 +182,7 @@ public:
|
|||||||
NodeMetaInfo qtQuick3DSpotLightMetaInfo() const;
|
NodeMetaInfo qtQuick3DSpotLightMetaInfo() const;
|
||||||
NodeMetaInfo qtQuick3DTextureMetaInfo() const;
|
NodeMetaInfo qtQuick3DTextureMetaInfo() const;
|
||||||
NodeMetaInfo qtQuick3DTextureInputMetaInfo() const;
|
NodeMetaInfo qtQuick3DTextureInputMetaInfo() const;
|
||||||
|
NodeMetaInfo qtQuick3DView3DMetaInfo() const;
|
||||||
NodeMetaInfo qtQuickBorderImageMetaInfo() const;
|
NodeMetaInfo qtQuickBorderImageMetaInfo() const;
|
||||||
NodeMetaInfo qtQuickControlsLabelMetaInfo() const;
|
NodeMetaInfo qtQuickControlsLabelMetaInfo() const;
|
||||||
NodeMetaInfo qtQuickControlsTextAreaMetaInfo() const;
|
NodeMetaInfo qtQuickControlsTextAreaMetaInfo() const;
|
||||||
|
@@ -2613,6 +2613,16 @@ NodeMetaInfo Model::qtQuick3DTextureInputMetaInfo() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NodeMetaInfo Model::qtQuick3DView3DMetaInfo() const
|
||||||
|
{
|
||||||
|
if constexpr (useProjectStorage()) {
|
||||||
|
using namespace Storage::Info;
|
||||||
|
return createNodeMetaInfo<QtQuick3D, View3D>();
|
||||||
|
} else {
|
||||||
|
return metaInfo("QtQuick3D.View3D");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NodeMetaInfo Model::qtQuickBorderImageMetaInfo() const
|
NodeMetaInfo Model::qtQuickBorderImageMetaInfo() const
|
||||||
{
|
{
|
||||||
if constexpr (useProjectStorage()) {
|
if constexpr (useProjectStorage()) {
|
||||||
|
@@ -85,6 +85,12 @@ const QStringList &Asset::supportedEffectComposerSuffixes()
|
|||||||
return retList;
|
return retList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QStringList &Asset::supportedImported3dSuffixes()
|
||||||
|
{
|
||||||
|
static QStringList retList {"*.q3d"};
|
||||||
|
return retList;
|
||||||
|
}
|
||||||
|
|
||||||
const QSet<QString> &Asset::supportedSuffixes()
|
const QSet<QString> &Asset::supportedSuffixes()
|
||||||
{
|
{
|
||||||
static QSet<QString> allSuffixes;
|
static QSet<QString> allSuffixes;
|
||||||
@@ -100,6 +106,7 @@ const QSet<QString> &Asset::supportedSuffixes()
|
|||||||
insertSuffixes(supportedVideoSuffixes());
|
insertSuffixes(supportedVideoSuffixes());
|
||||||
insertSuffixes(supportedTexture3DSuffixes());
|
insertSuffixes(supportedTexture3DSuffixes());
|
||||||
insertSuffixes(supportedEffectComposerSuffixes());
|
insertSuffixes(supportedEffectComposerSuffixes());
|
||||||
|
insertSuffixes(supportedImported3dSuffixes());
|
||||||
}
|
}
|
||||||
return allSuffixes;
|
return allSuffixes;
|
||||||
}
|
}
|
||||||
@@ -182,6 +189,11 @@ bool Asset::isEffect() const
|
|||||||
return m_type == Asset::Type::Effect;
|
return m_type == Asset::Type::Effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Asset::isImported3D() const
|
||||||
|
{
|
||||||
|
return m_type == Asset::Type::Imported3D;
|
||||||
|
}
|
||||||
|
|
||||||
const QString Asset::suffix() const
|
const QString Asset::suffix() const
|
||||||
{
|
{
|
||||||
return m_suffix;
|
return m_suffix;
|
||||||
@@ -236,7 +248,8 @@ void Asset::resolveType()
|
|||||||
m_type = Asset::Type::Texture3D;
|
m_type = Asset::Type::Texture3D;
|
||||||
else if (supportedEffectComposerSuffixes().contains(m_suffix))
|
else if (supportedEffectComposerSuffixes().contains(m_suffix))
|
||||||
m_type = Asset::Type::Effect;
|
m_type = Asset::Type::Effect;
|
||||||
}
|
else if (supportedImported3dSuffixes().contains(m_suffix))
|
||||||
|
m_type = Asset::Type::Imported3D;}
|
||||||
|
|
||||||
bool Asset::hasSuffix() const
|
bool Asset::hasSuffix() const
|
||||||
{
|
{
|
||||||
|
@@ -15,7 +15,8 @@ namespace QmlDesigner {
|
|||||||
class QMLDESIGNERUTILS_EXPORT Asset
|
class QMLDESIGNERUTILS_EXPORT Asset
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Type { Unknown,
|
enum Type {
|
||||||
|
Unknown,
|
||||||
Image,
|
Image,
|
||||||
MissingImage,
|
MissingImage,
|
||||||
FragmentShader,
|
FragmentShader,
|
||||||
@@ -25,7 +26,9 @@ public:
|
|||||||
Video,
|
Video,
|
||||||
Texture3D,
|
Texture3D,
|
||||||
Effect,
|
Effect,
|
||||||
Folder };
|
Folder,
|
||||||
|
Imported3D
|
||||||
|
};
|
||||||
|
|
||||||
Asset(const QString &filePath);
|
Asset(const QString &filePath);
|
||||||
|
|
||||||
@@ -38,6 +41,7 @@ public:
|
|||||||
static const QStringList &supportedVideoSuffixes();
|
static const QStringList &supportedVideoSuffixes();
|
||||||
static const QStringList &supportedTexture3DSuffixes();
|
static const QStringList &supportedTexture3DSuffixes();
|
||||||
static const QStringList &supportedEffectComposerSuffixes();
|
static const QStringList &supportedEffectComposerSuffixes();
|
||||||
|
static const QStringList &supportedImported3dSuffixes();
|
||||||
static const QSet<QString> &supportedSuffixes();
|
static const QSet<QString> &supportedSuffixes();
|
||||||
static bool isSupported(const QString &path);
|
static bool isSupported(const QString &path);
|
||||||
|
|
||||||
@@ -59,6 +63,7 @@ public:
|
|||||||
bool isHdrFile() const;
|
bool isHdrFile() const;
|
||||||
bool isKtxFile() const;
|
bool isKtxFile() const;
|
||||||
bool isEffect() const;
|
bool isEffect() const;
|
||||||
|
bool isImported3D() const;
|
||||||
bool isSupported() const;
|
bool isSupported() const;
|
||||||
bool isValidTextureSource();
|
bool isValidTextureSource();
|
||||||
bool isFolder() const;
|
bool isFolder() const;
|
||||||
|
@@ -87,6 +87,7 @@ inline constexpr char MIME_TYPE_ASSET_TEXTURE3D[]
|
|||||||
= "application/vnd.qtdesignstudio.asset.texture3d";
|
= "application/vnd.qtdesignstudio.asset.texture3d";
|
||||||
inline constexpr char MIME_TYPE_MODELNODE_LIST[] = "application/vnd.qtdesignstudio.modelnode.list";
|
inline constexpr char MIME_TYPE_MODELNODE_LIST[] = "application/vnd.qtdesignstudio.modelnode.list";
|
||||||
inline constexpr char MIME_TYPE_ASSET_EFFECT[] = "application/vnd.qtdesignstudio.asset.effect";
|
inline constexpr char MIME_TYPE_ASSET_EFFECT[] = "application/vnd.qtdesignstudio.asset.effect";
|
||||||
|
inline constexpr char MIME_TYPE_ASSET_IMPORTED3D[] = "application/vnd.qtdesignstudio.asset.imported3d";
|
||||||
|
|
||||||
// Menus
|
// Menus
|
||||||
inline constexpr char M_VIEW_WORKSPACES[] = "QmlDesigner.Menu.View.Workspaces";
|
inline constexpr char M_VIEW_WORKSPACES[] = "QmlDesigner.Menu.View.Workspaces";
|
||||||
|
@@ -482,6 +482,58 @@ QmlVisualNode QmlVisualNode::createQml3DNode(AbstractView *view,
|
|||||||
return createQmlObjectNode(view, itemLibraryEntry, position, sceneNodeProperty, createInTransaction).modelNode();
|
return createQmlObjectNode(view, itemLibraryEntry, position, sceneNodeProperty, createInTransaction).modelNode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QmlVisualNode QmlVisualNode::createQml3DNode(AbstractView *view,
|
||||||
|
const TypeName &typeName,
|
||||||
|
const ModelNode &parentNode,
|
||||||
|
const QString &importName,
|
||||||
|
const QVector3D &position,
|
||||||
|
bool createInTransaction)
|
||||||
|
{
|
||||||
|
NodeAbstractProperty targetParentProperty = parentNode.defaultNodeListProperty();
|
||||||
|
|
||||||
|
QTC_ASSERT(targetParentProperty.isValid(), return {});
|
||||||
|
|
||||||
|
QTC_ASSERT(!typeName.isEmpty(), return {});
|
||||||
|
|
||||||
|
QmlVisualNode newQmlObjectNode;
|
||||||
|
|
||||||
|
auto createNodeFunc = [&]() {
|
||||||
|
if (!importName.isEmpty()) {
|
||||||
|
Import import = Import::createLibraryImport(importName);
|
||||||
|
view->model()->changeImports({import}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QPair<PropertyName, QVariant> > propertyPairList;
|
||||||
|
propertyPairList.append(Position(position).propertyPairList());
|
||||||
|
#ifdef QDS_USE_PROJECTSTORAGE
|
||||||
|
newQmlObjectNode = QmlVisualNode(view->createModelNode(typeName,
|
||||||
|
propertyPairList));
|
||||||
|
#else
|
||||||
|
NodeMetaInfo metaInfo = view->model()->metaInfo(typeName);
|
||||||
|
newQmlObjectNode = QmlVisualNode(view->createModelNode(typeName,
|
||||||
|
metaInfo.majorVersion(),
|
||||||
|
metaInfo.minorVersion(),
|
||||||
|
propertyPairList));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (newQmlObjectNode.id().isEmpty()) {
|
||||||
|
newQmlObjectNode.modelNode().setIdWithoutRefactoring(
|
||||||
|
view->model()->generateNewId(QString::fromUtf8(typeName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetParentProperty.isValid())
|
||||||
|
targetParentProperty.reparentHere(newQmlObjectNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (createInTransaction)
|
||||||
|
view->executeInTransaction(__FUNCTION__, createNodeFunc);
|
||||||
|
else
|
||||||
|
createNodeFunc();
|
||||||
|
|
||||||
|
return newQmlObjectNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
NodeListProperty QmlVisualNode::findSceneNodeProperty(AbstractView *view, qint32 sceneRootId)
|
NodeListProperty QmlVisualNode::findSceneNodeProperty(AbstractView *view, qint32 sceneRootId)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(view, return {});
|
QTC_ASSERT(view, return {});
|
||||||
|
@@ -88,6 +88,13 @@ public:
|
|||||||
qint32 sceneRootId = -1, const QVector3D &position = {},
|
qint32 sceneRootId = -1, const QVector3D &position = {},
|
||||||
bool createInTransaction = true);
|
bool createInTransaction = true);
|
||||||
|
|
||||||
|
static QmlVisualNode createQml3DNode(AbstractView *view,
|
||||||
|
const TypeName &typeName,
|
||||||
|
const ModelNode &parentNode,
|
||||||
|
const QString &importName = {},
|
||||||
|
const QVector3D &position = {},
|
||||||
|
bool createInTransaction = true);
|
||||||
|
|
||||||
static NodeListProperty findSceneNodeProperty(AbstractView *view, qint32 sceneRootId);
|
static NodeListProperty findSceneNodeProperty(AbstractView *view, qint32 sceneRootId);
|
||||||
|
|
||||||
static bool isFlowTransition(const ModelNode &node);
|
static bool isFlowTransition(const ModelNode &node);
|
||||||
|
Reference in New Issue
Block a user