From 7b183a774c981c760c2e6a8e2a8b2f6f05dee633 Mon Sep 17 00:00:00 2001 From: Amr Essam Date: Fri, 15 Sep 2023 15:36:15 +0300 Subject: QmlDesigner: Move effect maker to an independent plugin Task-number: QDS-10656 Change-Id: I1c1e67d3461650bfaec46ccc10b832effce76ad9 Reviewed-by: Mahmoud Badri --- src/plugins/CMakeLists.txt | 4 + src/plugins/effectmakernew/CMakeLists.txt | 27 + src/plugins/effectmakernew/EffectMakerNew.json.in | 15 + src/plugins/effectmakernew/compositionnode.cpp | 123 ++++ src/plugins/effectmakernew/compositionnode.h | 61 ++ .../effectmakernew/effectmakercontextobject.cpp | 186 +++++ .../effectmakernew/effectmakercontextobject.h | 102 +++ src/plugins/effectmakernew/effectmakermodel.cpp | 764 +++++++++++++++++++++ src/plugins/effectmakernew/effectmakermodel.h | 139 ++++ .../effectmakernew/effectmakernodesmodel.cpp | 108 +++ src/plugins/effectmakernew/effectmakernodesmodel.h | 44 ++ src/plugins/effectmakernew/effectmakerplugin.cpp | 46 ++ src/plugins/effectmakernew/effectmakerplugin.h | 32 + .../effectmakernew/effectmakeruniformsmodel.cpp | 76 ++ .../effectmakernew/effectmakeruniformsmodel.h | 46 ++ src/plugins/effectmakernew/effectmakerview.cpp | 83 +++ src/plugins/effectmakernew/effectmakerview.h | 46 ++ src/plugins/effectmakernew/effectmakerwidget.cpp | 147 ++++ src/plugins/effectmakernew/effectmakerwidget.h | 60 ++ src/plugins/effectmakernew/effectnode.cpp | 43 ++ src/plugins/effectmakernew/effectnode.h | 35 + src/plugins/effectmakernew/effectnodescategory.cpp | 23 + src/plugins/effectmakernew/effectnodescategory.h | 31 + src/plugins/effectmakernew/effectutils.cpp | 24 + src/plugins/effectmakernew/effectutils.h | 21 + src/plugins/effectmakernew/shaderfeatures.cpp | 81 +++ src/plugins/effectmakernew/shaderfeatures.h | 40 ++ .../effectmakernew/syntaxhighlighterdata.cpp | 191 ++++++ src/plugins/effectmakernew/syntaxhighlighterdata.h | 23 + src/plugins/effectmakernew/uniform.cpp | 324 +++++++++ src/plugins/effectmakernew/uniform.h | 110 +++ src/plugins/qmldesigner/CMakeLists.txt | 18 - .../components/componentcore/viewmanager.cpp | 9 - .../components/effectmaker/compositionnode.cpp | 122 ---- .../components/effectmaker/compositionnode.h | 60 -- .../effectmaker/effectmakercontextobject.cpp | 185 ----- .../effectmaker/effectmakercontextobject.h | 101 --- .../components/effectmaker/effectmakermodel.cpp | 763 -------------------- .../components/effectmaker/effectmakermodel.h | 138 ---- .../effectmaker/effectmakernodesmodel.cpp | 107 --- .../components/effectmaker/effectmakernodesmodel.h | 43 -- .../effectmaker/effectmakeruniformsmodel.cpp | 75 -- .../effectmaker/effectmakeruniformsmodel.h | 45 -- .../components/effectmaker/effectmakerview.cpp | 69 -- .../components/effectmaker/effectmakerview.h | 34 - .../components/effectmaker/effectmakerwidget.cpp | 145 ---- .../components/effectmaker/effectmakerwidget.h | 59 -- .../components/effectmaker/effectnode.cpp | 42 -- .../components/effectmaker/effectnode.h | 34 - .../components/effectmaker/effectnodescategory.cpp | 22 - .../components/effectmaker/effectnodescategory.h | 30 - .../components/effectmaker/effectutils.cpp | 23 - .../components/effectmaker/effectutils.h | 20 - .../components/effectmaker/shaderfeatures.cpp | 80 --- .../components/effectmaker/shaderfeatures.h | 39 -- .../effectmaker/syntaxhighlighterdata.cpp | 190 ----- .../components/effectmaker/syntaxhighlighterdata.h | 22 - .../qmldesigner/components/effectmaker/uniform.cpp | 324 --------- .../qmldesigner/components/effectmaker/uniform.h | 108 --- .../propertyeditor/propertyeditorvalue.h | 2 +- .../components/propertyeditor/qmlmodelnodeproxy.h | 2 +- src/plugins/qmldesigner/designmodecontext.cpp | 13 - src/plugins/qmldesigner/designmodecontext.h | 9 - 63 files changed, 3057 insertions(+), 2931 deletions(-) create mode 100644 src/plugins/effectmakernew/CMakeLists.txt create mode 100644 src/plugins/effectmakernew/EffectMakerNew.json.in create mode 100644 src/plugins/effectmakernew/compositionnode.cpp create mode 100644 src/plugins/effectmakernew/compositionnode.h create mode 100644 src/plugins/effectmakernew/effectmakercontextobject.cpp create mode 100644 src/plugins/effectmakernew/effectmakercontextobject.h create mode 100644 src/plugins/effectmakernew/effectmakermodel.cpp create mode 100644 src/plugins/effectmakernew/effectmakermodel.h create mode 100644 src/plugins/effectmakernew/effectmakernodesmodel.cpp create mode 100644 src/plugins/effectmakernew/effectmakernodesmodel.h create mode 100644 src/plugins/effectmakernew/effectmakerplugin.cpp create mode 100644 src/plugins/effectmakernew/effectmakerplugin.h create mode 100644 src/plugins/effectmakernew/effectmakeruniformsmodel.cpp create mode 100644 src/plugins/effectmakernew/effectmakeruniformsmodel.h create mode 100644 src/plugins/effectmakernew/effectmakerview.cpp create mode 100644 src/plugins/effectmakernew/effectmakerview.h create mode 100644 src/plugins/effectmakernew/effectmakerwidget.cpp create mode 100644 src/plugins/effectmakernew/effectmakerwidget.h create mode 100644 src/plugins/effectmakernew/effectnode.cpp create mode 100644 src/plugins/effectmakernew/effectnode.h create mode 100644 src/plugins/effectmakernew/effectnodescategory.cpp create mode 100644 src/plugins/effectmakernew/effectnodescategory.h create mode 100644 src/plugins/effectmakernew/effectutils.cpp create mode 100644 src/plugins/effectmakernew/effectutils.h create mode 100644 src/plugins/effectmakernew/shaderfeatures.cpp create mode 100644 src/plugins/effectmakernew/shaderfeatures.h create mode 100644 src/plugins/effectmakernew/syntaxhighlighterdata.cpp create mode 100644 src/plugins/effectmakernew/syntaxhighlighterdata.h create mode 100644 src/plugins/effectmakernew/uniform.cpp create mode 100644 src/plugins/effectmakernew/uniform.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/compositionnode.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakerview.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectnode.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectnode.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectutils.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/effectutils.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h delete mode 100644 src/plugins/qmldesigner/components/effectmaker/uniform.cpp delete mode 100644 src/plugins/qmldesigner/components/effectmaker/uniform.h diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index 4fd8b65573..68985fbf00 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -112,3 +112,7 @@ add_subdirectory(mcusupport) add_subdirectory(saferenderer) add_subdirectory(copilot) add_subdirectory(terminal) + +if (WITH_QMLDESIGNER) + add_subdirectory(effectmakernew) +endif() diff --git a/src/plugins/effectmakernew/CMakeLists.txt b/src/plugins/effectmakernew/CMakeLists.txt new file mode 100644 index 0000000000..09d70a7fea --- /dev/null +++ b/src/plugins/effectmakernew/CMakeLists.txt @@ -0,0 +1,27 @@ +find_package(Qt6 OPTIONAL_COMPONENTS Gui Quick ShaderTools) + +add_qtc_plugin(EffectMakerNew + CONDITION TARGET QmlDesigner AND TARGET Qt::ShaderTools + PLUGIN_DEPENDS + QtCreator::Core QtCreator::QmlDesigner + DEPENDS + Qt::Core + QtCreator::Utils Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick Qt::ShaderTools + SOURCES + effectmakerplugin.cpp effectmakerplugin.h + effectmakerwidget.cpp effectmakerwidget.h + effectmakerview.cpp effectmakerview.h + effectmakermodel.cpp effectmakermodel.h + effectmakernodesmodel.cpp effectmakernodesmodel.h + effectmakeruniformsmodel.cpp effectmakeruniformsmodel.h + effectnode.cpp effectnode.h + effectnodescategory.cpp effectnodescategory.h + compositionnode.cpp compositionnode.h + uniform.cpp uniform.h + effectutils.cpp effectutils.h + effectmakercontextobject.cpp effectmakercontextobject.h + shaderfeatures.cpp shaderfeatures.h + syntaxhighlighterdata.cpp syntaxhighlighterdata.h + + BUILD_DEFAULT OFF +) diff --git a/src/plugins/effectmakernew/EffectMakerNew.json.in b/src/plugins/effectmakernew/EffectMakerNew.json.in new file mode 100644 index 0000000000..46c5e12247 --- /dev/null +++ b/src/plugins/effectmakernew/EffectMakerNew.json.in @@ -0,0 +1,15 @@ +{ + \"Name\" : \"EffectMakerNew\", + \"Version\" : \"$$QTCREATOR_VERSION\", + \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", + \"Revision\" : \"$$QTC_PLUGIN_REVISION\", + \"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 Maker.\", + \"Url\" : \"http://www.qt.io\", + $$dependencyList +} diff --git a/src/plugins/effectmakernew/compositionnode.cpp b/src/plugins/effectmakernew/compositionnode.cpp new file mode 100644 index 0000000000..74e43c76d5 --- /dev/null +++ b/src/plugins/effectmakernew/compositionnode.cpp @@ -0,0 +1,123 @@ +// 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 "effectmakeruniformsmodel.h" +#include "uniform.h" + +#include +#include +#include +#include + +namespace EffectMaker { + +CompositionNode::CompositionNode(const QString &qenPath) +{ + parse(qenPath); +} + +QString CompositionNode::fragmentCode() const +{ + return m_fragmentCode; +} + +QString CompositionNode::vertexCode() const +{ + return m_vertexCode; +} + +QString CompositionNode::description() const +{ + return m_description; +} + +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(); + } +} + +CompositionNode::NodeType CompositionNode::type() const +{ + return m_type; +} + +void CompositionNode::parse(const QString &qenPath) +{ + 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 the effect node: %1:").arg(qenPath); + QString errorDetails = QString("%1: %2").arg(parseError.offset).arg(parseError.errorString()); + qWarning() << qPrintable(error); + qWarning() << qPrintable(errorDetails); + return; + } + + QJsonObject json = jsonDoc.object().value("QEN").toObject(); + + 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()); + + // parse properties + QJsonArray jsonProps = json.value("properties").toArray(); + for (const auto /*QJsonValueRef*/ &prop : jsonProps) + m_unifomrsModel.addUniform(new Uniform(prop.toObject())); + + // 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 l = trimmedLine.sliced(9).trimmed(); + QString nodeName = trimmedLine.sliced(10); + if (!nodeName.isEmpty() && !m_requiredNodes.contains(nodeName)) + m_requiredNodes << nodeName; + } + } +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/compositionnode.h b/src/plugins/effectmakernew/compositionnode.h new file mode 100644 index 0000000000..2637bfb387 --- /dev/null +++ b/src/plugins/effectmakernew/compositionnode.h @@ -0,0 +1,61 @@ +// 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 "effectmakeruniformsmodel.h" + +#include + +namespace EffectMaker { + +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(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged) + +public: + enum NodeType { + SourceNode = 0, + DestinationNode, + CustomNode + }; + + CompositionNode(const QString &qenPath); + + QString fragmentCode() const; + QString vertexCode() const; + QString description() const; + + QObject *uniformsModel(); + + QStringList requiredNodes() const; + + NodeType type() const; + + bool isEnabled() const; + void setIsEnabled(bool newIsEnabled); + +signals: + void uniformsModelChanged(); + void isEnabledChanged(); + +private: + void parse(const QString &qenPath); + + QString m_name; + NodeType m_type = CustomNode; + QString m_fragmentCode; + QString m_vertexCode; + QString m_description; + QStringList m_requiredNodes; + bool m_isEnabled = true; + + EffectMakerUniformsModel m_unifomrsModel; +}; + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakercontextobject.cpp b/src/plugins/effectmakernew/effectmakercontextobject.cpp new file mode 100644 index 0000000000..6a37765d5c --- /dev/null +++ b/src/plugins/effectmakernew/effectmakercontextobject.cpp @@ -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 "effectmakercontextobject.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace EffectMaker { + +EffectMakerContextObject::EffectMakerContextObject(QQmlContext *context, QObject *parent) + : QObject(parent) + , m_qmlContext(context) +{ +} + +QString EffectMakerContextObject::convertColorToString(const QVariant &color) +{ + QString colorString; + QColor theColor; + if (color.canConvert(QVariant::Color)) { + theColor = color.value(); + } else if (color.canConvert(QVariant::Vector3D)) { + auto vec = color.value(); + 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 EffectMakerContextObject::colorFromString(const QString &colorString) +{ + return colorString; +} + +int EffectMakerContextObject::majorVersion() const +{ + return m_majorVersion; +} + +void EffectMakerContextObject::setMajorVersion(int majorVersion) +{ + if (m_majorVersion == majorVersion) + return; + + m_majorVersion = majorVersion; + + emit majorVersionChanged(); +} + +void EffectMakerContextObject::setStateName(const QString &newStateName) +{ + if (newStateName == m_stateName) + return; + + m_stateName = newStateName; + emit stateNameChanged(); +} + +void EffectMakerContextObject::setAllStateNames(const QStringList &allStates) +{ + if (allStates == m_allStateNames) + return; + + m_allStateNames = allStates; + emit allStateNamesChanged(); +} + +void EffectMakerContextObject::setIsBaseState(bool newIsBaseState) +{ + if (newIsBaseState == m_isBaseState) + return; + + m_isBaseState = newIsBaseState; + emit isBaseStateChanged(); +} + +void EffectMakerContextObject::setSelectionChanged(bool newSelectionChanged) +{ + if (newSelectionChanged == m_selectionChanged) + return; + + m_selectionChanged = newSelectionChanged; + emit selectionChangedChanged(); +} + +void EffectMakerContextObject::setBackendValues(QQmlPropertyMap *newBackendValues) +{ + if (newBackendValues == m_backendValues) + return; + + m_backendValues = newBackendValues; + emit backendValuesChanged(); +} + +void EffectMakerContextObject::setModel(QmlDesigner::Model *model) +{ + m_model = model; +} + +void EffectMakerContextObject::triggerSelectionChanged() +{ + setSelectionChanged(!m_selectionChanged); +} + +void EffectMakerContextObject::hideCursor() +{ + if (QApplication::overrideCursor()) + return; + + QApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); + + if (QWidget *w = QApplication::activeWindow()) + m_lastPos = QCursor::pos(w->screen()); +} + +void EffectMakerContextObject::restoreCursor() +{ + if (!QApplication::overrideCursor()) + return; + + QApplication::restoreOverrideCursor(); + + if (QWidget *w = QApplication::activeWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +void EffectMakerContextObject::holdCursorInPlace() +{ + if (!QApplication::overrideCursor()) + return; + + if (QWidget *w = QApplication::activeWindow()) + QCursor::setPos(w->screen(), m_lastPos); +} + +int EffectMakerContextObject::devicePixelRatio() +{ + if (QWidget *w = QApplication::activeWindow()) + return w->devicePixelRatio(); + + return 1; +} + +QStringList EffectMakerContextObject::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 EffectMakerContextObject::isBlocked(const QString &) const +{ + return false; +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakercontextobject.h b/src/plugins/effectmakernew/effectmakercontextobject.h new file mode 100644 index 0000000000..9b76dc36d8 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakercontextobject.h @@ -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 +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace EffectMaker { + +class EffectMakerContextObject : 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: + EffectMakerContextObject(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 EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp new file mode 100644 index 0000000000..438fb8e420 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -0,0 +1,764 @@ +// 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 "effectmakermodel.h" + +#include "compositionnode.h" +#include "syntaxhighlighterdata.h" +#include "uniform.h" + +#include +#include + +#include + +namespace EffectMaker { + +EffectMakerModel::EffectMakerModel(QObject *parent) + : QAbstractListModel{parent} +{ +} + +QHash EffectMakerModel::roleNames() const +{ + QHash roles; + roles[NameRole] = "nodeName"; + roles[EnabledRole] = "nodeEnabled"; + roles[UniformsRole] = "nodeUniformsModel"; + return roles; +} + +int EffectMakerModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_nodes.count(); +} + +QVariant EffectMakerModel::data(const QModelIndex &index, int role) const +{ + QTC_ASSERT(index.isValid() && index.row() < m_nodes.size(), return {}); + QTC_ASSERT(roleNames().contains(role), return {}); + + return m_nodes.at(index.row())->property(roleNames().value(role)); +} + +bool EffectMakerModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || !roleNames().contains(role)) + return false; + + if (role == EnabledRole) { + m_nodes.at(index.row())->setIsEnabled(value.toBool()); + emit dataChanged(index, index, {role}); + } + + return true; +} + +void EffectMakerModel::setIsEmpty(bool val) +{ + if (m_isEmpty != val) { + m_isEmpty = val; + emit isEmptyChanged(); + } +} + +void EffectMakerModel::addNode(const QString &nodeQenPath) +{ + beginInsertRows({}, m_nodes.size(), m_nodes.size()); + auto *node = new CompositionNode(nodeQenPath); + m_nodes.append(node); + endInsertRows(); + + setIsEmpty(false); +} + +void EffectMakerModel::moveNode(int fromIdx, int toIdx) +{ + if (fromIdx == toIdx) + return; + + int toIdxAdjusted = fromIdx < toIdx ? toIdx + 1 : toIdx; // otherwise beginMoveRows() crashes + beginMoveRows({}, fromIdx, fromIdx, {}, toIdxAdjusted); + m_nodes.move(fromIdx, toIdx); + endMoveRows(); +} + +void EffectMakerModel::removeNode(int idx) +{ + beginRemoveRows({}, idx, idx); + CompositionNode *node = m_nodes.at(idx); + m_nodes.removeAt(idx); + delete node; + endRemoveRows(); + + if (m_nodes.isEmpty()) + setIsEmpty(true); +} + +QString EffectMakerModel::fragmentShader() const +{ + return m_fragmentShader; +} + +void EffectMakerModel::setFragmentShader(const QString &newFragmentShader) +{ + if (m_fragmentShader == newFragmentShader) + return; + + m_fragmentShader = newFragmentShader; +} + +QString EffectMakerModel::vertexShader() const +{ + return m_vertexShader; +} + +void EffectMakerModel::setVertexShader(const QString &newVertexShader) +{ + if (m_vertexShader == newVertexShader) + return; + + m_vertexShader = newVertexShader; +} + +const QList EffectMakerModel::allUniforms() +{ + QList uniforms = {}; + for (const auto &node : std::as_const(m_nodes)) + uniforms.append(static_cast(node->uniformsModel())->uniforms()); + return uniforms; +} + +const QString EffectMakerModel::getBufUniform() +{ + QList uniforms = allUniforms(); + QString s; + s += "layout(std140, binding = 0) uniform buf {\n"; + s += " mat4 qt_Matrix;\n"; + s += " float qt_Opacity;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Time)) + s += " float iTime;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Frame)) + s += " int iFrame;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Resolution)) + s += " vec3 iResolution;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::Mouse)) + s += " vec4 iMouse;\n"; + for (const auto uniform : uniforms) { + // TODO: Check if uniform is already added. + if (uniform->type() != Uniform::Type::Sampler && uniform->type() != Uniform::Type::Define) { + QString type = Uniform::stringFromType(uniform->type()); + QString props = " " + type + " " + uniform->name() + ";\n"; + s += props; + } + } + s += "};\n"; + return s; +} + +const QString EffectMakerModel::getVSUniforms() +{ + QString s; + s += "#version 440\n"; + s += '\n'; + s += "layout(location = 0) in vec4 qt_Vertex;\n"; + s += "layout(location = 1) in vec2 qt_MultiTexCoord0;\n"; + s += "layout(location = 0) out vec2 texCoord;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::FragCoord)) + s += "layout(location = 1) out vec2 fragCoord;\n"; + s += '\n'; + s += getBufUniform(); + s += '\n'; + s += "out gl_PerVertex { vec4 gl_Position; };\n"; + s += '\n'; + return s; +} + +const QString EffectMakerModel::getFSUniforms() +{ + const QList uniforms = allUniforms(); + QString s; + s += "#version 440\n"; + s += '\n'; + s += "layout(location = 0) in vec2 texCoord;\n"; + if (m_shaderFeatures.enabled(ShaderFeatures::FragCoord)) + s += "layout(location = 1) in vec2 fragCoord;\n"; + s += "layout(location = 0) out vec4 fragColor;\n"; + s += '\n'; + s += getBufUniform(); + s += '\n'; + + bool usesSource = m_shaderFeatures.enabled(ShaderFeatures::Source); + if (usesSource) + s += "layout(binding = 1) uniform sampler2D iSource;\n"; + + // Add sampler uniforms + int bindingIndex = usesSource ? 2 : 1; + for (const auto uniform : uniforms) { + // TODO: Check if uniform is already added. + if (uniform->type() == Uniform::Type::Sampler) { + // Start index from 2, 1 is source item + QString props = QString("layout(binding = %1) uniform sampler2D %2") + .arg(bindingIndex).arg(uniform->name()); + s += props + ";\n"; + bindingIndex++; + } + } + s += '\n'; + if (m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) { + const int blurItems = 5; + for (int i = 1; i <= blurItems; i++) { + QString props = QString("layout(binding = %1) uniform sampler2D iSourceBlur%2") + .arg(bindingIndex).arg(QString::number(i)); + s += props + ";\n"; + bindingIndex++; + } + s += '\n'; + } + return s; +} + +// Detects common GLSL error messages and returns potential +// additional error information related to them. +QString EffectMakerModel::detectErrorMessage(const QString &errorMessage) +{ + static QHash nodeErrors { + { "'BLUR_HELPER_MAX_LEVEL' : undeclared identifier", "BlurHelper"}, + { "'iSourceBlur1' : undeclared identifier", "BlurHelper"}, + { "'hash23' : no matching overloaded function found", "NoiseHelper" }, + { "'HASH_BOX_SIZE' : undeclared identifier", "NoiseHelper" }, + { "'pseudo3dNoise' : no matching overloaded function found", "NoiseHelper" } + }; + + QString missingNodeError = QStringLiteral("Are you missing a %1 node?\n"); + QHash::const_iterator i = nodeErrors.constBegin(); + while (i != nodeErrors.constEnd()) { + if (errorMessage.contains(i.key())) + return missingNodeError.arg(i.value()); + ++i; + } + return QString(); +} + +// Return first error message (if any) +EffectError EffectMakerModel::effectError() const +{ + for (const EffectError &e : std::as_const(m_effectErrors)) { + if (!e.m_message.isEmpty()) + return e; + } + return {}; +} + +// Set the effect error message with optional type and lineNumber. +// Type comes from ErrorTypes, defaulting to common errors (-1). +// Note that type must match with UI editor tab index. +void EffectMakerModel::setEffectError(const QString &errorMessage, int type, int lineNumber) +{ + EffectError error; + error.m_type = type; + if (type == 1 || type == 2) { + // For shaders, get the line number from baker output. + // Which is something like "ERROR: :15: message" + int glslErrorLineNumber = -1; + QStringList errorStringList = errorMessage.split(m_spaceReg, Qt::SkipEmptyParts); + if (errorStringList.size() >= 2) { + QString lineString = errorStringList.at(1).trimmed(); + if (lineString.size() >= 3) { + // String is ":[linenumber]:", get only the number. + glslErrorLineNumber = lineString.sliced(1, lineString.size() - 2).toInt(); + } + } + error.m_line = glslErrorLineNumber; + } else { + // For QML (and others) use given linenumber + error.m_line = lineNumber; + } + + QString additionalErrorInfo = detectErrorMessage(errorMessage); + error.m_message = additionalErrorInfo + errorMessage; + m_effectErrors.insert(type, error); + Q_EMIT effectErrorChanged(); +} + +void EffectMakerModel::resetEffectError(int type) +{ + if (m_effectErrors.contains(type)) { + m_effectErrors.remove(type); + Q_EMIT effectErrorChanged(); + } +} + +// Get value in QML format that used for exports +QString EffectMakerModel::valueAsString(const Uniform &uniform) +{ + if (uniform.type() == Uniform::Type::Bool) { + return uniform.value().toBool() ? QString("true") : QString("false"); + } else if (uniform.type() == Uniform::Type::Int) { + return QString::number(uniform.value().toInt()); + } else if (uniform.type() == Uniform::Type::Float) { + return QString::number(uniform.value().toDouble()); + } else if (uniform.type() == Uniform::Type::Vec2) { + QVector2D v2 = uniform.value().value(); + return QString("Qt.point(%1, %2)").arg(v2.x(), v2.y()); + } else if (uniform.type() == Uniform::Type::Vec3) { + QVector3D v3 = uniform.value().value(); + return QString("Qt.vector3d(%1, %2, %3)").arg(v3.x(), v3.y(), v3.z()); + } else if (uniform.type() == Uniform::Type::Vec4) { + QVector4D v4 = uniform.value().value(); + return QString("Qt.vector4d(%1, %2, %3, %4)").arg(v4.x(), v4.y(), v4.z(), v4.w()); + } else if (uniform.type() == Uniform::Type::Color) { + QColor c = uniform.value().value(); + return QString("Qt.rgba(%1, %2, %3, %4)").arg(c.redF(), c.greenF(), c.blueF(), c.alphaF()); + } else if (uniform.type() == Uniform::Type::Sampler) { + return getImageElementName(uniform); + } else if (uniform.type() == Uniform::Type::Define) { + return uniform.value().toString(); + } else { + qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); + return QString(); + } +} + +// Get value in QML binding that used for previews +QString EffectMakerModel::valueAsBinding(const Uniform &uniform) +{ + if (uniform.type() == Uniform::Type::Bool || uniform.type() == Uniform::Type::Int + || uniform.type() == Uniform::Type::Float || uniform.type() == Uniform::Type::Define) { + return "g_propertyData." + uniform.name(); + } else if (uniform.type() == Uniform::Type::Vec2) { + QString sx = QString("g_propertyData.%1.x").arg(uniform.name()); + QString sy = QString("g_propertyData.%1.y").arg(uniform.name()); + return QString("Qt.point(%1, %2)").arg(sx, sy); + } else if (uniform.type() == Uniform::Type::Vec3) { + QString sx = QString("g_propertyData.%1.x").arg(uniform.name()); + QString sy = QString("g_propertyData.%1.y").arg(uniform.name()); + QString sz = QString("g_propertyData.%1.z").arg(uniform.name()); + return QString("Qt.vector3d(%1, %2, %3)").arg(sx, sy, sz); + } else if (uniform.type() == Uniform::Type::Vec4) { + QString sx = QString("g_propertyData.%1.x").arg(uniform.name()); + QString sy = QString("g_propertyData.%1.y").arg(uniform.name()); + QString sz = QString("g_propertyData.%1.z").arg(uniform.name()); + QString sw = QString("g_propertyData.%1.w").arg(uniform.name()); + return QString("Qt.vector4d(%1, %2, %3, %4)").arg(sx, sy, sz, sw); + } else if (uniform.type() == Uniform::Type::Color) { + QString sr = QString("g_propertyData.%1.r").arg(uniform.name()); + QString sg = QString("g_propertyData.%1.g").arg(uniform.name()); + QString sb = QString("g_propertyData.%1.b").arg(uniform.name()); + QString sa = QString("g_propertyData.%1.a").arg(uniform.name()); + return QString("Qt.rgba(%1, %2, %3, %4)").arg(sr, sg, sb, sa); + } else if (uniform.type() == Uniform::Type::Sampler) { + return getImageElementName(uniform); + } else { + qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); + return QString(); + } +} + +// Get value in GLSL format that is used for non-exported const properties +QString EffectMakerModel::valueAsVariable(const Uniform &uniform) +{ + if (uniform.type() == Uniform::Type::Bool) { + return uniform.value().toBool() ? QString("true") : QString("false"); + } else if (uniform.type() == Uniform::Type::Int) { + return QString::number(uniform.value().toInt()); + } else if (uniform.type() == Uniform::Type::Float) { + return QString::number(uniform.value().toDouble()); + } else if (uniform.type() == Uniform::Type::Vec2) { + QVector2D v2 = uniform.value().value(); + return QString("vec2(%1, %2)").arg(v2.x(), v2.y()); + } else if (uniform.type() == Uniform::Type::Vec3) { + QVector3D v3 = uniform.value().value(); + return QString("vec3(%1, %2, %3)").arg(v3.x(), v3.y(), v3.z()); + } else if (uniform.type() == Uniform::Type::Vec4) { + QVector4D v4 = uniform.value().value(); + return QString("vec4(%1, %2, %3, %4)").arg(v4.x(), v4.y(), v4.z(), v4.w()); + } else if (uniform.type() == Uniform::Type::Color) { + QColor c = uniform.value().value(); + return QString("vec4(%1, %2, %3, %4)").arg(c.redF(), c.greenF(), c.blueF(), c.alphaF()); + } else { + qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); + return QString(); + } +} + +// Return name for the image property Image element +QString EffectMakerModel::getImageElementName(const Uniform &uniform) +{ + // TODO + Q_UNUSED(uniform) + return {}; +} + +const QString EffectMakerModel::getConstVariables() +{ + const QList uniforms = allUniforms(); + QString s; + for (Uniform *uniform : uniforms) { + // TODO: Check if uniform is already added. + QString constValue = valueAsVariable(*uniform); + QString type = Uniform::stringFromType(uniform->type()); + s += QString("const %1 %2 = %3;\n").arg(type, uniform->name(), constValue); + } + if (!s.isEmpty()) + s += '\n'; + + return s; +} + +const QString EffectMakerModel::getDefineProperties() +{ + const QList uniforms = allUniforms(); + QString s; + for (Uniform *uniform : uniforms) { + // TODO: Check if uniform is already added. + if (uniform->type() == Uniform::Type::Define) { + QString defineValue = uniform->value().toString(); + s += QString("#define %1 %2\n").arg(uniform->name(), defineValue); + } + } + if (!s.isEmpty()) + s += '\n'; + + return s; +} + +int EffectMakerModel::getTagIndex(const QStringList &code, const QString &tag) +{ + int index = -1; + int line = 0; + const QString tagString = QString("@%1").arg(tag); + for (const QString &s : code) { + auto st = s.trimmed(); + // Check if line or first non-space content of the line matches to tag + static auto spaceReg = QRegularExpression("\\s"); + auto firstSpace = st.indexOf(spaceReg); + QString firstWord = st; + if (firstSpace > 0) + firstWord = st.sliced(0, firstSpace); + if (firstWord == tagString) { + index = line; + break; + } + line++; + } + return index; +} + +QString EffectMakerModel::processVertexRootLine(const QString &line) +{ + QString output; + QStringList lineList = line.split(m_spaceReg, Qt::SkipEmptyParts); + if (lineList.length() > 1 && lineList.at(0) == QStringLiteral("out")) { + lineList.removeFirst(); + QString outLine = lineList.join(' '); + m_shaderVaryingVariables << outLine; + } else { + output = line + '\n'; + } + return output; +} + +QString EffectMakerModel::processFragmentRootLine(const QString &line) +{ + QString output; + QStringList lineList = line.split(m_spaceReg, Qt::SkipEmptyParts); + // Just skip all "in" variables. It is enough to have "out" variable in vertex. + if (lineList.length() > 1 && lineList.at(0) == QStringLiteral("in")) + return QString(); + output = line + '\n'; + return output; +} + +QStringList EffectMakerModel::getDefaultRootVertexShader() +{ + if (m_defaultRootVertexShader.isEmpty()) { + m_defaultRootVertexShader << "void main() {"; + m_defaultRootVertexShader << " texCoord = qt_MultiTexCoord0;"; + m_defaultRootVertexShader << " fragCoord = qt_Vertex.xy;"; + m_defaultRootVertexShader << " vec2 vertCoord = qt_Vertex.xy;"; + m_defaultRootVertexShader << " @nodes"; + m_defaultRootVertexShader << " gl_Position = qt_Matrix * vec4(vertCoord, 0.0, 1.0);"; + m_defaultRootVertexShader << "}"; + } + return m_defaultRootVertexShader; +} + +QStringList EffectMakerModel::getDefaultRootFragmentShader() +{ + if (m_defaultRootFragmentShader.isEmpty()) { + m_defaultRootFragmentShader << "void main() {"; + m_defaultRootFragmentShader << " fragColor = texture(iSource, texCoord);"; + m_defaultRootFragmentShader << " @nodes"; + m_defaultRootFragmentShader << " fragColor = fragColor * qt_Opacity;"; + m_defaultRootFragmentShader << "}"; + } + return m_defaultRootFragmentShader; +} + +// Remove all post-processing tags ("@tag") from the code. +// Except "@nodes" tag as that is handled later. +QStringList EffectMakerModel::removeTagsFromCode(const QStringList &codeLines) +{ + QStringList s; + for (const QString &line : codeLines) { + const auto trimmedLine = line.trimmed(); + if (!trimmedLine.startsWith('@') || trimmedLine.startsWith("@nodes")) { + s << line; + } else { + // Check if the tag is known + bool validTag = false; + const QList tags = SyntaxHighlighterData::reservedTagNames(); + QString firstWord = trimmedLine.split(m_spaceReg, Qt::SkipEmptyParts).first(); + for (const QByteArrayView &tag : tags) { + if (firstWord == QString::fromUtf8(tag)) { + validTag = true; + break; + } + } + if (!validTag) + setEffectError(QString("Unknown tag: %1").arg(trimmedLine), ErrorPreprocessor); + } + } + return s; +} + +QString EffectMakerModel::removeTagsFromCode(const QString &code) +{ + QStringList codeLines = removeTagsFromCode(code.split('\n')); + return codeLines.join('\n'); +} + +QString EffectMakerModel::getCustomShaderVaryings(bool outState) +{ + QString output; + QString direction = outState ? QStringLiteral("out") : QStringLiteral("in"); + int varLocation = m_shaderFeatures.enabled(ShaderFeatures::FragCoord) ? 2 : 1; + for (const QString &var : std::as_const(m_shaderVaryingVariables)) { + output += QString("layout(location = %1) %2 %3\n").arg(QString::number(varLocation), direction, var); + varLocation++; + } + return output; +} + +QString EffectMakerModel::generateVertexShader(bool includeUniforms) +{ + QString s; + + if (includeUniforms) + s += getVSUniforms(); + + // Remove tags when not generating for features check + const bool removeTags = includeUniforms; + + s += getDefineProperties(); + s += getConstVariables(); + + // When the node is complete, add shader code in correct nodes order + // split to root and main parts + QString s_root; + QString s_main; + QStringList s_sourceCode; + m_shaderVaryingVariables.clear(); + for (const CompositionNode *n : std::as_const(m_nodes)) { + if (!n->vertexCode().isEmpty() && n->isEnabled()) { + if (n->type() == CompositionNode::NodeType::SourceNode) { + s_sourceCode = n->vertexCode().split('\n'); + } else if (n->type() == CompositionNode::NodeType::CustomNode) { + const QStringList vertexCode = n->vertexCode().split('\n'); + int mainIndex = getTagIndex(vertexCode, QStringLiteral("main")); + int line = 0; + for (const QString &ss : vertexCode) { + if (mainIndex == -1 || line > mainIndex) + s_main += QStringLiteral(" ") + ss + '\n'; + else if (line < mainIndex) + s_root += processVertexRootLine(ss); + line++; + } + } + } + } + + if (s_sourceCode.isEmpty()) { + // If source nodes doesn't contain any code, use default one + s_sourceCode << getDefaultRootVertexShader(); + } + + if (removeTags) { + s_sourceCode = removeTagsFromCode(s_sourceCode); + s_root = removeTagsFromCode(s_root); + s_main = removeTagsFromCode(s_main); + } + + s += getCustomShaderVaryings(true); + s += s_root + '\n'; + + int nodesIndex = getTagIndex(s_sourceCode, QStringLiteral("nodes")); + int line = 0; + for (const QString &ss : std::as_const(s_sourceCode)) + s += (line++ == nodesIndex) ? s_main : ss + '\n'; + + return s; +} + +QString EffectMakerModel::generateFragmentShader(bool includeUniforms) +{ + QString s; + + if (includeUniforms) + s += getFSUniforms(); + + // Remove tags when not generating for features check + const bool removeTags = includeUniforms; + + s += getDefineProperties(); + s += getConstVariables(); + + // When the node is complete, add shader code in correct nodes order + // split to root and main parts + QString s_root; + QString s_main; + QStringList s_sourceCode; + for (const CompositionNode *n : std::as_const(m_nodes)) { + if (!n->fragmentCode().isEmpty() && n->isEnabled()) { + if (n->type() == CompositionNode::NodeType::SourceNode) { + s_sourceCode = n->fragmentCode().split('\n'); + } else if (n->type() == CompositionNode::NodeType::CustomNode) { + const QStringList fragmentCode = n->fragmentCode().split('\n'); + int mainIndex = getTagIndex(fragmentCode, QStringLiteral("main")); + int line = 0; + for (const QString &ss : fragmentCode) { + if (mainIndex == -1 || line > mainIndex) + s_main += QStringLiteral(" ") + ss + '\n'; + else if (line < mainIndex) + s_root += processFragmentRootLine(ss); + line++; + } + } + } + } + + if (s_sourceCode.isEmpty()) { + // If source nodes doesn't contain any code, use default one + s_sourceCode << getDefaultRootFragmentShader(); + } + + if (removeTags) { + s_sourceCode = removeTagsFromCode(s_sourceCode); + s_root = removeTagsFromCode(s_root); + s_main = removeTagsFromCode(s_main); + } + + s += getCustomShaderVaryings(false); + s += s_root + '\n'; + + int nodesIndex = getTagIndex(s_sourceCode, QStringLiteral("nodes")); + int line = 0; + for (const QString &ss : std::as_const(s_sourceCode)) + s += (line++ == nodesIndex) ? s_main : ss + '\n'; + + return s; +} + +// Generates string of the custom properties (uniforms) into ShaderEffect component +// Also generates QML images elements for samplers. +void EffectMakerModel::updateCustomUniforms() +{ + QString exportedRootPropertiesString; + QString previewEffectPropertiesString; + QString exportedEffectPropertiesString; + + const QList uniforms = allUniforms(); + for (Uniform *uniform : uniforms) { + // TODO: Check if uniform is already added. + const bool isDefine = uniform->type() == Uniform::Type::Define; + QString type = Uniform::typeToProperty(uniform->type()); + QString value = valueAsString(*uniform); + QString bindedValue = valueAsBinding(*uniform); + // When user has set custom uniform value, use it as-is + if (uniform->useCustomValue()) { + value = uniform->customValue(); + bindedValue = value; + } + // Note: Define type properties appear also as QML properties (in preview) in case QML side + // needs to use them. This is used at least by BlurHelper BLUR_HELPER_MAX_LEVEL. + QString propertyName = isDefine ? uniform->name().toLower() : uniform->name(); + if (!uniform->useCustomValue() && !isDefine && !uniform->description().isEmpty()) { + // When exporting, add API documentation for properties + const QStringList descriptionLines = uniform->description().split('\n'); + for (const QString &line : descriptionLines) { + if (line.trimmed().isEmpty()) + exportedRootPropertiesString += QStringLiteral(" //\n"); + else + exportedRootPropertiesString += QStringLiteral(" // ") + line + '\n'; + } + } + QString valueString = value.isEmpty() ? QString() : QString(": %1").arg(value); + QString bindedValueString = bindedValue.isEmpty() ? QString() : QString(": %1").arg(bindedValue); + // Custom values are not readonly, others inside the effect can be + QString readOnly = uniform->useCustomValue() ? QString() : QStringLiteral("readonly "); + previewEffectPropertiesString += " " + readOnly + "property " + type + " " + + propertyName + bindedValueString + '\n'; + // Define type properties are not added into exports + if (!isDefine) { + if (uniform->useCustomValue()) { + // Custom values are only inside the effect, with description comments + if (!uniform->description().isEmpty()) { + const QStringList descriptionLines = uniform->description().split('\n'); + for (const QString &line : descriptionLines) + exportedEffectPropertiesString += QStringLiteral(" // ") + line + '\n'; + } + exportedEffectPropertiesString += QStringLiteral(" ") + readOnly + + "property " + type + " " + propertyName + + bindedValueString + '\n'; + } else { + // Custom values are not added into root + exportedRootPropertiesString += " property " + type + " " + propertyName + + valueString + '\n'; + exportedEffectPropertiesString += QStringLiteral(" ") + + readOnly + "property alias " + propertyName + + ": rootItem." + uniform->name() + '\n'; + } + } + } + + // See if any of the properties changed + // TODO +} + +void EffectMakerModel::bakeShaders() +{ + resetEffectError(ErrorPreprocessor); + if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) { + setShadersUpToDate(true); + return; + } + + setShadersUpToDate(false); + + // First update the features based on shader content + // This will make sure that next calls to "generate" will produce correct uniforms. + m_shaderFeatures.update(generateVertexShader(false), generateFragmentShader(false), m_previewEffectPropertiesString); + + updateCustomUniforms(); + + // TODO: Shaders baking +} + +bool EffectMakerModel::shadersUpToDate() const +{ + return m_shadersUpToDate; +} + +void EffectMakerModel::setShadersUpToDate(bool UpToDate) +{ + if (m_shadersUpToDate == UpToDate) + return; + m_shadersUpToDate = UpToDate; + emit shadersUpToDateChanged(); +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakermodel.h b/src/plugins/effectmakernew/effectmakermodel.h new file mode 100644 index 0000000000..08441bfb43 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakermodel.h @@ -0,0 +1,139 @@ +// 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 +#include +#include + +namespace EffectMaker { + +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 EffectMakerModel : 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 shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged) + +public: + EffectMakerModel(QObject *parent = nullptr); + + QHash 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); + + Q_INVOKABLE void moveNode(int fromIdx, int toIdx); + Q_INVOKABLE void removeNode(int idx); + + bool shadersUpToDate() const; + void setShadersUpToDate(bool newShadersUpToDate); + + QString fragmentShader() const; + void setFragmentShader(const QString &newFragmentShader); + QString vertexShader() const; + void setVertexShader(const QString &newVertexShader); + +signals: + void isEmptyChanged(); + void selectedIndexChanged(int idx); + void effectErrorChanged(); + void shadersUpToDateChanged(); + +private: + enum Roles { + NameRole = Qt::UserRole + 1, + EnabledRole, + UniformsRole + }; + + enum ErrorTypes { + ErrorCommon = -1, + ErrorQMLParsing, + ErrorVert, + ErrorFrag, + ErrorQMLRuntime, + ErrorPreprocessor + }; + + bool isValidIndex(int idx) const; + + const QList allUniforms(); + + const QString getBufUniform(); + const QString getVSUniforms(); + const QString getFSUniforms(); + + QString detectErrorMessage(const QString &errorMessage); + EffectError effectError() const; + void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); + void resetEffectError(int type); + + QString valueAsString(const Uniform &uniform); + QString valueAsBinding(const Uniform &uniform); + QString valueAsVariable(const Uniform &uniform); + QString getImageElementName(const Uniform &uniform); + 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 updateCustomUniforms(); + void bakeShaders(); + + QList m_nodes; + + int m_selectedIndex = -1; + bool m_isEmpty = true; + // True when shaders haven't changed since last baking + bool m_shadersUpToDate = true; + QMap m_effectErrors; + ShaderFeatures m_shaderFeatures; + QStringList m_shaderVaryingVariables; + QString m_fragmentShader; + QString m_vertexShader; + QStringList m_defaultRootVertexShader; + QStringList m_defaultRootFragmentShader; + // 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; + + const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); +}; + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakernodesmodel.cpp b/src/plugins/effectmakernew/effectmakernodesmodel.cpp new file mode 100644 index 0000000000..3b894d9157 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakernodesmodel.cpp @@ -0,0 +1,108 @@ +// 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 "effectmakernodesmodel.h" + +#include + +#include + +namespace EffectMaker { + +EffectMakerNodesModel::EffectMakerNodesModel(QObject *parent) + : QAbstractListModel{parent} +{ +} + +QHash EffectMakerNodesModel::roleNames() const +{ + QHash roles; + roles[CategoryNameRole] = "categoryName"; + roles[CategoryNodesRole] = "categoryNodes"; + + return roles; +} + +int EffectMakerNodesModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_categories.count(); +} + +QVariant EffectMakerNodesModel::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 EffectMakerNodesModel::findNodesPath() +{ + if (m_nodesPath.exists() || m_probeNodesDir) + return; + + QDir nodesDir; + + if (!qEnvironmentVariable("EFFECT_MAKER_NODES_PATH").isEmpty()) + nodesDir.setPath(qEnvironmentVariable("EFFECT_MAKER_NODES_PATH")); + else if (Utils::HostOsInfo::isMacHost()) + nodesDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/effect_maker_nodes"); + + // search for nodesDir from exec dir and up + if (nodesDir.dirName() == ".") { + m_probeNodesDir = true; // probe only once + nodesDir.setPath(QCoreApplication::applicationDirPath()); + while (!nodesDir.cd("effect_maker_nodes") && nodesDir.cdUp()) + ; // do nothing + + if (nodesDir.dirName() != "effect_maker_nodes") // bundlePathDir not found + return; + } + + m_nodesPath = Utils::FilePath::fromString(nodesDir.path()); +} + +void EffectMakerNodesModel::loadModel() +{ + findNodesPath(); + + if (!m_nodesPath.exists()) { + qWarning() << __FUNCTION__ << "Effects not found."; + return; + } + + QDirIterator itCategories(m_nodesPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot); + while (itCategories.hasNext()) { + itCategories.next(); + + if (itCategories.fileName() == "images" || itCategories.fileName() == "common") + continue; + + QString catName = itCategories.fileName(); + + QList effects = {}; + Utils::FilePath categoryPath = m_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); + } + + resetModel(); +} + +void EffectMakerNodesModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakernodesmodel.h b/src/plugins/effectmakernew/effectmakernodesmodel.h new file mode 100644 index 0000000000..28a4e8484f --- /dev/null +++ b/src/plugins/effectmakernew/effectmakernodesmodel.h @@ -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 + +#include + +namespace EffectMaker { + +class EffectMakerNodesModel : public QAbstractListModel +{ + Q_OBJECT + + enum Roles { + CategoryNameRole = Qt::UserRole + 1, + CategoryNodesRole + }; + +public: + EffectMakerNodesModel(QObject *parent = nullptr); + + QHash 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 categories() const { return m_categories; } + +private: + void findNodesPath(); + + QList m_categories; + Utils::FilePath m_nodesPath; + bool m_probeNodesDir = false; +}; + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakerplugin.cpp b/src/plugins/effectmakernew/effectmakerplugin.cpp new file mode 100644 index 0000000000..3890d3c9d6 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakerplugin.cpp @@ -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 + +#include "effectmakerplugin.h" + +#include "effectmakerview.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace EffectMaker { + +bool EffectMakerPlugin::delayedInitialize() +{ + if (m_delayedInitialized) + return true; + + auto *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance(); + auto &viewManager = designerPlugin->viewManager(); + viewManager.registerView(std::make_unique( + QmlDesigner::QmlDesignerPlugin::externalDependenciesForPluginInitializationOnly())); + + m_delayedInitialized = true; + + return true; +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakerplugin.h b/src/plugins/effectmakernew/effectmakerplugin.h new file mode 100644 index 0000000000..116115629e --- /dev/null +++ b/src/plugins/effectmakernew/effectmakerplugin.h @@ -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 +#include + +namespace Core { +class ActionContainer; +class ExternalTool; +} + +namespace EffectMaker { + +class EffectMakerPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "EffectMakerNew.json") + +public: + EffectMakerPlugin() {} + ~EffectMakerPlugin() override {} + + bool delayedInitialize() override; + +private: + bool m_delayedInitialized = false; +}; + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp b/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp new file mode 100644 index 0000000000..9313b98640 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakeruniformsmodel.cpp @@ -0,0 +1,76 @@ +// 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 "effectmakeruniformsmodel.h" + +#include "uniform.h" + +#include + +namespace EffectMaker { + +EffectMakerUniformsModel::EffectMakerUniformsModel(QObject *parent) + : QAbstractListModel{parent} +{ +} + +QHash EffectMakerUniformsModel::roleNames() const +{ + QHash 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"; + return roles; +} + +int EffectMakerUniformsModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_uniforms.size(); +} + +QVariant EffectMakerUniformsModel::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 EffectMakerUniformsModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid() || !roleNames().contains(role)) + return false; + + m_uniforms.at(index.row())->setValue(value); + emit dataChanged(index, index, {role}); + + return true; +} + +void EffectMakerUniformsModel::resetModel() +{ + beginResetModel(); + endResetModel(); +} + +void EffectMakerUniformsModel::addUniform(Uniform *uniform) +{ + beginInsertRows({}, m_uniforms.size(), m_uniforms.size()); + m_uniforms.append(uniform); + endInsertRows(); +} + +QList EffectMakerUniformsModel::uniforms() const +{ + return m_uniforms; +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakeruniformsmodel.h b/src/plugins/effectmakernew/effectmakeruniformsmodel.h new file mode 100644 index 0000000000..9b9651a872 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakeruniformsmodel.h @@ -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 + +namespace EffectMaker { + +class Uniform; + +class EffectMakerUniformsModel : public QAbstractListModel +{ + Q_OBJECT + +public: + EffectMakerUniformsModel(QObject *parent = nullptr); + + QHash 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 uniforms() const; + +private: + enum Roles { + NameRole = Qt::UserRole + 1, + DescriptionRole, + ValueRole, + BackendValueRole, + DefaultValueRole, + MaxValueRole, + MinValueRole, + TypeRole, + }; + + QList m_uniforms; +}; + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakerview.cpp b/src/plugins/effectmakernew/effectmakerview.cpp new file mode 100644 index 0000000000..4bb68f358a --- /dev/null +++ b/src/plugins/effectmakernew/effectmakerview.cpp @@ -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 + +#include "effectmakerview.h" + +#include "effectmakerwidget.h" +#include "effectmakernodesmodel.h" + +#include "nodeinstanceview.h" +#include "qmldesignerconstants.h" + +#include + +#include +#include +#include +#include +#include + +namespace EffectMaker { + +EffectMakerContext::EffectMakerContext(QWidget *widget) + : IContext(widget) +{ + setWidget(widget); + setContext(Core::Context(QmlDesigner::Constants::C_QMLEFFECTMAKER, + QmlDesigner::Constants::C_QT_QUICK_TOOLS_MENU)); +} + +void EffectMakerContext::contextHelp(const HelpCallback &callback) const +{ + qobject_cast(m_widget)->contextHelp(callback); +} + +EffectMakerView::EffectMakerView(QmlDesigner::ExternalDependenciesInterface &externalDependencies) + : AbstractView{externalDependencies} +{ +} + +EffectMakerView::~EffectMakerView() +{} + +bool EffectMakerView::hasWidget() const +{ + return true; +} + +QmlDesigner::WidgetInfo EffectMakerView::widgetInfo() +{ + if (m_widget.isNull()) { + m_widget = new EffectMakerWidget{this}; + + auto context = new EffectMakerContext(m_widget.data()); + Core::ICore::addContextObject(context); + } + + return createWidgetInfo(m_widget.data(), "Effect Maker", + QmlDesigner::WidgetInfo::LeftPane, 0, tr("Effect Maker")); +} + +void EffectMakerView::customNotification(const AbstractView * /*view*/, + const QString & /*identifier*/, + const QList & /*nodeList*/, + const QList & /*data*/) +{ + // TODO +} + +void EffectMakerView::modelAttached(QmlDesigner::Model *model) +{ + AbstractView::modelAttached(model); + + m_widget->effectMakerNodesModel()->loadModel(); + m_widget->initView(); +} + +void EffectMakerView::modelAboutToBeDetached(QmlDesigner::Model *model) +{ + AbstractView::modelAboutToBeDetached(model); +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakerview.h b/src/plugins/effectmakernew/effectmakerview.h new file mode 100644 index 0000000000..2bed1cfc10 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakerview.h @@ -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 + +#include + +namespace EffectMaker { + +class EffectMakerWidget; + +class EffectMakerContext : public Core::IContext +{ + Q_OBJECT + +public: + EffectMakerContext(QWidget *widget); + void contextHelp(const Core::IContext::HelpCallback &callback) const override; +}; + +class EffectMakerView : public QmlDesigner::AbstractView +{ +public: + EffectMakerView(QmlDesigner::ExternalDependenciesInterface &externalDependencies); + ~EffectMakerView() 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 &nodeList, const QList &data) override; + + QPointer m_widget; +}; + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakerwidget.cpp b/src/plugins/effectmakernew/effectmakerwidget.cpp new file mode 100644 index 0000000000..72d4c1b2cd --- /dev/null +++ b/src/plugins/effectmakernew/effectmakerwidget.cpp @@ -0,0 +1,147 @@ +// 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 "effectmakerwidget.h" + +#include "effectmakercontextobject.h" +#include "effectmakermodel.h" +#include "effectmakernodesmodel.h" +#include "effectmakerview.h" +#include "qmldesignerconstants.h" +#include "qmldesignerplugin.h" +#include "qqmlcontext.h" +#include "theme.h" + +#include + +#include + +#include +#include +#include + +#include +#include + +namespace EffectMaker { + +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(); +} + +EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) + : m_effectMakerModel{new EffectMakerModel(this)} + , m_effectMakerNodesModel{new EffectMakerNodesModel(this)} + , m_effectMakerView(view) + , m_quickWidget{new StudioQuickWidget(this)} +{ + setWindowTitle(tr("Effect Maker", "Title of effect maker widget")); + setMinimumWidth(250); + + m_quickWidget->quickWidget()->installEventFilter(this); + + // create the inner widget + m_quickWidget->quickWidget()->setObjectName(QmlDesigner::Constants::OBJECT_NAME_EFFECT_MAKER); + m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); + QmlDesigner::Theme::setupTheme(m_quickWidget->engine()); + m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); + 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_EFFECTMAKER_TIME); + + auto map = m_quickWidget->registerPropertyMap("EffectMakerBackend"); + map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())}, + {"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, + {"rootView", QVariant::fromValue(this)}}); +} + + +bool EffectMakerWidget::eventFilter(QObject *obj, QEvent *event) +{ + Q_UNUSED(obj) + Q_UNUSED(event) + + // TODO + + return false; +} + +void EffectMakerWidget::contextHelp(const Core::IContext::HelpCallback &callback) const +{ + Q_UNUSED(callback) +} + +StudioQuickWidget *EffectMakerWidget::quickWidget() const +{ + return m_quickWidget.data(); +} + +QPointer EffectMakerWidget::effectMakerModel() const +{ + return m_effectMakerModel; +} + +QPointer EffectMakerWidget::effectMakerNodesModel() const +{ + return m_effectMakerNodesModel; +} + +void EffectMakerWidget::addEffectNode(const QString &nodeQenPath) +{ + m_effectMakerModel->addNode(nodeQenPath); +} + +void EffectMakerWidget::focusSection(int section) +{ + Q_UNUSED(section) +} + +QSize EffectMakerWidget::sizeHint() const +{ + return {420, 420}; +} + +QString EffectMakerWidget::qmlSourcesPath() +{ +#ifdef SHARE_QML_PATH + if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) + return QLatin1String(SHARE_QML_PATH) + "/effectMakerQmlSources"; +#endif + return Core::ICore::resourcePath("qmldesigner/effectMakerQmlSources").toString(); +} + +void EffectMakerWidget::initView() +{ + auto ctxObj = new EffectMakerContextObject(m_quickWidget->rootContext()); + m_quickWidget->rootContext()->setContextObject(ctxObj); + + m_backendModelNode.setup(m_effectMakerView->rootModelNode()); + m_quickWidget->rootContext()->setContextProperty("modelNodeBackend", &m_backendModelNode); + + // init the first load of the QML UI elements + reloadQmlSource(); +} + +void EffectMakerWidget::reloadQmlSource() +{ + const QString effectMakerQmlPath = qmlSourcesPath() + "/EffectMaker.qml"; + QTC_ASSERT(QFileInfo::exists(effectMakerQmlPath), return); + m_quickWidget->setSource(QUrl::fromLocalFile(effectMakerQmlPath)); +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectmakerwidget.h b/src/plugins/effectmakernew/effectmakerwidget.h new file mode 100644 index 0000000000..6f55cbc786 --- /dev/null +++ b/src/plugins/effectmakernew/effectmakerwidget.h @@ -0,0 +1,60 @@ +// 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/qmlmodelnodeproxy.h" + +#include + +#include + +class StudioQuickWidget; + +namespace EffectMaker { + +class EffectMakerView; +class EffectMakerModel; +class EffectMakerNodesModel; + +class EffectMakerWidget : public QFrame +{ + Q_OBJECT + +public: + EffectMakerWidget(EffectMakerView *view); + ~EffectMakerWidget() = default; + + void contextHelp(const Core::IContext::HelpCallback &callback) const; + + static QString qmlSourcesPath(); + void clearSearchFilter(); + + void delayedUpdateModel(); + void updateModel(); + void initView(); + + StudioQuickWidget *quickWidget() const; + QPointer effectMakerModel() const; + QPointer effectMakerNodesModel() const; + + Q_INVOKABLE void addEffectNode(const QString &nodeQenPath); + Q_INVOKABLE void focusSection(int section); + + QSize sizeHint() const override; + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + +private: + void reloadQmlSource(); + + QPointer m_effectMakerModel; + QPointer m_effectMakerNodesModel; + QPointer m_effectMakerView; + QPointer m_quickWidget; + QmlDesigner::QmlModelNodeProxy m_backendModelNode; +}; + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectnode.cpp b/src/plugins/effectmakernew/effectnode.cpp new file mode 100644 index 0000000000..292c04d13e --- /dev/null +++ b/src/plugins/effectmakernew/effectnode.cpp @@ -0,0 +1,43 @@ +// 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 +#include + +namespace EffectMaker { + +EffectNode::EffectNode(const QString &qenPath) + : m_qenPath(qenPath) +{ + const QFileInfo fileInfo = QFileInfo(qenPath); + m_name = fileInfo.baseName(); + + QString iconPath = QStringLiteral("%1/icon/%2.svg").arg(fileInfo.absolutePath(), m_name); + 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); +} + +QString EffectNode::name() const +{ + return m_name; +} + +QString EffectNode::description() const +{ + return m_description; +} + +QString EffectNode::qenPath() const +{ + return m_qenPath; +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectnode.h b/src/plugins/effectmakernew/effectnode.h new file mode 100644 index 0000000000..5c457e2a6d --- /dev/null +++ b/src/plugins/effectmakernew/effectnode.h @@ -0,0 +1,35 @@ +// 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 +#include + +namespace EffectMaker { + +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) + +public: + EffectNode(const QString &qenPath); + + QString name() const; + QString description() const; + QString qenPath() const; + +private: + QString m_name; + QString m_description; + QString m_qenPath; + QUrl m_iconPath; +}; + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectnodescategory.cpp b/src/plugins/effectmakernew/effectnodescategory.cpp new file mode 100644 index 0000000000..7f89766cca --- /dev/null +++ b/src/plugins/effectmakernew/effectnodescategory.cpp @@ -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 EffectMaker { + +EffectNodesCategory::EffectNodesCategory(const QString &name, const QList &nodes) + : m_name(name), + m_categoryNodes(nodes) {} + +QString EffectNodesCategory::name() const +{ + return m_name; +} + +QList EffectNodesCategory::nodes() const +{ + return m_categoryNodes; +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectnodescategory.h b/src/plugins/effectmakernew/effectnodescategory.h new file mode 100644 index 0000000000..25f5d8d4bd --- /dev/null +++ b/src/plugins/effectmakernew/effectnodescategory.h @@ -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 + +namespace EffectMaker { + +class EffectNodesCategory : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString categoryName MEMBER m_name CONSTANT) + Q_PROPERTY(QList categoryNodes MEMBER m_categoryNodes CONSTANT) + +public: + EffectNodesCategory(const QString &name, const QList &nodes); + + QString name() const; + QList nodes() const; + +private: + QString m_name; + QList m_categoryNodes; +}; + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectutils.cpp b/src/plugins/effectmakernew/effectutils.cpp new file mode 100644 index 0000000000..8e2bb62543 --- /dev/null +++ b/src/plugins/effectmakernew/effectutils.cpp @@ -0,0 +1,24 @@ +// 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 + +namespace EffectMaker { + +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; +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/effectutils.h b/src/plugins/effectmakernew/effectutils.h new file mode 100644 index 0000000000..e3de9312dc --- /dev/null +++ b/src/plugins/effectmakernew/effectutils.h @@ -0,0 +1,21 @@ +// 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 + +QT_FORWARD_DECLARE_CLASS(QJsonArray) + +namespace EffectMaker { + +class EffectUtils +{ +public: + EffectUtils() = delete; + + static QString codeFromJsonArray(const QJsonArray &codeArray); +}; + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/shaderfeatures.cpp b/src/plugins/effectmakernew/shaderfeatures.cpp new file mode 100644 index 0000000000..9ab7a28232 --- /dev/null +++ b/src/plugins/effectmakernew/shaderfeatures.cpp @@ -0,0 +1,81 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "shaderfeatures.h" +#include +#include + +namespace EffectMaker { + +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); +} + +} // namespace EffectMaker + diff --git a/src/plugins/effectmakernew/shaderfeatures.h b/src/plugins/effectmakernew/shaderfeatures.h new file mode 100644 index 0000000000..c0d3b72b84 --- /dev/null +++ b/src/plugins/effectmakernew/shaderfeatures.h @@ -0,0 +1,40 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#pragma once + +#include +#include + +namespace EffectMaker { + +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; + +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 EffectMaker + diff --git a/src/plugins/effectmakernew/syntaxhighlighterdata.cpp b/src/plugins/effectmakernew/syntaxhighlighterdata.cpp new file mode 100644 index 0000000000..4a6face819 --- /dev/null +++ b/src/plugins/effectmakernew/syntaxhighlighterdata.cpp @@ -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 EffectMaker { + +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 SyntaxHighlighterData::reservedArgumentNames() +{ + return { std::begin(shader_arg_names), std::end(shader_arg_names) }; +} + +QList SyntaxHighlighterData::reservedTagNames() +{ + return { std::begin(shader_tag_names), std::end(shader_tag_names) }; +} + +QList SyntaxHighlighterData::reservedFunctionNames() +{ + return { std::begin(shader_function_names), std::end(shader_function_names) }; +} + +} // namespace EffectMaker + + diff --git a/src/plugins/effectmakernew/syntaxhighlighterdata.h b/src/plugins/effectmakernew/syntaxhighlighterdata.h new file mode 100644 index 0000000000..bce1100e05 --- /dev/null +++ b/src/plugins/effectmakernew/syntaxhighlighterdata.h @@ -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 +#include + +namespace EffectMaker { + +class SyntaxHighlighterData +{ +public: + SyntaxHighlighterData(); + + static QList reservedArgumentNames(); + static QList reservedTagNames(); + static QList reservedFunctionNames(); +}; + +} // namespace EffectMaker + + diff --git a/src/plugins/effectmakernew/uniform.cpp b/src/plugins/effectmakernew/uniform.cpp new file mode 100644 index 0000000000..5cad43e0c9 --- /dev/null +++ b/src/plugins/effectmakernew/uniform.cpp @@ -0,0 +1,324 @@ +// 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 + + +#include +#include +#include + +namespace EffectMaker { + +Uniform::Uniform(const QJsonObject &propObj) +{ + 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; + + if (m_type == Type::Sampler) { + if (!defaultValue.isEmpty()) + defaultValue = getResourcePath(defaultValue); + if (propObj.contains("enableMipmap")) + m_enableMipmap = getBoolValue(propObj.value("enableMipmap"), false); + // Update the mipmap property + QString mipmapProperty = mipmapPropertyName(m_name); + } + if (propObj.contains("value")) { + value = propObj.value("value").toString(); + if (m_type == Type::Sampler && !value.isEmpty()) + value = getResourcePath(value); + } else { + // QEN files don't store the current value, so with those use default value + value = defaultValue; + } + 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::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 &value) const +{ + Q_UNUSED(value) + //TODO + return {}; +} + +// 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) +{ + 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 "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") + 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 EffectMaker diff --git a/src/plugins/effectmakernew/uniform.h b/src/plugins/effectmakernew/uniform.h new file mode 100644 index 0000000000..7bad706cb3 --- /dev/null +++ b/src/plugins/effectmakernew/uniform.h @@ -0,0 +1,110 @@ +// 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 +#include + +#include + +QT_FORWARD_DECLARE_CLASS(QColor) +QT_FORWARD_DECLARE_CLASS(QJsonObject) +QT_FORWARD_DECLARE_CLASS(QVector2D) + +namespace EffectMaker { + + + +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) + +public: + enum class Type + { + Bool, + Int, + Float, + Vec2, + Vec3, + Vec4, + Color, + Sampler, + Define + }; + + Uniform(const QJsonObject &props); + + 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 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); + 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 &value) const; + void setValueData(const QString &value, const QString &defaultValue, + const QString &minValue, const QString &maxValue); + + QVariant getInitializedVariant(bool maxValue); + QVariant valueStringToVariant(const QString &value); + + 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 EffectMaker diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 5d9263771c..dfa4f63b8b 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -709,24 +709,6 @@ extend_qtc_plugin(QmlDesigner assetslibraryiconprovider.cpp assetslibraryiconprovider.h ) -extend_qtc_plugin(QmlDesigner - SOURCES_PREFIX components/effectmaker - SOURCES - effectmakerwidget.cpp effectmakerwidget.h - effectmakerview.cpp effectmakerview.h - effectmakermodel.cpp effectmakermodel.h - effectmakernodesmodel.cpp effectmakernodesmodel.h - effectmakeruniformsmodel.cpp effectmakeruniformsmodel.h - effectnode.cpp effectnode.h - effectnodescategory.cpp effectnodescategory.h - compositionnode.cpp compositionnode.h - uniform.cpp uniform.h - effectutils.cpp effectutils.h - effectmakercontextobject.cpp effectmakercontextobject.h - shaderfeatures.cpp shaderfeatures.h - syntaxhighlighterdata.cpp syntaxhighlighterdata.h -) - extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/navigator SOURCES diff --git a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp index 8c06085393..26f9a97421 100644 --- a/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/viewmanager.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -56,7 +55,6 @@ public: , contentLibraryView{externalDependencies} , componentView{externalDependencies} , edit3DView{externalDependencies} - , effectMakerView{externalDependencies} , formEditorView{externalDependencies} , textEditorView{externalDependencies} , assetsLibraryView{externalDependencies} @@ -79,7 +77,6 @@ public: ContentLibraryView contentLibraryView; ComponentView componentView; Edit3DView edit3DView; - EffectMakerView effectMakerView; FormEditorView formEditorView; TextEditorView textEditorView; AssetsLibraryView assetsLibraryView; @@ -212,9 +209,6 @@ QList ViewManager::standardViews() const .toBool()) list.append(&d->debugView); - if (qEnvironmentVariableIsSet("ENABLE_QDS_EFFECTMAKER")) - list.append(&d->effectMakerView); - if (qEnvironmentVariableIsSet("ENABLE_QDS_COLLECTIONVIEW")) list.append(&d->collectionView); @@ -393,9 +387,6 @@ QList ViewManager::widgetInfos() const widgetInfoList.append(d->textureEditorView.widgetInfo()); widgetInfoList.append(d->statesEditorView.widgetInfo()); - if (qEnvironmentVariableIsSet("ENABLE_QDS_EFFECTMAKER")) - widgetInfoList.append(d->effectMakerView.widgetInfo()); - if (qEnvironmentVariableIsSet("ENABLE_QDS_COLLECTIONVIEW")) widgetInfoList.append(d->collectionView.widgetInfo()); diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp b/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp deleted file mode 100644 index 2e70763609..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.cpp +++ /dev/null @@ -1,122 +0,0 @@ -// 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 "effectmakeruniformsmodel.h" -#include "uniform.h" - -#include -#include -#include -#include - -namespace QmlDesigner { - -CompositionNode::CompositionNode(const QString &qenPath) -{ - parse(qenPath); -} - -QString CompositionNode::fragmentCode() const -{ - return m_fragmentCode; -} - -QString CompositionNode::vertexCode() const -{ - return m_vertexCode; -} - -QString CompositionNode::description() const -{ - return m_description; -} - -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(); - } -} - -CompositionNode::NodeType CompositionNode::type() const -{ - return m_type; -} - -void CompositionNode::parse(const QString &qenPath) -{ - 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 the effect node: %1:").arg(qenPath); - QString errorDetails = QString("%1: %2").arg(parseError.offset).arg(parseError.errorString()); - qWarning() << qPrintable(error); - qWarning() << qPrintable(errorDetails); - return; - } - - QJsonObject json = jsonDoc.object().value("QEN").toObject(); - - 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()); - - // parse properties - QJsonArray jsonProps = json.value("properties").toArray(); - for (const auto /*QJsonValueRef*/ &prop : jsonProps) - m_unifomrsModel.addUniform(new Uniform(prop.toObject())); - - // 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 l = trimmedLine.sliced(9).trimmed(); - QString nodeName = trimmedLine.sliced(10); - if (!nodeName.isEmpty() && !m_requiredNodes.contains(nodeName)) - m_requiredNodes << nodeName; - } - } -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h b/src/plugins/qmldesigner/components/effectmaker/compositionnode.h deleted file mode 100644 index 840abde96e..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/compositionnode.h +++ /dev/null @@ -1,60 +0,0 @@ -// 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 "effectmakeruniformsmodel.h" - -#include - -namespace QmlDesigner { - -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(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged) - -public: - enum NodeType { - SourceNode = 0, - DestinationNode, - CustomNode - }; - - CompositionNode(const QString &qenPath); - - QString fragmentCode() const; - QString vertexCode() const; - QString description() const; - - QObject *uniformsModel(); - - QStringList requiredNodes() const; - - NodeType type() const; - - bool isEnabled() const; - void setIsEnabled(bool newIsEnabled); - -signals: - void uniformsModelChanged(); - void isEnabledChanged(); - -private: - void parse(const QString &qenPath); - - QString m_name; - NodeType m_type = CustomNode; - QString m_fragmentCode; - QString m_vertexCode; - QString m_description; - QStringList m_requiredNodes; - bool m_isEnabled = true; - - EffectMakerUniformsModel m_unifomrsModel; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp deleted file mode 100644 index 7ee8399c2c..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// 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 "effectmakercontextobject.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -namespace QmlDesigner { - -EffectMakerContextObject::EffectMakerContextObject(QQmlContext *context, QObject *parent) - : QObject(parent) - , m_qmlContext(context) -{ -} - -QString EffectMakerContextObject::convertColorToString(const QVariant &color) -{ - QString colorString; - QColor theColor; - if (color.canConvert(QVariant::Color)) { - theColor = color.value(); - } else if (color.canConvert(QVariant::Vector3D)) { - auto vec = color.value(); - 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 EffectMakerContextObject::colorFromString(const QString &colorString) -{ - return colorString; -} - -int EffectMakerContextObject::majorVersion() const -{ - return m_majorVersion; -} - -void EffectMakerContextObject::setMajorVersion(int majorVersion) -{ - if (m_majorVersion == majorVersion) - return; - - m_majorVersion = majorVersion; - - emit majorVersionChanged(); -} - -void EffectMakerContextObject::setStateName(const QString &newStateName) -{ - if (newStateName == m_stateName) - return; - - m_stateName = newStateName; - emit stateNameChanged(); -} - -void EffectMakerContextObject::setAllStateNames(const QStringList &allStates) -{ - if (allStates == m_allStateNames) - return; - - m_allStateNames = allStates; - emit allStateNamesChanged(); -} - -void EffectMakerContextObject::setIsBaseState(bool newIsBaseState) -{ - if (newIsBaseState == m_isBaseState) - return; - - m_isBaseState = newIsBaseState; - emit isBaseStateChanged(); -} - -void EffectMakerContextObject::setSelectionChanged(bool newSelectionChanged) -{ - if (newSelectionChanged == m_selectionChanged) - return; - - m_selectionChanged = newSelectionChanged; - emit selectionChangedChanged(); -} - -void EffectMakerContextObject::setBackendValues(QQmlPropertyMap *newBackendValues) -{ - if (newBackendValues == m_backendValues) - return; - - m_backendValues = newBackendValues; - emit backendValuesChanged(); -} - -void EffectMakerContextObject::setModel(Model *model) -{ - m_model = model; -} - -void EffectMakerContextObject::triggerSelectionChanged() -{ - setSelectionChanged(!m_selectionChanged); -} - -void EffectMakerContextObject::hideCursor() -{ - if (QApplication::overrideCursor()) - return; - - QApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); - - if (QWidget *w = QApplication::activeWindow()) - m_lastPos = QCursor::pos(w->screen()); -} - -void EffectMakerContextObject::restoreCursor() -{ - if (!QApplication::overrideCursor()) - return; - - QApplication::restoreOverrideCursor(); - - if (QWidget *w = QApplication::activeWindow()) - QCursor::setPos(w->screen(), m_lastPos); -} - -void EffectMakerContextObject::holdCursorInPlace() -{ - if (!QApplication::overrideCursor()) - return; - - if (QWidget *w = QApplication::activeWindow()) - QCursor::setPos(w->screen(), m_lastPos); -} - -int EffectMakerContextObject::devicePixelRatio() -{ - if (QWidget *w = QApplication::activeWindow()) - return w->devicePixelRatio(); - - return 1; -} - -QStringList EffectMakerContextObject::allStatesForId(const QString &id) -{ - if (m_model && m_model->rewriterView()) { - const QmlObjectNode node = m_model->rewriterView()->modelNodeForId(id); - if (node.isValid()) - return node.allStateNames(); - } - - return {}; -} - -bool EffectMakerContextObject::isBlocked(const QString &) const -{ - return false; -} - -} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h b/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h deleted file mode 100644 index e27957f4ec..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakercontextobject.h +++ /dev/null @@ -1,101 +0,0 @@ -// 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 -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace QmlDesigner { - -class EffectMakerContextObject : 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: - EffectMakerContextObject(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; - Model *m_model = nullptr; - - QPoint m_lastPos; - - bool m_isBaseState = false; - bool m_selectionChanged = false; -}; - -} // QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp deleted file mode 100644 index 42f0977f7d..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.cpp +++ /dev/null @@ -1,763 +0,0 @@ -// 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 "effectmakermodel.h" - -#include "compositionnode.h" -#include "syntaxhighlighterdata.h" -#include "uniform.h" - -#include -#include - -#include - -namespace QmlDesigner { - -EffectMakerModel::EffectMakerModel(QObject *parent) - : QAbstractListModel{parent} -{ -} - -QHash EffectMakerModel::roleNames() const -{ - QHash roles; - roles[NameRole] = "nodeName"; - roles[EnabledRole] = "nodeEnabled"; - roles[UniformsRole] = "nodeUniformsModel"; - return roles; -} - -int EffectMakerModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent) - - return m_nodes.count(); -} - -QVariant EffectMakerModel::data(const QModelIndex &index, int role) const -{ - QTC_ASSERT(index.isValid() && index.row() < m_nodes.size(), return {}); - QTC_ASSERT(roleNames().contains(role), return {}); - - return m_nodes.at(index.row())->property(roleNames().value(role)); -} - -bool EffectMakerModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid() || !roleNames().contains(role)) - return false; - - if (role == EnabledRole) { - m_nodes.at(index.row())->setIsEnabled(value.toBool()); - emit dataChanged(index, index, {role}); - } - - return true; -} - -void EffectMakerModel::setIsEmpty(bool val) -{ - if (m_isEmpty != val) { - m_isEmpty = val; - emit isEmptyChanged(); - } -} - -void EffectMakerModel::addNode(const QString &nodeQenPath) -{ - beginInsertRows({}, m_nodes.size(), m_nodes.size()); - auto *node = new CompositionNode(nodeQenPath); - m_nodes.append(node); - endInsertRows(); - - setIsEmpty(false); -} - -void EffectMakerModel::moveNode(int fromIdx, int toIdx) -{ - if (fromIdx == toIdx) - return; - - int toIdxAdjusted = fromIdx < toIdx ? toIdx + 1 : toIdx; // otherwise beginMoveRows() crashes - beginMoveRows({}, fromIdx, fromIdx, {}, toIdxAdjusted); - m_nodes.move(fromIdx, toIdx); - endMoveRows(); -} - -void EffectMakerModel::removeNode(int idx) -{ - beginRemoveRows({}, idx, idx); - CompositionNode *node = m_nodes.at(idx); - m_nodes.removeAt(idx); - delete node; - endRemoveRows(); - - if (m_nodes.isEmpty()) - setIsEmpty(true); -} - -QString EffectMakerModel::fragmentShader() const -{ - return m_fragmentShader; -} - -void EffectMakerModel::setFragmentShader(const QString &newFragmentShader) -{ - if (m_fragmentShader == newFragmentShader) - return; - - m_fragmentShader = newFragmentShader; -} - -QString EffectMakerModel::vertexShader() const -{ - return m_vertexShader; -} - -void EffectMakerModel::setVertexShader(const QString &newVertexShader) -{ - if (m_vertexShader == newVertexShader) - return; - - m_vertexShader = newVertexShader; -} - -const QList EffectMakerModel::allUniforms() -{ - QList uniforms = {}; - for (const auto &node : std::as_const(m_nodes)) - uniforms.append(static_cast(node->uniformsModel())->uniforms()); - return uniforms; -} - -const QString EffectMakerModel::getBufUniform() -{ - QList uniforms = allUniforms(); - QString s; - s += "layout(std140, binding = 0) uniform buf {\n"; - s += " mat4 qt_Matrix;\n"; - s += " float qt_Opacity;\n"; - if (m_shaderFeatures.enabled(ShaderFeatures::Time)) - s += " float iTime;\n"; - if (m_shaderFeatures.enabled(ShaderFeatures::Frame)) - s += " int iFrame;\n"; - if (m_shaderFeatures.enabled(ShaderFeatures::Resolution)) - s += " vec3 iResolution;\n"; - if (m_shaderFeatures.enabled(ShaderFeatures::Mouse)) - s += " vec4 iMouse;\n"; - for (const auto uniform : uniforms) { - // TODO: Check if uniform is already added. - if (uniform->type() != Uniform::Type::Sampler && uniform->type() != Uniform::Type::Define) { - QString type = Uniform::stringFromType(uniform->type()); - QString props = " " + type + " " + uniform->name() + ";\n"; - s += props; - } - } - s += "};\n"; - return s; -} - -const QString EffectMakerModel::getVSUniforms() -{ - QString s; - s += "#version 440\n"; - s += '\n'; - s += "layout(location = 0) in vec4 qt_Vertex;\n"; - s += "layout(location = 1) in vec2 qt_MultiTexCoord0;\n"; - s += "layout(location = 0) out vec2 texCoord;\n"; - if (m_shaderFeatures.enabled(ShaderFeatures::FragCoord)) - s += "layout(location = 1) out vec2 fragCoord;\n"; - s += '\n'; - s += getBufUniform(); - s += '\n'; - s += "out gl_PerVertex { vec4 gl_Position; };\n"; - s += '\n'; - return s; -} - -const QString EffectMakerModel::getFSUniforms() -{ - const QList uniforms = allUniforms(); - QString s; - s += "#version 440\n"; - s += '\n'; - s += "layout(location = 0) in vec2 texCoord;\n"; - if (m_shaderFeatures.enabled(ShaderFeatures::FragCoord)) - s += "layout(location = 1) in vec2 fragCoord;\n"; - s += "layout(location = 0) out vec4 fragColor;\n"; - s += '\n'; - s += getBufUniform(); - s += '\n'; - - bool usesSource = m_shaderFeatures.enabled(ShaderFeatures::Source); - if (usesSource) - s += "layout(binding = 1) uniform sampler2D iSource;\n"; - - // Add sampler uniforms - int bindingIndex = usesSource ? 2 : 1; - for (const auto uniform : uniforms) { - // TODO: Check if uniform is already added. - if (uniform->type() == Uniform::Type::Sampler) { - // Start index from 2, 1 is source item - QString props = QString("layout(binding = %1) uniform sampler2D %2") - .arg(bindingIndex).arg(uniform->name()); - s += props + ";\n"; - bindingIndex++; - } - } - s += '\n'; - if (m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) { - const int blurItems = 5; - for (int i = 1; i <= blurItems; i++) { - QString props = QString("layout(binding = %1) uniform sampler2D iSourceBlur%2") - .arg(bindingIndex).arg(QString::number(i)); - s += props + ";\n"; - bindingIndex++; - } - s += '\n'; - } - return s; -} - -// Detects common GLSL error messages and returns potential -// additional error information related to them. -QString EffectMakerModel::detectErrorMessage(const QString &errorMessage) -{ - static QHash nodeErrors { - { "'BLUR_HELPER_MAX_LEVEL' : undeclared identifier", "BlurHelper"}, - { "'iSourceBlur1' : undeclared identifier", "BlurHelper"}, - { "'hash23' : no matching overloaded function found", "NoiseHelper" }, - { "'HASH_BOX_SIZE' : undeclared identifier", "NoiseHelper" }, - { "'pseudo3dNoise' : no matching overloaded function found", "NoiseHelper" } - }; - - QString missingNodeError = QStringLiteral("Are you missing a %1 node?\n"); - QHash::const_iterator i = nodeErrors.constBegin(); - while (i != nodeErrors.constEnd()) { - if (errorMessage.contains(i.key())) - return missingNodeError.arg(i.value()); - ++i; - } - return QString(); -} - -// Return first error message (if any) -EffectError EffectMakerModel::effectError() const -{ - for (const EffectError &e : std::as_const(m_effectErrors)) { - if (!e.m_message.isEmpty()) - return e; - } - return {}; -} - -// Set the effect error message with optional type and lineNumber. -// Type comes from ErrorTypes, defaulting to common errors (-1). -// Note that type must match with UI editor tab index. -void EffectMakerModel::setEffectError(const QString &errorMessage, int type, int lineNumber) -{ - EffectError error; - error.m_type = type; - if (type == 1 || type == 2) { - // For shaders, get the line number from baker output. - // Which is something like "ERROR: :15: message" - int glslErrorLineNumber = -1; - QStringList errorStringList = errorMessage.split(m_spaceReg, Qt::SkipEmptyParts); - if (errorStringList.size() >= 2) { - QString lineString = errorStringList.at(1).trimmed(); - if (lineString.size() >= 3) { - // String is ":[linenumber]:", get only the number. - glslErrorLineNumber = lineString.sliced(1, lineString.size() - 2).toInt(); - } - } - error.m_line = glslErrorLineNumber; - } else { - // For QML (and others) use given linenumber - error.m_line = lineNumber; - } - - QString additionalErrorInfo = detectErrorMessage(errorMessage); - error.m_message = additionalErrorInfo + errorMessage; - m_effectErrors.insert(type, error); - Q_EMIT effectErrorChanged(); -} - -void EffectMakerModel::resetEffectError(int type) -{ - if (m_effectErrors.contains(type)) { - m_effectErrors.remove(type); - Q_EMIT effectErrorChanged(); - } -} - -// Get value in QML format that used for exports -QString EffectMakerModel::valueAsString(const Uniform &uniform) -{ - if (uniform.type() == Uniform::Type::Bool) { - return uniform.value().toBool() ? QString("true") : QString("false"); - } else if (uniform.type() == Uniform::Type::Int) { - return QString::number(uniform.value().toInt()); - } else if (uniform.type() == Uniform::Type::Float) { - return QString::number(uniform.value().toDouble()); - } else if (uniform.type() == Uniform::Type::Vec2) { - QVector2D v2 = uniform.value().value(); - return QString("Qt.point(%1, %2)").arg(v2.x(), v2.y()); - } else if (uniform.type() == Uniform::Type::Vec3) { - QVector3D v3 = uniform.value().value(); - return QString("Qt.vector3d(%1, %2, %3)").arg(v3.x(), v3.y(), v3.z()); - } else if (uniform.type() == Uniform::Type::Vec4) { - QVector4D v4 = uniform.value().value(); - return QString("Qt.vector4d(%1, %2, %3, %4)").arg(v4.x(), v4.y(), v4.z(), v4.w()); - } else if (uniform.type() == Uniform::Type::Color) { - QColor c = uniform.value().value(); - return QString("Qt.rgba(%1, %2, %3, %4)").arg(c.redF(), c.greenF(), c.blueF(), c.alphaF()); - } else if (uniform.type() == Uniform::Type::Sampler) { - return getImageElementName(uniform); - } else if (uniform.type() == Uniform::Type::Define) { - return uniform.value().toString(); - } else { - qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); - return QString(); - } -} - -// Get value in QML binding that used for previews -QString EffectMakerModel::valueAsBinding(const Uniform &uniform) -{ - if (uniform.type() == Uniform::Type::Bool || uniform.type() == Uniform::Type::Int - || uniform.type() == Uniform::Type::Float || uniform.type() == Uniform::Type::Define) { - return "g_propertyData." + uniform.name(); - } else if (uniform.type() == Uniform::Type::Vec2) { - QString sx = QString("g_propertyData.%1.x").arg(uniform.name()); - QString sy = QString("g_propertyData.%1.y").arg(uniform.name()); - return QString("Qt.point(%1, %2)").arg(sx, sy); - } else if (uniform.type() == Uniform::Type::Vec3) { - QString sx = QString("g_propertyData.%1.x").arg(uniform.name()); - QString sy = QString("g_propertyData.%1.y").arg(uniform.name()); - QString sz = QString("g_propertyData.%1.z").arg(uniform.name()); - return QString("Qt.vector3d(%1, %2, %3)").arg(sx, sy, sz); - } else if (uniform.type() == Uniform::Type::Vec4) { - QString sx = QString("g_propertyData.%1.x").arg(uniform.name()); - QString sy = QString("g_propertyData.%1.y").arg(uniform.name()); - QString sz = QString("g_propertyData.%1.z").arg(uniform.name()); - QString sw = QString("g_propertyData.%1.w").arg(uniform.name()); - return QString("Qt.vector4d(%1, %2, %3, %4)").arg(sx, sy, sz, sw); - } else if (uniform.type() == Uniform::Type::Color) { - QString sr = QString("g_propertyData.%1.r").arg(uniform.name()); - QString sg = QString("g_propertyData.%1.g").arg(uniform.name()); - QString sb = QString("g_propertyData.%1.b").arg(uniform.name()); - QString sa = QString("g_propertyData.%1.a").arg(uniform.name()); - return QString("Qt.rgba(%1, %2, %3, %4)").arg(sr, sg, sb, sa); - } else if (uniform.type() == Uniform::Type::Sampler) { - return getImageElementName(uniform); - } else { - qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); - return QString(); - } -} - -// Get value in GLSL format that is used for non-exported const properties -QString EffectMakerModel::valueAsVariable(const Uniform &uniform) -{ - if (uniform.type() == Uniform::Type::Bool) { - return uniform.value().toBool() ? QString("true") : QString("false"); - } else if (uniform.type() == Uniform::Type::Int) { - return QString::number(uniform.value().toInt()); - } else if (uniform.type() == Uniform::Type::Float) { - return QString::number(uniform.value().toDouble()); - } else if (uniform.type() == Uniform::Type::Vec2) { - QVector2D v2 = uniform.value().value(); - return QString("vec2(%1, %2)").arg(v2.x(), v2.y()); - } else if (uniform.type() == Uniform::Type::Vec3) { - QVector3D v3 = uniform.value().value(); - return QString("vec3(%1, %2, %3)").arg(v3.x(), v3.y(), v3.z()); - } else if (uniform.type() == Uniform::Type::Vec4) { - QVector4D v4 = uniform.value().value(); - return QString("vec4(%1, %2, %3, %4)").arg(v4.x(), v4.y(), v4.z(), v4.w()); - } else if (uniform.type() == Uniform::Type::Color) { - QColor c = uniform.value().value(); - return QString("vec4(%1, %2, %3, %4)").arg(c.redF(), c.greenF(), c.blueF(), c.alphaF()); - } else { - qWarning() << QString("Unhandled const variable type: %1").arg(int(uniform.type())).toLatin1(); - return QString(); - } -} - -// Return name for the image property Image element -QString EffectMakerModel::getImageElementName(const Uniform &uniform) -{ - // TODO - Q_UNUSED(uniform) - return {}; -} - -const QString EffectMakerModel::getConstVariables() -{ - const QList uniforms = allUniforms(); - QString s; - for (Uniform *uniform : uniforms) { - // TODO: Check if uniform is already added. - QString constValue = valueAsVariable(*uniform); - QString type = Uniform::stringFromType(uniform->type()); - s += QString("const %1 %2 = %3;\n").arg(type, uniform->name(), constValue); - } - if (!s.isEmpty()) - s += '\n'; - - return s; -} - -const QString EffectMakerModel::getDefineProperties() -{ - const QList uniforms = allUniforms(); - QString s; - for (Uniform *uniform : uniforms) { - // TODO: Check if uniform is already added. - if (uniform->type() == Uniform::Type::Define) { - QString defineValue = uniform->value().toString(); - s += QString("#define %1 %2\n").arg(uniform->name(), defineValue); - } - } - if (!s.isEmpty()) - s += '\n'; - - return s; -} - -int EffectMakerModel::getTagIndex(const QStringList &code, const QString &tag) -{ - int index = -1; - int line = 0; - const QString tagString = QString("@%1").arg(tag); - for (const QString &s : code) { - auto st = s.trimmed(); - // Check if line or first non-space content of the line matches to tag - static auto spaceReg = QRegularExpression("\\s"); - auto firstSpace = st.indexOf(spaceReg); - QString firstWord = st; - if (firstSpace > 0) - firstWord = st.sliced(0, firstSpace); - if (firstWord == tagString) { - index = line; - break; - } - line++; - } - return index; -} - -QString EffectMakerModel::processVertexRootLine(const QString &line) -{ - QString output; - QStringList lineList = line.split(m_spaceReg, Qt::SkipEmptyParts); - if (lineList.length() > 1 && lineList.at(0) == QStringLiteral("out")) { - lineList.removeFirst(); - QString outLine = lineList.join(' '); - m_shaderVaryingVariables << outLine; - } else { - output = line + '\n'; - } - return output; -} - -QString EffectMakerModel::processFragmentRootLine(const QString &line) -{ - QString output; - QStringList lineList = line.split(m_spaceReg, Qt::SkipEmptyParts); - // Just skip all "in" variables. It is enough to have "out" variable in vertex. - if (lineList.length() > 1 && lineList.at(0) == QStringLiteral("in")) - return QString(); - output = line + '\n'; - return output; -} - -QStringList EffectMakerModel::getDefaultRootVertexShader() -{ - if (m_defaultRootVertexShader.isEmpty()) { - m_defaultRootVertexShader << "void main() {"; - m_defaultRootVertexShader << " texCoord = qt_MultiTexCoord0;"; - m_defaultRootVertexShader << " fragCoord = qt_Vertex.xy;"; - m_defaultRootVertexShader << " vec2 vertCoord = qt_Vertex.xy;"; - m_defaultRootVertexShader << " @nodes"; - m_defaultRootVertexShader << " gl_Position = qt_Matrix * vec4(vertCoord, 0.0, 1.0);"; - m_defaultRootVertexShader << "}"; - } - return m_defaultRootVertexShader; -} - -QStringList EffectMakerModel::getDefaultRootFragmentShader() -{ - if (m_defaultRootFragmentShader.isEmpty()) { - m_defaultRootFragmentShader << "void main() {"; - m_defaultRootFragmentShader << " fragColor = texture(iSource, texCoord);"; - m_defaultRootFragmentShader << " @nodes"; - m_defaultRootFragmentShader << " fragColor = fragColor * qt_Opacity;"; - m_defaultRootFragmentShader << "}"; - } - return m_defaultRootFragmentShader; -} - -// Remove all post-processing tags ("@tag") from the code. -// Except "@nodes" tag as that is handled later. -QStringList EffectMakerModel::removeTagsFromCode(const QStringList &codeLines) -{ - QStringList s; - for (const QString &line : codeLines) { - const auto trimmedLine = line.trimmed(); - if (!trimmedLine.startsWith('@') || trimmedLine.startsWith("@nodes")) { - s << line; - } else { - // Check if the tag is known - bool validTag = false; - const QList tags = SyntaxHighlighterData::reservedTagNames(); - QString firstWord = trimmedLine.split(m_spaceReg, Qt::SkipEmptyParts).first(); - for (const QByteArrayView &tag : tags) { - if (firstWord == QString::fromUtf8(tag)) { - validTag = true; - break; - } - } - if (!validTag) - setEffectError(QString("Unknown tag: %1").arg(trimmedLine), ErrorPreprocessor); - } - } - return s; -} - -QString EffectMakerModel::removeTagsFromCode(const QString &code) -{ - QStringList codeLines = removeTagsFromCode(code.split('\n')); - return codeLines.join('\n'); -} - -QString EffectMakerModel::getCustomShaderVaryings(bool outState) -{ - QString output; - QString direction = outState ? QStringLiteral("out") : QStringLiteral("in"); - int varLocation = m_shaderFeatures.enabled(ShaderFeatures::FragCoord) ? 2 : 1; - for (const QString &var : std::as_const(m_shaderVaryingVariables)) { - output += QString("layout(location = %1) %2 %3\n").arg(QString::number(varLocation), direction, var); - varLocation++; - } - return output; -} - -QString EffectMakerModel::generateVertexShader(bool includeUniforms) -{ - QString s; - - if (includeUniforms) - s += getVSUniforms(); - - // Remove tags when not generating for features check - const bool removeTags = includeUniforms; - - s += getDefineProperties(); - s += getConstVariables(); - - // When the node is complete, add shader code in correct nodes order - // split to root and main parts - QString s_root; - QString s_main; - QStringList s_sourceCode; - m_shaderVaryingVariables.clear(); - for (const CompositionNode *n : std::as_const(m_nodes)) { - if (!n->vertexCode().isEmpty() && n->isEnabled()) { - if (n->type() == CompositionNode::NodeType::SourceNode) { - s_sourceCode = n->vertexCode().split('\n'); - } else if (n->type() == CompositionNode::NodeType::CustomNode) { - const QStringList vertexCode = n->vertexCode().split('\n'); - int mainIndex = getTagIndex(vertexCode, QStringLiteral("main")); - int line = 0; - for (const QString &ss : vertexCode) { - if (mainIndex == -1 || line > mainIndex) - s_main += QStringLiteral(" ") + ss + '\n'; - else if (line < mainIndex) - s_root += processVertexRootLine(ss); - line++; - } - } - } - } - - if (s_sourceCode.isEmpty()) { - // If source nodes doesn't contain any code, use default one - s_sourceCode << getDefaultRootVertexShader(); - } - - if (removeTags) { - s_sourceCode = removeTagsFromCode(s_sourceCode); - s_root = removeTagsFromCode(s_root); - s_main = removeTagsFromCode(s_main); - } - - s += getCustomShaderVaryings(true); - s += s_root + '\n'; - - int nodesIndex = getTagIndex(s_sourceCode, QStringLiteral("nodes")); - int line = 0; - for (const QString &ss : std::as_const(s_sourceCode)) - s += (line++ == nodesIndex) ? s_main : ss + '\n'; - - return s; -} - -QString EffectMakerModel::generateFragmentShader(bool includeUniforms) -{ - QString s; - - if (includeUniforms) - s += getFSUniforms(); - - // Remove tags when not generating for features check - const bool removeTags = includeUniforms; - - s += getDefineProperties(); - s += getConstVariables(); - - // When the node is complete, add shader code in correct nodes order - // split to root and main parts - QString s_root; - QString s_main; - QStringList s_sourceCode; - for (const CompositionNode *n : std::as_const(m_nodes)) { - if (!n->fragmentCode().isEmpty() && n->isEnabled()) { - if (n->type() == CompositionNode::NodeType::SourceNode) { - s_sourceCode = n->fragmentCode().split('\n'); - } else if (n->type() == CompositionNode::NodeType::CustomNode) { - const QStringList fragmentCode = n->fragmentCode().split('\n'); - int mainIndex = getTagIndex(fragmentCode, QStringLiteral("main")); - int line = 0; - for (const QString &ss : fragmentCode) { - if (mainIndex == -1 || line > mainIndex) - s_main += QStringLiteral(" ") + ss + '\n'; - else if (line < mainIndex) - s_root += processFragmentRootLine(ss); - line++; - } - } - } - } - - if (s_sourceCode.isEmpty()) { - // If source nodes doesn't contain any code, use default one - s_sourceCode << getDefaultRootFragmentShader(); - } - - if (removeTags) { - s_sourceCode = removeTagsFromCode(s_sourceCode); - s_root = removeTagsFromCode(s_root); - s_main = removeTagsFromCode(s_main); - } - - s += getCustomShaderVaryings(false); - s += s_root + '\n'; - - int nodesIndex = getTagIndex(s_sourceCode, QStringLiteral("nodes")); - int line = 0; - for (const QString &ss : std::as_const(s_sourceCode)) - s += (line++ == nodesIndex) ? s_main : ss + '\n'; - - return s; -} - -// Generates string of the custom properties (uniforms) into ShaderEffect component -// Also generates QML images elements for samplers. -void EffectMakerModel::updateCustomUniforms() -{ - QString exportedRootPropertiesString; - QString previewEffectPropertiesString; - QString exportedEffectPropertiesString; - - const QList uniforms = allUniforms(); - for (Uniform *uniform : uniforms) { - // TODO: Check if uniform is already added. - const bool isDefine = uniform->type() == Uniform::Type::Define; - QString type = Uniform::typeToProperty(uniform->type()); - QString value = valueAsString(*uniform); - QString bindedValue = valueAsBinding(*uniform); - // When user has set custom uniform value, use it as-is - if (uniform->useCustomValue()) { - value = uniform->customValue(); - bindedValue = value; - } - // Note: Define type properties appear also as QML properties (in preview) in case QML side - // needs to use them. This is used at least by BlurHelper BLUR_HELPER_MAX_LEVEL. - QString propertyName = isDefine ? uniform->name().toLower() : uniform->name(); - if (!uniform->useCustomValue() && !isDefine && !uniform->description().isEmpty()) { - // When exporting, add API documentation for properties - const QStringList descriptionLines = uniform->description().split('\n'); - for (const QString &line : descriptionLines) { - if (line.trimmed().isEmpty()) - exportedRootPropertiesString += QStringLiteral(" //\n"); - else - exportedRootPropertiesString += QStringLiteral(" // ") + line + '\n'; - } - } - QString valueString = value.isEmpty() ? QString() : QString(": %1").arg(value); - QString bindedValueString = bindedValue.isEmpty() ? QString() : QString(": %1").arg(bindedValue); - // Custom values are not readonly, others inside the effect can be - QString readOnly = uniform->useCustomValue() ? QString() : QStringLiteral("readonly "); - previewEffectPropertiesString += " " + readOnly + "property " + type + " " - + propertyName + bindedValueString + '\n'; - // Define type properties are not added into exports - if (!isDefine) { - if (uniform->useCustomValue()) { - // Custom values are only inside the effect, with description comments - if (!uniform->description().isEmpty()) { - const QStringList descriptionLines = uniform->description().split('\n'); - for (const QString &line : descriptionLines) - exportedEffectPropertiesString += QStringLiteral(" // ") + line + '\n'; - } - exportedEffectPropertiesString += QStringLiteral(" ") + readOnly - + "property " + type + " " + propertyName - + bindedValueString + '\n'; - } else { - // Custom values are not added into root - exportedRootPropertiesString += " property " + type + " " + propertyName - + valueString + '\n'; - exportedEffectPropertiesString += QStringLiteral(" ") - + readOnly + "property alias " + propertyName - + ": rootItem." + uniform->name() + '\n'; - } - } - } - - // See if any of the properties changed - // TODO -} - -void EffectMakerModel::bakeShaders() -{ - resetEffectError(ErrorPreprocessor); - if (m_vertexShader == generateVertexShader() && m_fragmentShader == generateFragmentShader()) { - setShadersUpToDate(true); - return; - } - - setShadersUpToDate(false); - - // First update the features based on shader content - // This will make sure that next calls to "generate" will produce correct uniforms. - m_shaderFeatures.update(generateVertexShader(false), generateFragmentShader(false), m_previewEffectPropertiesString); - - updateCustomUniforms(); - - // TODO: Shaders baking -} - -bool EffectMakerModel::shadersUpToDate() const -{ - return m_shadersUpToDate; -} - -void EffectMakerModel::setShadersUpToDate(bool UpToDate) -{ - if (m_shadersUpToDate == UpToDate) - return; - m_shadersUpToDate = UpToDate; - emit shadersUpToDateChanged(); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h deleted file mode 100644 index 58c6a93fd8..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakermodel.h +++ /dev/null @@ -1,138 +0,0 @@ -// 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 -#include -#include - -namespace QmlDesigner { - -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 EffectMakerModel : 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 shadersUpToDate READ shadersUpToDate WRITE setShadersUpToDate NOTIFY shadersUpToDateChanged) - -public: - EffectMakerModel(QObject *parent = nullptr); - - QHash 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); - - Q_INVOKABLE void moveNode(int fromIdx, int toIdx); - Q_INVOKABLE void removeNode(int idx); - - bool shadersUpToDate() const; - void setShadersUpToDate(bool newShadersUpToDate); - - QString fragmentShader() const; - void setFragmentShader(const QString &newFragmentShader); - QString vertexShader() const; - void setVertexShader(const QString &newVertexShader); - -signals: - void isEmptyChanged(); - void selectedIndexChanged(int idx); - void effectErrorChanged(); - void shadersUpToDateChanged(); - -private: - enum Roles { - NameRole = Qt::UserRole + 1, - EnabledRole, - UniformsRole - }; - - enum ErrorTypes { - ErrorCommon = -1, - ErrorQMLParsing, - ErrorVert, - ErrorFrag, - ErrorQMLRuntime, - ErrorPreprocessor - }; - - bool isValidIndex(int idx) const; - - const QList allUniforms(); - - const QString getBufUniform(); - const QString getVSUniforms(); - const QString getFSUniforms(); - - QString detectErrorMessage(const QString &errorMessage); - EffectError effectError() const; - void setEffectError(const QString &errorMessage, int type = -1, int lineNumber = -1); - void resetEffectError(int type); - - QString valueAsString(const Uniform &uniform); - QString valueAsBinding(const Uniform &uniform); - QString valueAsVariable(const Uniform &uniform); - QString getImageElementName(const Uniform &uniform); - 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 updateCustomUniforms(); - void bakeShaders(); - - QList m_nodes; - - int m_selectedIndex = -1; - bool m_isEmpty = true; - // True when shaders haven't changed since last baking - bool m_shadersUpToDate = true; - QMap m_effectErrors; - ShaderFeatures m_shaderFeatures; - QStringList m_shaderVaryingVariables; - QString m_fragmentShader; - QString m_vertexShader; - QStringList m_defaultRootVertexShader; - QStringList m_defaultRootFragmentShader; - // 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; - - const QRegularExpression m_spaceReg = QRegularExpression("\\s+"); -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp deleted file mode 100644 index 521e3e7ce2..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// 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 "effectmakernodesmodel.h" - -#include - -#include - -namespace QmlDesigner { - -EffectMakerNodesModel::EffectMakerNodesModel(QObject *parent) - : QAbstractListModel{parent} -{ -} - -QHash EffectMakerNodesModel::roleNames() const -{ - QHash roles; - roles[CategoryNameRole] = "categoryName"; - roles[CategoryNodesRole] = "categoryNodes"; - - return roles; -} - -int EffectMakerNodesModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent) - - return m_categories.count(); -} - -QVariant EffectMakerNodesModel::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 EffectMakerNodesModel::findNodesPath() -{ - if (m_nodesPath.exists() || m_probeNodesDir) - return; - - QDir nodesDir; - - if (!qEnvironmentVariable("EFFECT_MAKER_NODES_PATH").isEmpty()) - nodesDir.setPath(qEnvironmentVariable("EFFECT_MAKER_NODES_PATH")); - else if (Utils::HostOsInfo::isMacHost()) - nodesDir.setPath(QCoreApplication::applicationDirPath() + "/../Resources/effect_maker_nodes"); - - // search for nodesDir from exec dir and up - if (nodesDir.dirName() == ".") { - m_probeNodesDir = true; // probe only once - nodesDir.setPath(QCoreApplication::applicationDirPath()); - while (!nodesDir.cd("effect_maker_nodes") && nodesDir.cdUp()) - ; // do nothing - - if (nodesDir.dirName() != "effect_maker_nodes") // bundlePathDir not found - return; - } - - m_nodesPath = Utils::FilePath::fromString(nodesDir.path()); -} - -void EffectMakerNodesModel::loadModel() -{ - findNodesPath(); - - if (!m_nodesPath.exists()) { - qWarning() << __FUNCTION__ << "Effects not found."; - return; - } - - QDirIterator itCategories(m_nodesPath.toString(), QDir::Dirs | QDir::NoDotAndDotDot); - while (itCategories.hasNext()) { - itCategories.next(); - - if (itCategories.fileName() == "images" || itCategories.fileName() == "common") - continue; - - QString catName = itCategories.fileName(); - - QList effects = {}; - Utils::FilePath categoryPath = m_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); - } - - resetModel(); -} - -void EffectMakerNodesModel::resetModel() -{ - beginResetModel(); - endResetModel(); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h deleted file mode 100644 index 5ed702f84b..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakernodesmodel.h +++ /dev/null @@ -1,43 +0,0 @@ -// 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 - -#include - -namespace QmlDesigner { - -class EffectMakerNodesModel : public QAbstractListModel -{ - Q_OBJECT - - enum Roles { - CategoryNameRole = Qt::UserRole + 1, - CategoryNodesRole - }; - -public: - EffectMakerNodesModel(QObject *parent = nullptr); - - QHash 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 categories() const { return m_categories; } - -private: - void findNodesPath(); - - QList m_categories; - Utils::FilePath m_nodesPath; - bool m_probeNodesDir = false; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp deleted file mode 100644 index dac01905b6..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// 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 "effectmakeruniformsmodel.h" - -#include "uniform.h" - -#include - -namespace QmlDesigner { - -EffectMakerUniformsModel::EffectMakerUniformsModel(QObject *parent) - : QAbstractListModel{parent} -{ -} - -QHash EffectMakerUniformsModel::roleNames() const -{ - QHash 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"; - return roles; -} - -int EffectMakerUniformsModel::rowCount(const QModelIndex &parent) const -{ - Q_UNUSED(parent) - - return m_uniforms.size(); -} - -QVariant EffectMakerUniformsModel::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 EffectMakerUniformsModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid() || !roleNames().contains(role)) - return false; - - m_uniforms.at(index.row())->setValue(value); - emit dataChanged(index, index, {role}); - - return true; -} - -void EffectMakerUniformsModel::resetModel() -{ - beginResetModel(); - endResetModel(); -} - -void EffectMakerUniformsModel::addUniform(Uniform *uniform) -{ - beginInsertRows({}, m_uniforms.size(), m_uniforms.size()); - m_uniforms.append(uniform); - endInsertRows(); -} - -QList EffectMakerUniformsModel::uniforms() const -{ - return m_uniforms; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h b/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h deleted file mode 100644 index 1d69d6d1b2..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakeruniformsmodel.h +++ /dev/null @@ -1,45 +0,0 @@ -// 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 - -namespace QmlDesigner { - -class Uniform; - -class EffectMakerUniformsModel : public QAbstractListModel -{ - Q_OBJECT - -public: - EffectMakerUniformsModel(QObject *parent = nullptr); - - QHash 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 uniforms() const; - -private: - enum Roles { - NameRole = Qt::UserRole + 1, - DescriptionRole, - ValueRole, - BackendValueRole, - DefaultValueRole, - MaxValueRole, - MinValueRole, - TypeRole, - }; - - QList m_uniforms; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp deleted file mode 100644 index 641b41a8ce..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// 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 "effectmakerview.h" - -#include "effectmakerwidget.h" -#include "effectmakernodesmodel.h" -#include "designmodecontext.h" -#include "nodeinstanceview.h" - -#include - -#include -#include -#include -#include -#include - -namespace QmlDesigner { - -EffectMakerView::EffectMakerView(ExternalDependenciesInterface &externalDependencies) - : AbstractView{externalDependencies} -{ -} - -EffectMakerView::~EffectMakerView() -{} - -bool EffectMakerView::hasWidget() const -{ - return true; -} - -WidgetInfo EffectMakerView::widgetInfo() -{ - if (m_widget.isNull()) { - m_widget = new EffectMakerWidget{this}; - - auto context = new Internal::EffectMakerContext(m_widget.data()); - Core::ICore::addContextObject(context); - } - - return createWidgetInfo(m_widget.data(), "Effect Maker", WidgetInfo::LeftPane, 0, tr("Effect Maker")); -} - -void EffectMakerView::customNotification(const AbstractView * /*view*/, - const QString & /*identifier*/, - const QList & /*nodeList*/, - const QList & /*data*/) -{ - // TODO -} - -void EffectMakerView::modelAttached(Model *model) -{ - AbstractView::modelAttached(model); - - // Add some dummy effects data - //m_widget->effectMakerModel()->setEffects({"Drop Shadow", "Colorize", "Fast Blue"}); // TODO - m_widget->effectMakerNodesModel()->loadModel(); - m_widget->initView(); -} - -void EffectMakerView::modelAboutToBeDetached(Model *model) -{ - AbstractView::modelAboutToBeDetached(model); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.h b/src/plugins/qmldesigner/components/effectmaker/effectmakerview.h deleted file mode 100644 index 53e58acc67..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerview.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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 - -namespace QmlDesigner { - -class EffectMakerWidget; - -class EffectMakerView : public AbstractView -{ -public: - EffectMakerView(ExternalDependenciesInterface &externalDependencies); - ~EffectMakerView() override; - - bool hasWidget() const override; - WidgetInfo widgetInfo() override; - - // AbstractView - void modelAttached(Model *model) override; - void modelAboutToBeDetached(Model *model) override; - -private: - void customNotification(const AbstractView *view, const QString &identifier, - const QList &nodeList, const QList &data) override; - - QPointer m_widget; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp deleted file mode 100644 index f6f96bc886..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// 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 "effectmakerwidget.h" - -#include "effectmakercontextobject.h" -#include "effectmakermodel.h" -#include "effectmakernodesmodel.h" -#include "effectmakerview.h" -#include "qmldesignerconstants.h" -#include "qmldesignerplugin.h" -#include "qqmlcontext.h" -#include "theme.h" - -#include - -#include - -#include -#include -#include - -#include -#include - -namespace QmlDesigner { - -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(); -} - -EffectMakerWidget::EffectMakerWidget(EffectMakerView *view) - : m_effectMakerModel{new EffectMakerModel(this)} - , m_effectMakerNodesModel{new EffectMakerNodesModel(this)} - , m_effectMakerView(view) - , m_quickWidget{new StudioQuickWidget(this)} -{ - setWindowTitle(tr("Effect Maker", "Title of effect maker widget")); - setMinimumWidth(250); - - m_quickWidget->quickWidget()->installEventFilter(this); - - // create the inner widget - m_quickWidget->quickWidget()->setObjectName(Constants::OBJECT_NAME_EFFECT_MAKER); - m_quickWidget->setResizeMode(QQuickWidget::SizeRootObjectToView); - Theme::setupTheme(m_quickWidget->engine()); - m_quickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports"); - m_quickWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate)); - - auto layout = new QHBoxLayout(this); - layout->setContentsMargins({}); - layout->setSpacing(0); - layout->addWidget(m_quickWidget.data()); - - setStyleSheet(Theme::replaceCssColors( - QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css")))); - - QmlDesignerPlugin::trackWidgetFocusTime(this, Constants::EVENT_EFFECTMAKER_TIME); - - auto map = m_quickWidget->registerPropertyMap("EffectMakerBackend"); - map->setProperties({{"effectMakerNodesModel", QVariant::fromValue(m_effectMakerNodesModel.data())}, - {"effectMakerModel", QVariant::fromValue(m_effectMakerModel.data())}, - {"rootView", QVariant::fromValue(this)}}); -} - - -bool EffectMakerWidget::eventFilter(QObject *obj, QEvent *event) -{ - Q_UNUSED(obj) - Q_UNUSED(event) - - // TODO - - return false; -} - -void EffectMakerWidget::contextHelp(const Core::IContext::HelpCallback &callback) const -{ - Q_UNUSED(callback) -} - -StudioQuickWidget *EffectMakerWidget::quickWidget() const -{ - return m_quickWidget.data(); -} - -QPointer EffectMakerWidget::effectMakerModel() const -{ - return m_effectMakerModel; -} - -QPointer EffectMakerWidget::effectMakerNodesModel() const -{ - return m_effectMakerNodesModel; -} - -void EffectMakerWidget::addEffectNode(const QString &nodeQenPath) -{ - m_effectMakerModel->addNode(nodeQenPath); -} - -void EffectMakerWidget::focusSection(int section) -{ - Q_UNUSED(section) -} - -QSize EffectMakerWidget::sizeHint() const -{ - return {420, 420}; -} - -QString EffectMakerWidget::qmlSourcesPath() -{ -#ifdef SHARE_QML_PATH - if (Utils::qtcEnvironmentVariableIsSet("LOAD_QML_FROM_SOURCE")) - return QLatin1String(SHARE_QML_PATH) + "/effectMakerQmlSources"; -#endif - return Core::ICore::resourcePath("qmldesigner/effectMakerQmlSources").toString(); -} - -void EffectMakerWidget::initView() -{ - auto ctxObj = new EffectMakerContextObject(m_quickWidget->rootContext()); - m_quickWidget->rootContext()->setContextObject(ctxObj); - - m_backendModelNode.setup(m_effectMakerView->rootModelNode()); - m_quickWidget->rootContext()->setContextProperty("modelNodeBackend", &m_backendModelNode); - - // init the first load of the QML UI elements - reloadQmlSource(); -} - -void EffectMakerWidget::reloadQmlSource() -{ - const QString effectMakerQmlPath = qmlSourcesPath() + "/EffectMaker.qml"; - QTC_ASSERT(QFileInfo::exists(effectMakerQmlPath), return); - m_quickWidget->setSource(QUrl::fromLocalFile(effectMakerQmlPath)); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h b/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h deleted file mode 100644 index d59318eb45..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectmakerwidget.h +++ /dev/null @@ -1,59 +0,0 @@ -// 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 "qmlmodelnodeproxy.h" - -#include - -#include - -class StudioQuickWidget; - -namespace QmlDesigner { - -class EffectMakerView; -class EffectMakerModel; -class EffectMakerNodesModel; - -class EffectMakerWidget : public QFrame -{ - Q_OBJECT - -public: - EffectMakerWidget(EffectMakerView *view); - ~EffectMakerWidget() = default; - - void contextHelp(const Core::IContext::HelpCallback &callback) const; - - static QString qmlSourcesPath(); - void clearSearchFilter(); - - void delayedUpdateModel(); - void updateModel(); - void initView(); - - StudioQuickWidget *quickWidget() const; - QPointer effectMakerModel() const; - QPointer effectMakerNodesModel() const; - - Q_INVOKABLE void addEffectNode(const QString &nodeQenPath); - Q_INVOKABLE void focusSection(int section); - - QSize sizeHint() const override; - -protected: - bool eventFilter(QObject *obj, QEvent *event) override; - -private: - void reloadQmlSource(); - - QPointer m_effectMakerModel; - QPointer m_effectMakerNodesModel; - QPointer m_effectMakerView; - QPointer m_quickWidget; - QmlModelNodeProxy m_backendModelNode; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp b/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp deleted file mode 100644 index 08d11925f5..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// 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 -#include - -namespace QmlDesigner { - -EffectNode::EffectNode(const QString &qenPath) - : m_qenPath(qenPath) -{ - const QFileInfo fileInfo = QFileInfo(qenPath); - m_name = fileInfo.baseName(); - - QString iconPath = QStringLiteral("%1/icon/%2.svg").arg(fileInfo.absolutePath(), m_name); - 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); -} - -QString EffectNode::name() const -{ - return m_name; -} - -QString EffectNode::description() const -{ - return m_description; -} - -QString EffectNode::qenPath() const -{ - return m_qenPath; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnode.h b/src/plugins/qmldesigner/components/effectmaker/effectnode.h deleted file mode 100644 index 823fe092db..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectnode.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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 -#include - -namespace QmlDesigner { - -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) - -public: - EffectNode(const QString &qenPath); - - QString name() const; - QString description() const; - QString qenPath() const; - -private: - QString m_name; - QString m_description; - QString m_qenPath; - QUrl m_iconPath; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp b/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp deleted file mode 100644 index 36a8f0a0d0..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// 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 QmlDesigner { - -EffectNodesCategory::EffectNodesCategory(const QString &name, const QList &nodes) - : m_name(name), - m_categoryNodes(nodes) {} - -QString EffectNodesCategory::name() const -{ - return m_name; -} - -QList EffectNodesCategory::nodes() const -{ - return m_categoryNodes; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h b/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h deleted file mode 100644 index ba7d6868bc..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectnodescategory.h +++ /dev/null @@ -1,30 +0,0 @@ -// 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 - -namespace QmlDesigner { - -class EffectNodesCategory : public QObject -{ - Q_OBJECT - - Q_PROPERTY(QString categoryName MEMBER m_name CONSTANT) - Q_PROPERTY(QList categoryNodes MEMBER m_categoryNodes CONSTANT) - -public: - EffectNodesCategory(const QString &name, const QList &nodes); - - QString name() const; - QList nodes() const; - -private: - QString m_name; - QList m_categoryNodes; -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectutils.cpp b/src/plugins/qmldesigner/components/effectmaker/effectutils.cpp deleted file mode 100644 index 8f45b9a137..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectutils.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// 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 - -namespace QmlDesigner { - -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; -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/effectutils.h b/src/plugins/qmldesigner/components/effectmaker/effectutils.h deleted file mode 100644 index 0abe4d64e6..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/effectutils.h +++ /dev/null @@ -1,20 +0,0 @@ -// 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 - -QT_FORWARD_DECLARE_CLASS(QJsonArray) - -namespace QmlDesigner { - -class EffectUtils -{ -public: - EffectUtils() = delete; - - static QString codeFromJsonArray(const QJsonArray &codeArray); -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp b/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp deleted file mode 100644 index 755b203d23..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.cpp +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#include "shaderfeatures.h" -#include -#include - -namespace QmlDesigner { - -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); -} - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h b/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h deleted file mode 100644 index 35fb507066..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/shaderfeatures.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#pragma once - -#include -#include - -namespace QmlDesigner { - -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; - -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 QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp b/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp deleted file mode 100644 index 47020ed0b0..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#include "syntaxhighlighterdata.h" - -namespace QmlDesigner { - -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 SyntaxHighlighterData::reservedArgumentNames() -{ - return { std::begin(shader_arg_names), std::end(shader_arg_names) }; -} - -QList SyntaxHighlighterData::reservedTagNames() -{ - return { std::begin(shader_tag_names), std::end(shader_tag_names) }; -} - -QList SyntaxHighlighterData::reservedFunctionNames() -{ - return { std::begin(shader_function_names), std::end(shader_function_names) }; -} - -} // namespace QmlDesigner - diff --git a/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h b/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h deleted file mode 100644 index 6342ea094a..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/syntaxhighlighterdata.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - -#pragma once - -#include -#include - -namespace QmlDesigner { - -class SyntaxHighlighterData -{ -public: - SyntaxHighlighterData(); - - static QList reservedArgumentNames(); - static QList reservedTagNames(); - static QList reservedFunctionNames(); -}; - -} // namespace QmlDesigner - diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp b/src/plugins/qmldesigner/components/effectmaker/uniform.cpp deleted file mode 100644 index 8074c3cc95..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.cpp +++ /dev/null @@ -1,324 +0,0 @@ -// 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 "propertyeditorvalue.h" - -#include -#include -#include - -namespace QmlDesigner { - -Uniform::Uniform(const QJsonObject &propObj) -{ - 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; - - if (m_type == Type::Sampler) { - if (!defaultValue.isEmpty()) - defaultValue = getResourcePath(defaultValue); - if (propObj.contains("enableMipmap")) - m_enableMipmap = getBoolValue(propObj.value("enableMipmap"), false); - // Update the mipmap property - QString mipmapProperty = mipmapPropertyName(m_name); - } - if (propObj.contains("value")) { - value = propObj.value("value").toString(); - if (m_type == Type::Sampler && !value.isEmpty()) - value = getResourcePath(value); - } else { - // QEN files don't store the current value, so with those use default value - value = defaultValue; - } - minValue = propObj.value("minValue").toString(); - maxValue = propObj.value("maxValue").toString(); - - setValueData(value, defaultValue, minValue, maxValue); - - m_backendValue = new 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::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 &value) const -{ - Q_UNUSED(value) - //TODO - return {}; -} - -// 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) -{ - 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 "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") - 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 QmlDesigner diff --git a/src/plugins/qmldesigner/components/effectmaker/uniform.h b/src/plugins/qmldesigner/components/effectmaker/uniform.h deleted file mode 100644 index 67699c5e53..0000000000 --- a/src/plugins/qmldesigner/components/effectmaker/uniform.h +++ /dev/null @@ -1,108 +0,0 @@ -// 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 -#include - -QT_FORWARD_DECLARE_CLASS(QColor) -QT_FORWARD_DECLARE_CLASS(QJsonObject) -QT_FORWARD_DECLARE_CLASS(QVector2D) - -namespace QmlDesigner { - -class PropertyEditorValue; - -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) - -public: - enum class Type - { - Bool, - Int, - Float, - Vec2, - Vec3, - Vec4, - Color, - Sampler, - Define - }; - - Uniform(const QJsonObject &props); - - 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 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); - 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 &value) const; - void setValueData(const QString &value, const QString &defaultValue, - const QString &minValue, const QString &maxValue); - - QVariant getInitializedVariant(bool maxValue); - QVariant valueStringToVariant(const QString &value); - - 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; - PropertyEditorValue *m_backendValue = nullptr; - - bool operator==(const Uniform &rhs) const noexcept - { - return this->m_name == rhs.m_name; - } -}; - -} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 9a4bbd280e..59236c4fe2 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -51,7 +51,7 @@ private: PropertyEditorValue *m_editorValue = nullptr; }; -class PropertyEditorValue : public QObject +class QMLDESIGNERCORE_EXPORT PropertyEditorValue : public QObject { Q_OBJECT diff --git a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h index baee63efc1..4740b01fbd 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h +++ b/src/plugins/qmldesigner/components/propertyeditor/qmlmodelnodeproxy.h @@ -9,7 +9,7 @@ namespace QmlDesigner { -class QmlModelNodeProxy : public QObject +class QMLDESIGNERCORE_EXPORT QmlModelNodeProxy : public QObject { Q_OBJECT diff --git a/src/plugins/qmldesigner/designmodecontext.cpp b/src/plugins/qmldesigner/designmodecontext.cpp index 13a03bc10b..43fb0d9d75 100644 --- a/src/plugins/qmldesigner/designmodecontext.cpp +++ b/src/plugins/qmldesigner/designmodecontext.cpp @@ -6,7 +6,6 @@ #include "collectionwidget.h" #include "designmodewidget.h" #include "edit3dwidget.h" -#include "effectmakerwidget.h" #include "formeditorwidget.h" #include "materialbrowserwidget.h" #include "navigatorwidget.h" @@ -99,18 +98,6 @@ void TextEditorContext::contextHelp(const HelpCallback &callback) const qobject_cast(m_widget)->contextHelp(callback); } -EffectMakerContext::EffectMakerContext(QWidget *widget) - : IContext(widget) -{ - setWidget(widget); - setContext(Core::Context(Constants::C_QMLEFFECTMAKER, Constants::C_QT_QUICK_TOOLS_MENU)); -} - -void EffectMakerContext::contextHelp(const HelpCallback &callback) const -{ - qobject_cast(m_widget)->contextHelp(callback); -} - CollectionEditorContext::CollectionEditorContext(QWidget *widget) : IContext(widget) { diff --git a/src/plugins/qmldesigner/designmodecontext.h b/src/plugins/qmldesigner/designmodecontext.h index 72c0a19523..12f0113d97 100644 --- a/src/plugins/qmldesigner/designmodecontext.h +++ b/src/plugins/qmldesigner/designmodecontext.h @@ -74,15 +74,6 @@ public: void contextHelp(const Core::IContext::HelpCallback &callback) const override; }; -class EffectMakerContext : public Core::IContext -{ - Q_OBJECT - -public: - EffectMakerContext(QWidget *widget); - void contextHelp(const Core::IContext::HelpCallback &callback) const override; -}; - class CollectionEditorContext : public Core::IContext { Q_OBJECT -- cgit v1.2.3