diff options
author | Amr Essam <[email protected]> | 2023-11-14 14:49:42 +0200 |
---|---|---|
committer | Amr Elsayed <[email protected]> | 2023-11-15 14:29:52 +0000 |
commit | 306ce4ab35086dc90165433d09b53e11435b7b30 (patch) | |
tree | a24617ff27063b20f83fe3a73fc6bc57063a4ba6 | |
parent | de4c871655afb116bead829599ef55d42f3b0b40 (diff) |
EffectMaker: Open saved compositions
- Also fixing issues related to image paths
- Composition name is shown in save dialog when re-save
- Clear current composition for reset or open a new one
Task-number: QDS-11192
Change-Id: I97aad4b5216e6b116343bb274db0f9abd1275fec
Reviewed-by: Mahmoud Badri <[email protected]>
-rw-r--r-- | share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml | 1 | ||||
-rw-r--r-- | share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml | 2 | ||||
-rw-r--r-- | src/plugins/effectmakernew/compositionnode.cpp | 53 | ||||
-rw-r--r-- | src/plugins/effectmakernew/compositionnode.h | 5 | ||||
-rw-r--r-- | src/plugins/effectmakernew/effectmakermodel.cpp | 139 | ||||
-rw-r--r-- | src/plugins/effectmakernew/effectmakermodel.h | 14 | ||||
-rw-r--r-- | src/plugins/effectmakernew/effectmakerview.cpp | 13 | ||||
-rw-r--r-- | src/plugins/effectmakernew/uniform.cpp | 31 | ||||
-rw-r--r-- | src/plugins/effectmakernew/uniform.h | 4 | ||||
-rw-r--r-- | src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp | 18 |
10 files changed, 181 insertions, 99 deletions
diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml index 19e08acd525..a60b41acf31 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMaker.qml @@ -18,6 +18,7 @@ Item { SaveDialog { id: saveDialog + compositionName: EffectMakerBackend.effectMakerModel.currentComposition anchors.centerIn: parent onAccepted: { let name = saveDialog.compositionName diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml index 08bb8c0832a..8bd48e1d6c4 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml @@ -21,7 +21,7 @@ StudioControls.Dialog { property string compositionName: "" onOpened: { - nameText.text = "" //TODO: Generate unique name + nameText.text = compositionName //TODO: Generate unique name emptyText.text = "" nameText.forceActiveFocus() } diff --git a/src/plugins/effectmakernew/compositionnode.cpp b/src/plugins/effectmakernew/compositionnode.cpp index 796b06151f9..8cd5bda971e 100644 --- a/src/plugins/effectmakernew/compositionnode.cpp +++ b/src/plugins/effectmakernew/compositionnode.cpp @@ -11,13 +11,36 @@ #include <QFile> #include <QJsonArray> #include <QJsonDocument> -#include <QJsonObject> namespace EffectMaker { -CompositionNode::CompositionNode(const QString &qenPath) +CompositionNode::CompositionNode(const QString &effectName, const QString &qenPath, const QJsonObject &jsonObject) { - parse(qenPath); + QJsonObject json; + if (jsonObject.isEmpty()) { + QFile qenFile(qenPath); + if (!qenFile.open(QIODevice::ReadOnly)) { + qWarning("Couldn't open effect file."); + return; + } + + QByteArray loadData = qenFile.readAll(); + QJsonParseError parseError; + QJsonDocument jsonDoc(QJsonDocument::fromJson(loadData, &parseError)); + + if (parseError.error != QJsonParseError::NoError) { + QString error = QString("Error parsing effect node"); + QString errorDetails = QString("%1: %2").arg(parseError.offset).arg(parseError.errorString()); + qWarning() << error; + qWarning() << errorDetails; + return; + } + json = jsonDoc.object().value("QEN").toObject(); + parse(effectName, qenPath, json); + } + else { + parse(effectName, "", jsonObject); + } } QString CompositionNode::fragmentCode() const @@ -63,28 +86,8 @@ CompositionNode::NodeType CompositionNode::type() const return m_type; } -void CompositionNode::parse(const QString &qenPath) +void CompositionNode::parse(const QString &effectName, const QString &qenPath, const QJsonObject &json) { - QFile qenFile(qenPath); - - if (!qenFile.open(QIODevice::ReadOnly)) { - qWarning("Couldn't open effect file."); - return; - } - - QByteArray loadData = qenFile.readAll(); - QJsonParseError parseError; - QJsonDocument jsonDoc(QJsonDocument::fromJson(loadData, &parseError)); - if (parseError.error != QJsonParseError::NoError) { - QString error = QString("Error parsing the effect node: %1:").arg(qenPath); - QString errorDetails = QString("%1: %2").arg(parseError.offset).arg(parseError.errorString()); - qWarning() << qPrintable(error); - qWarning() << qPrintable(errorDetails); - return; - } - - QJsonObject json = jsonDoc.object().value("QEN").toObject(); - int version = -1; if (json.contains("version")) version = json["version"].toInt(-1); @@ -102,7 +105,7 @@ void CompositionNode::parse(const QString &qenPath) // parse properties QJsonArray jsonProps = json.value("properties").toArray(); for (const auto /*QJsonValueRef*/ &prop : jsonProps) { - const auto uniform = new Uniform(prop.toObject(), qenPath); + const auto uniform = new Uniform(effectName, prop.toObject(), qenPath); m_unifomrsModel.addUniform(uniform); m_uniforms.append(uniform); g_propertyData.insert(uniform->name(), uniform->value()); diff --git a/src/plugins/effectmakernew/compositionnode.h b/src/plugins/effectmakernew/compositionnode.h index 37203ef4219..4736f1d8afa 100644 --- a/src/plugins/effectmakernew/compositionnode.h +++ b/src/plugins/effectmakernew/compositionnode.h @@ -5,6 +5,7 @@ #include "effectmakeruniformsmodel.h" +#include <QJsonObject> #include <QObject> namespace EffectMaker { @@ -24,7 +25,7 @@ public: CustomNode }; - CompositionNode(const QString &qenPath); + CompositionNode(const QString &effectName, const QString &qenPath, const QJsonObject &json = {}); QString fragmentCode() const; QString vertexCode() const; @@ -48,7 +49,7 @@ signals: void isEnabledChanged(); private: - void parse(const QString &qenPath); + void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json); QString m_name; NodeType m_type = CustomNode; diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index fa11f2fb3c7..ee147218a6d 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -49,19 +49,6 @@ static bool writeToFile(const QByteArray &buf, const QString &filename, FileType EffectMakerModel::EffectMakerModel(QObject *parent) : QAbstractListModel{parent} { - connect(&m_fileWatcher, &Utils::FileSystemWatcher::fileChanged, this, [this]() { - // Update component with images not set. - m_loadComponentImages = false; - updateQmlComponent(); - // Then enable component images with a longer delay than - // the component updating delay. This way Image elements - // will reload the changed image files. - const int enableImagesDelay = 200; - QTimer::singleShot(enableImagesDelay, this, [this]() { - m_loadComponentImages = true; - updateQmlComponent(); - } ); - }); } QHash<int, QByteArray> EffectMakerModel::roleNames() const @@ -117,7 +104,7 @@ void EffectMakerModel::setIsEmpty(bool val) void EffectMakerModel::addNode(const QString &nodeQenPath) { beginInsertRows({}, m_nodes.size(), m_nodes.size()); - auto *node = new CompositionNode(nodeQenPath); + auto *node = new CompositionNode("", nodeQenPath); m_nodes.append(node); endInsertRows(); @@ -189,12 +176,17 @@ void EffectMakerModel::clear() if (m_nodes.isEmpty()) return; + beginRemoveRows({}, 0, m_nodes.count()); + for (CompositionNode *node : std::as_const(m_nodes)) delete node; m_nodes.clear(); + endRemoveRows(); + setIsEmpty(true); + bakeShaders(); } const QList<Uniform *> EffectMakerModel::allUniforms() @@ -571,6 +563,69 @@ void EffectMakerModel::exportComposition(const QString &name) saveFile.close(); } +void EffectMakerModel::openComposition(const QString &path) +{ + clear(); + + QFile compFile(path); + if (!compFile.open(QIODevice::ReadOnly)) { + QString error = QString("Couldn't open composition file: '%1'").arg(path); + qWarning() << qPrintable(error); + setEffectError(error); + return; + } + + QByteArray data = compFile.readAll(); + QJsonParseError parseError; + QJsonDocument jsonDoc(QJsonDocument::fromJson(data, &parseError)); + if (parseError.error != QJsonParseError::NoError) { + QString error = QString("Error parsing the project file: %1").arg(parseError.errorString()); + qWarning() << error; + setEffectError(error); + return; + } + QJsonObject rootJson = jsonDoc.object(); + if (!rootJson.contains("QEP")) { + QString error = QStringLiteral("Error: Invalid project file"); + qWarning() << error; + setEffectError(error); + return; + } + + QJsonObject json = rootJson["QEP"].toObject(); + + int version = -1; + if (json.contains("version")) + version = json["version"].toInt(-1); + + if (version != 1) { + QString error = QString("Error: Unknown project version (%1)").arg(version); + qWarning() << error; + setEffectError(error); + return; + } + + // Get effects dir + const QString effectName = QFileInfo(path).baseName(); + const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory(); + const QString effectsResPath = effectsResDir.pathAppended(effectName).toString(); + + if (json.contains("nodes") && json["nodes"].isArray()) { + const QJsonArray nodesArray = json["nodes"].toArray(); + for (const auto &nodeElement : nodesArray) { + beginInsertRows({}, m_nodes.size(), m_nodes.size()); + auto *node = new CompositionNode(effectName, "", nodeElement.toObject()); + m_nodes.append(node); + endInsertRows(); + } + + setIsEmpty(m_nodes.isEmpty()); + bakeShaders(); + } + + setCurrentComposition(effectName); +} + void EffectMakerModel::exportResources(const QString &name) { // Make sure that uniforms are up-to-date @@ -647,18 +702,15 @@ void EffectMakerModel::exportResources(const QString &name) QString imagePath = uniform->value().toString(); QFileInfo fi(imagePath); QString imageFilename = fi.fileName(); - sources.append(imagePath); + sources.append(imagePath.remove(0, 7)); // Removes "file://" dests.append(imageFilename); } } - //TODO: Copy source files if requested in future versions - - // Copy files for (int i = 0; i < sources.count(); ++i) { Utils::FilePath source = Utils::FilePath::fromString(sources[i]); Utils::FilePath target = Utils::FilePath::fromString(effectsResPath + dests[i]); - if (target.exists()) + if (target.exists() && source.fileName() != target.fileName()) target.removeFile(); // Remove existing file for update if (!source.copyFile(target)) @@ -1254,15 +1306,16 @@ QString EffectMakerModel::getQmlImagesString(bool localFiles) if (localFiles) { QFileInfo fi(imagePath); imagePath = fi.fileName(); - } - if (m_loadComponentImages) + imagesString += QString(" source: %1\n").arg(uniform->name()); + } else { imagesString += QString(" source: g_propertyData.%1\n").arg(uniform->name()); - if (!localFiles) { - QString mipmapProperty = mipmapPropertyName(uniform->name()); - imagesString += QString(" mipmap: g_propertyData.%1\n").arg(mipmapProperty); - } else if (uniform->enableMipmap()) { - imagesString += " mipmap: true\n"; + + if (uniform->enableMipmap()) + imagesString += " mipmap: true\n"; + else + QString mipmapProperty = mipmapPropertyName(uniform->name()); } + imagesString += " visible: false\n"; imagesString += " }\n"; } @@ -1336,6 +1389,19 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) return s; } +QString EffectMakerModel::currentComposition() const +{ + return m_currentComposition; +} + +void EffectMakerModel::setCurrentComposition(const QString &newCurrentComposition) +{ + if (m_currentComposition == newCurrentComposition) + return; + m_currentComposition = newCurrentComposition; + emit currentCompositionChanged(); +} + void EffectMakerModel::updateQmlComponent() { // Clear possible QML runtime errors @@ -1352,25 +1418,4 @@ QString EffectMakerModel::stripFileFromURL(const QString &urlString) const return filePath; } -void EffectMakerModel::updateImageWatchers() -{ - const QList<Uniform *> uniforms = allUniforms(); - for (Uniform *uniform : uniforms) { - if (uniform->type() == Uniform::Type::Sampler) { - // Watch all image properties files - QString imagePath = stripFileFromURL(uniform->value().toString()); - if (imagePath.isEmpty()) - continue; - m_fileWatcher.addFile(imagePath, Utils::FileSystemWatcher::WatchAllChanges); - } - } -} - -void EffectMakerModel::clearImageWatchers() -{ - const auto watchedFiles = m_fileWatcher.files(); - if (!watchedFiles.isEmpty()) - m_fileWatcher.removeFiles(watchedFiles); -} - } // namespace EffectMaker diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h index 9647b8886a1..96eb0e19b46 100644 --- a/src/plugins/effectmakernew/effectmakermodel.h +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -6,7 +6,6 @@ #include "shaderfeatures.h" #include <utils/filepath.h> -#include <utils/filesystemwatcher.h> #include <QFileSystemWatcher> #include <QMap> @@ -47,7 +46,7 @@ class EffectMakerModel : public QAbstractListModel Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged) Q_PROPERTY(bool shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged) Q_PROPERTY(QString qmlComponentString READ qmlComponentString) - + Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged) public: EffectMakerModel(QObject *parent = nullptr); @@ -86,6 +85,11 @@ public: Q_INVOKABLE void exportComposition(const QString &name); Q_INVOKABLE void exportResources(const QString &name); + void openComposition(const QString &path); + + QString currentComposition() const; + void setCurrentComposition(const QString &newCurrentComposition); + signals: void isEmptyChanged(); void selectedIndexChanged(int idx); @@ -93,6 +97,8 @@ signals: void shadersUpToDateChanged(); void shadersBaked(); + void currentCompositionChanged(); + private: enum Roles { NameRole = Qt::UserRole + 1, @@ -138,8 +144,6 @@ private: QString generateFragmentShader(bool includeUniforms = true); void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader); QString stripFileFromURL(const QString &urlString) const; - void updateImageWatchers(); - void clearImageWatchers(); QString getQmlEffectString(); void updateCustomUniforms(); @@ -179,7 +183,7 @@ private: QString m_previewEffectPropertiesString; QString m_qmlComponentString; bool m_loadComponentImages = true; - Utils::FileSystemWatcher m_fileWatcher; + QString m_currentComposition; const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); }; diff --git a/src/plugins/effectmakernew/effectmakerview.cpp b/src/plugins/effectmakernew/effectmakerview.cpp index 40ba4987e67..637c12f6d5a 100644 --- a/src/plugins/effectmakernew/effectmakerview.cpp +++ b/src/plugins/effectmakernew/effectmakerview.cpp @@ -59,12 +59,15 @@ QmlDesigner::WidgetInfo EffectMakerView::widgetInfo() QmlDesigner::WidgetInfo::LeftPane, 0, tr("Effect Maker")); } -void EffectMakerView::customNotification(const AbstractView * /*view*/, - const QString & /*identifier*/, - const QList<QmlDesigner::ModelNode> & /*nodeList*/, - const QList<QVariant> & /*data*/) +void EffectMakerView::customNotification([[maybe_unused]] const AbstractView *view, + const QString &identifier, + [[maybe_unused]] const QList<QmlDesigner::ModelNode> &nodeList, + const QList<QVariant> &data) { - // TODO + if (identifier == "open_effectmaker_composition" && data.count() > 0) { + const QString compositionPath = data[0].toString(); + m_widget->effectMakerModel()->openComposition(compositionPath); + } } void EffectMakerView::modelAttached(QmlDesigner::Model *model) diff --git a/src/plugins/effectmakernew/uniform.cpp b/src/plugins/effectmakernew/uniform.cpp index c7e460e460e..be10cc7f421 100644 --- a/src/plugins/effectmakernew/uniform.cpp +++ b/src/plugins/effectmakernew/uniform.cpp @@ -6,13 +6,15 @@ #include "propertyhandler.h" +#include <modelnodeoperations.h> + #include <QColor> #include <QJsonObject> #include <QVector2D> namespace EffectMaker { -Uniform::Uniform(const QJsonObject &propObj, const QString &qenPath) +Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QString &qenPath) : m_qenPath(qenPath) { QString value, defaultValue, minValue, maxValue; @@ -26,9 +28,11 @@ Uniform::Uniform(const QJsonObject &propObj, const QString &qenPath) if (m_displayName.isEmpty()) m_displayName = m_name; + QString resPath; if (m_type == Type::Sampler) { + resPath = getResourcePath(effectName, defaultValue, qenPath); if (!defaultValue.isEmpty()) - defaultValue = getResourcePath(defaultValue); + defaultValue = resPath; if (propObj.contains("enableMipmap")) m_enableMipmap = getBoolValue(propObj.value("enableMipmap"), false); // Update the mipmap property @@ -39,7 +43,7 @@ Uniform::Uniform(const QJsonObject &propObj, const QString &qenPath) if (propObj.contains("value")) { value = propObj.value("value").toString(); if (m_type == Type::Sampler) - value = getResourcePath(value); + value = resPath; } else { // QEN files don't store the current value, so with those use default value value = defaultValue; @@ -166,15 +170,20 @@ bool Uniform::getBoolValue(const QJsonValue &jsonValue, bool defaultValue) // Returns the path for a shader resource // Used with sampler types -QString Uniform::getResourcePath(const QString &value) const +QString Uniform::getResourcePath(const QString &effectName, const QString &value, const QString &qenPath) const { QString filePath = value; - QDir dir(m_qenPath); - dir.cdUp(); - QString absPath = dir.absoluteFilePath(filePath); - absPath = QDir::cleanPath(absPath); - absPath = QUrl::fromLocalFile(absPath).toString(); - return absPath; + if (qenPath.isEmpty()) { + const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory(); + return effectsResDir.pathAppended(effectName).pathAppended(value).toString(); + } else { + QDir dir(m_qenPath); + dir.cdUp(); + QString absPath = dir.absoluteFilePath(filePath); + absPath = QDir::cleanPath(absPath); + absPath = QUrl::fromLocalFile(absPath).toString(); + return absPath; + } } // Validation and setting values @@ -300,7 +309,7 @@ Uniform::Type Uniform::typeFromString(const QString &typeString) return Uniform::Type::Vec4; else if (typeString == "color") return Uniform::Type::Color; - else if (typeString == "image") + else if (typeString == "sampler2D" || typeString == "image") //TODO: change image to sample2D in all QENs return Uniform::Type::Sampler; else if (typeString == "define") return Uniform::Type::Define; diff --git a/src/plugins/effectmakernew/uniform.h b/src/plugins/effectmakernew/uniform.h index 8f14383bb4c..f5731af00a8 100644 --- a/src/plugins/effectmakernew/uniform.h +++ b/src/plugins/effectmakernew/uniform.h @@ -40,7 +40,7 @@ public: Define }; - Uniform(const QJsonObject &props, const QString &qenPath); + Uniform(const QString &effectName, const QJsonObject &props, const QString &qenPath); Type type() const; QString typeName() const; @@ -78,7 +78,7 @@ signals: private: QString mipmapPropertyName(const QString &name) const; bool getBoolValue(const QJsonValue &jsonValue, bool defaultValue); - QString getResourcePath(const QString &value) const; + QString getResourcePath(const QString &effectName, const QString &value, const QString &qenPath) const; void setValueData(const QString &value, const QString &defaultValue, const QString &minValue, const QString &maxValue); diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 48d10a34d20..a141c696978 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -13,6 +13,9 @@ #include "qmldesignerplugin.h" #include "theme.h" +#include <extensionsystem/pluginmanager.h> +#include <extensionsystem/pluginspec.h> + #include <studioquickwidget.h> #include <coreplugin/fileutils.h> @@ -364,9 +367,22 @@ QSet<QString> AssetsLibraryWidget::supportedAssetSuffixes(bool complex) return suffixes; } +bool isEffectMakerActivated() +{ + const QVector<ExtensionSystem::PluginSpec *> specs = ExtensionSystem::PluginManager::plugins(); + return std::find_if(specs.begin(), specs.end(), + [](ExtensionSystem::PluginSpec *spec) { + return spec->name() == "EffectMakerNew" && spec->isEffectivelyEnabled(); + }) + != specs.end(); +} + void AssetsLibraryWidget::openEffectMaker(const QString &filePath) { - ModelNodeOperations::openEffectMaker(filePath); + if (isEffectMakerActivated()) + m_assetsView->emitCustomNotification("open_effectmaker_composition", {}, {filePath}); + else + ModelNodeOperations::openEffectMaker(filePath); } QString AssetsLibraryWidget::qmlSourcesPath() |