forked from qt-creator/qt-creator
EffectComposer: Allow user to choose custom preview image
The current preview image selection and any custom image path is stored in the .qep file on effect save. When a custom image outside of project is chosen, the image is imported into the project into the default image assets folder. Fixes: QDS-13438 Change-Id: If15049612bbca9a744a383c49716cb3648a52af3 Reviewed-by: Mats Honkamaa <mats.honkamaa@qt.io> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Ali Kianian <ali.kianian@qt.io>
This commit is contained in:
@@ -22,12 +22,17 @@ StudioControls.ComboBox {
|
||||
|
||||
required property Item mainRoot
|
||||
|
||||
property var images: ["images/preview0.png",
|
||||
"images/preview1.png",
|
||||
"images/preview2.png",
|
||||
"images/preview3.png",
|
||||
"images/preview4.png"]
|
||||
property string selectedImage: images[0]
|
||||
property var images: [Qt.url(""),
|
||||
Qt.url("images/preview0.png"),
|
||||
Qt.url("images/preview1.png"),
|
||||
Qt.url("images/preview2.png"),
|
||||
Qt.url("images/preview3.png"),
|
||||
Qt.url("images/preview4.png")]
|
||||
property url selectedImage: EffectComposerBackend.effectComposerModel.currentPreviewImage != Qt.url("")
|
||||
? EffectComposerBackend.effectComposerModel.currentPreviewImage
|
||||
: images[1]
|
||||
|
||||
Component.onCompleted: EffectComposerBackend.effectComposerModel.currentPreviewImage = images[1]
|
||||
|
||||
readonly property int popupHeight: Math.min(800, col.height + 2)
|
||||
|
||||
@@ -122,9 +127,30 @@ StudioControls.ComboBox {
|
||||
border.width: 1
|
||||
focus: true
|
||||
|
||||
HelperWidgets.ScrollView {
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 1
|
||||
|
||||
Item {
|
||||
id: setCustomItem
|
||||
width: parent.width
|
||||
height: 40
|
||||
|
||||
HelperWidgets.Button {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 2
|
||||
text: qsTr("Set Custom Image")
|
||||
onClicked: {
|
||||
EffectComposerBackend.effectComposerModel.chooseCustomPreviewImage()
|
||||
root.popup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HelperWidgets.ScrollView {
|
||||
width: parent.width - 2
|
||||
height: parent.height - setCustomItem.height
|
||||
|
||||
clip: true
|
||||
|
||||
Column {
|
||||
@@ -146,9 +172,13 @@ StudioControls.ComboBox {
|
||||
|
||||
width: 200
|
||||
height: 200
|
||||
visible: index > 0
|
||||
|| EffectComposerBackend.effectComposerModel.customPreviewImage !== Qt.url("")
|
||||
|
||||
Image {
|
||||
source: modelData
|
||||
source: index > 0
|
||||
? parent.modelData
|
||||
: EffectComposerBackend.effectComposerModel.customPreviewImage
|
||||
anchors.fill: parent
|
||||
fillMode: Image.PreserveAspectFit
|
||||
smooth: true
|
||||
@@ -159,7 +189,13 @@ StudioControls.ComboBox {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
root.selectedImage = root.images[index]
|
||||
if (parent.index > 0) {
|
||||
EffectComposerBackend.effectComposerModel.currentPreviewImage
|
||||
= root.images[index]
|
||||
} else {
|
||||
EffectComposerBackend.effectComposerModel.currentPreviewImage
|
||||
= EffectComposerBackend.effectComposerModel.customPreviewImage
|
||||
}
|
||||
root.popup.close()
|
||||
}
|
||||
}
|
||||
@@ -167,6 +203,7 @@ StudioControls.ComboBox {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: function(event) {
|
||||
if (event.key === Qt.Key_Escape && root.popup.opened)
|
||||
|
@@ -10,18 +10,24 @@
|
||||
#include "syntaxhighlighterdata.h"
|
||||
#include "uniform.h"
|
||||
|
||||
#include <asset.h>
|
||||
#include <designdocument.h>
|
||||
#include <modelnodeoperations.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <uniquename.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <projectexplorer/projecttree.h>
|
||||
#include <projectexplorer/target.h>
|
||||
|
||||
#include <qtsupport/qtkitaspect.h>
|
||||
|
||||
#include <uniquename.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcprocess.h>
|
||||
|
||||
#include <modelnodeoperations.h>
|
||||
|
||||
#include <QByteArrayView>
|
||||
#include <QFileDialog>
|
||||
#include <QLibraryInfo>
|
||||
#include <QTemporaryDir>
|
||||
#include <QVector2D>
|
||||
@@ -247,6 +253,47 @@ bool EffectComposerModel::nameExists(const QString &name) const
|
||||
return QFile::exists(path.arg(name));
|
||||
}
|
||||
|
||||
void EffectComposerModel::chooseCustomPreviewImage()
|
||||
{
|
||||
QTimer::singleShot(0, this, [&]() {
|
||||
using Utils::FilePath;
|
||||
static FilePath lastDir;
|
||||
const QStringList &suffixes = QmlDesigner::Asset::supportedImageSuffixes();
|
||||
QmlDesigner::DesignDocument *document = QmlDesigner::QmlDesignerPlugin::instance()->currentDesignDocument();
|
||||
const FilePath currentDir = lastDir.isEmpty() ? document->fileName().parentDir()
|
||||
: lastDir;
|
||||
const QStringList fileNames = QFileDialog::getOpenFileNames(Core::ICore::dialogParent(),
|
||||
tr("Select custom effect background image"),
|
||||
currentDir.toFSPathString(),
|
||||
tr("Image Files (%1)").arg(suffixes.join(" ")));
|
||||
|
||||
if (!fileNames.isEmpty()) {
|
||||
FilePath imageFile = FilePath::fromString(fileNames.first());
|
||||
lastDir = imageFile.absolutePath();
|
||||
if (imageFile.exists()) {
|
||||
FilePath projDir = QmlDesigner::QmlDesignerPlugin::instance()->documentManager()
|
||||
.currentProjectDirPath();
|
||||
if (!imageFile.isChildOf(projDir)) {
|
||||
FilePath imagesDir = QmlDesigner::ModelNodeOperations::getImagesDefaultDirectory();
|
||||
FilePath targetFile = imagesDir.pathAppended(imageFile.fileName());
|
||||
if (!targetFile.exists())
|
||||
imageFile.copyFile(targetFile);
|
||||
if (targetFile.exists())
|
||||
imageFile = targetFile;
|
||||
}
|
||||
|
||||
m_customPreviewImage = QUrl::fromLocalFile(imageFile.toFSPathString());
|
||||
m_currentPreviewImage = m_customPreviewImage;
|
||||
|
||||
setHasUnsavedChanges(true);
|
||||
|
||||
emit currentPreviewImageChanged();
|
||||
emit customPreviewImageChanged();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
QString EffectComposerModel::fragmentShader() const
|
||||
{
|
||||
return m_fragmentShader;
|
||||
@@ -1000,6 +1047,9 @@ void EffectComposerModel::saveComposition(const QString &name)
|
||||
return;
|
||||
}
|
||||
|
||||
const Utils::FilePath compositionPath = Utils::FilePath::fromString(path);
|
||||
const Utils::FilePath compositionDir = compositionPath.absolutePath();
|
||||
|
||||
updateExtraMargin();
|
||||
|
||||
QJsonObject json;
|
||||
@@ -1007,6 +1057,17 @@ void EffectComposerModel::saveComposition(const QString &name)
|
||||
json.insert("version", 1);
|
||||
json.insert("tool", "EffectComposer");
|
||||
|
||||
Utils::FilePath customPreviewPath = Utils::FilePath::fromUrl(m_customPreviewImage);
|
||||
if (m_customPreviewImage.isLocalFile())
|
||||
customPreviewPath = customPreviewPath.relativePathFrom(compositionDir);
|
||||
json.insert("customPreviewImage", customPreviewPath.toUrl().toString());
|
||||
|
||||
QUrl previewUrl = m_currentPreviewImage;
|
||||
if (m_currentPreviewImage == m_customPreviewImage)
|
||||
previewUrl = customPreviewPath.toUrl();
|
||||
|
||||
json.insert("previewImage", previewUrl.toString());
|
||||
|
||||
// Add nodes
|
||||
QJsonArray nodesArray;
|
||||
for (const CompositionNode *node : std::as_const(m_nodes)) {
|
||||
@@ -1034,7 +1095,7 @@ void EffectComposerModel::saveComposition(const QString &name)
|
||||
saveFile.close();
|
||||
|
||||
setCurrentComposition(name);
|
||||
setCompositionPath(Utils::FilePath::fromString(path));
|
||||
setCompositionPath(compositionPath);
|
||||
|
||||
saveResources(name);
|
||||
setHasUnsavedChanges(false);
|
||||
@@ -1083,10 +1144,11 @@ void EffectComposerModel::openComposition(const QString &path)
|
||||
{
|
||||
clear(true);
|
||||
|
||||
const QString effectName = QFileInfo(path).baseName();
|
||||
Utils::FilePath effectPath = Utils::FilePath::fromString(path);
|
||||
const QString effectName = effectPath.baseName();
|
||||
|
||||
setCurrentComposition(effectName);
|
||||
setCompositionPath(Utils::FilePath::fromString(path));
|
||||
setCompositionPath(effectPath);
|
||||
|
||||
QFile compFile(path);
|
||||
if (!compFile.open(QIODevice::ReadOnly)) {
|
||||
@@ -1166,6 +1228,39 @@ void EffectComposerModel::openComposition(const QString &path)
|
||||
else
|
||||
resetRootFragmentShader();
|
||||
|
||||
m_currentPreviewImage.clear();
|
||||
if (json.contains("previewImage")) {
|
||||
const QString imageStr = json["previewImage"].toString();
|
||||
if (!imageStr.isEmpty()) {
|
||||
const QUrl imageUrl{imageStr};
|
||||
Utils::FilePath imagePath = Utils::FilePath::fromUrl(imageUrl);
|
||||
if (imageStr.startsWith("images/preview")) { // built-in preview image
|
||||
m_currentPreviewImage = imageUrl;
|
||||
} else if (imagePath.isAbsolutePath()) {
|
||||
if (imagePath.exists())
|
||||
m_currentPreviewImage = imageUrl;
|
||||
} else {
|
||||
imagePath = effectPath.absolutePath().resolvePath(imagePath);
|
||||
if (imagePath.exists())
|
||||
m_currentPreviewImage = imagePath.toUrl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_customPreviewImage.clear();
|
||||
if (json.contains("customPreviewImage")) {
|
||||
QUrl imageUrl{json["customPreviewImage"].toString()};
|
||||
Utils::FilePath imagePath = Utils::FilePath::fromUrl(imageUrl);
|
||||
if (imagePath.isAbsolutePath()) {
|
||||
if (imagePath.exists())
|
||||
m_customPreviewImage = imageUrl;
|
||||
} else {
|
||||
imagePath = effectPath.absolutePath().resolvePath(imagePath);
|
||||
if (imagePath.exists())
|
||||
m_customPreviewImage = imagePath.toUrl();
|
||||
}
|
||||
}
|
||||
|
||||
if (json.contains("nodes") && json["nodes"].isArray()) {
|
||||
beginResetModel();
|
||||
QHash<QString, int> refCounts;
|
||||
@@ -1194,6 +1289,8 @@ void EffectComposerModel::openComposition(const QString &path)
|
||||
|
||||
setHasUnsavedChanges(false);
|
||||
emit nodesChanged();
|
||||
emit currentPreviewImageChanged();
|
||||
emit customPreviewImageChanged();
|
||||
}
|
||||
|
||||
void EffectComposerModel::saveResources(const QString &name)
|
||||
@@ -2151,6 +2248,28 @@ void EffectComposerModel::setCurrentComposition(const QString &newCurrentComposi
|
||||
m_shadersCodeEditor.reset();
|
||||
}
|
||||
|
||||
QUrl EffectComposerModel::customPreviewImage() const
|
||||
{
|
||||
return m_customPreviewImage;
|
||||
}
|
||||
|
||||
QUrl EffectComposerModel::currentPreviewImage() const
|
||||
{
|
||||
return m_currentPreviewImage;
|
||||
}
|
||||
|
||||
void EffectComposerModel::setCurrentPreviewImage(const QUrl &path)
|
||||
{
|
||||
if (m_currentPreviewImage == path)
|
||||
return;
|
||||
|
||||
if (!m_nodes.isEmpty())
|
||||
setHasUnsavedChanges(true);
|
||||
|
||||
m_currentPreviewImage = path;
|
||||
emit currentPreviewImageChanged();
|
||||
}
|
||||
|
||||
Utils::FilePath EffectComposerModel::compositionPath() const
|
||||
{
|
||||
return m_compositionPath;
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include <QSet>
|
||||
#include <QTemporaryDir>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Target;
|
||||
@@ -53,6 +54,8 @@ class EffectComposerModel : public QAbstractListModel
|
||||
Q_PROPERTY(bool isEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
|
||||
Q_PROPERTY(bool hasValidTarget READ hasValidTarget WRITE setHasValidTarget NOTIFY hasValidTargetChanged)
|
||||
Q_PROPERTY(QString currentComposition READ currentComposition WRITE setCurrentComposition NOTIFY currentCompositionChanged)
|
||||
Q_PROPERTY(QUrl currentPreviewImage READ currentPreviewImage WRITE setCurrentPreviewImage NOTIFY currentPreviewImageChanged)
|
||||
Q_PROPERTY(QUrl customPreviewImage READ customPreviewImage NOTIFY customPreviewImageChanged)
|
||||
|
||||
public:
|
||||
EffectComposerModel(QObject *parent = nullptr);
|
||||
@@ -77,6 +80,7 @@ public:
|
||||
Q_INVOKABLE void assignToSelected();
|
||||
Q_INVOKABLE QString getUniqueEffectName() const;
|
||||
Q_INVOKABLE bool nameExists(const QString &name) const;
|
||||
Q_INVOKABLE void chooseCustomPreviewImage();
|
||||
|
||||
bool shadersUpToDate() const;
|
||||
void setShadersUpToDate(bool newShadersUpToDate);
|
||||
@@ -116,6 +120,10 @@ public:
|
||||
QString currentComposition() const;
|
||||
void setCurrentComposition(const QString &newCurrentComposition);
|
||||
|
||||
QUrl customPreviewImage() const;
|
||||
QUrl currentPreviewImage() const;
|
||||
void setCurrentPreviewImage(const QUrl &path);
|
||||
|
||||
Utils::FilePath compositionPath() const;
|
||||
void setCompositionPath(const Utils::FilePath &newCompositionPath);
|
||||
|
||||
@@ -140,6 +148,8 @@ signals:
|
||||
void hasUnsavedChangesChanged();
|
||||
void assignToSelectedTriggered(const QString &effectPath);
|
||||
void removePropertiesFromScene(QSet<QByteArray> props, const QString &typeName);
|
||||
void currentPreviewImageChanged();
|
||||
void customPreviewImageChanged();
|
||||
|
||||
private:
|
||||
enum Roles {
|
||||
@@ -242,6 +252,8 @@ private:
|
||||
QString m_effectTypePrefix;
|
||||
Utils::FilePath m_compositionPath;
|
||||
Utils::UniqueObjectLatePtr<EffectShadersCodeEditor> m_shadersCodeEditor;
|
||||
QUrl m_currentPreviewImage;
|
||||
QUrl m_customPreviewImage;
|
||||
|
||||
const QRegularExpression m_spaceReg = QRegularExpression("\\s+");
|
||||
};
|
||||
|
@@ -137,7 +137,7 @@ bool useLayerEffect();
|
||||
bool validateEffect(const QString &effectPath);
|
||||
bool isEffectComposerActivated();
|
||||
|
||||
Utils::FilePath getImagesDefaultDirectory();
|
||||
QMLDESIGNERCOMPONENTS_EXPORT Utils::FilePath getImagesDefaultDirectory();
|
||||
|
||||
//Item Library and Assets related drop operations
|
||||
QMLDESIGNERCOMPONENTS_EXPORT ModelNode handleItemLibraryEffectDrop(const QString &effectPath,
|
||||
|
Reference in New Issue
Block a user