forked from qt-creator/qt-creator
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:
@@ -183,4 +183,16 @@ StudioControls.Menu {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StudioControls.MenuItem {
|
||||
text: qsTr("New Effect")
|
||||
|
||||
NewEffectDialog {
|
||||
id: newEffectDialog
|
||||
parent: root.assetsView
|
||||
dirPath: root.__dirPath
|
||||
}
|
||||
|
||||
onTriggered: newEffectDialog.open()
|
||||
}
|
||||
}
|
||||
|
@@ -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()
|
||||
}
|
||||
}
|
@@ -134,32 +134,10 @@ bool AssetsLibraryModel::renameFolder(const QString &folderPath, const QString &
|
||||
bool AssetsLibraryModel::addNewFolder(const QString &folderPath)
|
||||
{
|
||||
QString iterPath = folderPath;
|
||||
static QRegularExpression rgx("\\d+$"); // matches a number at the end of a string
|
||||
QDir dir{folderPath};
|
||||
|
||||
while (dir.exists()) {
|
||||
// if the folder name ends with a number, increment it
|
||||
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';
|
||||
}
|
||||
iterPath = getUniqueName(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
|
||||
{
|
||||
QString path = m_sourceFsModel->filePath(sourceParent);
|
||||
@@ -242,6 +250,36 @@ void AssetsLibraryModel::syncHaveFiles()
|
||||
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)
|
||||
{
|
||||
beginResetModel();
|
||||
|
@@ -47,6 +47,9 @@ public:
|
||||
Q_INVOKABLE bool deleteFolderRecursively(const QModelIndex &folderIndex);
|
||||
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 result = QSortFilterProxyModel::columnCount(parent);
|
||||
@@ -79,6 +82,7 @@ private:
|
||||
void destroyBackendModel();
|
||||
bool checkHaveFiles(const QModelIndex &parentIdx) const;
|
||||
bool checkHaveFiles() const;
|
||||
QString getUniqueName(const QString &oldName);
|
||||
|
||||
QString m_searchText;
|
||||
QString m_rootPath;
|
||||
|
Reference in New Issue
Block a user