aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmr Essam <[email protected]>2023-11-14 14:49:42 +0200
committerAmr Elsayed <[email protected]>2023-11-15 14:29:52 +0000
commit306ce4ab35086dc90165433d09b53e11435b7b30 (patch)
treea24617ff27063b20f83fe3a73fc6bc57063a4ba6
parentde4c871655afb116bead829599ef55d42f3b0b40 (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.qml1
-rw-r--r--share/qtcreator/qmldesigner/effectMakerQmlSources/SaveDialog.qml2
-rw-r--r--src/plugins/effectmakernew/compositionnode.cpp53
-rw-r--r--src/plugins/effectmakernew/compositionnode.h5
-rw-r--r--src/plugins/effectmakernew/effectmakermodel.cpp139
-rw-r--r--src/plugins/effectmakernew/effectmakermodel.h14
-rw-r--r--src/plugins/effectmakernew/effectmakerview.cpp13
-rw-r--r--src/plugins/effectmakernew/uniform.cpp31
-rw-r--r--src/plugins/effectmakernew/uniform.h4
-rw-r--r--src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp18
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()