EffectComposer: Delete obsolete resource files at effect save

If a new version of same effect is saved, remove files that are
no longer part of the effect from the effect import dir after the save.

Fixes: QDS-11737
Change-Id: Iae4da39f9f4713c2e26f1b90263881c8c9e13d78
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Miikka Heikkinen
2024-02-05 14:59:46 +02:00
parent 9201491292
commit ca9e72fe6c
13 changed files with 109 additions and 15 deletions

View File

@@ -678,6 +678,12 @@ R"(
void EffectComposerModel::saveComposition(const QString &name)
{
if (name.isEmpty() || name.size() < 3 || name[0].isLower()) {
QString error = QString("Error: Couldn't save composition '%1', name is invalid").arg(name);
qWarning() << error;
return;
}
const QString effectsAssetsDir = QmlDesigner::ModelNodeOperations::getEffectsDefaultDirectory();
const QString path = effectsAssetsDir + QDir::separator() + name + ".qep";
auto saveFile = QFile(path);
@@ -708,9 +714,9 @@ void EffectComposerModel::saveComposition(const QString &name)
saveFile.write(jsonDoc.toJson());
saveFile.close();
setCurrentComposition(name);
setHasUnsavedChanges(false);
saveResources(name);
setHasUnsavedChanges(false);
}
void EffectComposerModel::openComposition(const QString &path)
@@ -808,24 +814,31 @@ void EffectComposerModel::saveResources(const QString &name)
// Get effects dir
const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory();
const QString effectsResPath = effectsResDir.pathAppended(name).toString() + QDir::separator();
Utils::FilePath effectPath = Utils::FilePath::fromString(effectsResPath);
// Create the qmldir for effects
Utils::FilePath qmldirPath = effectsResDir.resolvePath(QStringLiteral("qmldir"));
QString qmldirFileName("qmldir");
Utils::FilePath qmldirPath = effectsResDir.resolvePath(qmldirFileName);
QString qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray()));
if (qmldirContent.isEmpty()) {
qmldirContent.append("module Effects\n");
qmldirPath.writeFileContents(qmldirContent.toUtf8());
}
Utils::FilePaths oldFiles;
QStringList newFileNames;
// Create effect folder if not created
Utils::FilePath effectPath = Utils::FilePath::fromString(effectsResPath);
if (!effectPath.exists()) {
QDir effectDir(effectsResDir.toString());
effectDir.mkdir(name);
} else {
oldFiles = effectPath.dirEntries(QDir::Files);
}
// Create effect qmldir
qmldirPath = effectPath.resolvePath(QStringLiteral("qmldir"));
newFileNames.append(qmldirFileName);
qmldirPath = effectPath.resolvePath(qmldirFileName);
qmldirContent = QString::fromUtf8(qmldirPath.fileContents().value_or(QByteArray()));
if (qmldirContent.isEmpty()) {
qmldirContent.append("module Effects.");
@@ -857,13 +870,16 @@ void EffectComposerModel::saveResources(const QString &name)
const QString qmlString = qmlStringList.join('\n');
QString qmlFilePath = effectsResPath + qmlFilename;
writeToFile(qmlString.toUtf8(), qmlFilePath, FileType::Text);
newFileNames.append(qmlFilename);
// Save shaders and images
QStringList sources = {m_vertexShaderFilename, m_fragmentShaderFilename};
QStringList dests = {vsFilename, fsFilename};
QHash<QString, Uniform *> fileNameToUniformHash;
const QList<Uniform *> uniforms = allUniforms();
for (const Uniform *uniform : uniforms) {
bool hasSampler = false;
for (Uniform *uniform : uniforms) {
if (uniform->type() == Uniform::Type::Sampler && !uniform->value().toString().isEmpty()) {
QString imagePath = uniform->value().toString();
QFileInfo fi(imagePath);
@@ -874,6 +890,8 @@ void EffectComposerModel::saveResources(const QString &name)
}
sources.append(imagePath);
dests.append(imageFilename);
fileNameToUniformHash.insert(imageFilename, uniform);
hasSampler = true;
}
}
@@ -893,11 +911,30 @@ void EffectComposerModel::saveResources(const QString &name)
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]);
newFileNames.append(target.fileName());
if (target.exists() && source.fileName() != target.fileName())
target.removeFile(); // Remove existing file for update
if (!source.copyFile(target))
qWarning() << __FUNCTION__ << " Failed to copy file: " << source;
if (fileNameToUniformHash.contains(dests[i])) {
Uniform *uniform = fileNameToUniformHash[dests[i]];
const QVariant newValue = target.toString();
uniform->setDefaultValue(newValue);
uniform->setValue(newValue);
}
}
// Delete old content that was not overwritten
for (const Utils::FilePath &oldFile : oldFiles) {
if (!newFileNames.contains(oldFile.fileName()))
oldFile.removeFile();
}
// Refresh UI to update sampler UrlChoosers
if (hasSampler) {
beginResetModel();
endResetModel();
}
emit resourcesSaved(QString("Effects.%1.%1").arg(name).toUtf8(), effectPath);

View File

