From 276af0ab756481525f48c06b9a0b7f298f7e937a Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Sun, 7 Nov 2021 12:09:57 +0000 Subject: [PATCH 01/36] Take recent catch2 to fix build issue --- external/CMakeLists.txt | 2 +- external/Catch2/CMakeLists.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 8e12fb119..0159b3ad2 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,5 +1,5 @@ if(BUILD_TESTING) - find_package(Catch2 2.3.0 QUIET) + find_package(Catch2 2.13.7 QUIET) if(NOT Catch2_FOUND) add_subdirectory(Catch2) diff --git a/external/Catch2/CMakeLists.txt b/external/Catch2/CMakeLists.txt index 17689e01b..4cc7e8c87 100644 --- a/external/Catch2/CMakeLists.txt +++ b/external/Catch2/CMakeLists.txt @@ -1,7 +1,7 @@ if(NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/single_include/catch2/catch.hpp") - file(DOWNLOAD https://raw.githubusercontent.com/catchorg/Catch2/v2.4.1/single_include/catch2/catch.hpp + file(DOWNLOAD https://raw.githubusercontent.com/catchorg/Catch2/v2.13.7/single_include/catch2/catch.hpp "${CMAKE_CURRENT_BINARY_DIR}/single_include/catch2/catch.hpp" - EXPECTED_HASH SHA256=a4b90030cb813f0452bb00e97c92ca6c2ecf9386a2f000b6effb8e265a53959e + EXPECTED_HASH SHA256=ea379c4a3cb5799027b1eb451163dff065a3d641aaba23bf4e24ee6b536bd9bc ) endif() From 8b33a8b5125e60fd84d0bf2c8ea1c9cc83d6a566 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Sat, 12 Feb 2022 03:54:13 +0100 Subject: [PATCH 02/36] Update README --- README.md | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c772b5e61..383860c8c 100644 --- a/README.md +++ b/README.md @@ -59,17 +59,31 @@ make -j && make install 4. `Build -> Build All` 5. Click the button `Run` -### Roadmap - -1. Extend set of examples -2. GUI: fix scrolling for scene view window scrolling -3. Implement grouping nodes -4. Split graph and GUI parts -5. Build data propagation on top of the graph code +### >>> Version 3 Roadmap <<< + +1. Headless mode. [done] + You can create, populate, modify the derivative of AbsractGraphModel + without adding it to the actual Flow Scene. + The library is now designed to be general-purpose graph + visualization and modification tool, without specialization on only + data propagation. +2. Build data propagation on top of the graph code [done]. + - Fix old unit-tests. [in progress]. + - Fix save/restore. [in progress]. + - Fix CI scriptst on travis and appveyor. [not started]. +3. Backward compatibility with Qt5 [not started/help needed]. +3. Write improved documentation based on Sphynx platform [done]. +4. Extend set of examples [partially done]. +5. Undo Redo [not started]. +6. Python wrappring using PySide [HELP NEEDED]. +7. Implement grouping nodes [not started]. +8. GUI: fix scrolling for scene view window scrolling [need to check Qt6] + +Any suggestions are welcome. ### Citing - Dmitry Pinaev et al, Qt5 Node Editor, (2017), GitHub repository, https://github.com/paceholder/nodeeditor + Dmitry Pinaev et al, Qt Node Editor, (2017), GitHub repository, https://github.com/paceholder/nodeeditor BibTeX From b9c203259c30db2c3f3c6b2c095a42b9dc071336 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Sat, 12 Feb 2022 18:34:28 +0000 Subject: [PATCH 03/36] Make deleteConnection take const. (#244) --- include/nodes/internal/FlowScene.hpp | 2 +- src/FlowScene.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 6d59725cd..2a0aec10a 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -54,7 +54,7 @@ class NODE_EDITOR_PUBLIC FlowScene std::shared_ptr restoreConnection(QJsonObject const &connectionJson); - void deleteConnection(Connection& connection); + void deleteConnection(Connection const& connection); Node&createNode(std::unique_ptr && dataModel); diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index f332f1404..f94b3e627 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -182,7 +182,7 @@ restoreConnection(QJsonObject const &connectionJson) void FlowScene:: -deleteConnection(Connection& connection) +deleteConnection(Connection const& connection) { auto it = _connections.find(connection.id()); if (it != _connections.end()) From da8078e1d547e04fe53b473f038558102db48cc1 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Sat, 12 Feb 2022 19:40:41 +0100 Subject: [PATCH 04/36] Bump required CMake version to 3.8 --- CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e7056d379..e13305300 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,4 @@ -cmake_minimum_required(VERSION 3.2) -# version 3.4 is required as other do not work with C++14 and clang +cmake_minimum_required(VERSION 3.8) project(NodeEditor CXX) From 55f68e6a1500f3b3ca2a9e9f02c895b9f681416b Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Sat, 12 Feb 2022 19:48:16 +0100 Subject: [PATCH 05/36] Enforce C++14 for the whole library in CMake --- CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e13305300..1430b4c28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,10 +116,7 @@ target_compile_options(nodes $<$:-Wall -Wextra> ) -target_compile_features(nodes - PUBLIC - cxx_generic_lambdas # Require C++14 -) +target_compile_features(nodes PUBLIC cxx_std_14) set_target_properties(nodes PROPERTIES From 71709985ed7a02c159c92bd8110c07027183e51d Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Sat, 12 Feb 2022 19:48:43 +0100 Subject: [PATCH 06/36] Use C++11 random lib instead of qrand() --- src/ConnectionStyle.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ConnectionStyle.cpp b/src/ConnectionStyle.cpp index 245116449..ed9c78b35 100644 --- a/src/ConnectionStyle.cpp +++ b/src/ConnectionStyle.cpp @@ -1,6 +1,6 @@ #include "ConnectionStyle.hpp" -#include +#include "StyleCollection.hpp" #include #include @@ -10,7 +10,7 @@ #include -#include "StyleCollection.hpp" +#include using QtNodes::ConnectionStyle; @@ -166,8 +166,10 @@ normalColor(QString typeId) const std::size_t const hue_range = 0xFF; - qsrand(hash); - std::size_t hue = qrand() % hue_range; + std::mt19937 gen(hash); + std::uniform_int_distribution<> distrib(0, hue_range); + + std::size_t hue = distrib(gen); std::size_t sat = 120 + hash % 129; From 25809938615814c9e5736cc90d1c70163d00d9ed Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Sat, 12 Feb 2022 20:47:06 +0100 Subject: [PATCH 07/36] Support Qt 5.15 and Qt 6.xx --- CMakeLists.txt | 38 ++++++++++++++++++++++++++------------ src/NodeGeometry.cpp | 14 ++++++++++---- test/CMakeLists.txt | 9 +++++++-- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1430b4c28..d9684d676 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(NodeEditor CXX) set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) set(CMAKE_DISABLE_SOURCE_CHANGES ON) +set(OpenGL_GL_PREFERENCE LEGACY) get_directory_property(_has_parent PARENT_DIRECTORY) if(_has_parent) @@ -37,14 +38,28 @@ endif() add_subdirectory(external) + # Find the QtWidgets library -find_package(Qt5 5.3 COMPONENTS - Core - Widgets - Gui - OpenGL) +find_package(Qt6 + REQUIRED COMPONENTS + Core + Widgets + Gui + OpenGL +) + +if (NOT Qt6_FOUND) + find_package(Qt5 5.15 + REQUIRED COMPONENTS + Core + Widgets + Gui + OpenGL + ) +endif() + -qt5_add_resources(RESOURCES ./resources/resources.qrc) +qt_add_resources(RESOURCES ./resources/resources.qrc) # Unfortunately, as we have a split include/src, AUTOMOC doesn't work. # We'll have to manually specify some files @@ -93,15 +108,14 @@ target_include_directories(nodes target_link_libraries(nodes PUBLIC - Qt5::Core - Qt5::Widgets - Qt5::Gui - Qt5::OpenGL + Qt::Core + Qt::Widgets + Qt::Gui + Qt::OpenGL ) target_compile_definitions(nodes PUBLIC - ${Qt5Widgets_DEFINITIONS} NODE_EDITOR_SHARED PRIVATE NODE_EDITOR_EXPORTS @@ -131,7 +145,7 @@ set_target_properties(nodes file(GLOB_RECURSE HEADERS_TO_MOC include/nodes/internal/*.hpp) -qt5_wrap_cpp(nodes_moc +qt_wrap_cpp(nodes_moc ${HEADERS_TO_MOC} TARGET nodes OPTIONS --no-notes # Don't display a note for the headers which don't produce a moc_*.cpp diff --git a/src/NodeGeometry.cpp b/src/NodeGeometry.cpp index 9a215bf17..91d90dc66 100644 --- a/src/NodeGeometry.cpp +++ b/src/NodeGeometry.cpp @@ -1,16 +1,17 @@ #include "NodeGeometry.hpp" -#include -#include - #include "PortType.hpp" #include "NodeState.hpp" #include "NodeDataModel.hpp" #include "Node.hpp" #include "NodeGraphicsObject.hpp" - #include "StyleCollection.hpp" +#include + +#include +#include + using QtNodes::NodeGeometry; using QtNodes::NodeDataModel; using QtNodes::PortIndex; @@ -356,8 +357,13 @@ portWidth(PortType portType) const name = _dataModel->dataType(portType, i).name; } +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + width = std::max(unsigned(_fontMetrics.horizontalAdvance(name)), + width); +#else width = std::max(unsigned(_fontMetrics.width(name)), width); +#endif } return width; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7030ab106..61c091806 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,10 @@ find_package(Catch2 2.3.0 REQUIRED) -find_package(Qt5 COMPONENTS Test) + +if (Qt6_FOUND) + find_package(Qt6 COMPONENTS Test) +else() + find_package(Qt5 COMPONENTS Test) +endif() add_executable(test_nodes test_main.cpp @@ -20,7 +25,7 @@ target_link_libraries(test_nodes PRIVATE NodeEditor::nodes Catch2::Catch2 - Qt5::Test + Qt::Test ) add_test( From 63a4c33ec656897185c29229c11ad8ea40d087cb Mon Sep 17 00:00:00 2001 From: Mario Emmenlauer Date: Sat, 29 Jan 2022 10:37:21 +0100 Subject: [PATCH 08/36] src/ConnectionPainter.cpp: Renamed a shadowed variable --- src/ConnectionPainter.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ConnectionPainter.cpp b/src/ConnectionPainter.cpp index 11d31af0a..c6da391be 100644 --- a/src/ConnectionPainter.cpp +++ b/src/ConnectionPainter.cpp @@ -226,10 +226,10 @@ drawNormalLine(QPainter * painter, { painter->setBrush(Qt::NoBrush); - QColor c = normalColorOut; + QColor cOut = normalColorOut; if (selected) - c = c.darker(200); - p.setColor(c); + cOut = cOut.darker(200); + p.setColor(cOut); painter->setPen(p); unsigned int const segments = 60; @@ -241,11 +241,11 @@ drawNormalLine(QPainter * painter, if (i == segments / 2) { - QColor c = normalColorIn; + QColor cIn = normalColorIn; if (selected) - c = c.darker(200); + cIn = cIn.darker(200); - p.setColor(c); + p.setColor(cIn); painter->setPen(p); } painter->drawLine(cubic.pointAtPercent(ratioPrev), From eb99ea91105a5457a2545291e6b9c38f7f5ef3e9 Mon Sep 17 00:00:00 2001 From: Mario Emmenlauer Date: Sat, 29 Jan 2022 11:21:44 +0100 Subject: [PATCH 09/36] CMakeLists.txt: Allow users to override c++ standard --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d9684d676..2da88ffdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,9 +24,6 @@ option(NE_FORCE_TEST_COLOR "Force colorized unit test output" OFF) enable_testing() if(NE_DEVELOPER_DEFAULTS) - set(CMAKE_CXX_STANDARD 14) - set(CMAKE_CXX_EXTENSIONS OFF) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") @@ -130,7 +127,10 @@ target_compile_options(nodes $<$:-Wall -Wextra> ) -target_compile_features(nodes PUBLIC cxx_std_14) +if(NE_DEVELOPER_DEFAULTS) + target_compile_features(nodes PUBLIC cxx_std_14) + set_target_properties(nodes PROPERTIES CXX_EXTENSIONS OFF) +endif() set_target_properties(nodes PROPERTIES From 511859052f1b60d4549710ff5934f546a61b8caf Mon Sep 17 00:00:00 2001 From: Mario Emmenlauer Date: Sat, 29 Jan 2022 11:22:07 +0100 Subject: [PATCH 10/36] CMakeLists.txt: Added support for Clang-Cl on MSVC --- CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2da88ffdf..c085ed978 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,8 +124,14 @@ target_compile_options(nodes PRIVATE $<$:/W4 /wd4127 /EHsc> $<$:-Wall -Wextra> - $<$:-Wall -Wextra> ) +if(NOT "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC") + # Clang-Cl on MSVC identifies as "Clang" but behaves more like MSVC: + target_compile_options(nodes + PRIVATE + $<$:-Wall -Wextra> + ) +endif() if(NE_DEVELOPER_DEFAULTS) target_compile_features(nodes PUBLIC cxx_std_14) From 8b53dac8e7c8ec12154941712c3fbef8563fde7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taiguara=20Tupinamb=C3=A1s?= Date: Sat, 12 Feb 2022 17:04:15 -0300 Subject: [PATCH 11/36] adding nodeclick signal (#255) --- include/nodes/internal/FlowScene.hpp | 2 ++ src/NodeGraphicsObject.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/nodes/internal/FlowScene.hpp b/include/nodes/internal/FlowScene.hpp index 2a0aec10a..9303c442a 100644 --- a/include/nodes/internal/FlowScene.hpp +++ b/include/nodes/internal/FlowScene.hpp @@ -124,6 +124,8 @@ class NODE_EDITOR_PUBLIC FlowScene void nodeDoubleClicked(Node& n); + void nodeClicked(Node& n); + void connectionHovered(Connection& c, QPoint screenPos); void nodeHovered(Node& n, QPoint screenPos); diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index efa473a20..ed16c38dd 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -337,6 +337,8 @@ mouseReleaseEvent(QGraphicsSceneMouseEvent* event) // position connections precisely after fast node move moveConnections(); + + _scene.nodeClicked(node()); } From d301d9c85e416643aa27667b0b2e7d5083c2ad1e Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Sat, 12 Feb 2022 21:14:10 +0100 Subject: [PATCH 12/36] Make Qt components not required for flexibility between Qt6 and Qt5 --- .appveyor.yml | 2 +- CMakeLists.txt | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 7b9b05a7b..34b15ec91 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -10,7 +10,7 @@ environment: - GENERATOR : "Visual Studio 15 2017" ARCHITECTURE : "" APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - QTDIR: C:\Qt\5.13.2\msvc2017 + QTDIR: C:\Qt\5.15.2\msvc2017 PLATFORM: Win32 - GENERATOR : "MinGW Makefiles" ARCHITECTURE : "" diff --git a/CMakeLists.txt b/CMakeLists.txt index c085ed978..68f61cd91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ add_subdirectory(external) # Find the QtWidgets library find_package(Qt6 - REQUIRED COMPONENTS + COMPONENTS Core Widgets Gui @@ -47,7 +47,7 @@ find_package(Qt6 if (NOT Qt6_FOUND) find_package(Qt5 5.15 - REQUIRED COMPONENTS + COMPONENTS Core Widgets Gui @@ -55,6 +55,10 @@ if (NOT Qt6_FOUND) ) endif() +if (NOT (Qt6_FOUND OR Qt5_FOUND)) + message(FATAL_ERRROR "Qt libraries were not found.") +endif() + qt_add_resources(RESOURCES ./resources/resources.qrc) From 40aae1495ef08ae324ee24b0e1d0d166b6a47354 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Sat, 12 Feb 2022 21:22:10 +0100 Subject: [PATCH 13/36] Fix MSVC warnings --- src/ConnectionStyle.cpp | 9 ++++----- src/FlowScene.cpp | 2 +- src/NodePainter.cpp | 8 +++++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/ConnectionStyle.cpp b/src/ConnectionStyle.cpp index ed9c78b35..91dc4d705 100644 --- a/src/ConnectionStyle.cpp +++ b/src/ConnectionStyle.cpp @@ -166,12 +166,11 @@ normalColor(QString typeId) const std::size_t const hue_range = 0xFF; - std::mt19937 gen(hash); - std::uniform_int_distribution<> distrib(0, hue_range); + std::mt19937 gen(static_cast(hash)); + std::uniform_int_distribution distrib(0, hue_range); - std::size_t hue = distrib(gen); - - std::size_t sat = 120 + hash % 129; + int hue = distrib(gen); + int sat = 120 + hash % 129; return QColor::fromHsl(hue, sat, diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index f94b3e627..5862d1e5c 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -338,7 +338,7 @@ iterateOverNodeDataDependentOrder(std::function const & vi { for (size_t i = 0; i < model.nPorts(PortType::In); ++i) { - auto connections = node.nodeState().connections(PortType::In, i); + auto connections = node.nodeState().connections(PortType::In, static_cast(i)); for (auto& conn : connections) { diff --git a/src/NodePainter.cpp b/src/NodePainter.cpp index c3bdf7ae0..c3b7507c1 100644 --- a/src/NodePainter.cpp +++ b/src/NodePainter.cpp @@ -126,7 +126,8 @@ drawConnectionPoints(QPainter* painter, { QPointF p = geom.portScenePosition(i, portType); - auto const & dataType = model->dataType(portType, i); + auto const & dataType = + model->dataType(portType, static_cast(i)); bool canConnect = (state.getEntries(portType)[i].empty() || (portType == PortType::Out && @@ -208,7 +209,8 @@ drawFilledConnectionPoints(QPainter * painter, if (!state.getEntries(portType)[i].empty()) { - auto const & dataType = model->dataType(portType, i); + auto const & dataType = + model->dataType(portType, static_cast(i)); if (connectionStyle.useDataDefinedColors()) { @@ -302,7 +304,7 @@ drawEntryLabels(QPainter * painter, } else { - s = model->dataType(portType, i).name; + s = model->dataType(portType, static_cast(i)).name; } auto rect = metrics.boundingRect(s); From d3edfa60fdcf84214d33df4911d618792147fd21 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Sat, 12 Feb 2022 21:33:09 +0100 Subject: [PATCH 14/36] Fix appveyor, remove MinGW because of Qt6 --- .appveyor.yml | 27 +++++++++++---------------- CMakeLists.txt | 2 +- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 34b15ec91..119a143d1 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,30 +2,25 @@ clone_depth: 5 environment: matrix: - - GENERATOR : "Visual Studio 16 2019" + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + GENERATOR : "Visual Studio 16 2019" + ARCHITECTURE : "-A Win32" + QTDIR: C:\Qt\5.15\msvc2019 + QT_MAJOR: 5 + PLATFORM: Win32 + - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + GENERATOR : "Visual Studio 16 2019" ARCHITECTURE : "-A x64" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - QTDIR: C:\Qt\5.15.2\msvc2019_64 + QTDIR: C:\Qt\6.2.2\msvc2019_64 + QT_MAJOR: 6 PLATFORM: x64 - - GENERATOR : "Visual Studio 15 2017" - ARCHITECTURE : "" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - QTDIR: C:\Qt\5.15.2\msvc2017 - PLATFORM: Win32 - - GENERATOR : "MinGW Makefiles" - ARCHITECTURE : "" - QTDIR: C:\Qt\5.11.3\mingw53_32 - PLATFORM: x86 - CMAKE_CXX_FLAGS_INIT: -DCATCH_CONFIG_NO_CPP11_TO_STRING configuration: - Release install: - set PATH=%QTDIR%\bin;%PATH% - - set Qt5_DIR=%QTDIR%\lib\cmake\Qt5 - - set PATH=C:\MinGW\bin;C:\MinGW\msys\1.0;%PATH% - - set PATH=C:\Qt\Tools\mingw530_32;%PATH% + - set Qt%QT_MAJOR%_DIR=%QTDIR%\lib\cmake\Qt%QT_MAJOR% - set PATH=%PATH:C:\Program Files\Git\usr\bin=% # trick to remove sh.exe before_build: diff --git a/CMakeLists.txt b/CMakeLists.txt index 68f61cd91..9afbc9d9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,7 @@ find_package(Qt6 ) if (NOT Qt6_FOUND) - find_package(Qt5 5.15 + find_package(Qt5 5.13 COMPONENTS Core Widgets From ed618c04969fdac1c1c4adad924bb165f2be1338 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Sat, 19 Feb 2022 19:15:54 +0100 Subject: [PATCH 15/36] Reflect CI changes in README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 383860c8c..f71e4927b 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,13 @@ connections updating the whole graph. ### Platforms * OSX (Apple Clang - LLVM 3.6), Linux (x64, gcc-7.0, clang-7): [![Build Status](https://travis-ci.org/paceholder/nodeeditor.svg?branch=master)](https://travis-ci.org/paceholder/nodeeditor) -* Windows (Win32, x64, msvc2017, MinGW 5.3): [![Build status](https://ci.appveyor.com/api/projects/status/wxp47wv3uyyiujjw/branch/master?svg=true)](https://ci.appveyor.com/project/paceholder/nodeeditor/branch/master) +* Windows (msvc2019/Qt5/Win32, msvc2019/Qt6/x64): [![Build status](https://ci.appveyor.com/api/projects/status/wxp47wv3uyyiujjw/branch/master?svg=true)](https://ci.appveyor.com/project/paceholder/nodeeditor/branch/master) ### Dependencies -* Qt >5.2 -* CMake 3.2 +* Qt >5.15 +* CMake 3.8 * Catch2 ### Current state From 411d4075e75a431cf6efef9253937ccb0404d5b1 Mon Sep 17 00:00:00 2001 From: vhutter <16247361+vhutter@users.noreply.github.com> Date: Wed, 30 Mar 2022 11:44:06 +0200 Subject: [PATCH 16/36] Adjust the behavior of Connection's destructor (#308) Modify Connection's destructor so that it doesn't fire propagateEmptyData without an output node connected to it previously. --- src/Connection.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Connection.cpp b/src/Connection.cpp index 2375b848b..587995b02 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -71,8 +71,6 @@ Connection:: connectionMadeIncomplete(*this); } - propagateEmptyData(); - if (_inNode) { _inNode->nodeGraphicsObject().update(); @@ -80,6 +78,7 @@ Connection:: if (_outNode) { + propagateEmptyData(); _outNode->nodeGraphicsObject().update(); } } From 6ef6166435ef96f3ad0cd83e29b8ff0cb0a976b8 Mon Sep 17 00:00:00 2001 From: SaulBerrenson <64542974+SaulBerrenson@users.noreply.github.com> Date: Wed, 30 Mar 2022 13:07:59 +0300 Subject: [PATCH 17/36] missing binary to install (#306) --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9afbc9d9c..bce3756ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,6 +191,7 @@ install(TARGETS nodes EXPORT NodeEditorTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) From 3ec420f1fb3debf3c15e6ea20ce646eb424d88a2 Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 6 Mar 2022 08:17:26 +0100 Subject: [PATCH 18/36] Support for multiple connections for a single input port - added NodeDataModel::portInConnectionPolicy (similar to NodeDataModel::portOutConnectionPolicy) - added a new parameter to NodeDataModel::setInData representing the respective connection - modified NodeConnectionInteraction::disconnect to only erase one connection from the port rather than clear all connections --- examples/example2/TextDisplayDataModel.hpp | 31 ++++++++++++++-- examples/example2/TextSourceDataModel.cpp | 6 ++-- examples/example2/TextSourceDataModel.hpp | 1 + include/nodes/internal/DataModelRegistry.hpp | 12 ++----- include/nodes/internal/Node.hpp | 3 +- include/nodes/internal/NodeData.hpp | 12 +++++++ include/nodes/internal/NodeDataModel.hpp | 18 ++++++++++ include/nodes/internal/TypeConverter.hpp | 9 +++++ src/Connection.cpp | 2 +- src/Node.cpp | 5 +-- src/NodeConnectionInteraction.cpp | 38 +++++++++++++++++--- 11 files changed, 114 insertions(+), 23 deletions(-) diff --git a/examples/example2/TextDisplayDataModel.hpp b/examples/example2/TextDisplayDataModel.hpp index e8bff0e16..8b997f3ef 100644 --- a/examples/example2/TextDisplayDataModel.hpp +++ b/examples/example2/TextDisplayDataModel.hpp @@ -8,6 +8,7 @@ #include #include +#include using QtNodes::PortType; using QtNodes::PortIndex; @@ -54,20 +55,45 @@ class TextDisplayDataModel : public NodeDataModel std::shared_ptr outData(PortIndex port) override; + ConnectionPolicy + portInConnectionPolicy(PortIndex) const override + { + return ConnectionPolicy::Many; + } + void setInData(std::shared_ptr data, int) override + { + } + + void + setInData(std::shared_ptr data, int, const QUuid& connectionId) override { auto textData = std::dynamic_pointer_cast(data); + auto it = std::find_if(inputTexts.begin(), inputTexts.end(), + [this, &connectionId](const auto& e) + { + return e.first == connectionId; + }); if (textData) { - _label->setText(textData->text()); + if (it == inputTexts.end()) + inputTexts.emplace_back(connectionId, textData->text()); + else + it->second = textData->text(); } else { - _label->clear(); + inputTexts.erase(it); } + QStringList textList; + for (auto&& entry : inputTexts) textList.push_back(entry.second); + + _label->setText(QStringLiteral("%1 inputs: %2") + .arg(textList.size()) + .arg(textList.join(QStringLiteral(", ")))); _label->adjustSize(); } @@ -77,4 +103,5 @@ class TextDisplayDataModel : public NodeDataModel private: QLabel * _label; + std::vector> inputTexts; }; diff --git a/examples/example2/TextSourceDataModel.cpp b/examples/example2/TextSourceDataModel.cpp index de9e2e67c..2486ee780 100644 --- a/examples/example2/TextSourceDataModel.cpp +++ b/examples/example2/TextSourceDataModel.cpp @@ -2,7 +2,8 @@ TextSourceDataModel:: TextSourceDataModel() - : _lineEdit(new QLineEdit("Default Text")) + : _lineEdit(new QLineEdit("Default Text")), + _textData(std::make_shared()) { connect(_lineEdit, &QLineEdit::textEdited, this, &TextSourceDataModel::onTextEdited); @@ -54,5 +55,6 @@ std::shared_ptr TextSourceDataModel:: outData(PortIndex) { - return std::make_shared(_lineEdit->text()); + *_textData = TextData(_lineEdit->text()); + return _textData; } diff --git a/examples/example2/TextSourceDataModel.hpp b/examples/example2/TextSourceDataModel.hpp index 65f85da7d..8968801cd 100644 --- a/examples/example2/TextSourceDataModel.hpp +++ b/examples/example2/TextSourceDataModel.hpp @@ -69,4 +69,5 @@ private Q_SLOTS: private: QLineEdit * _lineEdit; + std::shared_ptr _textData; }; diff --git a/include/nodes/internal/DataModelRegistry.hpp b/include/nodes/internal/DataModelRegistry.hpp index eaabe2659..7638ef1cd 100644 --- a/include/nodes/internal/DataModelRegistry.hpp +++ b/include/nodes/internal/DataModelRegistry.hpp @@ -19,15 +19,6 @@ namespace QtNodes { -inline -bool -operator<(QtNodes::NodeDataType const & d1, - QtNodes::NodeDataType const & d2) -{ - return d1.id < d2.id; -} - - /// Class uses map for storing models (name, model) class NODE_EDITOR_PUBLIC DataModelRegistry { @@ -40,7 +31,8 @@ class NODE_EDITOR_PUBLIC DataModelRegistry using RegisteredModelsCategoryMap = std::unordered_map; using CategoriesSet = std::set; - using RegisteredTypeConvertersMap = std::map; + using RegisteredTypeConvertersMap = std::unordered_map< + TypeConverterId, TypeConverter, TypeConverterIdHash>; DataModelRegistry() = default; ~DataModelRegistry() = default; diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index cab7d828b..cd8ef30ab 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -90,7 +90,8 @@ public Q_SLOTS: // data propagation /// Propagates incoming data to the underlying model. void propagateData(std::shared_ptr nodeData, - PortIndex inPortIndex) const; + PortIndex inPortIndex, + const QUuid& connectionId) const; /// Fetches data from model's OUT #index port /// and propagates it to the connection diff --git a/include/nodes/internal/NodeData.hpp b/include/nodes/internal/NodeData.hpp index 5f8c7502e..44e6d4f3c 100644 --- a/include/nodes/internal/NodeData.hpp +++ b/include/nodes/internal/NodeData.hpp @@ -11,6 +11,18 @@ struct NodeDataType { QString id; QString name; + + friend bool operator<(QtNodes::NodeDataType const& d1, + QtNodes::NodeDataType const& d2) + { + return d1.id < d2.id; + } + + friend bool operator==(const QtNodes::NodeDataType& d1, + const QtNodes::NodeDataType& d2) noexcept + { + return d1.id == d2.id; + } }; /// Class represents data transferred between nodes. diff --git a/include/nodes/internal/NodeDataModel.hpp b/include/nodes/internal/NodeDataModel.hpp index b65b9ecd7..da614d26d 100644 --- a/include/nodes/internal/NodeDataModel.hpp +++ b/include/nodes/internal/NodeDataModel.hpp @@ -87,6 +87,13 @@ class NODE_EDITOR_PUBLIC NodeDataModel return ConnectionPolicy::Many; } + virtual + ConnectionPolicy + portInConnectionPolicy(PortIndex) const + { + return ConnectionPolicy::One; + } + NodeStyle const& nodeStyle() const; @@ -101,6 +108,17 @@ class NODE_EDITOR_PUBLIC NodeDataModel setInData(std::shared_ptr nodeData, PortIndex port) = 0; + // Use this if portInConnectionPolicy returns ConnectionPolicy::Many + virtual + void + setInData(std::shared_ptr nodeData, + PortIndex port, + const QUuid& connectionId) + { + Q_UNUSED(connectionId); + setInData(nodeData, port); + } + virtual std::shared_ptr outData(PortIndex port) = 0; diff --git a/include/nodes/internal/TypeConverter.hpp b/include/nodes/internal/TypeConverter.hpp index 671ce4041..58a62e86d 100644 --- a/include/nodes/internal/TypeConverter.hpp +++ b/include/nodes/internal/TypeConverter.hpp @@ -18,4 +18,13 @@ using TypeConverter = using TypeConverterId = std::pair; +struct TypeConverterIdHash +{ + std::size_t operator()(const QtNodes::TypeConverterId& converter) const noexcept + { + return qHash(converter.first.id) + ^ qHash(converter.second.id); + } +}; + } diff --git a/src/Connection.cpp b/src/Connection.cpp index 587995b02..c1dbd6cdf 100644 --- a/src/Connection.cpp +++ b/src/Connection.cpp @@ -435,7 +435,7 @@ propagateData(std::shared_ptr nodeData) const nodeData = _converter(nodeData); } - _inNode->propagateData(nodeData, _inPortIndex); + _inNode->propagateData(nodeData, _inPortIndex, id()); } } diff --git a/src/Node.cpp b/src/Node.cpp index 8ad34c343..5b6e904fa 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -185,9 +185,10 @@ nodeDataModel() const void Node:: propagateData(std::shared_ptr nodeData, - PortIndex inPortIndex) const + PortIndex inPortIndex, + const QUuid& connectionId) const { - _nodeDataModel->setInData(std::move(nodeData), inPortIndex); + _nodeDataModel->setInData(std::move(nodeData), inPortIndex, connectionId); //Recalculate the nodes visuals. A data change can result in the node taking more space than before, so this forces a recalculate+repaint on the affected node _nodeGraphicsObject->setGeometryChanged(); diff --git a/src/NodeConnectionInteraction.cpp b/src/NodeConnectionInteraction.cpp index fb701be27..82689f4dc 100644 --- a/src/NodeConnectionInteraction.cpp +++ b/src/NodeConnectionInteraction.cpp @@ -149,7 +149,7 @@ disconnect(PortType portToDisconnect) const NodeState &state = _node->nodeState(); // clear pointer to Connection in the NodeState - state.getEntries(portToDisconnect)[portIndex].clear(); + state.getEntries(portToDisconnect)[portIndex].erase(_connection->id()); // 4) Propagate invalid data to IN node _connection->propagateEmptyData(); @@ -230,9 +230,37 @@ nodePortIsEmpty(PortType portType, PortIndex portIndex) const NodeState const & nodeState = _node->nodeState(); auto const & entries = nodeState.getEntries(portType); + auto const & connections = entries[portIndex]; + if (connections.empty()) return true; + + // Check if the connection already exists connected to the respective + // input and output ports + auto sourcePortType = oppositePort(portType); + auto it = std::find_if(connections.begin(), connections.end(), + [this, sourcePortType](const auto& connection) + { + const auto* const currentConn = connection.second; + + assert(_connection->getNode(sourcePortType)); + assert(currentConn->getNode(sourcePortType)); + return _connection->getNode(sourcePortType) == currentConn->getNode(sourcePortType) && + _connection->getPortIndex(sourcePortType) == currentConn->getPortIndex(sourcePortType); + }); + if (it != connections.end()) + return false; - if (entries[portIndex].empty()) return true; - - const auto outPolicy = _node->nodeDataModel()->portOutConnectionPolicy(portIndex); - return ( portType == PortType::Out && outPolicy == NodeDataModel::ConnectionPolicy::Many); + switch (portType) + { + case PortType::In: + { + const auto policy = _node->nodeDataModel()->portInConnectionPolicy(portIndex); + return policy == NodeDataModel::ConnectionPolicy::Many; + } + case PortType::Out: + { + const auto policy = _node->nodeDataModel()->portOutConnectionPolicy(portIndex); + return policy == NodeDataModel::ConnectionPolicy::Many; + } + default: return false; + } } From bcc176e1044c633e80e246b8b13614d32456094c Mon Sep 17 00:00:00 2001 From: vince Date: Sun, 6 Mar 2022 09:20:02 +0100 Subject: [PATCH 19/36] Fix MSVC warnings for implicit cast --- src/NodePainter.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/NodePainter.cpp b/src/NodePainter.cpp index c3b7507c1..aa96d15a6 100644 --- a/src/NodePainter.cpp +++ b/src/NodePainter.cpp @@ -205,7 +205,9 @@ drawFilledConnectionPoints(QPainter * painter, for (size_t i = 0; i < n; ++i) { - QPointF p = geom.portScenePosition(i, portType); + QPointF p = geom.portScenePosition( + static_cast(i), + static_cast(portType)); if (!state.getEntries(portType)[i].empty()) { @@ -289,7 +291,7 @@ drawEntryLabels(QPainter * painter, for (size_t i = 0; i < n; ++i) { - QPointF p = geom.portScenePosition(i, portType); + QPointF p = geom.portScenePosition(static_cast(i), portType); if (entries[i].empty()) painter->setPen(nodeStyle.FontColorFaded); @@ -298,9 +300,9 @@ drawEntryLabels(QPainter * painter, QString s; - if (model->portCaptionVisible(portType, i)) + if (model->portCaptionVisible(portType, static_cast(i))) { - s = model->portCaption(portType, i); + s = model->portCaption(portType, static_cast(i)); } else { From fed7ced93246bbadc29a7a690189b4fde934a3f8 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Sun, 10 Apr 2022 14:45:58 +0200 Subject: [PATCH 20/36] Try to fix graphics artefacts --- src/FlowView.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/FlowView.cpp b/src/FlowView.cpp index c15af16f7..d7f768361 100644 --- a/src/FlowView.cpp +++ b/src/FlowView.cpp @@ -40,14 +40,13 @@ FlowView(QWidget *parent) setBackgroundBrush(flowViewStyle.BackgroundColor); - //setViewportUpdateMode(QGraphicsView::FullViewportUpdate); - //setViewportUpdateMode(QGraphicsView::MinimalViewportUpdate); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setTransformationAnchor(QGraphicsView::AnchorUnderMouse); setCacheMode(QGraphicsView::CacheBackground); + setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); //setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); } From 21ede690bc38c482576cbcc36c2c55b0050acd47 Mon Sep 17 00:00:00 2001 From: cpi47 <76054120+cpi47@users.noreply.github.com> Date: Thu, 21 Apr 2022 11:42:19 +0200 Subject: [PATCH 21/36] fix typo in README (#313) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f71e4927b..5da23a74e 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ make -j && make install ### >>> Version 3 Roadmap <<< 1. Headless mode. [done] - You can create, populate, modify the derivative of AbsractGraphModel + You can create, populate, modify the derivative of AbstractGraphModel without adding it to the actual Flow Scene. The library is now designed to be general-purpose graph visualization and modification tool, without specialization on only From e5c7bc6904e2dfb956e63e784d210d09570ff684 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Tue, 12 Jul 2022 16:12:14 +0200 Subject: [PATCH 22/36] Bump Qt version to 6.3 for appveyor CI --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 119a143d1..9ae10c1aa 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -11,7 +11,7 @@ environment: - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 GENERATOR : "Visual Studio 16 2019" ARCHITECTURE : "-A x64" - QTDIR: C:\Qt\6.2.2\msvc2019_64 + QTDIR: C:\Qt\6.3\msvc2019_64 QT_MAJOR: 6 PLATFORM: x64 From 6168de9d965f3a7f0a24fce02bb8e4518e1b4949 Mon Sep 17 00:00:00 2001 From: Yadunund Date: Thu, 30 Jun 2022 00:20:09 +0800 Subject: [PATCH 23/36] Support Qt5 build Signed-off-by: Yadunund --- CMakeLists.txt | 35 ++++++++++++++++++++++++----------- test/CMakeLists.txt | 4 +++- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bce3756ce..feea6ad9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ add_subdirectory(external) # Find the QtWidgets library -find_package(Qt6 +find_package(Qt6 QUIET COMPONENTS Core Widgets @@ -46,7 +46,7 @@ find_package(Qt6 ) if (NOT Qt6_FOUND) - find_package(Qt5 5.13 + find_package(Qt5 QUIET COMPONENTS Core Widgets @@ -59,8 +59,13 @@ if (NOT (Qt6_FOUND OR Qt5_FOUND)) message(FATAL_ERRROR "Qt libraries were not found.") endif() - -qt_add_resources(RESOURCES ./resources/resources.qrc) +if (Qt6_FOUND) + qt_add_resources(RESOURCES ./resources/resources.qrc) + set(Qt Qt) +else() + qt5_add_resources(RESOURCES ./resources/resources.qrc) + set(Qt Qt5) +endif() # Unfortunately, as we have a split include/src, AUTOMOC doesn't work. # We'll have to manually specify some files @@ -109,10 +114,10 @@ target_include_directories(nodes target_link_libraries(nodes PUBLIC - Qt::Core - Qt::Widgets - Qt::Gui - Qt::OpenGL + ${Qt}::Core + ${Qt}::Widgets + ${Qt}::Gui + ${Qt}::OpenGL ) target_compile_definitions(nodes @@ -155,11 +160,19 @@ set_target_properties(nodes file(GLOB_RECURSE HEADERS_TO_MOC include/nodes/internal/*.hpp) -qt_wrap_cpp(nodes_moc - ${HEADERS_TO_MOC} +if (Qt6_FOUND) + qt_wrap_cpp(nodes_moc + ${HEADERS_TO_MOC} + TARGET nodes + OPTIONS --no-notes # Don't display a note for the headers which don't produce a moc_*.cpp + ) +else() + qt5_wrap_cpp(nodes_moc + ${HEADERS_TO_MOC} TARGET nodes OPTIONS --no-notes # Don't display a note for the headers which don't produce a moc_*.cpp -) + ) +endif() target_sources(nodes PRIVATE ${nodes_moc}) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 61c091806..dffa83bfb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,8 +2,10 @@ find_package(Catch2 2.3.0 REQUIRED) if (Qt6_FOUND) find_package(Qt6 COMPONENTS Test) + set(Qt Qt) else() find_package(Qt5 COMPONENTS Test) + set(Qt Qt5) endif() add_executable(test_nodes @@ -25,7 +27,7 @@ target_link_libraries(test_nodes PRIVATE NodeEditor::nodes Catch2::Catch2 - Qt::Test + ${Qt}::Test ) add_test( From 9b07d919ce1510150ea6b7b25d119685802baaf1 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Tue, 12 Jul 2022 16:54:19 +0200 Subject: [PATCH 24/36] Don't std::move shared_ptr --- src/FlowScene.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FlowScene.cpp b/src/FlowScene.cpp index 5862d1e5c..dd4306df1 100644 --- a/src/FlowScene.cpp +++ b/src/FlowScene.cpp @@ -42,7 +42,7 @@ FlowScene:: FlowScene(std::shared_ptr registry, QObject * parent) : QGraphicsScene(parent) - , _registry(std::move(registry)) + , _registry(registry) { setItemIndexMethod(QGraphicsScene::NoIndex); From 5fb7d8a368bd8c1634d5d1c9209cd5626630f71c Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Tue, 12 Jul 2022 22:46:32 +0200 Subject: [PATCH 25/36] Add comment regarding embedded widget leak --- .../calculator/NumberDisplayDataModel.cpp | 17 ++++++++- .../calculator/NumberDisplayDataModel.hpp | 5 ++- examples/calculator/NumberSourceDataModel.cpp | 38 +++++++++++++------ examples/calculator/NumberSourceDataModel.hpp | 5 ++- include/nodes/internal/NodeDataModel.hpp | 10 +++++ include/nodes/internal/NodeGraphicsObject.hpp | 1 + 6 files changed, 59 insertions(+), 17 deletions(-) diff --git a/examples/calculator/NumberDisplayDataModel.cpp b/examples/calculator/NumberDisplayDataModel.cpp index 96237a410..77cc7655e 100644 --- a/examples/calculator/NumberDisplayDataModel.cpp +++ b/examples/calculator/NumberDisplayDataModel.cpp @@ -2,11 +2,12 @@ #include "DecimalData.hpp" +#include + NumberDisplayDataModel:: NumberDisplayDataModel() - : _label(new QLabel()) + : _label{nullptr} { - _label->setMargin(3); } @@ -72,6 +73,18 @@ setInData(std::shared_ptr data, int) _label->adjustSize(); } +QWidget* +NumberDisplayDataModel:: +embeddedWidget() +{ + if (!_label) + { + _label = new QLabel(); + _label->setMargin(3); + } + + return _label; +} NodeValidationState NumberDisplayDataModel:: diff --git a/examples/calculator/NumberDisplayDataModel.hpp b/examples/calculator/NumberDisplayDataModel.hpp index 72795df69..7b2934de1 100644 --- a/examples/calculator/NumberDisplayDataModel.hpp +++ b/examples/calculator/NumberDisplayDataModel.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include @@ -14,6 +13,8 @@ using QtNodes::NodeDataType; using QtNodes::NodeDataModel; using QtNodes::NodeValidationState; +class QLabel; + /// The model dictates the number of inputs and outputs for the Node. /// In this example it has no logic. class NumberDisplayDataModel : public NodeDataModel @@ -56,7 +57,7 @@ class NumberDisplayDataModel : public NodeDataModel setInData(std::shared_ptr data, int) override; QWidget * - embeddedWidget() override { return _label; } + embeddedWidget() override; NodeValidationState validationState() const override; diff --git a/examples/calculator/NumberSourceDataModel.cpp b/examples/calculator/NumberSourceDataModel.cpp index ce534fd82..8702b39e4 100644 --- a/examples/calculator/NumberSourceDataModel.cpp +++ b/examples/calculator/NumberSourceDataModel.cpp @@ -1,22 +1,15 @@ #include "NumberSourceDataModel.hpp" +#include "DecimalData.hpp" + #include #include - -#include "DecimalData.hpp" +#include NumberSourceDataModel:: NumberSourceDataModel() - : _lineEdit(new QLineEdit()) + : _lineEdit{nullptr} { - _lineEdit->setValidator(new QDoubleValidator()); - - _lineEdit->setMaximumSize(_lineEdit->sizeHint()); - - connect(_lineEdit, &QLineEdit::textChanged, - this, &NumberSourceDataModel::onTextEdited); - - _lineEdit->setText("0.0"); } @@ -114,3 +107,26 @@ outData(PortIndex) { return _number; } + + +QWidget * +NumberSourceDataModel:: +embeddedWidget() +{ + return _lineEdit; + + if (!_lineEdit) + { + _lineEdit = new QLineEdit(); + + _lineEdit->setValidator(new QDoubleValidator()); + _lineEdit->setMaximumSize(_lineEdit->sizeHint()); + + connect(_lineEdit, &QLineEdit::textChanged, + this, &NumberSourceDataModel::onTextEdited); + + _lineEdit->setText("0.0"); + } + + return _lineEdit; +} diff --git a/examples/calculator/NumberSourceDataModel.hpp b/examples/calculator/NumberSourceDataModel.hpp index 64d506c48..f3b3aabb7 100644 --- a/examples/calculator/NumberSourceDataModel.hpp +++ b/examples/calculator/NumberSourceDataModel.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include @@ -16,6 +15,8 @@ using QtNodes::NodeDataType; using QtNodes::NodeDataModel; using QtNodes::NodeValidationState; +class QLineEdit; + /// The model dictates the number of inputs and outputs for the Node. /// In this example it has no logic. class NumberSourceDataModel @@ -67,7 +68,7 @@ class NumberSourceDataModel { } QWidget * - embeddedWidget() override { return _lineEdit; } + embeddedWidget() override; private Q_SLOTS: diff --git a/include/nodes/internal/NodeDataModel.hpp b/include/nodes/internal/NodeDataModel.hpp index da614d26d..1535860a5 100644 --- a/include/nodes/internal/NodeDataModel.hpp +++ b/include/nodes/internal/NodeDataModel.hpp @@ -123,6 +123,16 @@ class NODE_EDITOR_PUBLIC NodeDataModel std::shared_ptr outData(PortIndex port) = 0; + /** + * It is recommented to preform a lazy initialization for the + * embedded widget and create it inside this function, not in the + * constructor of the current model. + * + * Our Model Registry is able to shortly instantiate models in order + * to call the non-static `Model::name()`. If the embedded widget is + * allocated in the constructor but not actually embedded into some + * QGraphicsProxyWidget, we'll gonna have a dangling pointer. + */ virtual QWidget * embeddedWidget() = 0; diff --git a/include/nodes/internal/NodeGraphicsObject.hpp b/include/nodes/internal/NodeGraphicsObject.hpp index fe87c6c80..ee717b5d5 100644 --- a/include/nodes/internal/NodeGraphicsObject.hpp +++ b/include/nodes/internal/NodeGraphicsObject.hpp @@ -88,6 +88,7 @@ class NodeGraphicsObject : public QGraphicsObject contextMenuEvent(QGraphicsSceneContextMenuEvent* event) override; private: + void embedQWidget(); From bb3bcc4db54d4a66db40319f9fb3754b7a9e205f Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Tue, 12 Jul 2022 23:27:08 +0200 Subject: [PATCH 26/36] Rely on GraphicsScene selection model. GraphicsScene knows already how to select the items when various mouse events occur. We do not need to manually implement the behavior of the CTRL key for selecting/deselecting the items. One thing let to fix was explicit deselection of all the items when the current item is started being moved by the mouse. --- src/NodeGraphicsObject.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/NodeGraphicsObject.cpp b/src/NodeGraphicsObject.cpp index ed16c38dd..7516d0bbd 100644 --- a/src/NodeGraphicsObject.cpp +++ b/src/NodeGraphicsObject.cpp @@ -203,13 +203,6 @@ mousePressEvent(QGraphicsSceneMouseEvent * event) if (_locked) return; - // deselect all other items after this one is selected - if (!isSelected() && - !(event->modifiers() & Qt::ControlModifier)) - { - _scene.clearSelection(); - } - for (PortType portToCheck: {PortType::In, PortType::Out}) { NodeGeometry const & nodeGeometry = _node.nodeGeometry(); @@ -281,6 +274,13 @@ mouseMoveEvent(QGraphicsSceneMouseEvent * event) auto & geom = _node.nodeGeometry(); auto & state = _node.nodeState(); + // deselect all other items after this one is selected + if (!isSelected()) + { + _scene.clearSelection(); + setSelected(true); + } + if (state.resizing()) { auto diff = event->pos() - event->lastPos(); From 3b124f3b4ffa418361492185e6a798a842bafec3 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Tue, 12 Jul 2022 23:30:08 +0200 Subject: [PATCH 27/36] Remove wrong line (typo) --- examples/calculator/NumberSourceDataModel.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/calculator/NumberSourceDataModel.cpp b/examples/calculator/NumberSourceDataModel.cpp index 8702b39e4..7b63fb67e 100644 --- a/examples/calculator/NumberSourceDataModel.cpp +++ b/examples/calculator/NumberSourceDataModel.cpp @@ -113,8 +113,6 @@ QWidget * NumberSourceDataModel:: embeddedWidget() { - return _lineEdit; - if (!_lineEdit) { _lineEdit = new QLineEdit(); From 6aeec6c7b2d847a616a8506083a7c45933f83ad6 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Tue, 12 Jul 2022 23:55:31 +0200 Subject: [PATCH 28/36] Add code for dataInvalidated signal to the Node --- include/nodes/internal/Node.hpp | 7 +++-- src/Node.cpp | 49 ++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/include/nodes/internal/Node.hpp b/include/nodes/internal/Node.hpp index cd8ef30ab..9047690a9 100644 --- a/include/nodes/internal/Node.hpp +++ b/include/nodes/internal/Node.hpp @@ -98,6 +98,10 @@ public Q_SLOTS: // data propagation void onDataUpdated(PortIndex index); + /// Propagates empty data to the attached connection. + void + onDataInvalidated(PortIndex index); + /// update the graphic part if the size of the embeddedwidget changes void onNodeSizeUpdated(); @@ -105,17 +109,14 @@ public Q_SLOTS: // data propagation private: // addressing - QUuid _uid; // data - std::unique_ptr _nodeDataModel; NodeState _nodeState; // painting - NodeGeometry _nodeGeometry; std::unique_ptr _nodeGraphicsObject; diff --git a/src/Node.cpp b/src/Node.cpp index 5b6e904fa..05571c40d 100644 --- a/src/Node.cpp +++ b/src/Node.cpp @@ -37,6 +37,9 @@ Node(std::unique_ptr && dataModel) connect(_nodeDataModel.get(), &NodeDataModel::dataUpdated, this, &Node::onDataUpdated); + connect(_nodeDataModel.get(), &NodeDataModel::dataInvalidated, + this, &Node::onDataInvalidated); + connect(_nodeDataModel.get(), &NodeDataModel::embeddedWidgetSizeUpdated, this, &Node::onNodeSizeUpdated ); } @@ -190,7 +193,9 @@ propagateData(std::shared_ptr nodeData, { _nodeDataModel->setInData(std::move(nodeData), inPortIndex, connectionId); - //Recalculate the nodes visuals. A data change can result in the node taking more space than before, so this forces a recalculate+repaint on the affected node + // Recalculate the nodes visuals. A data change can result in the + // node taking more space than before, so this forces a + // recalculate+repaint on the affected node. _nodeGraphicsObject->setGeometryChanged(); _nodeGeometry.recalculateSize(); _nodeGraphicsObject->update(); @@ -204,31 +209,43 @@ onDataUpdated(PortIndex index) { auto nodeData = _nodeDataModel->outData(index); - auto connections = + auto const &connections = _nodeState.connections(PortType::Out, index); for (auto const & c : connections) c.second->propagateData(nodeData); } + +void +Node:: +onDataInvalidated(PortIndex index) +{ + auto const &connections = + _nodeState.connections(PortType::Out, index); + + for (auto const & c : connections) + c.second->propagateEmptyData(); +} + void Node:: onNodeSizeUpdated() { - if( nodeDataModel()->embeddedWidget() ) - { - nodeDataModel()->embeddedWidget()->adjustSize(); - } - nodeGeometry().recalculateSize(); - for(PortType type: {PortType::In, PortType::Out}) + if( nodeDataModel()->embeddedWidget() ) + { + nodeDataModel()->embeddedWidget()->adjustSize(); + } + nodeGeometry().recalculateSize(); + for(PortType type: {PortType::In, PortType::Out}) + { + for(auto& conn_set : nodeState().getEntries(type)) { - for(auto& conn_set : nodeState().getEntries(type)) - { - for(auto& pair: conn_set) - { - Connection* conn = pair.second; - conn->getConnectionGraphicsObject().move(); - } - } + for(auto& pair: conn_set) + { + Connection* conn = pair.second; + conn->getConnectionGraphicsObject().move(); + } } + } } From 95b5d8ff5a6696236a0ad4a75e1c0dbd947aa58c Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Tue, 12 Jul 2022 23:55:31 +0200 Subject: [PATCH 29/36] Add code for dataInvalidated signal to the Node --- include/nodes/internal/NodeDataModel.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/nodes/internal/NodeDataModel.hpp b/include/nodes/internal/NodeDataModel.hpp index 1535860a5..af7b06c3a 100644 --- a/include/nodes/internal/NodeDataModel.hpp +++ b/include/nodes/internal/NodeDataModel.hpp @@ -176,9 +176,11 @@ public Q_SLOTS: Q_SIGNALS: + /// Triggers the updates in the nodes downstream. void dataUpdated(PortIndex index); + /// Triggers the propagation of the empty data downstream. void dataInvalidated(PortIndex index); From 2deb0448f855cffa8203e48c6aa21bef66294bd8 Mon Sep 17 00:00:00 2001 From: githubuser0xFFFF Date: Wed, 31 Aug 2022 15:25:03 +0200 Subject: [PATCH 30/36] Improved README.md and added showcase for CANdevStudio (#322) --- README.md | 60 +++++++++++++++++++++-------- pictures/showcase_CANdevStudio.png | Bin 0 -> 92514 bytes 2 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 pictures/showcase_CANdevStudio.png diff --git a/README.md b/README.md index 5da23a74e..7971f2028 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -### Purpose +# Qt NodeEditor **NodeEditor** is conceived as a general-purpose Qt-based library aimed at graph-controlled data processing. Nodes represent algorithms with certain inputs @@ -16,19 +16,38 @@ data and propagates is further. Each change in the source node is immediately propagated through all the connections updating the whole graph. -### Platforms +## Navigation + +- [Navigation](#navigation) +- [Platforms](#platforms) +- [Dependencies](#dependencies) +- [Current state](#current-state) +- [Building](#building) + - [Linux](#linux) + - [Qt Creator](#qt-creator) +- [>>> Version 3 Roadmap <<<](#-version-3-roadmap-) +- [Citing](#citing) +- [Youtube video:](#youtube-video) +- [Now with styles](#now-with-styles) +- [Buy me a beer](#buy-me-a-beer) +- [Showcase](#showcase) + - [CANdevStudio](#candevstudio) + - [Chigraph](#chigraph) + - [Spkgen particle engine editor](#spkgen-particle-engine-editor) + +## Platforms * OSX (Apple Clang - LLVM 3.6), Linux (x64, gcc-7.0, clang-7): [![Build Status](https://travis-ci.org/paceholder/nodeeditor.svg?branch=master)](https://travis-ci.org/paceholder/nodeeditor) * Windows (msvc2019/Qt5/Win32, msvc2019/Qt6/x64): [![Build status](https://ci.appveyor.com/api/projects/status/wxp47wv3uyyiujjw/branch/master?svg=true)](https://ci.appveyor.com/project/paceholder/nodeeditor/branch/master) -### Dependencies +## Dependencies * Qt >5.15 * CMake 3.8 * Catch2 -### Current state +## Current state * Model-based nodes * Automatic data propagation @@ -38,9 +57,9 @@ connections updating the whole graph. * JSON-based interface styles * Saving scenes to JSON files -### Building +## Building -#### Linux +### Linux ~~~ git clone git@github.com:paceholder/nodeeditor.git @@ -51,7 +70,7 @@ cmake .. make -j && make install ~~~ -#### Qt Creator +### Qt Creator 1. Open `CMakeLists.txt` as project. 2. If you don't have the `Catch2` library installed, go to `Build Settings`, disable the checkbox `BUILD_TESTING`. @@ -59,7 +78,7 @@ make -j && make install 4. `Build -> Build All` 5. Click the button `Run` -### >>> Version 3 Roadmap <<< +## >>> Version 3 Roadmap <<< 1. Headless mode. [done] You can create, populate, modify the derivative of AbstractGraphModel @@ -81,7 +100,7 @@ make -j && make install Any suggestions are welcome. -### Citing +## Citing Dmitry Pinaev et al, Qt Node Editor, (2017), GitHub repository, https://github.com/paceholder/nodeeditor @@ -98,23 +117,30 @@ BibTeX } -### Youtube video: +## Youtube video: -[![Youtube demonstration](https://bitbucket.org/paceholder/nodeeditor/raw/master/pictures/vid1.png)](https://www.youtube.com/watch?v=pxMXjSvlOFw) +[![Youtube demonstration](pictures/vid1.png)](https://www.youtube.com/watch?v=pxMXjSvlOFw) -### Now with styles +## Now with styles -[![Styles](https://bitbucket.org/paceholder/nodeeditor/raw/master/pictures/style_example.png)](https://www.youtube.com/watch?v=i_pB-Y0hCYQ) +[![Styles](pictures/style_example.png)](https://www.youtube.com/watch?v=i_pB-Y0hCYQ) -### Buy me a beer +## Buy me a beer [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://paypal.me/DmitryPinaev) -### Showcase +## Showcase -#### [Chigraph](https://github.com/chigraph/chigraph) +### [CANdevStudio](https://github.com/GENIVI/CANdevStudio) + +Cost-effective, cross-platform replacement for CAN simulation software. +CANdevStudio enables to simulate CAN signals such as ignition status, doors status or reverse gear by every automotive developer. Thanks to modularity it is easy to implement new, custom features. + +[![Qt Design Studio](pictures/showcase_CANdevStudio.png)](https://youtu.be/1TfAyg6DG04?t=22) + +### [Chigraph](https://github.com/chigraph/chigraph) Chigraph is a programming language for beginners that is unique in that it is an intuitive flow graph: @@ -123,7 +149,7 @@ intuitive flow graph: It features easy bindings to C/C++, package management, and a cool interface. -#### [Spkgen particle engine editor](https://github.com/fredakilla/spkgen) +### [Spkgen particle engine editor](https://github.com/fredakilla/spkgen) ![spkgen screenshot](pictures/spkgen.png) diff --git a/pictures/showcase_CANdevStudio.png b/pictures/showcase_CANdevStudio.png new file mode 100644 index 0000000000000000000000000000000000000000..aa9e5dc31cc4fa38417c1ae00c02f05dde797040 GIT binary patch literal 92514 zcmb?@1yEewvhE}VNYD@x+zAkZ3{LP6+=B<#!C|n$H6#$6;0_7y!QFyGaCdiiXWoYY zKj+?C=iF28RlQxin6=kl-o3iN?$sOrkFsLuC`y`!Ej1SDt()rXKtSn3%=6d-zr&Nf{TUZ4hrsnRF=PcqWn22e{z zz56nZPL|d{Z4ii8z{y(Az#L*vrVlYTwc?}LuWh6tGd1L+P-T~4lCc(on3#&Y*g_Ot zWR(nD%ni5%I*Z76zaMgPpULy`B?;l^x|j zS`dcV8Q7Xy+nYkI$nIOz(}%+B`6z&q{xtwh+DW%u_o0Sg9p(z6CLGcw&z=^q7U zWd3teOUr+jwzF4&{MUH@ZNqj-&ejmH0>lmqvo!z+XGD45l{L4JEkw^AYO4f=TKr=b zKbk=8p>`%vYce544zf3@rdEbfM?2bw@-i~q5>|HhdR7Jy31L18pan)#Q$ubcLDmnV zBFv&JTr8~2%p$^Ef-FqJ?4lwlXvj0b2!++LgVftU|-m`+G zHPEv##MaaSVkl}0wIq9(GPmjfJ{IBswY|UV8vd_iVHWvc>w&=IfPiRleL6#=wy9!cUtI4_H@hW*5W(6jfa1w#4nymuG(RDWQ^<6tz(~{EC4EgfY z;zmb{gx)RB`@R3_v z>0?)Sgw{iJiPd@@N~=b`o#oJ?ZXTij#*@X-xu=&R5w)G<^DXbs_g$c}JlAt;U&yVP zs`ImwuXd```1Jd2X~((NjN0bMZn|iGNDPU!xN0o5sHv)U=m1iY>4&h0i1XF0^XFLh zw6il=A8H<+>x0D>HoONBXMJ&+W?nXOFEl}*jnK$Pf93l8OX3xdB?aW+CFsf&gYxgB zCOd3P9RIIu&J!O|&3x!}?Kz{P1b56WK`=FAr&!THLgcYo!FA4xz!fbFD?s_J<=Ar$Re z_C#A`GC#iEEGFMuHZ8|u`)VInibSYC@bO?O&zTxB999mo6BXX~9!lf#%YgBWXL9eq zk#L-hfLil+EX(#sDnu3Vi@3U%*fL1e`G?!@gYOjxC>DwLv>myRI;h5v#APEC+HhHo zOlsdpFfuyopR_daD{$16a3X^J+jo2Zr6|#GX`>#Elq5%}?L_M4=Wj3`$IP=j`ZrM& zE|tfGL&F(2pM90GJgS-ptS?M9!(7i0X1x-lKNc2gYDp67w+p`dUhHg)IS2V-qP-Yg=GI!5HYhGXI5 z*H5FXN~NDS0UVXsM1K zKIC+CKj&p3L(N0&yNZ9Ear2IQd>izUz!&U8$R!mXChrc0Y4I*0=uC8UX z<>z4BcMb|N9eZyP%B#b466vkGX;teWu_cL>blvPjJbzHzkN2%0(t`_u7PiZsWmjEj}8<5LZX@?L8Ufz%M|Dqa3;wMgRkh%ASF z#qm8&8m^3IGqZ`)=G9!eL>>iRh!1`1y2&KMkh3btGtn$mT-}2wr(pAT;QH$VaDEE0 zfOuWk$QubP1Vgm+Z;|$!qpTVD?hW~>;Ydfa9nXT%ed*a>Q&6(FZea*Sve-vnWAT^#iN^2OTP8_7_59(McE@C1_ObLmumey=L!b zj_rkMDR_TJI%X;{P{P+_b6O!e!p;L9y5d-=u;R8XyoWx|EgyBG+^$J{Aw=tb|5Tzn z?@=M_`0z)dKVzMa4!N>%S6t=XU4z4&DvdB!xr0dOQkLYTcSGf_v4kW;hFAuarn1N-0L&E&7r2JMJa+Xxkm# z>@_jDI_0NtM<{-KcCtBi36oOsu6dY^F3NDjU5%d~{nWke?J~2O%NjQ6j^}xMx8Ep^ z&WS|l+*^o%(Kz#cZ+16*|03hOS^GI>v3c+@UDoeW1EDJY`C+m3Jxvs%g%@cau!p{p zr18=0f6h260%|(RjMZ4z4 zkF6X=JI2e8x?5kLc5l+fC=`?5g4GqdiMqtGNcnOLHQ} zHq8fWH^mOPzJhF4a7IaJ5x4B#@Fyxe16T-pg8iZ=WvZ-Jc<<`YP_Yfn;eH!C#CO*P zcGR>>CL;@BolI9*2zeA%zZZs8z=6x zwa+@XZA*qcb1uTHw{w%;{Ug;=`ZkhCu=?bn`vg0@B+NUn8v#tZ%_r7HFD~I9g`CW@ zR@LFhH+Kn;Hd#}6DQ8@c0egIcuFH-znef7w4wHA3Q{Hw#?;JY6n!g?yaWzi(FvR6Z zmS5^Ss$dCUlP)vdX`w#^RVRLaZ})K%97yfzBFV4V#N@;-;^*zzu-%-#x^BGXcfpO2)~Tld0j+-! z3QUqGGBPrIq4thbfwoi$%JE#PC9wIKB17~g4e6_~tM6J@6MI?V9+wda$jMh{Inv&>p{F8e+xqMRS?egcDX1Z-WuKUF3=)xOd3-3R>doFvk0_;bJS6w}_qwFAhhx)PaSVw$qhsu>3+SvG-L$it%Nu3k z7ozP1nEw)$(#EEyrs`&YJ)F?UNpD>AZ7V%A4XWBa-Dm2)uK)s73I4w%eq^CAZQYzs z$z}P1gV9k=hm|W~DcL5Mzw#$#>p@MOCa*UK5eIeDh*-fa@3RJj>YA=FdTQH!Jq46) z(h8<6y;5W&NU)w`Z!G7H26bzgY*s|GJ4RR955|K)VJuDdCvj?6{e{W+#2d>K#|8A8 zX#vqRA(7h9y>t(d?!@J1odUUOUQXMWJwa##P@U(unO{FwaIq z*1TGulg{3BHa+#cot$!*dI|FCN@5X5KfeA$?=#nei#n^~>H*vC3W&z?a3$Kw(@1&2 zK{-;?W42#1Rmkp`lxArFr(W>Qyc53~g<%`E><;R!{k6#$IkMQK8)@UFZI~K^(*>s6CFgfd!OxCf zIWlt4t7s^@R&Io0?C;X}T@^^8oF3}y>eg3Z*AKNb?lZD|?ADz}C|xIbjKmMuGH4mc z!1tSNkwZIkH$S%QB5&y9=~1t#XmH+?Y%JmtU22~6g=?m)x!?SnAJ0;35vg}3^}H}! zR-I?%a8z5zdwf@AT6j?}Q+L=;T=i*JFDSb-l1bn&bMG4}$q=!J!u+AY9G#7|m{pvZ;%8G?4GsVOz0*>c#Jkru>Tq z#i)W1zQwz%=Q^_!S~jBDqyR!`+PLb?8XnL1f=P7Tb~$LS%?;+5n_zR4w_j*#CRAu? zyih6TjA>wCA>(T8Js)1*o}yE)JyGvHx(EZ^_6oa+cxe;8AUsZcERr=*>}eFcYX}K! z<5s3HGp z_^bz(BQG15h1)mop)^Rrfz$EC9VW4()004XbK2se^Rufh1#Yv}{TrAP)Q$trQ( zH=eA-_?kanr{^2?KKE}qHzzi&Zseupm5(qcc>YtkfK@!F$yk$I)+@(%#=jCnrsa+U zQ<6%};9TGi7JC0@FFemrwEOjUD<<1|Jpp>$%2Dxg3%Ep-+(x)6Hk2``&+Lw~=zHE( zzP;l@@cI$YbK-g6cXmw2GQb6SzLQ2c+=Ccwa|!Y?-I&AI`?+1boTOi=lVTp3_(9a- zqh?s>6fIr0mFrDEm6hxIPr}OcyUCyL!QiClJ085QZoi-+<}r=38uNNKbI@(#bIa06 zE!z!>s!=22Emxf=B7%*y-YE^GY5ohxe)v(2;ULd($%C%m%s#S}vY zARpei6gX0S#o#dy!^a&Nvz$ItDK@C{GwE#PEzxy$JNV@0i()?2I=%VpX^x)p^;K6Ntq-Lv?v=Xe%zUnT%C$yRn^7p{(dBrp z(am!45R<^H0E3iLDU!g2($P33A#zn~d)Wb3%k3#M^MvE^OedUQV_D{aGrE1#uVgDq zHcbNM5ZhBVsL)?{j^w~KjvQ7Aa5ASyAXZ9d#5QY6!;h2jkiBnBbBg1Wn!VQ3m7rmqE)WAN1rKW_RlsZ58;EKREoyr`7# zE|8xtmadKoxb>=oz2^;qoIh7FMRe3pvc-mK&qhsUi~^5up4ZOUCIPHn>wS7E_4I7^XpQRd`&CE&6PvP}VU~kU zRAfRC)?XspDTN^kcthaw##-oDh+{ygwt24U{+KBn{B#olwFqB^>OZYM0x_W(o6jW? zI)@f9S`!tHj#-`>se3nS^9%@y?PuwXDecYMl z22EE&NjI^vHC@J2;KQqo+v3;9V9jhT0^FB_Q1XBgrpBy2>4QC zQujP3`_h-B(VTGOW^{HPGJbThyTZ9#&kLQaox}pD_Ruz45B3KSd)u({4PNZbVY6c+ zcTkc_a^J!QdtZ+REQE**_H%HiE@O<*TzNkqC2v-HD{_6<>_Yz;sJ*r8$i1ToKAsic zVDGwPTozwUd^SH~TEEsNWc~QwSColfYY5#e3#lUt7R6r5OE7=>PI;7N=L3!i^4)$d zK+7w%=f@1IwPu4l*QxoJo_FPcb|0f$pY0NU^`qxLFKknWGgRt-K_saiD_f}~J)if~ zFl_o!a7?Q8{PvI7>RB<=_SC)pa-i;%cJaIq&5lRVz$qv=X?pH$2+Ba-<>zxrLeB;c z<()=W{)@K$KbOBXVs;(6dZM_BI34-V_M+^{cjuJ8ZRGcJJh@pL-kihyWpgq7YS?kE z(TK%W+WyjnJ%u+JYc0zzA3}EaO>Qf0tHP5{o|^w;%OWmZlocKi*#EaHX1oeDG>u!Q z%B+rk%ri1y0mI!Yo~IxyS6wV4JIeou_Z1dj*@m&X!e@7wCj%+(=0eu+P95Lba(sm< z<5(>^o<}#?`=VP?DXYRcdb4#v?fR_=hwwrc{5QS^n}mPkW}prD5v@{v@B;CH6YThIk`M1#Q*hH?a`eCi2%3S-i^uj zrMx%p_B3Db^z&M90(S#^@pN2((^1@U4pJ^)lRm=S`o3s1-S1k5N0vB4`GIXz$r++9Q_h@sRXz! zmYetkP){dz&%Ouu$zq&d-%iHQ&YkuO@brYeT(VJGU{XZ-nc!mVcEIY`Z#^k+(CPQx zj-HFN9wQ(Ou7!chC;p`Q(`fjC;;B<_m8@C<0j^t zTrO$E_?20GFr6>wyMwzL1!&yrcrsl7s+%09MW9Dsl7P8Z9&3HQ+T@?9IEV^_3Gl1Q z54zYfZreFWoo9$VxiUGQ6PTVbOwP*htLitcO&`4R6liE}qy{lzbZhkO37iL4f6r6x zoxtBCl>=y+S+~ZxUC;W#v(6=R+A2*&`vd<6A`=#kk-I9V%VlNOnX`s6g5Wo1J1W?C zuML=7y8I@w?&w|!&5&$WOzGUDTMfMMjbpjE^BbRM=cwZK-t=>%5(|9?NgX$fZbJ@q ze1*F*4bQ&%mNT1YUZ)r)JQ_R+0u912f;FSm^zj>`lyNb-@lg9x(d3ScL3O7i)G3j!ym$S3BNca;CC1NDLRKNv+ z_B|yvbKyO&hIL|E`7UzO!I#@Y*;t^amuJxGSN$`5%ZF{u2(35tMGEc{H{kx6i>slk z4ru7sA*D2a>yxV$ zH*R;x5?SN0n~g`Lfvb?*HSzL9qY|59rN7pITyXR3T_Zl6#uX0C`(_TC-YL?` zZ5*4q+&(pBy()BsW!CKO!!>Q|t0O!+LAr@*yU{nfFCkfgwM%q$(u5H!cac?4fky)|A8E_RYl zf6MY7zQe}~zdM^C^-wx9Hb`^gQF9FhpyeGf`E7;Y02A)T7+X~l??0G9Y2qg?77n5A zEqx6_$r?5JTFEKA#uYOqhlAZ@=lLmDhWzMd!h)r|pmy0nDEF?~E6a)lwv9PIUozR!@P}7!|Xi1bIW6cAhu?)IOt}n}z(JP}%6l6N%Xg_IY6W z>d*W6)dowpTejEDrsY)2JwA@rF;d<1w~HPjf~-+D*{-N;Fbl<}uu?HIH~=zD2-%_a z7f7HV9adpz#f&o6h%~+pi&4Gy3#!^L5I-6XgAlD>4EgzF>`T}`U_&UE7p5|VC} zkM*HpYn_iH^Byi%f2Xkc0^RDn2-~XI6VS}&(-=P{YVEb71XM{Rr(^xiLSm@-RAqxR zEl1~iC}wZZlm?sVt7+Yeo9oT)`ILgqieSs!T!0;Q`0MWEMNr{NkD7$T@JbGn<8+qE z13_M!lq~N(2PQ81Ni~mOKHn?C-@@TJW33!5J&FbU#+Gi#Y{Up#Ed`c8b6e%ToB-^49uMZu`|B8Ni$UiS(Yk z3iZK$k zEm`Nc`Zhk!FQpnzX?@b4>m1HEx;f4E7(Z%9yu2AMX$3q9jY$NJeug34=j&(**ik;zM2+&*Ms1|9}V2~vcp?tHHjM>E-ntq zlePQqP@C@eyK;b4y67bNyNT^6*N^qHTN(VOxjeJ)P{Ro7gpJ)kBC^W-1W-;#Rx^pc zha=aj;5criy>!pF%)&R$Kjx;h@H9=CjI+M<1{^dFB^;UTmCe<|XHxqGKgGQP_iD{F zEx!Qi^3n%XLuU$Kt)u}EF}3dz2oJS|mNn$AUQOajGb4->6nSZ`R4d79>35yL zP0wBMa*3z&`o$v(>DF(G=|AMCB@`LcN5vnx>^9+|oJG-D%G(bF4gBEKJM3?72e!)o zOxzm}XiuJ5`N^EV+pi8=dxGAjjV3slRogB6?vusQ(TAV8rHa3`FLx68E|nn+S|PXo z$)wL{=gLi}ZKA7+i<5~Sfrh_aaAgzsoJQ1M@#nha`+Z}6j1FSj%6F*UU=4uhJDlHI zm)jZd-jdU3_dPu=t=;FAkmS+ysJm(F)1D0Or6j+%}| z@jn{sC>bXb&R4%xkL_4CR+cF5DZ#7GKiJjfHC2)%ue;T767VqS@H?$?rpUqpwIP%n z05Boo^vJ7g7$6Ol2{d@zSj=`ubDoK|`C+N$pu9!YgZ7D+yiM1Ljm8R|N3rLRhW?}X z1+xZsCpv&1imAqyPD7R2cV+hz?|W&9G&TFLYy_L?ipGF7d>4a+awnx$IzHRfxAMI4 z&qV5=3%ETKtJkc*!0d7>yh_H70y+3(*!0GJ>XFK`5kq5RTn~rJX@eX+kCZsbfrmSP zVDH`7E5*&{jmjYyCzEW`Oss$8==MaZ*SX3R_I+=Ue61A7Vux2ig-Ds*ZJd8x2Eu}@g(TY-Dxf zg^xR6-4JiBAJS-9x}JODj-HTi?Mq0f%7qnKtx!JRb#Rr6DJV>Hef{-Y%7TveOKd{6 zi)`e>VYm{chldK?%GC;08{YT+{%nd#@SBdHM>)SGV1>(r)ovtD76@^#iv|h*;WxZ{ z;j3(^Wo$lest8EMD#RjBF*(4w&2%_PwMmMBFQ-(-+wjgv+t5pv6$O=W1ZLl;ICK#) z{K|^db9l^>M{O{igycDPI%z?xpj6DMiV`}*GYV=^s40w)%|f(K8(;wd;o zJ>A9K%5ep7G%ZOi+5-TYA%i^pg8nt3X*u8U&m*<+ePOP@kJ<)8594pb+RpdUQ{O(+ z^=jeNHfH$y91i0j2XaXND4z2D|KeQkUr*}(L(Z#LuQEs%4t{?{&7lM)Q(?E>$JHn; zE1M|`Onj=lySwgLUS6KTeYc8Zo5NZCDFXX-Vs$Tha_;S6YwpyVnknZ{r%gymssP_B z6hQOaU!80M=aoWvqx@GJX~*mRxUP(vRlhvp9tpkF75jC^vbj$|UO6$~cdlpo7n_at zn?td&c*R+nnVG4n?{7B+f{MQcv4B9jW+*5qFB+LY6^!9(J1t^=op=sz>swgR-9iSc zcZ`p#-lSGeS{U~w=BRu_KUCOx>E|m?mIlxwHXeum)4@hsXs+qw!>m~=Tte<7E5Hh` zoOK{PMUWQbM497Lq-q1#gDFG0L7C|j6B8LyF}bFnKYxB)EY^$r4v9F71w>sLZy*5Q zbX5*7GrTShrRP0g8oowZ=5)JGFxU50OH}yX8zV$3#iv}2X`0^$VrpOOPib_&J_82Z ztY6$IaC+);XggQuJc7o=sAbpDKTqy3c%4klVyx_|WPM{BI8o)Qa`0&;l}&uGrHidh z`|`j^`6jn^*~-nS4&so)>dGj)_853>+AsH9Yd-4dKVo9mtg;qNicKBw5jbSP>1lAkCdwm!jE7pF8j!bAw99(6f*NV% zEVZAPZ2v*diu`qYMn;BX(a6+WsP7ZkZpx}4%&>C_tL|qGt|FD6jUj(>xILW$ zIZJzOtJS;Txbir~Qw4 zQR9qC#&4LIAH}NUFsPU8?{JBAr>;%(6X$>+znVxx7;cu9F3xjvbIn@?P>xuYh%^oZ zacgcnQr9%)mHXvS@pnW#CusV;o+EZ&KJJiy3M2bX*>4r&g8g`}@UAyD?PxK%p1nhx zm-G7jt`tgKEJ&Jcy+38X(X%lgJW*`mH)}Ot2~BC!cV7I8TVXj}q1*g8_bFaPr1y}e zqn06?lgj?PM;UaU)eDj{v-QhJORRgfwaR-&I*bj>bVB^a8LPH(#?$3Og%0zxs~DO+ z{)7twx8X=zA{y!$YI}X%p9pkDjaP&;;dn*uie=igRWmOdPpnvwnqqSb3msNaSy)-y zREzU~km5`rzis3LQ6}^3h^EQG4r)Gsfv(TeZU6zKvOT~}1 zHQ(7VcqGCf#3Wx7?e90uIyySgwfjJ-M04&g#KXrJ0YW2R(N+-ABZEFbf0N19q4Q%l z;cf2iRZQ=iHIQ+0tM6bDUS3}6XT4(!dMtqt!hUDGP-pw2wj6c!&i!<9BKKkf%&2}e zOexmSeRbzvos|2WlqdbW1S2HT^*i-r>YDs~`g(0rJiP2R)F;Ko1|1=1XJyp3cN8KJX0eel_4>f@fIJ+ux}Nwt>kOIPQ%vAN0TbBXNYK^>;gnmKdT zTTvknJ}x(ZY75e;eo}(e6coZm(+*Ehm5&iWPjvqIv$M4&DJ@MT>aD)@GPa7#XRbFE z&-sb`sH<52Y3Lhz`bZkW*4B2_S2!x@>{ts`vWsffJ9fsX*T%#%+V$)ClpT@er~tPq zae}1j?@Sbh^rRpEW$rQ>4*$_k zrGe+&@OfkuViajfx8vQ`XI}*i>7c2iqNA#?X0LJQm1)9UVn|@PRt$x;Ck$5ZGq;Iy zNQfheI&Sq1>=wF;2|!3q&31RG49ETxMzK^zzEpZ-vV9bJms^r*&nvxs@w?CZh>7byuF~_YG^Ki9jD7{zLw71y zKIP~8anjhMzNBqdFIq0HIPOQ(2r!TLHyix;HujySm=f;cK8ajp1|Vr(2m~_s<#vH^ zUKdbrn3InsNa%oti#^h8AU82H`zh=7?2q%y51on1*kB#990c^p;GpXz6J}kOn-j;& zyQb#W)+eCbw*1SrQuAyi-5Q`3rks<)pr@d;^WphYHU;3ESK9oK&=Zhu%`;R~I}7M+ z%_*|Lg?E;Ca_ob)2Tn`3@`^B=X2k#H1b1;)9mxk>wP~an(t{IEa$h`CA`1!<`g9jo zxNLvWO;1xp+n5<I>=nd+75B5`#DsgZRN~%Vw+$v@b(L3N@385-u(^HHX*p@Z_oc z9zgGY-H<;dtcB!Wc^y84eR$o8<4Zdte?)SKQ^N9+0*v6iV7o{`nbotnPpNL!-(>i-4x4tDBl7mz*Uu%rs%a zp-5Op*8FfYh+7VPelq;|iQF8HeKSCY`)lDJFzMeU+P|pX{VHB4S3Q!hFB%%()lAqr z^%qOu>BV0hnrMB%TTBcx=`_?pa91v$Gnadq-AGTqpi}!=ZyA7#fGf@71HU0Nufeg( zW;rEda1Q<=rx@uQd_;*lHptm)Y{}3yyx|IhBUe%MZ!vySk&jmdQLxv ziQ2CFeILOWvDhGV1=EHZ=lQtYvz^eF=~jK6(*4ZQg)*;dC4;FpP0cgfZ<0gcdZtMP zxeljVa%ks?sS|O7t_mKe?NcKO<6+?M)f{y?rjy}@-4TujNuNf?=H@&AptiTS_vepv zN8j(?zZ-9Np@bY3e}mZA8(IWq0|Or=+jm*%=P(za*BO}^q$*d(8STFL(u0BEj}%f; z>G?Y&-m)j}`kI{a5q*Bh3!iBVZAnPJJfZ6)!;cJnxu!oXmy}JO^6#IjQM{BDfbEU}k$qiV-(4K(bWVG?e0UYt z)YqAxEaFdcQm(Q;n@yV7|6r?YBph-z6qamd-6x1AnVy_3+&JBtOr(YY^O57a=O|s4 zEHo>|4;yVB&RN4}MwQ>SajV8=-}+`;IxAd_S%(_S+|clZ)87{bN&81OKN&vGe)9Hb zOqb!2eWwM7t4?1eCQ0hgY;N^1-G>_|eal4fv#H=`8V}TP7CcSh1R`hTGqz9jf`g~4 zt1GaVV!ldW>K<~=D2m9#{%bFd4*s>5ZgA|GnPUY0+D(=J3_-FdeAk1Ln)tLH>A|v@}!_|^%XEu zP4>h6lqH9$W(!|l^9ZbJ!WlZ;!=b7#uvhO0BW=?Q_?J!)EAtSqUiZ|pp}H)I6!RJ2 zXi7aS9cVe0W#CJuvpCgFpBS-An5%F^nf5r3!+Xklb7yeQ@n`vnlVA@76voK}5pTOy z&88y@jlJ8W0UymYmXig8p0AV&*$T2p?%Z7_4Dg9db&uoWe82ENejW@|eCv5e*qTxc z#}6I757nr-(VH|#8ah9v3o(yk$6)&rK0?1=!KoA8LCHo)McZpYKw(-h8gAb+Kgi(t znp)UOguuufNk3=jOM}Ga0wn4;|nYMS5%P z-58(mhneFLohN|R>{2$~!o?a_C+A*qs74xq8`CWa8T?dEwL}kJXLpwR?I<_|CcR@f8n%${j-TN?v(JhKMEE1I3Uyu4oZzXSIt`Z66jfw(is2 z^M^N8|3By@h$}7q&>F}Ja56kx|CRW4PdBfdNRtjEs0JGZx_4W7$xGqrK332W>je*JgO~u=2;^;cu4YX1ua(npl7i<)(p*? z(h_g^;v=9R19zX*I7gj)tE&q?fkklG$?NFo$jcX-3{u3*+X7Cp=Y)iW`1oBkBp{~V zW5i_cmfPzT?KAN%f*|={SwBb_)ew*fKuss&_>lxsXui_yu3^CUH@-EBCwo$?W4`yO z@Te#J+sRXdoXGeo-EcI7ldbEhu=wwB$p-XV3Gr`r-tgI3R=v~ho~dS4mSG~I74MCH zt(aVAzWds$57^0BKjU}+Sw0|q97oo_>HVy z-BkXBmY1F3_E#cnSkOI46?y+RocwZv)J3*OZWrDkC@w3jU1>GD*y7jL-v@Z%xQ`I; z4fo%MtxOnLf2r>S;iowD-60_%g4j<&`e>9nu|8`JJu*Y(#^6Fe=_mNu|DfV2pCyaa zB4jxup#LKj{iC+A4 z3CZ^NW;>O0`UZk`k>Zbg6S;=Jvp+vNIvOj|0Xu~Jn)tVHfkquQ_wuus7sy$oWYm7W z!d!~sOjd=P$o)~8X0q%N`mao3vr_I}t-HHBv-tOx?+R45X{(!b=-C|&ak^_CL#(-`4#0Upg+ zdRk_~8fhtOY3s0+Hq_b3RB945s_=NJje2caxd3u=^Rt(K2(k&?x&ecpOJ@O3Z~$UX zp~Hl)3|sLv7g#ku$ClX?#|~o$Hg6cqQ(&sq{dvjksN-ikK1{IPzLC3x+rFLh|ABb9 z=KqXz5*%D2fuxV!ybUyNOb0oDX_T|Ot6Nlvi*uG3ZB=v^8hnwpdV zKW}5?rv>Yy%A8R%7}}fXgngy5nj}e0Xo#Q(BZx_yMsfCF&3rw3VwP}Nh2ZmwrQ)=| zqOqfv6>&TJ_7e2v)OSY;p5YK*H zH@6h|oy+EuSp1_8vX(TmDYLK=*j8C)uTg{x4Q00FR&3NlUHhYIauA-DdM+S+r8A_x zd!8e#S3lc3Ml`*yU{E(@;cr;)+~N}>X{0EasdAeSSn!8y8;c=HC`^ymEWGrwzm=4+ zQQC>&Z2$9dzO%U^%277Naq(3`#GU1o0A1_J6A&K5y;~im$YjH`k*vKPlhGSy=5Znm zD?Tc@Sm_##SSd!y@g>^~I1GDPHcb{aS}6%Ds6A6UC#uzS*Ea}avC>CDXZt9I^R7r? z(9`O3>wAR1)@eZ|^F46~|3^}mvt+e!`;4ow>$GN`eVsDPXcoO@)9gm>M~khS6)QXj zM&d)KbjFp9ihK)6h($WzE3}?POIu#cCG~E`T`V-XIm6FZr_m^?j2gRSY_=>AA$Ehv za&O`DRr*<7rXN}pC!!RKY_7pli;2|M9`>K5UH}1z77vO`k;^}|&~KfV?CqP$Cm$tY#a}JDwEZuKnGDSi~#K5>DAkzAT?9j>)Dv&dLDyrjJ3kK2)d<8OU(w? zzW1v=*XWklZFlaYv|A{s?p;FUXNi2|5;PbjYVN;8!_7R(I-DPU1-QYW6<(Mym1)t? z#9Y<%oorzk1C)M`tdNG_YR_&f>m4?dDD%!$rAl%4=Ngt=OIO$g1Db7q?Y8JU3%}2q zVQ`$APhkmbHoPg|ElE8ckVxCuD$uw`sSq1Bn+AC zC2>59<+xtaR$~)6CF$P106~v;pLzKfuQq2!yIa3Z(&6M>J}&O)6~fPwHs=lnTWh0# z_Xae|mZ;~YP_1G8m<|t5f0gv{u{*k{ud5M~#fW&#kL2n9Zq4x~H(QPWQLW_R z=B97Jacm>vhRpEO?Yf?KM8Cz9`KXHXQfqH_%MN?R;=}2W?6Tbue*~D&ExGz_3|*i~ zpXBvXBEev=P9ue7#_Oba5c+6+zR05XjyXaIePQ73QIg|GCX6kCkfG@94k#x9f z-2-GKp3~z5s_2zY+H)sOd_*S5&LgQosLklvsY7?~wpy@}!Pne==d{;Oc+yXk zdd3(r!)Bu(*@Pe@%lYKrz-2d7jR7ji!iC}! z!YWe-%}fs-PCeLY?OC;}Atw^ejpJiqRReM+MM;^Ru89?hPyKX8%`-E5H%5er~Y8oPjPSOJOMS?3kgKvJOQC0 z^pFBpkM0Lbz#~(Ma*rvQ*mKM`^9V=UM@(drgMb*#pnE79i{r-Tt@QhKM#SUnC!i{- zb_NKOYul2$oRKFPyi)%>V1YcT_Rhm*Z0VG;9@xc8Tjw1^8-TC{Z^aCGOj+mZhaU}p zalUL71~nN<<7bDyyR(LWA9W4MpVLWlTR(CKzPAGQS~?jb zODk*W%u;H)f#PDIOT}>=2^fn>f}xvRXz@EqVaBW8+o3JTbPq(N-MQ$Bq1S#Ab~tb& z9CQqT0I&9VuDf;`rM)?aMn{#kw6rudrbk9bJjve=CSjnW1_GEYIQaDRG>O3Se7y0l zT;6Pj6%C}w3R#dpExaA-g9M)AN7_6(a(YBXV`at=Wru7D36I=iM#r+N6y&xUcoRyW z4IFNTpYZ{bqiaJwHN4=RjpVZjee*{$o(Mn=CT4%WRH_$@ztR_~8`-ocCnxtnIV@2e z1U{|GW+Dh?eMw_#<6O@F-i+)P1K%&i3!)?N@g&F;zUBr+>IVw6@MM;VN(=Rd# ziq9wj54Ke0BPiWY4_gl9mnF&96JOJmuC8LDH?zLZ$*-#Ugd#~|OYn0|D5A_(ZOS5} zX}5GG10~=ZcjG08t1P_)VBuvp+!>r^fd-Gk^z^`qPe5?6R)gDRo%1mok`Ua@`=&NY zg$~0Xghl=-}qQgPOqrmXVx)0tq@RC?d z!cqaI(L^Q*)|EI!1o|=T<9eFX#w@G&pwNBRH6mU*OX=(02-EPIn;+MY^XxDP6eE9a ztC|%`4WT_Oued!s4fO5e)@Yt+nta~qoWr!Gjg5_V@~h|1f9W=V?(FOg3@9utjEszw zNTR=Yi7K)~fDg3;DpKqx$K!$h93lm{9smKLl&9f0$F;Op{R}qBb)9R6ME5b5b#KZz z{f8LvIk7vQ-_yVR`%b(*qahi~a_dBb=H})G25B22G*nbFg(8oD$6(Pk?IN8LLkSa; zjgNAnfq{ULPESw&^6L{sL=iDDxgi~qe3d+tj|m?vw(Ysu{Zlc@ttH-Mr=Wwys1C*h z?#~aLDALiGs{Q9Vc-@b9iQ|0%s>c28*K7F7upy4~PFu2j<_;B?@f&~Xm4ZDxCv>h( zzYS%)&+v57*s*aJuOTJuvdMsFtSzA6XT2PMO3Fb~Vb-_=#nK;zVywKKe3$o=`3=>M zC*|ek{t391HP(y2^;S@omRb=}abCW9<^OZ9YB}Wp@b%VVS#`m?FrbulhoqE9Nq0(0 zhjh1ecS?hFcZY;@HRj>9?9ZIIu!RV3PqVn>3Ra%91hN3AW zWMI+2W$&>m{foN#hbK(--Kzn(PZV#^MAG@apMcGh5mZC`ht-o=SXr@fa1!I<92^~g z@NQ(6S7w5xvA&Lhfx)b96q-Y?)3)&U@B3m?yD~_4h=wo!&_R@p&5K^3;P5F3x%GA{ z6^C9EmDf^2?UgKV-p{K^e%q{xU7ql(NUiI?ui)cvM~~?gm|=e3!88-eC4vrGN+o4w zWBTYmtY92{_wNN1FLOHhb8~WdjChMt`0w)$W?Mz3WXdynOZjYyO|6forE`kF4obMb zd6fy`A-kw|TxaYoF(8gKB_!bE!Knv@kB={t$>+J=i?r?KC@wDU;o%`ACFS5y)86iF zYg-v?t34v{x5_9iGBPqq!C;2>F}|28li%egBL7PaSD*k8o*Z`N5QZ77V~|sOg>YdA z%8+bGO7rf&1BhxzZuu`^dm3Wa2nB zzC+4*(l?k>6mMjuq&Ci9K{P=BL;h4w&(5kTDLLGp8TAo`i!vMZes8qj?(OalwghS* z>Q_j>D4Lp^`JZkK`aWUTTg+B@+}g2+eB+J433EP>*Djr}{&|cnAzZeP!v-^CTz$h# zLek;co3$gAkJ&Ci0^dy~e*0xgA@f3D%-tlx%4KdbWUm6D_C%ul6hb6KjOsrQ3UYFK zy6ySto{*5xj9sB*YDDGc{=WIpvLG)H2u&QFoC*pHsi>&jiF7{t7BSO4JUlEEA{Kw3 zpk6wyDsFP?3xq{JT6%1WJL#a`Jvm-LVMLvzK%CqJW!Zl+#R6nt5~piDEiF8KD#H>I z5^8E{ot>Q{I)WuAa&q-kNI*_NM6`5_29J?=hbV44zm$PG&;68bdR#5Z&O`a+!Pm}2 zdQKyIY?Hx}T_D-w-a!rlLF)~uLQ43s9=nH|Q^3@eH2QRObkv&9{8X!^BqulYsU{ee z#$B7huwtyWF6Sd3m-Z6SR#>Gh#KJ8>Aud&KQvJY{MEo4Dg(7|%AmS;b6bl`zp9cLe zqW4h|*17JFCQ~Wb{-Oi6gXg5<6}ayRh;8bl2m;^p;-FX>qbwN-xk9Q22|QZul&peR z8`N*$Pb0nyf|Xul|9SoZ2kZE`%h01MXD;rMIBj4Y214M4RF*Q&+4k7^lfAc>Q3yk? zUSm2{{3Rm+#^FehiwRo&ko-PfOe;}PtGrWdM0e2u#ZZ~|c_VgI#mH-uQ7j^>sKRVF zu;*+wd~N5bK3^&kC?D(Vx!=G-=*pG8$m=Ls#w__9cI3dxG56fQ|Fanw(B#kZ`PYKa zn}`IAu{&CC5d-Ju=I-w9fT|2d!jDk4e15gc{xqV{+Ujt2?3q?ROZ{f!4zU4|!Y29a zxc3g^%Zl{I4_X^*6a7bcub{yIUaTD$4CqUbVN%eYtug|~qFIt;MGBeCt~S>hoN@K+ z!~`s5K~YhWWFr04)D-8^Op!y&RGgrL!$scxmu|D#IV`{7qa$P#luQS6ykO%g>h3;)a$s~GzD=va z4jybR5_l)hQQjh;F8Hfo=W)CHhi&xMt#c{lS7xJGpxg*YTy*HfMWTa>w$OALEosZzS zlVfZ6Io+=;(q&9o@TXYe0;SXF!QY2KQSFL#ao#iIVF>(dUb>h`!&~cpktLp88yg$k zM-2uwTxqc~a&mtjZqG%&r15*VH8#4s1O#LB?B<}#$Gx{X^ZJ;^3_XHC{5jTs4IVV9 z12e+KAG_xEdT;^rvHl_>A+_EgH3uzXrA=0&6XfLPPK=M!Vh<)Wn^-M0FiuWPo#L-K z$oCt1$bGZ_Ca`zyA_CE&3V3U9j6!ibA87)-n8))@?{aU7kB{#Y*W%{9@wTC$ipmUm zS!`S!raVd_gHD^xs*tB=>&^p6_YmEb!F9(>OMm{vj{gYohrhr7!2OSpsl)V^doEc8 zWFaf#7vH)6&FghPGbT$xLqqRf5jrn0en7bVQL-7bWME=axZ`?vdn?_WVW_VUI=oi^ zss&0-Fd%GgeLXifm+alU8ub~pT(;D*?Ck8iy1L@x&n>n(I?Dj21ounaMayMI&rVNo zt*@{1Obb{l#z6h&z6d!OH)CSNKm4F#Vq&(2Q`slc?_>pRc2%))aDe(#$Dx|aKt<8v zUZqskR!jcJpZ`EP+Mge$U%ytWm#yVGf^e=YdG3$Rj9 zP*BE7SOg=V{Env)YgJ)*V_|NN@i^Mh(2$iyQfsigQ18IgWW6HU>>$?KK1zxde~n3i zkIw~GDljk*=tKc^-P_yyWUbiJTv1VRa$?h}&G1gk%1+73z`t{e=T{ei^6cz7%}&D> zY}^VkW=(yz?+^(7Jqtcwh9I8+qIvQV*v_xzW$(YPkhSZ3J8?}G06Bzc7wtr-W3dI0 zsgbDCVyDY&YxYBEY03*d|A2gT;VZ`ecPs$Ao{DYq9)}97DaaPS&s%Z?7f*ktPuV#`(uYH1*(S4W>TeS2ke9(q-~ByzJZwDncVmkTVg4mYdxT^=jkDB}Joi$xartjrq3m z9=KQ33#Nsr5ZyUGHW`Ff2&!&sa!c0ERT`lJo@_tTVzru|^4>3r z+>ZjcBrTSBK(&%$O6e2F#{#YiO092(B;|Q7OM;jK0##=@4Tf+pmBfLU--a;vm3w~y z#@SUI0-@%@!op@Eb!G<10~Y$bH&4YCnRtW5@nWa*$!%Fx%C_v-ZRx68QSKu#PFeFG z3Q@Y%P1L=lVtgC$?Lx!|0<+La;j!cooYTM{n5dQ*K`F+sB&QX$zblzoS!@04brB74 zf8q9i)SkRKALTb;0|P1N>ns&@8a_*I(cOFSz58s_|LvB-{q?aD!8S3kJaOx`-e!>= zd~fvRo6g(BI9dQBZG55-bjMO5SMPh3Or{+w@xy4S)jmbzee|Tld^6yO@J?E`<16$( z*BNBgRvXZ@v#ZL=g2-?k7veUB7aN;z5GP|8*VzB#;^u~5=1b(y=!V+Mi09s_O}f82 zlFmI9gj@6@5Y@Efd(Q`-mGHqSNUo^ADFJqi0L-BRk)e{>b_1^D8rSN|wFS5} zgXGHJ)F_No$!?c>=gMy=#MEfyHBHp3I6eyV(FjH%+NiI72qh9W&w@=fXjzMa)F29Z z+8~=InS~4b6&o8{3JwDZAHx;k!3QDr1BP=Awgw&uI3D7z9&MtaO3Ur z>wVFf31Ig2^#R>Va(lh=@eNlkTsm~PQU=hcF{?$EzC3;gFCQSV^*FV=K_sPk5C;pERMw|u2 zbfMtR^+^7ViqUum;;f`Cn}D>9cxLU%N97-UM2IBy?pbo^;u|; zl2j3reTMa|MB|ZK4On5HZhMeG)yXR>*M*Y8gpT{1{LvtA%Nmc@;#{Ayi%FcuCr=RY zOZu!mvhgyfY=8pq@1n_A>44PnP9udljFoeDQuOZb*UQaP*Y)>E0R^8>x^Q_LM#5kD zc7VV-do^9AfyGTD;r8eFx-iW)rlxmi$Mma+$HwM5S(G{&xCM%ty=Nf=9={`q>+Iwd_)D)zUQ@Hh^WN!#WJK_PeL|&&@>$^S z6SzBwQjS%dn=pnp-T6qqyZ87YHHQ#~CjoVI09kr=4-btFjYWt;%~At(rbQap`>yn!goQ zG%+8_WT5VTQiwgrYwBBcT={;zvk>TqnH+I6ah+aSum8^;pX$4~BpLM#_V-t)mzlmO z7f2{>E@fz3`xq{A}K3H75OEDL*Hz(&cNhn z6sLu>Wq3=u!(F%w(c zI{B_;M7}7}5xcE8vDG`UHQlH}tXVkko71}G`#@P|rcUBsD({_0Mo?@OaGmQ)&YxNp z$k}A7F0bm%AJXC6<<&9`!Q!}VUqCIzpgx8eOl=MmhSN_B-2KMq`>2;yxL?bR{FXE* zl7L6gp)MA^sXhIf7Tg(xuz`oi0|?I!WOQ_v+au}!KKPd%(%q1LBe}d~e8JBgY9-G_ zjfVoCRGKR{sP`WxyvR7+E7t|8Jv&qtbSza}g4@#WPOQqMy-Ejfqf;&D@~#3+XeZC9 zj1tiI7wXPw&kyP1w<8Gio@JEZPxelaosLs5uzKZY`{6KrFkjGOV67oEJ;JY1YWpzR zU36@oor%!;a%*zFQ9W6ur9-+Ovjo9EeUv(dy5^=HJo-+J?HqKz#73iFe^gk`@LoB- zy0ANiTM`r^GDS+O*O6#|eY#4#8Gx=bAoR3SFrluDz}@?9*l|e5j9-TpUHIMqffRx{rm53}-8-UrGkf&hT@iEm%RPRx%KF%t!t3PU!S%N} z24As=8yr>zKFu%i3!2-wyD?gUUxi@3xV{bouA;|+9~KriM)LB1v-j2gi}s=rCj;aY4)IPUC;?U+K$t~ zm#sq1IJKdmZJJV!#D4-TT5@{&;qI>9ysE}aVDK?Sg-@Vd`N2gW+0n=~v6BCDhKna} ze(8XcwH9`C3Lj=sbK`lfgud}Wnm5m){I=q(GEGXLVE{qqEorM&c|}=W23sElHK%Rq zooRGobeK-od`*|J-7XXp7+U}VQOU@|b8u=Cdk300>MiFJhQ6djpXvsvqZgtBugBiJ z-Gv`C(%s)FJ&rm!XBnZhf6wGz(P!JDXGQPxxAanKegXeYl>tu6;65fSSB}TDA1}%h zdijd6vk=7x{Zh3ezK$UUD7wQ;zHYM( zyX@uLJ`L-qE`R4j?e^Dl7EIj~^5*|H7J&+V#EcRtvg( zOTFQ5Yt_qspJ6DQsIym|Q@aKqUb;;sBCf}?8S`$Q-);~F*`xM;3}pPS(gjYj;AAjY z_y2-^;%qn;=DF)T{s8~_f}K%8gxQ>a7VLbXYnR~%N?7tof`uy|-WXhvxP5Hv{a*nN zn8d_AQ5VgKMxBL3aT8+@b3x<%%tq(%C$Zm-Z681x+l+H@>Pg8AgYFfWLI4_XDp`gzaiC(kA@(K*J&DSw*Qn$jDS;O3=lpL%5bFJ zS%665=H~u!0d*@`fGqCdpQLG21Vdv&TN*=hggI^da#<3=7$8;VQ~$>ZZ99*z=MSCg z(s|5jKCdCXepakPD_I^y#^lG+nI!7f&*Z1n5|x7`fBW*6x|=sMqhYnbe*H3ixgbP$ z0GPz{|2xWTm&-sFPX8uEr6CC#rjm?QQaTl7J5KLdcu)3gi%&@(g|Gv01z5q}zxL7m z2b%<^+I#I!)77@uf#VoC8eg!cw1B|Ev8hEDGu=Z1JJ z=7M?XpwWz8YBB$15CZ{y>wt_zp4YzY4myJ!agfz@B3O(IeLdi>$`n-uqkjk?51tRz zdthbs%5Ch;%SBWv7Z(?YENK9_J~$`|(<*RAi~nR8D#T6C6RF86_niZL2bCde@a4M@ zg8_GTY^mXvZ^c*+TOnon>G1`iv&lmu<7lnv*t9SXtad2G&_i5#u)7n{v>Q zpAeF2m^Ef5o_3U@w0DrYG08o!F)4H~pRL4aY6~VO3xEf2z z@14D3I+*6YpHCSS?z&rhpZcap+f9mN*>uMO3)8X_ktA!izr*ndEmLPiIrZE> ze}lRtd#rYn>YRRC%*sA}>_RGfv6p*Kx#Y+9k0u#WH($Q!rpn8@9h3=skBh^IHJGup z)v_|Nb{saO0s)ygB&PDF-?mGD7kCGL^AifJ_I=S_5uqcW{v~l_m?4ka*>8SsB zx@IX3`BKUEn$9|t5Zd1z7c~J#rDaN)1Aozi7H$G|`gDxulG z-W%kA?weg0ALxN9danalpzD4!{M)|o=GnYNgb^A>Mw(=#da;CkNR|GT?MAR6-#3?J zMA8L_LO`L@V$fE-L<*n$_48XdclX&VvPhm7e^$fuC9LHS<6;ucHaGoGTw&MYTXnCH zEUhng%&(M*=+Kw)VCVK5AM=Lvu@Ako&m1m z+*`?zFD{A0pQFB9c59!>4+gwK!dbFOoSgbnLaX!M7opsAC+7;-Gpu^r17aAgJ!-=l z-O?4d`~KdZpb++}N^R79Z{SX}#KL$GQFHyrvHDG}-%fK5f3z9$j6m(jrm|a)3=RE3 ziXBi=QmSc+4{^Y+dStH7R()g6J29mpjHUE1M~(#j-c4C`A>2&f;GDZv`gBzlmi$aYrkdT%fHXrx%XO8ucvD$yr1 zMy3C{jcHpmp8LX^{l@8xKelv={dNC2cz%iI>l=DwJx(>E1js`UiH=|wjip^V0EbB; zNPYt#6bIjZn(vv3c8wrPtxDxDlnrja>QIc>lV!l1`{-@lfvqbnSt>D~g6Gel zRZBaZ^W&GIk-tZ6+Q$AkWzF(+wC~u{DJYc;ONb9lV9|PXjI@gI0+u4{F(*9{rf6Q) zU>eJWeMBkA2btNeJ|E@=pW+ird%Lcz9|NwjaGxQNR4Q1CL$(5$8Bc zm9pR%RX)qt(CsB_!-58ZF)v9~;Dm)+4eeTT{0ehTSm6%gP4576jn#4+A>8X7Q7fyX z1UikFlTt%8RJ3_D7i0!m6J58t9IA8IH^}zxEuL16nfq1kP6HQPOEDOiTT7IC$zt@O zcdNA&8jYCWHE#s=gIE1Ubgb2)Sbg&4ODmPp!p=%xQ+mWT-m;=mSpRsRskgu5@~a

<2=Ih`cm`TLLF*e*FKE{xg@K=L3q(|>Gbt~v1 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

    i|D(-psBHg=S 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≈!=oWn{Rkx^L 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 Date: Mon, 12 Sep 2022 10:26:37 -0300 Subject: [PATCH 31/36] Add forward declaration in NodeDataModel.hpp (#323) * without this forward declaration it it wasn't compilling * Update NodeDataModel.hpp Co-authored-by: tatolevicz Co-authored-by: Dmitry Pinaev --- include/nodes/internal/NodeDataModel.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/nodes/internal/NodeDataModel.hpp b/include/nodes/internal/NodeDataModel.hpp index af7b06c3a..57428dba5 100644 --- a/include/nodes/internal/NodeDataModel.hpp +++ b/include/nodes/internal/NodeDataModel.hpp @@ -15,6 +15,8 @@ namespace QtNodes { +class NodePainterDelegate; + enum class NodeValidationState { Valid, From a6c403252edb3d05b0009eb2325e197496cc4647 Mon Sep 17 00:00:00 2001 From: Dmitry Pinaev Date: Tue, 15 Nov 2022 21:47:21 +0100 Subject: [PATCH 32/36] Fix redefined find_package macro for vcpkg (#328) (#329) (#324) The original idea was to be able to include the NodeEditor library to some super-project along with the included Catch2 (also as a subprobject), i.e.: ``` add_subdirectory(catch2) add_subdirectory(NodeEditor) ``` To do this we need to detect inside NodeEditor's CMake scripts whether the Catch2 target was already included. If not, we included our own Catch2 subdirectory. The chosen names of the macro caused recursion when called from within vcpkg. --- README.md | 14 ++++++++++++-- external/CMakeLists.txt | 8 +------- test/CMakeLists.txt | 2 -- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7971f2028..eca740043 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ connections updating the whole graph. - [Building](#building) - [Linux](#linux) - [Qt Creator](#qt-creator) +- [With Cmake using `vcpkg`](#with-cmake-using-vcpkg) - [>>> Version 3 Roadmap <<<](#-version-3-roadmap-) - [Citing](#citing) - [Youtube video:](#youtube-video) @@ -78,6 +79,15 @@ make -j && make install 4. `Build -> Build All` 5. Click the button `Run` +### With Cmake using [`vcpkg`](https://github.com/microsoft/vcpkg) + +1. Install `vcpkg` +2. Add the following flag in configuration step of `CMake` + ```bash + -DCMAKE_TOOLCHAIN_FILE=/scripts/buildsystems/scripts/buildsystems/vcpkg.cmake + ``` + + ## >>> Version 3 Roadmap <<< 1. Headless mode. [done] @@ -88,12 +98,12 @@ make -j && make install data propagation. 2. Build data propagation on top of the graph code [done]. - Fix old unit-tests. [in progress]. - - Fix save/restore. [in progress]. + - Fix save/restore. [done]. - Fix CI scriptst on travis and appveyor. [not started]. 3. Backward compatibility with Qt5 [not started/help needed]. 3. Write improved documentation based on Sphynx platform [done]. 4. Extend set of examples [partially done]. -5. Undo Redo [not started]. +5. Undo Redo [done]. 6. Python wrappring using PySide [HELP NEEDED]. 7. Implement grouping nodes [not started]. 8. GUI: fix scrolling for scene view window scrolling [need to check Qt6] diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 0159b3ad2..d7fbfcca7 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -1,13 +1,7 @@ if(BUILD_TESTING) - find_package(Catch2 2.13.7 QUIET) + find_package(Catch2 QUIET) if(NOT Catch2_FOUND) add_subdirectory(Catch2) endif() endif() - -macro(find_package pkg) - if(NOT TARGET "${pkg}") - _find_package(${ARGV}) - endif() -endmacro() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dffa83bfb..0c4a99a24 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,3 @@ -find_package(Catch2 2.3.0 REQUIRED) - if (Qt6_FOUND) find_package(Qt6 COMPONENTS Test) set(Qt Qt) From d09d173c1b9bce756dd9edc93b3a1232e446b639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=87=E8=9B=BE?= <55644167+QianMoth@users.noreply.github.com> Date: Thu, 17 Nov 2022 19:18:15 +0800 Subject: [PATCH 33/36] Add cmake build action (#330) --- .github/workflows/cmake_build.yml | 80 +++++++++++++++++++++++++++++++ .gitignore | 3 ++ 2 files changed, 83 insertions(+) create mode 100644 .github/workflows/cmake_build.yml diff --git a/.github/workflows/cmake_build.yml b/.github/workflows/cmake_build.yml new file mode 100644 index 000000000..90882e2ef --- /dev/null +++ b/.github/workflows/cmake_build.yml @@ -0,0 +1,80 @@ +name: build nodeeditor + +on: + push: + branches: + - master + - main + tags: + - "*" + pull_request: + +jobs: + build-and-test: + + name: ${{ matrix.toolchain }} + runs-on: ${{ matrix.os }} + + strategy: + fail-fast: false + matrix: + toolchain: + - linux-gcc + - macos-clang + - windows-msvc + + configuration: + - Release + + include: + - toolchain: linux-gcc + os: ubuntu-20.04 + compiler: gcc + qt_version: "5.15.2" + modules: "" + + - toolchain: macos-clang + os: macos-latest + compiler: clang + qt_version: "5.15.2" + modules: "" + + - toolchain: windows-msvc + os: windows-latest + compiler: msvc + qt_version: "5.15.2" + modules: "" + + - toolchain: windows-msvc + os: windows-latest + compiler: msvc + qt_version: "6.3.0" + modules: "qt5compat" + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + with: + submodules: true + + - name: Install Qt + uses: jurplel/install-qt-action@v3 + with: + version: ${{ matrix.qt_version }} + modules: ${{ matrix.modules }} + + - name: Setup (Linux) + if: startsWith (matrix.os, 'ubuntu') + run: sudo apt-get install libxkbcommon-dev + + - name: Setup VS tools (Windows) + if: startsWith (matrix.os, 'windows') + uses: egor-tensin/vs-shell@v2 + with: + arch: x64 + + - name: Configure (${{ matrix.configuration }}) + run: cmake -S . -Bbuild -DCMAKE_BUILD_TYPE=${{ matrix.configuration }} + + - name: Build with ${{ matrix.compiler }} + run: cmake --build build --config ${{ matrix.configuration }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index c9eb5941f..d5272d93e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ *.py *.pyc CMakeLists.txt.user + +build/ +.vscode/ From 8f9d655b50c8abad34a84a824bc9528ef6251e4c Mon Sep 17 00:00:00 2001 From: Romain Chardiny <38137329+romch007@users.noreply.github.com> Date: Thu, 24 Nov 2022 15:19:29 +0100 Subject: [PATCH 34/36] Add header files to CMake targets (#331) --- CMakeLists.txt | 32 +++++++++++++++++++++++ examples/calculator/CMakeLists.txt | 3 ++- examples/connection_colors/CMakeLists.txt | 3 ++- examples/example2/CMakeLists.txt | 3 ++- examples/images/CMakeLists.txt | 3 ++- examples/styles/CMakeLists.txt | 3 ++- test/CMakeLists.txt | 3 +++ 7 files changed, 45 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index feea6ad9f..ed54c86a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,10 +95,42 @@ set(CPP_SOURCE_FILES src/StyleCollection.cpp ) +set(HPP_HEADER_FILES + include/nodes/internal/Compiler.hpp + include/nodes/internal/Connection.hpp + include/nodes/internal/ConnectionGeometry.hpp + include/nodes/internal/ConnectionGraphicsObject.hpp + include/nodes/internal/ConnectionState.hpp + include/nodes/internal/ConnectionStyle.hpp + include/nodes/internal/DataModelRegistry.hpp + include/nodes/internal/Export.hpp + include/nodes/internal/FlowScene.hpp + include/nodes/internal/FlowView.hpp + include/nodes/internal/FlowViewStyle.hpp + include/nodes/internal/memory.hpp + include/nodes/internal/Node.hpp + include/nodes/internal/NodeData.hpp + include/nodes/internal/NodeDataModel.hpp + include/nodes/internal/NodeGeometry.hpp + include/nodes/internal/NodeGraphicsObject.hpp + include/nodes/internal/NodePainterDelegate.hpp + include/nodes/internal/NodeState.hpp + include/nodes/internal/NodeStyle.hpp + include/nodes/internal/OperatingSystem.hpp + include/nodes/internal/PortType.hpp + include/nodes/internal/QStringStdHash.hpp + include/nodes/internal/QUuidStdHash.hpp + include/nodes/internal/Serializable.hpp + include/nodes/internal/Style.hpp + include/nodes/internal/StyleCollection.hpp + include/nodes/internal/TypeConverter.hpp +) + # If we want to give the option to build a static library, # set BUILD_SHARED_LIBS option to OFF add_library(nodes ${CPP_SOURCE_FILES} + ${HPP_HEADER_FILES} ${RESOURCES} ) add_library(NodeEditor::nodes ALIAS nodes) diff --git a/examples/calculator/CMakeLists.txt b/examples/calculator/CMakeLists.txt index badf02f8d..b9ab36f31 100644 --- a/examples/calculator/CMakeLists.txt +++ b/examples/calculator/CMakeLists.txt @@ -1,5 +1,6 @@ file(GLOB_RECURSE CPPS ./*.cpp ) +file(GLOB_RECURSE HPPS ./*.hpp) -add_executable(calculator ${CPPS}) +add_executable(calculator ${CPPS} ${HPPS}) target_link_libraries(calculator nodes) diff --git a/examples/connection_colors/CMakeLists.txt b/examples/connection_colors/CMakeLists.txt index 784976743..1ef2d18e3 100644 --- a/examples/connection_colors/CMakeLists.txt +++ b/examples/connection_colors/CMakeLists.txt @@ -1,5 +1,6 @@ file(GLOB_RECURSE CPPS ./*.cpp ) +file(GLOB_RECURSE HPPS ./*.hpp ) -add_executable(connection_colors ${CPPS}) +add_executable(connection_colors ${CPPS} ${HPPS}) target_link_libraries(connection_colors nodes) diff --git a/examples/example2/CMakeLists.txt b/examples/example2/CMakeLists.txt index ae1f34dcf..2c8a980ac 100644 --- a/examples/example2/CMakeLists.txt +++ b/examples/example2/CMakeLists.txt @@ -1,5 +1,6 @@ file(GLOB_RECURSE CPPS ./*.cpp ) +file(GLOB_RECURSE HPPS ./*.hpp ) -add_executable(example2 ${CPPS}) +add_executable(example2 ${CPPS} ${HPPS}) target_link_libraries(example2 nodes) diff --git a/examples/images/CMakeLists.txt b/examples/images/CMakeLists.txt index c775b0986..cf2e8d4bc 100644 --- a/examples/images/CMakeLists.txt +++ b/examples/images/CMakeLists.txt @@ -1,5 +1,6 @@ file(GLOB_RECURSE CPPS ./*.cpp ) +file(GLOB_RECURSE HPPS ./*.hpp ) -add_executable(images ${CPPS}) +add_executable(images ${CPPS} ${HPPS}) target_link_libraries(images nodes) diff --git a/examples/styles/CMakeLists.txt b/examples/styles/CMakeLists.txt index 9c9e10c96..94a0b3965 100644 --- a/examples/styles/CMakeLists.txt +++ b/examples/styles/CMakeLists.txt @@ -1,5 +1,6 @@ file(GLOB_RECURSE CPPS ./*.cpp ) +file(GLOB_RECURSE HPPS ./*.hpp ) -add_executable(styles ${CPPS}) +add_executable(styles ${CPPS} ${HPPS}) target_link_libraries(styles nodes) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0c4a99a24..a225ef22e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,6 +12,9 @@ add_executable(test_nodes src/TestDataModelRegistry.cpp src/TestFlowScene.cpp src/TestNodeGraphicsObject.cpp + include/ApplicationSetup.hpp + include/Stringify.hpp + include/StubNodeDataModel.hpp ) target_include_directories(test_nodes From e0a1ab8414f2bb9e0741e53355f3483e999da668 Mon Sep 17 00:00:00 2001 From: RealXuChe <47659370+RealXuChe@users.noreply.github.com> Date: Tue, 26 Mar 2024 17:19:11 +0800 Subject: [PATCH 35/36] Fix compile definition in CMakeLists for static library --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed54c86a7..8ad47c31b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,7 +154,7 @@ target_link_libraries(nodes target_compile_definitions(nodes PUBLIC - NODE_EDITOR_SHARED + $, NODE_EDITOR_SHARED, NODE_EDITOR_STATIC> PRIVATE NODE_EDITOR_EXPORTS #NODE_DEBUG_DRAWING From a67b8f273b0e5feb5fa14e02b860895629064bbb Mon Sep 17 00:00:00 2001 From: Xu Che <47659370+RealXuChe@users.noreply.github.com> Date: Tue, 30 Apr 2024 14:04:29 +0800 Subject: [PATCH 36/36] Fix compile definition in CMakeLists for static library --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ad47c31b..6917b22c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,7 +154,7 @@ target_link_libraries(nodes target_compile_definitions(nodes PUBLIC - $, NODE_EDITOR_SHARED, NODE_EDITOR_STATIC> + $, NODE_EDITOR_SHARED, NODE_EDITOR_STATIC> PRIVATE NODE_EDITOR_EXPORTS #NODE_DEBUG_DRAWING