QmlDesigner: Create menu for Effects creation in the Asset Library

- Add menu item for effect creation instead of new file dialog
- New effect dialog with validating qml file name with qml naming conventions
- Open Effect Maker automatically when an effect is created

Task-number: QDS-8490
Task-number: QDS-8578
Change-Id: I04b075a0b283318906f309c7d394eda48577ae74
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Amr Essam
2022-12-19 11:35:21 +02:00
committed by Amr Elsayed
parent 90b8e482c9
commit 08d4eab619
4 changed files with 182 additions and 23 deletions

View File

@@ -183,4 +183,16 @@ StudioControls.Menu {
} }
} }
} }
StudioControls.MenuItem {
text: qsTr("New Effect")
NewEffectDialog {
id: newEffectDialog
parent: root.assetsView
dirPath: root.__dirPath
}
onTriggered: newEffectDialog.open()
}
} }

View File

@@ -0,0 +1,105 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
import HelperWidgets as HelperWidgets
import StudioControls as StudioControls
import StudioTheme as StudioTheme
Dialog {
id: root
title: qsTr("Create New Effect")
anchors.centerIn: parent
closePolicy: Popup.CloseOnEscape
modal: true
required property string dirPath
readonly property int __maxPath: 32
HelperWidgets.RegExpValidator {
id: effectNameValidator
regExp: /^[A-Z]\w[A-Za-z0-9_]*$/
}
ErrorDialog {
id: creationFailedDialog
title: qsTr("Could not create effect")
message: qsTr("An error occurred while trying to create the effect.")
}
contentItem: Column {
spacing: 2
Row {
Text {
text: qsTr("Effect name: ")
anchors.verticalCenter: parent.verticalCenter
color: StudioTheme.Values.themeTextColor
}
StudioControls.TextField {
id: effectName
actionIndicator.visible: false
translationIndicator.visible: false
validator: effectNameValidator
Keys.onEnterPressed: btnCreate.onClicked()
Keys.onReturnPressed: btnCreate.onClicked()
}
}
Text {
text: qsTr("Effect name cannot be empty.")
color: "#ff0000"
anchors.right: parent.right
visible: effectName.text === ""
}
Text {
text: qsTr("Effect path is too long.")
color: "#ff0000"
anchors.right: parent.right
visible: effectName.text.length > root.__maxPath
}
Item { // spacer
width: 1
height: 20
}
Row {
anchors.right: parent.right
HelperWidgets.Button {
id: btnCreate
text: qsTr("Create")
enabled: effectName.text !== ""
&& effectName.length >=3
&& effectName.text.length <= root.__maxPath
onClicked: {
const path = assetsModel.getUniqueEffectPath(root.dirPath, effectName.text)
if (assetsModel.createNewEffect(path))
root.accept()
else
creationFailedDialog.open()
}
}
HelperWidgets.Button {
text: qsTr("Cancel")
onClicked: root.reject()
}
}
}
onOpened: {
const path = assetsModel.getUniqueEffectPath(root.dirPath, "Effect01")
effectName.text = path.split('/').pop().replace(".qep", '')
effectName.selectAll()
effectName.forceActiveFocus()
}
}

View File

