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