QmlDesigner: Export effect maker composition qep project into assets

Meanwhile, composition resources are not yet exported

Task-number: QDS-10500
Change-Id: I3687d1d62a64472c7ec84716c584dced5fbb6a85
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
This commit is contained in:
Amr Essam
2023-11-08 16:12:35 +02:00
committed by Amr Elsayed
parent a923d3ca4a
commit 0cbda147a8
8 changed files with 203 additions and 5 deletions

View File

@@ -19,7 +19,7 @@ Item {
SaveDialog { SaveDialog {
id: saveDialog id: saveDialog
anchors.centerIn: parent anchors.centerIn: parent
onAccepted: print("TODO: export and save effect files") onAccepted: EffectMakerBackend.effectMakerModel.exportComposition(saveDialog.compositionName)
} }
Column { Column {

View File

@@ -17,8 +17,10 @@ StudioControls.Dialog {
modal: true modal: true
implicitWidth: 250 implicitWidth: 250
property string compositionName: null
onOpened: { onOpened: {
nameText.text = "" nameText.text = "" //TODO: Generate unique name
emptyText.opacity = 0 emptyText.opacity = 0
nameText.forceActiveFocus() nameText.forceActiveFocus()
} }
@@ -47,7 +49,9 @@ StudioControls.Dialog {
validator: validator validator: validator
onTextChanged: { onTextChanged: {
emptyText.opacity = nameText.text === "" ? 1 : 0 let validator = /^[A-Z]\w{2,}[A-Za-z0-9_]*$/
emptyText.visible = text.length > 0 && !validator.test(text)
btnSave.enabled = !emptyText.visible
} }
Keys.onEnterPressed: btnSave.onClicked() Keys.onEnterPressed: btnSave.onClicked()
Keys.onReturnPressed: btnSave.onClicked() Keys.onReturnPressed: btnSave.onClicked()
@@ -76,7 +80,10 @@ StudioControls.Dialog {
text: qsTr("Save") text: qsTr("Save")
enabled: nameText.text !== "" enabled: nameText.text !== ""
onClicked: root.accept() onClicked: {
root.compositionName = nameText.text
root.accept() //TODO: Check if name is unique
}
} }
HelperWidgets.Button { HelperWidgets.Button {

View File

@@ -104,6 +104,7 @@ void CompositionNode::parse(const QString &qenPath)
for (const auto /*QJsonValueRef*/ &prop : jsonProps) { for (const auto /*QJsonValueRef*/ &prop : jsonProps) {
const auto uniform = new Uniform(prop.toObject(), qenPath); const auto uniform = new Uniform(prop.toObject(), qenPath);
m_unifomrsModel.addUniform(uniform); m_unifomrsModel.addUniform(uniform);
m_uniforms.append(uniform);
g_propertyData.insert(uniform->name(), uniform->value()); g_propertyData.insert(uniform->name(), uniform->value());
} }
@@ -123,5 +124,15 @@ void CompositionNode::parse(const QString &qenPath)
} }
} }
QList<Uniform *> CompositionNode::uniforms() const
{
return m_uniforms;
}
QString CompositionNode::name() const
{
return m_name;
}
} // namespace EffectMaker } // namespace EffectMaker

View File

@@ -39,6 +39,10 @@ public:
bool isEnabled() const; bool isEnabled() const;
void setIsEnabled(bool newIsEnabled); void setIsEnabled(bool newIsEnabled);
QString name() const;
QList<Uniform *> uniforms() const;
signals: signals:
void uniformsModelChanged(); void uniformsModelChanged();
void isEnabledChanged(); void isEnabledChanged();
@@ -54,6 +58,8 @@ private:
QStringList m_requiredNodes; QStringList m_requiredNodes;
bool m_isEnabled = true; bool m_isEnabled = true;
QList<Uniform*> m_uniforms;
EffectMakerUniformsModel m_unifomrsModel; EffectMakerUniformsModel m_unifomrsModel;
}; };