@@ -134,32 +134,10 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString &
bool AssetsLibraryModel::addNewFolder(const QString &folderPath) bool AssetsLibraryModel::addNewFolder(const QString &folderPath)
{ {
QString iterPath = folderPath; QString iterPath = folderPath;
static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
QDir dir{folderPath}; QDir dir{folderPath};
while (dir.exists()) { while (dir.exists()) {
// if the folder name ends with a number, increment it iterPath = getUniqueName(iterPath);
QRegularExpressionMatch match = rgx.match(iterPath);
if (match.hasMatch()) { // ends with a number
QString numStr = match.captured(0);
int num = match.captured(0).toInt();
// get number of padding zeros, ex: for "005" = 2
int nPaddingZeros = 0;
for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros);
++num;
// if the incremented number's digits increased, decrease the padding zeros
if (std::fmod(std::log10(num), 1.0) == 0)
--nPaddingZeros;
iterPath = folderPath.mid(0, match.capturedStart())
+ QString('0').repeated(nPaddingZeros)
+ QString::number(num);
} else {
iterPath = folderPath + '1';
}
dir.setPath(iterPath); dir.setPath(iterPath);
} }
@@ -186,6 +164,36 @@ bool AssetsLibraryModel::allFilePathsAreImages(const QStringList &filePaths) con
}); });
} }
QString AssetsLibraryModel::getUniqueEffectPath(const QString &parentFolder, const QString &effectName)
{
auto genEffectPath = [=](const QString &name) {
return QString(parentFolder + "/" + name + ".qep");
};
QString uniqueName = effectName;
QString path = genEffectPath(uniqueName);
QFileInfo file{path};
while (file.exists()) {
uniqueName = getUniqueName(uniqueName);
path = genEffectPath(uniqueName);
file.setFile(path);
}
return path;
}
bool AssetsLibraryModel::createNewEffect(const QString &effectPath, bool openEffectMaker)
{
bool created = QFile(effectPath).open(QIODevice::WriteOnly);
if (created && openEffectMaker)
ModelNodeOperations::openEffectMaker(effectPath);
return created;
}
bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const bool AssetsLibraryModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{ {
QString path = m_sourceFsModel->filePath(sourceParent); QString path = m_sourceFsModel->filePath(sourceParent);
@@ -242,6 +250,36 @@ void AssetsLibraryModel::syncHaveFiles()
setHaveFiles(checkHaveFiles()); setHaveFiles(checkHaveFiles());
} }
QString AssetsLibraryModel::getUniqueName(const QString &oldName) {
static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
QString uniqueName = oldName;
// if the folder name ends with a number, increment it
QRegularExpressionMatch match = rgx.match(uniqueName);
if (match.hasMatch()) { // ends with a number
QString numStr = match.captured(0);
int num = match.captured(0).toInt();
// get number of padding zeros, ex: for "005" = 2
int nPaddingZeros = 0;
for (; nPaddingZeros < numStr.size() && numStr[nPaddingZeros] == '0'; ++nPaddingZeros);
++num;
// if the incremented number's digits increased, decrease the padding zeros
if (std::fmod(std::log10(num), 1.0) == 0)
--nPaddingZeros;
uniqueName = oldName.mid(0, match.capturedStart())
+ QString('0').repeated(nPaddingZeros)
+ QString::number(num);
} else {
uniqueName = oldName + '1';
}
return uniqueName;
}
void AssetsLibraryModel::setRootPath(const QString &newPath) void AssetsLibraryModel::setRootPath(const QString &newPath)
{ {
beginResetModel(); beginResetModel();

View File

@@ -47,6 +47,9 @@ public:
Q_INVOKABLE bool deleteFolderRecursively(const QModelIndex &folderIndex); Q_INVOKABLE bool deleteFolderRecursively(const QModelIndex &folderIndex);
Q_INVOKABLE bool allFilePathsAreImages(const QStringList &filePaths) const; Q_INVOKABLE bool allFilePathsAreImages(const QStringList &filePaths) const;
Q_INVOKABLE QString getUniqueEffectPath(const QString &parentFolder, const QString &effectName);
Q_INVOKABLE bool createNewEffect(const QString &effectPath, bool openEffectMaker = true);
int columnCount(const QModelIndex &parent = QModelIndex()) const override int columnCount(const QModelIndex &parent = QModelIndex()) const override
{ {
int result = QSortFilterProxyModel::columnCount(parent); int result = QSortFilterProxyModel::columnCount(parent);
@@ -79,6 +82,7 @@ private:
void destroyBackendModel(); void destroyBackendModel();
bool checkHaveFiles(const QModelIndex &parentIdx) const; bool checkHaveFiles(const QModelIndex &parentIdx) const;
bool checkHaveFiles() const; bool checkHaveFiles() const;
QString getUniqueName(const QString &oldName);
QString m_searchText; QString m_searchText;
QString m_rootPath; QString m_rootPath;