EffectMaker: Improve adding and saving compositions

Add and implement 2 icons for adding and saving compositions.

Fixes: QDS-11511
Change-Id: I113eeb81ea05fc6db9019d95d476bc0fe20b409f
Reviewed-by: Qt CI Patch Build Bot <ci_patchbuild_bot@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
Mahmoud Badri
2023-12-08 14:58:04 +02:00
parent 4ab995da59
commit cc07031cd6
7 changed files with 153 additions and 17 deletions

View File

@@ -16,24 +16,45 @@ Item {
property int moveToIdx: 0
property bool previewAnimationRunning: false
SaveDialog {
SaveAsDialog {
id: saveDialog
compositionName: EffectMakerBackend.effectMakerModel.currentComposition
anchors.centerIn: parent
onAccepted: {
let name = saveDialog.compositionName
EffectMakerBackend.effectMakerModel.exportComposition(name)
EffectMakerBackend.effectMakerModel.exportResources(name)
EffectMakerBackend.effectMakerModel.saveComposition(name)
}
}
SaveChangesDialog {
id: saveChangesDialog
anchors.centerIn: parent
onAccepted: EffectMakerBackend.effectMakerModel.clear()
}
Column {
id: col
anchors.fill: parent
spacing: 1
EffectMakerTopBar {
onSaveClicked: saveDialog.open()
onAddClicked: {
if (EffectMakerBackend.effectMakerModel.hasUnsavedChanges)
saveChangesDialog.open()
else
EffectMakerBackend.effectMakerModel.clear()
}
onSaveClicked: {
let name = EffectMakerBackend.effectMakerModel.currentComposition
if (name === "")
saveDialog.open()
else
EffectMakerBackend.effectMakerModel.saveComposition(name)
}
}
EffectMakerPreview {

View File

@@ -15,13 +15,29 @@ Rectangle {
height: StudioTheme.Values.toolbarHeight
color: StudioTheme.Values.themeToolbarBackground
signal addClicked
signal saveClicked
HelperWidgets.Button {
HelperWidgets.AbstractButton {
id: addButton
anchors.verticalCenter: parent.verticalCenter
x: 5
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.add_medium
tooltip: qsTr("Add new composition")
text: qsTr("Save in Library")
onClicked: root.addClicked()
}
HelperWidgets.AbstractButton {
anchors.verticalCenter: parent.verticalCenter
x: addButton.x + addButton.width + 5
style: StudioTheme.Values.viewBarButtonStyle
buttonIcon: StudioTheme.Constants.save_medium
tooltip: qsTr("Save current composition")
enabled: EffectMakerBackend.effectMakerModel.hasUnsavedChanges
|| EffectMakerBackend.effectMakerModel.currentComposition === ""
onClicked: root.saveClicked()
}

View File

@@ -6,7 +6,7 @@ import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
import AssetsLibraryBackend
import EffectMakerBackend
StudioControls.Dialog {
id: root
@@ -21,7 +21,7 @@ StudioControls.Dialog {
property string compositionName: ""
onOpened: {
nameText.text = compositionName //TODO: Generate unique name
nameText.text = EffectMakerBackend.effectMakerModel.currentComposition
emptyText.text = ""
nameText.forceActiveFocus()
}

View File

@@ -0,0 +1,62 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
import EffectMakerBackend
StudioControls.Dialog {
id: root
title: qsTr("Save Changes")
closePolicy: Popup.CloseOnEscape
modal: true
implicitWidth: 250
implicitHeight: 140
contentItem: Column {
spacing: 35
Text {
text: qsTr("Current composition has unsaved changes.")
color: StudioTheme.Values.themeTextColor
}
Row {
anchors.right: parent.right
spacing: 2
HelperWidgets.Button {
id: btnSave
width: 70
text: qsTr("Save")
onClicked: {
if (btnSave.enabled) {
let name = EffectMakerBackend.effectMakerModel.currentComposition
EffectMakerBackend.effectMakerModel.saveComposition(name)
root.accept()
}
}
}
HelperWidgets.Button {
id: btnDontSave
width: 70
text: qsTr("Don't Save")
onClicked: root.accept()
}
HelperWidgets.Button {
width: 70
text: qsTr("Cancel")
onClicked: root.reject()
}
}
}
}

View File

@@ -106,12 +106,17 @@ void EffectMakerModel::addNode(const QString &nodeQenPath)
{
beginInsertRows({}, m_nodes.size(), m_nodes.size());
auto *node = new CompositionNode("", nodeQenPath);
connect(qobject_cast<EffectMakerUniformsModel *>(node->uniformsModel()),
&EffectMakerUniformsModel::dataChanged, this, [this] {
setHasUnsavedChanges(true);
});
m_nodes.append(node);
endInsertRows();
setIsEmpty(false);
bakeShaders();
setHasUnsavedChanges(true);
emit nodesChanged();
}
@@ -126,6 +131,7 @@ void EffectMakerModel::moveNode(int fromIdx, int toIdx)
m_nodes.move(fromIdx, toIdx);
endMoveRows();
setHasUnsavedChanges(true);
bakeShaders();
}
@@ -141,6 +147,7 @@ void EffectMakerModel::removeNode(int idx)
else
bakeShaders();
setHasUnsavedChanges(true);
emit nodesChanged();
}
@@ -150,6 +157,7 @@ void EffectMakerModel::clear()
qDeleteAll(m_nodes);
m_nodes.clear();
endResetModel();
setHasUnsavedChanges(!m_currentComposition.isEmpty());
setCurrentComposition("");
setIsEmpty(true);
@@ -528,7 +536,7 @@ QString EffectMakerModel::getQmlEffectString()
return s;
}
void EffectMakerModel::exportComposition(const QString &name)
void EffectMakerModel::saveComposition(const QString &name)
{
const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
const QString path = effectsAssetsDir + QDir::separator() + name + ".qep";
@@ -559,6 +567,10 @@ void EffectMakerModel::exportComposition(const QString &name)
saveFile.write(jsonDoc.toJson());
saveFile.close();
setCurrentComposition(name);
setHasUnsavedChanges(false);
saveResources(name);
}
void EffectMakerModel::openComposition(const QString &path)
@@ -615,6 +627,10 @@ void EffectMakerModel::openComposition(const QString &path)
for (const auto &nodeElement : nodesArray) {
beginInsertRows({}, m_nodes.size(), m_nodes.size());
auto *node = new CompositionNode(effectName, "", nodeElement.toObject());
connect(qobject_cast<EffectMakerUniformsModel *>(node->uniformsModel()),
&EffectMakerUniformsModel::dataChanged, this, [this] {
setHasUnsavedChanges(true);
});
m_nodes.append(node);
endInsertRows();
}
@@ -623,10 +639,11 @@ void EffectMakerModel::openComposition(const QString &path)
bakeShaders();
}
setHasUnsavedChanges(false);
emit nodesChanged();
}
void EffectMakerModel::exportResources(const QString &name)
void EffectMakerModel::saveResources(const QString &name)
{
// Make sure that uniforms are up-to-date
updateCustomUniforms();
@@ -692,7 +709,7 @@ void EffectMakerModel::exportResources(const QString &name)
QString qmlFilePath = effectsResPath + qmlFilename;
writeToFile(qmlString.toUtf8(), qmlFilePath, FileType::Text);
// Export shaders and images
// Save shaders and images
QStringList sources = {m_vertexShaderFilename, m_fragmentShaderFilename};
QStringList dests = {vsFilename, fsFilename};
@@ -721,7 +738,7 @@ void EffectMakerModel::exportResources(const QString &name)
qWarning() << __FUNCTION__ << " Failed to copy file: " << source;
}
emit resourcesExported(QString("Effects.%1.%1").arg(name).toUtf8(), effectPath);
emit resourcesSaved(QString("Effects.%1.%1").arg(name).toUtf8(), effectPath);
}
void EffectMakerModel::resetEffectError(int type)
@@ -1440,10 +1457,25 @@ void EffectMakerModel::setCurrentComposition(const QString &newCurrentCompositio
{
if (m_currentComposition == newCurrentComposition)
return;
m_currentComposition = newCurrentComposition;
emit currentCompositionChanged();
}
bool EffectMakerModel::hasUnsavedChanges() const
{
return m_hasUnsavedChanges;
}
void EffectMakerModel::setHasUnsavedChanges(bool val)
{
if (m_hasUnsavedChanges == val)
return;
m_hasUnsavedChanges = val;
emit hasUnsavedChangesChanged();
}
QStringList EffectMakerModel::uniformNames() const
{
QStringList usedList;

View File

@@ -44,6 +44,7 @@ class EffectMakerModel : public QAbstractListModel
Q_PROPERTY(bool isEmpty MEMBER m_isEmpty NOTIFY isEmptyChanged)
Q_PROPERTY(int selectedIndex MEMBER m_selectedIndex NOTIFY selectedIndexChanged)
Q_PROPERTY(bool hasUnsavedChanges MEMBER m_hasUnsavedChanges WRITE setHasUnsavedChanges NOTIFY hasUnsavedChangesChanged)
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)
@@ -81,14 +82,16 @@ public:
Q_INVOKABLE void resetEffectError(int type);
Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1);
Q_INVOKABLE void exportComposition(const QString &name);
Q_INVOKABLE void exportResources(const QString &name);
Q_INVOKABLE void saveComposition(const QString &name);
void openComposition(const QString &path);
QString currentComposition() const;
void setCurrentComposition(const QString &newCurrentComposition);
bool hasUnsavedChanges() const;
void setHasUnsavedChanges(bool val);
QStringList uniformNames() const;
signals:
@@ -97,10 +100,10 @@ signals:
void effectErrorChanged();
void shadersUpToDateChanged();
void shadersBaked();
void currentCompositionChanged();
void nodesChanged();
void resourcesExported(const QByteArray &type, const Utils::FilePath &path);
void resourcesSaved(const QByteArray &type, const Utils::FilePath &path);
void hasUnsavedChangesChanged();
private:
enum Roles {
@@ -152,6 +155,7 @@ private:
void updateCustomUniforms();
void createFiles();
void bakeShaders();
void saveResources(const QString &name);
QString mipmapPropertyName(const QString &name) const;
QString getQmlImagesString(bool localFiles);
@@ -161,6 +165,7 @@ private:
int m_selectedIndex = -1;
bool m_isEmpty = true;
bool m_hasUnsavedChanges = false;
// True when shaders haven't changed since last baking
bool m_shadersUpToDate = true;
int m_remainingQsbTargets = 0;

View File

@@ -86,7 +86,7 @@ EffectMakerWidget::EffectMakerWidget(EffectMakerView *view)
m_effectMakerNodesModel->updateCanBeAdded(m_effectMakerModel->uniformNames());
});
connect(m_effectMakerModel.data(), &EffectMakerModel::resourcesExported,
connect(m_effectMakerModel.data(), &EffectMakerModel::resourcesSaved,
this, [this](const QmlDesigner::TypeName &type, const Utils::FilePath &path) {
if (!m_importScan.timer) {
m_importScan.timer = new QTimer(this);