forked from qt-creator/qt-creator
QmlDesigner: Rename effect maker plugin files to effect composer
Change-Id: I1d09c1088b4067a479f2e7cc396a348f1b48614f Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
24
src/plugins/effectcomposer/CMakeLists.txt
Normal file
24
src/plugins/effectcomposer/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
add_qtc_plugin(EffectComposer
|
||||
CONDITION TARGET Qt::Quick AND TARGET QtCreator::QmlDesigner
|
||||
PLUGIN_DEPENDS
|
||||
QtCreator::QmlDesigner QtCreator::ProjectExplorer QtCreator::QmlProjectManager
|
||||
DEPENDS
|
||||
Qt::Core Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick
|
||||
QtCreator::Utils
|
||||
SOURCES
|
||||
effectcomposerplugin.cpp effectcomposerplugin.h
|
||||
effectcomposerwidget.cpp effectcomposerwidget.h
|
||||
effectcomposerview.cpp effectcomposerview.h
|
||||
effectcomposermodel.cpp effectcomposermodel.h
|
||||
effectcomposernodesmodel.cpp effectcomposernodesmodel.h
|
||||
effectcomposeruniformsmodel.cpp effectcomposeruniformsmodel.h
|
||||
effectnode.cpp effectnode.h
|
||||
effectnodescategory.cpp effectnodescategory.h
|
||||
compositionnode.cpp compositionnode.h
|
||||
uniform.cpp uniform.h
|
||||
effectutils.cpp effectutils.h
|
||||
effectcomposercontextobject.cpp effectcomposercontextobject.h
|
||||
shaderfeatures.cpp shaderfeatures.h
|
||||
syntaxhighlighterdata.cpp syntaxhighlighterdata.h
|
||||
propertyhandler.cpp propertyhandler.h
|
||||
)
|
||||
16
src/plugins/effectcomposer/EffectComposer.json.in
Normal file
16
src/plugins/effectcomposer/EffectComposer.json.in
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"Name" : "EffectComposer",
|
||||
"Version" : "${IDE_VERSION}",
|
||||
"CompatVersion" : "${IDE_VERSION_COMPAT}",
|
||||
"Vendor" : "The Qt Company Ltd",
|
||||
"Copyright" : "(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd",
|
||||
"License" : [ "Commercial Usage",
|
||||
"",
|
||||
"Licensees holding valid Qt Enterprise licenses may use this plugin in accordance with the Qt Enterprise License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company."
|
||||
],
|
||||
"Description" : "Plugin for Effect Composer.",
|
||||
"Url" : "http://www.qt.io",
|
||||
"DisabledByDefault" : true,
|
||||
${IDE_PLUGIN_DEPENDENCIES}
|
||||
}
|
||||
|
||||
191
src/plugins/effectcomposer/compositionnode.cpp
Normal file
191
src/plugins/effectcomposer/compositionnode.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "compositionnode.h"
|
||||
|
||||
#include "effectutils.h"
|
||||
#include "effectcomposeruniformsmodel.h"
|
||||
#include "propertyhandler.h"
|
||||
#include "uniform.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
CompositionNode::CompositionNode(const QString &effectName, const QString &qenPath,
|
||||
const QJsonObject &jsonObject)
|
||||
{
|
||||
QJsonObject json;
|
||||
if (jsonObject.isEmpty()) {
|
||||
QFile qenFile(qenPath);
|
||||
if (!qenFile.open(QIODevice::ReadOnly)) {
|
||||
qWarning("Couldn't open effect file.");
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray loadData = qenFile.readAll();
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument jsonDoc(QJsonDocument::fromJson(loadData, &parseError));
|
||||
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
QString error = QString("Error parsing effect node");
|
||||
QString errorDetails = QString("%1: %2").arg(parseError.offset).arg(parseError.errorString());
|
||||
qWarning() << error;
|
||||
qWarning() << errorDetails;
|
||||
return;
|
||||
}
|
||||
json = jsonDoc.object().value("QEN").toObject();
|
||||
parse(effectName, qenPath, json);
|
||||
}
|
||||
else {
|
||||
parse(effectName, "", jsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
QString CompositionNode::fragmentCode() const
|
||||
{
|
||||
return m_fragmentCode;
|
||||
}
|
||||
|
||||
QString CompositionNode::vertexCode() const
|
||||
{
|
||||
return m_vertexCode;
|
||||
}
|
||||
|
||||
QString CompositionNode::description() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
QString CompositionNode::id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
QObject *CompositionNode::uniformsModel()
|
||||
{
|
||||
return &m_unifomrsModel;
|
||||
}
|
||||
|
||||
QStringList CompositionNode::requiredNodes() const
|
||||
{
|
||||
return m_requiredNodes;
|
||||
}
|
||||
|
||||
bool CompositionNode::isEnabled() const
|
||||
{
|
||||
return m_isEnabled;
|
||||
}
|
||||
|
||||
void CompositionNode::setIsEnabled(bool newIsEnabled)
|
||||
{
|
||||
if (newIsEnabled != m_isEnabled) {
|
||||
m_isEnabled = newIsEnabled;
|
||||
emit isEnabledChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool CompositionNode::isDependency() const
|
||||
{
|
||||
return m_refCount > 0;
|
||||
}
|
||||
|
||||
CompositionNode::NodeType CompositionNode::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
void CompositionNode::parse(const QString &effectName, const QString &qenPath, const QJsonObject &json)
|
||||
{
|
||||
int version = -1;
|
||||
if (json.contains("version"))
|
||||
version = json["version"].toInt(-1);
|
||||
if (version != 1) {
|
||||
QString error = QString("Error: Unknown effect version (%1)").arg(version);
|
||||
qWarning() << qPrintable(error);
|
||||
return;
|
||||
}
|
||||
|
||||
m_name = json.value("name").toString();
|
||||
m_description = json.value("description").toString();
|
||||
m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray());
|
||||
m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray());
|
||||
|
||||
if (json.contains("enabled"))
|
||||
m_isEnabled = json["enabled"].toBool();
|
||||
|
||||
m_id = json.value("id").toString();
|
||||
if (m_id.isEmpty() && !qenPath.isEmpty()) {
|
||||
QString fileName = qenPath.split('/').last();
|
||||
fileName.chop(4); // remove ".qen"
|
||||
m_id = fileName;
|
||||
}
|
||||
|
||||
// parse properties
|
||||
QJsonArray jsonProps = json.value("properties").toArray();
|
||||
for (const auto /*QJsonValueRef*/ &prop : jsonProps) {
|
||||
const auto uniform = new Uniform(effectName, prop.toObject(), qenPath);
|
||||
m_unifomrsModel.addUniform(uniform);
|
||||
m_uniforms.append(uniform);
|
||||
g_propertyData.insert(uniform->name(), uniform->value());
|
||||
}
|
||||
|
||||
// Seek through code to get tags
|
||||
QStringList shaderCodeLines;
|
||||
shaderCodeLines += m_vertexCode.split('\n');
|
||||
shaderCodeLines += m_fragmentCode.split('\n');
|
||||
for (const QString &codeLine : std::as_const(shaderCodeLines)) {
|
||||
QString trimmedLine = codeLine.trimmed();
|
||||
if (trimmedLine.startsWith("@requires")) {
|
||||
// Get the required node, remove "@requires "
|
||||
QString nodeId = trimmedLine.sliced(10).toLower();
|
||||
if (!nodeId.isEmpty() && !m_requiredNodes.contains(nodeId))
|
||||
m_requiredNodes << nodeId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<Uniform *> CompositionNode::uniforms() const
|
||||
{
|
||||
return m_uniforms;
|
||||
}
|
||||
|
||||
int CompositionNode::incRefCount()
|
||||
{
|
||||
++m_refCount;
|
||||
|
||||
if (m_refCount == 1)
|
||||
emit isDepencyChanged();
|
||||
|
||||
return m_refCount;
|
||||
}
|
||||
|
||||
int CompositionNode::decRefCount()
|
||||
{
|
||||
--m_refCount;
|
||||
|
||||
if (m_refCount == 0)
|
||||
emit isDepencyChanged();
|
||||
|
||||
return m_refCount;
|
||||
}
|
||||
|
||||
void CompositionNode::setRefCount(int count)
|
||||
{
|
||||
bool notifyChange = (m_refCount > 0 && count == 0) || (m_refCount <= 0 && count > 0);
|
||||
|
||||
m_refCount = count;
|
||||
|
||||
if (notifyChange)
|
||||
emit isDepencyChanged();
|
||||
}
|
||||
|
||||
QString CompositionNode::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
78
src/plugins/effectcomposer/compositionnode.h
Normal file
78
src/plugins/effectcomposer/compositionnode.h
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "effectcomposeruniformsmodel.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
#include <QObject>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class CompositionNode : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT)
|
||||
Q_PROPERTY(bool nodeEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
|
||||
Q_PROPERTY(bool isDependency READ isDependency NOTIFY isDepencyChanged)
|
||||
Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged)
|
||||
|
||||
public:
|
||||
enum NodeType {
|
||||
SourceNode = 0,
|
||||
DestinationNode,
|
||||
CustomNode
|
||||
};
|
||||
|
||||
CompositionNode(const QString &effectName, const QString &qenPath, const QJsonObject &json = {});
|
||||
|
||||
QString fragmentCode() const;
|
||||
QString vertexCode() const;
|
||||
QString description() const;
|
||||
QString id() const;
|
||||
|
||||
QObject *uniformsModel();
|
||||
|
||||
QStringList requiredNodes() const;
|
||||
|
||||
NodeType type() const;
|
||||
|
||||
bool isEnabled() const;
|
||||
void setIsEnabled(bool newIsEnabled);
|
||||
|
||||
bool isDependency() const;
|
||||
|
||||
QString name() const;
|
||||
|
||||
QList<Uniform *> uniforms() const;
|
||||
|
||||
int incRefCount();
|
||||
int decRefCount();
|
||||
void setRefCount(int count);
|
||||
|
||||
signals:
|
||||
void uniformsModelChanged();
|
||||
void isEnabledChanged();
|
||||
void isDepencyChanged();
|
||||
|
||||
private:
|
||||
void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json);
|
||||
|
||||
QString m_name;
|
||||
NodeType m_type = CustomNode;
|
||||
QString m_fragmentCode;
|
||||
QString m_vertexCode;
|
||||
QString m_description;
|
||||
QStringList m_requiredNodes;
|
||||
QString m_id;
|
||||
bool m_isEnabled = true;
|
||||
int m_refCount = 0;
|
||||
|
||||
QList<Uniform *> m_uniforms;
|
||||
|
||||
EffectComposerUniformsModel m_unifomrsModel;
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
186
src/plugins/effectcomposer/effectcomposercontextobject.cpp
Normal file
186
src/plugins/effectcomposer/effectcomposercontextobject.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "effectcomposercontextobject.h"
|
||||
|
||||
#include <abstractview.h>
|
||||
#include <bindingproperty.h>
|
||||
#include <documentmanager.h>
|
||||
#include <nodemetainfo.h>
|
||||
#include <rewritingexception.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h>
|
||||
#include <qmlobjectnode.h>
|
||||
#include <qmltimeline.h>
|
||||
#include <qmltimelinekeyframegroup.h>
|
||||
#include <variantproperty.h>
|
||||
|
||||
#include <coreplugin/messagebox.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QCursor>
|
||||
#include <QMessageBox>
|
||||
#include <QQmlContext>
|
||||
#include <QWindow>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
EffectComposerContextObject::EffectComposerContextObject(QQmlContext *context, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_qmlContext(context)
|
||||
{
|
||||
}
|
||||
|
||||
QString EffectComposerContextObject::convertColorToString(const QVariant &color)
|
||||
{
|
||||
QString colorString;
|
||||
QColor theColor;
|
||||
if (color.canConvert(QVariant::Color)) {
|
||||
theColor = color.value<QColor>();
|
||||
} else if (color.canConvert(QVariant::Vector3D)) {
|
||||
auto vec = color.value<QVector3D>();
|
||||
theColor = QColor::fromRgbF(vec.x(), vec.y(), vec.z());
|
||||
}
|
||||
|
||||
colorString = theColor.name(QColor::HexArgb);
|
||||
|
||||
return colorString;
|
||||
}
|
||||
|
||||
// TODO: this method is used by the ColorEditor helper widget, check if at all needed?
|
||||
QColor EffectComposerContextObject::colorFromString(const QString &colorString)
|
||||
{
|
||||
return colorString;
|
||||
}
|
||||
|
||||
int EffectComposerContextObject::majorVersion() const
|
||||
{
|
||||
return m_majorVersion;
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::setMajorVersion(int majorVersion)
|
||||
{
|
||||
if (m_majorVersion == majorVersion)
|
||||
return;
|
||||
|
||||
m_majorVersion = majorVersion;
|
||||
|
||||
emit majorVersionChanged();
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::setStateName(const QString &newStateName)
|
||||
{
|
||||
if (newStateName == m_stateName)
|
||||
return;
|
||||
|
||||
m_stateName = newStateName;
|
||||
emit stateNameChanged();
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::setAllStateNames(const QStringList &allStates)
|
||||
{
|
||||
if (allStates == m_allStateNames)
|
||||
return;
|
||||
|
||||
m_allStateNames = allStates;
|
||||
emit allStateNamesChanged();
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::setIsBaseState(bool newIsBaseState)
|
||||
{
|
||||
if (newIsBaseState == m_isBaseState)
|
||||
return;
|
||||
|
||||
m_isBaseState = newIsBaseState;
|
||||
emit isBaseStateChanged();
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::setSelectionChanged(bool newSelectionChanged)
|
||||
{
|
||||
if (newSelectionChanged == m_selectionChanged)
|
||||
return;
|
||||
|
||||
m_selectionChanged = newSelectionChanged;
|
||||
emit selectionChangedChanged();
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::setBackendValues(QQmlPropertyMap *newBackendValues)
|
||||
{
|
||||
if (newBackendValues == m_backendValues)
|
||||
return;
|
||||
|
||||
m_backendValues = newBackendValues;
|
||||
emit backendValuesChanged();
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::setModel(QmlDesigner::Model *model)
|
||||
{
|
||||
m_model = model;
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::triggerSelectionChanged()
|
||||
{
|
||||
setSelectionChanged(!m_selectionChanged);
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::hideCursor()
|
||||
{
|
||||
if (QApplication::overrideCursor())
|
||||
return;
|
||||
|
||||
QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
|
||||
|
||||
if (QWidget *w = QApplication::activeWindow())
|
||||
m_lastPos = QCursor::pos(w->screen());
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::restoreCursor()
|
||||
{
|
||||
if (!QApplication::overrideCursor())
|
||||
return;
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
if (QWidget *w = QApplication::activeWindow())
|
||||
QCursor::setPos(w->screen(), m_lastPos);
|
||||
}
|
||||
|
||||
void EffectComposerContextObject::holdCursorInPlace()
|
||||
{
|
||||
if (!QApplication::overrideCursor())
|
||||
return;
|
||||
|
||||
if (QWidget *w = QApplication::activeWindow())
|
||||
QCursor::setPos(w->screen(), m_lastPos);
|
||||
}
|
||||
|
||||
int EffectComposerContextObject::devicePixelRatio()
|
||||
{
|
||||
if (QWidget *w = QApplication::activeWindow())
|
||||
return w->devicePixelRatio();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
QStringList EffectComposerContextObject::allStatesForId(const QString &id)
|
||||
{
|
||||
if (m_model && m_model->rewriterView()) {
|
||||
const QmlDesigner::QmlObjectNode node = m_model->rewriterView()->modelNodeForId(id);
|
||||
if (node.isValid())
|
||||
return node.allStateNames();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool EffectComposerContextObject::isBlocked(const QString &) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
102
src/plugins/effectcomposer/effectcomposercontextobject.h
Normal file
102
src/plugins/effectcomposer/effectcomposercontextobject.h
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <model.h>
|
||||
#include <modelnode.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
#include <QQmlPropertyMap>
|
||||
#include <QQmlComponent>
|
||||
#include <QColor>
|
||||
#include <QPoint>
|
||||
#include <QMouseEvent>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class EffectComposerContextObject : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString stateName READ stateName WRITE setStateName NOTIFY stateNameChanged)
|
||||
Q_PROPERTY(QStringList allStateNames READ allStateNames WRITE setAllStateNames NOTIFY allStateNamesChanged)
|
||||
Q_PROPERTY(int possibleTypeIndex READ possibleTypeIndex NOTIFY possibleTypeIndexChanged)
|
||||
|
||||
Q_PROPERTY(bool isBaseState READ isBaseState WRITE setIsBaseState NOTIFY isBaseStateChanged)
|
||||
Q_PROPERTY(bool selectionChanged READ selectionChanged WRITE setSelectionChanged NOTIFY selectionChangedChanged)
|
||||
|
||||
Q_PROPERTY(int majorVersion READ majorVersion WRITE setMajorVersion NOTIFY majorVersionChanged)
|
||||
|
||||
Q_PROPERTY(QQmlPropertyMap *backendValues READ backendValues WRITE setBackendValues NOTIFY backendValuesChanged)
|
||||
|
||||
public:
|
||||
EffectComposerContextObject(QQmlContext *context, QObject *parent = nullptr);
|
||||
|
||||
QString stateName() const { return m_stateName; }
|
||||
QStringList allStateNames() const { return m_allStateNames; }
|
||||
int possibleTypeIndex() const { return m_possibleTypeIndex; }
|
||||
|
||||
bool isBaseState() const { return m_isBaseState; }
|
||||
bool selectionChanged() const { return m_selectionChanged; }
|
||||
|
||||
QQmlPropertyMap *backendValues() const { return m_backendValues; }
|
||||
|
||||
Q_INVOKABLE QString convertColorToString(const QVariant &color);
|
||||
Q_INVOKABLE QColor colorFromString(const QString &colorString);
|
||||
|
||||
Q_INVOKABLE void hideCursor();
|
||||
Q_INVOKABLE void restoreCursor();
|
||||
Q_INVOKABLE void holdCursorInPlace();
|
||||
|
||||
Q_INVOKABLE int devicePixelRatio();
|
||||
|
||||
Q_INVOKABLE QStringList allStatesForId(const QString &id);
|
||||
|
||||
Q_INVOKABLE bool isBlocked(const QString &propName) const;
|
||||
|
||||
int majorVersion() const;
|
||||
void setMajorVersion(int majorVersion);
|
||||
|
||||
void setStateName(const QString &newStateName);
|
||||
void setAllStateNames(const QStringList &allStates);
|
||||
void setIsBaseState(bool newIsBaseState);
|
||||
void setSelectionChanged(bool newSelectionChanged);
|
||||
void setBackendValues(QQmlPropertyMap *newBackendValues);
|
||||
void setModel(QmlDesigner::Model *model);
|
||||
|
||||
void triggerSelectionChanged();
|
||||
|
||||
signals:
|
||||
void stateNameChanged();
|
||||
void allStateNamesChanged();
|
||||
void possibleTypeIndexChanged();
|
||||
void isBaseStateChanged();
|
||||
void selectionChangedChanged();
|
||||
void backendValuesChanged();
|
||||
void majorVersionChanged();
|
||||
|
||||
private:
|
||||
void updatePossibleTypeIndex();
|
||||
|
||||
QQmlContext *m_qmlContext = nullptr;
|
||||
|
||||
QString m_stateName;
|
||||
QStringList m_allStateNames;
|
||||
int m_possibleTypeIndex = -1;
|
||||
QString m_currentType;
|
||||
|
||||
int m_majorVersion = 1;
|
||||
|
||||
QQmlPropertyMap *m_backendValues = nullptr;
|
||||
QmlDesigner::Model *m_model = nullptr;
|
||||
|
||||
QPoint m_lastPos;
|
||||
|
||||
bool m_isBaseState = false;
|
||||
bool m_selectionChanged = false;
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
1671
src/plugins/effectcomposer/effectcomposermodel.cpp
Normal file
1671
src/plugins/effectcomposer/effectcomposermodel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
210
src/plugins/effectcomposer/effectcomposermodel.h
Normal file
210
src/plugins/effectcomposer/effectcomposermodel.h
Normal file
@@ -0,0 +1,210 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shaderfeatures.h"
|
||||
|
||||
#include <utils/filepath.h>
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QMap>
|
||||
#include <QRegularExpression>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Target;
|
||||
}
|
||||
|
||||
namespace Utils {
|
||||
class Process;
|
||||
}
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class CompositionNode;
|
||||
class Uniform;
|
||||
|
||||
struct EffectError {
|
||||
Q_GADGET
|
||||
Q_PROPERTY(QString message MEMBER m_message)
|
||||
Q_PROPERTY(int line MEMBER m_line)
|
||||
Q_PROPERTY(int type MEMBER m_type)
|
||||
|
||||
public:
|
||||
QString m_message;
|
||||
int m_line = -1;
|
||||
int m_type = -1;
|
||||
};
|
||||
|
||||
class EffectComposerModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
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)
|
||||
|
||||
public:
|
||||
EffectComposerModel(QObject *parent = nullptr);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
|
||||
bool isEmpty() const { return m_isEmpty; }
|
||||
void setIsEmpty(bool val);
|
||||
|
||||
void addNode(const QString &nodeQenPath);
|
||||
|
||||
CompositionNode *findNodeById(const QString &id) const;
|
||||
|
||||
Q_INVOKABLE void moveNode(int fromIdx, int toIdx);
|
||||
Q_INVOKABLE void removeNode(int idx);
|
||||
Q_INVOKABLE void clear(bool clearName = false);
|
||||
Q_INVOKABLE void assignToSelected();
|
||||
Q_INVOKABLE QString getUniqueEffectName() const;
|
||||
|
||||
bool shadersUpToDate() const;
|
||||
void setShadersUpToDate(bool newShadersUpToDate);
|
||||
|
||||
QString fragmentShader() const;
|
||||
void setFragmentShader(const QString &newFragmentShader);
|
||||
|
||||
QString vertexShader() const;
|
||||
void setVertexShader(const QString &newVertexShader);
|
||||
|
||||
const QString &qmlComponentString() const;
|
||||
|
||||
Q_INVOKABLE void updateQmlComponent();
|
||||
|
||||
Q_INVOKABLE void resetEffectError(int type);
|
||||
Q_INVOKABLE void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1);
|
||||
|
||||
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;
|
||||
|
||||
Q_INVOKABLE bool isDependencyNode(int index) const;
|
||||
|
||||
signals:
|
||||
void isEmptyChanged();
|
||||
void selectedIndexChanged(int idx);
|
||||
void effectErrorChanged();
|
||||
void shadersUpToDateChanged();
|
||||
void shadersBaked();
|
||||
void currentCompositionChanged();
|
||||
void nodesChanged();
|
||||
void resourcesSaved(const QByteArray &type, const Utils::FilePath &path);
|
||||
void hasUnsavedChangesChanged();
|
||||
void assignToSelectedTriggered(const QString &effectPath);
|
||||
|
||||
private:
|
||||
enum Roles {
|
||||
NameRole = Qt::UserRole + 1,
|
||||
EnabledRole,
|
||||
UniformsRole,
|
||||
Dependency
|
||||
};
|
||||
|
||||
enum ErrorTypes {
|
||||
ErrorCommon = -1,
|
||||
ErrorQMLParsing,
|
||||
ErrorVert,
|
||||
ErrorFrag,
|
||||
ErrorQMLRuntime,
|
||||
ErrorPreprocessor
|
||||
};
|
||||
|
||||
bool isValidIndex(int idx) const;
|
||||
|
||||
const QList<Uniform *> allUniforms() const;
|
||||
|
||||
const QString getBufUniform();
|
||||
const QString getVSUniforms();
|
||||
const QString getFSUniforms();
|
||||
|
||||
QString detectErrorMessage(const QString &errorMessage);
|
||||
EffectError effectError() const;
|
||||
|
||||
QString valueAsString(const Uniform &uniform);
|
||||
QString valueAsBinding(const Uniform &uniform);
|
||||
QString valueAsVariable(const Uniform &uniform);
|
||||
QString getImageElementName(const Uniform &uniform, bool localFiles);
|
||||
const QString getConstVariables();
|
||||
const QString getDefineProperties();
|
||||
int getTagIndex(const QStringList &code, const QString &tag);
|
||||
QString processVertexRootLine(const QString &line);
|
||||
QString processFragmentRootLine(const QString &line);
|
||||
QStringList getDefaultRootVertexShader();
|
||||
QStringList getDefaultRootFragmentShader();
|
||||
QStringList removeTagsFromCode(const QStringList &codeLines);
|
||||
QString removeTagsFromCode(const QString &code);
|
||||
QString getCustomShaderVaryings(bool outState);
|
||||
QString generateVertexShader(bool includeUniforms = true);
|
||||
QString generateFragmentShader(bool includeUniforms = true);
|
||||
void handleQsbProcessExit(Utils::Process *qsbProcess, const QString &shader, bool preview);
|
||||
QString stripFileFromURL(const QString &urlString) const;
|
||||
QString getQmlEffectString();
|
||||
|
||||
void updateCustomUniforms();
|
||||
void createFiles();
|
||||
void bakeShaders();
|
||||
void saveResources(const QString &name);
|
||||
|
||||
QString mipmapPropertyName(const QString &name) const;
|
||||
QString getQmlImagesString(bool localFiles);
|
||||
QString getQmlComponentString(bool localFiles);
|
||||
|
||||
QList<CompositionNode *> m_nodes;
|
||||
|
||||
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;
|
||||
QMap<int, EffectError> m_effectErrors;
|
||||
ShaderFeatures m_shaderFeatures;
|
||||
QStringList m_shaderVaryingVariables;
|
||||
QString m_fragmentShader;
|
||||
QString m_vertexShader;
|
||||
QStringList m_defaultRootVertexShader;
|
||||
QStringList m_defaultRootFragmentShader;
|
||||
// Temp files to store shaders sources and binary data
|
||||
QTemporaryFile m_fragmentSourceFile;
|
||||
QTemporaryFile m_vertexSourceFile;
|
||||
QString m_fragmentSourceFilename;
|
||||
QString m_vertexSourceFilename;
|
||||
QString m_fragmentShaderFilename;
|
||||
QString m_vertexShaderFilename;
|
||||
QString m_fragmentShaderPreviewFilename;
|
||||
QString m_vertexShaderPreviewFilename;
|
||||
// Used in exported QML, at root of the file
|
||||
QString m_exportedRootPropertiesString;
|
||||
// Used in exported QML, at ShaderEffect component of the file
|
||||
QString m_exportedEffectPropertiesString;
|
||||
// Used in preview QML, at ShaderEffect component of the file
|
||||
QString m_previewEffectPropertiesString;
|
||||
QString m_qmlComponentString;
|
||||
bool m_loadComponentImages = true;
|
||||
QString m_currentComposition;
|
||||
|
||||
const QRegularExpression m_spaceReg = QRegularExpression("\\s+");
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
111
src/plugins/effectcomposer/effectcomposernodesmodel.cpp
Normal file
111
src/plugins/effectcomposer/effectcomposernodesmodel.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "effectcomposernodesmodel.h"
|
||||
#include "effectutils.h"
|
||||
|
||||
#include <utils/filepath.h>
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
EffectComposerNodesModel::EffectComposerNodesModel(QObject *parent)
|
||||
: QAbstractListModel{parent}
|
||||
{
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> EffectComposerNodesModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[CategoryNameRole] = "categoryName";
|
||||
roles[CategoryNodesRole] = "categoryNodes";
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
int EffectComposerNodesModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
|
||||
return m_categories.count();
|
||||
}
|
||||
|
||||
QVariant EffectComposerNodesModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
QTC_ASSERT(index.isValid() && index.row() < m_categories.size(), return {});
|
||||
QTC_ASSERT(roleNames().contains(role), return {});
|
||||
|
||||
return m_categories.at(index.row())->property(roleNames().value(role));
|
||||
}
|
||||
|
||||
void EffectComposerNodesModel::loadModel()
|
||||
{
|
||||
if (m_modelLoaded)
|
||||
return;
|
||||
|
||||
auto nodesPath = Utils::FilePath::fromString(EffectUtils::nodesSourcesPath());
|
||||
|
||||
if (!nodesPath.exists()) {
|
||||
qWarning() << __FUNCTION__ << "Effects not found.";
|
||||
return;
|
||||
}
|
||||
|
||||
m_categories = {};
|
||||
|
||||
QDirIterator itCategories(nodesPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
while (itCategories.hasNext()) {
|
||||
itCategories.next();
|
||||
|
||||
if (itCategories.fileName() == "images" || itCategories.fileName() == "common")
|
||||
continue;
|
||||
|
||||
QString catName = itCategories.fileName();
|
||||
|
||||
QList<EffectNode *> effects = {};
|
||||
Utils::FilePath categoryPath = nodesPath.resolvePath(itCategories.fileName());
|
||||
QDirIterator itEffects(categoryPath.toString(), {"*.qen"}, QDir::Files);
|
||||
while (itEffects.hasNext()) {
|
||||
itEffects.next();
|
||||
effects.push_back(new EffectNode(itEffects.filePath()));
|
||||
}
|
||||
|
||||
catName[0] = catName[0].toUpper(); // capitalize first letter
|
||||
EffectNodesCategory *category = new EffectNodesCategory(catName, effects);
|
||||
m_categories.push_back(category);
|
||||
}
|
||||
|
||||
std::sort(m_categories.begin(), m_categories.end(),
|
||||
[](EffectNodesCategory *a, EffectNodesCategory *b) {
|
||||
return a->name() < b->name();
|
||||
});
|
||||
|
||||
m_modelLoaded = true;
|
||||
|
||||
resetModel();
|
||||
}
|
||||
|
||||
void EffectComposerNodesModel::resetModel()
|
||||
{
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void EffectComposerNodesModel::updateCanBeAdded(const QStringList &uniforms)
|
||||
{
|
||||
for (const EffectNodesCategory *cat : std::as_const(m_categories)) {
|
||||
const QList<EffectNode *> nodes = cat->nodes();
|
||||
for (EffectNode *node : nodes) {
|
||||
bool match = false;
|
||||
for (const QString &uniform : uniforms) {
|
||||
match = node->hasUniform(uniform);
|
||||
if (match)
|
||||
break;
|
||||
}
|
||||
node->setCanBeAdded(!match);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
44
src/plugins/effectcomposer/effectcomposernodesmodel.h
Normal file
44
src/plugins/effectcomposer/effectcomposernodesmodel.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "effectnodescategory.h"
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class EffectComposerNodesModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
enum Roles {
|
||||
CategoryNameRole = Qt::UserRole + 1,
|
||||
CategoryNodesRole
|
||||
};
|
||||
|
||||
public:
|
||||
EffectComposerNodesModel(QObject *parent = nullptr);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
void loadModel();
|
||||
void resetModel();
|
||||
|
||||
QList<EffectNodesCategory *> categories() const { return m_categories; }
|
||||
|
||||
void updateCanBeAdded(const QStringList &uniforms);
|
||||
|
||||
private:
|
||||
QString nodesSourcesPath() const;
|
||||
|
||||
QList<EffectNodesCategory *> m_categories;
|
||||
bool m_probeNodesDir = false;
|
||||
bool m_modelLoaded = false;
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
53
src/plugins/effectcomposer/effectcomposerplugin.cpp
Normal file
53
src/plugins/effectcomposer/effectcomposerplugin.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "effectcomposerplugin.h"
|
||||
|
||||
#include "effectcomposerview.h"
|
||||
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/imode.h>
|
||||
#include <coreplugin/modemanager.h>
|
||||
#include <coreplugin/messagemanager.h>
|
||||
#include <coreplugin/externaltool.h>
|
||||
#include <coreplugin/externaltoolmanager.h>
|
||||
|
||||
#include <componentcore_constants.h>
|
||||
#include <designeractionmanager.h>
|
||||
#include <viewmanager.h>
|
||||
#include <qmldesigner/dynamiclicensecheck.h>
|
||||
#include <qmldesignerplugin.h>
|
||||
#include <modelnodeoperations.h>
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QMap>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
static bool enableEffectComposer()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EffectComposerPlugin::delayedInitialize()
|
||||
{
|
||||
if (m_delayedInitialized)
|
||||
return true;
|
||||
|
||||
if (enableEffectComposer()) {
|
||||
auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance();
|
||||
auto &viewManager = designerPlugin->viewManager();
|
||||
|
||||
viewManager.registerView(std::make_unique<EffectComposerView>(
|
||||
QmlDesigner::QmlDesignerPlugin::externalDependenciesForPluginInitializationOnly()));
|
||||
}
|
||||
|
||||
m_delayedInitialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
32
src/plugins/effectcomposer/effectcomposerplugin.h
Normal file
32
src/plugins/effectcomposer/effectcomposerplugin.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <extensionsystem/iplugin.h>
|
||||
#include <utils/process.h>
|
||||
|
||||
namespace Core {
|
||||
class ActionContainer;
|
||||
class ExternalTool;
|
||||
}
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class EffectComposerPlugin : public ExtensionSystem::IPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "EffectComposer.json")
|
||||
|
||||
public:
|
||||
EffectComposerPlugin() {}
|
||||
~EffectComposerPlugin() override {}
|
||||
|
||||
bool delayedInitialize() override;
|
||||
|
||||
private:
|
||||
bool m_delayedInitialized = false;
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
93
src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp
Normal file
93
src/plugins/effectcomposer/effectcomposeruniformsmodel.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "effectcomposeruniformsmodel.h"
|
||||
|
||||
#include "propertyhandler.h"
|
||||
#include "uniform.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
EffectComposerUniformsModel::EffectComposerUniformsModel(QObject *parent)
|
||||
: QAbstractListModel{parent}
|
||||
{
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> EffectComposerUniformsModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roles;
|
||||
roles[NameRole] = "uniformName";
|
||||
roles[DescriptionRole] = "uniformDescription";
|
||||
roles[ValueRole] = "uniformValue";
|
||||
roles[BackendValueRole] = "uniformBackendValue";
|
||||
roles[DefaultValueRole] = "uniformDefaultValue";
|
||||
roles[MinValueRole] = "uniformMinValue";
|
||||
roles[MaxValueRole] = "uniformMaxValue";
|
||||
roles[TypeRole] = "uniformType";
|
||||
roles[UseCustomValueRole] = "uniformUseCustomValue";
|
||||
return roles;
|
||||
}
|
||||
|
||||
int EffectComposerUniformsModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
|
||||
return m_uniforms.size();
|
||||
}
|
||||
|
||||
QVariant EffectComposerUniformsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
QTC_ASSERT(index.isValid() && index.row() < m_uniforms.size(), return {});
|
||||
QTC_ASSERT(roleNames().contains(role), return {});
|
||||
|
||||
return m_uniforms.at(index.row())->property(roleNames().value(role));
|
||||
}
|
||||
|
||||
bool EffectComposerUniformsModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (!index.isValid() || !roleNames().contains(role))
|
||||
return false;
|
||||
|
||||
auto uniform = m_uniforms.at(index.row());
|
||||
|
||||
if (uniform->type() == Uniform::Type::Sampler) {
|
||||
QString updatedValue = value.toString();
|
||||
int idx = value.toString().indexOf("file:");
|
||||
|
||||
QString path = idx > 0 ? updatedValue.right(updatedValue.size() - idx - 5) : updatedValue;
|
||||
updatedValue = QUrl::fromLocalFile(path).toString();
|
||||
|
||||
uniform->setValue(updatedValue);
|
||||
g_propertyData.insert(uniform->name(), updatedValue);
|
||||
} else {
|
||||
uniform->setValue(value);
|
||||
g_propertyData.insert(uniform->name(), value);
|
||||
}
|
||||
|
||||
emit dataChanged(index, index, {role});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EffectComposerUniformsModel::resetModel()
|
||||
{
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void EffectComposerUniformsModel::addUniform(Uniform *uniform)
|
||||
{
|
||||
beginInsertRows({}, m_uniforms.size(), m_uniforms.size());
|
||||
m_uniforms.append(uniform);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
QList<Uniform *> EffectComposerUniformsModel::uniforms() const
|
||||
{
|
||||
return m_uniforms;
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
47
src/plugins/effectcomposer/effectcomposeruniformsmodel.h
Normal file
47
src/plugins/effectcomposer/effectcomposeruniformsmodel.h
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class Uniform;
|
||||
|
||||
class EffectComposerUniformsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EffectComposerUniformsModel(QObject *parent = nullptr);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex & parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
|
||||
void resetModel();
|
||||
|
||||
void addUniform(Uniform *uniform);
|
||||
|
||||
QList<Uniform *> uniforms() const;
|
||||
|
||||
private:
|
||||
enum Roles {
|
||||
NameRole = Qt::UserRole + 1,
|
||||
DescriptionRole,
|
||||
ValueRole,
|
||||
BackendValueRole,
|
||||
DefaultValueRole,
|
||||
MaxValueRole,
|
||||
MinValueRole,
|
||||
TypeRole,
|
||||
UseCustomValueRole
|
||||
};
|
||||
|
||||
QList<Uniform *> m_uniforms;
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
99
src/plugins/effectcomposer/effectcomposerview.cpp
Normal file
99
src/plugins/effectcomposer/effectcomposerview.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "effectcomposerview.h"
|
||||
|
||||
#include "effectcomposermodel.h"
|
||||
#include "effectcomposernodesmodel.h"
|
||||
#include "effectcomposerwidget.h"
|
||||
|
||||
#include <documentmanager.h>
|
||||
#include <modelnodeoperations.h>
|
||||
#include <qmldesignerconstants.h>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
EffectComposerContext::EffectComposerContext(QWidget *widget)
|
||||
: IContext(widget)
|
||||
{
|
||||
setWidget(widget);
|
||||
setContext(Core::Context(QmlDesigner::Constants::C_QMLEFFECTCOMPOSER,
|
||||
QmlDesigner::Constants::C_QT_QUICK_TOOLS_MENU));
|
||||
}
|
||||
|
||||
void EffectComposerContext::contextHelp(const HelpCallback &callback) const
|
||||
{
|
||||
qobject_cast<EffectComposerWidget *>(m_widget)->contextHelp(callback);
|
||||
}
|
||||
|
||||
EffectComposerView::EffectComposerView(QmlDesigner::ExternalDependenciesInterface &externalDependencies)
|
||||
: AbstractView{externalDependencies}
|
||||
{
|
||||
}
|
||||
|
||||
EffectComposerView::~EffectComposerView()
|
||||
{}
|
||||
|
||||
bool EffectComposerView::hasWidget() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QmlDesigner::WidgetInfo EffectComposerView::widgetInfo()
|
||||
{
|
||||
if (m_widget.isNull()) {
|
||||
m_widget = new EffectComposerWidget{this};
|
||||
|
||||
connect(m_widget->effectComposerModel(), &EffectComposerModel::assignToSelectedTriggered, this,
|
||||
[&] (const QString &effectPath) {
|
||||
executeInTransaction("EffectComposerView::widgetInfo", [&] {
|
||||
const QList<QmlDesigner::ModelNode> selectedNodes = selectedModelNodes();
|
||||
for (const QmlDesigner::ModelNode &node : selectedNodes)
|
||||
QmlDesigner::ModelNodeOperations::handleItemLibraryEffectDrop(effectPath, node);
|
||||
});
|
||||
});
|
||||
|
||||
auto context = new EffectComposerContext(m_widget.data());
|
||||
Core::ICore::addContextObject(context);
|
||||
}
|
||||
|
||||
return createWidgetInfo(m_widget.data(), "Effect Composer",
|
||||
QmlDesigner::WidgetInfo::LeftPane, 0, tr("Effect Composer [beta]"));
|
||||
}
|
||||
|
||||
void EffectComposerView::customNotification([[maybe_unused]] const AbstractView *view,
|
||||
const QString &identifier,
|
||||
[[maybe_unused]] const QList<QmlDesigner::ModelNode> &nodeList,
|
||||
const QList<QVariant> &data)
|
||||
{
|
||||
if (identifier == "open_effectcomposer_composition" && data.count() > 0) {
|
||||
const QString compositionPath = data[0].toString();
|
||||
m_widget->openComposition(compositionPath);
|
||||
}
|
||||
}
|
||||
|
||||
void EffectComposerView::modelAttached(QmlDesigner::Model *model)
|
||||
{
|
||||
AbstractView::modelAttached(model);
|
||||
|
||||
m_widget->effectComposerNodesModel()->loadModel();
|
||||
|
||||
QString currProjectPath = QmlDesigner::DocumentManager::currentProjectDirPath().toString();
|
||||
|
||||
// if starting a new project, clear the effect composer
|
||||
if (m_currProjectPath != currProjectPath)
|
||||
m_widget->effectComposerModel()->clear();
|
||||
|
||||
m_currProjectPath = currProjectPath;
|
||||
|
||||
m_widget->initView();
|
||||
}
|
||||
|
||||
void EffectComposerView::modelAboutToBeDetached(QmlDesigner::Model *model)
|
||||
{
|
||||
AbstractView::modelAboutToBeDetached(model);
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
46
src/plugins/effectcomposer/effectcomposerview.h
Normal file
46
src/plugins/effectcomposer/effectcomposerview.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "abstractview.h"
|
||||
|
||||
#include <coreplugin/icontext.h>
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class EffectComposerWidget;
|
||||
|
||||
class EffectComposerContext : public Core::IContext
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EffectComposerContext(QWidget *widget);
|
||||
void contextHelp(const Core::IContext::HelpCallback &callback) const override;
|
||||
};
|
||||
|
||||
class EffectComposerView : public QmlDesigner::AbstractView
|
||||
{
|
||||
public:
|
||||
EffectComposerView(QmlDesigner::ExternalDependenciesInterface &externalDependencies);
|
||||
~EffectComposerView() override;
|
||||
|
||||
bool hasWidget() const override;
|
||||
QmlDesigner::WidgetInfo widgetInfo() override;
|
||||
|
||||
// AbstractView
|
||||
void modelAttached(QmlDesigner::Model *model) override;
|
||||
void modelAboutToBeDetached(QmlDesigner::Model *model) override;
|
||||
|
||||
private:
|
||||
void customNotification(const AbstractView *view, const QString &identifier,
|
||||
const QList<QmlDesigner::ModelNode> &nodeList, const QList<QVariant> &data) override;
|
||||
|
||||
QPointer<EffectComposerWidget> m_widget;
|
||||
QString m_currProjectPath;
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
284
src/plugins/effectcomposer/effectcomposerwidget.cpp
Normal file
284
src/plugins/effectcomposer/effectcomposerwidget.cpp
Normal file
@@ -0,0 +1,284 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "effectcomposerwidget.h"
|
||||
|
||||
#include "effectcomposercontextobject.h"
|
||||
#include "effectcomposermodel.h"
|
||||
#include "effectcomposernodesmodel.h"
|
||||
#include "effectcomposerview.h"
|
||||
#include "effectutils.h"
|
||||
#include "propertyhandler.h"
|
||||
|
||||
//#include "qmldesigner/designercore/imagecache/midsizeimagecacheprovider.h"
|
||||
#include "qmldesignerconstants.h"
|
||||
#include "qmldesignerplugin.h"
|
||||
#include "theme.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <qmldesigner/documentmanager.h>
|
||||
#include <qmldesigner/qmldesignerconstants.h>
|
||||
#include <qmldesigner/qmldesignerplugin.h>
|
||||
#include <studioquickwidget.h>
|
||||
|
||||
#include <qmljs/qmljsmodelmanagerinterface.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/async.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QQmlContext>
|
||||
#include <QQmlEngine>
|
||||
#include <QQuickItem>
|
||||
#include <QTimer>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
static QString propertyEditorResourcesPath()
|
||||
{
|
||||
#ifdef SHARE_QML_PATH
|
||||
if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||
return QLatin1String(SHARE_QML_PATH) + "/propertyEditorQmlSources";
|
||||
#endif
|
||||
return Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources").toString();
|
||||
}
|
||||
|
||||
EffectComposerWidget::EffectComposerWidget(EffectComposerView *view)
|
||||
: m_effectComposerModel{new EffectComposerModel(this)}
|
||||
, m_effectComposerNodesModel{new EffectComposerNodesModel(this)}
|
||||
, m_effectComposerView(view)
|
||||
, m_quickWidget{new StudioQuickWidget(this)}
|
||||
{
|
||||
setWindowTitle(tr("Effect Composer", "Title of effect composer widget"));
|
||||
setMinimumWidth(250);
|
||||
|
||||
m_quickWidget->quickWidget()->installEventFilter(this);
|
||||
|
||||
// create the inner widget
|
||||
m_quickWidget->quickWidget()->setObjectName(QmlDesigner::Constants::OBJECT_NAME_EFFECT_COMPOSER);
|
||||
m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView);
|
||||
QmlDesigner::Theme::setupTheme(m_quickWidget->engine());
|
||||
m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
|
||||
m_quickWidget->engine()->addImportPath(EffectUtils::nodesSourcesPath() + "/common");
|
||||
m_quickWidget->setClearColor(QmlDesigner::Theme::getColor(
|
||||
QmlDesigner::Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
|
||||
|
||||
auto layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins({});
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(m_quickWidget.data());
|
||||
|
||||
setStyleSheet(QmlDesigner::Theme::replaceCssColors(
|
||||
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
|
||||
|
||||
QmlDesigner::QmlDesignerPlugin::trackWidgetFocusTime(this, QmlDesigner::Constants::EVENT_EFFECTCOMPOSER_TIME);
|
||||
|
||||
m_quickWidget->rootContext()->setContextProperty("g_propertyData", &g_propertyData);
|
||||
|
||||
QString blurPath = "file:" + EffectUtils::nodesSourcesPath() + "/common/";
|
||||
g_propertyData.insert(QString("blur_vs_path"), QString(blurPath + "bluritems.vert.qsb"));
|
||||
g_propertyData.insert(QString("blur_fs_path"), QString(blurPath + "bluritems.frag.qsb"));
|
||||
|
||||
auto map = m_quickWidget->registerPropertyMap("EffectComposerBackend");
|
||||
map->setProperties({{"effectComposerNodesModel", QVariant::fromValue(m_effectComposerNodesModel.data())},
|
||||
{"effectComposerModel", QVariant::fromValue(m_effectComposerModel.data())},
|
||||
{"rootView", QVariant::fromValue(this)}});
|
||||
|
||||
connect(m_effectComposerModel.data(), &EffectComposerModel::nodesChanged, this, [this]() {
|
||||
m_effectComposerNodesModel->updateCanBeAdded(m_effectComposerModel->uniformNames());
|
||||
});
|
||||
|
||||
connect(m_effectComposerModel.data(), &EffectComposerModel::resourcesSaved,
|
||||
this, [this](const QmlDesigner::TypeName &type, const Utils::FilePath &path) {
|
||||
if (!m_importScan.timer) {
|
||||
m_importScan.timer = new QTimer(this);
|
||||
connect(m_importScan.timer, &QTimer::timeout,
|
||||
this, &EffectComposerWidget::handleImportScanTimer);
|
||||
}
|
||||
|
||||
if (m_importScan.timer->isActive() && !m_importScan.future.isFinished())
|
||||
m_importScan.future.cancel();
|
||||
|
||||
m_importScan.counter = 0;
|
||||
m_importScan.type = type;
|
||||
m_importScan.path = path;
|
||||
|
||||
m_importScan.timer->start(100);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool EffectComposerWidget::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
Q_UNUSED(obj)
|
||||
Q_UNUSED(event)
|
||||
|
||||
// TODO
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EffectComposerWidget::contextHelp(const Core::IContext::HelpCallback &callback) const
|
||||
{
|
||||
Q_UNUSED(callback)
|
||||
}
|
||||
|
||||
StudioQuickWidget *EffectComposerWidget::quickWidget() const
|
||||
{
|
||||
return m_quickWidget.data();
|
||||
}
|
||||
|
||||
QPointer<EffectComposerModel> EffectComposerWidget::effectComposerModel() const
|
||||
{
|
||||
return m_effectComposerModel;
|
||||
}
|
||||
|
||||
QPointer<EffectComposerNodesModel> EffectComposerWidget::effectComposerNodesModel() const
|
||||
{
|
||||
return m_effectComposerNodesModel;
|
||||
}
|
||||
|
||||
void EffectComposerWidget::addEffectNode(const QString &nodeQenPath)
|
||||
{
|
||||
m_effectComposerModel->addNode(nodeQenPath);
|
||||
}
|
||||
|
||||
void EffectComposerWidget::focusSection(int section)
|
||||
{
|
||||
Q_UNUSED(section)
|
||||
}
|
||||
|
||||
QRect EffectComposerWidget::screenRect() const
|
||||
{
|
||||
if (m_quickWidget && m_quickWidget->screen())
|
||||
return m_quickWidget->screen()->availableGeometry();
|
||||
return {};
|
||||
}
|
||||
|
||||
QPoint EffectComposerWidget::globalPos(const QPoint &point) const
|
||||
{
|
||||
if (m_quickWidget)
|
||||
return m_quickWidget->mapToGlobal(point);
|
||||
return point;
|
||||
}
|
||||
|
||||
QSize EffectComposerWidget::sizeHint() const
|
||||
{
|
||||
return {420, 420};
|
||||
}
|
||||
|
||||
QString EffectComposerWidget::qmlSourcesPath()
|
||||
{
|
||||
#ifdef SHARE_QML_PATH
|
||||
if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||
return QLatin1String(SHARE_QML_PATH) + "/effectComposerQmlSources";
|
||||
#endif
|
||||
return Core::ICore::resourcePath("qmldesigner/effectComposerQmlSources").toString();
|
||||
}
|
||||
|
||||
void EffectComposerWidget::initView()
|
||||
{
|
||||
auto ctxObj = new EffectComposerContextObject(m_quickWidget->rootContext());
|
||||
m_quickWidget->rootContext()->setContextObject(ctxObj);
|
||||
|
||||
m_backendModelNode.setup(m_effectComposerView->rootModelNode());
|
||||
m_quickWidget->rootContext()->setContextProperty("anchorBackend", &m_backendAnchorBinding);
|
||||
m_quickWidget->rootContext()->setContextProperty("modelNodeBackend", &m_backendModelNode);
|
||||
m_quickWidget->rootContext()->setContextProperty("activeDragSuffix", "");
|
||||
|
||||
//TODO: Fix crash on macos
|
||||
// m_quickWidget->engine()->addImageProvider("qmldesigner_thumbnails",
|
||||
// new QmlDesigner::AssetImageProvider(
|
||||
// QmlDesigner::QmlDesignerPlugin::imageCache()));
|
||||
|
||||
// init the first load of the QML UI elements
|
||||
reloadQmlSource();
|
||||
}
|
||||
|
||||
void EffectComposerWidget::openComposition(const QString &path)
|
||||
{
|
||||
m_compositionPath = path;
|
||||
|
||||
if (effectComposerModel()->hasUnsavedChanges())
|
||||
QMetaObject::invokeMethod(quickWidget()->rootObject(), "promptToSaveBeforeOpen");
|
||||
else
|
||||
doOpenComposition();
|
||||
}
|
||||
|
||||
void EffectComposerWidget::doOpenComposition()
|
||||
{
|
||||
effectComposerModel()->openComposition(m_compositionPath);
|
||||
}
|
||||
|
||||
void EffectComposerWidget::reloadQmlSource()
|
||||
{
|
||||
const QString effectComposerQmlPath = qmlSourcesPath() + "/EffectComposer.qml";
|
||||
QTC_ASSERT(QFileInfo::exists(effectComposerQmlPath), return);
|
||||
m_quickWidget->setSource(QUrl::fromLocalFile(effectComposerQmlPath));
|
||||
}
|
||||
|
||||
void EffectComposerWidget::handleImportScanTimer()
|
||||
{
|
||||
++m_importScan.counter;
|
||||
|
||||
if (m_importScan.counter == 1) {
|
||||
// Rescan the effect import to update code model
|
||||
auto modelManager = QmlJS::ModelManagerInterface::instance();
|
||||
if (modelManager) {
|
||||
QmlJS::PathsAndLanguages pathToScan;
|
||||
pathToScan.maybeInsert(m_importScan.path);
|
||||
m_importScan.future = ::Utils::asyncRun(&QmlJS::ModelManagerInterface::importScan,
|
||||
modelManager->workingCopy(),
|
||||
pathToScan, modelManager, true, true, true);
|
||||
}
|
||||
} else if (m_importScan.counter < 100) {
|
||||
// We have to wait a while to ensure qmljs detects new files and updates its
|
||||
// internal model. Then we force amend on rewriter to trigger qmljs snapshot update.
|
||||
if (m_importScan.future.isCanceled() || m_importScan.future.isFinished())
|
||||
m_importScan.counter = 100; // skip the timeout step
|
||||
} else if (m_importScan.counter == 100) {
|
||||
// Scanning is taking too long, abort
|
||||
m_importScan.future.cancel();
|
||||
m_importScan.timer->stop();
|
||||
m_importScan.counter = 0;
|
||||
} else if (m_importScan.counter == 101) {
|
||||
if (m_effectComposerView->model() && m_effectComposerView->model()->rewriterView()) {
|
||||
QmlDesigner::QmlDesignerPlugin::instance()->documentManager().resetPossibleImports();
|
||||
m_effectComposerView->model()->rewriterView()->forceAmend();
|
||||
}
|
||||
} else if (m_importScan.counter == 102) {
|
||||
if (m_effectComposerView->model()) {
|
||||
// If type is in use, we have to reset puppet to update 2D view
|
||||
if (!m_effectComposerView->allModelNodesOfType(
|
||||
m_effectComposerView->model()->metaInfo(m_importScan.type)).isEmpty()) {
|
||||
m_effectComposerView->resetPuppet();
|
||||
}
|
||||
}
|
||||
} else if (m_importScan.counter >= 103) {
|
||||
// Refresh property view by resetting selection if any selected node is of updated type
|
||||
if (m_effectComposerView->model() && m_effectComposerView->hasSelectedModelNodes()) {
|
||||
const auto nodes = m_effectComposerView->selectedModelNodes();
|
||||
QmlDesigner::MetaInfoType metaType
|
||||
= m_effectComposerView->model()->metaInfo(m_importScan.type).type();
|
||||
bool match = false;
|
||||
for (const QmlDesigner::ModelNode &node : nodes) {
|
||||
if (node.metaInfo().type() == metaType) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
m_effectComposerView->clearSelectedModelNodes();
|
||||
m_effectComposerView->setSelectedModelNodes(nodes);
|
||||
}
|
||||
}
|
||||
m_importScan.timer->stop();
|
||||
m_importScan.counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
83
src/plugins/effectcomposer/effectcomposerwidget.h
Normal file
83
src/plugins/effectcomposer/effectcomposerwidget.h
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <qmldesigner/components/propertyeditor/qmlanchorbindingproxy.h>
|
||||
#include <qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h>
|
||||
|
||||
#include <coreplugin/icontext.h>
|
||||
|
||||
#include <QFrame>
|
||||
#include <QFuture>
|
||||
|
||||
class StudioQuickWidget;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTimer;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class EffectComposerView;
|
||||
class EffectComposerModel;
|
||||
class EffectComposerNodesModel;
|
||||
|
||||
class EffectComposerWidget : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
EffectComposerWidget(EffectComposerView *view);
|
||||
~EffectComposerWidget() = default;
|
||||
|
||||
void contextHelp(const Core::IContext::HelpCallback &callback) const;
|
||||
|
||||
static QString qmlSourcesPath();
|
||||
void clearSearchFilter();
|
||||
|
||||
void delayedUpdateModel();
|
||||
void updateModel();
|
||||
void initView();
|
||||
void openComposition(const QString &path);
|
||||
|
||||
StudioQuickWidget *quickWidget() const;
|
||||
QPointer<EffectComposerModel> effectComposerModel() const;
|
||||
QPointer<EffectComposerNodesModel> effectComposerNodesModel() const;
|
||||
|
||||
Q_INVOKABLE void addEffectNode(const QString &nodeQenPath);
|
||||
Q_INVOKABLE void focusSection(int section);
|
||||
Q_INVOKABLE void doOpenComposition();
|
||||
Q_INVOKABLE QRect screenRect() const;
|
||||
Q_INVOKABLE QPoint globalPos(const QPoint &point) const;
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
private:
|
||||
void reloadQmlSource();
|
||||
void handleImportScanTimer();
|
||||
|
||||
QPointer<EffectComposerModel> m_effectComposerModel;
|
||||
QPointer<EffectComposerNodesModel> m_effectComposerNodesModel;
|
||||
QPointer<EffectComposerView> m_effectComposerView;
|
||||
QPointer<StudioQuickWidget> m_quickWidget;
|
||||
QmlDesigner::QmlModelNodeProxy m_backendModelNode;
|
||||
QmlDesigner::QmlAnchorBindingProxy m_backendAnchorBinding;
|
||||
|
||||
struct ImportScanData {
|
||||
QFuture<void> future;
|
||||
int counter = 0;
|
||||
QTimer *timer = nullptr;
|
||||
QmlDesigner::TypeName type;
|
||||
Utils::FilePath path;
|
||||
};
|
||||
|
||||
ImportScanData m_importScan;
|
||||
QString m_compositionPath;
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
67
src/plugins/effectcomposer/effectnode.cpp
Normal file
67
src/plugins/effectcomposer/effectnode.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "effectnode.h"
|
||||
#include "compositionnode.h"
|
||||
#include "uniform.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
EffectNode::EffectNode(const QString &qenPath)
|
||||
: m_qenPath(qenPath)
|
||||
{
|
||||
const QFileInfo fileInfo = QFileInfo(qenPath);
|
||||
|
||||
QString iconPath = QStringLiteral("%1/icon/%2.svg").arg(fileInfo.absolutePath(),
|
||||
fileInfo.baseName());
|
||||
if (!QFileInfo::exists(iconPath)) {
|
||||
QDir parentDir = QDir(fileInfo.absolutePath());
|
||||
parentDir.cdUp();
|
||||
|
||||
iconPath = QStringLiteral("%1/%2").arg(parentDir.path(), "placeholder.svg");
|
||||
}
|
||||
m_iconPath = QUrl::fromLocalFile(iconPath);
|
||||
|
||||
CompositionNode node({}, qenPath);
|
||||
|
||||
m_name = node.name();
|
||||
m_description = node.description();
|
||||
|
||||
const QList<Uniform *> uniforms = node.uniforms();
|
||||
for (const Uniform *uniform : uniforms)
|
||||
m_uniformNames.insert(uniform->name());
|
||||
}
|
||||
|
||||
QString EffectNode::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
QString EffectNode::description() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
QString EffectNode::qenPath() const
|
||||
{
|
||||
return m_qenPath;
|
||||
}
|
||||
|
||||
void EffectNode::setCanBeAdded(bool enabled)
|
||||
{
|
||||
if (enabled != m_canBeAdded) {
|
||||
m_canBeAdded = enabled;
|
||||
emit canBeAddedChanged();
|
||||
}
|
||||
}
|
||||
|
||||
bool EffectNode::hasUniform(const QString &name)
|
||||
{
|
||||
return m_uniformNames.contains(name);
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
46
src/plugins/effectcomposer/effectnode.h
Normal file
46
src/plugins/effectcomposer/effectnode.h
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QUrl>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class EffectNode : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString nodeName MEMBER m_name CONSTANT)
|
||||
Q_PROPERTY(QString nodeDescription MEMBER m_description CONSTANT)
|
||||
Q_PROPERTY(QUrl nodeIcon MEMBER m_iconPath CONSTANT)
|
||||
Q_PROPERTY(QString nodeQenPath MEMBER m_qenPath CONSTANT)
|
||||
Q_PROPERTY(bool canBeAdded MEMBER m_canBeAdded NOTIFY canBeAddedChanged)
|
||||
|
||||
public:
|
||||
EffectNode(const QString &qenPath);
|
||||
|
||||
QString name() const;
|
||||
QString description() const;
|
||||
QString qenPath() const;
|
||||
|
||||
void setCanBeAdded(bool enabled);
|
||||
|
||||
bool hasUniform(const QString &name);
|
||||
|
||||
signals:
|
||||
void canBeAddedChanged();
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QString m_description;
|
||||
QString m_qenPath;
|
||||
QUrl m_iconPath;
|
||||
bool m_canBeAdded = true;
|
||||
QSet<QString> m_uniformNames;
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
23
src/plugins/effectcomposer/effectnodescategory.cpp
Normal file
23
src/plugins/effectcomposer/effectnodescategory.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "effectnodescategory.h"
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
EffectNodesCategory::EffectNodesCategory(const QString &name, const QList<EffectNode *> &nodes)
|
||||
: m_name(name),
|
||||
m_categoryNodes(nodes) {}
|
||||
|
||||
QString EffectNodesCategory::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
QList<EffectNode *> EffectNodesCategory::nodes() const
|
||||
{
|
||||
return m_categoryNodes;
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
31
src/plugins/effectcomposer/effectnodescategory.h
Normal file
31
src/plugins/effectcomposer/effectnodescategory.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "effectnode.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class EffectNodesCategory : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString categoryName MEMBER m_name CONSTANT)
|
||||
Q_PROPERTY(QList<EffectNode *> categoryNodes MEMBER m_categoryNodes CONSTANT)
|
||||
|
||||
public:
|
||||
EffectNodesCategory(const QString &name, const QList<EffectNode *> &nodes);
|
||||
|
||||
QString name() const;
|
||||
QList<EffectNode *> nodes() const;
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QList<EffectNode *> m_categoryNodes;
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
34
src/plugins/effectcomposer/effectutils.cpp
Normal file
34
src/plugins/effectcomposer/effectutils.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "effectutils.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <QJsonArray>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
QString EffectUtils::codeFromJsonArray(const QJsonArray &codeArray)
|
||||
{
|
||||
if (codeArray.isEmpty())
|
||||
return {};
|
||||
|
||||
QString codeString;
|
||||
for (const auto &element : codeArray)
|
||||
codeString += element.toString() + '\n';
|
||||
|
||||
codeString.chop(1); // Remove last '\n'
|
||||
return codeString;
|
||||
}
|
||||
|
||||
QString EffectUtils::nodesSourcesPath()
|
||||
{
|
||||
#ifdef SHARE_QML_PATH
|
||||
if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE"))
|
||||
return QLatin1String(SHARE_QML_PATH) + "/effectComposerNodes";
|
||||
#endif
|
||||
return Core::ICore::resourcePath("qmldesigner/effectComposerNodes").toString();
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
23
src/plugins/effectcomposer/effectutils.h
Normal file
23
src/plugins/effectcomposer/effectutils.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QJsonArray)
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class EffectUtils
|
||||
{
|
||||
public:
|
||||
EffectUtils() = delete;
|
||||
|
||||
static QString codeFromJsonArray(const QJsonArray &codeArray);
|
||||
|
||||
static QString nodesSourcesPath();
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
10
src/plugins/effectcomposer/propertyhandler.cpp
Normal file
10
src/plugins/effectcomposer/propertyhandler.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "propertyhandler.h"
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
QQmlPropertyMap g_propertyData;
|
||||
|
||||
}
|
||||
15
src/plugins/effectcomposer/propertyhandler.h
Normal file
15
src/plugins/effectcomposer/propertyhandler.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QQmlPropertyMap>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
// This will be used for binding dynamic properties
|
||||
// changes between C++ and QML.
|
||||
extern QQmlPropertyMap g_propertyData;
|
||||
|
||||
}
|
||||
|
||||
91
src/plugins/effectcomposer/shaderfeatures.cpp
Normal file
91
src/plugins/effectcomposer/shaderfeatures.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "shaderfeatures.h"
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
ShaderFeatures::ShaderFeatures()
|
||||
{
|
||||
}
|
||||
|
||||
// Browse the shaders and check which features are used in them.
|
||||
void ShaderFeatures::update(const QString &vs, const QString &fs, const QString &qml)
|
||||
{
|
||||
QStringList vsList = vs.split("\n");
|
||||
QStringList fsList = fs.split("\n");
|
||||
|
||||
const QStringList code = vsList + fsList;
|
||||
Features newFeatures = {};
|
||||
m_gridMeshWidth = 1;
|
||||
m_gridMeshHeight = 1;
|
||||
for (const QString &line : code)
|
||||
checkLine(line, newFeatures);
|
||||
|
||||
// iTime may also be used in QML side, without being used in shaders.
|
||||
// In this case enable the time helpers creation.
|
||||
if (qml.contains("iTime"))
|
||||
newFeatures.setFlag(Time, true);
|
||||
|
||||
if (newFeatures != m_enabledFeatures)
|
||||
m_enabledFeatures = newFeatures;
|
||||
}
|
||||
|
||||
bool ShaderFeatures::enabled(ShaderFeatures::Feature feature) const
|
||||
{
|
||||
return m_enabledFeatures.testFlag(feature);
|
||||
}
|
||||
|
||||
void ShaderFeatures::checkLine(const QString &line, Features &features)
|
||||
{
|
||||
if (line.contains("iTime"))
|
||||
features.setFlag(Time, true);
|
||||
|
||||
if (line.contains("iFrame"))
|
||||
features.setFlag(Frame, true);
|
||||
|
||||
if (line.contains("iResolution"))
|
||||
features.setFlag(Resolution, true);
|
||||
|
||||
if (line.contains("iSource"))
|
||||
features.setFlag(Source, true);
|
||||
|
||||
if (line.contains("iMouse"))
|
||||
features.setFlag(Mouse, true);
|
||||
|
||||
if (line.contains("fragCoord"))
|
||||
features.setFlag(FragCoord, true);
|
||||
|
||||
if (line.contains("@mesh")) {
|
||||
// Get the mesh size, remove "@mesh"
|
||||
QString l = line.trimmed().sliced(5);
|
||||
QStringList list = l.split(QLatin1Char(','));
|
||||
if (list.size() >= 2) {
|
||||
int w = list.at(0).trimmed().toInt();
|
||||
int h = list.at(1).trimmed().toInt();
|
||||
// Set size to max values
|
||||
m_gridMeshWidth = std::max(m_gridMeshWidth, w);
|
||||
m_gridMeshHeight = std::max(m_gridMeshHeight, h);
|
||||
}
|
||||
// If is bigger than default (1, 1), set the feature
|
||||
if (m_gridMeshWidth > 1 || m_gridMeshHeight > 1)
|
||||
features.setFlag(GridMesh, true);
|
||||
}
|
||||
if (line.contains("@blursources"))
|
||||
features.setFlag(BlurSources, true);
|
||||
}
|
||||
|
||||
int ShaderFeatures::gridMeshHeight() const
|
||||
{
|
||||
return m_gridMeshHeight;
|
||||
}
|
||||
|
||||
int ShaderFeatures::gridMeshWidth() const
|
||||
{
|
||||
return m_gridMeshWidth;
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
44
src/plugins/effectcomposer/shaderfeatures.h
Normal file
44
src/plugins/effectcomposer/shaderfeatures.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QFlags>
|
||||
#include <QString>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class ShaderFeatures
|
||||
{
|
||||
public:
|
||||
enum Feature {
|
||||
Time = 1 << 0,
|
||||
Frame = 1 << 1,
|
||||
Resolution = 1 << 2,
|
||||
Source = 1 << 3,
|
||||
Mouse = 1 << 4,
|
||||
FragCoord = 1 << 5,
|
||||
GridMesh = 1 << 6,
|
||||
BlurSources = 1 << 7
|
||||
};
|
||||
Q_DECLARE_FLAGS(Features, Feature)
|
||||
|
||||
ShaderFeatures();
|
||||
void update(const QString &vs, const QString &fs, const QString &qml);
|
||||
|
||||
bool enabled(ShaderFeatures::Feature feature) const;
|
||||
|
||||
int gridMeshWidth() const;
|
||||
|
||||
int gridMeshHeight() const;
|
||||
|
||||
private:
|
||||
void checkLine(const QString &line, ShaderFeatures::Features &features);
|
||||
ShaderFeatures::Features m_enabledFeatures;
|
||||
int m_gridMeshWidth = 1;
|
||||
int m_gridMeshHeight = 1;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(ShaderFeatures::Features)
|
||||
} // namespace EffectComposer
|
||||
|
||||
191
src/plugins/effectcomposer/syntaxhighlighterdata.cpp
Normal file
191
src/plugins/effectcomposer/syntaxhighlighterdata.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include "syntaxhighlighterdata.h"
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
static constexpr QByteArrayView shader_arg_names[] {
|
||||
{ "gl_Position" },
|
||||
{ "qt_MultiTexCoord0" },
|
||||
{ "qt_Vertex" },
|
||||
{ "qt_Matrix" },
|
||||
{ "qt_Opacity" },
|
||||
{ "vertCoord" },
|
||||
{ "fragCoord" },
|
||||
{ "texCoord" },
|
||||
{ "fragColor" },
|
||||
{ "iMouse" },
|
||||
{ "iResolution" },
|
||||
{ "iTime" },
|
||||
{ "iFrame" },
|
||||
{ "iSource" },
|
||||
{ "iSourceBlur1" },
|
||||
{ "iSourceBlur2" },
|
||||
{ "iSourceBlur3" },
|
||||
{ "iSourceBlur4" },
|
||||
{ "iSourceBlur5" },
|
||||
{ "iSourceBlur6" }
|
||||
};
|
||||
|
||||
static constexpr QByteArrayView shader_tag_names[] {
|
||||
{ "@main" },
|
||||
{ "@nodes" },
|
||||
{ "@mesh" },
|
||||
{ "@blursources" },
|
||||
{ "@requires" }
|
||||
};
|
||||
|
||||
// From https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.40.pdf
|
||||
// Not including functions only available with compatibility profile
|
||||
static constexpr QByteArrayView shader_function_names[] {
|
||||
{ "radians()" },
|
||||
{ "degrees()" },
|
||||
{ "sin()" },
|
||||
{ "cos()" },
|
||||
{ "tan()" },
|
||||
{ "asin()" },
|
||||
{ "acos()" },
|
||||
{ "atan()" },
|
||||
{ "sinh()" },
|
||||
{ "cosh()" },
|
||||
{ "tanh()" },
|
||||
{ "asinh()" },
|
||||
{ "acosh()" },
|
||||
{ "atanh()" },
|
||||
{ "pow()" },
|
||||
{ "exp()" },
|
||||
{ "log()" },
|
||||
{ "exp2()" },
|
||||
{ "log2()" },
|
||||
{ "sqrt()" },
|
||||
{ "inversesqrt()" },
|
||||
{ "abs()" },
|
||||
{ "sign()" },
|
||||
{ "floor()" },
|
||||
{ "trunc()" },
|
||||
{ "round()" },
|
||||
{ "roundEven()" },
|
||||
{ "ceil()" },
|
||||
{ "fract()" },
|
||||
{ "mod()" },
|
||||
{ "modf()" },
|
||||
{ "min()" },
|
||||
{ "max()" },
|
||||
{ "clamp()" },
|
||||
{ "mix()" },
|
||||
{ "step()" },
|
||||
{ "smoothstep()" },
|
||||
{ "isnan()" },
|
||||
{ "isinf()" },
|
||||
{ "floatBitsToInt()" },
|
||||
{ "intBitsToFloat()" },
|
||||
{ "fma()" },
|
||||
{ "frexp()" },
|
||||
{ "ldexp()" },
|
||||
{ "packUnorm2x16()" },
|
||||
{ "packSnorm2x16()" },
|
||||
{ "packUnorm4x8()" },
|
||||
{ "packSnorm4x8()" },
|
||||
{ "unpackUnorm2x16()" },
|
||||
{ "unpackSnorm2x16()" },
|
||||
{ "unpackUnorm4x8()" },
|
||||
{ "unpackSnorm4x8()" },
|
||||
//{ "packDouble2x32()" }, // Not supported in HLSL
|
||||
//{ "unpackDouble2x32()" },
|
||||
{ "packHalf2x16()" },
|
||||
{ "unpackHalf2x16()" },
|
||||
{ "length()" },
|
||||
{ "distance()" },
|
||||
{ "dot()" },
|
||||
{ "cross()" },
|
||||
{ "normalize()" },
|
||||
{ "faceforward()" },
|
||||
{ "reflect()" },
|
||||
{ "refract()" },
|
||||
{ "matrixCompMult()" },
|
||||
{ "outerProduct()" },
|
||||
{ "transpose()" },
|
||||
{ "determinant()" },
|
||||
{ "inverse()" },
|
||||
{ "lessThan()" },
|
||||
{ "lessThanEqual()" },
|
||||
{ "greaterThan()" },
|
||||
{ "greaterThanEqual()" },
|
||||
{ "equal()" },
|
||||
{ "notEqual()" },
|
||||
{ "any()" },
|
||||
{ "all()" },
|
||||
{ "not()" },
|
||||
//{ "uaddCarry()" }, // Extended arithmetic is only available from ESSL 310
|
||||
//{ "usubBorrow()" },
|
||||
//{ "umulExtended()" },
|
||||
//{ "imulExtended()" },
|
||||
{ "bitfieldExtract()" },
|
||||
{ "bitfieldInsert()" },
|
||||
{ "bitfieldReverse()" },
|
||||
{ "bitCount()" },
|
||||
{ "findLSB()" },
|
||||
{ "findMSB()" },
|
||||
{ "textureSize()" },
|
||||
//{ "textureQueryLod()" }, // ImageQueryLod is only supported on MSL 2.2 and up.
|
||||
//{ "textureQueryLevels()" }, // textureQueryLevels not supported in ES profile.
|
||||
{ "texture()" },
|
||||
{ "textureProj()" },
|
||||
{ "textureLod()" },
|
||||
{ "textureOffset()" },
|
||||
{ "texelFetch()" },
|
||||
{ "texelFetchOffset()" },
|
||||
{ "textureProjOffset()" },
|
||||
{ "textureLodOffset()" },
|
||||
{ "textureProjLod()" },
|
||||
{ "textureProjLodOffset()" },
|
||||
{ "textureGrad()" },
|
||||
{ "textureGradOffset()" },
|
||||
{ "textureProjGrad()" },
|
||||
{ "textureProjGradOffset()" },
|
||||
//{ "textureGather()" }, // textureGather requires ESSL 310.
|
||||
//{ "textureGatherOffset()" },
|
||||
//{ "textureGatherOffsets()" },
|
||||
//{ "atomicCounterIncrement()" }, // 'atomic counter types' : not allowed when using GLSL for Vulkan
|
||||
//{ "atomicCounterDecrement()" },
|
||||
//{ "atomicCounter()" },
|
||||
//{ "atomicAdd()" }, // HLSL: interlocked targets must be groupshared or UAV elements
|
||||
//{ "atomicMin()" },
|
||||
//{ "atomicMax()" },
|
||||
//{ "atomicAnd()" },
|
||||
//{ "atomicOr()" },
|
||||
//{ "atomicXor()" },
|
||||
//{ "atomicExchange()" },
|
||||
//{ "atomicCompSwap()" },
|
||||
{ "dFdx()" },
|
||||
{ "dFdy()" },
|
||||
{ "fwidth()" }
|
||||
//{ "interpolateAtCentroid()" }, // Pull-model interpolation requires MSL 2.3.
|
||||
//{ "interpolateAtSample()" },
|
||||
//{ "interpolateAtOffset()" }
|
||||
};
|
||||
|
||||
SyntaxHighlighterData::SyntaxHighlighterData()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QList<QByteArrayView> SyntaxHighlighterData::reservedArgumentNames()
|
||||
{
|
||||
return { std::begin(shader_arg_names), std::end(shader_arg_names) };
|
||||
}
|
||||
|
||||
QList<QByteArrayView> SyntaxHighlighterData::reservedTagNames()
|
||||
{
|
||||
return { std::begin(shader_tag_names), std::end(shader_tag_names) };
|
||||
}
|
||||
|
||||
QList<QByteArrayView> SyntaxHighlighterData::reservedFunctionNames()
|
||||
{
|
||||
return { std::begin(shader_function_names), std::end(shader_function_names) };
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
|
||||
23
src/plugins/effectcomposer/syntaxhighlighterdata.h
Normal file
23
src/plugins/effectcomposer/syntaxhighlighterdata.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArrayView>
|
||||
#include <QList>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class SyntaxHighlighterData
|
||||
{
|
||||
public:
|
||||
SyntaxHighlighterData();
|
||||
|
||||
static QList<QByteArrayView> reservedArgumentNames();
|
||||
static QList<QByteArrayView> reservedTagNames();
|
||||
static QList<QByteArrayView> reservedFunctionNames();
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
|
||||
|
||||
345
src/plugins/effectcomposer/uniform.cpp
Normal file
345
src/plugins/effectcomposer/uniform.cpp
Normal file
@@ -0,0 +1,345 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "uniform.h"
|
||||
#include <qmldesignerplugin.h>
|
||||
|
||||
#include "propertyhandler.h"
|
||||
|
||||
#include <modelnodeoperations.h>
|
||||
|
||||
#include <QColor>
|
||||
#include <QJsonObject>
|
||||
#include <QVector2D>
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
Uniform::Uniform(const QString &effectName, const QJsonObject &propObj, const QString &qenPath)
|
||||
: m_qenPath(qenPath)
|
||||
{
|
||||
QString value, defaultValue, minValue, maxValue;
|
||||
|
||||
m_name = propObj.value("name").toString();
|
||||
m_description = propObj.value("description").toString();
|
||||
m_type = Uniform::typeFromString(propObj.value("type").toString());
|
||||
defaultValue = propObj.value("defaultValue").toString();
|
||||
|
||||
m_displayName = propObj.value("displayName").toString();
|
||||
if (m_displayName.isEmpty())
|
||||
m_displayName = m_name;
|
||||
|
||||
value = propObj.contains("value") ? propObj.value("value").toString() : defaultValue;
|
||||
|
||||
if (m_type == Type::Sampler) {
|
||||
if (!defaultValue.isEmpty())
|
||||
defaultValue = getResourcePath(effectName, defaultValue, qenPath);
|
||||
if (!value.isEmpty())
|
||||
value = getResourcePath(effectName, value, qenPath);
|
||||
if (value.isEmpty())
|
||||
value = defaultValue;
|
||||
if (propObj.contains("enableMipmap"))
|
||||
m_enableMipmap = getBoolValue(propObj.value("enableMipmap"), false);
|
||||
// Update the mipmap property
|
||||
QString mipmapProperty = mipmapPropertyName(m_name);
|
||||
g_propertyData[mipmapProperty] = m_enableMipmap;
|
||||
}
|
||||
|
||||
m_customValue = propObj.value("customValue").toString();
|
||||
m_useCustomValue = getBoolValue(propObj.value("useCustomValue"), false);
|
||||
|
||||
minValue = propObj.value("minValue").toString();
|
||||
maxValue = propObj.value("maxValue").toString();
|
||||
|
||||
setValueData(value, defaultValue, minValue, maxValue);
|
||||
|
||||
m_backendValue = new QmlDesigner::PropertyEditorValue(this);
|
||||
m_backendValue->setValue(value);
|
||||
}
|
||||
|
||||
Uniform::Type Uniform::type() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
// String representation of the type for qml
|
||||
QString Uniform::typeName() const
|
||||
{
|
||||
return Uniform::stringFromType(m_type);
|
||||
}
|
||||
|
||||
QVariant Uniform::value() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
QVariant Uniform::backendValue() const
|
||||
{
|
||||
return QVariant::fromValue(m_backendValue);
|
||||
}
|
||||
|
||||
void Uniform::setValue(const QVariant &newValue)
|
||||
{
|
||||
if (m_value != newValue) {
|
||||
m_value = newValue;
|
||||
emit uniformValueChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant Uniform::defaultValue() const
|
||||
{
|
||||
return m_defaultValue;
|
||||
}
|
||||
|
||||
QVariant Uniform::minValue() const
|
||||
{
|
||||
return m_minValue;
|
||||
}
|
||||
|
||||
QVariant Uniform::maxValue() const
|
||||
{
|
||||
return m_maxValue;
|
||||
}
|
||||
|
||||
QString Uniform::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
QString Uniform::description() const
|
||||
{
|
||||
return m_description;
|
||||
}
|
||||
|
||||
QString Uniform::displayName() const
|
||||
{
|
||||
return m_displayName;
|
||||
}
|
||||
|
||||
QString Uniform::customValue() const
|
||||
{
|
||||
return m_customValue;
|
||||
}
|
||||
|
||||
void Uniform::setCustomValue(const QString &newCustomValue)
|
||||
{
|
||||
m_customValue = newCustomValue;
|
||||
}
|
||||
|
||||
bool Uniform::useCustomValue() const
|
||||
{
|
||||
return m_useCustomValue;
|
||||
}
|
||||
|
||||
bool Uniform::enabled() const
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
void Uniform::setEnabled(bool newEnabled)
|
||||
{
|
||||
m_enabled = newEnabled;
|
||||
}
|
||||
|
||||
bool Uniform::enableMipmap() const
|
||||
{
|
||||
return m_enableMipmap;
|
||||
}
|
||||
|
||||
// Returns name for image mipmap property.
|
||||
// e.g. "myImage" -> "myImageMipmap".
|
||||
QString Uniform::mipmapPropertyName(const QString &name) const
|
||||
{
|
||||
QString simplifiedName = name.simplified();
|
||||
simplifiedName = simplifiedName.remove(' ');
|
||||
simplifiedName += "Mipmap";
|
||||
return simplifiedName;
|
||||
}
|
||||
|
||||
// Returns the boolean value of QJsonValue. It can be either boolean
|
||||
// (true, false) or string ("true", "false"). Returns the defaultValue
|
||||
// if QJsonValue is undefined, empty, or some other type.
|
||||
bool Uniform::getBoolValue(const QJsonValue &jsonValue, bool defaultValue)
|
||||
{
|
||||
if (jsonValue.isBool())
|
||||
return jsonValue.toBool();
|
||||
|
||||
if (jsonValue.isString())
|
||||
return jsonValue.toString().toLower() == "true";
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// Returns the path for a shader resource
|
||||
// Used with sampler types
|
||||
QString Uniform::getResourcePath(const QString &effectName, const QString &value, const QString &qenPath) const
|
||||
{
|
||||
QString filePath = value;
|
||||
if (qenPath.isEmpty()) {
|
||||
const Utils::FilePath effectsResDir = QmlDesigner::ModelNodeOperations::getEffectsImportDirectory();
|
||||
return effectsResDir.pathAppended(effectName).pathAppended(value).toString();
|
||||
} else {
|
||||
QDir dir(m_qenPath);
|
||||
dir.cdUp();
|
||||
QString absPath = dir.absoluteFilePath(filePath);
|
||||
absPath = QDir::cleanPath(absPath);
|
||||
absPath = QUrl::fromLocalFile(absPath).toString();
|
||||
return absPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Validation and setting values
|
||||
void Uniform::setValueData(const QString &value, const QString &defaultValue,
|
||||
const QString &minValue, const QString &maxValue)
|
||||
{
|
||||
m_value = value.isEmpty() ? getInitializedVariant(false) : valueStringToVariant(value);
|
||||
m_defaultValue = defaultValue.isEmpty() ? getInitializedVariant(false)
|
||||
: valueStringToVariant(defaultValue);
|
||||
m_minValue = minValue.isEmpty() ? getInitializedVariant(false) : valueStringToVariant(minValue);
|
||||
m_maxValue = maxValue.isEmpty() ? getInitializedVariant(true) : valueStringToVariant(maxValue);
|
||||
}
|
||||
|
||||
// Initialize the value variant with correct type
|
||||
QVariant Uniform::getInitializedVariant(bool maxValue)
|
||||
{
|
||||
switch (m_type) {
|
||||
case Uniform::Type::Bool:
|
||||
return maxValue ? true : false;
|
||||
case Uniform::Type::Int:
|
||||
return maxValue ? 100 : 0;
|
||||
case Uniform::Type::Float:
|
||||
return maxValue ? 1.0 : 0.0;
|
||||
case Uniform::Type::Vec2:
|
||||
return maxValue ? QVector2D(1.0, 1.0) : QVector2D(0.0, 0.0);
|
||||
case Uniform::Type::Vec3:
|
||||
return maxValue ? QVector3D(1.0, 1.0, 1.0) : QVector3D(0.0, 0.0, 0.0);
|
||||
case Uniform::Type::Vec4:
|
||||
return maxValue ? QVector4D(1.0, 1.0, 1.0, 1.0) : QVector4D(0.0, 0.0, 0.0, 0.0);
|
||||
case Uniform::Type::Color:
|
||||
return maxValue ? QColor::fromRgbF(1.0f, 1.0f, 1.0f, 1.0f) : QColor::fromRgbF(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QVariant Uniform::valueStringToVariant(const QString &value)
|
||||
{
|
||||
QVariant variant;
|
||||
switch (m_type) {
|
||||
case Type::Bool:
|
||||
variant = (value == "true");
|
||||
break;
|
||||
case Type::Int:
|
||||
case Type::Float:
|
||||
variant = value;
|
||||
break;
|
||||
case Type::Vec2: {
|
||||
QStringList list = value.split(QLatin1Char(','));
|
||||
if (list.size() >= 2)
|
||||
variant = QVector2D(list.at(0).toDouble(), list.at(1).toDouble());
|
||||
}
|
||||
break;
|
||||
case Type::Vec3: {
|
||||
QStringList list = value.split(QLatin1Char(','));
|
||||
if (list.size() >= 3)
|
||||
variant = QVector3D(list.at(0).toDouble(), list.at(1).toDouble(), list.at(2).toDouble());
|
||||
}
|
||||
break;
|
||||
case Type::Vec4: {
|
||||
QStringList list = value.split(QLatin1Char(','));
|
||||
if (list.size() >= 4)
|
||||
variant = QVector4D(list.at(0).toDouble(), list.at(1).toDouble(),
|
||||
list.at(2).toDouble(), list.at(3).toDouble());
|
||||
}
|
||||
break;
|
||||
case Type::Color: {
|
||||
QStringList list = value.split(QLatin1Char(','));
|
||||
if (list.size() >= 4)
|
||||
variant = QColor::fromRgbF(list.at(0).toDouble(), list.at(1).toDouble(),
|
||||
list.at(2).toDouble(), list.at(3).toDouble());
|
||||
}
|
||||
break;
|
||||
case Type::Sampler:
|
||||
variant = value;
|
||||
break;
|
||||
case Uniform::Type::Define:
|
||||
variant = value;
|
||||
break;
|
||||
}
|
||||
|
||||
return variant;
|
||||
}
|
||||
|
||||
QString Uniform::stringFromType(Uniform::Type type, bool isShader)
|
||||
{
|
||||
if (type == Type::Bool)
|
||||
return "bool";
|
||||
else if (type == Type::Int)
|
||||
return "int";
|
||||
else if (type == Type::Float)
|
||||
return "float";
|
||||
else if (type == Type::Vec2)
|
||||
return "vec2";
|
||||
else if (type == Type::Vec3)
|
||||
return "vec3";
|
||||
else if (type == Type::Vec4)
|
||||
return "vec4";
|
||||
else if (type == Type::Color)
|
||||
return isShader ? QString("vec4") : QString("color");
|
||||
else if (type == Type::Sampler)
|
||||
return "sampler2D";
|
||||
else if (type == Type::Define)
|
||||
return "define";
|
||||
|
||||
qWarning() << QString("Unknown type");
|
||||
return "float";
|
||||
}
|
||||
|
||||
Uniform::Type Uniform::typeFromString(const QString &typeString)
|
||||
{
|
||||
if (typeString == "bool")
|
||||
return Uniform::Type::Bool;
|
||||
else if (typeString == "int")
|
||||
return Uniform::Type::Int;
|
||||
else if (typeString == "float")
|
||||
return Uniform::Type::Float;
|
||||
else if (typeString == "vec2")
|
||||
return Uniform::Type::Vec2;
|
||||
else if (typeString == "vec3")
|
||||
return Uniform::Type::Vec3;
|
||||
else if (typeString == "vec4")
|
||||
return Uniform::Type::Vec4;
|
||||
else if (typeString == "color")
|
||||
return Uniform::Type::Color;
|
||||
else if (typeString == "sampler2D" || typeString == "image") //TODO: change image to sample2D in all QENs
|
||||
return Uniform::Type::Sampler;
|
||||
else if (typeString == "define")
|
||||
return Uniform::Type::Define;
|
||||
|
||||
qWarning() << QString("Unknown type: %1").arg(typeString).toLatin1();
|
||||
return Uniform::Type::Float;
|
||||
}
|
||||
|
||||
QString Uniform::typeToProperty(Uniform::Type type)
|
||||
{
|
||||
if (type == Uniform::Type::Bool)
|
||||
return "bool";
|
||||
else if (type == Uniform::Type::Int)
|
||||
return "int";
|
||||
else if (type == Uniform::Type::Float)
|
||||
return "real";
|
||||
else if (type == Uniform::Type::Vec2)
|
||||
return "point";
|
||||
else if (type == Uniform::Type::Vec3)
|
||||
return "vector3d";
|
||||
else if (type == Uniform::Type::Vec4)
|
||||
return "vector4d";
|
||||
else if (type == Uniform::Type::Color)
|
||||
return "color";
|
||||
else if (type == Uniform::Type::Sampler || type == Uniform::Type::Define)
|
||||
return "var";
|
||||
|
||||
qWarning() << QString("Unhandled const variable type: %1").arg(int(type)).toLatin1();
|
||||
return QString();
|
||||
}
|
||||
|
||||
} // namespace EffectComposer
|
||||
112
src/plugins/effectcomposer/uniform.h
Normal file
112
src/plugins/effectcomposer/uniform.h
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
#include <qmldesigner/components/propertyeditor/propertyeditorvalue.h>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QColor)
|
||||
QT_FORWARD_DECLARE_CLASS(QJsonObject)
|
||||
QT_FORWARD_DECLARE_CLASS(QVector2D)
|
||||
|
||||
namespace EffectComposer {
|
||||
|
||||
class Uniform : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString uniformName MEMBER m_displayName CONSTANT)
|
||||
Q_PROPERTY(QString uniformType READ typeName CONSTANT)
|
||||
Q_PROPERTY(QString uniformDescription READ description CONSTANT)
|
||||
Q_PROPERTY(QVariant uniformValue READ value WRITE setValue NOTIFY uniformValueChanged)
|
||||
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 uniformUseCustomValue MEMBER m_useCustomValue CONSTANT)
|
||||
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
Bool,
|
||||
Int,
|
||||
Float,
|
||||
Vec2,
|
||||
Vec3,
|
||||
Vec4,
|
||||
Color,
|
||||
Sampler,
|
||||
Define
|
||||
};
|
||||
|
||||
Uniform(const QString &effectName, const QJsonObject &props, const QString &qenPath);
|
||||
|
||||
Type type() const;
|
||||
QString typeName() const;
|
||||
|
||||
QVariant value() const;
|
||||
void setValue(const QVariant &newValue);
|
||||
|
||||
QVariant backendValue() const;
|
||||
|
||||
QVariant defaultValue() const;
|
||||
|
||||
QVariant minValue() const;
|
||||
QVariant maxValue() const;
|
||||
|
||||
QString name() const;
|
||||
QString description() const;
|
||||
QString displayName() const;
|
||||
|
||||
QString customValue() const;
|
||||
void setCustomValue(const QString &newCustomValue);
|
||||
bool useCustomValue() const;
|
||||
|
||||
bool enabled() const;
|
||||
void setEnabled(bool newEnabled);
|
||||
|
||||
bool enableMipmap() const;
|
||||
|
||||
static QString stringFromType(Uniform::Type type, bool isShader = false);
|
||||
static Uniform::Type typeFromString(const QString &typeString);
|
||||
static QString typeToProperty(Uniform::Type type);
|
||||
|
||||
signals:
|
||||
void uniformValueChanged();
|
||||
void uniformBackendValueChanged();
|
||||
|
||||
private:
|
||||
QString mipmapPropertyName(const QString &name) const;
|
||||
bool getBoolValue(const QJsonValue &jsonValue, bool defaultValue);
|
||||
QString getResourcePath(const QString &effectName, const QString &value, const QString &qenPath) const;
|
||||
void setValueData(const QString &value, const QString &defaultValue,
|
||||
const QString &minValue, const QString &maxValue);
|
||||
|
||||
QVariant getInitializedVariant(bool maxValue);
|
||||
QVariant valueStringToVariant(const QString &value);
|
||||
|
||||
QString m_qenPath;
|
||||
Type m_type;
|
||||
QVariant m_value;
|
||||
QVariant m_defaultValue;
|
||||
QVariant m_minValue;
|
||||
QVariant m_maxValue;
|
||||
QString m_name;
|
||||
QString m_displayName;
|
||||
QString m_description;
|
||||
QString m_customValue;
|
||||
bool m_useCustomValue = false;
|
||||
bool m_enabled = true;
|
||||
bool m_enableMipmap = false;
|
||||
QmlDesigner::PropertyEditorValue *m_backendValue = nullptr;
|
||||
|
||||
bool operator==(const Uniform &rhs) const noexcept
|
||||
{
|
||||
return this->m_name == rhs.m_name;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace EffectComposer
|
||||
Reference in New Issue
Block a user