@@ -68,7 +68,10 @@ void EffectComposerNodesModel::loadModel()
QDirIterator itEffects(categoryPath.toString(), {"*.qen"}, QDir::Files);
while (itEffects.hasNext()) {
itEffects.next();
effects.push_back(new EffectNode(itEffects.filePath()));
auto node = new EffectNode(itEffects.filePath());
if (!node->defaultImagesHash().isEmpty())
m_defaultImagesHash.insert(node->name(), node->defaultImagesHash());
effects.push_back(node);
}
catName[0] = catName[0].toUpper(); // capitalize first letter
@@ -108,4 +111,9 @@ void EffectComposerNodesModel::updateCanBeAdded(const QStringList &uniforms)
}
}
QHash<QString, QString> EffectComposerNodesModel::defaultImagesForNode(const QString &name) const
{
return m_defaultImagesHash.value(name);
}
} // namespace EffectComposer

View File

@@ -32,12 +32,15 @@ public:
void updateCanBeAdded(const QStringList &uniforms);
QHash<QString, QString> defaultImagesForNode(const QString &name) const;
private:
QString nodesSourcesPath() const;
QList<EffectNodesCategory *> m_categories;
bool m_probeNodesDir = false;
bool m_modelLoaded = false;
QHash<QString, QHash<QString, QString>> m_defaultImagesHash;
};
} // namespace EffectComposer

View File

@@ -19,6 +19,7 @@ QHash<int, QByteArray> EffectComposerUniformsModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "uniformName";
roles[DisplayNameRole] = "uniformDisplayName";
roles[DescriptionRole] = "uniformDescription";
roles[ValueRole] = "uniformValue";
roles[BackendValueRole] = "uniformBackendValue";

View File

@@ -30,6 +30,7 @@ public:
private:
enum Roles {
NameRole = Qt::UserRole + 1,
DisplayNameRole,
DescriptionRole,
ValueRole,
BackendValueRole,

View File

@@ -188,6 +188,11 @@ QPoint EffectComposerWidget::globalPos(const QPoint &point) const
return point;
}
QString EffectComposerWidget::uniformDefaultImage(const QString &nodeName, const QString &uniformName) const
{
return m_effectComposerNodesModel->defaultImagesForNode(nodeName).value(uniformName);
}
QSize EffectComposerWidget::sizeHint() const
{
return {420, 420};

View File

@@ -50,6 +50,8 @@ public:
Q_INVOKABLE void doOpenComposition();
Q_INVOKABLE QRect screenRect() const;
Q_INVOKABLE QPoint globalPos(const QPoint &point) const;
Q_INVOKABLE QString uniformDefaultImage(const QString &nodeName,
const QString &uniformName) const;
QSize sizeHint() const override;

View File

@@ -31,8 +31,13 @@ EffectNode::EffectNode(const QString &qenPath)
m_description = node.description();
const QList<Uniform *> uniforms = node.uniforms();
for (const Uniform *uniform : uniforms)
for (const Uniform *uniform : uniforms) {
m_uniformNames.insert(uniform->name());
if (uniform->type() == Uniform::Type::Sampler) {
m_defaultImagesHash.insert(
uniform->name(), uniform->defaultValue().toString());
}
}
}
QString EffectNode::name() const

View File

@@ -25,6 +25,7 @@ public:
QString name() const;
QString description() const;
QString qenPath() const;
QHash<QString, QString> defaultImagesHash() const { return m_defaultImagesHash; }
void setCanBeAdded(bool enabled);
@@ -40,6 +41,7 @@ private:
QUrl m_iconPath;
bool m_canBeAdded = true;
QSet<QString> m_uniformNames;
QHash<QString, QString> m_defaultImagesHash;
};
} // namespace EffectComposer

View File

@@ -102,6 +102,14 @@ void Uniform::setValue(const QVariant &newValue)
}
}
void Uniform::setDefaultValue(const QVariant &newValue)
{
if (m_defaultValue != newValue) {
m_defaultValue = newValue;
emit uniformDefaultValueChanged();
}
}
QVariant Uniform::defaultValue() const
{
return m_defaultValue;

View File

@@ -18,7 +18,8 @@ class Uniform : public QObject
{
Q_OBJECT
Q_PROPERTY(QString uniformName MEMBER m_displayName CONSTANT)
Q_PROPERTY(QString uniformName MEMBER m_name CONSTANT)
Q_PROPERTY(QString uniformDisplayName MEMBER m_displayName CONSTANT)
Q_PROPERTY(QString uniformType READ typeName CONSTANT)
Q_PROPERTY(QString uniformControlType READ controlTypeName CONSTANT)
Q_PROPERTY(QString uniformDescription READ description CONSTANT)
@@ -26,7 +27,7 @@ class Uniform : public QObject
Q_PROPERTY(QVariant uniformBackendValue READ backendValue NOTIFY uniformBackendValueChanged)
Q_PROPERTY(QVariant uniformMinValue MEMBER m_minValue CONSTANT)
Q_PROPERTY(QVariant uniformMaxValue MEMBER m_maxValue CONSTANT)
Q_PROPERTY(QVariant uniformDefaultValue MEMBER m_defaultValue CONSTANT)
Q_PROPERTY(QVariant uniformDefaultValue MEMBER m_defaultValue NOTIFY uniformDefaultValueChanged)
Q_PROPERTY(QVariant uniformUseCustomValue MEMBER m_useCustomValue CONSTANT)
public:
@@ -52,6 +53,7 @@ public:
QVariant value() const;
void setValue(const QVariant &newValue);
void setDefaultValue(const QVariant &newValue);
QVariant backendValue() const;
@@ -80,6 +82,7 @@ public:
signals:
void uniformValueChanged();
void uniformBackendValueChanged();
void uniformDefaultValueChanged();
private:
QString mipmapPropertyName(const QString &name) const;