View File

@@ -18,6 +18,8 @@
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/process.h> #include <utils/process.h>
#include <modelnodeoperations.h>
#include <QByteArrayView> #include <QByteArrayView>
#include <QVector2D> #include <QVector2D>
@@ -355,6 +357,170 @@ void EffectMakerModel::setEffectError(const QString &errorMessage, int type, int
Q_EMIT effectErrorChanged(); Q_EMIT effectErrorChanged();
} }
QString variantAsDataString(const Uniform::Type type, const QVariant &variant)
{
QString s;
switch (type) {
case Uniform::Type::Bool:
s = variant.toBool() ? QString("true") : QString("false");
break;
case Uniform::Type::Int:
s = QString::number(variant.toInt());
break;
case Uniform::Type::Float:
s = QString::number(variant.toDouble());
break;
case Uniform::Type::Vec2: {
QStringList list;
QVector2D v2 = variant.value<QVector2D>();
list << QString::number(v2.x());
list << QString::number(v2.y());
s = list.join(", ");
break;
}
case Uniform::Type::Vec3: {
QStringList list;
QVector3D v3 = variant.value<QVector3D>();
list << QString::number(v3.x());
list << QString::number(v3.y());
list << QString::number(v3.z());
s = list.join(", ");
break;
}
case Uniform::Type::Vec4: {
QStringList list;
QVector4D v4 = variant.value<QVector4D>();
list << QString::number(v4.x());
list << QString::number(v4.y());
list << QString::number(v4.z());
list << QString::number(v4.w());
s = list.join(", ");
break;
}
case Uniform::Type::Color: {
QStringList list;
QColor c = variant.value<QColor>();
list << QString::number(c.redF(), 'g', 3);
list << QString::number(c.greenF(), 'g', 3);
list << QString::number(c.blueF(), 'g', 3);
list << QString::number(c.alphaF(), 'g', 3);
s = list.join(", ");
break;
}
case Uniform::Type::Sampler:
case Uniform::Type::Define: {
s = variant.toString();
break;
}
}
return s;
}
QJsonObject nodeToJson(const CompositionNode &node)
{
QJsonObject nodeObject;
nodeObject.insert("name", node.name());
if (!node.description().isEmpty())
nodeObject.insert("description", node.description());
nodeObject.insert("enabled", node.isEnabled());
nodeObject.insert("version", 1);
// Add properties
QJsonArray propertiesArray;
const QList<Uniform *> uniforms = node.uniforms();
for (const Uniform *uniform : uniforms) {
QJsonObject uniformObject;
uniformObject.insert("name", QString(uniform->name()));
QString type = Uniform::stringFromType(uniform->type());
uniformObject.insert("type", type);
QString value = variantAsDataString(uniform->type(), uniform->value());
if (uniform->type() == Uniform::Type::Sampler)
value = QFileInfo(value).fileName();
uniformObject.insert("value", value);
QString defaultValue = variantAsDataString(uniform->type(), uniform->defaultValue());
if (uniform->type() == Uniform::Type::Sampler) {
defaultValue = QFileInfo(value).fileName();
if (uniform->enableMipmap())
uniformObject.insert("enableMipmap", uniform->enableMipmap());
}
uniformObject.insert("defaultValue", defaultValue);
if (!uniform->description().isEmpty())
uniformObject.insert("description", uniform->description());
if (uniform->type() == Uniform::Type::Float
|| uniform->type() == Uniform::Type::Int
|| uniform->type() == Uniform::Type::Vec2
|| uniform->type() == Uniform::Type::Vec3
|| uniform->type() == Uniform::Type::Vec4) {
uniformObject.insert("minValue", variantAsDataString(uniform->type(), uniform->minValue()));
uniformObject.insert("maxValue", variantAsDataString(uniform->type(), uniform->maxValue()));
}
if (!uniform->customValue().isEmpty())
uniformObject.insert("customValue", uniform->customValue());
if (uniform->useCustomValue())
uniformObject.insert("useCustomValue", true);
propertiesArray.append(uniformObject);
}
if (!propertiesArray.isEmpty())
nodeObject.insert("properties", propertiesArray);
// Add shaders
if (!node.fragmentCode().trimmed().isEmpty()) {
QJsonArray fragmentCodeArray;
const QStringList fsLines = node.fragmentCode().split('\n');
for (const QString &line : fsLines)
fragmentCodeArray.append(line);
if (!fragmentCodeArray.isEmpty())
nodeObject.insert("fragmentCode", fragmentCodeArray);
}
if (!node.vertexCode().trimmed().isEmpty()) {
QJsonArray vertexCodeArray;
const QStringList vsLines = node.vertexCode().split('\n');
for (const QString &line : vsLines)
vertexCodeArray.append(line);
if (!vertexCodeArray.isEmpty())
nodeObject.insert("vertexCode", vertexCodeArray);
}
return nodeObject;
}
void EffectMakerModel::exportComposition(const QString &name)
{
const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
const QString path = effectsAssetsDir + QDir::separator() + name + ".qep";
auto saveFile = QFile(path);
if (!saveFile.open(QIODevice::WriteOnly)) {
QString error = QString("Error: Couldn't save composition file: '%1'").arg(path);
qWarning() << error;
return;
}
QJsonObject json;
// File format version
json.insert("version", 1);
// Add nodes
QJsonArray nodesArray;
for (const CompositionNode *node : std::as_const(m_nodes)) {
QJsonObject nodeObject = nodeToJson(*node);
nodesArray.append(nodeObject);
}
if (!nodesArray.isEmpty())
json.insert("nodes", nodesArray);
QJsonObject rootJson;
rootJson.insert("QEP", json);
QJsonDocument jsonDoc(rootJson);
saveFile.write(jsonDoc.toJson());
saveFile.close();
}
void EffectMakerModel::resetEffectError(int type) void EffectMakerModel::resetEffectError(int type)
{ {
if (m_effectErrors.contains(type)) { if (m_effectErrors.contains(type)) {

View File

@@ -83,6 +83,8 @@ public:
Q_INVOKABLE void resetEffectError(int type); Q_INVOKABLE void resetEffectError(int type);
Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1);
Q_INVOKABLE void exportComposition(const QString &name);
signals: signals:
void isEmptyChanged(); void isEmptyChanged();
void selectedIndexChanged(int idx); void selectedIndexChanged(int idx);

View File

@@ -1683,6 +1683,12 @@ Utils::FilePath getEffectsImportDirectory()
QString getEffectsDefaultDirectory(const QString &defaultDir) QString getEffectsDefaultDirectory(const QString &defaultDir)
{ {
if (defaultDir.isEmpty()) {
return Utils::FilePath::fromString(getAssetDefaultDirectory(
"effects",
QmlDesignerPlugin::instance()->documentManager().currentProjectDirPath().toString())).toString();
}
return getAssetDefaultDirectory("effects", defaultDir); return getAssetDefaultDirectory("effects", defaultDir);
} }

View File

@@ -125,7 +125,7 @@ void openSignalDialog(const SelectionContext &selectionContext);
void updateImported3DAsset(const SelectionContext &selectionContext); void updateImported3DAsset(const SelectionContext &selectionContext);
QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getEffectsImportDirectory(); QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getEffectsImportDirectory();
QMLDESIGNERCOMPONENTS_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir); QMLDESIGNERCOMPONENTS_EXPORT QString getEffectsDefaultDirectory(const QString &defaultDir = {});
void openEffectMaker(const QString &filePath); void openEffectMaker(const QString &filePath);
QString getEffectIcon(const QString &effectPath); QString getEffectIcon(const QString &effectPath);
bool useLayerEffect(); bool useLayerEffect();