forked from qt-creator/qt-creator
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:
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user