diff --git a/.gitignore b/.gitignore
index fc8ef420c..9f353d970 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
*.pyc
CMakeLists.txt.user
-build*/
+build/
.vscode/
tags
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4c8701d02..51920faf3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.11)
cmake_policy(SET CMP0072 NEW) # new in 3.11. The NEW behavior for this policy is to set OpenGL_GL_PREFERENCE to GLVND.
cmake_policy(SET CMP0068 NEW) # new in 3.9. The NEW behavior of this policy is to ignore the RPATH settings for install_name on macOS.
+include(FetchContent)
project(QtNodesLibrary CXX)
@@ -53,6 +54,16 @@ else()
find_package(QT NAMES Qt5 REQUIRED COMPONENTS Widgets)
endif()
+FetchContent_Declare(
+ qt-property-browser
+ GIT_REPOSITORY https://github.com/ramin-raeisi/qt-property-browser.git
+ GIT_TAG epen/6.9.1
+ GIT_SHALLOW TRUE
+ SOURCE_DIR "${CMAKE_SOURCE_DIR}/external/qt-property-browser"
+ BINARY_DIR "${CMAKE_BINARY_DIR}/external/qt-property-browser"
+)
+FetchContent_MakeAvailable(qt-property-browser)
+
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets Gui OpenGL)
message(STATUS "QT_VERSION: ${QT_VERSION}, QT_DIR: ${QT_DIR}")
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 49494da2e..9abaff33e 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,18 +1,21 @@
-add_subdirectory(simple_graph_model)
+#add_subdirectory(simple_graph_model)
-add_subdirectory(vertical_layout)
+#add_subdirectory(vertical_layout)
-add_subdirectory(calculator)
+#add_subdirectory(calculator)
-add_subdirectory(text)
+#add_subdirectory(text)
-add_subdirectory(resizable_images)
+#add_subdirectory(resizable_images)
-add_subdirectory(styles)
+#add_subdirectory(styles)
-add_subdirectory(connection_colors)
+#add_subdirectory(connection_colors)
-add_subdirectory(dynamic_ports)
+#add_subdirectory(dynamic_ports)
-add_subdirectory(lock_nodes_and_connections)
+#add_subdirectory(lock_nodes_and_connections)
+add_subdirectory(epen_graph_editor)
+
+add_subdirectory(demo)
\ No newline at end of file
diff --git a/examples/demo/CMakeLists.txt b/examples/demo/CMakeLists.txt
new file mode 100644
index 000000000..36ad76e89
--- /dev/null
+++ b/examples/demo/CMakeLists.txt
@@ -0,0 +1,14 @@
+file(GLOB_RECURSE CPPS ./*.cpp )
+file(GLOB_RECURSE HPPS ./*.h )
+
+find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
+
+add_executable(demo ${CPPS} ${HPPS})
+
+target_include_directories(demo
+ PUBLIC
+ include
+)
+
+target_link_libraries(demo PRIVATE Qt6PropertyBrowser QtNodes Qt6::Core
+ Qt6::Widgets)
diff --git a/examples/demo/demo.qrc b/examples/demo/demo.qrc
new file mode 100644
index 000000000..c6be0cefb
--- /dev/null
+++ b/examples/demo/demo.qrc
@@ -0,0 +1,8 @@
+
+
+ images/up.png
+ images/down.png
+ images/right.png
+ images/left.png
+
+
diff --git a/examples/demo/images/down.png b/examples/demo/images/down.png
new file mode 100644
index 000000000..29d1d4439
Binary files /dev/null and b/examples/demo/images/down.png differ
diff --git a/examples/demo/images/left.png b/examples/demo/images/left.png
new file mode 100644
index 000000000..e58177f43
Binary files /dev/null and b/examples/demo/images/left.png differ
diff --git a/examples/demo/images/right.png b/examples/demo/images/right.png
new file mode 100644
index 000000000..34b91f09f
Binary files /dev/null and b/examples/demo/images/right.png differ
diff --git a/examples/demo/images/up.png b/examples/demo/images/up.png
new file mode 100644
index 000000000..e43731221
Binary files /dev/null and b/examples/demo/images/up.png differ
diff --git a/examples/demo/main.cpp b/examples/demo/main.cpp
new file mode 100644
index 000000000..cb80a88e3
--- /dev/null
+++ b/examples/demo/main.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Solutions component.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "qtpropertymanager.h"
+#include "qteditorfactory.h"
+#include "qttreepropertybrowser.h"
+#include "qtbuttonpropertybrowser.h"
+#include "qtgroupboxpropertybrowser.h"
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ QWidget *w = new QWidget();
+
+ QtIntPropertyManager *intManager = new QtIntPropertyManager(w);
+ QObject::connect(intManager,&QtIntPropertyManager::valueChanged,[=](QtProperty* property, int val){
+ qDebug()<<"VVV"<addProperty("direction");
+ QStringList enumNames;
+ enumNames << "Up" << "Right" << "Down" << "Left";
+ enumManager->setEnumNames(item8, enumNames);
+ QMap enumIcons;
+ enumIcons[0] = QIcon(":/demo/images/up.png");
+ enumIcons[1] = QIcon(":/demo/images/right.png");
+ enumIcons[2] = QIcon(":/demo/images/down.png");
+ enumIcons[3] = QIcon(":/demo/images/left.png");
+ enumManager->setEnumIcons(item8, enumIcons);
+
+
+ QtProperty *item9 = intManager->addProperty("value");
+ intManager->setRange(item9, -100, 100);
+
+
+
+ QtSpinBoxFactory *spinBoxFactory = new QtSpinBoxFactory(w);
+
+ QtEnumEditorFactory *comboBoxFactory = new QtEnumEditorFactory(w);
+
+ QtAbstractPropertyBrowser *editor1 = new QtTreePropertyBrowser();
+ editor1->setFactoryForManager(intManager, spinBoxFactory);
+ editor1->setFactoryForManager(enumManager, comboBoxFactory);
+
+ editor1->addProperty(item8);
+ editor1->addProperty(item9);
+
+
+
+ QGridLayout *layout = new QGridLayout(w);
+
+
+ layout->addWidget(editor1, 1, 0);
+ w->show();
+
+ int ret = app.exec();
+ delete w;
+ return ret;
+}
diff --git a/examples/epen_graph_editor/CMakeLists.txt b/examples/epen_graph_editor/CMakeLists.txt
new file mode 100644
index 000000000..8789bdf87
--- /dev/null
+++ b/examples/epen_graph_editor/CMakeLists.txt
@@ -0,0 +1,37 @@
+file(GLOB_RECURSE CPPS ./src/*.cpp )
+file(GLOB_RECURSE HPPS ./include/*.hpp )
+
+find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
+
+# Always build with all compiler support
+# The runtime will check availability
+add_definitions(-DALWAYS_INCLUDE_ALL_COMPILERS)
+
+add_executable(epen_graph_editor ${CPPS} ${HPPS})
+
+target_include_directories(epen_graph_editor
+ PUBLIC
+ include
+)
+
+# Base libraries
+set(BASE_LIBS QtPropertyBrowser QtNodes Qt6::Core Qt6::Widgets ${QSCINTILLA_TARGET_NAME})
+
+target_link_libraries(epen_graph_editor PRIVATE ${BASE_LIBS})
+
+# Platform-specific settings
+if(APPLE)
+ # On macOS, we might need to link against system frameworks for dynamic loading
+ find_library(DL_LIBRARY dl)
+ if(DL_LIBRARY)
+ target_link_libraries(epen_graph_editor PRIVATE ${DL_LIBRARY})
+ endif()
+endif()
+
+if(UNIX AND NOT APPLE)
+ # On Linux, we need libdl for dynamic loading
+ target_link_libraries(epen_graph_editor PRIVATE dl)
+endif()
+
+# Always enable runtime detection for all compilers
+add_definitions(-DRUNTIME_GPU_DETECTION)
\ No newline at end of file
diff --git a/examples/epen_graph_editor/include/.DS_Store b/examples/epen_graph_editor/include/.DS_Store
new file mode 100644
index 000000000..18f8c0137
Binary files /dev/null and b/examples/epen_graph_editor/include/.DS_Store differ
diff --git a/examples/epen_graph_editor/include/CodeEditor.hpp b/examples/epen_graph_editor/include/CodeEditor.hpp
new file mode 100644
index 000000000..cde5c882b
--- /dev/null
+++ b/examples/epen_graph_editor/include/CodeEditor.hpp
@@ -0,0 +1,135 @@
+#ifndef CODEEDITOR_HPP
+#define CODEEDITOR_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+class Process;
+class QsciScintilla;
+class GPULanguageLexer;
+class QsciAPIs;
+
+class ReadOnlyLinesEditor : public QsciScintilla
+{
+public:
+ ReadOnlyLinesEditor(QWidget *parent = nullptr)
+ : QsciScintilla(parent)
+ {
+ // Define a marker for background color
+ markerDefine(QsciScintilla::Background, READONLY_MARKER);
+
+ // Set grey background color for read-only lines
+ setMarkerBackgroundColor(QColor(220, 220, 220), READONLY_MARKER); // Light grey
+ }
+
+ void setReadonlyLines(QSet readOnlyLines)
+ {
+ _readOnlyLines = readOnlyLines;
+ markerDeleteAll(READONLY_MARKER);
+ for (int line : _readOnlyLines) {
+ markerAdd(line, READONLY_MARKER);
+ }
+ }
+
+protected:
+ void keyPressEvent(QKeyEvent *event) override
+ {
+ // Get current line
+ int line, col;
+ getCursorPosition(&line, &col);
+
+ // If on read-only line and not navigation key
+ if (_readOnlyLines.contains(line) && event->key() != Qt::Key_Up
+ && event->key() != Qt::Key_Down && event->key() != Qt::Key_Left
+ && event->key() != Qt::Key_Right && event->key() != Qt::Key_Home
+ && event->key() != Qt::Key_End && event->key() != Qt::Key_PageUp
+ && event->key() != Qt::Key_PageDown) {
+ // Block the key press
+ return;
+ }
+
+ // Otherwise, normal handling
+ QsciScintilla::keyPressEvent(event);
+ }
+
+private:
+ QSet _readOnlyLines;
+ static const int READONLY_MARKER = 1;
+};
+
+class CodeEditor : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit CodeEditor(QWidget *parent = nullptr);
+ ~CodeEditor();
+
+ // Code management
+ void setCode(const QString &code);
+ QString getCode() const;
+
+ // Language management
+ void setLanguage(const QString &language);
+ QString getCurrentLanguage() const;
+ QStringList getSupportedLanguages() const { return m_supportedLanguages; }
+
+ // Editor settings
+ void setReadOnly(bool readOnly);
+ bool isReadOnly() const;
+
+ // Theme management
+ void setDarkMode(bool dark);
+ bool isDarkMode() const { return m_isDarkMode; }
+
+ // Editor access (for advanced operations)
+ QsciScintilla *editor() { return m_editor; }
+ const QsciScintilla *editor() const { return m_editor; }
+
+ // Cursor and selection
+ void setCursorPosition(int line, int column);
+ void getCursorPosition(int &line, int &column) const;
+ void ensureLineVisible(int line);
+ void highlightLine(int line, int duration = 2000);
+ void setProcessNode(Process *processNode);
+ // Focus
+ void setFocus();
+
+ void updateCode();
+signals:
+ void codeChanged();
+ void languageChanged(const QString &language);
+
+private slots:
+ void onTextChanged();
+
+private:
+ void setupEditor();
+ void setupCommonEditorFeatures();
+ void updateHighlighter();
+ void updateLexerColors();
+ void forceRefreshLexer();
+ void applyDarkTheme();
+ void applyLightTheme();
+ void setupAutoCompletion();
+ void setReadOnlyLines();
+ // Default code generators
+ QString getDefaultCode(const QString &language) const;
+
+ // UI elements
+ ReadOnlyLinesEditor *m_editor;
+ GPULanguageLexer *m_lexer;
+
+ // State
+ QString m_currentLanguage;
+ QStringList m_supportedLanguages;
+ bool m_isDarkMode;
+ Process *_processNode;
+ bool _codeUpdateLock = false;
+};
+
+#endif // CODEEDITOR_HPP
\ No newline at end of file
diff --git a/examples/epen_graph_editor/include/Color.hpp b/examples/epen_graph_editor/include/Color.hpp
new file mode 100644
index 000000000..03d6aaa54
--- /dev/null
+++ b/examples/epen_graph_editor/include/Color.hpp
@@ -0,0 +1,224 @@
+#ifndef COLOR_HPP
+#define COLOR_HPP
+
+#include
+#include
+#include
+#include
+
+class Color : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(float red READ red WRITE setRed NOTIFY redChanged)
+ Q_PROPERTY(float green READ green WRITE setGreen NOTIFY greenChanged)
+ Q_PROPERTY(float blue READ blue WRITE setBlue NOTIFY blueChanged)
+ Q_PROPERTY(float alpha READ alpha WRITE setAlpha NOTIFY alphaChanged)
+
+public:
+ explicit Color(QObject *parent = nullptr)
+ : QObject(parent)
+ , m_red(0.0f)
+ , m_green(0.0f)
+ , m_blue(0.0f)
+ , m_alpha(1.0f)
+ {}
+
+ Color(float r, float g, float b, float a = 1.0f, QObject *parent = nullptr)
+ : QObject(parent)
+ , m_red(clamp(r))
+ , m_green(clamp(g))
+ , m_blue(clamp(b))
+ , m_alpha(clamp(a))
+ {}
+
+ // Copy constructor
+ Color(const Color &other, QObject *parent = nullptr)
+ : QObject(parent)
+ , m_red(other.m_red)
+ , m_green(other.m_green)
+ , m_blue(other.m_blue)
+ , m_alpha(other.m_alpha)
+ {}
+
+ // Constructor from QColor
+ Color(const QColor &qcolor, QObject *parent = nullptr)
+ : QObject(parent)
+ {
+ setFromQColor(qcolor);
+ }
+
+ // Getters
+ float red() const { return m_red; }
+ float green() const { return m_green; }
+ float blue() const { return m_blue; }
+ float alpha() const { return m_alpha; }
+
+ // Setters with clamping to [0.0, 1.0]
+ void setRed(float red)
+ {
+ float clamped = clamp(red);
+
+ m_red = clamped;
+ emit redChanged(m_red);
+ emit colorChanged();
+ }
+
+ void setGreen(float green)
+ {
+ float clamped = clamp(green);
+
+ m_green = clamped;
+ emit greenChanged(m_green);
+ emit colorChanged();
+ }
+
+ void setBlue(float blue)
+ {
+ float clamped = clamp(blue);
+
+ m_blue = clamped;
+ emit blueChanged(m_blue);
+ emit colorChanged();
+ }
+
+ void setAlpha(float alpha)
+ {
+ float clamped = clamp(alpha);
+ if (qFuzzyCompare(m_alpha, clamped))
+ return;
+ m_alpha = clamped;
+ emit alphaChanged(m_alpha);
+ emit colorChanged();
+ }
+
+ // Convenience method to set all values at once
+ void setColor(float r, float g, float b, float a = 1.0f)
+ {
+ bool changed = false;
+
+ float clampedR = clamp(r);
+ float clampedG = clamp(g);
+ float clampedB = clamp(b);
+ float clampedA = clamp(a);
+
+ if (!qFuzzyCompare(m_red, clampedR)) {
+ m_red = clampedR;
+ emit redChanged(m_red);
+ changed = true;
+ }
+
+ if (!qFuzzyCompare(m_green, clampedG)) {
+ m_green = clampedG;
+ emit greenChanged(m_green);
+ changed = true;
+ }
+
+ if (!qFuzzyCompare(m_blue, clampedB)) {
+ m_blue = clampedB;
+ emit blueChanged(m_blue);
+ changed = true;
+ }
+
+ if (!qFuzzyCompare(m_alpha, clampedA)) {
+ m_alpha = clampedA;
+ emit alphaChanged(m_alpha);
+ changed = true;
+ }
+
+ if (changed) {
+ emit colorChanged();
+ }
+ }
+
+ // Convert to/from QColor
+ QColor toQColor() const { return QColor::fromRgbF(m_red, m_green, m_blue, m_alpha); }
+
+ void setFromQColor(const QColor &qcolor)
+ {
+ setColor(qcolor.redF(), qcolor.greenF(), qcolor.blueF(), qcolor.alphaF());
+ }
+
+ // Convert to/from integer values (0-255)
+ void setFromRgba255(int r, int g, int b, int a = 255)
+ {
+ setColor(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
+ }
+
+ int red255() const { return static_cast(m_red * 255.0f + 0.5f); }
+ int green255() const { return static_cast(m_green * 255.0f + 0.5f); }
+ int blue255() const { return static_cast(m_blue * 255.0f + 0.5f); }
+ int alpha255() const { return static_cast(m_alpha * 255.0f + 0.5f); }
+
+ // Convert to hex string
+ QString toHex() const
+ {
+ return QString("#%1%2%3%4")
+ .arg(red255(), 2, 16, QChar('0'))
+ .arg(green255(), 2, 16, QChar('0'))
+ .arg(blue255(), 2, 16, QChar('0'))
+ .arg(alpha255(), 2, 16, QChar('0'));
+ }
+
+ // Assignment operator
+ Color &operator=(const Color &other)
+ {
+ if (this != &other) {
+ setColor(other.m_red, other.m_green, other.m_blue, other.m_alpha);
+ }
+ return *this;
+ }
+
+ // Comparison operators
+ bool operator==(const Color &other) const
+ {
+ return qFuzzyCompare(m_red, other.m_red) && qFuzzyCompare(m_green, other.m_green)
+ && qFuzzyCompare(m_blue, other.m_blue) && qFuzzyCompare(m_alpha, other.m_alpha);
+ }
+
+ bool operator!=(const Color &other) const { return !(*this == other); }
+
+ // Useful color operations
+ void invert() { setColor(1.0f - m_red, 1.0f - m_green, 1.0f - m_blue, m_alpha); }
+
+ void makeGrayscale()
+ {
+ // Using luminance formula
+ float gray = 0.299f * m_red + 0.587f * m_green + 0.114f * m_blue;
+ setColor(gray, gray, gray, m_alpha);
+ }
+
+ float luminance() const { return 0.299f * m_red + 0.587f * m_green + 0.114f * m_blue; }
+
+ // Blend with another color
+ void blend(const Color &other, float factor = 0.5f)
+ {
+ factor = clamp(factor);
+ float invFactor = 1.0f - factor;
+
+ setColor(m_red * invFactor + other.m_red * factor,
+ m_green * invFactor + other.m_green * factor,
+ m_blue * invFactor + other.m_blue * factor,
+ m_alpha * invFactor + other.m_alpha * factor);
+ }
+
+signals:
+ void redChanged(float red);
+ void greenChanged(float green);
+ void blueChanged(float blue);
+ void alphaChanged(float alpha);
+ void colorChanged(); // Emitted when any component changes
+
+private:
+ // Helper function to clamp values to [0.0, 1.0]
+ static float clamp(float value) { return std::max(0.0f, std::min(1.0f, value)); }
+
+ float m_red;
+ float m_green;
+ float m_blue;
+ float m_alpha;
+};
+
+// Register the type for use with Qt's meta-type system
+Q_DECLARE_METATYPE(Color *)
+
+#endif // COLOR_HPP
\ No newline at end of file
diff --git a/examples/epen_graph_editor/include/DataFlowModel.hpp b/examples/epen_graph_editor/include/DataFlowModel.hpp
new file mode 100644
index 000000000..bfb27ed9d
--- /dev/null
+++ b/examples/epen_graph_editor/include/DataFlowModel.hpp
@@ -0,0 +1,87 @@
+#pragma once
+
+#include "PortAddRemoveWidget.hpp"
+#include "data_models/OperationDataModel.hpp"
+#include "data_models/Process.hpp"
+#include "panels/FloatingProperties.hpp"
+#include "ProcessPort.hpp"
+#include
+#include
+
+using QtNodes::ConnectionId;
+using QtNodes::DataFlowGraphModel;
+using QtNodes::InvalidNodeId;
+using QtNodes::NodeDelegateModelRegistry;
+using QtNodes::NodeFlag;
+using QtNodes::NodeFlags;
+using QtNodes::NodeId;
+using QtNodes::NodeRole;
+using QtNodes::PortIndex;
+using QtNodes::PortType;
+
+class DataFlowModel : public DataFlowGraphModel
+{
+ Q_OBJECT
+
+public:
+ DataFlowModel(std::shared_ptr);
+
+ NodeId addNode(QString const nodeType) override;
+ void addConnection(ConnectionId const connectionId) override;
+ bool deleteConnection(ConnectionId const connectionId) override;
+
+ bool detachPossible(ConnectionId const) const override { return true; }
+
+ bool deleteNode(NodeId const nodeId) override;
+
+ QVariant nodeData(NodeId nodeId, NodeRole role) const override;
+
+ bool setNodeData(NodeId nodeId, NodeRole role, QVariant value) override;
+
+ void addProcessNodePort(NodeId nodeId, PortType portType, PortIndex portIndex, ProcessPort *port);
+
+ void removeProcessNodePort(NodeId nodeId, PortType portType, PortIndex portIndex);
+ void setFloatingProperties(QPointer);
+
+ float getlastProcessLeft();
+
+ QPair getProcessNodeRange(NodeId nodeId, QPointF currentPos);
+
+ void setSelectedNode(OperationDataModel *, NodeId);
+ void deselectNode();
+
+ void setSelectedPort(NodeId nodeId, bool isRightPort, int portIndex);
+
+ void notifyPortInsertion(NodeId nodeId);
+
+ PortAddRemoveWidget *widget(NodeId nodeId) const;
+
+ void addProcessPort(NodeId nodeId, bool isRight, bool isImage);
+
+ QString getInputImagePortName();
+ QString getInputBufferPortName();
+ QString getOutputImagePortName();
+ QString getOutputBufferPortName();
+signals:
+ void nodePortSelected(bool isRightPort, Process *node, int portIndex);
+
+private:
+ QString generateNewNodeName(QString typeNamePrefix);
+ std::unordered_map> nodesMap;
+ struct NodePortCount
+ {
+ unsigned int in = 0;
+ unsigned int out = 0;
+ };
+ mutable std::unordered_map _nodePortCounts;
+ mutable std::unordered_map _nodeWidgets;
+ mutable std::unordered_map _processNodeSize;
+ mutable std::unordered_map _nodeNames;
+
+ QPointer _propertyPanel;
+
+ int _inputImagePortCount = 0;
+ int _inputBufferPortCount = 0;
+ int _outputImagePortCount = 0;
+ int _outputBufferPortCount = 0;
+};
diff --git a/examples/epen_graph_editor/include/DraggableButton.hpp b/examples/epen_graph_editor/include/DraggableButton.hpp
new file mode 100644
index 000000000..2a7f8b9fd
--- /dev/null
+++ b/examples/epen_graph_editor/include/DraggableButton.hpp
@@ -0,0 +1,14 @@
+#ifndef DRAGGABLE_BUTTON
+#define DRAGGABLE_BUTTON
+#include
+
+class DraggableButton : public QPushButton
+{
+public:
+ explicit DraggableButton(QString actionName, QWidget *parent = nullptr);
+ void mousePressEvent(QMouseEvent *event) override;
+
+private:
+ QString _actionName;
+};
+#endif
\ No newline at end of file
diff --git a/examples/epen_graph_editor/include/ExpandableCategoryWidget.hpp b/examples/epen_graph_editor/include/ExpandableCategoryWidget.hpp
new file mode 100644
index 000000000..b9a6add46
--- /dev/null
+++ b/examples/epen_graph_editor/include/ExpandableCategoryWidget.hpp
@@ -0,0 +1,79 @@
+#ifndef EXPANDABLE_CATEGORY_WIDGET_HPP
+#define EXPANDABLE_CATEGORY_WIDGET_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class ExpandableCategoryWidget : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(int contentHeight READ contentHeight WRITE setContentHeight)
+
+public:
+ explicit ExpandableCategoryWidget(const QString &title,
+ int indentLevel = 0,
+ QWidget *parent = nullptr);
+
+ void setContentWidget(QWidget *widget);
+ QWidget *contentWidget() const;
+
+ void addWidget(QWidget *widget);
+ QVBoxLayout *contentLayout() const;
+
+ void setExpanded(bool expanded);
+ bool isExpanded() const;
+
+ void setIndentLevel(int level);
+ int indentLevel() const;
+
+ // Styling
+ void setHeaderStyle(const QString &style);
+ void setArrowColor(const QColor &color);
+
+signals:
+ void expandedChanged(bool expanded);
+ void contentHeightChanged();
+
+public slots:
+ void toggle();
+ void expand();
+ void collapse();
+ void updateContentHeight();
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+ bool eventFilter(QObject *watched, QEvent *event) override;
+
+ // UI elements - made protected for height calculation
+ QWidget *m_headerWidget;
+ QWidget *m_contentArea;
+
+private:
+ void setupUI();
+ void updateArrow();
+ int contentHeight() const;
+ void setContentHeight(int height);
+
+ // UI elements
+ QPushButton *m_toggleButton;
+ QLabel *m_titleLabel;
+ QLabel *m_chevronLabel;
+ QVBoxLayout *m_contentLayout;
+
+ // Animation
+ QPropertyAnimation *m_animation;
+
+ // State
+ bool m_isExpanded;
+ int m_indentLevel;
+ QString m_title;
+ QColor m_arrowColor;
+};
+
+#endif // EXPANDABLE_CATEGORY_WIDGET_HPP
\ No newline at end of file
diff --git a/examples/epen_graph_editor/include/GpuLanguageLexer.hpp b/examples/epen_graph_editor/include/GpuLanguageLexer.hpp
new file mode 100644
index 000000000..df707b4c2
--- /dev/null
+++ b/examples/epen_graph_editor/include/GpuLanguageLexer.hpp
@@ -0,0 +1,60 @@
+#ifndef GPULANGUAGELEXER_HPP
+#define GPULANGUAGELEXER_HPP
+
+#include
+#include
+#include
+
+class GPULanguageLexer : public QsciLexerCustom
+{
+ Q_OBJECT
+
+public:
+ enum {
+ Default = 0,
+ Comment = 1,
+ CommentLine = 2,
+ Number = 3,
+ Keyword = 4,
+ LanguageKeyword = 5,
+ String = 6,
+ Operator = 7,
+ Identifier = 8,
+ Preprocessor = 9
+ };
+
+ explicit GPULanguageLexer(QObject *parent = nullptr);
+
+ const char *language() const override;
+ QString description(int style) const override;
+ QColor defaultColor(int style) const override;
+ QColor defaultPaper(int style) const override;
+ QFont defaultFont(int style) const override;
+ void styleText(int start, int end) override;
+
+ void setLanguageMode(const QString &language);
+ void setDarkMode(bool dark);
+
+ // Get collected identifiers
+ QSet getIdentifiers() const { return m_identifiers; }
+ void clearIdentifiers() { m_identifiers.clear(); }
+
+ // Public helper methods for syntax checking
+ bool isKeyword(const QString &word) const;
+ bool isLanguageKeyword(const QString &word) const;
+ bool isBuiltinFunction(const QString &word) const;
+ bool isBuiltinType(const QString &word) const;
+
+private:
+ bool isNumber(const QString &text) const;
+
+ QString m_language;
+ bool m_isDarkMode;
+ QSet m_keywords;
+ QSet m_languageKeywords;
+ QSet m_builtinFunctions;
+ QSet m_types;
+ QSet m_identifiers; // Track identifiers found during lexing
+};
+
+#endif // GPULANGUAGELEXER_HPP
\ No newline at end of file
diff --git a/examples/epen_graph_editor/include/GraphEditorMainWindow.hpp b/examples/epen_graph_editor/include/GraphEditorMainWindow.hpp
new file mode 100644
index 000000000..3f3fafb3c
--- /dev/null
+++ b/examples/epen_graph_editor/include/GraphEditorMainWindow.hpp
@@ -0,0 +1,85 @@
+#ifndef GRAPH_EDITOR_WINDOW
+#define GRAPH_EDITOR_WINDOW
+
+#include "DataFlowModel.hpp"
+#include "QtNodes/GraphicsView"
+#include "data_models/OperationDataModel.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+
+using QtNodes::BasicGraphicsScene;
+using QtNodes::DataFlowGraphicsScene;
+using QtNodes::DataFlowGraphModel;
+using QtNodes::GraphicsView;
+using QtNodes::InvalidNodeId;
+using QtNodes::NodeDelegateModel;
+using QtNodes::NodeId;
+
+class FloatingToolbar;
+class FloatingProperties;
+class FloatingCodeEditor;
+class SimpleGraphModel;
+
+class GraphEditorWindow : public GraphicsView
+{
+ Q_OBJECT
+public:
+ GraphEditorWindow(DataFlowGraphicsScene *scene, DataFlowModel *model);
+ ~GraphEditorWindow();
+
+public slots:
+ // Slot for creating a node at a specific position
+ void createNodeAtPosition(const QPointF &scenePos, const QString nodeType);
+
+ // Slot for creating a node at cursor position
+ void createNodeAtCursor();
+
+ // Slots for node selection and property changes
+ void onNodeSelected(NodeId nodeId);
+ void onNodeDeselected();
+
+ // Panel layout management
+ void updateDockedPanelLayouts();
+
+private slots:
+ // Code editor compile handler
+ void onCompileRequested(const QString &code, const QString &language);
+
+protected:
+ // Override to maintain toolbar position
+ void moveEvent(QMoveEvent *event) override;
+ void resizeEvent(QResizeEvent *event) override;
+ void showEvent(QShowEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void dragEnterEvent(QDragEnterEvent *event) override;
+ void dropEvent(QDropEvent *event) override;
+ void dragMoveEvent(QDragMoveEvent *event) override;
+
+ void drawBackground(QPainter *painter, const QRectF &r) override;
+ void dragLeaveEvent(QDragLeaveEvent *event) override;
+
+private:
+ void createFloatingToolbar();
+ void createFloatingProperties();
+ void createFloatingCodeEditor();
+ void setupNodeCreation();
+
+ QPointer m_toolbar;
+ QPointer m_properties;
+ QPointer m_codeEditor;
+
+ bool m_toolbarCreated;
+ bool m_propertiesCreated;
+ bool m_codeEditorCreated;
+
+ DataFlowModel *_model;
+ NodeId m_currentSelectedNodeId;
+
+ float _allowedDropAreaLeft = -1;
+};
+
+#endif // GRAPH_EDITOR_WINDOW
\ No newline at end of file
diff --git a/examples/epen_graph_editor/include/GraphEditorScene.hpp b/examples/epen_graph_editor/include/GraphEditorScene.hpp
new file mode 100644
index 000000000..daf1de162
--- /dev/null
+++ b/examples/epen_graph_editor/include/GraphEditorScene.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include
+#include
+
+using QtNodes::DataFlowGraphicsScene;
+using QtNodes::DataFlowGraphModel;
+class GraphEditorScene : public DataFlowGraphicsScene
+{
+public:
+ GraphEditorScene(DataFlowGraphModel &model)
+ : DataFlowGraphicsScene(model)
+ {}
+
+ QMenu *createSceneMenu(QPointF const scenePos) override { return nullptr; }
+};
\ No newline at end of file
diff --git a/examples/epen_graph_editor/include/ObjectPropertyBrowserNew.hpp b/examples/epen_graph_editor/include/ObjectPropertyBrowserNew.hpp
new file mode 100644
index 000000000..c530055d6
--- /dev/null
+++ b/examples/epen_graph_editor/include/ObjectPropertyBrowserNew.hpp
@@ -0,0 +1,35 @@
+#ifndef OBJECTPROPERTYBROWSER_HPP
+#define OBJECTPROPERTYBROWSER_HPP
+
+#include "data_models/OperationDataModel.hpp"
+#include "qttreepropertybrowser_p.h"
+#include "qtvariantproperty_p.h"
+#include
+#include
+#include
+
+class ObjectPropertyBrowser : public QtTreePropertyBrowser
+{
+ Q_OBJECT
+
+public:
+ explicit ObjectPropertyBrowser(QWidget *parent = nullptr);
+ void setActiveObject(QObject *obj); // Changed from OperationDataModel* to QObject*
+
+private slots:
+ void valueChanged(QtProperty *property, const QVariant &value);
+ void objectUpdated();
+
+private:
+ QtVariantProperty* createPropertyForMetaProperty(QObject *parentObj, const QMetaProperty &mp, const QString &parentPath = "");
+ void addQObjectProperties(QObject *obj, QtVariantProperty *parentProperty, const QString &parentPath);
+ void updatePropertyValues(QObject *rootObj);
+ QString setupName(QString name);
+
+ QtVariantPropertyManager *variantManager;
+ QMap propertyMap; // Changed from const char* to QString
+ QObject *currentlyConnectedObject = nullptr;
+ QList activeConnections; // Track all active connections
+};
+
+#endif // OBJECTPROPERTYBROWSER_HPP
\ No newline at end of file
diff --git a/examples/epen_graph_editor/include/PortAddRemoveWidget.hpp b/examples/epen_graph_editor/include/PortAddRemoveWidget.hpp
new file mode 100644
index 000000000..df06f166e
--- /dev/null
+++ b/examples/epen_graph_editor/include/PortAddRemoveWidget.hpp
@@ -0,0 +1,64 @@
+#pragma once
+#include "ProcessPort.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include