forked from qt-creator/qt-creator
QmlDesigner: Add code editor for Effect Composer
Task-number: QDS-13443 Change-Id: I02c7a85336f283e0e55bab24459a91fa299abb40 Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
This commit is contained in:
@@ -241,6 +241,9 @@
|
|||||||
"LocalOrientIcon": {
|
"LocalOrientIcon": {
|
||||||
"iconName": "localOrient_medium"
|
"iconName": "localOrient_medium"
|
||||||
},
|
},
|
||||||
|
"LiveUpdateIcon": {
|
||||||
|
"iconName": "restartParticles_medium"
|
||||||
|
},
|
||||||
"MoveToolIcon": {
|
"MoveToolIcon": {
|
||||||
"iconName": "move_medium"
|
"iconName": "move_medium"
|
||||||
},
|
},
|
||||||
@@ -276,6 +279,9 @@
|
|||||||
"SplitViewIcon": {
|
"SplitViewIcon": {
|
||||||
"iconName": "splitScreen_medium"
|
"iconName": "splitScreen_medium"
|
||||||
},
|
},
|
||||||
|
"SyncIcon": {
|
||||||
|
"iconName": "updateContent_medium"
|
||||||
|
},
|
||||||
"ToggleGroupIcon": {
|
"ToggleGroupIcon": {
|
||||||
"Off": {
|
"Off": {
|
||||||
"iconName": "selectOutline_medium"
|
"iconName": "selectOutline_medium"
|
||||||
|
@@ -195,6 +195,10 @@ Item {
|
|||||||
onAssignToSelectedClicked: {
|
onAssignToSelectedClicked: {
|
||||||
root.backendModel.assignToSelected()
|
root.backendModel.assignToSelected()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onOpenShadersCodeEditor: {
|
||||||
|
root.backendModel.openMainShadersCodeEditor()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SplitView {
|
SplitView {
|
||||||
@@ -366,6 +370,8 @@ Item {
|
|||||||
expanded = wasExpanded
|
expanded = wasExpanded
|
||||||
dragAnimation.enabled = true
|
dragAnimation.enabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onOpenShadersCodeEditor: (idx) => root.backendModel.openShadersCodeEditor(idx)
|
||||||
}
|
}
|
||||||
} // Repeater
|
} // Repeater
|
||||||
} // Column
|
} // Column
|
||||||
|
@@ -19,6 +19,7 @@ Rectangle {
|
|||||||
signal saveClicked
|
signal saveClicked
|
||||||
signal saveAsClicked
|
signal saveAsClicked
|
||||||
signal assignToSelectedClicked
|
signal assignToSelectedClicked
|
||||||
|
signal openShadersCodeEditor
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
spacing: 5
|
spacing: 5
|
||||||
@@ -48,12 +49,24 @@ Rectangle {
|
|||||||
style: StudioTheme.Values.viewBarButtonStyle
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
buttonIcon: StudioTheme.Constants.saveAs_medium
|
buttonIcon: StudioTheme.Constants.saveAs_medium
|
||||||
tooltip: qsTr("Save current composition with a new name")
|
tooltip: qsTr("Save current composition with a new name")
|
||||||
enabled: root.backendModel ? root.backendModel.isEnabled && root.backendModel.currentComposition !== ""
|
enabled: root.backendModel ? root.backendModel.isEnabled
|
||||||
|
&& root.backendModel.currentComposition !== ""
|
||||||
: false
|
: false
|
||||||
|
|
||||||
onClicked: root.saveAsClicked()
|
onClicked: root.saveAsClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HelperWidgets.AbstractButton {
|
||||||
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
|
buttonIcon: StudioTheme.Constants.codeEditor_medium
|
||||||
|
tooltip: qsTr("Open Code")
|
||||||
|
enabled: root.backendModel ? root.backendModel.isEnabled
|
||||||
|
&& root.backendModel.currentComposition !== ""
|
||||||
|
: false
|
||||||
|
|
||||||
|
onClicked: root.openShadersCodeEditor()
|
||||||
|
}
|
||||||
|
|
||||||
HelperWidgets.AbstractButton {
|
HelperWidgets.AbstractButton {
|
||||||
style: StudioTheme.Values.viewBarButtonStyle
|
style: StudioTheme.Values.viewBarButtonStyle
|
||||||
buttonIcon: StudioTheme.Constants.assignTo_medium
|
buttonIcon: StudioTheme.Constants.assignTo_medium
|
||||||
|
@@ -30,10 +30,34 @@ HelperWidgets.Section {
|
|||||||
eyeEnabled: nodeEnabled
|
eyeEnabled: nodeEnabled
|
||||||
eyeButtonToolTip: qsTr("Enable/Disable Node")
|
eyeButtonToolTip: qsTr("Enable/Disable Node")
|
||||||
|
|
||||||
|
signal openShadersCodeEditor(index: int)
|
||||||
|
|
||||||
onEyeButtonClicked: {
|
onEyeButtonClicked: {
|
||||||
nodeEnabled = root.eyeEnabled
|
nodeEnabled = root.eyeEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
icons: HelperWidgets.IconButton {
|
||||||
|
icon: StudioTheme.Constants.codeEditor_medium
|
||||||
|
transparentBg: true
|
||||||
|
buttonSize: 21
|
||||||
|
iconSize: StudioTheme.Values.smallIconFontSize
|
||||||
|
iconColor: StudioTheme.Values.themeTextColor
|
||||||
|
iconScale: containsMouse ? 1.2 : 1
|
||||||
|
implicitWidth: width
|
||||||
|
onClicked: root.openShadersCodeEditor(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
content: Label {
|
||||||
|
text: root.caption
|
||||||
|
color: root.labelColor
|
||||||
|
elide: Text.ElideRight
|
||||||
|
font.pixelSize: root.sectionFontSize
|
||||||
|
font.capitalization: root.labelCapitalization
|
||||||
|
anchors.verticalCenter: parent?.verticalCenter
|
||||||
|
textFormat: Text.RichText
|
||||||
|
leftPadding: StudioTheme.Values.toolbarSpacing
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
spacing: 10
|
spacing: 10
|
||||||
|
|
||||||
|
@@ -39,6 +39,8 @@ Item {
|
|||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property Item icons
|
||||||
|
|
||||||
property int leftPadding: StudioTheme.Values.sectionLeftPadding
|
property int leftPadding: StudioTheme.Values.sectionLeftPadding
|
||||||
property int rightPadding: 0
|
property int rightPadding: 0
|
||||||
property int topPadding: StudioTheme.Values.sectionHeadSpacerHeight
|
property int topPadding: StudioTheme.Values.sectionHeadSpacerHeight
|
||||||
@@ -214,6 +216,13 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: iconsContent
|
||||||
|
height: header.height
|
||||||
|
children: [ section.icons ]
|
||||||
|
Layout.preferredWidth: childrenRect.width
|
||||||
|
}
|
||||||
|
|
||||||
IconButton {
|
IconButton {
|
||||||
id: arrow
|
id: arrow
|
||||||
icon: StudioTheme.Constants.sectionToggle
|
icon: StudioTheme.Constants.sectionToggle
|
||||||
|
@@ -6,12 +6,14 @@ add_qtc_plugin(EffectComposer
|
|||||||
Qt::Core Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick
|
Qt::Core Qt::CorePrivate Qt::Widgets Qt::Qml Qt::QmlPrivate Qt::Quick
|
||||||
QtCreator::Utils
|
QtCreator::Utils
|
||||||
SOURCES
|
SOURCES
|
||||||
|
effectcodeeditorwidget.cpp effectcodeeditorwidget.h
|
||||||
effectcomposerplugin.cpp
|
effectcomposerplugin.cpp
|
||||||
effectcomposerwidget.cpp effectcomposerwidget.h
|
effectcomposerwidget.cpp effectcomposerwidget.h
|
||||||
effectcomposerview.cpp effectcomposerview.h
|
effectcomposerview.cpp effectcomposerview.h
|
||||||
effectcomposermodel.cpp effectcomposermodel.h
|
effectcomposermodel.cpp effectcomposermodel.h
|
||||||
effectcomposernodesmodel.cpp effectcomposernodesmodel.h
|
effectcomposernodesmodel.cpp effectcomposernodesmodel.h
|
||||||
effectcomposeruniformsmodel.cpp effectcomposeruniformsmodel.h
|
effectcomposeruniformsmodel.cpp effectcomposeruniformsmodel.h
|
||||||
|
effectshaderscodeeditor.cpp effectshaderscodeeditor.h
|
||||||
effectnode.cpp effectnode.h
|
effectnode.cpp effectnode.h
|
||||||
effectnodescategory.cpp effectnodescategory.h
|
effectnodescategory.cpp effectnodescategory.h
|
||||||
compositionnode.cpp compositionnode.h
|
compositionnode.cpp compositionnode.h
|
||||||
|
@@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
#include "compositionnode.h"
|
#include "compositionnode.h"
|
||||||
|
|
||||||
#include "effectutils.h"
|
|
||||||
#include "effectcomposeruniformsmodel.h"
|
#include "effectcomposeruniformsmodel.h"
|
||||||
|
#include "effectshaderscodeeditor.h"
|
||||||
|
#include "effectutils.h"
|
||||||
#include "propertyhandler.h"
|
#include "propertyhandler.h"
|
||||||
#include "uniform.h"
|
#include "uniform.h"
|
||||||
|
|
||||||
@@ -44,6 +45,8 @@ CompositionNode::CompositionNode(const QString &effectName, const QString &qenPa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompositionNode::~CompositionNode() = default;
|
||||||
|
|
||||||
QString CompositionNode::fragmentCode() const
|
QString CompositionNode::fragmentCode() const
|
||||||
{
|
{
|
||||||
return m_fragmentCode;
|
return m_fragmentCode;
|
||||||
@@ -110,8 +113,8 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
|
|||||||
|
|
||||||
m_name = json.value("name").toString();
|
m_name = json.value("name").toString();
|
||||||
m_description = json.value("description").toString();
|
m_description = json.value("description").toString();
|
||||||
m_fragmentCode = EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray());
|
setFragmentCode(EffectUtils::codeFromJsonArray(json.value("fragmentCode").toArray()));
|
||||||
m_vertexCode = EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray());
|
setVertexCode(EffectUtils::codeFromJsonArray(json.value("vertexCode").toArray()));
|
||||||
|
|
||||||
if (json.contains("extraMargin"))
|
if (json.contains("extraMargin"))
|
||||||
m_extraMargin = json.value("extraMargin").toInt();
|
m_extraMargin = json.value("extraMargin").toInt();
|
||||||
@@ -154,6 +157,36 @@ void CompositionNode::parse(const QString &effectName, const QString &qenPath, c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompositionNode::ensureShadersCodeEditor()
|
||||||
|
{
|
||||||
|
if (m_shadersCodeEditor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_shadersCodeEditor = Utils::makeUniqueObjectLatePtr<EffectShadersCodeEditor>(name());
|
||||||
|
m_shadersCodeEditor->setFragmentValue(fragmentCode());
|
||||||
|
m_shadersCodeEditor->setVertexValue(vertexCode());
|
||||||
|
|
||||||
|
connect(m_shadersCodeEditor.get(), &EffectShadersCodeEditor::vertexValueChanged, this, [this] {
|
||||||
|
setVertexCode(m_shadersCodeEditor->vertexValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_shadersCodeEditor.get(), &EffectShadersCodeEditor::fragmentValueChanged, this, [this] {
|
||||||
|
setFragmentCode(m_shadersCodeEditor->fragmentValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(
|
||||||
|
m_shadersCodeEditor.get(),
|
||||||
|
&EffectShadersCodeEditor::rebakeRequested,
|
||||||
|
this,
|
||||||
|
&CompositionNode::rebakeRequested);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompositionNode::requestRebakeIfLiveUpdateMode()
|
||||||
|
{
|
||||||
|
if (m_shadersCodeEditor && m_shadersCodeEditor->liveUpdate())
|
||||||
|
emit rebakeRequested();
|
||||||
|
}
|
||||||
|
|
||||||
QList<Uniform *> CompositionNode::uniforms() const
|
QList<Uniform *> CompositionNode::uniforms() const
|
||||||
{
|
{
|
||||||
return m_uniforms;
|
return m_uniforms;
|
||||||
@@ -189,6 +222,34 @@ void CompositionNode::setRefCount(int count)
|
|||||||
emit isDepencyChanged();
|
emit isDepencyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompositionNode::setFragmentCode(const QString &fragmentCode)
|
||||||
|
{
|
||||||
|
if (m_fragmentCode == fragmentCode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_fragmentCode = fragmentCode;
|
||||||
|
emit fragmentCodeChanged();
|
||||||
|
|
||||||
|
requestRebakeIfLiveUpdateMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompositionNode::setVertexCode(const QString &vertexCode)
|
||||||
|
{
|
||||||
|
if (m_vertexCode == vertexCode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_vertexCode = vertexCode;
|
||||||
|
emit vertexCodeChanged();
|
||||||
|
|
||||||
|
requestRebakeIfLiveUpdateMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompositionNode::openShadersCodeEditor()
|
||||||
|
{
|
||||||
|
ensureShadersCodeEditor();
|
||||||
|
m_shadersCodeEditor->showWidget();
|
||||||
|
}
|
||||||
|
|
||||||
QString CompositionNode::name() const
|
QString CompositionNode::name() const
|
||||||
{
|
{
|
||||||
return m_name;
|
return m_name;
|
||||||
|
@@ -5,11 +5,15 @@
|
|||||||
|
|
||||||
#include "effectcomposeruniformsmodel.h"
|
#include "effectcomposeruniformsmodel.h"
|
||||||
|
|
||||||
|
#include <utils/uniqueobjectptr.h>
|
||||||
|
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
namespace EffectComposer {
|
namespace EffectComposer {
|
||||||
|
|
||||||
|
class EffectShadersCodeEditor;
|
||||||
|
|
||||||
class CompositionNode : public QObject
|
class CompositionNode : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -18,6 +22,12 @@ class CompositionNode : public QObject
|
|||||||
Q_PROPERTY(bool nodeEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
|
Q_PROPERTY(bool nodeEnabled READ isEnabled WRITE setIsEnabled NOTIFY isEnabledChanged)
|
||||||
Q_PROPERTY(bool isDependency READ isDependency NOTIFY isDepencyChanged)
|
Q_PROPERTY(bool isDependency READ isDependency NOTIFY isDepencyChanged)
|
||||||
Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged)
|
Q_PROPERTY(QObject *nodeUniformsModel READ uniformsModel NOTIFY uniformsModelChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QString fragmentCode
|
||||||
|
READ fragmentCode
|
||||||
|
WRITE setFragmentCode
|
||||||
|
NOTIFY fragmentCodeChanged)
|
||||||
|
Q_PROPERTY(QString vertexCode READ vertexCode WRITE setVertexCode NOTIFY vertexCodeChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum NodeType {
|
enum NodeType {
|
||||||
@@ -27,6 +37,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
CompositionNode(const QString &effectName, const QString &qenPath, const QJsonObject &json = {});
|
CompositionNode(const QString &effectName, const QString &qenPath, const QJsonObject &json = {});
|
||||||
|
virtual ~CompositionNode();
|
||||||
|
|
||||||
QString fragmentCode() const;
|
QString fragmentCode() const;
|
||||||
QString vertexCode() const;
|
QString vertexCode() const;
|
||||||
@@ -54,14 +65,23 @@ public:
|
|||||||
|
|
||||||
int extraMargin() const { return m_extraMargin; }
|
int extraMargin() const { return m_extraMargin; }
|
||||||
|
|
||||||
|
void setFragmentCode(const QString &fragmentCode);
|
||||||
|
void setVertexCode(const QString &vertexCode);
|
||||||
|
|
||||||
|
void openShadersCodeEditor();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void uniformsModelChanged();
|
void uniformsModelChanged();
|
||||||
void isEnabledChanged();
|
void isEnabledChanged();
|
||||||
void isDepencyChanged();
|
void isDepencyChanged();
|
||||||
void rebakeRequested();
|
void rebakeRequested();
|
||||||
|
void fragmentCodeChanged();
|
||||||
|
void vertexCodeChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json);
|
void parse(const QString &effectName, const QString &qenPath, const QJsonObject &json);
|
||||||
|
void ensureShadersCodeEditor();
|
||||||
|
void requestRebakeIfLiveUpdateMode();
|
||||||
|
|
||||||
QString m_name;
|
QString m_name;
|
||||||
NodeType m_type = CustomNode;
|
NodeType m_type = CustomNode;
|
||||||
@@ -77,6 +97,7 @@ private:
|
|||||||
QList<Uniform *> m_uniforms;
|
QList<Uniform *> m_uniforms;
|
||||||
|
|
||||||
EffectComposerUniformsModel m_unifomrsModel;
|
EffectComposerUniformsModel m_unifomrsModel;
|
||||||
|
Utils::UniqueObjectLatePtr<EffectShadersCodeEditor> m_shadersCodeEditor;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace EffectComposer
|
} // namespace EffectComposer
|
||||||
|
142
src/plugins/effectcomposer/effectcodeeditorwidget.cpp
Normal file
142
src/plugins/effectcomposer/effectcodeeditorwidget.cpp
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "effectcodeeditorwidget.h"
|
||||||
|
|
||||||
|
#include <qmldesigner/textmodifier/indentingtexteditormodifier.h>
|
||||||
|
|
||||||
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
|
#include <coreplugin/coreplugintr.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
|
|
||||||
|
#include <qmljseditor/qmljsautocompleter.h>
|
||||||
|
#include <qmljseditor/qmljscompletionassist.h>
|
||||||
|
#include <qmljseditor/qmljshighlighter.h>
|
||||||
|
#include <qmljseditor/qmljshoverhandler.h>
|
||||||
|
#include <qmljseditor/qmljssemantichighlighter.h>
|
||||||
|
|
||||||
|
#include <qmljstools/qmljsindenter.h>
|
||||||
|
|
||||||
|
#include <utils/mimeconstants.h>
|
||||||
|
#include <utils/transientscroll.h>
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
|
namespace EffectComposer {
|
||||||
|
|
||||||
|
constexpr char EFFECTEDITOR_CONTEXT_ID[] = "EffectEditor.EffectEditorContext";
|
||||||
|
|
||||||
|
EffectCodeEditorWidget::EffectCodeEditorWidget()
|
||||||
|
: m_context(new Core::IContext(this))
|
||||||
|
{
|
||||||
|
Core::Context context(EFFECTEDITOR_CONTEXT_ID, ProjectExplorer::Constants::QMLJS_LANGUAGE_ID);
|
||||||
|
|
||||||
|
m_context->setWidget(this);
|
||||||
|
m_context->setContext(context);
|
||||||
|
Core::ICore::addContextObject(m_context);
|
||||||
|
|
||||||
|
Utils::TransientScrollAreaSupport::support(this);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have to register our own active auto completion shortcut, because the original shortcut will
|
||||||
|
* use the cursor position of the original editor in the editor manager.
|
||||||
|
*/
|
||||||
|
m_completionAction = new QAction(tr("Trigger Completion"), this);
|
||||||
|
|
||||||
|
Core::Command *command = Core::ActionManager::registerAction(
|
||||||
|
m_completionAction, TextEditor::Constants::COMPLETE_THIS, context);
|
||||||
|
command->setDefaultKeySequence(QKeySequence(
|
||||||
|
Core::useMacShortcuts
|
||||||
|
? tr("Meta+Space")
|
||||||
|
: tr("Ctrl+Space")));
|
||||||
|
|
||||||
|
connect(m_completionAction, &QAction::triggered, this, [this] {
|
||||||
|
invokeAssist(TextEditor::Completion);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectCodeEditorWidget::~EffectCodeEditorWidget()
|
||||||
|
{
|
||||||
|
unregisterAutoCompletion();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectCodeEditorWidget::unregisterAutoCompletion()
|
||||||
|
{
|
||||||
|
if (m_completionAction) {
|
||||||
|
Core::ActionManager::unregisterAction(m_completionAction, TextEditor::Constants::COMPLETE_THIS);
|
||||||
|
delete m_completionAction;
|
||||||
|
m_completionAction = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectCodeEditorWidget::setEditorTextWithIndentation(const QString &text)
|
||||||
|
{
|
||||||
|
auto *doc = document();
|
||||||
|
doc->setPlainText(text);
|
||||||
|
|
||||||
|
// We don't need to indent an empty text but is also needed for safer text.length()-1 below
|
||||||
|
if (text.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto modifier = std::make_unique<QmlDesigner::IndentingTextEditModifier>(doc, QTextCursor{doc});
|
||||||
|
modifier->indent(0, text.length()-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectDocument::EffectDocument()
|
||||||
|
: QmlJSEditor::QmlJSEditorDocument(EFFECTEDITOR_CONTEXT_ID)
|
||||||
|
, m_semanticHighlighter(new QmlJSEditor::SemanticHighlighter(this))
|
||||||
|
{}
|
||||||
|
|
||||||
|
EffectDocument::~EffectDocument()
|
||||||
|
{
|
||||||
|
delete m_semanticHighlighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectDocument::applyFontSettings()
|
||||||
|
{
|
||||||
|
TextDocument::applyFontSettings();
|
||||||
|
m_semanticHighlighter->updateFontSettings(fontSettings());
|
||||||
|
if (!isSemanticInfoOutdated() && semanticInfo().isValid())
|
||||||
|
m_semanticHighlighter->rerun(semanticInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectDocument::triggerPendingUpdates()
|
||||||
|
{
|
||||||
|
TextDocument::triggerPendingUpdates(); // Calls applyFontSettings if necessary
|
||||||
|
if (!isSemanticInfoOutdated() && semanticInfo().isValid())
|
||||||
|
m_semanticHighlighter->rerun(semanticInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectCodeEditorFactory::EffectCodeEditorFactory()
|
||||||
|
{
|
||||||
|
setId(EFFECTEDITOR_CONTEXT_ID);
|
||||||
|
setDisplayName(::Core::Tr::tr("Effect Code Editor"));
|
||||||
|
addMimeType(EFFECTEDITOR_CONTEXT_ID);
|
||||||
|
addMimeType(Utils::Constants::QML_MIMETYPE);
|
||||||
|
addMimeType(Utils::Constants::QMLTYPES_MIMETYPE);
|
||||||
|
addMimeType(Utils::Constants::JS_MIMETYPE);
|
||||||
|
|
||||||
|
setDocumentCreator([]() { return new EffectDocument; });
|
||||||
|
setEditorWidgetCreator([]() { return new EffectCodeEditorWidget; });
|
||||||
|
setEditorCreator([]() { return new QmlJSEditor::QmlJSEditor; });
|
||||||
|
setAutoCompleterCreator([]() { return new QmlJSEditor::AutoCompleter; });
|
||||||
|
setCommentDefinition(Utils::CommentDefinition::CppStyle);
|
||||||
|
setParenthesesMatchingEnabled(true);
|
||||||
|
setCodeFoldingSupported(true);
|
||||||
|
|
||||||
|
addHoverHandler(new QmlJSEditor::QmlJSHoverHandler);
|
||||||
|
setCompletionAssistProvider(new QmlJSEditor::QmlJSCompletionAssistProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectCodeEditorFactory::decorateEditor(TextEditor::TextEditorWidget *editor)
|
||||||
|
{
|
||||||
|
editor->textDocument()->resetSyntaxHighlighter(
|
||||||
|
[] { return new QmlJSEditor::QmlJSHighlighter(); });
|
||||||
|
editor->textDocument()->setIndenter(QmlJSEditor::createQmlJsIndenter(
|
||||||
|
editor->textDocument()->document()));
|
||||||
|
editor->setAutoCompleter(new QmlJSEditor::AutoCompleter);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace EffectComposer
|
63
src/plugins/effectcomposer/effectcodeeditorwidget.h
Normal file
63
src/plugins/effectcomposer/effectcodeeditorwidget.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qmljseditor/qmljseditor.h>
|
||||||
|
#include <qmljseditor/qmljseditordocument.h>
|
||||||
|
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QAction)
|
||||||
|
|
||||||
|
namespace QmlJSEditor {
|
||||||
|
class SemanticHighlighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class IContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace EffectComposer {
|
||||||
|
|
||||||
|
class EffectCodeEditorWidget : public QmlJSEditor::QmlJSEditorWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
EffectCodeEditorWidget();
|
||||||
|
~EffectCodeEditorWidget() override;
|
||||||
|
|
||||||
|
void unregisterAutoCompletion();
|
||||||
|
void setEditorTextWithIndentation(const QString &text);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void returnKeyClicked();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Core::IContext *m_context = nullptr;
|
||||||
|
QAction *m_completionAction = nullptr;
|
||||||
|
bool m_isMultiline = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EffectDocument : public QmlJSEditor::QmlJSEditorDocument
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EffectDocument();
|
||||||
|
~EffectDocument();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void applyFontSettings() final;
|
||||||
|
void triggerPendingUpdates() final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QmlJSEditor::SemanticHighlighter *m_semanticHighlighter = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EffectCodeEditorFactory : public TextEditor::TextEditorFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EffectCodeEditorFactory();
|
||||||
|
|
||||||
|
static void decorateEditor(TextEditor::TextEditorWidget *editor);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace EffectComposer
|
@@ -4,6 +4,7 @@
|
|||||||
#include "effectcomposermodel.h"
|
#include "effectcomposermodel.h"
|
||||||
|
|
||||||
#include "compositionnode.h"
|
#include "compositionnode.h"
|
||||||
|
#include "effectshaderscodeeditor.h"
|
||||||
#include "effectutils.h"
|
#include "effectutils.h"
|
||||||
#include "propertyhandler.h"
|
#include "propertyhandler.h"
|
||||||
#include "syntaxhighlighterdata.h"
|
#include "syntaxhighlighterdata.h"
|
||||||
@@ -252,6 +253,8 @@ void EffectComposerModel::setFragmentShader(const QString &newFragmentShader)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_fragmentShader = newFragmentShader;
|
m_fragmentShader = newFragmentShader;
|
||||||
|
|
||||||
|
rebakeIfLiveUpdateMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EffectComposerModel::vertexShader() const
|
QString EffectComposerModel::vertexShader() const
|
||||||
@@ -265,6 +268,8 @@ void EffectComposerModel::setVertexShader(const QString &newVertexShader)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_vertexShader = newVertexShader;
|
m_vertexShader = newVertexShader;
|
||||||
|
|
||||||
|
rebakeIfLiveUpdateMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EffectComposerModel::qmlComponentString() const
|
QString EffectComposerModel::qmlComponentString() const
|
||||||
@@ -990,6 +995,50 @@ void EffectComposerModel::saveComposition(const QString &name)
|
|||||||
setHasUnsavedChanges(false);
|
setHasUnsavedChanges(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EffectComposerModel::openShadersCodeEditor(int idx)
|
||||||
|
{
|
||||||
|
if (m_nodes.size() < idx || idx < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CompositionNode *node = m_nodes.at(idx);
|
||||||
|
node->openShadersCodeEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectComposerModel::openMainShadersCodeEditor()
|
||||||
|
{
|
||||||
|
if (!m_shadersCodeEditor) {
|
||||||
|
m_shadersCodeEditor = Utils::makeUniqueObjectLatePtr<EffectShadersCodeEditor>(
|
||||||
|
currentComposition());
|
||||||
|
m_shadersCodeEditor->setFragmentValue(generateFragmentShader(true));
|
||||||
|
m_shadersCodeEditor->setVertexValue(generateVertexShader(true));
|
||||||
|
|
||||||
|
connect(
|
||||||
|
m_shadersCodeEditor.get(),
|
||||||
|
&EffectShadersCodeEditor::vertexValueChanged,
|
||||||
|
this,
|
||||||
|
[this] {
|
||||||
|
setVertexShader(m_shadersCodeEditor->vertexValue());
|
||||||
|
setHasUnsavedChanges(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(
|
||||||
|
m_shadersCodeEditor.get(),
|
||||||
|
&EffectShadersCodeEditor::fragmentValueChanged,
|
||||||
|
this,
|
||||||
|
[this] {
|
||||||
|
setFragmentShader(m_shadersCodeEditor->fragmentValue());
|
||||||
|
setHasUnsavedChanges(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(
|
||||||
|
m_shadersCodeEditor.get(),
|
||||||
|
&EffectShadersCodeEditor::rebakeRequested,
|
||||||
|
this,
|
||||||
|
&EffectComposerModel::startRebakeTimer);
|
||||||
|
}
|
||||||
|
m_shadersCodeEditor->showWidget();
|
||||||
|
}
|
||||||
|
|
||||||
void EffectComposerModel::openComposition(const QString &path)
|
void EffectComposerModel::openComposition(const QString &path)
|
||||||
{
|
{
|
||||||
clear(true);
|
clear(true);
|
||||||
@@ -1828,7 +1877,6 @@ void EffectComposerModel::bakeShaders()
|
|||||||
|
|
||||||
runQsb(qsbPath, outPaths, false);
|
runQsb(qsbPath, outPaths, false);
|
||||||
runQsb(qsbPrevPath, outPrevPaths, true);
|
runQsb(qsbPrevPath, outPrevPaths, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EffectComposerModel::shadersUpToDate() const
|
bool EffectComposerModel::shadersUpToDate() const
|
||||||
@@ -2003,14 +2051,16 @@ QString EffectComposerModel::getQmlComponentString(bool localFiles)
|
|||||||
|
|
||||||
void EffectComposerModel::connectCompositionNode(CompositionNode *node)
|
void EffectComposerModel::connectCompositionNode(CompositionNode *node)
|
||||||
{
|
{
|
||||||
connect(qobject_cast<EffectComposerUniformsModel *>(node->uniformsModel()),
|
auto setUnsaved = std::bind(&EffectComposerModel::setHasUnsavedChanges, this, true);
|
||||||
&EffectComposerUniformsModel::dataChanged, this, [this] {
|
connect(
|
||||||
setHasUnsavedChanges(true);
|
qobject_cast<EffectComposerUniformsModel *>(node->uniformsModel()),
|
||||||
});
|
&EffectComposerUniformsModel::dataChanged,
|
||||||
connect(node, &CompositionNode::rebakeRequested, this, [this] {
|
this,
|
||||||
// This can come multiple times in a row in response to property changes, so let's buffer it
|
setUnsaved);
|
||||||
m_rebakeTimer.start(200);
|
|
||||||
});
|
connect(node, &CompositionNode::rebakeRequested, this, &EffectComposerModel::startRebakeTimer);
|
||||||
|
connect(node, &CompositionNode::fragmentCodeChanged, this, setUnsaved);
|
||||||
|
connect(node, &CompositionNode::vertexCodeChanged, this, setUnsaved);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectComposerModel::updateExtraMargin()
|
void EffectComposerModel::updateExtraMargin()
|
||||||
@@ -2020,6 +2070,18 @@ void EffectComposerModel::updateExtraMargin()
|
|||||||
m_extraMargin = qMax(node->extraMargin(), m_extraMargin);
|
m_extraMargin = qMax(node->extraMargin(), m_extraMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EffectComposerModel::startRebakeTimer()
|
||||||
|
{
|
||||||
|
// This can come multiple times in a row in response to property changes, so let's buffer it
|
||||||
|
m_rebakeTimer.start(200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectComposerModel::rebakeIfLiveUpdateMode()
|
||||||
|
{
|
||||||
|
if (m_shadersCodeEditor && m_shadersCodeEditor->liveUpdate())
|
||||||
|
startRebakeTimer();
|
||||||
|
}
|
||||||
|
|
||||||
QSet<QByteArray> EffectComposerModel::getExposedProperties(const QByteArray &qmlContent)
|
QSet<QByteArray> EffectComposerModel::getExposedProperties(const QByteArray &qmlContent)
|
||||||
{
|
{
|
||||||
QSet<QByteArray> returnSet;
|
QSet<QByteArray> returnSet;
|
||||||
@@ -2051,6 +2113,8 @@ void EffectComposerModel::setCurrentComposition(const QString &newCurrentComposi
|
|||||||
|
|
||||||
m_currentComposition = newCurrentComposition;
|
m_currentComposition = newCurrentComposition;
|
||||||
emit currentCompositionChanged();
|
emit currentCompositionChanged();
|
||||||
|
|
||||||
|
m_shadersCodeEditor.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::FilePath EffectComposerModel::compositionPath() const
|
Utils::FilePath EffectComposerModel::compositionPath() const
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
#include "shaderfeatures.h"
|
#include "shaderfeatures.h"
|
||||||
|
|
||||||
#include <utils/filepath.h>
|
#include <utils/filepath.h>
|
||||||
|
#include <utils/uniqueobjectptr.h>
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
@@ -26,6 +27,7 @@ class Process;
|
|||||||
namespace EffectComposer {
|
namespace EffectComposer {
|
||||||
|
|
||||||
class CompositionNode;
|
class CompositionNode;
|
||||||
|
class EffectShadersCodeEditor;
|
||||||
class Uniform;
|
class Uniform;
|
||||||
|
|
||||||
struct EffectError {
|
struct EffectError {
|
||||||
@@ -100,6 +102,9 @@ public:
|
|||||||
|
|
||||||
Q_INVOKABLE void saveComposition(const QString &name);
|
Q_INVOKABLE void saveComposition(const QString &name);
|
||||||
|
|
||||||
|
Q_INVOKABLE void openShadersCodeEditor(int idx);
|
||||||
|
Q_INVOKABLE void openMainShadersCodeEditor();
|
||||||
|
|
||||||
void openComposition(const QString &path);
|
void openComposition(const QString &path);
|
||||||
|
|
||||||
QString currentComposition() const;
|
QString currentComposition() const;
|
||||||
@@ -190,6 +195,8 @@ private:
|
|||||||
|
|
||||||
void connectCompositionNode(CompositionNode *node);
|
void connectCompositionNode(CompositionNode *node);
|
||||||
void updateExtraMargin();
|
void updateExtraMargin();
|
||||||
|
void startRebakeTimer();
|
||||||
|
void rebakeIfLiveUpdateMode();
|
||||||
QSet<QByteArray> getExposedProperties(const QByteArray &qmlContent);
|
QSet<QByteArray> getExposedProperties(const QByteArray &qmlContent);
|
||||||
|
|
||||||
QList<CompositionNode *> m_nodes;
|
QList<CompositionNode *> m_nodes;
|
||||||
@@ -230,6 +237,7 @@ private:
|
|||||||
int m_extraMargin = 0;
|
int m_extraMargin = 0;
|
||||||
QString m_effectTypePrefix;
|
QString m_effectTypePrefix;
|
||||||
Utils::FilePath m_compositionPath;
|
Utils::FilePath m_compositionPath;
|
||||||
|
Utils::UniqueObjectLatePtr<EffectShadersCodeEditor> m_shadersCodeEditor;
|
||||||
|
|
||||||
const QRegularExpression m_spaceReg = QRegularExpression("\\s+");
|
const QRegularExpression m_spaceReg = QRegularExpression("\\s+");
|
||||||
};
|
};
|
||||||
|
226
src/plugins/effectcomposer/effectshaderscodeeditor.cpp
Normal file
226
src/plugins/effectcomposer/effectshaderscodeeditor.cpp
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "effectshaderscodeeditor.h"
|
||||||
|
#include "effectcodeeditorwidget.h"
|
||||||
|
|
||||||
|
#include <texteditor/textdocument.h>
|
||||||
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
|
#include <componentcore/designeractionmanager.h>
|
||||||
|
#include <componentcore/designericons.h>
|
||||||
|
#include <componentcore/theme.h>
|
||||||
|
|
||||||
|
#include <qmldesigner/qmldesignerplugin.h>
|
||||||
|
#include <qmljseditor/qmljseditor.h>
|
||||||
|
#include <qmljseditor/qmljseditordocument.h>
|
||||||
|
|
||||||
|
#include <qmldesignerplugin.h>
|
||||||
|
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QTabWidget>
|
||||||
|
#include <QToolBar>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using IconId = QmlDesigner::DesignerIcons::IconId;
|
||||||
|
|
||||||
|
inline constexpr char EFFECTCOMPOSER_LIVE_UPDATE_KEY[] = "EffectComposer/CodeEditor/LiveUpdate";
|
||||||
|
|
||||||
|
QIcon toolbarIcon(IconId iconId)
|
||||||
|
{
|
||||||
|
return QmlDesigner::DesignerActionManager::instance().toolbarIcon(iconId);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace EffectComposer {
|
||||||
|
|
||||||
|
EffectShadersCodeEditor::EffectShadersCodeEditor(const QString &title, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, m_settings(new QSettings(qApp->organizationName(), qApp->applicationName(), this))
|
||||||
|
{
|
||||||
|
setWindowFlag(Qt::Tool, true);
|
||||||
|
setWindowTitle(title);
|
||||||
|
|
||||||
|
m_fragmentEditor = createJSEditor();
|
||||||
|
m_vertexEditor = createJSEditor();
|
||||||
|
|
||||||
|
connect(
|
||||||
|
m_fragmentEditor,
|
||||||
|
&QPlainTextEdit::textChanged,
|
||||||
|
this,
|
||||||
|
&EffectShadersCodeEditor::fragmentValueChanged);
|
||||||
|
connect(
|
||||||
|
m_vertexEditor,
|
||||||
|
&QPlainTextEdit::textChanged,
|
||||||
|
this,
|
||||||
|
&EffectShadersCodeEditor::vertexValueChanged);
|
||||||
|
|
||||||
|
setupUIComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectShadersCodeEditor::~EffectShadersCodeEditor()
|
||||||
|
{
|
||||||
|
m_fragmentEditor->deleteLater();
|
||||||
|
m_vertexEditor->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectShadersCodeEditor::showWidget()
|
||||||
|
{
|
||||||
|
readAndApplyLiveUpdateSettings();
|
||||||
|
show();
|
||||||
|
raise();
|
||||||
|
m_vertexEditor->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectShadersCodeEditor::showWidget(int x, int y)
|
||||||
|
{
|
||||||
|
showWidget();
|
||||||
|
move(QPoint(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EffectShadersCodeEditor::fragmentValue() const
|
||||||
|
{
|
||||||
|
if (!m_fragmentEditor)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return m_fragmentEditor->document()->toPlainText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectShadersCodeEditor::setFragmentValue(const QString &text)
|
||||||
|
{
|
||||||
|
if (m_fragmentEditor)
|
||||||
|
m_fragmentEditor->setEditorTextWithIndentation(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString EffectShadersCodeEditor::vertexValue() const
|
||||||
|
{
|
||||||
|
if (!m_vertexEditor)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return m_vertexEditor->document()->toPlainText();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectShadersCodeEditor::setVertexValue(const QString &text)
|
||||||
|
{
|
||||||
|
if (m_vertexEditor)
|
||||||
|
m_vertexEditor->setEditorTextWithIndentation(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EffectShadersCodeEditor::liveUpdate() const
|
||||||
|
{
|
||||||
|
return m_liveUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectShadersCodeEditor::setLiveUpdate(bool liveUpdate)
|
||||||
|
{
|
||||||
|
if (m_liveUpdate == liveUpdate)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_liveUpdate = liveUpdate;
|
||||||
|
writeLiveUpdateSettings();
|
||||||
|
|
||||||
|
emit liveUpdateChanged(m_liveUpdate);
|
||||||
|
|
||||||
|
if (m_liveUpdate)
|
||||||
|
emit rebakeRequested();
|
||||||
|
}
|
||||||
|
|
||||||
|
EffectCodeEditorWidget *EffectShadersCodeEditor::createJSEditor()
|
||||||
|
{
|
||||||
|
static EffectCodeEditorFactory f;
|
||||||
|
TextEditor::BaseTextEditor *editor = qobject_cast<TextEditor::BaseTextEditor *>(
|
||||||
|
f.createEditor());
|
||||||
|
Q_ASSERT(editor);
|
||||||
|
|
||||||
|
editor->setParent(this);
|
||||||
|
|
||||||
|
EffectCodeEditorWidget *editorWidget = qobject_cast<EffectCodeEditorWidget *>(
|
||||||
|
editor->editorWidget());
|
||||||
|
Q_ASSERT(editorWidget);
|
||||||
|
|
||||||
|
editorWidget->setLineNumbersVisible(false);
|
||||||
|
editorWidget->setMarksVisible(false);
|
||||||
|
editorWidget->setCodeFoldingSupported(false);
|
||||||
|
editorWidget->setTabChangesFocus(true);
|
||||||
|
editorWidget->unregisterAutoCompletion();
|
||||||
|
editorWidget->setParent(this);
|
||||||
|
editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
|
||||||
|
|
||||||
|
return editorWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectShadersCodeEditor::setupUIComponents()
|
||||||
|
{
|
||||||
|
QVBoxLayout *verticalLayout = new QVBoxLayout(this);
|
||||||
|
QTabWidget *tabWidget = new QTabWidget(this);
|
||||||
|
|
||||||
|
tabWidget->addTab(m_fragmentEditor, tr("Fragment Shader"));
|
||||||
|
tabWidget->addTab(m_vertexEditor, tr("Vertex Shader"));
|
||||||
|
|
||||||
|
verticalLayout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
verticalLayout->addWidget(createToolbar());
|
||||||
|
verticalLayout->addWidget(tabWidget);
|
||||||
|
|
||||||
|
this->resize(660, 240);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectShadersCodeEditor::closeEvent(QCloseEvent *event)
|
||||||
|
{
|
||||||
|
QWidget::closeEvent(event);
|
||||||
|
|
||||||
|
if (!liveUpdate())
|
||||||
|
emit rebakeRequested();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectShadersCodeEditor::writeLiveUpdateSettings()
|
||||||
|
{
|
||||||
|
m_settings->setValue(EFFECTCOMPOSER_LIVE_UPDATE_KEY, m_liveUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EffectShadersCodeEditor::readAndApplyLiveUpdateSettings()
|
||||||
|
{
|
||||||
|
bool liveUpdateStatus = m_settings->value(EFFECTCOMPOSER_LIVE_UPDATE_KEY, false).toBool();
|
||||||
|
|
||||||
|
setLiveUpdate(liveUpdateStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
QToolBar *EffectShadersCodeEditor::createToolbar()
|
||||||
|
{
|
||||||
|
using QmlDesigner::Theme;
|
||||||
|
|
||||||
|
QToolBar *toolbar = new QToolBar(this);
|
||||||
|
|
||||||
|
toolbar->setFixedHeight(Theme::toolbarSize());
|
||||||
|
toolbar->setFloatable(false);
|
||||||
|
toolbar->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
toolbar->setStyleSheet(Theme::replaceCssColors(
|
||||||
|
QString::fromUtf8(Utils::FileReader::fetchQrc(":/qmldesigner/stylesheet.css"))));
|
||||||
|
|
||||||
|
QAction *liveUpdateButton
|
||||||
|
= toolbar->addAction(toolbarIcon(IconId::LiveUpdateIcon), tr("Live Update"));
|
||||||
|
liveUpdateButton->setCheckable(true);
|
||||||
|
connect(liveUpdateButton, &QAction::toggled, this, &EffectShadersCodeEditor::setLiveUpdate);
|
||||||
|
|
||||||
|
QAction *applyAction = toolbar->addAction(toolbarIcon(IconId::SyncIcon), tr("Apply"));
|
||||||
|
connect(applyAction, &QAction::triggered, this, &EffectShadersCodeEditor::rebakeRequested);
|
||||||
|
|
||||||
|
auto syncLive = [liveUpdateButton, applyAction](bool liveState) {
|
||||||
|
liveUpdateButton->setChecked(liveState);
|
||||||
|
applyAction->setDisabled(liveState);
|
||||||
|
};
|
||||||
|
|
||||||
|
connect(this, &EffectShadersCodeEditor::liveUpdateChanged, this, syncLive);
|
||||||
|
syncLive(liveUpdate());
|
||||||
|
|
||||||
|
toolbar->addAction(liveUpdateButton);
|
||||||
|
toolbar->addAction(applyAction);
|
||||||
|
|
||||||
|
return toolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace EffectComposer
|
61
src/plugins/effectcomposer/effectshaderscodeeditor.h
Normal file
61
src/plugins/effectcomposer/effectshaderscodeeditor.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QSettings)
|
||||||
|
QT_FORWARD_DECLARE_CLASS(QToolBar)
|
||||||
|
|
||||||
|
namespace EffectComposer {
|
||||||
|
|
||||||
|
class EffectCodeEditorWidget;
|
||||||
|
|
||||||
|
class EffectShadersCodeEditor : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(bool liveUpdate READ liveUpdate WRITE setLiveUpdate NOTIFY liveUpdateChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
EffectShadersCodeEditor(const QString &title = tr("Untitled Editor"), QWidget *parent = nullptr);
|
||||||
|
~EffectShadersCodeEditor() override;
|
||||||
|
|
||||||
|
void showWidget();
|
||||||
|
void showWidget(int x, int y);
|
||||||
|
|
||||||
|
QString fragmentValue() const;
|
||||||
|
void setFragmentValue(const QString &text);
|
||||||
|
|
||||||
|
QString vertexValue() const;
|
||||||
|
void setVertexValue(const QString &text);
|
||||||
|
|
||||||
|
bool liveUpdate() const;
|
||||||
|
void setLiveUpdate(bool liveUpdate);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void liveUpdateChanged(bool);
|
||||||
|
void fragmentValueChanged();
|
||||||
|
void vertexValueChanged();
|
||||||
|
void rebakeRequested();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using QWidget::show;
|
||||||
|
EffectCodeEditorWidget *createJSEditor();
|
||||||
|
void setupUIComponents();
|
||||||
|
|
||||||
|
void closeEvent(QCloseEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writeLiveUpdateSettings();
|
||||||
|
void readAndApplyLiveUpdateSettings();
|
||||||
|
QToolBar *createToolbar();
|
||||||
|
|
||||||
|
QSettings *m_settings = nullptr;
|
||||||
|
QPointer<EffectCodeEditorWidget> m_fragmentEditor;
|
||||||
|
QPointer<EffectCodeEditorWidget> m_vertexEditor;
|
||||||
|
|
||||||
|
bool m_liveUpdate = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace EffectComposer
|
@@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include <qmljstools/qmljsindenter.h>
|
#include <qmljstools/qmljsindenter.h>
|
||||||
|
|
||||||
#include <utils/fancylineedit.h>
|
|
||||||
#include <utils/mimeconstants.h>
|
#include <utils/mimeconstants.h>
|
||||||
#include <utils/transientscroll.h>
|
#include <utils/transientscroll.h>
|
||||||
|
|
||||||
|
@@ -80,6 +80,7 @@ public:
|
|||||||
LightDirectionalIcon,
|
LightDirectionalIcon,
|
||||||
LightPointIcon,
|
LightPointIcon,
|
||||||
LightSpotIcon,
|
LightSpotIcon,
|
||||||
|
LiveUpdateIcon,
|
||||||
LocalOrientIcon,
|
LocalOrientIcon,
|
||||||
MakeComponentIcon,
|
MakeComponentIcon,
|
||||||
MaterialIcon,
|
MaterialIcon,
|
||||||
@@ -107,6 +108,7 @@ public:
|
|||||||
SnappingIcon,
|
SnappingIcon,
|
||||||
SnappingConfIcon,
|
SnappingConfIcon,
|
||||||
SplitViewIcon,
|
SplitViewIcon,
|
||||||
|
SyncIcon,
|
||||||
TimelineIcon,
|
TimelineIcon,
|
||||||
ToggleGroupIcon,
|
ToggleGroupIcon,
|
||||||
VisibilityIcon
|
VisibilityIcon
|
||||||
|
@@ -1759,13 +1759,10 @@ void editInEffectComposer(const SelectionContext &selectionContext)
|
|||||||
|
|
||||||
bool isEffectComposerActivated()
|
bool isEffectComposerActivated()
|
||||||
{
|
{
|
||||||
const ExtensionSystem::PluginSpecs specs = ExtensionSystem::PluginManager::plugins();
|
using namespace ExtensionSystem;
|
||||||
return std::ranges::find_if(specs,
|
return Utils::anyOf(PluginManager::plugins(), [](PluginSpec *spec) {
|
||||||
[](ExtensionSystem::PluginSpec *spec) {
|
return spec->name() == "EffectComposer" && spec->isEffectivelyEnabled();
|
||||||
return spec->name() == "EffectComposer"
|
});
|
||||||
&& spec->isEffectivelyEnabled();
|
|
||||||
})
|
|
||||||
!= specs.end();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void openEffectComposer(const QString &filePath)
|
void openEffectComposer(const QString &filePath)
|
||||||
|
Reference in New Issue
Block a user