delete legacy editor
|
|
@ -1,200 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.11)
|
|
||||||
|
|
||||||
project(story-editor VERSION 0.1 LANGUAGES CXX C)
|
|
||||||
|
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
|
||||||
|
|
||||||
set(CMAKE_AUTOUIC ON)
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
|
||||||
set(CMAKE_AUTORCC ON)
|
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets OpenGLWidgets Multimedia OpenGL)
|
|
||||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets Gui OpenGLWidgets Multimedia OpenGL )
|
|
||||||
|
|
||||||
set(PROJECT_SOURCES
|
|
||||||
src/main.cpp
|
|
||||||
src/main_window.h
|
|
||||||
src/main_window.cpp
|
|
||||||
src/toolbar.h
|
|
||||||
src/toolbar.cpp
|
|
||||||
src/ost-editor.qrc
|
|
||||||
src/story_project.cpp
|
|
||||||
src/story_project.h
|
|
||||||
src/media_node_model.h
|
|
||||||
src/media_node_model.cpp
|
|
||||||
src/story_graph_model.cpp
|
|
||||||
src/story_graph_model.h
|
|
||||||
src/story_node_base.cpp
|
|
||||||
src/story_node_base.h
|
|
||||||
src/story_graph_scene.h
|
|
||||||
src/story_graph_scene.cpp
|
|
||||||
src/resource_model.h
|
|
||||||
src/resource_model.cpp
|
|
||||||
src/resources_dock.h
|
|
||||||
src/resources_dock.cpp
|
|
||||||
src/script_editor_dock.h
|
|
||||||
src/script_editor_dock.cpp
|
|
||||||
src/memory_view_dock.h
|
|
||||||
src/memory_view_dock.cpp
|
|
||||||
src/osthmi_dock.h
|
|
||||||
src/osthmi_dock.cpp
|
|
||||||
src/log_dock.h
|
|
||||||
src/log_dock.cpp
|
|
||||||
src/vm_dock.h
|
|
||||||
src/vm_dock.cpp
|
|
||||||
src/new_project_dialog.h
|
|
||||||
src/new_project_dialog.cpp
|
|
||||||
src/dock_widget_base.h
|
|
||||||
src/dock_widget_base.cpp
|
|
||||||
src/code_editor.h
|
|
||||||
src/code_editor.cpp
|
|
||||||
src/graph.h
|
|
||||||
src/graph.cpp
|
|
||||||
src/event_node_model.h
|
|
||||||
src/event_node_model.cpp
|
|
||||||
src/highlighter.h
|
|
||||||
src/highlighter.cpp
|
|
||||||
src/dr_mp3.h
|
|
||||||
src/media_converter.h
|
|
||||||
src/media_converter.cpp
|
|
||||||
src/ost-hmi.ui
|
|
||||||
src/ost-vm.ui
|
|
||||||
src/ost-data.ui
|
|
||||||
src/ost-script.ui
|
|
||||||
src/ost-resources.ui
|
|
||||||
src/ost-log.ui
|
|
||||||
src/about.ui
|
|
||||||
src/media-node.ui
|
|
||||||
src/event-node.ui
|
|
||||||
src/choose-file.ui
|
|
||||||
src/new-project.ui
|
|
||||||
../software/chip32/chip32_assembler.cpp
|
|
||||||
../software/chip32/chip32_vm.c
|
|
||||||
../software/library/miniaudio.c
|
|
||||||
../software/library/miniaudio.h
|
|
||||||
)
|
|
||||||
|
|
||||||
include_directories(../software/chip32)
|
|
||||||
include_directories(../software/library)
|
|
||||||
|
|
||||||
add_subdirectory(QHexView)
|
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
|
||||||
|
|
||||||
set(NODEEDITOR_SOURCE_FILES
|
|
||||||
./nodeeditor/src/AbstractGraphModel.cpp
|
|
||||||
./nodeeditor/src/AbstractNodeGeometry.cpp
|
|
||||||
./nodeeditor/src/BasicGraphicsScene.cpp
|
|
||||||
./nodeeditor/src/ConnectionGraphicsObject.cpp
|
|
||||||
./nodeeditor/src/ConnectionPainter.cpp
|
|
||||||
./nodeeditor/src/ConnectionState.cpp
|
|
||||||
./nodeeditor/src/ConnectionStyle.cpp
|
|
||||||
./nodeeditor/src/DataFlowGraphModel.cpp
|
|
||||||
./nodeeditor/src/DataFlowGraphicsScene.cpp
|
|
||||||
./nodeeditor/src/DefaultHorizontalNodeGeometry.cpp
|
|
||||||
./nodeeditor/src/DefaultVerticalNodeGeometry.cpp
|
|
||||||
./nodeeditor/src/Definitions.cpp
|
|
||||||
./nodeeditor/src/GraphicsView.cpp
|
|
||||||
./nodeeditor/src/GraphicsViewStyle.cpp
|
|
||||||
./nodeeditor/src/NodeDelegateModelRegistry.cpp
|
|
||||||
./nodeeditor/src/NodeConnectionInteraction.cpp
|
|
||||||
./nodeeditor/src/NodeDelegateModel.cpp
|
|
||||||
./nodeeditor/src/NodeGraphicsObject.cpp
|
|
||||||
./nodeeditor/src/DefaultNodePainter.cpp
|
|
||||||
./nodeeditor/src/NodeState.cpp
|
|
||||||
./nodeeditor/src/NodeStyle.cpp
|
|
||||||
./nodeeditor/src/StyleCollection.cpp
|
|
||||||
./nodeeditor/src/UndoCommands.cpp
|
|
||||||
./nodeeditor/src/locateNode.cpp
|
|
||||||
./nodeeditor/resources/resources.qrc
|
|
||||||
)
|
|
||||||
|
|
||||||
set(NODEEDITOR_HEADER_FILES
|
|
||||||
./nodeeditor/include/QtNodes/internal/AbstractGraphModel.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/AbstractNodeGeometry.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/AbstractNodePainter.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/BasicGraphicsScene.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/Compiler.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/ConnectionGraphicsObject.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/ConnectionIdHash.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/ConnectionIdUtils.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/ConnectionState.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/ConnectionStyle.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/DataFlowGraphicsScene.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/DataFlowGraphModel.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/DefaultNodePainter.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/Definitions.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/Export.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/GraphicsView.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/GraphicsViewStyle.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/locateNode.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/NodeData.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/NodeDelegateModel.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/NodeDelegateModelRegistry.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/NodeGraphicsObject.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/NodeState.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/NodeStyle.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/OperatingSystem.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/QStringStdHash.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/QUuidStdHash.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/Serializable.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/Style.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/StyleCollection.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/ConnectionPainter.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/NodeConnectionInteraction.hpp
|
|
||||||
./nodeeditor/include/QtNodes/internal/UndoCommands.hpp
|
|
||||||
)
|
|
||||||
|
|
||||||
if (WIN32)
|
|
||||||
list(APPEND PROJECT_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/icon.rc")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
qt_add_executable(${PROJECT_NAME}
|
|
||||||
MANUAL_FINALIZATION
|
|
||||||
${PROJECT_SOURCES}
|
|
||||||
${NODEEDITOR_SOURCE_FILES}
|
|
||||||
${NODEEDITOR_HEADER_FILES}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_definitions(${PROJECT_NAME} PUBLIC NODE_EDITOR_STATIC)
|
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC nodeeditor/include nodeeditor/include/QtNodes/internal)
|
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC
|
|
||||||
Qt${QT_VERSION_MAJOR}::Widgets
|
|
||||||
Qt${QT_VERSION_MAJOR}::Core
|
|
||||||
Qt${QT_VERSION_MAJOR}::Widgets
|
|
||||||
Qt${QT_VERSION_MAJOR}::Gui
|
|
||||||
Qt${QT_VERSION_MAJOR}::OpenGLWidgets
|
|
||||||
Qt${QT_VERSION_MAJOR}::OpenGL
|
|
||||||
Qt${QT_VERSION_MAJOR}::Multimedia
|
|
||||||
QHexView
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (UNIX)
|
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC dl)
|
|
||||||
endif (UNIX)
|
|
||||||
|
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
|
||||||
MACOSX_BUNDLE_GUI_IDENTIFIER storyeditor.d8s.eu
|
|
||||||
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
|
||||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
|
||||||
MACOSX_BUNDLE TRUE
|
|
||||||
WIN32_EXECUTABLE TRUE
|
|
||||||
)
|
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME}
|
|
||||||
BUNDLE DESTINATION .
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
||||||
|
|
||||||
if(QT_VERSION_MAJOR EQUAL 6)
|
|
||||||
qt_finalize_executable(${PROJECT_NAME})
|
|
||||||
endif()
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
|
@ -1,131 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
version="1.1"
|
|
||||||
id="svg4"
|
|
||||||
sodipodi:docname="build.svg"
|
|
||||||
inkscape:export-filename="/mnt/data/git/OpenStoryTeller/software/ost_editor/assets/build.png"
|
|
||||||
inkscape:export-xdpi="599.172"
|
|
||||||
inkscape:export-ydpi="599.172"
|
|
||||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs8" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview6"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="27.125401"
|
|
||||||
inkscape:cx="9.769441"
|
|
||||||
inkscape:cy="9.5298132"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1136"
|
|
||||||
inkscape:window-x="3840"
|
|
||||||
inkscape:window-y="40"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg4" />
|
|
||||||
<g
|
|
||||||
id="g1850"
|
|
||||||
transform="translate(0.70835287,0.93011029)">
|
|
||||||
<path
|
|
||||||
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="M 3.2156211,6.5613148 V 18.525394 H 19.075779 V 6.5677311"
|
|
||||||
id="path954" />
|
|
||||||
<path
|
|
||||||
sodipodi:type="star"
|
|
||||||
style="opacity:0.998;fill:#000000;stroke:none;stroke-width:1.88976;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:4, 4;paint-order:fill markers stroke"
|
|
||||||
id="path1459"
|
|
||||||
inkscape:flatsided="true"
|
|
||||||
sodipodi:sides="3"
|
|
||||||
sodipodi:cx="11.1457"
|
|
||||||
sodipodi:cy="5.4293532"
|
|
||||||
sodipodi:r1="4.4335914"
|
|
||||||
sodipodi:r2="2.2167957"
|
|
||||||
sodipodi:arg1="1.5707963"
|
|
||||||
sodipodi:arg2="2.6179939"
|
|
||||||
inkscape:rounded="0"
|
|
||||||
inkscape:randomized="0"
|
|
||||||
d="m 11.145701,9.8629446 -3.8396034,-6.6503869 7.6792054,-2e-7 z"
|
|
||||||
inkscape:transform-center-y="1.108398" />
|
|
||||||
<rect
|
|
||||||
style="opacity:0.998;fill:#000000;stroke:none;stroke-width:0.781951;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1.65513, 1.65513;paint-order:fill markers stroke"
|
|
||||||
id="rect1563"
|
|
||||||
width="1.2475514"
|
|
||||||
height="1.2941761"
|
|
||||||
x="6.0001993"
|
|
||||||
y="11.433697"
|
|
||||||
rx="0"
|
|
||||||
ry="0" />
|
|
||||||
<rect
|
|
||||||
style="opacity:0.998;fill:#000000;stroke:none;stroke-width:0.781951;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1.65513, 1.65513;paint-order:fill markers stroke"
|
|
||||||
id="rect1563-3"
|
|
||||||
width="1.2475514"
|
|
||||||
height="1.2941761"
|
|
||||||
x="10.620297"
|
|
||||||
y="11.629229"
|
|
||||||
rx="0"
|
|
||||||
ry="0" />
|
|
||||||
<rect
|
|
||||||
style="opacity:0.998;fill:#000000;stroke:none;stroke-width:0.781951;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1.65513, 1.65513;paint-order:fill markers stroke"
|
|
||||||
id="rect1563-6"
|
|
||||||
width="1.2475514"
|
|
||||||
height="1.2941761"
|
|
||||||
x="9.0350676"
|
|
||||||
y="14.689091"
|
|
||||||
rx="0"
|
|
||||||
ry="0" />
|
|
||||||
<rect
|
|
||||||
style="opacity:0.998;fill:#000000;stroke:none;stroke-width:0.781951;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1.65513, 1.65513;paint-order:fill markers stroke"
|
|
||||||
id="rect1563-7"
|
|
||||||
width="1.2475514"
|
|
||||||
height="1.2941761"
|
|
||||||
x="14.12255"
|
|
||||||
y="15.278944"
|
|
||||||
rx="0"
|
|
||||||
ry="0" />
|
|
||||||
<rect
|
|
||||||
style="opacity:0.998;fill:#000000;stroke:none;stroke-width:0.781951;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1.65513, 1.65513;paint-order:fill markers stroke"
|
|
||||||
id="rect1563-5"
|
|
||||||
width="1.2475514"
|
|
||||||
height="1.2941761"
|
|
||||||
x="15.634048"
|
|
||||||
y="11.481765"
|
|
||||||
rx="0"
|
|
||||||
ry="0" />
|
|
||||||
<rect
|
|
||||||
style="opacity:0.998;fill:#000000;stroke:none;stroke-width:0.781951;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1.65513, 1.65513;paint-order:fill markers stroke"
|
|
||||||
id="rect1563-35"
|
|
||||||
width="1.2475514"
|
|
||||||
height="1.2941761"
|
|
||||||
x="14.491208"
|
|
||||||
y="8.9748898"
|
|
||||||
rx="0"
|
|
||||||
ry="0" />
|
|
||||||
<rect
|
|
||||||
style="opacity:0.998;fill:#000000;stroke:none;stroke-width:0.781951;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1.65513, 1.65513;paint-order:fill markers stroke"
|
|
||||||
id="rect1563-62"
|
|
||||||
width="1.2475514"
|
|
||||||
height="1.2941761"
|
|
||||||
x="6.5650582"
|
|
||||||
y="14.504762"
|
|
||||||
rx="0"
|
|
||||||
ry="0" />
|
|
||||||
<rect
|
|
||||||
style="opacity:0.998;fill:#000000;stroke:none;stroke-width:0.781951;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1.65513, 1.65513;paint-order:fill markers stroke"
|
|
||||||
id="rect1563-9"
|
|
||||||
width="1.2475514"
|
|
||||||
height="1.2941761"
|
|
||||||
x="7.8922276"
|
|
||||||
y="9.9702663"
|
|
||||||
rx="0"
|
|
||||||
ry="0" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3,16.74L7.76,12L3,7.26L7.26,3L12,7.76L16.74,3L21,7.26L16.24,12L21,16.74L16.74,21L12,16.24L7.26,21L3,16.74M12,13.41L16.74,18.16L18.16,16.74L13.41,12L18.16,7.26L16.74,5.84L12,10.59L7.26,5.84L5.84,7.26L10.59,12L5.84,16.74L7.26,18.16L12,13.41Z" /></svg>
|
|
||||||
|
Before Width: | Height: | Size: 319 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
|
@ -1,39 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
version="1.1"
|
|
||||||
id="svg4"
|
|
||||||
sodipodi:docname="file-document-plus-outline.svg"
|
|
||||||
inkscape:export-filename="file-document-plus-outline.png"
|
|
||||||
inkscape:export-xdpi="599.172"
|
|
||||||
inkscape:export-ydpi="599.172"
|
|
||||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs8" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview6"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="35.06856"
|
|
||||||
inkscape:cx="4.6908114"
|
|
||||||
inkscape:cy="10.707597"
|
|
||||||
inkscape:window-width="2112"
|
|
||||||
inkscape:window-height="1436"
|
|
||||||
inkscape:window-x="1352"
|
|
||||||
inkscape:window-y="380"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="svg4" />
|
|
||||||
<path
|
|
||||||
d="M23 18H20V15H18V18H15V20H18V23H20V20H23M6 2C4.89 2 4 2.9 4 4V20C4 21.11 4.89 22 6 22H13.81C13.45 21.38 13.2 20.7 13.08 20H6V4H13V9H18V13.08C18.33 13.03 18.67 13 19 13C19.34 13 19.67 13.03 20 13.08V8L14 2M8 12V14H16V12M8 16V18H13V16Z"
|
|
||||||
id="path2" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M5,3A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5.5L18.5,3H17V9A1,1 0 0,1 16,10H8A1,1 0 0,1 7,9V3H5M12,4V9H15V4H12M7,12H17A1,1 0 0,1 18,13V19H6V13A1,1 0 0,1 7,12Z" /></svg>
|
|
||||||
|
Before Width: | Height: | Size: 249 B |
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.1,10L4,18V8H21A2,2 0 0,0 19,6H12L10,4H4A2,2 0 0,0 2,6V18A2,2 0 0,0 4,20H19C19.9,20 20.7,19.4 20.9,18.5L23.2,10H6.1M19,18H6L7.6,12H20.6L19,18Z" /></svg>
|
|
||||||
|
Before Width: | Height: | Size: 223 B |
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19,20H4C2.89,20 2,19.1 2,18V6C2,4.89 2.89,4 4,4H10L12,6H19A2,2 0 0,1 21,8H21L4,8V18L6.14,10H23.21L20.93,18.5C20.7,19.37 19.92,20 19,20Z" /></svg>
|
|
||||||
|
Before Width: | Height: | Size: 215 B |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 7 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
|
@ -1,41 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
version="1.1"
|
|
||||||
id="svg4"
|
|
||||||
sodipodi:docname="play-circle-green.svg"
|
|
||||||
inkscape:export-filename="play-circle-green.png"
|
|
||||||
inkscape:export-xdpi="599.172"
|
|
||||||
inkscape:export-ydpi="599.172"
|
|
||||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs8" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview6"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="38.514415"
|
|
||||||
inkscape:cx="8.0619166"
|
|
||||||
inkscape:cy="14.708779"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1136"
|
|
||||||
inkscape:window-x="3840"
|
|
||||||
inkscape:window-y="40"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg4"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:deskcolor="#d1d1d1" />
|
|
||||||
<path
|
|
||||||
d="M10,16.5V7.5L16,12M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"
|
|
||||||
id="path2"
|
|
||||||
style="fill:#4e9a06" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
|
@ -1,38 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
version="1.1"
|
|
||||||
id="svg4"
|
|
||||||
sodipodi:docname="play-circle.svg"
|
|
||||||
inkscape:export-filename="/mnt/data/git/OpenStoryTeller/software/ost_editor/assets/play-circle.png"
|
|
||||||
inkscape:export-xdpi="599.172"
|
|
||||||
inkscape:export-ydpi="599.172"
|
|
||||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs8" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview6"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="15.153839"
|
|
||||||
inkscape:cx="2.5406103"
|
|
||||||
inkscape:cy="15.738586"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1136"
|
|
||||||
inkscape:window-x="3840"
|
|
||||||
inkscape:window-y="40"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg4" />
|
|
||||||
<path
|
|
||||||
d="M10,16.5V7.5L16,12M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"
|
|
||||||
id="path2" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 7 KiB |
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M14,3.23V5.29C16.89,6.15 19,8.83 19,12C19,15.17 16.89,17.84 14,18.7V20.77C18,19.86 21,16.28 21,12C21,7.72 18,4.14 14,3.23M16.5,12C16.5,10.23 15.5,8.71 14,7.97V16C15.5,15.29 16.5,13.76 16.5,12M3,9V15H7L12,20V4L7,9H3Z" /></svg>
|
|
||||||
|
Before Width: | Height: | Size: 294 B |
|
Before Width: | Height: | Size: 2.3 KiB |
|
|
@ -1,38 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
version="1.1"
|
|
||||||
id="svg4"
|
|
||||||
sodipodi:docname="volume-off.svg"
|
|
||||||
inkscape:export-filename="/mnt/data/git/open-story-teller/story-editor/assets/volume-off.png"
|
|
||||||
inkscape:export-xdpi="599.172"
|
|
||||||
inkscape:export-ydpi="599.172"
|
|
||||||
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<defs
|
|
||||||
id="defs8" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview6"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="36.375"
|
|
||||||
inkscape:cx="10.62543"
|
|
||||||
inkscape:cy="12"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1136"
|
|
||||||
inkscape:window-x="3840"
|
|
||||||
inkscape:window-y="40"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="svg4" />
|
|
||||||
<path
|
|
||||||
d="M12,4L9.91,6.09L12,8.18M4.27,3L3,4.27L7.73,9H3V15H7L12,20V13.27L16.25,17.53C15.58,18.04 14.83,18.46 14,18.7V20.77C15.38,20.45 16.63,19.82 17.68,18.96L19.73,21L21,19.73L12,10.73M19,12C19,12.94 18.8,13.82 18.46,14.64L19.97,16.15C20.62,14.91 21,13.5 21,12C21,7.72 18,4.14 14,3.23V5.29C16.89,6.15 19,8.83 19,12M16.5,12C16.5,10.23 15.5,8.71 14,7.97V10.18L16.45,12.63C16.5,12.43 16.5,12.21 16.5,12Z"
|
|
||||||
id="path2" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 290 KiB |
|
|
@ -1,96 +0,0 @@
|
||||||
; jump over the data, to our entry label
|
|
||||||
jump .entry
|
|
||||||
|
|
||||||
; Constant elements are separated by commas
|
|
||||||
$imageBird DC8, "example.bmp", 8 ; string of chars, followed by one byte
|
|
||||||
$soundChoice DC8, "choose1.snd"
|
|
||||||
$yaya DC8, "yaya.bmp"
|
|
||||||
$rabbit DC8, "rabbit.bmp"
|
|
||||||
$someConstant DC32, 12456789
|
|
||||||
|
|
||||||
; Liste des noeuds à appeler
|
|
||||||
$ChoiceObject DC32, 2, .MEDIA_02, .MEDIA_03
|
|
||||||
|
|
||||||
; DVsxx to declare a variable in RAM, followed by the number of elements
|
|
||||||
$MyArray DV8, 10 ; array of 10 bytes
|
|
||||||
$RamData1 DV32, 1 ; one 32-bit integer
|
|
||||||
$ChoiceMem DV32, 10 ; 10 elements for the choices, to be generated
|
|
||||||
|
|
||||||
; label definition
|
|
||||||
.entry: ;; comment here should work
|
|
||||||
|
|
||||||
|
|
||||||
; Syscall test: show image and play sound
|
|
||||||
lcons r0, $imageBird ; image name address in ROM located in R0 (null terminated)
|
|
||||||
lcons r1, $soundChoice ; set to 0 if no sound
|
|
||||||
syscall 1
|
|
||||||
lcons r0, $ChoiceObject
|
|
||||||
jump .media ; no return possible, so a jump is enough
|
|
||||||
|
|
||||||
; Generic media choice manager
|
|
||||||
.media:
|
|
||||||
; Les adresses des différents medias sont dans la stack
|
|
||||||
; Arguments:
|
|
||||||
; r0: address d'une structure de type "media choice"
|
|
||||||
; Local:
|
|
||||||
; t0: loop counter
|
|
||||||
; t1: increment 1
|
|
||||||
; t2: increment 4
|
|
||||||
; t3: current media address
|
|
||||||
|
|
||||||
.media_loop_start:
|
|
||||||
load t0, @r0, 4 ; Le premier élément est le nombre de choix possibles, t0 = 3 (exemple)
|
|
||||||
lcons t1, 1
|
|
||||||
lcons t2, 4
|
|
||||||
mov t3, r0
|
|
||||||
.media_loop:
|
|
||||||
add t3, t2 ; @++
|
|
||||||
|
|
||||||
|
|
||||||
; ------- On appelle un autre media node
|
|
||||||
push r0 ; save r0
|
|
||||||
load r0, @t3, 4 ; r0 = content in ram at address in T4
|
|
||||||
call r0
|
|
||||||
pop r0
|
|
||||||
; TODO: wait for event
|
|
||||||
|
|
||||||
sub t0, t1 ; i--
|
|
||||||
skipnz t0 ; if (r0) goto start_loop;
|
|
||||||
jump .media_loop_start
|
|
||||||
jump .media_loop
|
|
||||||
|
|
||||||
.MEDIA_02:
|
|
||||||
lcons r0, $yaya ; image name address in ROM located in R0 (null terminated)
|
|
||||||
lcons r1, $soundChoice ; set to 0 if no sound
|
|
||||||
syscall 1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.MEDIA_03:
|
|
||||||
lcons r0, $rabbit
|
|
||||||
lcons r1, $soundChoice
|
|
||||||
syscall 1
|
|
||||||
ret
|
|
||||||
|
|
||||||
.SYSCALL_TEST:
|
|
||||||
; syscall test: wait for event
|
|
||||||
lcons r0, 0xFF ; wait for all event, blocking
|
|
||||||
syscall 2
|
|
||||||
|
|
||||||
; We create a stupid loop just for RAM variable testing
|
|
||||||
|
|
||||||
lcons r0, 4 ; prepare loop: 4 iterations
|
|
||||||
lcons r6, $RamData1 ; store address to R6
|
|
||||||
store @r6, r0, 4 ; save R0 in RAM
|
|
||||||
lcons r1, 1
|
|
||||||
.loop:
|
|
||||||
load r0, @r6, 4 ; load this variable
|
|
||||||
sub r0, r1
|
|
||||||
store @r6, r0, 4 ; save R0 in RAM
|
|
||||||
skipz r0 ; skip loop if R0 == 0
|
|
||||||
jump .loop
|
|
||||||
|
|
||||||
|
|
||||||
mov r0, r2 ; copy R2 into R0 (blank space between , and R2)
|
|
||||||
mov R0,R2 ; copy R2 into R0 (NO blank space between , and R2)
|
|
||||||
|
|
||||||
halt
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
IDI_ICON1 ICON DISCARDABLE "story-editor-logo.ico"
|
|
||||||
|
|
@ -1,274 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.8)
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
project(QtNodesLibrary CXX)
|
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
|
||||||
|
|
||||||
set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
|
|
||||||
set(CMAKE_DISABLE_SOURCE_CHANGES ON)
|
|
||||||
set(OpenGL_GL_PREFERENCE LEGACY)
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
||||||
|
|
||||||
get_directory_property(_has_parent PARENT_DIRECTORY)
|
|
||||||
if(_has_parent)
|
|
||||||
set(is_root_project OFF)
|
|
||||||
else()
|
|
||||||
set(is_root_project ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(QT_NODES_DEVELOPER_DEFAULTS "${is_root_project}" CACHE BOOL "Turns on default settings for development of QtNodes")
|
|
||||||
|
|
||||||
option(BUILD_TESTING "Build tests" "${QT_NODES_DEVELOPER_DEFAULTS}")
|
|
||||||
option(BUILD_EXAMPLES "Build Examples" "${QT_NODES_DEVELOPER_DEFAULTS}")
|
|
||||||
option(BUILD_DOCS "Build Documentation" "${QT_NODES_DEVELOPER_DEFAULTS}")
|
|
||||||
option(BUILD_SHARED_LIBS "Build as shared library" ON)
|
|
||||||
option(BUILD_DEBUG_POSTFIX_D "Append d suffix to debug libraries" OFF)
|
|
||||||
option(QT_NODES_FORCE_TEST_COLOR "Force colorized unit test output" OFF)
|
|
||||||
|
|
||||||
enable_testing()
|
|
||||||
|
|
||||||
if(QT_NODES_DEVELOPER_DEFAULTS)
|
|
||||||
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")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_DEBUG_POSTFIX_D)
|
|
||||||
set(CMAKE_DEBUG_POSTFIX "d")
|
|
||||||
set(CMAKE_RELEASE_POSTFIX "")
|
|
||||||
set(CMAKE_RELWITHDEBINFO_POSTFIX "rd")
|
|
||||||
set(CMAKE_MINSIZEREL_POSTFIX "s")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_subdirectory(external)
|
|
||||||
|
|
||||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
|
|
||||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Widgets Gui OpenGL)
|
|
||||||
message(STATUS "QT_VERSION: ${QT_VERSION}, QT_DIR: ${QT_DIR}")
|
|
||||||
|
|
||||||
if (${QT_VERSION} VERSION_LESS 5.11.0)
|
|
||||||
message(FATAL_ERROR "Requires qt version >= 5.11.0, Your current version is ${QT_VERSION}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (${QT_VERSION_MAJOR} EQUAL 6)
|
|
||||||
qt_add_resources(RESOURCES ./resources/resources.qrc)
|
|
||||||
else()
|
|
||||||
qt5_add_resources(RESOURCES ./resources/resources.qrc)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Unfortunately, as we have a split include/src, AUTOMOC doesn't work.
|
|
||||||
# We'll have to manually specify some files
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
|
||||||
|
|
||||||
set(CPP_SOURCE_FILES
|
|
||||||
src/AbstractGraphModel.cpp
|
|
||||||
src/AbstractNodeGeometry.cpp
|
|
||||||
src/BasicGraphicsScene.cpp
|
|
||||||
src/ConnectionGraphicsObject.cpp
|
|
||||||
src/ConnectionPainter.cpp
|
|
||||||
src/ConnectionState.cpp
|
|
||||||
src/ConnectionStyle.cpp
|
|
||||||
src/DataFlowGraphModel.cpp
|
|
||||||
src/DataFlowGraphicsScene.cpp
|
|
||||||
src/DefaultHorizontalNodeGeometry.cpp
|
|
||||||
src/DefaultVerticalNodeGeometry.cpp
|
|
||||||
src/Definitions.cpp
|
|
||||||
src/GraphicsView.cpp
|
|
||||||
src/GraphicsViewStyle.cpp
|
|
||||||
src/NodeDelegateModelRegistry.cpp
|
|
||||||
src/NodeConnectionInteraction.cpp
|
|
||||||
src/NodeDelegateModel.cpp
|
|
||||||
src/NodeGraphicsObject.cpp
|
|
||||||
src/DefaultNodePainter.cpp
|
|
||||||
src/NodeState.cpp
|
|
||||||
src/NodeStyle.cpp
|
|
||||||
src/StyleCollection.cpp
|
|
||||||
src/UndoCommands.cpp
|
|
||||||
src/locateNode.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
set(HPP_HEADER_FILES
|
|
||||||
include/QtNodes/internal/AbstractGraphModel.hpp
|
|
||||||
include/QtNodes/internal/AbstractNodeGeometry.hpp
|
|
||||||
include/QtNodes/internal/AbstractNodePainter.hpp
|
|
||||||
include/QtNodes/internal/BasicGraphicsScene.hpp
|
|
||||||
include/QtNodes/internal/Compiler.hpp
|
|
||||||
include/QtNodes/internal/ConnectionGraphicsObject.hpp
|
|
||||||
include/QtNodes/internal/ConnectionIdHash.hpp
|
|
||||||
include/QtNodes/internal/ConnectionIdUtils.hpp
|
|
||||||
include/QtNodes/internal/ConnectionState.hpp
|
|
||||||
include/QtNodes/internal/ConnectionStyle.hpp
|
|
||||||
include/QtNodes/internal/DataFlowGraphicsScene.hpp
|
|
||||||
include/QtNodes/internal/DataFlowGraphModel.hpp
|
|
||||||
include/QtNodes/internal/DefaultNodePainter.hpp
|
|
||||||
include/QtNodes/internal/Definitions.hpp
|
|
||||||
include/QtNodes/internal/Export.hpp
|
|
||||||
include/QtNodes/internal/GraphicsView.hpp
|
|
||||||
include/QtNodes/internal/GraphicsViewStyle.hpp
|
|
||||||
include/QtNodes/internal/locateNode.hpp
|
|
||||||
include/QtNodes/internal/NodeData.hpp
|
|
||||||
include/QtNodes/internal/NodeDelegateModel.hpp
|
|
||||||
include/QtNodes/internal/NodeDelegateModelRegistry.hpp
|
|
||||||
include/QtNodes/internal/NodeGraphicsObject.hpp
|
|
||||||
include/QtNodes/internal/NodeState.hpp
|
|
||||||
include/QtNodes/internal/NodeStyle.hpp
|
|
||||||
include/QtNodes/internal/OperatingSystem.hpp
|
|
||||||
include/QtNodes/internal/QStringStdHash.hpp
|
|
||||||
include/QtNodes/internal/QUuidStdHash.hpp
|
|
||||||
include/QtNodes/internal/Serializable.hpp
|
|
||||||
include/QtNodes/internal/Style.hpp
|
|
||||||
include/QtNodes/internal/StyleCollection.hpp
|
|
||||||
include/QtNodes/internal/ConnectionPainter.hpp
|
|
||||||
include/QtNodes/internal/DefaultHorizontalNodeGeometry.hpp
|
|
||||||
include/QtNodes/internal/DefaultVerticalNodeGeometry.hpp
|
|
||||||
include/QtNodes/internal/NodeConnectionInteraction.hpp
|
|
||||||
include/QtNodes/internal/UndoCommands.hpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# If we want to give the option to build a static library,
|
|
||||||
# set BUILD_SHARED_LIBS option to OFF
|
|
||||||
add_library(QtNodes
|
|
||||||
${CPP_SOURCE_FILES}
|
|
||||||
${HPP_HEADER_FILES}
|
|
||||||
${RESOURCES}
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(QtNodes::QtNodes ALIAS QtNodes)
|
|
||||||
|
|
||||||
|
|
||||||
target_include_directories(QtNodes
|
|
||||||
PUBLIC
|
|
||||||
$<INSTALL_INTERFACE:include>
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
|
||||||
PRIVATE
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/QtNodes/internal>
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(QtNodes
|
|
||||||
PUBLIC
|
|
||||||
Qt${QT_VERSION_MAJOR}::Core
|
|
||||||
Qt${QT_VERSION_MAJOR}::Widgets
|
|
||||||
Qt${QT_VERSION_MAJOR}::Gui
|
|
||||||
Qt${QT_VERSION_MAJOR}::OpenGL
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_definitions(QtNodes
|
|
||||||
PUBLIC
|
|
||||||
NODE_EDITOR_SHARED
|
|
||||||
PRIVATE
|
|
||||||
NODE_EDITOR_EXPORTS
|
|
||||||
#NODE_DEBUG_DRAWING
|
|
||||||
QT_NO_KEYWORDS
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
target_compile_options(QtNodes
|
|
||||||
PRIVATE
|
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:/W4 /wd4127 /EHsc /utf-8>
|
|
||||||
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra>
|
|
||||||
$<$<CXX_COMPILER_ID:AppleClang>:-Wall -Wextra -Werror>
|
|
||||||
)
|
|
||||||
if(NOT "${CMAKE_CXX_SIMULATE_ID}" STREQUAL "MSVC")
|
|
||||||
# Clang-Cl on MSVC identifies as "Clang" but behaves more like MSVC:
|
|
||||||
target_compile_options(QtNodes
|
|
||||||
PRIVATE
|
|
||||||
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra>
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(QT_NODES_DEVELOPER_DEFAULTS)
|
|
||||||
target_compile_features(QtNodes PUBLIC cxx_std_14)
|
|
||||||
set_target_properties(QtNodes PROPERTIES CXX_EXTENSIONS OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
set_target_properties(QtNodes
|
|
||||||
PROPERTIES
|
|
||||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
|
|
||||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
|
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
|
|
||||||
)
|
|
||||||
|
|
||||||
######
|
|
||||||
# Moc
|
|
||||||
##
|
|
||||||
|
|
||||||
file(GLOB_RECURSE HEADERS_TO_MOC include/QtNodes/internal/*.hpp)
|
|
||||||
|
|
||||||
if (${QT_VERSION_MAJOR} EQUAL 6)
|
|
||||||
qt_wrap_cpp(nodes_moc
|
|
||||||
${HEADERS_TO_MOC}
|
|
||||||
TARGET QtNodes
|
|
||||||
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 QtNodes
|
|
||||||
OPTIONS --no-notes # Don't display a note for the headers which don't produce a moc_*.cpp
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_sources(QtNodes PRIVATE ${nodes_moc})
|
|
||||||
|
|
||||||
###########
|
|
||||||
# Examples
|
|
||||||
##
|
|
||||||
|
|
||||||
if(BUILD_EXAMPLES)
|
|
||||||
add_subdirectory(examples)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_DOCS)
|
|
||||||
add_subdirectory(docs)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
##################
|
|
||||||
# Automated Tests
|
|
||||||
##
|
|
||||||
|
|
||||||
if(BUILD_TESTING)
|
|
||||||
#add_subdirectory(test)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
###############
|
|
||||||
# Installation
|
|
||||||
##
|
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/QtNodes)
|
|
||||||
|
|
||||||
install(TARGETS QtNodes
|
|
||||||
EXPORT QtNodesTargets
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
install(DIRECTORY include/
|
|
||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
|
||||||
|
|
||||||
install(EXPORT QtNodesTargets
|
|
||||||
FILE QtNodesTargets.cmake
|
|
||||||
NAMESPACE QtNodes::
|
|
||||||
DESTINATION ${INSTALL_CONFIGDIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
include(CMakePackageConfigHelpers)
|
|
||||||
|
|
||||||
configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/cmake/QtNodesConfig.cmake.in
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/QtNodesConfig.cmake
|
|
||||||
INSTALL_DESTINATION ${INSTALL_CONFIGDIR}
|
|
||||||
)
|
|
||||||
|
|
||||||
install(FILES
|
|
||||||
${CMAKE_CURRENT_BINARY_DIR}/QtNodesConfig.cmake
|
|
||||||
DESTINATION ${INSTALL_CONFIGDIR}
|
|
||||||
)
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
BSD-3-Clause license
|
|
||||||
====================
|
|
||||||
|
|
||||||
Copyright (c) 2022, Dmitry Pinaev
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
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 copyright holder, 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.
|
|
||||||
|
|
@ -1,261 +0,0 @@
|
||||||
QtNodes
|
|
||||||
#######
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
============
|
|
||||||
|
|
||||||
**QtNodes** is conceived as a general-purpose Qt-based library aimed at
|
|
||||||
developing Node Editors for various applications. The library could be used for
|
|
||||||
simple graph visualization and editing or extended further for using the
|
|
||||||
`Dataflow paradigm <https://en.wikipedia.org/wiki/Dataflow_programming>`_ .
|
|
||||||
|
|
||||||
The library is written using the Model-View approach. The whole graph structure
|
|
||||||
is defined by a class derived from ``AbstractGraphModel``. It is possible to
|
|
||||||
create or add Nodes and Connections. The underlying data structures could be of
|
|
||||||
any arbitrary type or representation.
|
|
||||||
|
|
||||||
An instance of ``AbstractGraphModel`` could or could not be attached to
|
|
||||||
specialized ``QGraphicsScene`` and ``QGraphicsView`` objects. I.e. the so-called
|
|
||||||
"headless" `modus operandi` is possible.
|
|
||||||
|
|
||||||
Documentation
|
|
||||||
=============
|
|
||||||
|
|
||||||
`Read the Docs for QtNodes <https://qtnodes.readthedocs.io/>`_
|
|
||||||
|
|
||||||
Warning
|
|
||||||
Many classes were changed in the version ``3.0``. If you had a large project
|
|
||||||
based on ``2.x.x``, make sure you read the documentation first and see the
|
|
||||||
examples before checking out the new code.
|
|
||||||
|
|
||||||
Branches
|
|
||||||
--------
|
|
||||||
|
|
||||||
There are branchses ``v2`` and ``v3`` for versions ``2.x.x`` and ``3.x``
|
|
||||||
respectively. The branch ``master`` contains the latest dev state.
|
|
||||||
|
|
||||||
|
|
||||||
.. contents:: Navigation
|
|
||||||
:depth: 2
|
|
||||||
|
|
||||||
|
|
||||||
Data Flow Paradigm
|
|
||||||
==================
|
|
||||||
|
|
||||||
The extended model class ``DataFlowGraphModel`` allows to register "processing
|
|
||||||
algorithms" represented by nodes and is equipped with a set of Qt's signals and
|
|
||||||
slots for propagating the data though the nodes.
|
|
||||||
|
|
||||||
The node's algorithm is triggered upon arriving of any new input data. The
|
|
||||||
computed result is propagated to the output connections. Each new connection
|
|
||||||
fetches available data and propagates is further. Each change in the source node
|
|
||||||
is immediately propagated through all the connections updating the whole graph.
|
|
||||||
|
|
||||||
|
|
||||||
Supported Environments
|
|
||||||
======================
|
|
||||||
|
|
||||||
Platforms
|
|
||||||
---------
|
|
||||||
|
|
||||||
* Linux (x64, gcc-7.0, clang-7) |ImageLink|_
|
|
||||||
* OSX (Apple Clang - LLVM 3.6) |ImageLink|_
|
|
||||||
|
|
||||||
.. |ImageLink| image:: https://travis-ci.org/paceholder/nodeeditor.svg?branch=master
|
|
||||||
.. _ImageLink: https://travis-ci.org/paceholder/nodeeditor
|
|
||||||
|
|
||||||
|
|
||||||
* Windows (Win32, x64, msvc2017, MinGW 5.3) |AppveyorImage|_
|
|
||||||
|
|
||||||
.. |AppveyorImage| image:: https://ci.appveyor.com/api/projects/status/wxp47wv3uyyiujjw/branch/master?svg=true
|
|
||||||
.. _AppveyorImage: https://ci.appveyor.com/project/paceholder/nodeeditor/branch/master)
|
|
||||||
|
|
||||||
Dependencies
|
|
||||||
------------
|
|
||||||
|
|
||||||
* Qt >5.15
|
|
||||||
* CMake 3.8
|
|
||||||
* Catch2
|
|
||||||
|
|
||||||
|
|
||||||
Current State (v3)
|
|
||||||
==================
|
|
||||||
|
|
||||||
* Model-based graph
|
|
||||||
* Headless mode
|
|
||||||
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 data propagation.
|
|
||||||
* Automatic data propagation built on top of the graph-model code
|
|
||||||
The library could be used for both pure graph visualization purposes and for
|
|
||||||
originally implemented data propagation.
|
|
||||||
* Dynamic ports
|
|
||||||
* Datatype-aware connections
|
|
||||||
* Embedded Qt widgets
|
|
||||||
* One-output to many-input connections
|
|
||||||
* JSON-based interface styles
|
|
||||||
* Saving scenes to JSON files
|
|
||||||
* Custom Node Geometry
|
|
||||||
* Vertical and Horizontal graph layouts
|
|
||||||
* Undo/Redo, Duplication (CTRL+D)
|
|
||||||
|
|
||||||
|
|
||||||
Building
|
|
||||||
========
|
|
||||||
|
|
||||||
Linux
|
|
||||||
-----
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
git clone git@github.com:paceholder/nodeeditor.git
|
|
||||||
cd nodeeditor
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make -j && make install
|
|
||||||
|
|
||||||
|
|
||||||
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`.
|
|
||||||
3. `Build -> Run CMake`
|
|
||||||
4. `Build -> Build All`
|
|
||||||
5. Click the button `Run`
|
|
||||||
|
|
||||||
|
|
||||||
With Cmake using `vcpkg`
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
1. Install `vcpkg`
|
|
||||||
2. Add the following flag in configuration step of `CMake`
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE=<vcpkg_dir>/scripts/buildsystems/scripts/buildsystems/vcpkg.cmake
|
|
||||||
|
|
||||||
|
|
||||||
Help Needed
|
|
||||||
===========
|
|
||||||
|
|
||||||
#. Python wrappring using PySide.
|
|
||||||
#. QML frontend.
|
|
||||||
#. Wirting a ClangFormat config.
|
|
||||||
|
|
||||||
Any suggestions are welcome!
|
|
||||||
|
|
||||||
|
|
||||||
Contribution
|
|
||||||
============
|
|
||||||
|
|
||||||
#. Be polite, respectful and collaborative.
|
|
||||||
#. For submitting a bug:
|
|
||||||
|
|
||||||
#. Describe your environment (Qt version, compiler, OS etc)
|
|
||||||
#. Describe steps to reproduce the issue
|
|
||||||
|
|
||||||
#. For submitting a pull request:
|
|
||||||
|
|
||||||
#. Create a proposal task first. We can come up with a better design together.
|
|
||||||
#. Create a pull-request. If applicable, create a simple example for your
|
|
||||||
problem, describe the changes in details, provide use cases.
|
|
||||||
|
|
||||||
#. For submitting a development request:
|
|
||||||
|
|
||||||
#. Describe your issue in details
|
|
||||||
#. Provide some use cases.
|
|
||||||
|
|
||||||
#. I maintain this probject in my free time, when I am not busy with my work or
|
|
||||||
my family. **If I do not react or do not answer for too long, please ping
|
|
||||||
me**.
|
|
||||||
|
|
||||||
|
|
||||||
Citing
|
|
||||||
======
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
Dmitry Pinaev et al, Qt Nodes, (2022), GitHub repository, https://github.com/paceholder/nodeeditor
|
|
||||||
|
|
||||||
BibTeX::
|
|
||||||
|
|
||||||
@misc{Pinaev2022,
|
|
||||||
author = {Dmitry Pinaev et al},
|
|
||||||
title = {QtNodes. Node Editor},
|
|
||||||
year = {2017},
|
|
||||||
publisher = {GitHub},
|
|
||||||
journal = {GitHub repository},
|
|
||||||
howpublished = {\url{https://github.com/paceholder/nodeeditor}},
|
|
||||||
commit = {877ddb8c447a7a061a5022e9956a3194132e3dd9}
|
|
||||||
}
|
|
||||||
|
|
||||||
Support
|
|
||||||
=======
|
|
||||||
|
|
||||||
If you like the project you could donate me on PayPal |ImagePaypal|_
|
|
||||||
|
|
||||||
.. |ImagePaypal| image:: https://img.shields.io/badge/Donate-PayPal-green.svg
|
|
||||||
.. _ImagePaypal: https://www.paypal.com/paypalme/DmitryPinaev
|
|
||||||
|
|
||||||
|
|
||||||
If you send more than $100, I'll forward $100 to some fund supporting sick
|
|
||||||
children and report to you back.
|
|
||||||
|
|
||||||
|
|
||||||
Thanks
|
|
||||||
======
|
|
||||||
|
|
||||||
The version 3 was released with a generous help of
|
|
||||||
`Davide Faconti <https://github.com/facontidavide>`_
|
|
||||||
|
|
||||||
|
|
||||||
Showcase
|
|
||||||
========
|
|
||||||
|
|
||||||
Youtube videos
|
|
||||||
--------------
|
|
||||||
|
|
||||||
.. image:: https://img.youtube.com/vi/pxMXjSvlOFw/0.jpg
|
|
||||||
:target: https://www.youtube.com/watch?v=pxMXjSvlOFw
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
||||||
.. image:: https://img.youtube.com/vi/i_pB-Y0hCYQ/0.jpg
|
|
||||||
:target: https://www.youtube.com/watch?v=i_pB-Y0hCYQ
|
|
||||||
|
|
||||||
CANdevStudio
|
|
||||||
------------
|
|
||||||
|
|
||||||
`CANdevStudio <https://github.com/GENIVI/CANdevStudio>`_ is a 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.
|
|
||||||
|
|
||||||
|
|
||||||
.. image:: docs/_static/showcase_CANdevStudio.png
|
|
||||||
|
|
||||||
|
|
||||||
Chigraph
|
|
||||||
--------
|
|
||||||
|
|
||||||
`Chigraph <https://github.com/chigraph/chigraph>`_ is a visual programming
|
|
||||||
language for beginners that is unique in that it is an intuitive flow graph:
|
|
||||||
|
|
||||||
.. image:: docs/_static/chigraph.png
|
|
||||||
|
|
||||||
|
|
||||||
It features easy bindings to C/C++, package management, and a cool interface.
|
|
||||||
|
|
||||||
|
|
||||||
Spkgen particle editor
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
`Spkgen <https://github.com/fredakilla/spkgen>`_ is an editor for the SPARK
|
|
||||||
particles engine that uses a node-based interface to create particles effects for
|
|
||||||
games
|
|
||||||
|
|
||||||
.. image:: docs/_static/spkgen.png
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/AbstractGraphModel.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/AbstractNodePainter.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/BasicGraphicsScene.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/ConnectionIdUtils.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/ConnectionStyle.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/DataFlowGraphModel.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/DataFlowGraphicsScene.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/DefaultNodePainter.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/Definitions.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/GraphicsView.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/GraphicsViewStyle.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/NodeData.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/NodeDelegateModel.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/NodeDelegateModelRegistry.hpp"
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
#include "internal/NodeGeometry.hpp"
|
|
||||||
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/NodeState.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/NodeStyle.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/StyleCollection.hpp"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "internal/UndoCommands.hpp"
|
|
||||||
|
|
@ -1,249 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
#include <QtCore/QJsonObject>
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QtCore/QVariant>
|
|
||||||
|
|
||||||
#include "ConnectionIdHash.hpp"
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The central class in the Model-View approach. It delivers all kinds
|
|
||||||
* of information from the backing user data structures that represent
|
|
||||||
* the graph. The class allows to modify the graph structure: create
|
|
||||||
* and remove nodes and connections.
|
|
||||||
*
|
|
||||||
* We use two types of the unique ids for graph manipulations:
|
|
||||||
* - NodeId
|
|
||||||
* - ConnectionId
|
|
||||||
*/
|
|
||||||
class NODE_EDITOR_PUBLIC AbstractGraphModel : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
/// Generates a new unique NodeId.
|
|
||||||
virtual NodeId newNodeId() = 0;
|
|
||||||
|
|
||||||
/// @brief Returns the full set of unique Node Ids.
|
|
||||||
/**
|
|
||||||
* Model creator is responsible for generating unique `unsigned int`
|
|
||||||
* Ids for all the nodes in the graph. From an Id it should be
|
|
||||||
* possible to trace back to the model's internal representation of
|
|
||||||
* the node.
|
|
||||||
*/
|
|
||||||
virtual std::unordered_set<NodeId> allNodeIds() const = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A collection of all input and output connections for the given `nodeId`.
|
|
||||||
*/
|
|
||||||
virtual std::unordered_set<ConnectionId> allConnectionIds(NodeId const nodeId) const = 0;
|
|
||||||
|
|
||||||
/// @brief Returns all connected Node Ids for given port.
|
|
||||||
/**
|
|
||||||
* The returned set of nodes and port indices correspond to the type
|
|
||||||
* opposite to the given `portType`.
|
|
||||||
*/
|
|
||||||
virtual std::unordered_set<ConnectionId> connections(NodeId nodeId,
|
|
||||||
PortType portType,
|
|
||||||
PortIndex index) const
|
|
||||||
= 0;
|
|
||||||
|
|
||||||
/// Checks if two nodes with the given `connectionId` are connected.
|
|
||||||
virtual bool connectionExists(ConnectionId const connectionId) const = 0;
|
|
||||||
|
|
||||||
/// Creates a new node instance in the derived class.
|
|
||||||
/**
|
|
||||||
* The model is responsible for generating a unique `NodeId`.
|
|
||||||
* @param[in] nodeType is free to be used and interpreted by the
|
|
||||||
* model on its own, it helps to distinguish between possible node
|
|
||||||
* types and create a correct instance inside.
|
|
||||||
*/
|
|
||||||
virtual NodeId addNode(QString const nodeType = QString()) = 0;
|
|
||||||
|
|
||||||
/// Model decides if a conection with a given connection Id possible.
|
|
||||||
/**
|
|
||||||
* The default implementation compares corresponding data types.
|
|
||||||
*
|
|
||||||
* It is possible to override the function and connect non-equal
|
|
||||||
* data types.
|
|
||||||
*/
|
|
||||||
virtual bool connectionPossible(ConnectionId const connectionId) const = 0;
|
|
||||||
|
|
||||||
/// Defines if detaching the connection is possible.
|
|
||||||
virtual bool detachPossible(ConnectionId const) const { return true; }
|
|
||||||
|
|
||||||
/// Creates a new connection between two nodes.
|
|
||||||
/**
|
|
||||||
* Default implementation emits signal
|
|
||||||
* `connectionCreated(connectionId)`
|
|
||||||
*
|
|
||||||
* In the derived classes user must emite the signal to notify the
|
|
||||||
* scene about the changes.
|
|
||||||
*/
|
|
||||||
virtual void addConnection(ConnectionId const connectionId) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns `true` if there is data in the model associated with the
|
|
||||||
* given `nodeId`.
|
|
||||||
*/
|
|
||||||
virtual bool nodeExists(NodeId const nodeId) const = 0;
|
|
||||||
|
|
||||||
/// @brief Returns node-related data for requested NodeRole.
|
|
||||||
/**
|
|
||||||
* @returns Node Caption, Node Caption Visibility, Node Position etc.
|
|
||||||
*/
|
|
||||||
virtual QVariant nodeData(NodeId nodeId, NodeRole role) const = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility function that unwraps the `QVariant` value returned from the
|
|
||||||
* standard `QVariant AbstractGraphModel::nodeData(NodeId, NodeRole)` function.
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
T nodeData(NodeId nodeId, NodeRole role) const
|
|
||||||
{
|
|
||||||
return nodeData(nodeId, role).value<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual NodeFlags nodeFlags(NodeId nodeId) const
|
|
||||||
{
|
|
||||||
Q_UNUSED(nodeId);
|
|
||||||
return NodeFlag::NoFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Sets node properties.
|
|
||||||
/**
|
|
||||||
* Sets: Node Caption, Node Caption Visibility,
|
|
||||||
* Shyle, State, Node Position etc.
|
|
||||||
* @see NodeRole.
|
|
||||||
*/
|
|
||||||
virtual bool setNodeData(NodeId nodeId, NodeRole role, QVariant value) = 0;
|
|
||||||
|
|
||||||
/// @brief Returns port-related data for requested NodeRole.
|
|
||||||
/**
|
|
||||||
* @returns Port Data Type, Port Data, Connection Policy, Port
|
|
||||||
* Caption.
|
|
||||||
*/
|
|
||||||
virtual QVariant portData(NodeId nodeId, PortType portType, PortIndex index, PortRole role) const
|
|
||||||
= 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility function that unwraps the `QVariant` value returned from the
|
|
||||||
* standard `QVariant AbstractGraphModel::portData(...)` function.
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
T portData(NodeId nodeId, PortType portType, PortIndex index, PortRole role) const
|
|
||||||
{
|
|
||||||
return portData(nodeId, portType, index, role).value<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool setPortData(NodeId nodeId,
|
|
||||||
PortType portType,
|
|
||||||
PortIndex index,
|
|
||||||
QVariant const &value,
|
|
||||||
PortRole role = PortRole::Data)
|
|
||||||
= 0;
|
|
||||||
|
|
||||||
virtual bool deleteConnection(ConnectionId const connectionId) = 0;
|
|
||||||
|
|
||||||
virtual bool deleteNode(NodeId const nodeId) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplement the function if you want to store/restore the node's
|
|
||||||
* inner state during undo/redo node deletion operations.
|
|
||||||
*/
|
|
||||||
virtual QJsonObject saveNode(NodeId const) const { return {}; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplement the function if you want to support:
|
|
||||||
*
|
|
||||||
* - graph save/restore operations,
|
|
||||||
* - undo/redo operations after deleting the node.
|
|
||||||
*
|
|
||||||
* QJsonObject must contain following fields:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* {
|
|
||||||
* id : 5,
|
|
||||||
* position : { x : 100, y : 200 },
|
|
||||||
* internal-data {
|
|
||||||
* "your model specific data here"
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* The function must do almost exacly the same thing as the normal addNode().
|
|
||||||
* The main difference is in a model-specific `inner-data` processing.
|
|
||||||
*/
|
|
||||||
virtual void loadNode(QJsonObject const &) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Function clears connections attached to the ports that are scheduled to be
|
|
||||||
* deleted. It must be called right before the model removes its old port data.
|
|
||||||
*
|
|
||||||
* @param nodeId Defines the node to be modified
|
|
||||||
* @param portType Is either PortType::In or PortType::Out
|
|
||||||
* @param first Index of the first port to be removed
|
|
||||||
* @param last Index of the last port to be removed
|
|
||||||
*/
|
|
||||||
void portsAboutToBeDeleted(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const first,
|
|
||||||
PortIndex const last);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signal emitted when model no longer has the old data associated with the
|
|
||||||
* given port indices and when the node must be repainted.
|
|
||||||
*/
|
|
||||||
void portsDeleted();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signal emitted when model is about to create new ports on the given node.
|
|
||||||
* @param first Is the first index of the new port after insertion.
|
|
||||||
* @param last Is the last index of the new port after insertion.
|
|
||||||
*
|
|
||||||
* Function caches existing connections that are located after the `last` port
|
|
||||||
* index. For such connections the new "post-insertion" addresses are computed
|
|
||||||
* and stored until the function AbstractGraphModel::portsInserted is called.
|
|
||||||
*/
|
|
||||||
void portsAboutToBeInserted(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const first,
|
|
||||||
PortIndex const last);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function re-creates the connections that were shifted during the port
|
|
||||||
* insertion. After that the node is updated.
|
|
||||||
*/
|
|
||||||
void portsInserted();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void connectionCreated(ConnectionId const connectionId);
|
|
||||||
|
|
||||||
void connectionDeleted(ConnectionId const connectionId);
|
|
||||||
|
|
||||||
void nodeCreated(NodeId const nodeId);
|
|
||||||
|
|
||||||
void nodeDeleted(NodeId const nodeId);
|
|
||||||
|
|
||||||
void nodeUpdated(NodeId const nodeId);
|
|
||||||
|
|
||||||
void nodeFlagsUpdated(NodeId const nodeId);
|
|
||||||
|
|
||||||
void nodePositionUpdated(NodeId const nodeId);
|
|
||||||
|
|
||||||
void modelReset();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<ConnectionId> _shiftedByDynamicPortsConnections;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
#include <QRectF>
|
|
||||||
#include <QSize>
|
|
||||||
#include <QTransform>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class AbstractGraphModel;
|
|
||||||
|
|
||||||
class NODE_EDITOR_PUBLIC AbstractNodeGeometry
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
AbstractNodeGeometry(AbstractGraphModel &, double marginsRatio = 0.2);
|
|
||||||
virtual ~AbstractNodeGeometry() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The node's size plus some additional margin around it to account for drawing
|
|
||||||
* effects (for example shadows) or node's parts outside the size rectangle
|
|
||||||
* (for example port points).
|
|
||||||
*
|
|
||||||
* The default implementation returns QSize + 20 percent of width and heights
|
|
||||||
* at each side of the rectangle.
|
|
||||||
*/
|
|
||||||
virtual QRectF boundingRect(NodeId const nodeId) const;
|
|
||||||
|
|
||||||
virtual void setMarginsRatio(double marginsRatio);
|
|
||||||
|
|
||||||
/// A direct rectangle defining the borders of the node's rectangle.
|
|
||||||
virtual QSize size(NodeId const nodeId) const = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The function is triggeren when a nuber of ports is changed or when an
|
|
||||||
* embedded widget needs an update.
|
|
||||||
*/
|
|
||||||
virtual void recomputeSize(NodeId const nodeId) const = 0;
|
|
||||||
|
|
||||||
/// Port position in node's coordinate system.
|
|
||||||
virtual QPointF portPosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const index) const
|
|
||||||
= 0;
|
|
||||||
|
|
||||||
/// A convenience function using the `portPosition` and a given transformation.
|
|
||||||
virtual QPointF portScenePosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const index,
|
|
||||||
QTransform const &t) const;
|
|
||||||
|
|
||||||
/// Defines where to draw port label. The point corresponds to a font baseline.
|
|
||||||
virtual QPointF portTextPosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const portIndex) const
|
|
||||||
= 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines where to start drawing the caption. The point corresponds to a font
|
|
||||||
* baseline.
|
|
||||||
*/
|
|
||||||
virtual QPointF captionPosition(NodeId const nodeId) const = 0;
|
|
||||||
|
|
||||||
/// Caption rect is needed for estimating the total node size.
|
|
||||||
virtual QRectF captionRect(NodeId const nodeId) const = 0;
|
|
||||||
|
|
||||||
/// Position for an embedded widget. Return any value if you don't embed.
|
|
||||||
virtual QPointF widgetPosition(NodeId const nodeId) const = 0;
|
|
||||||
|
|
||||||
virtual PortIndex checkPortHit(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
QPointF const nodePoint) const;
|
|
||||||
|
|
||||||
virtual QRect resizeHandleRect(NodeId const nodeId) const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
AbstractGraphModel &_graphModel;
|
|
||||||
double _marginsRatio{0.0};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QPainter>
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
class QPainter;
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class NodeGraphicsObject;
|
|
||||||
class NodeDataModel;
|
|
||||||
|
|
||||||
/// Class enables custom painting.
|
|
||||||
class NODE_EDITOR_PUBLIC AbstractNodePainter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~AbstractNodePainter() = default;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reimplement this function in order to have a custom painting.
|
|
||||||
*
|
|
||||||
* Useful functions:
|
|
||||||
* `NodeGraphicsObject::nodeScene()->nodeGeometry()`
|
|
||||||
* `NodeGraphicsObject::graphModel()`
|
|
||||||
*/
|
|
||||||
virtual void paint(QPainter *painter, NodeGraphicsObject &ngo) const = 0;
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,184 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtCore/QUuid>
|
|
||||||
#include <QtWidgets/QGraphicsScene>
|
|
||||||
#include <QtWidgets/QMenu>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <tuple>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "AbstractGraphModel.hpp"
|
|
||||||
#include "AbstractNodeGeometry.hpp"
|
|
||||||
#include "ConnectionIdHash.hpp"
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
#include "QUuidStdHash.hpp"
|
|
||||||
|
|
||||||
class QUndoStack;
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class AbstractGraphModel;
|
|
||||||
class AbstractNodePainter;
|
|
||||||
class ConnectionGraphicsObject;
|
|
||||||
class NodeGraphicsObject;
|
|
||||||
class NodeStyle;
|
|
||||||
|
|
||||||
/// An instance of QGraphicsScene, holds connections and nodes.
|
|
||||||
class NODE_EDITOR_PUBLIC BasicGraphicsScene : public QGraphicsScene
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
BasicGraphicsScene(AbstractGraphModel &graphModel, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
// Scenes without models are not supported
|
|
||||||
BasicGraphicsScene() = delete;
|
|
||||||
|
|
||||||
~BasicGraphicsScene();
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// @returns associated AbstractGraphModel.
|
|
||||||
AbstractGraphModel const &graphModel() const;
|
|
||||||
|
|
||||||
AbstractGraphModel &graphModel();
|
|
||||||
|
|
||||||
AbstractNodeGeometry &nodeGeometry();
|
|
||||||
|
|
||||||
AbstractNodePainter &nodePainter();
|
|
||||||
|
|
||||||
void setNodePainter(std::unique_ptr<AbstractNodePainter> newPainter);
|
|
||||||
|
|
||||||
QUndoStack &undoStack();
|
|
||||||
|
|
||||||
void setDropShadowEffect(bool enable);
|
|
||||||
|
|
||||||
bool isDropShadowEffectEnabled() const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Creates a "draft" instance of ConnectionGraphicsObject.
|
|
||||||
/**
|
|
||||||
* The scene caches a "draft" connection which has one loose end.
|
|
||||||
* After attachment the "draft" instance is deleted and instead a
|
|
||||||
* normal "full" connection is created.
|
|
||||||
* Function @returns the "draft" instance for further geometry
|
|
||||||
* manipulations.
|
|
||||||
*/
|
|
||||||
std::unique_ptr<ConnectionGraphicsObject> const &makeDraftConnection(
|
|
||||||
ConnectionId const newConnectionId);
|
|
||||||
|
|
||||||
/// Deletes "draft" connection.
|
|
||||||
/**
|
|
||||||
* The function is called when user releases the mouse button during
|
|
||||||
* the construction of the new connection without attaching it to any
|
|
||||||
* node.
|
|
||||||
*/
|
|
||||||
void resetDraftConnection();
|
|
||||||
|
|
||||||
/// Deletes all the nodes. Connections are removed automatically.
|
|
||||||
void clearScene();
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// @returns NodeGraphicsObject associated with the given nodeId.
|
|
||||||
/**
|
|
||||||
* @returns nullptr when the object is not found.
|
|
||||||
*/
|
|
||||||
NodeGraphicsObject *nodeGraphicsObject(NodeId nodeId);
|
|
||||||
|
|
||||||
/// @returns ConnectionGraphicsObject corresponding to `connectionId`.
|
|
||||||
/**
|
|
||||||
* @returns `nullptr` when the object is not found.
|
|
||||||
*/
|
|
||||||
ConnectionGraphicsObject *connectionGraphicsObject(ConnectionId connectionId);
|
|
||||||
|
|
||||||
Qt::Orientation orientation() const { return _orientation; }
|
|
||||||
|
|
||||||
void setOrientation(Qt::Orientation const orientation);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Can @return an instance of the scene context menu in subclass.
|
|
||||||
/**
|
|
||||||
* Default implementation returns `nullptr`.
|
|
||||||
*/
|
|
||||||
virtual QMenu *createSceneMenu(QPointF const scenePos);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void nodeMoved(NodeId const nodeId, QPointF const &newLocation);
|
|
||||||
|
|
||||||
void nodeClicked(NodeId const nodeId);
|
|
||||||
|
|
||||||
void nodeSelected(NodeId const nodeId);
|
|
||||||
|
|
||||||
void nodeDoubleClicked(NodeId const nodeId);
|
|
||||||
|
|
||||||
void nodeHovered(NodeId const nodeId, QPoint const screenPos);
|
|
||||||
|
|
||||||
void nodeHoverLeft(NodeId const nodeId);
|
|
||||||
|
|
||||||
void connectionHovered(ConnectionId const connectionId, QPoint const screenPos);
|
|
||||||
|
|
||||||
void connectionHoverLeft(ConnectionId const connectionId);
|
|
||||||
|
|
||||||
/// Signal allows showing custom context menu upon clicking a node.
|
|
||||||
void nodeContextMenu(NodeId const nodeId, QPointF const pos);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// @brief Creates Node and Connection graphics objects.
|
|
||||||
/**
|
|
||||||
* Function is used to populate an empty scene in the constructor. We
|
|
||||||
* perform depth-first AbstractGraphModel traversal. The connections are
|
|
||||||
* created by checking non-empty node `Out` ports.
|
|
||||||
*/
|
|
||||||
void traverseGraphAndPopulateGraphicsObjects();
|
|
||||||
|
|
||||||
/// Redraws adjacent nodes for given `connectionId`
|
|
||||||
void updateAttachedNodes(ConnectionId const connectionId, PortType const portType);
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
/// Slot called when the `connectionId` is erased form the AbstractGraphModel.
|
|
||||||
void onConnectionDeleted(ConnectionId const connectionId);
|
|
||||||
|
|
||||||
/// Slot called when the `connectionId` is created in the AbstractGraphModel.
|
|
||||||
void onConnectionCreated(ConnectionId const connectionId);
|
|
||||||
|
|
||||||
void onNodeDeleted(NodeId const nodeId);
|
|
||||||
|
|
||||||
void onNodeCreated(NodeId const nodeId);
|
|
||||||
|
|
||||||
void onNodePositionUpdated(NodeId const nodeId);
|
|
||||||
|
|
||||||
void onNodeUpdated(NodeId const nodeId);
|
|
||||||
|
|
||||||
void onNodeClicked(NodeId const nodeId);
|
|
||||||
|
|
||||||
void onModelReset();
|
|
||||||
|
|
||||||
private:
|
|
||||||
AbstractGraphModel &_graphModel;
|
|
||||||
|
|
||||||
using UniqueNodeGraphicsObject = std::unique_ptr<NodeGraphicsObject>;
|
|
||||||
|
|
||||||
using UniqueConnectionGraphicsObject = std::unique_ptr<ConnectionGraphicsObject>;
|
|
||||||
|
|
||||||
std::unordered_map<NodeId, UniqueNodeGraphicsObject> _nodeGraphicsObjects;
|
|
||||||
|
|
||||||
std::unordered_map<ConnectionId, UniqueConnectionGraphicsObject> _connectionGraphicsObjects;
|
|
||||||
|
|
||||||
std::unique_ptr<ConnectionGraphicsObject> _draftConnection;
|
|
||||||
|
|
||||||
std::unique_ptr<AbstractNodeGeometry> _nodeGeometry;
|
|
||||||
|
|
||||||
std::unique_ptr<AbstractNodePainter> _nodePainter;
|
|
||||||
|
|
||||||
bool _nodeDrag;
|
|
||||||
|
|
||||||
QUndoStack *_undoStack;
|
|
||||||
|
|
||||||
Qt::Orientation _orientation;
|
|
||||||
|
|
||||||
bool _dropShadowEffect{false};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#if defined(__MINGW32__) || defined(__MINGW64__)
|
|
||||||
#define NODE_EDITOR_COMPILER "MinGW"
|
|
||||||
#define NODE_EDITOR_COMPILER_MINGW
|
|
||||||
#elif defined(__clang__)
|
|
||||||
#define NODE_EDITOR_COMPILER "Clang"
|
|
||||||
#define NODE_EDITOR_COMPILER_CLANG
|
|
||||||
#elif defined(_MSC_VER)
|
|
||||||
#define NODE_EDITOR_COMPILER "Microsoft Visual C++"
|
|
||||||
#define NODE_EDITOR_COMPILER_MICROSOFT
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
#define NODE_EDITOR_COMPILER "GNU"
|
|
||||||
#define NODE_EDITOR_COMPILER_GNU
|
|
||||||
#define NODE_EDITOR_COMPILER_GNU_VERSION_MAJOR __GNUC__
|
|
||||||
#define NODE_EDITOR_COMPILER_GNU_VERSION_MINOR __GNUC_MINOR__
|
|
||||||
#define NODE_EDITOR_COMPILER_GNU_VERSION_PATCH __GNUC_PATCHLEVEL__
|
|
||||||
#elif defined(__BORLANDC__)
|
|
||||||
#define NODE_EDITOR_COMPILER "Borland C++ Builder"
|
|
||||||
#define NODE_EDITOR_COMPILER_BORLAND
|
|
||||||
#elif defined(__CODEGEARC__)
|
|
||||||
#define NODE_EDITOR_COMPILER "CodeGear C++ Builder"
|
|
||||||
#define NODE_EDITOR_COMPILER_CODEGEAR
|
|
||||||
#elif defined(__INTEL_COMPILER) || defined(__ICL)
|
|
||||||
#define NODE_EDITOR_COMPILER "Intel C++"
|
|
||||||
#define NODE_EDITOR_COMPILER_INTEL
|
|
||||||
#elif defined(__xlC__) || defined(__IBMCPP__)
|
|
||||||
#define NODE_EDITOR_COMPILER "IBM XL C++"
|
|
||||||
#define NODE_EDITOR_COMPILER_IBM
|
|
||||||
#elif defined(__HP_aCC)
|
|
||||||
#define NODE_EDITOR_COMPILER "HP aC++"
|
|
||||||
#define NODE_EDITOR_COMPILER_HP
|
|
||||||
#elif defined(__WATCOMC__)
|
|
||||||
#define NODE_EDITOR_COMPILER "Watcom C++"
|
|
||||||
#define NODE_EDITOR_COMPILER_WATCOM
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NODE_EDITOR_COMPILER
|
|
||||||
#error "Current compiler is not supported."
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <QtCore/QUuid>
|
|
||||||
#include <QtWidgets/QGraphicsObject>
|
|
||||||
|
|
||||||
#include "ConnectionState.hpp"
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
|
|
||||||
class QGraphicsSceneMouseEvent;
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class AbstractGraphModel;
|
|
||||||
class BasicGraphicsScene;
|
|
||||||
|
|
||||||
/// Graphic Object for connection. Adds itself to scene
|
|
||||||
class ConnectionGraphicsObject : public QGraphicsObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
// Needed for qgraphicsitem_cast
|
|
||||||
enum { Type = UserType + 2 };
|
|
||||||
|
|
||||||
int type() const override { return Type; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
ConnectionGraphicsObject(BasicGraphicsScene &scene, ConnectionId const connectionId);
|
|
||||||
|
|
||||||
~ConnectionGraphicsObject() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
AbstractGraphModel &graphModel() const;
|
|
||||||
|
|
||||||
BasicGraphicsScene *nodeScene() const;
|
|
||||||
|
|
||||||
ConnectionId const &connectionId() const;
|
|
||||||
|
|
||||||
QRectF boundingRect() const override;
|
|
||||||
|
|
||||||
QPainterPath shape() const override;
|
|
||||||
|
|
||||||
QPointF const &endPoint(PortType portType) const;
|
|
||||||
|
|
||||||
QPointF out() const { return _out; }
|
|
||||||
|
|
||||||
QPointF in() const { return _in; }
|
|
||||||
|
|
||||||
std::pair<QPointF, QPointF> pointsC1C2() const;
|
|
||||||
|
|
||||||
void setEndPoint(PortType portType, QPointF const &point);
|
|
||||||
|
|
||||||
/// Updates the position of both ends
|
|
||||||
void move();
|
|
||||||
|
|
||||||
ConnectionState const &connectionState() const;
|
|
||||||
|
|
||||||
ConnectionState &connectionState();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paint(QPainter *painter,
|
|
||||||
QStyleOptionGraphicsItem const *option,
|
|
||||||
QWidget *widget = 0) override;
|
|
||||||
|
|
||||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
|
||||||
|
|
||||||
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
|
|
||||||
|
|
||||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
|
||||||
|
|
||||||
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
|
|
||||||
|
|
||||||
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initializePosition();
|
|
||||||
|
|
||||||
void addGraphicsEffect();
|
|
||||||
|
|
||||||
std::pair<QPointF, QPointF> pointsC1C2Horizontal() const;
|
|
||||||
|
|
||||||
std::pair<QPointF, QPointF> pointsC1C2Vertical() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ConnectionId _connectionId;
|
|
||||||
|
|
||||||
AbstractGraphModel &_graphModel;
|
|
||||||
|
|
||||||
ConnectionState _connectionState;
|
|
||||||
|
|
||||||
mutable QPointF _out;
|
|
||||||
mutable QPointF _in;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
|
|
||||||
inline void hash_combine(std::size_t &seed)
|
|
||||||
{
|
|
||||||
Q_UNUSED(seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename... Rest>
|
|
||||||
inline void hash_combine(std::size_t &seed, const T &v, Rest... rest)
|
|
||||||
{
|
|
||||||
std::hash<T> hasher;
|
|
||||||
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
|
||||||
hash_combine(seed, rest...);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
template<>
|
|
||||||
struct hash<QtNodes::ConnectionId>
|
|
||||||
{
|
|
||||||
inline std::size_t operator()(QtNodes::ConnectionId const &id) const
|
|
||||||
{
|
|
||||||
std::size_t h = 0;
|
|
||||||
hash_combine(h, id.outNodeId, id.outPortIndex, id.inNodeId, id.inPortIndex);
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct hash<std::pair<QtNodes::NodeId, QtNodes::PortIndex>>
|
|
||||||
{
|
|
||||||
inline std::size_t operator()(std::pair<QtNodes::NodeId, QtNodes::PortIndex> const &nodePort) const
|
|
||||||
{
|
|
||||||
std::size_t h = 0;
|
|
||||||
hash_combine(h, nodePort.first, nodePort.second);
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<>
|
|
||||||
struct hash<std::tuple<QtNodes::NodeId, QtNodes::PortType, QtNodes::PortIndex>>
|
|
||||||
{
|
|
||||||
using Key = std::tuple<QtNodes::NodeId, QtNodes::PortType, QtNodes::PortIndex>;
|
|
||||||
|
|
||||||
inline std::size_t operator()(Key const &key) const
|
|
||||||
{
|
|
||||||
std::size_t h = 0;
|
|
||||||
hash_combine(h, std::get<0>(key), std::get<1>(key), std::get<2>(key));
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace std
|
|
||||||
|
|
@ -1,151 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
inline PortIndex getNodeId(PortType portType, ConnectionId connectionId)
|
|
||||||
{
|
|
||||||
NodeId id = InvalidNodeId;
|
|
||||||
|
|
||||||
if (portType == PortType::Out) {
|
|
||||||
id = connectionId.outNodeId;
|
|
||||||
} else if (portType == PortType::In) {
|
|
||||||
id = connectionId.inNodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline PortIndex getPortIndex(PortType portType, ConnectionId connectionId)
|
|
||||||
{
|
|
||||||
PortIndex index = InvalidPortIndex;
|
|
||||||
|
|
||||||
if (portType == PortType::Out) {
|
|
||||||
index = connectionId.outPortIndex;
|
|
||||||
} else if (portType == PortType::In) {
|
|
||||||
index = connectionId.inPortIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline PortType oppositePort(PortType port)
|
|
||||||
{
|
|
||||||
PortType result = PortType::None;
|
|
||||||
|
|
||||||
switch (port) {
|
|
||||||
case PortType::In:
|
|
||||||
result = PortType::Out;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PortType::Out:
|
|
||||||
result = PortType::In;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PortType::None:
|
|
||||||
result = PortType::None;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isPortIndexValid(PortIndex index)
|
|
||||||
{
|
|
||||||
return index != InvalidPortIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool isPortTypeValid(PortType portType)
|
|
||||||
{
|
|
||||||
return portType != PortType::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a connection Id instance filled just on one side.
|
|
||||||
*/
|
|
||||||
inline ConnectionId makeIncompleteConnectionId(NodeId const connectedNodeId,
|
|
||||||
PortType const connectedPort,
|
|
||||||
PortIndex const connectedPortIndex)
|
|
||||||
{
|
|
||||||
return (connectedPort == PortType::In)
|
|
||||||
? ConnectionId{InvalidNodeId, InvalidPortIndex, connectedNodeId, connectedPortIndex}
|
|
||||||
: ConnectionId{connectedNodeId, connectedPortIndex, InvalidNodeId, InvalidPortIndex};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turns a full connection Id into an incomplete one by removing the
|
|
||||||
* data on the given side
|
|
||||||
*/
|
|
||||||
inline ConnectionId makeIncompleteConnectionId(ConnectionId connectionId,
|
|
||||||
PortType const portToDisconnect)
|
|
||||||
{
|
|
||||||
if (portToDisconnect == PortType::Out) {
|
|
||||||
connectionId.outNodeId = InvalidNodeId;
|
|
||||||
connectionId.outPortIndex = InvalidPortIndex;
|
|
||||||
} else {
|
|
||||||
connectionId.inNodeId = InvalidNodeId;
|
|
||||||
connectionId.inPortIndex = InvalidPortIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return connectionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ConnectionId makeCompleteConnectionId(ConnectionId incompleteConnectionId,
|
|
||||||
NodeId const nodeId,
|
|
||||||
PortIndex const portIndex)
|
|
||||||
{
|
|
||||||
if (incompleteConnectionId.outNodeId == InvalidNodeId) {
|
|
||||||
incompleteConnectionId.outNodeId = nodeId;
|
|
||||||
incompleteConnectionId.outPortIndex = portIndex;
|
|
||||||
} else {
|
|
||||||
incompleteConnectionId.inNodeId = nodeId;
|
|
||||||
incompleteConnectionId.inPortIndex = portIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return incompleteConnectionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::ostream &operator<<(std::ostream &ostr, ConnectionId const connectionId)
|
|
||||||
{
|
|
||||||
ostr << "(" << connectionId.outNodeId << ", "
|
|
||||||
<< (isPortIndexValid(connectionId.outPortIndex) ? std::to_string(connectionId.outPortIndex)
|
|
||||||
: "INVALID")
|
|
||||||
<< ", " << connectionId.inNodeId << ", "
|
|
||||||
<< (isPortIndexValid(connectionId.inPortIndex) ? std::to_string(connectionId.inPortIndex)
|
|
||||||
: "INVALID")
|
|
||||||
<< ")" << std::endl;
|
|
||||||
|
|
||||||
return ostr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QJsonObject toJson(ConnectionId const &connId)
|
|
||||||
{
|
|
||||||
QJsonObject connJson;
|
|
||||||
|
|
||||||
connJson["outNodeId"] = static_cast<qint64>(connId.outNodeId);
|
|
||||||
connJson["outPortIndex"] = static_cast<qint64>(connId.outPortIndex);
|
|
||||||
connJson["intNodeId"] = static_cast<qint64>(connId.inNodeId);
|
|
||||||
connJson["inPortIndex"] = static_cast<qint64>(connId.inPortIndex);
|
|
||||||
|
|
||||||
return connJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline ConnectionId fromJson(QJsonObject const &connJson)
|
|
||||||
{
|
|
||||||
ConnectionId connId{static_cast<NodeId>(connJson["outNodeId"].toInt(InvalidNodeId)),
|
|
||||||
static_cast<PortIndex>(connJson["outPortIndex"].toInt(InvalidPortIndex)),
|
|
||||||
static_cast<NodeId>(connJson["intNodeId"].toInt(InvalidNodeId)),
|
|
||||||
static_cast<PortIndex>(connJson["inPortIndex"].toInt(InvalidPortIndex))};
|
|
||||||
|
|
||||||
return connId;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtGui/QPainter>
|
|
||||||
#include <QtGui/QPainterPath>
|
|
||||||
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class ConnectionGeometry;
|
|
||||||
class ConnectionGraphicsObject;
|
|
||||||
|
|
||||||
class ConnectionPainter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static void paint(QPainter *painter, ConnectionGraphicsObject const &cgo);
|
|
||||||
|
|
||||||
static QPainterPath getPainterStroke(ConnectionGraphicsObject const &cgo);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtCore/QUuid>
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
|
|
||||||
class QPointF;
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class ConnectionGraphicsObject;
|
|
||||||
|
|
||||||
/// Stores currently draggind end.
|
|
||||||
/// Remembers last hovered Node.
|
|
||||||
class NODE_EDITOR_PUBLIC ConnectionState
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/// Defines whether we construct a new connection
|
|
||||||
/// or it is already binding two nodes.
|
|
||||||
enum LooseEnd { Pending = 0, Connected = 1 };
|
|
||||||
|
|
||||||
public:
|
|
||||||
ConnectionState(ConnectionGraphicsObject &cgo)
|
|
||||||
: _cgo(cgo)
|
|
||||||
, _hovered(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
ConnectionState(ConnectionState const &) = delete;
|
|
||||||
ConnectionState(ConnectionState &&) = delete;
|
|
||||||
|
|
||||||
ConnectionState &operator=(ConnectionState const &) = delete;
|
|
||||||
ConnectionState &operator=(ConnectionState &&) = delete;
|
|
||||||
|
|
||||||
~ConnectionState();
|
|
||||||
|
|
||||||
public:
|
|
||||||
PortType requiredPort() const;
|
|
||||||
bool requiresPort() const;
|
|
||||||
|
|
||||||
bool hovered() const;
|
|
||||||
void setHovered(bool hovered);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Caches NodeId for further interaction.
|
|
||||||
void setLastHoveredNode(NodeId const nodeId);
|
|
||||||
|
|
||||||
NodeId lastHoveredNode() const;
|
|
||||||
|
|
||||||
void resetLastHoveredNode();
|
|
||||||
|
|
||||||
private:
|
|
||||||
ConnectionGraphicsObject &_cgo;
|
|
||||||
|
|
||||||
bool _hovered;
|
|
||||||
|
|
||||||
NodeId _lastHoveredNode{InvalidNodeId};
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtGui/QColor>
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
#include "Style.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class NODE_EDITOR_PUBLIC ConnectionStyle : public Style
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ConnectionStyle();
|
|
||||||
|
|
||||||
ConnectionStyle(QString jsonText);
|
|
||||||
|
|
||||||
~ConnectionStyle() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void setConnectionStyle(QString jsonText);
|
|
||||||
|
|
||||||
public:
|
|
||||||
void loadJson(QJsonObject const &json) override;
|
|
||||||
|
|
||||||
QJsonObject toJson() const override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
QColor constructionColor() const;
|
|
||||||
QColor normalColor() const;
|
|
||||||
QColor normalColor(QString typeId) const;
|
|
||||||
QColor selectedColor() const;
|
|
||||||
QColor selectedHaloColor() const;
|
|
||||||
QColor hoveredColor() const;
|
|
||||||
|
|
||||||
float lineWidth() const;
|
|
||||||
float constructionLineWidth() const;
|
|
||||||
float pointDiameter() const;
|
|
||||||
|
|
||||||
bool useDataDefinedColors() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QColor ConstructionColor;
|
|
||||||
QColor NormalColor;
|
|
||||||
QColor SelectedColor;
|
|
||||||
QColor SelectedHaloColor;
|
|
||||||
QColor HoveredColor;
|
|
||||||
|
|
||||||
float LineWidth;
|
|
||||||
float ConstructionLineWidth;
|
|
||||||
float PointDiameter;
|
|
||||||
|
|
||||||
bool UseDataDefinedColors;
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "AbstractGraphModel.hpp"
|
|
||||||
#include "ConnectionIdUtils.hpp"
|
|
||||||
#include "NodeDelegateModelRegistry.hpp"
|
|
||||||
#include "Serializable.hpp"
|
|
||||||
#include "StyleCollection.hpp"
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class NODE_EDITOR_PUBLIC DataFlowGraphModel : public AbstractGraphModel, public Serializable
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
struct NodeGeometryData
|
|
||||||
{
|
|
||||||
QSize size;
|
|
||||||
QPointF pos;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
DataFlowGraphModel(std::shared_ptr<NodeDelegateModelRegistry> registry);
|
|
||||||
|
|
||||||
std::shared_ptr<NodeDelegateModelRegistry> dataModelRegistry() { return _registry; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::unordered_set<NodeId> allNodeIds() const override;
|
|
||||||
|
|
||||||
std::unordered_set<ConnectionId> allConnectionIds(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
std::unordered_set<ConnectionId> connections(NodeId nodeId,
|
|
||||||
PortType portType,
|
|
||||||
PortIndex portIndex) const override;
|
|
||||||
|
|
||||||
bool connectionExists(ConnectionId const connectionId) const override;
|
|
||||||
|
|
||||||
NodeId addNode(QString const nodeType) override;
|
|
||||||
|
|
||||||
bool connectionPossible(ConnectionId const connectionId) const override;
|
|
||||||
|
|
||||||
void addConnection(ConnectionId const connectionId) override;
|
|
||||||
|
|
||||||
bool nodeExists(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
QVariant nodeData(NodeId nodeId, NodeRole role) const override;
|
|
||||||
|
|
||||||
NodeFlags nodeFlags(NodeId nodeId) const override;
|
|
||||||
|
|
||||||
bool setNodeData(NodeId nodeId, NodeRole role, QVariant value) override;
|
|
||||||
|
|
||||||
QVariant portData(NodeId nodeId,
|
|
||||||
PortType portType,
|
|
||||||
PortIndex portIndex,
|
|
||||||
PortRole role) const override;
|
|
||||||
|
|
||||||
bool setPortData(NodeId nodeId,
|
|
||||||
PortType portType,
|
|
||||||
PortIndex portIndex,
|
|
||||||
QVariant const &value,
|
|
||||||
PortRole role = PortRole::Data) override;
|
|
||||||
|
|
||||||
bool deleteConnection(ConnectionId const connectionId) override;
|
|
||||||
|
|
||||||
bool deleteNode(NodeId const nodeId) override;
|
|
||||||
|
|
||||||
QJsonObject saveNode(NodeId const) const override;
|
|
||||||
|
|
||||||
QJsonObject save() const override;
|
|
||||||
|
|
||||||
void loadNode(QJsonObject const &nodeJson) override;
|
|
||||||
|
|
||||||
void load(QJsonObject const &json) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the NodeDelegateModel for the given `nodeId` and tries to cast the
|
|
||||||
* stored pointer to the given type
|
|
||||||
*/
|
|
||||||
template<typename NodeDelegateModelType>
|
|
||||||
NodeDelegateModelType *delegateModel(NodeId const nodeId)
|
|
||||||
{
|
|
||||||
auto it = _models.find(nodeId);
|
|
||||||
if (it == _models.end())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
auto model = dynamic_cast<NodeDelegateModelType *>(it->second.get());
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void inPortDataWasSet(NodeId const, PortType const, PortIndex const);
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeId newNodeId() override { return _nextNodeId++; }
|
|
||||||
|
|
||||||
void sendConnectionCreation(ConnectionId const connectionId);
|
|
||||||
|
|
||||||
void sendConnectionDeletion(ConnectionId const connectionId);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
/**
|
|
||||||
* Fuction is called in three cases:
|
|
||||||
*
|
|
||||||
* - By underlying NodeDelegateModel when a node has new data to propagate.
|
|
||||||
* @see DataFlowGraphModel::addNode
|
|
||||||
* - When a new connection is created.
|
|
||||||
* @see DataFlowGraphModel::addConnection
|
|
||||||
* - When a node restored from JSON an needs to send data downstream.
|
|
||||||
* @see DataFlowGraphModel::loadNode
|
|
||||||
*/
|
|
||||||
void onOutPortDataUpdated(NodeId const nodeId, PortIndex const portIndex);
|
|
||||||
|
|
||||||
/// Function is called after detaching a connection.
|
|
||||||
void propagateEmptyDataTo(NodeId const nodeId, PortIndex const portIndex);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<NodeDelegateModelRegistry> _registry;
|
|
||||||
|
|
||||||
NodeId _nextNodeId;
|
|
||||||
|
|
||||||
std::unordered_map<NodeId, std::unique_ptr<NodeDelegateModel>> _models;
|
|
||||||
|
|
||||||
std::unordered_set<ConnectionId> _connectivity;
|
|
||||||
|
|
||||||
mutable std::unordered_map<NodeId, NodeGeometryData> _nodeGeometryData;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BasicGraphicsScene.hpp"
|
|
||||||
#include "DataFlowGraphModel.hpp"
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
/// @brief An advanced scene working with data-propagating graphs.
|
|
||||||
/**
|
|
||||||
* The class represents a scene that existed in v2.x but built wit the
|
|
||||||
* new model-view approach in mind.
|
|
||||||
*/
|
|
||||||
class NODE_EDITOR_PUBLIC DataFlowGraphicsScene : public BasicGraphicsScene
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
DataFlowGraphicsScene(DataFlowGraphModel &graphModel, QObject *parent = nullptr);
|
|
||||||
|
|
||||||
~DataFlowGraphicsScene() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::vector<NodeId> selectedNodes() const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
QMenu *createSceneMenu(QPointF const scenePos) override;
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
void save() const;
|
|
||||||
|
|
||||||
void load();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void sceneLoaded();
|
|
||||||
|
|
||||||
private:
|
|
||||||
DataFlowGraphModel &_graphModel;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "AbstractNodeGeometry.hpp"
|
|
||||||
|
|
||||||
#include <QtGui/QFontMetrics>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class AbstractGraphModel;
|
|
||||||
class BasicGraphicsScene;
|
|
||||||
|
|
||||||
class NODE_EDITOR_PUBLIC DefaultHorizontalNodeGeometry : public AbstractNodeGeometry
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DefaultHorizontalNodeGeometry(AbstractGraphModel &graphModel);
|
|
||||||
|
|
||||||
public:
|
|
||||||
QSize size(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
void recomputeSize(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
QPointF portPosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const index) const override;
|
|
||||||
|
|
||||||
QPointF portTextPosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const PortIndex) const override;
|
|
||||||
QPointF captionPosition(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
QRectF captionRect(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
QPointF widgetPosition(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
QRect resizeHandleRect(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QRectF portTextRect(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const portIndex) const;
|
|
||||||
|
|
||||||
/// Finds max number of ports and multiplies by (a port height + interval)
|
|
||||||
unsigned int maxVerticalPortsExtent(NodeId const nodeId) const;
|
|
||||||
|
|
||||||
unsigned int maxPortsTextAdvance(NodeId const nodeId, PortType const portType) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Some variables are mutable because we need to change drawing
|
|
||||||
// metrics corresponding to fontMetrics but this doesn't change
|
|
||||||
// constness of the Node.
|
|
||||||
|
|
||||||
mutable unsigned int _portSize;
|
|
||||||
unsigned int _portSpasing;
|
|
||||||
mutable QFontMetrics _fontMetrics;
|
|
||||||
mutable QFontMetrics _boldFontMetrics;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtGui/QPainter>
|
|
||||||
|
|
||||||
#include "AbstractNodePainter.hpp"
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class BasicGraphicsScene;
|
|
||||||
class GraphModel;
|
|
||||||
class NodeGeometry;
|
|
||||||
class NodeGraphicsObject;
|
|
||||||
class NodeState;
|
|
||||||
|
|
||||||
/// @ Lightweight class incapsulating paint code.
|
|
||||||
class NODE_EDITOR_PUBLIC DefaultNodePainter : public AbstractNodePainter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void paint(QPainter *painter, NodeGraphicsObject &ngo) const override;
|
|
||||||
|
|
||||||
void drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo) const;
|
|
||||||
|
|
||||||
void drawConnectionPoints(QPainter *painter, NodeGraphicsObject &ngo) const;
|
|
||||||
|
|
||||||
void drawFilledConnectionPoints(QPainter *painter, NodeGraphicsObject &ngo) const;
|
|
||||||
|
|
||||||
void drawNodeCaption(QPainter *painter, NodeGraphicsObject &ngo) const;
|
|
||||||
|
|
||||||
void drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const;
|
|
||||||
|
|
||||||
void drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const;
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "AbstractNodeGeometry.hpp"
|
|
||||||
|
|
||||||
#include <QtGui/QFontMetrics>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class AbstractGraphModel;
|
|
||||||
class BasicGraphicsScene;
|
|
||||||
|
|
||||||
class NODE_EDITOR_PUBLIC DefaultVerticalNodeGeometry : public AbstractNodeGeometry
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DefaultVerticalNodeGeometry(AbstractGraphModel &graphModel);
|
|
||||||
|
|
||||||
public:
|
|
||||||
QSize size(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
void recomputeSize(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
QPointF portPosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const index) const override;
|
|
||||||
|
|
||||||
QPointF portTextPosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const PortIndex) const override;
|
|
||||||
|
|
||||||
QPointF captionPosition(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
QRectF captionRect(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
QPointF widgetPosition(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
QRect resizeHandleRect(NodeId const nodeId) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QRectF portTextRect(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const portIndex) const;
|
|
||||||
/// Finds
|
|
||||||
unsigned int maxHorizontalPortsExtent(NodeId const nodeId) const;
|
|
||||||
|
|
||||||
unsigned int maxPortsTextAdvance(NodeId const nodeId, PortType const portType) const;
|
|
||||||
|
|
||||||
unsigned int portCaptionsHeight(NodeId const nodeId, PortType const portType) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Some variables are mutable because we need to change drawing
|
|
||||||
// metrics corresponding to fontMetrics but this doesn't change
|
|
||||||
// constness of the Node.
|
|
||||||
|
|
||||||
mutable unsigned int _portSize;
|
|
||||||
unsigned int _portSpasing;
|
|
||||||
mutable QFontMetrics _fontMetrics;
|
|
||||||
mutable QFontMetrics _boldFontMetrics;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
#include <QtCore/QMetaObject>
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
* Important definitions used throughout the library.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
|
||||||
NODE_EDITOR_PUBLIC Q_NAMESPACE
|
|
||||||
#else
|
|
||||||
Q_NAMESPACE_EXPORT(NODE_EDITOR_PUBLIC)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constants used for fetching QVariant data from GraphModel.
|
|
||||||
*/
|
|
||||||
enum class NodeRole {
|
|
||||||
Type = 0, ///< Type of the current node, usually a string.
|
|
||||||
Position = 1, ///< `QPointF` positon of the node on the scene.
|
|
||||||
Size = 2, ///< `QSize` for resizable nodes.
|
|
||||||
CaptionVisible = 3, ///< `bool` for caption visibility.
|
|
||||||
Caption = 4, ///< `QString` for node caption.
|
|
||||||
Style = 5, ///< Custom NodeStyle as QJsonDocument
|
|
||||||
InternalData = 6, ///< Node-stecific user data as QJsonObject
|
|
||||||
InPortCount = 7, ///< `unsigned int`
|
|
||||||
OutPortCount = 9, ///< `unsigned int`
|
|
||||||
Widget = 10, ///< Optional `QWidget*` or `nullptr`
|
|
||||||
Id = 11 ///< Return node ID
|
|
||||||
};
|
|
||||||
Q_ENUM_NS(NodeRole)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specific flags regulating node features and appeaarence.
|
|
||||||
*/
|
|
||||||
enum NodeFlag {
|
|
||||||
NoFlags = 0x0, ///< Default NodeFlag
|
|
||||||
Resizable = 0x1, ///< Lets the node be resizable
|
|
||||||
Locked = 0x2
|
|
||||||
};
|
|
||||||
|
|
||||||
Q_DECLARE_FLAGS(NodeFlags, NodeFlag)
|
|
||||||
Q_FLAG_NS(NodeFlags)
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(NodeFlags)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constants for fetching port-related information from the GraphModel.
|
|
||||||
*/
|
|
||||||
enum class PortRole {
|
|
||||||
Data = 0, ///< `std::shared_ptr<NodeData>`.
|
|
||||||
DataType = 1, ///< `QString` describing the port data type.
|
|
||||||
ConnectionPolicyRole = 2, ///< `enum` ConnectionPolicyRole
|
|
||||||
CaptionVisible = 3, ///< `bool` for caption visibility.
|
|
||||||
Caption = 4, ///< `QString` for port caption.
|
|
||||||
};
|
|
||||||
Q_ENUM_NS(PortRole)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines how many connections are possible to attach to ports. The
|
|
||||||
* values are fetched using PortRole::ConnectionPolicy.
|
|
||||||
*/
|
|
||||||
enum class ConnectionPolicy {
|
|
||||||
One, ///< Just one connection for each port.
|
|
||||||
Many, ///< Any number of connections possible for the port.
|
|
||||||
};
|
|
||||||
Q_ENUM_NS(ConnectionPolicy)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for distinguishing input and output node ports.
|
|
||||||
*/
|
|
||||||
enum class PortType {
|
|
||||||
In = 0, ///< Input node port (from the left).
|
|
||||||
Out = 1, ///< Output node port (from the right).
|
|
||||||
None = 2
|
|
||||||
};
|
|
||||||
Q_ENUM_NS(PortType)
|
|
||||||
|
|
||||||
using PortCount = unsigned int;
|
|
||||||
|
|
||||||
/// ports are consecutively numbered starting from zero.
|
|
||||||
using PortIndex = unsigned int;
|
|
||||||
|
|
||||||
static constexpr PortIndex InvalidPortIndex = std::numeric_limits<PortIndex>::max();
|
|
||||||
|
|
||||||
/// Unique Id associated with each node in the GraphModel.
|
|
||||||
using NodeId = unsigned int;
|
|
||||||
|
|
||||||
static constexpr NodeId InvalidNodeId = std::numeric_limits<NodeId>::max();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A unique connection identificator that stores
|
|
||||||
* out `NodeId`, out `PortIndex`, in `NodeId`, in `PortIndex`
|
|
||||||
*/
|
|
||||||
struct ConnectionId
|
|
||||||
{
|
|
||||||
NodeId outNodeId;
|
|
||||||
PortIndex outPortIndex;
|
|
||||||
NodeId inNodeId;
|
|
||||||
PortIndex inPortIndex;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline bool operator==(ConnectionId const &a, ConnectionId const &b)
|
|
||||||
{
|
|
||||||
return a.outNodeId == b.outNodeId && a.outPortIndex == b.outPortIndex
|
|
||||||
&& a.inNodeId == b.inNodeId && a.inPortIndex == b.inPortIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator!=(ConnectionId const &a, ConnectionId const &b)
|
|
||||||
{
|
|
||||||
return !(a == b);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void invertConnection(ConnectionId &id)
|
|
||||||
{
|
|
||||||
std::swap(id.outNodeId, id.inNodeId);
|
|
||||||
std::swap(id.outPortIndex, id.inPortIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Compiler.hpp"
|
|
||||||
#include "OperatingSystem.hpp"
|
|
||||||
|
|
||||||
#ifdef NODE_EDITOR_PLATFORM_WINDOWS
|
|
||||||
#define NODE_EDITOR_EXPORT __declspec(dllexport)
|
|
||||||
#define NODE_EDITOR_IMPORT __declspec(dllimport)
|
|
||||||
#define NODE_EDITOR_LOCAL
|
|
||||||
#elif NODE_EDITOR_COMPILER_GNU_VERSION_MAJOR >= 4 || defined(NODE_EDITOR_COMPILER_CLANG)
|
|
||||||
#define NODE_EDITOR_EXPORT __attribute__((visibility("default")))
|
|
||||||
#define NODE_EDITOR_IMPORT __attribute__((visibility("default")))
|
|
||||||
#define NODE_EDITOR_LOCAL __attribute__((visibility("hidden")))
|
|
||||||
#else
|
|
||||||
#define NODE_EDITOR_EXPORT
|
|
||||||
#define NODE_EDITOR_IMPORT
|
|
||||||
#define NODE_EDITOR_LOCAL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#define NODE_EDITOR_DEMANGLED extern "C"
|
|
||||||
#else
|
|
||||||
#define NODE_EDITOR_DEMANGLED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(NODE_EDITOR_SHARED) && !defined(NODE_EDITOR_STATIC)
|
|
||||||
#ifdef NODE_EDITOR_EXPORTS
|
|
||||||
#define NODE_EDITOR_PUBLIC NODE_EDITOR_EXPORT
|
|
||||||
#else
|
|
||||||
#define NODE_EDITOR_PUBLIC NODE_EDITOR_IMPORT
|
|
||||||
#endif
|
|
||||||
#define NODE_EDITOR_PRIVATE NODE_EDITOR_LOCAL
|
|
||||||
#elif !defined(NODE_EDITOR_SHARED) && defined(NODE_EDITOR_STATIC)
|
|
||||||
#define NODE_EDITOR_PUBLIC
|
|
||||||
#define NODE_EDITOR_PRIVATE
|
|
||||||
#elif defined(NODE_EDITOR_SHARED) && defined(NODE_EDITOR_STATIC)
|
|
||||||
#ifdef NODE_EDITOR_EXPORTS
|
|
||||||
#error "Cannot build as shared and static simultaneously."
|
|
||||||
#else
|
|
||||||
#error "Cannot link against shared and static simultaneously."
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#ifdef NODE_EDITOR_EXPORTS
|
|
||||||
#error "Choose whether to build as shared or static."
|
|
||||||
#else
|
|
||||||
#error "Choose whether to link against shared or static."
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtWidgets/QGraphicsView>
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class BasicGraphicsScene;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A central view able to render objects from `BasicGraphicsScene`.
|
|
||||||
*/
|
|
||||||
class NODE_EDITOR_PUBLIC GraphicsView : public QGraphicsView
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
struct ScaleRange
|
|
||||||
{
|
|
||||||
double minimum = 0;
|
|
||||||
double maximum = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
GraphicsView(QWidget *parent = Q_NULLPTR);
|
|
||||||
GraphicsView(BasicGraphicsScene *scene, QWidget *parent = Q_NULLPTR);
|
|
||||||
|
|
||||||
GraphicsView(const GraphicsView &) = delete;
|
|
||||||
GraphicsView operator=(const GraphicsView &) = delete;
|
|
||||||
|
|
||||||
QAction *clearSelectionAction() const;
|
|
||||||
|
|
||||||
QAction *deleteSelectionAction() const;
|
|
||||||
|
|
||||||
void setScene(BasicGraphicsScene *scene);
|
|
||||||
|
|
||||||
void centerScene();
|
|
||||||
|
|
||||||
/// @brief max=0/min=0 indicates infinite zoom in/out
|
|
||||||
void setScaleRange(double minimum = 0, double maximum = 0);
|
|
||||||
|
|
||||||
void setScaleRange(ScaleRange range);
|
|
||||||
|
|
||||||
double getScale() const;
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
void scaleUp();
|
|
||||||
|
|
||||||
void scaleDown();
|
|
||||||
|
|
||||||
void setupScale(double scale);
|
|
||||||
|
|
||||||
void onDeleteSelectedObjects();
|
|
||||||
|
|
||||||
void onDuplicateSelectedObjects();
|
|
||||||
|
|
||||||
void onCopySelectedObjects();
|
|
||||||
|
|
||||||
void onPasteObjects();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void scaleChanged(double scale);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
|
||||||
|
|
||||||
void wheelEvent(QWheelEvent *event) override;
|
|
||||||
|
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
|
||||||
|
|
||||||
void keyReleaseEvent(QKeyEvent *event) override;
|
|
||||||
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
|
|
||||||
void mouseMoveEvent(QMouseEvent *event) override;
|
|
||||||
|
|
||||||
void drawBackground(QPainter *painter, const QRectF &r) override;
|
|
||||||
|
|
||||||
void showEvent(QShowEvent *event) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
BasicGraphicsScene *nodeScene();
|
|
||||||
|
|
||||||
/// Computes scene position for pasting the copied/duplicated node groups.
|
|
||||||
QPointF scenePastePosition();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QAction *_clearSelectionAction = nullptr;
|
|
||||||
QAction *_deleteSelectionAction = nullptr;
|
|
||||||
QAction *_duplicateSelectionAction = nullptr;
|
|
||||||
QAction *_copySelectionAction = nullptr;
|
|
||||||
QAction *_pasteAction = nullptr;
|
|
||||||
|
|
||||||
QPointF _clickPos;
|
|
||||||
ScaleRange _scaleRange;
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtGui/QColor>
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
#include "Style.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class NODE_EDITOR_PUBLIC GraphicsViewStyle : public Style
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
GraphicsViewStyle();
|
|
||||||
|
|
||||||
GraphicsViewStyle(QString jsonText);
|
|
||||||
|
|
||||||
~GraphicsViewStyle() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void setStyle(QString jsonText);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loadJson(QJsonObject const &json) override;
|
|
||||||
|
|
||||||
QJsonObject toJson() const override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
QColor BackgroundColor;
|
|
||||||
QColor FineGridColor;
|
|
||||||
QColor CoarseGridColor;
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <QtCore/QPointF>
|
|
||||||
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class ConnectionGraphicsObject;
|
|
||||||
class NodeGraphicsObject;
|
|
||||||
class BasicGraphicsScene;
|
|
||||||
|
|
||||||
/// Class wraps conecting and disconnecting checks.
|
|
||||||
/**
|
|
||||||
* An instance should be created on the stack and destroyed
|
|
||||||
* automatically when the operation is completed
|
|
||||||
*/
|
|
||||||
class NodeConnectionInteraction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NodeConnectionInteraction(NodeGraphicsObject &ngo,
|
|
||||||
ConnectionGraphicsObject &cgo,
|
|
||||||
BasicGraphicsScene &scene);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can connect when following conditions are met:
|
|
||||||
* 1. Connection 'requires' a port.
|
|
||||||
* 2. Connection loose end is above the node port.
|
|
||||||
* 3. Source and target `nodeId`s are different.
|
|
||||||
* 4. GraphModel permits connection.
|
|
||||||
*/
|
|
||||||
bool canConnect(PortIndex *portIndex) const;
|
|
||||||
|
|
||||||
/// Creates a new connectino if possible.
|
|
||||||
/**
|
|
||||||
* 1. Check conditions from 'canConnect'.
|
|
||||||
* 2. Creates new connection with `GraphModel::addConnection`.
|
|
||||||
* 3. Adjust connection geometry.
|
|
||||||
*/
|
|
||||||
bool tryConnect() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Delete connection with `GraphModel::deleteConnection`.
|
|
||||||
* 2. Create a "draft" connection with incomplete `ConnectionId`.
|
|
||||||
* 3. Repaint both previously connected nodes.
|
|
||||||
*/
|
|
||||||
bool disconnect(PortType portToDisconnect) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
PortType connectionRequiredPort() const;
|
|
||||||
|
|
||||||
QPointF connectionEndScenePosition(PortType) const;
|
|
||||||
|
|
||||||
QPointF nodePortScenePosition(PortType portType, PortIndex portIndex) const;
|
|
||||||
|
|
||||||
PortIndex nodePortIndexUnderScenePoint(PortType portType, QPointF const &p) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeGraphicsObject &_ngo;
|
|
||||||
|
|
||||||
ConnectionGraphicsObject &_cgo;
|
|
||||||
|
|
||||||
BasicGraphicsScene &_scene;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QtCore/QString>
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* `id` represents an internal unique data type for the given port.
|
|
||||||
* `name` is a normal text description.
|
|
||||||
*/
|
|
||||||
struct NODE_EDITOR_PUBLIC NodeDataType
|
|
||||||
{
|
|
||||||
QString id;
|
|
||||||
QString name;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class represents data transferred between nodes.
|
|
||||||
* @param type is used for comparing the types
|
|
||||||
* The actual data is stored in subtypes
|
|
||||||
*/
|
|
||||||
class NODE_EDITOR_PUBLIC NodeData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~NodeData() = default;
|
|
||||||
|
|
||||||
virtual bool sameType(NodeData const &nodeData) const
|
|
||||||
{
|
|
||||||
return (this->type().id == nodeData.type().id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type for inner use
|
|
||||||
virtual NodeDataType type() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
Q_DECLARE_METATYPE(QtNodes::NodeDataType)
|
|
||||||
Q_DECLARE_METATYPE(std::shared_ptr<QtNodes::NodeData>)
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <QtWidgets/QWidget>
|
|
||||||
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
#include "Export.hpp"
|
|
||||||
#include "NodeData.hpp"
|
|
||||||
#include "NodeStyle.hpp"
|
|
||||||
#include "Serializable.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class StyleCollection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The class wraps Node-specific data operations and propagates it to
|
|
||||||
* the nesting DataFlowGraphModel which is a subclass of
|
|
||||||
* AbstractGraphModel.
|
|
||||||
* This class is the same what has been called NodeDataModel before v3.
|
|
||||||
*/
|
|
||||||
class NODE_EDITOR_PUBLIC NodeDelegateModel : public QObject, public Serializable
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
NodeDelegateModel();
|
|
||||||
|
|
||||||
virtual ~NodeDelegateModel() = default;
|
|
||||||
|
|
||||||
/// It is possible to hide caption in GUI
|
|
||||||
virtual bool captionVisible() const { return true; }
|
|
||||||
|
|
||||||
/// Caption is used in GUI
|
|
||||||
virtual QString caption() const = 0;
|
|
||||||
|
|
||||||
/// It is possible to hide port caption in GUI
|
|
||||||
virtual bool portCaptionVisible(PortType, PortIndex) const { return false; }
|
|
||||||
|
|
||||||
/// Port caption is used in GUI to label individual ports
|
|
||||||
virtual QString portCaption(PortType, PortIndex) const { return QString(); }
|
|
||||||
|
|
||||||
/// Name makes this model unique
|
|
||||||
virtual QString name() const = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
QJsonObject save() const override;
|
|
||||||
|
|
||||||
void load(QJsonObject const &) override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual unsigned int nPorts(PortType portType) const = 0;
|
|
||||||
|
|
||||||
virtual NodeDataType dataType(PortType portType, PortIndex portIndex) const = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ConnectionPolicy portConnectionPolicy(PortType, PortIndex) const;
|
|
||||||
|
|
||||||
NodeStyle const &nodeStyle() const;
|
|
||||||
|
|
||||||
void setNodeStyle(NodeStyle const &style);
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual void setInData(std::shared_ptr<NodeData> nodeData, PortIndex const portIndex) = 0;
|
|
||||||
|
|
||||||
virtual std::shared_ptr<NodeData> outData(PortIndex const 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;
|
|
||||||
|
|
||||||
virtual bool resizable() const { return false; }
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
|
|
||||||
virtual void inputConnectionCreated(ConnectionId const &) {}
|
|
||||||
|
|
||||||
virtual void inputConnectionDeleted(ConnectionId const &) {}
|
|
||||||
|
|
||||||
virtual void outputConnectionCreated(ConnectionId const &) {}
|
|
||||||
|
|
||||||
virtual void outputConnectionDeleted(ConnectionId const &) {}
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
|
|
||||||
/// Triggers the updates in the nodes downstream.
|
|
||||||
void dataUpdated(PortIndex const index);
|
|
||||||
|
|
||||||
/// Triggers the propagation of the empty data downstream.
|
|
||||||
void dataInvalidated(PortIndex const index);
|
|
||||||
|
|
||||||
void computingStarted();
|
|
||||||
|
|
||||||
void computingFinished();
|
|
||||||
|
|
||||||
void embeddedWidgetSizeUpdated();
|
|
||||||
|
|
||||||
/// Call this function before deleting the data associated with ports.
|
|
||||||
/**
|
|
||||||
* The function notifies the Graph Model and makes it remove and recompute the
|
|
||||||
* affected connection addresses.
|
|
||||||
*/
|
|
||||||
void portsAboutToBeDeleted(PortType const portType, PortIndex const first, PortIndex const last);
|
|
||||||
|
|
||||||
/// Call this function when data and port moditications are finished.
|
|
||||||
void portsDeleted();
|
|
||||||
|
|
||||||
/// Call this function before inserting the data associated with ports.
|
|
||||||
/**
|
|
||||||
* The function notifies the Graph Model and makes it recompute the affected
|
|
||||||
* connection addresses.
|
|
||||||
*/
|
|
||||||
void portsAboutToBeInserted(PortType const portType,
|
|
||||||
PortIndex const first,
|
|
||||||
PortIndex const last);
|
|
||||||
|
|
||||||
/// Call this function when data and port moditications are finished.
|
|
||||||
void portsInserted();
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeStyle _nodeStyle;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,171 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
#include "NodeData.hpp"
|
|
||||||
#include "NodeDelegateModel.hpp"
|
|
||||||
#include "QStringStdHash.hpp"
|
|
||||||
|
|
||||||
#include <QtCore/QString>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
/// Class uses map for storing models (name, model)
|
|
||||||
class NODE_EDITOR_PUBLIC NodeDelegateModelRegistry
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using RegistryItemPtr = std::unique_ptr<NodeDelegateModel>;
|
|
||||||
using RegistryItemCreator = std::function<RegistryItemPtr()>;
|
|
||||||
using RegisteredModelCreatorsMap = std::unordered_map<QString, RegistryItemCreator>;
|
|
||||||
using RegisteredModelsCategoryMap = std::unordered_map<QString, QString>;
|
|
||||||
using CategoriesSet = std::set<QString>;
|
|
||||||
|
|
||||||
//using RegisteredTypeConvertersMap = std::map<TypeConverterId, TypeConverter>;
|
|
||||||
|
|
||||||
NodeDelegateModelRegistry() = default;
|
|
||||||
~NodeDelegateModelRegistry() = default;
|
|
||||||
|
|
||||||
NodeDelegateModelRegistry(NodeDelegateModelRegistry const &) = delete;
|
|
||||||
NodeDelegateModelRegistry(NodeDelegateModelRegistry &&) = default;
|
|
||||||
|
|
||||||
NodeDelegateModelRegistry &operator=(NodeDelegateModelRegistry const &) = delete;
|
|
||||||
|
|
||||||
NodeDelegateModelRegistry &operator=(NodeDelegateModelRegistry &&) = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template<typename ModelType>
|
|
||||||
void registerModel(RegistryItemCreator creator, QString const &category = "Nodes")
|
|
||||||
{
|
|
||||||
QString const name = computeName<ModelType>(HasStaticMethodName<ModelType>{}, creator);
|
|
||||||
if (!_registeredItemCreators.count(name)) {
|
|
||||||
_registeredItemCreators[name] = std::move(creator);
|
|
||||||
_categories.insert(category);
|
|
||||||
_registeredModelsCategory[name] = category;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ModelType>
|
|
||||||
void registerModel(QString const &category = "Nodes")
|
|
||||||
{
|
|
||||||
RegistryItemCreator creator = []() { return std::make_unique<ModelType>(); };
|
|
||||||
registerModel<ModelType>(std::move(creator), category);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
template<typename ModelType>
|
|
||||||
void
|
|
||||||
registerModel(RegistryItemCreator creator,
|
|
||||||
QString const& category = "Nodes")
|
|
||||||
{
|
|
||||||
registerModel<ModelType>(std::move(creator), category);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename ModelCreator>
|
|
||||||
void
|
|
||||||
registerModel(ModelCreator&& creator, QString const& category = "Nodes")
|
|
||||||
{
|
|
||||||
using ModelType = compute_model_type_t<decltype(creator())>;
|
|
||||||
registerModel<ModelType>(std::forward<ModelCreator>(creator), category);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename ModelCreator>
|
|
||||||
void
|
|
||||||
registerModel(QString const& category, ModelCreator&& creator)
|
|
||||||
{
|
|
||||||
registerModel(std::forward<ModelCreator>(creator), category);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
registerTypeConverter(TypeConverterId const& id,
|
|
||||||
TypeConverter typeConverter)
|
|
||||||
{
|
|
||||||
_registeredTypeConverters[id] = std::move(typeConverter);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::unique_ptr<NodeDelegateModel> create(QString const &modelName);
|
|
||||||
|
|
||||||
RegisteredModelCreatorsMap const ®isteredModelCreators() const;
|
|
||||||
|
|
||||||
RegisteredModelsCategoryMap const ®isteredModelsCategoryAssociation() const;
|
|
||||||
|
|
||||||
CategoriesSet const &categories() const;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
TypeConverter
|
|
||||||
getTypeConverter(NodeDataType const& d1,
|
|
||||||
NodeDataType const& d2) const;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
RegisteredModelsCategoryMap _registeredModelsCategory;
|
|
||||||
|
|
||||||
CategoriesSet _categories;
|
|
||||||
|
|
||||||
RegisteredModelCreatorsMap _registeredItemCreators;
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
RegisteredTypeConvertersMap _registeredTypeConverters;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
// If the registered ModelType class has the static member method
|
|
||||||
// `static QString Name();`, use it. Otherwise use the non-static
|
|
||||||
// method: `virtual QString name() const;`
|
|
||||||
template<typename T, typename = void>
|
|
||||||
struct HasStaticMethodName : std::false_type
|
|
||||||
{};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct HasStaticMethodName<
|
|
||||||
T,
|
|
||||||
typename std::enable_if<std::is_same<decltype(T::Name()), QString>::value>::type>
|
|
||||||
: std::true_type
|
|
||||||
{};
|
|
||||||
|
|
||||||
template<typename ModelType>
|
|
||||||
static QString computeName(std::true_type, RegistryItemCreator const &)
|
|
||||||
{
|
|
||||||
return ModelType::Name();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ModelType>
|
|
||||||
static QString computeName(std::false_type, RegistryItemCreator const &creator)
|
|
||||||
{
|
|
||||||
return creator()->name();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct UnwrapUniquePtr
|
|
||||||
{
|
|
||||||
// Assert always fires, but the compiler doesn't know this:
|
|
||||||
static_assert(!std::is_same<T, T>::value,
|
|
||||||
"The ModelCreator must return a std::unique_ptr<T>, where T "
|
|
||||||
"inherits from NodeDelegateModel");
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct UnwrapUniquePtr<std::unique_ptr<T>>
|
|
||||||
{
|
|
||||||
static_assert(std::is_base_of<NodeDelegateModel, T>::value,
|
|
||||||
"The ModelCreator must return a std::unique_ptr<T>, where T "
|
|
||||||
"inherits from NodeDelegateModel");
|
|
||||||
using type = T;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename CreatorResult>
|
|
||||||
using compute_model_type_t = typename UnwrapUniquePtr<CreatorResult>::type;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtCore/QUuid>
|
|
||||||
#include <QtWidgets/QGraphicsObject>
|
|
||||||
|
|
||||||
#include "NodeState.hpp"
|
|
||||||
|
|
||||||
class QGraphicsProxyWidget;
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class BasicGraphicsScene;
|
|
||||||
class AbstractGraphModel;
|
|
||||||
|
|
||||||
class NodeGraphicsObject : public QGraphicsObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
// Needed for qgraphicsitem_cast
|
|
||||||
enum { Type = UserType + 1 };
|
|
||||||
|
|
||||||
int type() const override { return Type; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
NodeGraphicsObject(BasicGraphicsScene &scene, NodeId node);
|
|
||||||
|
|
||||||
~NodeGraphicsObject() override = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
AbstractGraphModel &graphModel() const;
|
|
||||||
|
|
||||||
BasicGraphicsScene *nodeScene() const;
|
|
||||||
|
|
||||||
NodeId nodeId() { return _nodeId; }
|
|
||||||
|
|
||||||
NodeId nodeId() const { return _nodeId; }
|
|
||||||
|
|
||||||
NodeState &nodeState() { return _nodeState; }
|
|
||||||
|
|
||||||
NodeState const &nodeState() const { return _nodeState; }
|
|
||||||
|
|
||||||
QRectF boundingRect() const override;
|
|
||||||
|
|
||||||
void setGeometryChanged();
|
|
||||||
|
|
||||||
/// Visits all attached connections and corrects
|
|
||||||
/// their corresponding end points.
|
|
||||||
void moveConnections() const;
|
|
||||||
|
|
||||||
/// Repaints the node once with reacting ports.
|
|
||||||
void reactToConnection(ConnectionGraphicsObject const *cgo);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paint(QPainter *painter,
|
|
||||||
QStyleOptionGraphicsItem const *option,
|
|
||||||
QWidget *widget = 0) override;
|
|
||||||
|
|
||||||
QVariant itemChange(GraphicsItemChange change, const QVariant &value) override;
|
|
||||||
|
|
||||||
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
|
|
||||||
|
|
||||||
void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override;
|
|
||||||
|
|
||||||
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override;
|
|
||||||
|
|
||||||
void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override;
|
|
||||||
|
|
||||||
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override;
|
|
||||||
|
|
||||||
void hoverMoveEvent(QGraphicsSceneHoverEvent *) override;
|
|
||||||
|
|
||||||
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override;
|
|
||||||
|
|
||||||
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void embedQWidget();
|
|
||||||
|
|
||||||
void setLockedState();
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeId _nodeId;
|
|
||||||
|
|
||||||
AbstractGraphModel &_graphModel;
|
|
||||||
|
|
||||||
NodeState _nodeState;
|
|
||||||
|
|
||||||
// either nullptr or owned by parent QGraphicsItem
|
|
||||||
QGraphicsProxyWidget *_proxyWidget;
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <QtCore/QPointF>
|
|
||||||
#include <QtCore/QPointer>
|
|
||||||
#include <QtCore/QUuid>
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
#include "NodeData.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class ConnectionGraphicsObject;
|
|
||||||
class NodeGraphicsObject;
|
|
||||||
|
|
||||||
/// Stores bool for hovering connections and resizing flag.
|
|
||||||
class NODE_EDITOR_PUBLIC NodeState
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NodeState(NodeGraphicsObject &ngo);
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool hovered() const { return _hovered; }
|
|
||||||
|
|
||||||
void setHovered(bool hovered = true) { _hovered = hovered; }
|
|
||||||
|
|
||||||
void setResizing(bool resizing);
|
|
||||||
|
|
||||||
bool resizing() const;
|
|
||||||
|
|
||||||
ConnectionGraphicsObject const *connectionForReaction() const;
|
|
||||||
|
|
||||||
void storeConnectionForReaction(ConnectionGraphicsObject const *cgo);
|
|
||||||
|
|
||||||
void resetConnectionForReaction();
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeGraphicsObject &_ngo;
|
|
||||||
|
|
||||||
bool _hovered;
|
|
||||||
|
|
||||||
bool _resizing;
|
|
||||||
|
|
||||||
// QPointer tracks the QObject inside and is automatically cleared
|
|
||||||
// when the object is destroyed.
|
|
||||||
QPointer<ConnectionGraphicsObject const> _connectionForReaction;
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtGui/QColor>
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
#include "Style.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class NODE_EDITOR_PUBLIC NodeStyle : public Style
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NodeStyle();
|
|
||||||
|
|
||||||
NodeStyle(QString jsonText);
|
|
||||||
|
|
||||||
NodeStyle(QJsonObject const &json);
|
|
||||||
|
|
||||||
virtual ~NodeStyle() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void setNodeStyle(QString jsonText);
|
|
||||||
|
|
||||||
public:
|
|
||||||
void loadJson(QJsonObject const &json) override;
|
|
||||||
|
|
||||||
QJsonObject toJson() const override;
|
|
||||||
|
|
||||||
public:
|
|
||||||
QColor NormalBoundaryColor;
|
|
||||||
QColor SelectedBoundaryColor;
|
|
||||||
QColor GradientColor0;
|
|
||||||
QColor GradientColor1;
|
|
||||||
QColor GradientColor2;
|
|
||||||
QColor GradientColor3;
|
|
||||||
QColor ShadowColor;
|
|
||||||
QColor FontColor;
|
|
||||||
QColor FontColorFaded;
|
|
||||||
|
|
||||||
QColor ConnectionPointColor;
|
|
||||||
QColor FilledConnectionPointColor;
|
|
||||||
|
|
||||||
QColor WarningColor;
|
|
||||||
QColor ErrorColor;
|
|
||||||
|
|
||||||
float PenWidth;
|
|
||||||
float HoveredPenWidth;
|
|
||||||
|
|
||||||
float ConnectionPointDiameter;
|
|
||||||
|
|
||||||
float Opacity;
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#if defined(__CYGWIN__) || defined(__CYGWIN32__)
|
|
||||||
#define NODE_EDITOR_PLATFORM "Cygwin"
|
|
||||||
#define NODE_EDITOR_PLATFORM_CYGWIN
|
|
||||||
#define NODE_EDITOR_PLATFORM_UNIX
|
|
||||||
#define NODE_EDITOR_PLATFORM_WINDOWS
|
|
||||||
#elif defined(_WIN16) || defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) \
|
|
||||||
|| defined(__TOS_WIN__) || defined(__WINDOWS__)
|
|
||||||
#define NODE_EDITOR_PLATFORM "Windows"
|
|
||||||
#define NODE_EDITOR_PLATFORM_WINDOWS
|
|
||||||
#elif defined(macintosh) || defined(Macintosh) || defined(__TOS_MACOS__) \
|
|
||||||
|| (defined(__APPLE__) && defined(__MACH__))
|
|
||||||
#define NODE_EDITOR_PLATFORM "Mac"
|
|
||||||
#define NODE_EDITOR_PLATFORM_MAC
|
|
||||||
#define NODE_EDITOR_PLATFORM_UNIX
|
|
||||||
#elif defined(linux) || defined(__linux) || defined(__linux__) || defined(__TOS_LINUX__)
|
|
||||||
#define NODE_EDITOR_PLATFORM "Linux"
|
|
||||||
#define NODE_EDITOR_PLATFORM_LINUX
|
|
||||||
#define NODE_EDITOR_PLATFORM_UNIX
|
|
||||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__bsdi__) \
|
|
||||||
|| defined(__DragonFly__)
|
|
||||||
#define NODE_EDITOR_PLATFORM "BSD"
|
|
||||||
#define NODE_EDITOR_PLATFORM_BSD
|
|
||||||
#define NODE_EDITOR_PLATFORM_UNIX
|
|
||||||
#elif defined(sun) || defined(__sun)
|
|
||||||
#define NODE_EDITOR_PLATFORM "Solaris"
|
|
||||||
#define NODE_EDITOR_PLATFORM_SOLARIS
|
|
||||||
#define NODE_EDITOR_PLATFORM_UNIX
|
|
||||||
#elif defined(_AIX) || defined(__TOS_AIX__)
|
|
||||||
#define NODE_EDITOR_PLATFORM "AIX"
|
|
||||||
#define NODE_EDITOR_PLATFORM_AIX
|
|
||||||
#define NODE_EDITOR_PLATFORM_UNIX
|
|
||||||
#elif defined(hpux) || defined(_hpux) || defined(__hpux)
|
|
||||||
#define NODE_EDITOR_PLATFORM "HPUX"
|
|
||||||
#define NODE_EDITOR_PLATFORM_HPUX
|
|
||||||
#define NODE_EDITOR_PLATFORM_UNIX
|
|
||||||
#elif defined(__QNX__)
|
|
||||||
#define NODE_EDITOR_PLATFORM "QNX"
|
|
||||||
#define NODE_EDITOR_PLATFORM_QNX
|
|
||||||
#define NODE_EDITOR_PLATFORM_UNIX
|
|
||||||
#elif defined(unix) || defined(__unix) || defined(__unix__)
|
|
||||||
#define NODE_EDITOR_PLATFORM "Unix"
|
|
||||||
#define NODE_EDITOR_PLATFORM_UNIX
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NODE_EDITOR_PLATFORM
|
|
||||||
#error "Current platform is not supported."
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
|
|
||||||
|
|
||||||
// As of 5.14 there is a specialization std::hash<QString>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <QtCore/QString>
|
|
||||||
#include <QtCore/QVariant>
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
template<>
|
|
||||||
struct hash<QString>
|
|
||||||
{
|
|
||||||
inline std::size_t operator()(QString const &s) const { return qHash(s); }
|
|
||||||
};
|
|
||||||
} // namespace std
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#include <QtCore/QUuid>
|
|
||||||
#include <QtCore/QVariant>
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
template<>
|
|
||||||
struct hash<QUuid>
|
|
||||||
{
|
|
||||||
inline std::size_t operator()(QUuid const &uid) const { return qHash(uid); }
|
|
||||||
};
|
|
||||||
} // namespace std
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtCore/QJsonObject>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class Serializable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~Serializable() = default;
|
|
||||||
|
|
||||||
virtual QJsonObject save() const { return {}; }
|
|
||||||
|
|
||||||
virtual void load(QJsonObject const & /*p*/) {}
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
#include <QtCore/QFile>
|
|
||||||
#include <QtCore/QJsonDocument>
|
|
||||||
#include <QtCore/QJsonObject>
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <QtCore/QString>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class Style // : public QObject
|
|
||||||
{
|
|
||||||
//Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~Style() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual void loadJson(QJsonObject const &json) = 0;
|
|
||||||
|
|
||||||
virtual QJsonObject toJson() const = 0;
|
|
||||||
|
|
||||||
/// Loads from utf-8 byte array.
|
|
||||||
virtual void loadJsonFromByteArray(QByteArray const &byteArray)
|
|
||||||
{
|
|
||||||
auto json = QJsonDocument::fromJson(byteArray).object();
|
|
||||||
|
|
||||||
loadJson(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void loadJsonText(QString jsonText) { loadJsonFromByteArray(jsonText.toUtf8()); }
|
|
||||||
|
|
||||||
virtual void loadJsonFile(QString fileName)
|
|
||||||
{
|
|
||||||
QFile file(fileName);
|
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadOnly)) {
|
|
||||||
qWarning() << "Couldn't open file " << fileName;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadJsonFromByteArray(file.readAll());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Export.hpp"
|
|
||||||
|
|
||||||
#include "ConnectionStyle.hpp"
|
|
||||||
#include "GraphicsViewStyle.hpp"
|
|
||||||
#include "NodeStyle.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class NODE_EDITOR_PUBLIC StyleCollection
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static NodeStyle const &nodeStyle();
|
|
||||||
|
|
||||||
static ConnectionStyle const &connectionStyle();
|
|
||||||
|
|
||||||
static GraphicsViewStyle const &flowViewStyle();
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void setNodeStyle(NodeStyle);
|
|
||||||
|
|
||||||
static void setConnectionStyle(ConnectionStyle);
|
|
||||||
|
|
||||||
static void setGraphicsViewStyle(GraphicsViewStyle);
|
|
||||||
|
|
||||||
private:
|
|
||||||
StyleCollection() = default;
|
|
||||||
|
|
||||||
StyleCollection(StyleCollection const &) = delete;
|
|
||||||
|
|
||||||
StyleCollection &operator=(StyleCollection const &) = delete;
|
|
||||||
|
|
||||||
static StyleCollection &instance();
|
|
||||||
|
|
||||||
private:
|
|
||||||
NodeStyle _nodeStyle;
|
|
||||||
|
|
||||||
ConnectionStyle _connectionStyle;
|
|
||||||
|
|
||||||
GraphicsViewStyle _flowViewStyle;
|
|
||||||
};
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,123 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
|
|
||||||
#include <QUndoCommand>
|
|
||||||
#include <QtCore/QJsonObject>
|
|
||||||
#include <QtCore/QPointF>
|
|
||||||
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class BasicGraphicsScene;
|
|
||||||
|
|
||||||
class CreateCommand : public QUndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CreateCommand(BasicGraphicsScene *scene, QString const name, QPointF const &mouseScenePos);
|
|
||||||
|
|
||||||
void undo() override;
|
|
||||||
void redo() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
BasicGraphicsScene *_scene;
|
|
||||||
NodeId _nodeId;
|
|
||||||
QJsonObject _sceneJson;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selected scene objects are serialized and then removed from the scene.
|
|
||||||
* The deleted elements could be restored in `undo`.
|
|
||||||
*/
|
|
||||||
class DeleteCommand : public QUndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DeleteCommand(BasicGraphicsScene *scene);
|
|
||||||
|
|
||||||
void undo() override;
|
|
||||||
void redo() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
BasicGraphicsScene *_scene;
|
|
||||||
QJsonObject _sceneJson;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CopyCommand : public QUndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CopyCommand(BasicGraphicsScene *scene);
|
|
||||||
};
|
|
||||||
|
|
||||||
class PasteCommand : public QUndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PasteCommand(BasicGraphicsScene *scene, QPointF const &mouseScenePos);
|
|
||||||
|
|
||||||
void undo() override;
|
|
||||||
void redo() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QJsonObject takeSceneJsonFromClipboard();
|
|
||||||
QJsonObject makeNewNodeIdsInScene(QJsonObject const &sceneJson);
|
|
||||||
|
|
||||||
private:
|
|
||||||
BasicGraphicsScene *_scene;
|
|
||||||
QPointF const &_mouseScenePos;
|
|
||||||
QJsonObject _newSceneJson;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DisconnectCommand : public QUndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DisconnectCommand(BasicGraphicsScene *scene, ConnectionId const);
|
|
||||||
|
|
||||||
void undo() override;
|
|
||||||
void redo() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
BasicGraphicsScene *_scene;
|
|
||||||
|
|
||||||
ConnectionId _connId;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ConnectCommand : public QUndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ConnectCommand(BasicGraphicsScene *scene, ConnectionId const);
|
|
||||||
|
|
||||||
void undo() override;
|
|
||||||
void redo() override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
BasicGraphicsScene *_scene;
|
|
||||||
|
|
||||||
ConnectionId _connId;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MoveNodeCommand : public QUndoCommand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MoveNodeCommand(BasicGraphicsScene *scene, QPointF const &diff);
|
|
||||||
|
|
||||||
void undo() override;
|
|
||||||
void redo() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A command ID is used in command compression. It must be an integer unique to
|
|
||||||
* this command's class, or -1 if the command doesn't support compression.
|
|
||||||
*/
|
|
||||||
int id() const override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Several sequential movements could be merged into one command.
|
|
||||||
*/
|
|
||||||
bool mergeWith(QUndoCommand const *c) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
BasicGraphicsScene *_scene;
|
|
||||||
std::unordered_set<NodeId> _selectedNodes;
|
|
||||||
QPointF _diff;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QtCore/QPointF>
|
|
||||||
#include <QtGui/QTransform>
|
|
||||||
|
|
||||||
class QGraphicsScene;
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
class NodeGraphicsObject;
|
|
||||||
|
|
||||||
NodeGraphicsObject *locateNodeAt(QPointF scenePoint,
|
|
||||||
QGraphicsScene &scene,
|
|
||||||
QTransform const &viewTransform);
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
{
|
|
||||||
"GraphicsViewStyle": {
|
|
||||||
"BackgroundColor": [53, 53, 53],
|
|
||||||
"FineGridColor": [60, 60, 60],
|
|
||||||
"CoarseGridColor": [25, 25, 25]
|
|
||||||
},
|
|
||||||
"NodeStyle": {
|
|
||||||
"NormalBoundaryColor": [255, 255, 255],
|
|
||||||
"SelectedBoundaryColor": [255, 165, 0],
|
|
||||||
"GradientColor0": "gray",
|
|
||||||
"GradientColor1": [80, 80, 80],
|
|
||||||
"GradientColor2": [64, 64, 64],
|
|
||||||
"GradientColor3": [58, 58, 58],
|
|
||||||
"ShadowColor": [20, 20, 20],
|
|
||||||
"FontColor" : "white",
|
|
||||||
"FontColorFaded" : "gray",
|
|
||||||
"ConnectionPointColor": [169, 169, 169],
|
|
||||||
"FilledConnectionPointColor": "cyan",
|
|
||||||
"ErrorColor": "red",
|
|
||||||
"WarningColor": [128, 128, 0],
|
|
||||||
|
|
||||||
"PenWidth": 1.0,
|
|
||||||
"HoveredPenWidth": 1.5,
|
|
||||||
|
|
||||||
"ConnectionPointDiameter": 8.0,
|
|
||||||
|
|
||||||
"Opacity": 0.8
|
|
||||||
},
|
|
||||||
"ConnectionStyle": {
|
|
||||||
"ConstructionColor": "gray",
|
|
||||||
"NormalColor": "darkcyan",
|
|
||||||
"SelectedColor": [100, 100, 100],
|
|
||||||
"SelectedHaloColor": "orange",
|
|
||||||
"HoveredColor": "lightcyan",
|
|
||||||
|
|
||||||
"LineWidth": 3.0,
|
|
||||||
"ConstructionLineWidth": 2.0,
|
|
||||||
"PointDiameter": 10.0,
|
|
||||||
|
|
||||||
"UseDataDefinedColors": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<RCC version="1.0">
|
|
||||||
<qresource>
|
|
||||||
<file>DefaultStyle.json</file>
|
|
||||||
</qresource>
|
|
||||||
</RCC>
|
|
||||||
|
|
@ -1,105 +0,0 @@
|
||||||
#include "AbstractGraphModel.hpp"
|
|
||||||
|
|
||||||
#include <QtNodes/ConnectionIdUtils>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
void AbstractGraphModel::portsAboutToBeDeleted(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const first,
|
|
||||||
PortIndex const last)
|
|
||||||
{
|
|
||||||
_shiftedByDynamicPortsConnections.clear();
|
|
||||||
|
|
||||||
auto portCountRole = portType == PortType::In ? NodeRole::InPortCount : NodeRole::OutPortCount;
|
|
||||||
|
|
||||||
unsigned int portCount = nodeData(nodeId, portCountRole).toUInt();
|
|
||||||
|
|
||||||
if (first > portCount - 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (last < first)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto clampedLast = std::min(last, portCount - 1);
|
|
||||||
|
|
||||||
for (PortIndex portIndex = first; portIndex <= clampedLast; ++portIndex) {
|
|
||||||
std::unordered_set<ConnectionId> conns = connections(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
for (auto connectionId : conns) {
|
|
||||||
deleteConnection(connectionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t const nRemovedPorts = clampedLast - first + 1;
|
|
||||||
|
|
||||||
for (PortIndex portIndex = clampedLast + 1; portIndex < portCount; ++portIndex) {
|
|
||||||
std::unordered_set<ConnectionId> conns = connections(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
for (auto connectionId : conns) {
|
|
||||||
// Erases the information about the port on one side;
|
|
||||||
auto c = makeIncompleteConnectionId(connectionId, portType);
|
|
||||||
|
|
||||||
c = makeCompleteConnectionId(c, nodeId, portIndex - nRemovedPorts);
|
|
||||||
|
|
||||||
_shiftedByDynamicPortsConnections.push_back(c);
|
|
||||||
|
|
||||||
deleteConnection(connectionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractGraphModel::portsDeleted()
|
|
||||||
{
|
|
||||||
for (auto const connectionId : _shiftedByDynamicPortsConnections) {
|
|
||||||
addConnection(connectionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
_shiftedByDynamicPortsConnections.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractGraphModel::portsAboutToBeInserted(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const first,
|
|
||||||
PortIndex const last)
|
|
||||||
{
|
|
||||||
_shiftedByDynamicPortsConnections.clear();
|
|
||||||
|
|
||||||
auto portCountRole = portType == PortType::In ? NodeRole::InPortCount : NodeRole::OutPortCount;
|
|
||||||
|
|
||||||
unsigned int portCount = nodeData(nodeId, portCountRole).toUInt();
|
|
||||||
|
|
||||||
if (first > portCount)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (last < first)
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::size_t const nNewPorts = last - first + 1;
|
|
||||||
|
|
||||||
for (PortIndex portIndex = first; portIndex < portCount; ++portIndex) {
|
|
||||||
std::unordered_set<ConnectionId> conns = connections(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
for (auto connectionId : conns) {
|
|
||||||
// Erases the information about the port on one side;
|
|
||||||
auto c = makeIncompleteConnectionId(connectionId, portType);
|
|
||||||
|
|
||||||
c = makeCompleteConnectionId(c, nodeId, portIndex + nNewPorts);
|
|
||||||
|
|
||||||
_shiftedByDynamicPortsConnections.push_back(c);
|
|
||||||
|
|
||||||
deleteConnection(connectionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractGraphModel::portsInserted()
|
|
||||||
{
|
|
||||||
for (auto const connectionId : _shiftedByDynamicPortsConnections) {
|
|
||||||
addConnection(connectionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
_shiftedByDynamicPortsConnections.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
#include "AbstractNodeGeometry.hpp"
|
|
||||||
|
|
||||||
#include "AbstractGraphModel.hpp"
|
|
||||||
#include "StyleCollection.hpp"
|
|
||||||
|
|
||||||
#include <QMargins>
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
AbstractNodeGeometry::AbstractNodeGeometry(AbstractGraphModel &graphModel, double marginsRatio)
|
|
||||||
: _graphModel(graphModel)
|
|
||||||
, _marginsRatio(marginsRatio)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectF AbstractNodeGeometry::boundingRect(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
QSize s = size(nodeId);
|
|
||||||
|
|
||||||
int widthMargin = s.width() * _marginsRatio;
|
|
||||||
int heightMargin = s.height() * _marginsRatio;
|
|
||||||
|
|
||||||
QMargins margins(widthMargin, heightMargin, widthMargin, heightMargin);
|
|
||||||
|
|
||||||
QRectF r(QPointF(0, 0), s);
|
|
||||||
|
|
||||||
return r.marginsAdded(margins);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AbstractNodeGeometry::setMarginsRatio(double marginsRatio)
|
|
||||||
{
|
|
||||||
_marginsRatio = marginsRatio;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF AbstractNodeGeometry::portScenePosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const index,
|
|
||||||
QTransform const &t) const
|
|
||||||
{
|
|
||||||
QPointF result = portPosition(nodeId, portType, index);
|
|
||||||
|
|
||||||
return t.map(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
PortIndex AbstractNodeGeometry::checkPortHit(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
QPointF const nodePoint) const
|
|
||||||
{
|
|
||||||
auto const &nodeStyle = StyleCollection::nodeStyle();
|
|
||||||
|
|
||||||
PortIndex result = InvalidPortIndex;
|
|
||||||
|
|
||||||
if (portType == PortType::None)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
double const tolerance = 2.0 * nodeStyle.ConnectionPointDiameter;
|
|
||||||
|
|
||||||
size_t const n = _graphModel.nodeData<unsigned int>(nodeId,
|
|
||||||
(portType == PortType::Out)
|
|
||||||
? NodeRole::OutPortCount
|
|
||||||
: NodeRole::InPortCount);
|
|
||||||
|
|
||||||
for (unsigned int portIndex = 0; portIndex < n; ++portIndex) {
|
|
||||||
auto pp = portPosition(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
QPointF p = pp - nodePoint;
|
|
||||||
auto distance = std::sqrt(QPointF::dotProduct(p, p));
|
|
||||||
|
|
||||||
if (distance < tolerance) {
|
|
||||||
result = portIndex;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,308 +0,0 @@
|
||||||
#include "BasicGraphicsScene.hpp"
|
|
||||||
|
|
||||||
#include "AbstractNodeGeometry.hpp"
|
|
||||||
#include "ConnectionGraphicsObject.hpp"
|
|
||||||
#include "ConnectionIdUtils.hpp"
|
|
||||||
#include "DefaultHorizontalNodeGeometry.hpp"
|
|
||||||
#include "DefaultNodePainter.hpp"
|
|
||||||
#include "DefaultVerticalNodeGeometry.hpp"
|
|
||||||
#include "GraphicsView.hpp"
|
|
||||||
#include "NodeGraphicsObject.hpp"
|
|
||||||
|
|
||||||
#include <QUndoStack>
|
|
||||||
|
|
||||||
#include <QtWidgets/QFileDialog>
|
|
||||||
#include <QtWidgets/QGraphicsSceneMoveEvent>
|
|
||||||
|
|
||||||
#include <QtCore/QBuffer>
|
|
||||||
#include <QtCore/QByteArray>
|
|
||||||
#include <QtCore/QDataStream>
|
|
||||||
#include <QtCore/QFile>
|
|
||||||
#include <QtCore/QJsonArray>
|
|
||||||
#include <QtCore/QJsonDocument>
|
|
||||||
#include <QtCore/QJsonObject>
|
|
||||||
#include <QtCore/QtGlobal>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <utility>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
BasicGraphicsScene::BasicGraphicsScene(AbstractGraphModel &graphModel, QObject *parent)
|
|
||||||
: QGraphicsScene(parent)
|
|
||||||
, _graphModel(graphModel)
|
|
||||||
, _nodeGeometry(std::make_unique<DefaultHorizontalNodeGeometry>(_graphModel))
|
|
||||||
, _nodePainter(std::make_unique<DefaultNodePainter>())
|
|
||||||
, _nodeDrag(false)
|
|
||||||
, _undoStack(new QUndoStack(this))
|
|
||||||
, _orientation(Qt::Horizontal)
|
|
||||||
{
|
|
||||||
setItemIndexMethod(QGraphicsScene::NoIndex);
|
|
||||||
|
|
||||||
connect(&_graphModel,
|
|
||||||
&AbstractGraphModel::connectionCreated,
|
|
||||||
this,
|
|
||||||
&BasicGraphicsScene::onConnectionCreated);
|
|
||||||
|
|
||||||
connect(&_graphModel,
|
|
||||||
&AbstractGraphModel::connectionDeleted,
|
|
||||||
this,
|
|
||||||
&BasicGraphicsScene::onConnectionDeleted);
|
|
||||||
|
|
||||||
connect(&_graphModel,
|
|
||||||
&AbstractGraphModel::nodeCreated,
|
|
||||||
this,
|
|
||||||
&BasicGraphicsScene::onNodeCreated);
|
|
||||||
|
|
||||||
connect(&_graphModel,
|
|
||||||
&AbstractGraphModel::nodeDeleted,
|
|
||||||
this,
|
|
||||||
&BasicGraphicsScene::onNodeDeleted);
|
|
||||||
|
|
||||||
connect(&_graphModel,
|
|
||||||
&AbstractGraphModel::nodePositionUpdated,
|
|
||||||
this,
|
|
||||||
&BasicGraphicsScene::onNodePositionUpdated);
|
|
||||||
|
|
||||||
connect(&_graphModel,
|
|
||||||
&AbstractGraphModel::nodeUpdated,
|
|
||||||
this,
|
|
||||||
&BasicGraphicsScene::onNodeUpdated);
|
|
||||||
|
|
||||||
connect(this, &BasicGraphicsScene::nodeClicked, this, &BasicGraphicsScene::onNodeClicked);
|
|
||||||
|
|
||||||
connect(&_graphModel, &AbstractGraphModel::modelReset, this, &BasicGraphicsScene::onModelReset);
|
|
||||||
|
|
||||||
traverseGraphAndPopulateGraphicsObjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicGraphicsScene::~BasicGraphicsScene() = default;
|
|
||||||
|
|
||||||
AbstractGraphModel const &BasicGraphicsScene::graphModel() const
|
|
||||||
{
|
|
||||||
return _graphModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractGraphModel &BasicGraphicsScene::graphModel()
|
|
||||||
{
|
|
||||||
return _graphModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractNodeGeometry &BasicGraphicsScene::nodeGeometry()
|
|
||||||
{
|
|
||||||
return *_nodeGeometry;
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractNodePainter &BasicGraphicsScene::nodePainter()
|
|
||||||
{
|
|
||||||
return *_nodePainter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::setNodePainter(std::unique_ptr<AbstractNodePainter> newPainter)
|
|
||||||
{
|
|
||||||
_nodePainter = std::move(newPainter);
|
|
||||||
}
|
|
||||||
|
|
||||||
QUndoStack &BasicGraphicsScene::undoStack()
|
|
||||||
{
|
|
||||||
return *_undoStack;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::setDropShadowEffect(bool enable)
|
|
||||||
{
|
|
||||||
_dropShadowEffect = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BasicGraphicsScene::isDropShadowEffectEnabled() const
|
|
||||||
{
|
|
||||||
return _dropShadowEffect;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<ConnectionGraphicsObject> const &BasicGraphicsScene::makeDraftConnection(
|
|
||||||
ConnectionId const incompleteConnectionId)
|
|
||||||
{
|
|
||||||
_draftConnection = std::make_unique<ConnectionGraphicsObject>(*this, incompleteConnectionId);
|
|
||||||
|
|
||||||
_draftConnection->grabMouse();
|
|
||||||
|
|
||||||
return _draftConnection;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::resetDraftConnection()
|
|
||||||
{
|
|
||||||
_draftConnection.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::clearScene()
|
|
||||||
{
|
|
||||||
auto const &allNodeIds = graphModel().allNodeIds();
|
|
||||||
|
|
||||||
for (auto nodeId : allNodeIds) {
|
|
||||||
graphModel().deleteNode(nodeId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeGraphicsObject *BasicGraphicsScene::nodeGraphicsObject(NodeId nodeId)
|
|
||||||
{
|
|
||||||
NodeGraphicsObject *ngo = nullptr;
|
|
||||||
auto it = _nodeGraphicsObjects.find(nodeId);
|
|
||||||
if (it != _nodeGraphicsObjects.end()) {
|
|
||||||
ngo = it->second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ngo;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionGraphicsObject *BasicGraphicsScene::connectionGraphicsObject(ConnectionId connectionId)
|
|
||||||
{
|
|
||||||
ConnectionGraphicsObject *cgo = nullptr;
|
|
||||||
auto it = _connectionGraphicsObjects.find(connectionId);
|
|
||||||
if (it != _connectionGraphicsObjects.end()) {
|
|
||||||
cgo = it->second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return cgo;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::setOrientation(Qt::Orientation const orientation)
|
|
||||||
{
|
|
||||||
if (_orientation != orientation) {
|
|
||||||
_orientation = orientation;
|
|
||||||
|
|
||||||
switch (_orientation) {
|
|
||||||
case Qt::Horizontal:
|
|
||||||
_nodeGeometry = std::make_unique<DefaultHorizontalNodeGeometry>(_graphModel);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Qt::Vertical:
|
|
||||||
_nodeGeometry = std::make_unique<DefaultVerticalNodeGeometry>(_graphModel);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
onModelReset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QMenu *BasicGraphicsScene::createSceneMenu(QPointF const scenePos)
|
|
||||||
{
|
|
||||||
Q_UNUSED(scenePos);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::traverseGraphAndPopulateGraphicsObjects()
|
|
||||||
{
|
|
||||||
auto allNodeIds = _graphModel.allNodeIds();
|
|
||||||
|
|
||||||
// First create all the nodes.
|
|
||||||
for (NodeId const nodeId : allNodeIds) {
|
|
||||||
_nodeGraphicsObjects[nodeId] = std::make_unique<NodeGraphicsObject>(*this, nodeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then for each node check output connections and insert them.
|
|
||||||
for (NodeId const nodeId : allNodeIds) {
|
|
||||||
unsigned int nOutPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::OutPortCount);
|
|
||||||
|
|
||||||
for (PortIndex index = 0; index < nOutPorts; ++index) {
|
|
||||||
auto const &outConnectionIds = _graphModel.connections(nodeId, PortType::Out, index);
|
|
||||||
|
|
||||||
for (auto cid : outConnectionIds) {
|
|
||||||
_connectionGraphicsObjects[cid] = std::make_unique<ConnectionGraphicsObject>(*this,
|
|
||||||
cid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::updateAttachedNodes(ConnectionId const connectionId,
|
|
||||||
PortType const portType)
|
|
||||||
{
|
|
||||||
auto node = nodeGraphicsObject(getNodeId(portType, connectionId));
|
|
||||||
|
|
||||||
if (node) {
|
|
||||||
node->update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::onConnectionDeleted(ConnectionId const connectionId)
|
|
||||||
{
|
|
||||||
auto it = _connectionGraphicsObjects.find(connectionId);
|
|
||||||
if (it != _connectionGraphicsObjects.end()) {
|
|
||||||
_connectionGraphicsObjects.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: do we need it?
|
|
||||||
if (_draftConnection && _draftConnection->connectionId() == connectionId) {
|
|
||||||
_draftConnection.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateAttachedNodes(connectionId, PortType::Out);
|
|
||||||
updateAttachedNodes(connectionId, PortType::In);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::onConnectionCreated(ConnectionId const connectionId)
|
|
||||||
{
|
|
||||||
_connectionGraphicsObjects[connectionId]
|
|
||||||
= std::make_unique<ConnectionGraphicsObject>(*this, connectionId);
|
|
||||||
|
|
||||||
updateAttachedNodes(connectionId, PortType::Out);
|
|
||||||
updateAttachedNodes(connectionId, PortType::In);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::onNodeDeleted(NodeId const nodeId)
|
|
||||||
{
|
|
||||||
auto it = _nodeGraphicsObjects.find(nodeId);
|
|
||||||
if (it != _nodeGraphicsObjects.end()) {
|
|
||||||
_nodeGraphicsObjects.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::onNodeCreated(NodeId const nodeId)
|
|
||||||
{
|
|
||||||
_nodeGraphicsObjects[nodeId] = std::make_unique<NodeGraphicsObject>(*this, nodeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::onNodePositionUpdated(NodeId const nodeId)
|
|
||||||
{
|
|
||||||
auto node = nodeGraphicsObject(nodeId);
|
|
||||||
if (node) {
|
|
||||||
node->setPos(_graphModel.nodeData(nodeId, NodeRole::Position).value<QPointF>());
|
|
||||||
node->update();
|
|
||||||
_nodeDrag = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::onNodeUpdated(NodeId const nodeId)
|
|
||||||
{
|
|
||||||
auto node = nodeGraphicsObject(nodeId);
|
|
||||||
|
|
||||||
if (node) {
|
|
||||||
node->setGeometryChanged();
|
|
||||||
|
|
||||||
_nodeGeometry->recomputeSize(nodeId);
|
|
||||||
|
|
||||||
node->update();
|
|
||||||
node->moveConnections();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::onNodeClicked(NodeId const nodeId)
|
|
||||||
{
|
|
||||||
if (_nodeDrag)
|
|
||||||
Q_EMIT nodeMoved(nodeId, _graphModel.nodeData(nodeId, NodeRole::Position).value<QPointF>());
|
|
||||||
_nodeDrag = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BasicGraphicsScene::onModelReset()
|
|
||||||
{
|
|
||||||
_connectionGraphicsObjects.clear();
|
|
||||||
_nodeGraphicsObjects.clear();
|
|
||||||
|
|
||||||
clear();
|
|
||||||
|
|
||||||
traverseGraphAndPopulateGraphicsObjects();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,380 +0,0 @@
|
||||||
#include "ConnectionGraphicsObject.hpp"
|
|
||||||
|
|
||||||
#include "AbstractGraphModel.hpp"
|
|
||||||
#include "AbstractNodeGeometry.hpp"
|
|
||||||
#include "BasicGraphicsScene.hpp"
|
|
||||||
#include "ConnectionIdUtils.hpp"
|
|
||||||
#include "ConnectionPainter.hpp"
|
|
||||||
#include "ConnectionState.hpp"
|
|
||||||
#include "ConnectionStyle.hpp"
|
|
||||||
#include "NodeConnectionInteraction.hpp"
|
|
||||||
#include "NodeGraphicsObject.hpp"
|
|
||||||
#include "StyleCollection.hpp"
|
|
||||||
#include "locateNode.hpp"
|
|
||||||
|
|
||||||
#include <QtWidgets/QGraphicsBlurEffect>
|
|
||||||
#include <QtWidgets/QGraphicsDropShadowEffect>
|
|
||||||
#include <QtWidgets/QGraphicsSceneMouseEvent>
|
|
||||||
#include <QtWidgets/QGraphicsView>
|
|
||||||
#include <QtWidgets/QStyleOptionGraphicsItem>
|
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
ConnectionGraphicsObject::ConnectionGraphicsObject(BasicGraphicsScene &scene,
|
|
||||||
ConnectionId const connectionId)
|
|
||||||
: _connectionId(connectionId)
|
|
||||||
, _graphModel(scene.graphModel())
|
|
||||||
, _connectionState(*this)
|
|
||||||
, _out{0, 0}
|
|
||||||
, _in{0, 0}
|
|
||||||
{
|
|
||||||
scene.addItem(this);
|
|
||||||
|
|
||||||
setFlag(QGraphicsItem::ItemIsMovable, true);
|
|
||||||
setFlag(QGraphicsItem::ItemIsFocusable, true);
|
|
||||||
setFlag(QGraphicsItem::ItemIsSelectable, true);
|
|
||||||
|
|
||||||
setAcceptHoverEvents(true);
|
|
||||||
|
|
||||||
//addGraphicsEffect();
|
|
||||||
|
|
||||||
setZValue(-1.0);
|
|
||||||
|
|
||||||
initializePosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionGraphicsObject::initializePosition()
|
|
||||||
{
|
|
||||||
// This function is only called when the ConnectionGraphicsObject
|
|
||||||
// is newly created. At this moment both end coordinates are (0, 0)
|
|
||||||
// in Connection G.O. coordinates. The position of the whole
|
|
||||||
// Connection G. O. in scene coordinate system is also (0, 0).
|
|
||||||
// By moving the whole object to the Node Port position
|
|
||||||
// we position both connection ends correctly.
|
|
||||||
|
|
||||||
if (_connectionState.requiredPort() != PortType::None) {
|
|
||||||
PortType attachedPort = oppositePort(_connectionState.requiredPort());
|
|
||||||
|
|
||||||
PortIndex portIndex = getPortIndex(attachedPort, _connectionId);
|
|
||||||
NodeId nodeId = getNodeId(attachedPort, _connectionId);
|
|
||||||
|
|
||||||
NodeGraphicsObject *ngo = nodeScene()->nodeGraphicsObject(nodeId);
|
|
||||||
|
|
||||||
if (ngo) {
|
|
||||||
QTransform nodeSceneTransform = ngo->sceneTransform();
|
|
||||||
|
|
||||||
AbstractNodeGeometry &geometry = nodeScene()->nodeGeometry();
|
|
||||||
|
|
||||||
QPointF pos = geometry.portScenePosition(nodeId,
|
|
||||||
attachedPort,
|
|
||||||
portIndex,
|
|
||||||
nodeSceneTransform);
|
|
||||||
|
|
||||||
this->setPos(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
move();
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractGraphModel &ConnectionGraphicsObject::graphModel() const
|
|
||||||
{
|
|
||||||
return _graphModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicGraphicsScene *ConnectionGraphicsObject::nodeScene() const
|
|
||||||
{
|
|
||||||
return dynamic_cast<BasicGraphicsScene *>(scene());
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionId const &ConnectionGraphicsObject::connectionId() const
|
|
||||||
{
|
|
||||||
return _connectionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectF ConnectionGraphicsObject::boundingRect() const
|
|
||||||
{
|
|
||||||
auto points = pointsC1C2();
|
|
||||||
|
|
||||||
// `normalized()` fixes inverted rects.
|
|
||||||
QRectF basicRect = QRectF(_out, _in).normalized();
|
|
||||||
|
|
||||||
QRectF c1c2Rect = QRectF(points.first, points.second).normalized();
|
|
||||||
|
|
||||||
QRectF commonRect = basicRect.united(c1c2Rect);
|
|
||||||
|
|
||||||
auto const &connectionStyle = StyleCollection::connectionStyle();
|
|
||||||
float const diam = connectionStyle.pointDiameter();
|
|
||||||
QPointF const cornerOffset(diam, diam);
|
|
||||||
|
|
||||||
// Expand rect by port circle diameter
|
|
||||||
commonRect.setTopLeft(commonRect.topLeft() - cornerOffset);
|
|
||||||
commonRect.setBottomRight(commonRect.bottomRight() + 2 * cornerOffset);
|
|
||||||
|
|
||||||
return commonRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPainterPath ConnectionGraphicsObject::shape() const
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_DRAWING
|
|
||||||
|
|
||||||
//QPainterPath path;
|
|
||||||
|
|
||||||
//path.addRect(boundingRect());
|
|
||||||
//return path;
|
|
||||||
|
|
||||||
#else
|
|
||||||
return ConnectionPainter::getPainterStroke(*this);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF const &ConnectionGraphicsObject::endPoint(PortType portType) const
|
|
||||||
{
|
|
||||||
Q_ASSERT(portType != PortType::None);
|
|
||||||
|
|
||||||
return (portType == PortType::Out ? _out : _in);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionGraphicsObject::setEndPoint(PortType portType, QPointF const &point)
|
|
||||||
{
|
|
||||||
if (portType == PortType::In)
|
|
||||||
_in = point;
|
|
||||||
else
|
|
||||||
_out = point;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionGraphicsObject::move()
|
|
||||||
{
|
|
||||||
auto moveEnd = [this](ConnectionId cId, PortType portType) {
|
|
||||||
NodeId nodeId = getNodeId(portType, cId);
|
|
||||||
|
|
||||||
if (nodeId == InvalidNodeId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
NodeGraphicsObject *ngo = nodeScene()->nodeGraphicsObject(nodeId);
|
|
||||||
|
|
||||||
if (ngo) {
|
|
||||||
AbstractNodeGeometry &geometry = nodeScene()->nodeGeometry();
|
|
||||||
|
|
||||||
QPointF scenePos = geometry.portScenePosition(nodeId,
|
|
||||||
portType,
|
|
||||||
getPortIndex(portType, cId),
|
|
||||||
ngo->sceneTransform());
|
|
||||||
|
|
||||||
QPointF connectionPos = sceneTransform().inverted().map(scenePos);
|
|
||||||
|
|
||||||
setEndPoint(portType, connectionPos);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
moveEnd(_connectionId, PortType::Out);
|
|
||||||
moveEnd(_connectionId, PortType::In);
|
|
||||||
|
|
||||||
prepareGeometryChange();
|
|
||||||
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionState const &ConnectionGraphicsObject::connectionState() const
|
|
||||||
{
|
|
||||||
return _connectionState;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionState &ConnectionGraphicsObject::connectionState()
|
|
||||||
{
|
|
||||||
return _connectionState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionGraphicsObject::paint(QPainter *painter,
|
|
||||||
QStyleOptionGraphicsItem const *option,
|
|
||||||
QWidget *)
|
|
||||||
{
|
|
||||||
if (!scene())
|
|
||||||
return;
|
|
||||||
|
|
||||||
painter->setClipRect(option->exposedRect);
|
|
||||||
|
|
||||||
ConnectionPainter::paint(painter, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionGraphicsObject::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
||||||
{
|
|
||||||
QGraphicsItem::mousePressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionGraphicsObject::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
||||||
{
|
|
||||||
prepareGeometryChange();
|
|
||||||
|
|
||||||
auto view = static_cast<QGraphicsView *>(event->widget());
|
|
||||||
auto ngo = locateNodeAt(event->scenePos(), *nodeScene(), view->transform());
|
|
||||||
if (ngo) {
|
|
||||||
ngo->reactToConnection(this);
|
|
||||||
|
|
||||||
_connectionState.setLastHoveredNode(ngo->nodeId());
|
|
||||||
} else {
|
|
||||||
_connectionState.resetLastHoveredNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------
|
|
||||||
|
|
||||||
auto requiredPort = _connectionState.requiredPort();
|
|
||||||
|
|
||||||
if (requiredPort != PortType::None) {
|
|
||||||
setEndPoint(requiredPort, event->pos());
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------
|
|
||||||
|
|
||||||
update();
|
|
||||||
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionGraphicsObject::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
||||||
{
|
|
||||||
QGraphicsItem::mouseReleaseEvent(event);
|
|
||||||
|
|
||||||
ungrabMouse();
|
|
||||||
event->accept();
|
|
||||||
|
|
||||||
auto view = static_cast<QGraphicsView *>(event->widget());
|
|
||||||
|
|
||||||
Q_ASSERT(view);
|
|
||||||
|
|
||||||
auto ngo = locateNodeAt(event->scenePos(), *nodeScene(), view->transform());
|
|
||||||
|
|
||||||
bool wasConnected = false;
|
|
||||||
|
|
||||||
if (ngo) {
|
|
||||||
NodeConnectionInteraction interaction(*ngo, *this, *nodeScene());
|
|
||||||
|
|
||||||
wasConnected = interaction.tryConnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If connection attempt was unsuccessful
|
|
||||||
if (!wasConnected) {
|
|
||||||
// Resulting unique_ptr is not used and automatically deleted.
|
|
||||||
nodeScene()->resetDraftConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionGraphicsObject::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
|
|
||||||
{
|
|
||||||
_connectionState.setHovered(true);
|
|
||||||
|
|
||||||
update();
|
|
||||||
|
|
||||||
// Signal
|
|
||||||
nodeScene()->connectionHovered(connectionId(), event->screenPos());
|
|
||||||
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionGraphicsObject::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
|
|
||||||
{
|
|
||||||
_connectionState.setHovered(false);
|
|
||||||
|
|
||||||
update();
|
|
||||||
|
|
||||||
// Signal
|
|
||||||
nodeScene()->connectionHoverLeft(connectionId());
|
|
||||||
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<QPointF, QPointF> ConnectionGraphicsObject::pointsC1C2() const
|
|
||||||
{
|
|
||||||
switch (nodeScene()->orientation()) {
|
|
||||||
case Qt::Horizontal:
|
|
||||||
return pointsC1C2Horizontal();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Qt::Vertical:
|
|
||||||
return pointsC1C2Vertical();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::logic_error("Unreachable code after switch statement");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionGraphicsObject::addGraphicsEffect()
|
|
||||||
{
|
|
||||||
auto effect = new QGraphicsBlurEffect;
|
|
||||||
|
|
||||||
effect->setBlurRadius(5);
|
|
||||||
setGraphicsEffect(effect);
|
|
||||||
|
|
||||||
//auto effect = new QGraphicsDropShadowEffect;
|
|
||||||
//auto effect = new ConnectionBlurEffect(this);
|
|
||||||
//effect->setOffset(4, 4);
|
|
||||||
//effect->setColor(QColor(Qt::gray).darker(800));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<QPointF, QPointF> ConnectionGraphicsObject::pointsC1C2Horizontal() const
|
|
||||||
{
|
|
||||||
double const defaultOffset = 200;
|
|
||||||
|
|
||||||
double xDistance = _in.x() - _out.x();
|
|
||||||
|
|
||||||
double horizontalOffset = qMin(defaultOffset, std::abs(xDistance));
|
|
||||||
|
|
||||||
double verticalOffset = 0;
|
|
||||||
|
|
||||||
double ratioX = 0.5;
|
|
||||||
|
|
||||||
if (xDistance <= 0) {
|
|
||||||
double yDistance = _in.y() - _out.y() + 20;
|
|
||||||
|
|
||||||
double vector = yDistance < 0 ? -1.0 : 1.0;
|
|
||||||
|
|
||||||
verticalOffset = qMin(defaultOffset, std::abs(yDistance)) * vector;
|
|
||||||
|
|
||||||
ratioX = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
horizontalOffset *= ratioX;
|
|
||||||
|
|
||||||
QPointF c1(_out.x() + horizontalOffset, _out.y() + verticalOffset);
|
|
||||||
|
|
||||||
QPointF c2(_in.x() - horizontalOffset, _in.y() - verticalOffset);
|
|
||||||
|
|
||||||
return std::make_pair(c1, c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<QPointF, QPointF> ConnectionGraphicsObject::pointsC1C2Vertical() const
|
|
||||||
{
|
|
||||||
double const defaultOffset = 200;
|
|
||||||
|
|
||||||
double yDistance = _in.y() - _out.y();
|
|
||||||
|
|
||||||
double verticalOffset = qMin(defaultOffset, std::abs(yDistance));
|
|
||||||
|
|
||||||
double horizontalOffset = 0;
|
|
||||||
|
|
||||||
double ratioY = 0.5;
|
|
||||||
|
|
||||||
if (yDistance <= 0) {
|
|
||||||
double xDistance = _in.x() - _out.x() + 20;
|
|
||||||
|
|
||||||
double vector = xDistance < 0 ? -1.0 : 1.0;
|
|
||||||
|
|
||||||
horizontalOffset = qMin(defaultOffset, std::abs(xDistance)) * vector;
|
|
||||||
|
|
||||||
ratioY = 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
verticalOffset *= ratioY;
|
|
||||||
|
|
||||||
QPointF c1(_out.x() + horizontalOffset, _out.y() + verticalOffset);
|
|
||||||
|
|
||||||
QPointF c2(_in.x() - horizontalOffset, _in.y() - verticalOffset);
|
|
||||||
|
|
||||||
return std::make_pair(c1, c2);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,254 +0,0 @@
|
||||||
#include "ConnectionPainter.hpp"
|
|
||||||
|
|
||||||
#include <QtGui/QIcon>
|
|
||||||
|
|
||||||
#include "AbstractGraphModel.hpp"
|
|
||||||
#include "ConnectionGraphicsObject.hpp"
|
|
||||||
#include "ConnectionState.hpp"
|
|
||||||
#include "Definitions.hpp"
|
|
||||||
#include "NodeData.hpp"
|
|
||||||
#include "StyleCollection.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
static QPainterPath cubicPath(ConnectionGraphicsObject const &connection)
|
|
||||||
{
|
|
||||||
QPointF const &in = connection.endPoint(PortType::In);
|
|
||||||
QPointF const &out = connection.endPoint(PortType::Out);
|
|
||||||
|
|
||||||
auto const c1c2 = connection.pointsC1C2();
|
|
||||||
|
|
||||||
// cubic spline
|
|
||||||
QPainterPath cubic(out);
|
|
||||||
|
|
||||||
cubic.cubicTo(c1c2.first, c1c2.second, in);
|
|
||||||
|
|
||||||
return cubic;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPainterPath ConnectionPainter::getPainterStroke(ConnectionGraphicsObject const &connection)
|
|
||||||
{
|
|
||||||
auto cubic = cubicPath(connection);
|
|
||||||
|
|
||||||
QPointF const &out = connection.endPoint(PortType::Out);
|
|
||||||
QPainterPath result(out);
|
|
||||||
|
|
||||||
unsigned segments = 20;
|
|
||||||
|
|
||||||
for (auto i = 0ul; i < segments; ++i) {
|
|
||||||
double ratio = double(i + 1) / segments;
|
|
||||||
result.lineTo(cubic.pointAtPercent(ratio));
|
|
||||||
}
|
|
||||||
|
|
||||||
QPainterPathStroker stroker;
|
|
||||||
stroker.setWidth(10.0);
|
|
||||||
|
|
||||||
return stroker.createStroke(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef NODE_DEBUG_DRAWING
|
|
||||||
static void debugDrawing(QPainter *painter, ConnectionGraphicsObject const &cgo)
|
|
||||||
{
|
|
||||||
Q_UNUSED(painter);
|
|
||||||
|
|
||||||
{
|
|
||||||
QPointF const &in = cgo.endPoint(PortType::In);
|
|
||||||
QPointF const &out = cgo.endPoint(PortType::Out);
|
|
||||||
|
|
||||||
auto const points = cgo.pointsC1C2();
|
|
||||||
|
|
||||||
painter->setPen(Qt::red);
|
|
||||||
painter->setBrush(Qt::red);
|
|
||||||
|
|
||||||
painter->drawLine(QLineF(out, points.first));
|
|
||||||
painter->drawLine(QLineF(points.first, points.second));
|
|
||||||
painter->drawLine(QLineF(points.second, in));
|
|
||||||
painter->drawEllipse(points.first, 3, 3);
|
|
||||||
painter->drawEllipse(points.second, 3, 3);
|
|
||||||
|
|
||||||
painter->setBrush(Qt::NoBrush);
|
|
||||||
painter->drawPath(cubicPath(cgo));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
painter->setPen(Qt::yellow);
|
|
||||||
painter->drawRect(cgo.boundingRect());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void drawSketchLine(QPainter *painter, ConnectionGraphicsObject const &cgo)
|
|
||||||
{
|
|
||||||
ConnectionState const &state = cgo.connectionState();
|
|
||||||
|
|
||||||
if (state.requiresPort()) {
|
|
||||||
auto const &connectionStyle = QtNodes::StyleCollection::connectionStyle();
|
|
||||||
|
|
||||||
QPen pen;
|
|
||||||
pen.setWidth(connectionStyle.constructionLineWidth());
|
|
||||||
pen.setColor(connectionStyle.constructionColor());
|
|
||||||
pen.setStyle(Qt::DashLine);
|
|
||||||
|
|
||||||
painter->setPen(pen);
|
|
||||||
painter->setBrush(Qt::NoBrush);
|
|
||||||
|
|
||||||
auto cubic = cubicPath(cgo);
|
|
||||||
|
|
||||||
// cubic spline
|
|
||||||
painter->drawPath(cubic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drawHoveredOrSelected(QPainter *painter, ConnectionGraphicsObject const &cgo)
|
|
||||||
{
|
|
||||||
bool const hovered = cgo.connectionState().hovered();
|
|
||||||
bool const selected = cgo.isSelected();
|
|
||||||
|
|
||||||
// drawn as a fat background
|
|
||||||
if (hovered || selected) {
|
|
||||||
auto const &connectionStyle = QtNodes::StyleCollection::connectionStyle();
|
|
||||||
|
|
||||||
double const lineWidth = connectionStyle.lineWidth();
|
|
||||||
|
|
||||||
QPen pen;
|
|
||||||
pen.setWidth(2 * lineWidth);
|
|
||||||
pen.setColor(selected ? connectionStyle.selectedHaloColor()
|
|
||||||
: connectionStyle.hoveredColor());
|
|
||||||
|
|
||||||
painter->setPen(pen);
|
|
||||||
painter->setBrush(Qt::NoBrush);
|
|
||||||
|
|
||||||
// cubic spline
|
|
||||||
auto const cubic = cubicPath(cgo);
|
|
||||||
painter->drawPath(cubic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drawNormalLine(QPainter *painter, ConnectionGraphicsObject const &cgo)
|
|
||||||
{
|
|
||||||
ConnectionState const &state = cgo.connectionState();
|
|
||||||
|
|
||||||
if (state.requiresPort())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// colors
|
|
||||||
|
|
||||||
auto const &connectionStyle = QtNodes::StyleCollection::connectionStyle();
|
|
||||||
|
|
||||||
QColor normalColorOut = connectionStyle.normalColor();
|
|
||||||
QColor normalColorIn = connectionStyle.normalColor();
|
|
||||||
QColor selectedColor = connectionStyle.selectedColor();
|
|
||||||
|
|
||||||
bool useGradientColor = false;
|
|
||||||
|
|
||||||
AbstractGraphModel const &graphModel = cgo.graphModel();
|
|
||||||
|
|
||||||
if (connectionStyle.useDataDefinedColors()) {
|
|
||||||
using QtNodes::PortType;
|
|
||||||
|
|
||||||
auto const cId = cgo.connectionId();
|
|
||||||
|
|
||||||
auto dataTypeOut = graphModel
|
|
||||||
.portData(cId.outNodeId,
|
|
||||||
PortType::Out,
|
|
||||||
cId.outPortIndex,
|
|
||||||
PortRole::DataType)
|
|
||||||
.value<NodeDataType>();
|
|
||||||
|
|
||||||
auto dataTypeIn
|
|
||||||
= graphModel.portData(cId.inNodeId, PortType::In, cId.inPortIndex, PortRole::DataType)
|
|
||||||
.value<NodeDataType>();
|
|
||||||
|
|
||||||
useGradientColor = (dataTypeOut.id != dataTypeIn.id);
|
|
||||||
|
|
||||||
normalColorOut = connectionStyle.normalColor(dataTypeOut.id);
|
|
||||||
normalColorIn = connectionStyle.normalColor(dataTypeIn.id);
|
|
||||||
selectedColor = normalColorOut.darker(200);
|
|
||||||
}
|
|
||||||
|
|
||||||
// geometry
|
|
||||||
|
|
||||||
double const lineWidth = connectionStyle.lineWidth();
|
|
||||||
|
|
||||||
// draw normal line
|
|
||||||
QPen p;
|
|
||||||
|
|
||||||
p.setWidth(lineWidth);
|
|
||||||
|
|
||||||
bool const selected = cgo.isSelected();
|
|
||||||
|
|
||||||
auto cubic = cubicPath(cgo);
|
|
||||||
if (useGradientColor) {
|
|
||||||
painter->setBrush(Qt::NoBrush);
|
|
||||||
|
|
||||||
QColor cOut = normalColorOut;
|
|
||||||
if (selected)
|
|
||||||
cOut = cOut.darker(200);
|
|
||||||
p.setColor(cOut);
|
|
||||||
painter->setPen(p);
|
|
||||||
|
|
||||||
unsigned int const segments = 60;
|
|
||||||
|
|
||||||
for (unsigned int i = 0ul; i < segments; ++i) {
|
|
||||||
double ratioPrev = double(i) / segments;
|
|
||||||
double ratio = double(i + 1) / segments;
|
|
||||||
|
|
||||||
if (i == segments / 2) {
|
|
||||||
QColor cIn = normalColorIn;
|
|
||||||
if (selected)
|
|
||||||
cIn = cIn.darker(200);
|
|
||||||
|
|
||||||
p.setColor(cIn);
|
|
||||||
painter->setPen(p);
|
|
||||||
}
|
|
||||||
painter->drawLine(cubic.pointAtPercent(ratioPrev), cubic.pointAtPercent(ratio));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
QIcon icon(":convert.png");
|
|
||||||
|
|
||||||
QPixmap pixmap = icon.pixmap(QSize(22, 22));
|
|
||||||
painter->drawPixmap(cubic.pointAtPercent(0.50)
|
|
||||||
- QPoint(pixmap.width() / 2, pixmap.height() / 2),
|
|
||||||
pixmap);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p.setColor(normalColorOut);
|
|
||||||
|
|
||||||
if (selected) {
|
|
||||||
p.setColor(selectedColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
painter->setPen(p);
|
|
||||||
painter->setBrush(Qt::NoBrush);
|
|
||||||
|
|
||||||
painter->drawPath(cubic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionPainter::paint(QPainter *painter, ConnectionGraphicsObject const &cgo)
|
|
||||||
{
|
|
||||||
drawHoveredOrSelected(painter, cgo);
|
|
||||||
|
|
||||||
drawSketchLine(painter, cgo);
|
|
||||||
|
|
||||||
drawNormalLine(painter, cgo);
|
|
||||||
|
|
||||||
#ifdef NODE_DEBUG_DRAWING
|
|
||||||
debugDrawing(painter, cgo);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// draw end points
|
|
||||||
auto const &connectionStyle = QtNodes::StyleCollection::connectionStyle();
|
|
||||||
|
|
||||||
double const pointDiameter = connectionStyle.pointDiameter();
|
|
||||||
|
|
||||||
painter->setPen(connectionStyle.constructionColor());
|
|
||||||
painter->setBrush(connectionStyle.constructionColor());
|
|
||||||
double const pointRadius = pointDiameter / 2.0;
|
|
||||||
painter->drawEllipse(cgo.out(), pointRadius, pointRadius);
|
|
||||||
painter->drawEllipse(cgo.in(), pointRadius, pointRadius);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
#include "ConnectionState.hpp"
|
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
#include <QtCore/QPointF>
|
|
||||||
|
|
||||||
#include "BasicGraphicsScene.hpp"
|
|
||||||
#include "ConnectionGraphicsObject.hpp"
|
|
||||||
#include "NodeGraphicsObject.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
ConnectionState::~ConnectionState()
|
|
||||||
{
|
|
||||||
//resetLastHoveredNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
PortType ConnectionState::requiredPort() const
|
|
||||||
{
|
|
||||||
PortType t = PortType::None;
|
|
||||||
|
|
||||||
if (_cgo.connectionId().outNodeId == InvalidNodeId) {
|
|
||||||
t = PortType::Out;
|
|
||||||
} else if (_cgo.connectionId().inNodeId == InvalidNodeId) {
|
|
||||||
t = PortType::In;
|
|
||||||
}
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConnectionState::requiresPort() const
|
|
||||||
{
|
|
||||||
const ConnectionId &id = _cgo.connectionId();
|
|
||||||
return id.outNodeId == InvalidNodeId || id.inNodeId == InvalidNodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConnectionState::hovered() const
|
|
||||||
{
|
|
||||||
return _hovered;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionState::setHovered(bool hovered)
|
|
||||||
{
|
|
||||||
_hovered = hovered;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionState::setLastHoveredNode(NodeId const nodeId)
|
|
||||||
{
|
|
||||||
_lastHoveredNode = nodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeId ConnectionState::lastHoveredNode() const
|
|
||||||
{
|
|
||||||
return _lastHoveredNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionState::resetLastHoveredNode()
|
|
||||||
{
|
|
||||||
if (_lastHoveredNode != InvalidNodeId) {
|
|
||||||
auto ngo = _cgo.nodeScene()->nodeGraphicsObject(_lastHoveredNode);
|
|
||||||
ngo->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastHoveredNode = InvalidNodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,205 +0,0 @@
|
||||||
#include "ConnectionStyle.hpp"
|
|
||||||
|
|
||||||
#include "StyleCollection.hpp"
|
|
||||||
|
|
||||||
#include <QtCore/QJsonArray>
|
|
||||||
#include <QtCore/QJsonObject>
|
|
||||||
#include <QtCore/QJsonValueRef>
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
|
|
||||||
using QtNodes::ConnectionStyle;
|
|
||||||
|
|
||||||
inline void initResources()
|
|
||||||
{
|
|
||||||
Q_INIT_RESOURCE(resources);
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionStyle::ConnectionStyle()
|
|
||||||
{
|
|
||||||
// Explicit resources inialization for preventing the static initialization
|
|
||||||
// order fiasco: https://isocpp.org/wiki/faq/ctors#static-init-order
|
|
||||||
initResources();
|
|
||||||
|
|
||||||
// This configuration is stored inside the compiled unit and is loaded statically
|
|
||||||
loadJsonFile(":DefaultStyle.json");
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionStyle::ConnectionStyle(QString jsonText)
|
|
||||||
{
|
|
||||||
loadJsonFile(":DefaultStyle.json");
|
|
||||||
loadJsonText(jsonText);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionStyle::setConnectionStyle(QString jsonText)
|
|
||||||
{
|
|
||||||
ConnectionStyle style(jsonText);
|
|
||||||
|
|
||||||
StyleCollection::setConnectionStyle(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef STYLE_DEBUG
|
|
||||||
#define CONNECTION_STYLE_CHECK_UNDEFINED_VALUE(v, variable) \
|
|
||||||
{ \
|
|
||||||
if (v.type() == QJsonValue::Undefined || v.type() == QJsonValue::Null) \
|
|
||||||
qWarning() << "Undefined value for parameter:" << #variable; \
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define CONNECTION_STYLE_CHECK_UNDEFINED_VALUE(v, variable)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CONNECTION_VALUE_EXISTS(v) \
|
|
||||||
(v.type() != QJsonValue::Undefined && v.type() != QJsonValue::Null)
|
|
||||||
|
|
||||||
#define CONNECTION_STYLE_READ_COLOR(values, variable) \
|
|
||||||
{ \
|
|
||||||
auto valueRef = values[#variable]; \
|
|
||||||
CONNECTION_STYLE_CHECK_UNDEFINED_VALUE(valueRef, variable) \
|
|
||||||
if (CONNECTION_VALUE_EXISTS(valueRef)) { \
|
|
||||||
if (valueRef.isArray()) { \
|
|
||||||
auto colorArray = valueRef.toArray(); \
|
|
||||||
std::vector<int> rgb; \
|
|
||||||
rgb.reserve(3); \
|
|
||||||
for (auto it = colorArray.begin(); it != colorArray.end(); ++it) { \
|
|
||||||
rgb.push_back((*it).toInt()); \
|
|
||||||
} \
|
|
||||||
variable = QColor(rgb[0], rgb[1], rgb[2]); \
|
|
||||||
} else { \
|
|
||||||
variable = QColor(valueRef.toString()); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CONNECTION_STYLE_WRITE_COLOR(values, variable) \
|
|
||||||
{ \
|
|
||||||
values[#variable] = variable.name(); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CONNECTION_STYLE_READ_FLOAT(values, variable) \
|
|
||||||
{ \
|
|
||||||
auto valueRef = values[#variable]; \
|
|
||||||
CONNECTION_STYLE_CHECK_UNDEFINED_VALUE(valueRef, variable) \
|
|
||||||
if (CONNECTION_VALUE_EXISTS(valueRef)) \
|
|
||||||
variable = valueRef.toDouble(); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CONNECTION_STYLE_WRITE_FLOAT(values, variable) \
|
|
||||||
{ \
|
|
||||||
values[#variable] = variable; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CONNECTION_STYLE_READ_BOOL(values, variable) \
|
|
||||||
{ \
|
|
||||||
auto valueRef = values[#variable]; \
|
|
||||||
CONNECTION_STYLE_CHECK_UNDEFINED_VALUE(valueRef, variable) \
|
|
||||||
if (CONNECTION_VALUE_EXISTS(valueRef)) \
|
|
||||||
variable = valueRef.toBool(); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CONNECTION_STYLE_WRITE_BOOL(values, variable) \
|
|
||||||
{ \
|
|
||||||
values[#variable] = variable; \
|
|
||||||
}
|
|
||||||
|
|
||||||
void ConnectionStyle::loadJson(QJsonObject const &json)
|
|
||||||
{
|
|
||||||
QJsonValue nodeStyleValues = json["ConnectionStyle"];
|
|
||||||
|
|
||||||
QJsonObject obj = nodeStyleValues.toObject();
|
|
||||||
|
|
||||||
CONNECTION_STYLE_READ_COLOR(obj, ConstructionColor);
|
|
||||||
CONNECTION_STYLE_READ_COLOR(obj, NormalColor);
|
|
||||||
CONNECTION_STYLE_READ_COLOR(obj, SelectedColor);
|
|
||||||
CONNECTION_STYLE_READ_COLOR(obj, SelectedHaloColor);
|
|
||||||
CONNECTION_STYLE_READ_COLOR(obj, HoveredColor);
|
|
||||||
|
|
||||||
CONNECTION_STYLE_READ_FLOAT(obj, LineWidth);
|
|
||||||
CONNECTION_STYLE_READ_FLOAT(obj, ConstructionLineWidth);
|
|
||||||
CONNECTION_STYLE_READ_FLOAT(obj, PointDiameter);
|
|
||||||
|
|
||||||
CONNECTION_STYLE_READ_BOOL(obj, UseDataDefinedColors);
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject ConnectionStyle::toJson() const
|
|
||||||
{
|
|
||||||
QJsonObject obj;
|
|
||||||
|
|
||||||
CONNECTION_STYLE_WRITE_COLOR(obj, ConstructionColor);
|
|
||||||
CONNECTION_STYLE_WRITE_COLOR(obj, NormalColor);
|
|
||||||
CONNECTION_STYLE_WRITE_COLOR(obj, SelectedColor);
|
|
||||||
CONNECTION_STYLE_WRITE_COLOR(obj, SelectedHaloColor);
|
|
||||||
CONNECTION_STYLE_WRITE_COLOR(obj, HoveredColor);
|
|
||||||
|
|
||||||
CONNECTION_STYLE_WRITE_FLOAT(obj, LineWidth);
|
|
||||||
CONNECTION_STYLE_WRITE_FLOAT(obj, ConstructionLineWidth);
|
|
||||||
CONNECTION_STYLE_WRITE_FLOAT(obj, PointDiameter);
|
|
||||||
|
|
||||||
CONNECTION_STYLE_WRITE_BOOL(obj, UseDataDefinedColors);
|
|
||||||
|
|
||||||
QJsonObject root;
|
|
||||||
root["ConnectionStyle"] = obj;
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor ConnectionStyle::constructionColor() const
|
|
||||||
{
|
|
||||||
return ConstructionColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor ConnectionStyle::normalColor() const
|
|
||||||
{
|
|
||||||
return NormalColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor ConnectionStyle::normalColor(QString typeId) const
|
|
||||||
{
|
|
||||||
std::size_t hash = qHash(typeId);
|
|
||||||
|
|
||||||
std::size_t const hue_range = 0xFF;
|
|
||||||
|
|
||||||
std::mt19937 gen(static_cast<unsigned int>(hash));
|
|
||||||
std::uniform_int_distribution<int> distrib(0, hue_range);
|
|
||||||
|
|
||||||
int hue = distrib(gen);
|
|
||||||
int sat = 120 + hash % 129;
|
|
||||||
|
|
||||||
return QColor::fromHsl(hue, sat, 160);
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor ConnectionStyle::selectedColor() const
|
|
||||||
{
|
|
||||||
return SelectedColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor ConnectionStyle::selectedHaloColor() const
|
|
||||||
{
|
|
||||||
return SelectedHaloColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor ConnectionStyle::hoveredColor() const
|
|
||||||
{
|
|
||||||
return HoveredColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
float ConnectionStyle::lineWidth() const
|
|
||||||
{
|
|
||||||
return LineWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
float ConnectionStyle::constructionLineWidth() const
|
|
||||||
{
|
|
||||||
return ConstructionLineWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
float ConnectionStyle::pointDiameter() const
|
|
||||||
{
|
|
||||||
return PointDiameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConnectionStyle::useDataDefinedColors() const
|
|
||||||
{
|
|
||||||
return UseDataDefinedColors;
|
|
||||||
}
|
|
||||||
|
|
@ -1,536 +0,0 @@
|
||||||
#include "DataFlowGraphModel.hpp"
|
|
||||||
#include "ConnectionIdHash.hpp"
|
|
||||||
|
|
||||||
#include <QJsonArray>
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
DataFlowGraphModel::DataFlowGraphModel(std::shared_ptr<NodeDelegateModelRegistry> registry)
|
|
||||||
: _registry(std::move(registry))
|
|
||||||
, _nextNodeId{0}
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::unordered_set<NodeId> DataFlowGraphModel::allNodeIds() const
|
|
||||||
{
|
|
||||||
std::unordered_set<NodeId> nodeIds;
|
|
||||||
for_each(_models.begin(), _models.end(), [&nodeIds](auto const &p) { nodeIds.insert(p.first); });
|
|
||||||
|
|
||||||
return nodeIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_set<ConnectionId> DataFlowGraphModel::allConnectionIds(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
std::unordered_set<ConnectionId> result;
|
|
||||||
|
|
||||||
std::copy_if(_connectivity.begin(),
|
|
||||||
_connectivity.end(),
|
|
||||||
std::inserter(result, std::end(result)),
|
|
||||||
[&nodeId](ConnectionId const &cid) {
|
|
||||||
return cid.inNodeId == nodeId || cid.outNodeId == nodeId;
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_set<ConnectionId> DataFlowGraphModel::connections(NodeId nodeId,
|
|
||||||
PortType portType,
|
|
||||||
PortIndex portIndex) const
|
|
||||||
{
|
|
||||||
std::unordered_set<ConnectionId> result;
|
|
||||||
|
|
||||||
std::copy_if(_connectivity.begin(),
|
|
||||||
_connectivity.end(),
|
|
||||||
std::inserter(result, std::end(result)),
|
|
||||||
[&portType, &portIndex, &nodeId](ConnectionId const &cid) {
|
|
||||||
return (getNodeId(portType, cid) == nodeId
|
|
||||||
&& getPortIndex(portType, cid) == portIndex);
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataFlowGraphModel::connectionExists(ConnectionId const connectionId) const
|
|
||||||
{
|
|
||||||
return (_connectivity.find(connectionId) != _connectivity.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeId DataFlowGraphModel::addNode(QString const nodeType)
|
|
||||||
{
|
|
||||||
std::unique_ptr<NodeDelegateModel> model = _registry->create(nodeType);
|
|
||||||
|
|
||||||
if (model) {
|
|
||||||
NodeId newId = newNodeId();
|
|
||||||
|
|
||||||
connect(model.get(),
|
|
||||||
&NodeDelegateModel::dataUpdated,
|
|
||||||
[newId, this](PortIndex const portIndex) {
|
|
||||||
onOutPortDataUpdated(newId, portIndex);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(model.get(),
|
|
||||||
&NodeDelegateModel::portsAboutToBeDeleted,
|
|
||||||
this,
|
|
||||||
[newId, this](PortType const portType, PortIndex const first, PortIndex const last) {
|
|
||||||
portsAboutToBeDeleted(newId, portType, first, last);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(model.get(),
|
|
||||||
&NodeDelegateModel::portsDeleted,
|
|
||||||
this,
|
|
||||||
&DataFlowGraphModel::portsDeleted);
|
|
||||||
|
|
||||||
connect(model.get(),
|
|
||||||
&NodeDelegateModel::portsAboutToBeInserted,
|
|
||||||
this,
|
|
||||||
[newId, this](PortType const portType, PortIndex const first, PortIndex const last) {
|
|
||||||
portsAboutToBeInserted(newId, portType, first, last);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(model.get(),
|
|
||||||
&NodeDelegateModel::portsInserted,
|
|
||||||
this,
|
|
||||||
&DataFlowGraphModel::portsInserted);
|
|
||||||
|
|
||||||
_models[newId] = std::move(model);
|
|
||||||
|
|
||||||
Q_EMIT nodeCreated(newId);
|
|
||||||
|
|
||||||
return newId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return InvalidNodeId;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataFlowGraphModel::connectionPossible(ConnectionId const connectionId) const
|
|
||||||
{
|
|
||||||
auto getDataType = [&](PortType const portType) {
|
|
||||||
return portData(getNodeId(portType, connectionId),
|
|
||||||
portType,
|
|
||||||
getPortIndex(portType, connectionId),
|
|
||||||
PortRole::DataType)
|
|
||||||
.value<NodeDataType>();
|
|
||||||
};
|
|
||||||
|
|
||||||
auto portVacant = [&](PortType const portType) {
|
|
||||||
NodeId const nodeId = getNodeId(portType, connectionId);
|
|
||||||
PortIndex const portIndex = getPortIndex(portType, connectionId);
|
|
||||||
auto const connected = connections(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
auto policy = portData(nodeId, portType, portIndex, PortRole::ConnectionPolicyRole)
|
|
||||||
.value<ConnectionPolicy>();
|
|
||||||
|
|
||||||
return connected.empty() || (policy == ConnectionPolicy::Many);
|
|
||||||
};
|
|
||||||
|
|
||||||
return getDataType(PortType::Out).id == getDataType(PortType::In).id
|
|
||||||
&& portVacant(PortType::Out) && portVacant(PortType::In);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowGraphModel::addConnection(ConnectionId const connectionId)
|
|
||||||
{
|
|
||||||
_connectivity.insert(connectionId);
|
|
||||||
|
|
||||||
sendConnectionCreation(connectionId);
|
|
||||||
|
|
||||||
QVariant const portDataToPropagate = portData(connectionId.outNodeId,
|
|
||||||
PortType::Out,
|
|
||||||
connectionId.outPortIndex,
|
|
||||||
PortRole::Data);
|
|
||||||
|
|
||||||
setPortData(connectionId.inNodeId,
|
|
||||||
PortType::In,
|
|
||||||
connectionId.inPortIndex,
|
|
||||||
portDataToPropagate,
|
|
||||||
PortRole::Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowGraphModel::sendConnectionCreation(ConnectionId const connectionId)
|
|
||||||
{
|
|
||||||
Q_EMIT connectionCreated(connectionId);
|
|
||||||
|
|
||||||
auto iti = _models.find(connectionId.inNodeId);
|
|
||||||
auto ito = _models.find(connectionId.outNodeId);
|
|
||||||
if (iti != _models.end() && ito != _models.end()) {
|
|
||||||
auto &modeli = iti->second;
|
|
||||||
auto &modelo = ito->second;
|
|
||||||
modeli->inputConnectionCreated(connectionId);
|
|
||||||
modelo->outputConnectionCreated(connectionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowGraphModel::sendConnectionDeletion(ConnectionId const connectionId)
|
|
||||||
{
|
|
||||||
Q_EMIT connectionDeleted(connectionId);
|
|
||||||
|
|
||||||
auto iti = _models.find(connectionId.inNodeId);
|
|
||||||
auto ito = _models.find(connectionId.outNodeId);
|
|
||||||
if (iti != _models.end() && ito != _models.end()) {
|
|
||||||
auto &modeli = iti->second;
|
|
||||||
auto &modelo = ito->second;
|
|
||||||
modeli->inputConnectionDeleted(connectionId);
|
|
||||||
modelo->outputConnectionDeleted(connectionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataFlowGraphModel::nodeExists(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
return (_models.find(nodeId) != _models.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant DataFlowGraphModel::nodeData(NodeId nodeId, NodeRole role) const
|
|
||||||
{
|
|
||||||
QVariant result;
|
|
||||||
|
|
||||||
auto it = _models.find(nodeId);
|
|
||||||
if (it == _models.end())
|
|
||||||
return result;
|
|
||||||
|
|
||||||
auto &model = it->second;
|
|
||||||
|
|
||||||
switch (role) {
|
|
||||||
case NodeRole::Id:
|
|
||||||
break;
|
|
||||||
case NodeRole::Type:
|
|
||||||
result = model->name();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::Position:
|
|
||||||
result = _nodeGeometryData[nodeId].pos;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::Size:
|
|
||||||
result = _nodeGeometryData[nodeId].size;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::CaptionVisible:
|
|
||||||
result = model->captionVisible();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::Caption:
|
|
||||||
result = model->caption();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::Style: {
|
|
||||||
auto style = StyleCollection::nodeStyle();
|
|
||||||
result = style.toJson().toVariantMap();
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NodeRole::InternalData: {
|
|
||||||
QJsonObject nodeJson;
|
|
||||||
|
|
||||||
nodeJson["internal-data"] = _models.at(nodeId)->save();
|
|
||||||
|
|
||||||
result = nodeJson.toVariantMap();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case NodeRole::InPortCount:
|
|
||||||
result = model->nPorts(PortType::In);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::OutPortCount:
|
|
||||||
result = model->nPorts(PortType::Out);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::Widget: {
|
|
||||||
auto w = model->embeddedWidget();
|
|
||||||
result = QVariant::fromValue(w);
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeFlags DataFlowGraphModel::nodeFlags(NodeId nodeId) const
|
|
||||||
{
|
|
||||||
auto it = _models.find(nodeId);
|
|
||||||
|
|
||||||
if (it != _models.end() && it->second->resizable())
|
|
||||||
return NodeFlag::Resizable;
|
|
||||||
|
|
||||||
return NodeFlag::NoFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataFlowGraphModel::setNodeData(NodeId nodeId, NodeRole role, QVariant value)
|
|
||||||
{
|
|
||||||
Q_UNUSED(nodeId);
|
|
||||||
Q_UNUSED(role);
|
|
||||||
Q_UNUSED(value);
|
|
||||||
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
switch (role) {
|
|
||||||
case NodeRole::Id:
|
|
||||||
break;
|
|
||||||
case NodeRole::Type:
|
|
||||||
break;
|
|
||||||
case NodeRole::Position: {
|
|
||||||
_nodeGeometryData[nodeId].pos = value.value<QPointF>();
|
|
||||||
|
|
||||||
Q_EMIT nodePositionUpdated(nodeId);
|
|
||||||
|
|
||||||
result = true;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NodeRole::Size: {
|
|
||||||
_nodeGeometryData[nodeId].size = value.value<QSize>();
|
|
||||||
result = true;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case NodeRole::CaptionVisible:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::Caption:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::Style:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::InternalData:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::InPortCount:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::OutPortCount:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NodeRole::Widget:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant DataFlowGraphModel::portData(NodeId nodeId,
|
|
||||||
PortType portType,
|
|
||||||
PortIndex portIndex,
|
|
||||||
PortRole role) const
|
|
||||||
{
|
|
||||||
QVariant result;
|
|
||||||
|
|
||||||
auto it = _models.find(nodeId);
|
|
||||||
if (it == _models.end())
|
|
||||||
return result;
|
|
||||||
|
|
||||||
auto &model = it->second;
|
|
||||||
|
|
||||||
switch (role) {
|
|
||||||
case PortRole::Data:
|
|
||||||
if (portType == PortType::Out)
|
|
||||||
result = QVariant::fromValue(model->outData(portIndex));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PortRole::DataType:
|
|
||||||
result = QVariant::fromValue(model->dataType(portType, portIndex));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PortRole::ConnectionPolicyRole:
|
|
||||||
result = QVariant::fromValue(model->portConnectionPolicy(portType, portIndex));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PortRole::CaptionVisible:
|
|
||||||
result = model->portCaptionVisible(portType, portIndex);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PortRole::Caption:
|
|
||||||
result = model->portCaption(portType, portIndex);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataFlowGraphModel::setPortData(
|
|
||||||
NodeId nodeId, PortType portType, PortIndex portIndex, QVariant const &value, PortRole role)
|
|
||||||
{
|
|
||||||
Q_UNUSED(nodeId);
|
|
||||||
|
|
||||||
QVariant result;
|
|
||||||
|
|
||||||
auto it = _models.find(nodeId);
|
|
||||||
if (it == _models.end())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
auto &model = it->second;
|
|
||||||
|
|
||||||
switch (role) {
|
|
||||||
case PortRole::Data:
|
|
||||||
if (portType == PortType::In) {
|
|
||||||
model->setInData(value.value<std::shared_ptr<NodeData>>(), portIndex);
|
|
||||||
|
|
||||||
// Triggers repainting on the scene.
|
|
||||||
Q_EMIT inPortDataWasSet(nodeId, portType, portIndex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataFlowGraphModel::deleteConnection(ConnectionId const connectionId)
|
|
||||||
{
|
|
||||||
bool disconnected = false;
|
|
||||||
|
|
||||||
auto it = _connectivity.find(connectionId);
|
|
||||||
|
|
||||||
if (it != _connectivity.end()) {
|
|
||||||
disconnected = true;
|
|
||||||
|
|
||||||
_connectivity.erase(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disconnected) {
|
|
||||||
sendConnectionDeletion(connectionId);
|
|
||||||
|
|
||||||
propagateEmptyDataTo(getNodeId(PortType::In, connectionId),
|
|
||||||
getPortIndex(PortType::In, connectionId));
|
|
||||||
}
|
|
||||||
|
|
||||||
return disconnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataFlowGraphModel::deleteNode(NodeId const nodeId)
|
|
||||||
{
|
|
||||||
// Delete connections to this node first.
|
|
||||||
auto connectionIds = allConnectionIds(nodeId);
|
|
||||||
for (auto &cId : connectionIds) {
|
|
||||||
deleteConnection(cId);
|
|
||||||
}
|
|
||||||
|
|
||||||
_nodeGeometryData.erase(nodeId);
|
|
||||||
_models.erase(nodeId);
|
|
||||||
|
|
||||||
Q_EMIT nodeDeleted(nodeId);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject DataFlowGraphModel::saveNode(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
QJsonObject nodeJson;
|
|
||||||
|
|
||||||
nodeJson["id"] = static_cast<qint64>(nodeId);
|
|
||||||
|
|
||||||
nodeJson["internal-data"] = _models.at(nodeId)->save();
|
|
||||||
|
|
||||||
{
|
|
||||||
QPointF const pos = nodeData(nodeId, NodeRole::Position).value<QPointF>();
|
|
||||||
|
|
||||||
QJsonObject posJson;
|
|
||||||
posJson["x"] = pos.x();
|
|
||||||
posJson["y"] = pos.y();
|
|
||||||
nodeJson["position"] = posJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject DataFlowGraphModel::save() const
|
|
||||||
{
|
|
||||||
QJsonObject sceneJson;
|
|
||||||
|
|
||||||
QJsonArray nodesJsonArray;
|
|
||||||
for (auto const nodeId : allNodeIds()) {
|
|
||||||
nodesJsonArray.append(saveNode(nodeId));
|
|
||||||
}
|
|
||||||
sceneJson["nodes"] = nodesJsonArray;
|
|
||||||
|
|
||||||
QJsonArray connJsonArray;
|
|
||||||
for (auto const &cid : _connectivity) {
|
|
||||||
connJsonArray.append(toJson(cid));
|
|
||||||
}
|
|
||||||
sceneJson["connections"] = connJsonArray;
|
|
||||||
|
|
||||||
return sceneJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowGraphModel::loadNode(QJsonObject const &nodeJson)
|
|
||||||
{
|
|
||||||
// Possibility of the id clash when reading it from json and not generating a
|
|
||||||
// new value.
|
|
||||||
// 1. When restoring a scene from a file.
|
|
||||||
// Conflict is not possible because the scene must be cleared by the time of
|
|
||||||
// loading.
|
|
||||||
// 2. When undoing the deletion command. Conflict is not possible
|
|
||||||
// because all the new ids were created past the removed nodes.
|
|
||||||
NodeId restoredNodeId = nodeJson["id"].toInt();
|
|
||||||
|
|
||||||
_nextNodeId = std::max(_nextNodeId, restoredNodeId + 1);
|
|
||||||
|
|
||||||
QJsonObject const internalDataJson = nodeJson["internal-data"].toObject();
|
|
||||||
|
|
||||||
QString delegateModelName = internalDataJson["model-name"].toString();
|
|
||||||
|
|
||||||
std::unique_ptr<NodeDelegateModel> model = _registry->create(delegateModelName);
|
|
||||||
|
|
||||||
if (model) {
|
|
||||||
connect(model.get(),
|
|
||||||
&NodeDelegateModel::dataUpdated,
|
|
||||||
[restoredNodeId, this](PortIndex const portIndex) {
|
|
||||||
onOutPortDataUpdated(restoredNodeId, portIndex);
|
|
||||||
});
|
|
||||||
|
|
||||||
_models[restoredNodeId] = std::move(model);
|
|
||||||
|
|
||||||
Q_EMIT nodeCreated(restoredNodeId);
|
|
||||||
|
|
||||||
QJsonObject posJson = nodeJson["position"].toObject();
|
|
||||||
QPointF const pos(posJson["x"].toDouble(), posJson["y"].toDouble());
|
|
||||||
|
|
||||||
setNodeData(restoredNodeId, NodeRole::Position, pos);
|
|
||||||
|
|
||||||
_models[restoredNodeId]->load(internalDataJson);
|
|
||||||
} else {
|
|
||||||
throw std::logic_error(std::string("No registered model with name ")
|
|
||||||
+ delegateModelName.toLocal8Bit().data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowGraphModel::load(QJsonObject const &jsonDocument)
|
|
||||||
{
|
|
||||||
QJsonArray nodesJsonArray = jsonDocument["nodes"].toArray();
|
|
||||||
|
|
||||||
for (QJsonValueRef nodeJson : nodesJsonArray) {
|
|
||||||
loadNode(nodeJson.toObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonArray connectionJsonArray = jsonDocument["connections"].toArray();
|
|
||||||
|
|
||||||
for (QJsonValueRef connection : connectionJsonArray) {
|
|
||||||
QJsonObject connJson = connection.toObject();
|
|
||||||
|
|
||||||
ConnectionId connId = fromJson(connJson);
|
|
||||||
|
|
||||||
// Restore the connection
|
|
||||||
addConnection(connId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowGraphModel::onOutPortDataUpdated(NodeId const nodeId, PortIndex const portIndex)
|
|
||||||
{
|
|
||||||
std::unordered_set<ConnectionId> const &connected = connections(nodeId,
|
|
||||||
PortType::Out,
|
|
||||||
portIndex);
|
|
||||||
|
|
||||||
QVariant const portDataToPropagate = portData(nodeId, PortType::Out, portIndex, PortRole::Data);
|
|
||||||
|
|
||||||
for (auto const &cn : connected) {
|
|
||||||
setPortData(cn.inNodeId, PortType::In, cn.inPortIndex, portDataToPropagate, PortRole::Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowGraphModel::propagateEmptyDataTo(NodeId const nodeId, PortIndex const portIndex)
|
|
||||||
{
|
|
||||||
QVariant emptyData{};
|
|
||||||
|
|
||||||
setPortData(nodeId, PortType::In, portIndex, emptyData, PortRole::Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,189 +0,0 @@
|
||||||
#include "DataFlowGraphicsScene.hpp"
|
|
||||||
|
|
||||||
#include "ConnectionGraphicsObject.hpp"
|
|
||||||
#include "GraphicsView.hpp"
|
|
||||||
#include "NodeDelegateModelRegistry.hpp"
|
|
||||||
#include "NodeGraphicsObject.hpp"
|
|
||||||
#include "UndoCommands.hpp"
|
|
||||||
|
|
||||||
#include <QtWidgets/QFileDialog>
|
|
||||||
#include <QtWidgets/QGraphicsSceneMoveEvent>
|
|
||||||
#include <QtWidgets/QHeaderView>
|
|
||||||
#include <QtWidgets/QLineEdit>
|
|
||||||
#include <QtWidgets/QTreeWidget>
|
|
||||||
#include <QtWidgets/QWidgetAction>
|
|
||||||
|
|
||||||
#include <QtCore/QBuffer>
|
|
||||||
#include <QtCore/QByteArray>
|
|
||||||
#include <QtCore/QDataStream>
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
#include <QtCore/QFile>
|
|
||||||
#include <QtCore/QJsonArray>
|
|
||||||
#include <QtCore/QJsonDocument>
|
|
||||||
#include <QtCore/QJsonObject>
|
|
||||||
#include <QtCore/QtGlobal>
|
|
||||||
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
DataFlowGraphicsScene::DataFlowGraphicsScene(DataFlowGraphModel &graphModel, QObject *parent)
|
|
||||||
: BasicGraphicsScene(graphModel, parent)
|
|
||||||
, _graphModel(graphModel)
|
|
||||||
{
|
|
||||||
connect(&_graphModel,
|
|
||||||
&DataFlowGraphModel::inPortDataWasSet,
|
|
||||||
[this](NodeId const nodeId, PortType const, PortIndex const) { onNodeUpdated(nodeId); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO constructor for an empyt scene?
|
|
||||||
|
|
||||||
std::vector<NodeId> DataFlowGraphicsScene::selectedNodes() const
|
|
||||||
{
|
|
||||||
QList<QGraphicsItem *> graphicsItems = selectedItems();
|
|
||||||
|
|
||||||
std::vector<NodeId> result;
|
|
||||||
result.reserve(graphicsItems.size());
|
|
||||||
|
|
||||||
for (QGraphicsItem *item : graphicsItems) {
|
|
||||||
auto ngo = qgraphicsitem_cast<NodeGraphicsObject *>(item);
|
|
||||||
|
|
||||||
if (ngo != nullptr) {
|
|
||||||
result.push_back(ngo->nodeId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMenu *DataFlowGraphicsScene::createSceneMenu(QPointF const scenePos)
|
|
||||||
{
|
|
||||||
QMenu *modelMenu = new QMenu();
|
|
||||||
|
|
||||||
// Add filterbox to the context menu
|
|
||||||
auto *txtBox = new QLineEdit(modelMenu);
|
|
||||||
txtBox->setPlaceholderText(QStringLiteral("Filter"));
|
|
||||||
txtBox->setClearButtonEnabled(true);
|
|
||||||
|
|
||||||
auto *txtBoxAction = new QWidgetAction(modelMenu);
|
|
||||||
txtBoxAction->setDefaultWidget(txtBox);
|
|
||||||
|
|
||||||
// 1.
|
|
||||||
modelMenu->addAction(txtBoxAction);
|
|
||||||
|
|
||||||
// Add result treeview to the context menu
|
|
||||||
QTreeWidget *treeView = new QTreeWidget(modelMenu);
|
|
||||||
treeView->header()->close();
|
|
||||||
|
|
||||||
auto *treeViewAction = new QWidgetAction(modelMenu);
|
|
||||||
treeViewAction->setDefaultWidget(treeView);
|
|
||||||
|
|
||||||
// 2.
|
|
||||||
modelMenu->addAction(treeViewAction);
|
|
||||||
|
|
||||||
auto registry = _graphModel.dataModelRegistry();
|
|
||||||
|
|
||||||
for (auto const &cat : registry->categories()) {
|
|
||||||
auto item = new QTreeWidgetItem(treeView);
|
|
||||||
item->setText(0, cat);
|
|
||||||
item->setFlags(item->flags() & ~Qt::ItemIsSelectable);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const &assoc : registry->registeredModelsCategoryAssociation()) {
|
|
||||||
QList<QTreeWidgetItem *> parent = treeView->findItems(assoc.second, Qt::MatchExactly);
|
|
||||||
|
|
||||||
if (parent.count() <= 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto item = new QTreeWidgetItem(parent.first());
|
|
||||||
item->setText(0, assoc.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
treeView->expandAll();
|
|
||||||
|
|
||||||
connect(treeView,
|
|
||||||
&QTreeWidget::itemClicked,
|
|
||||||
[this, modelMenu, scenePos](QTreeWidgetItem *item, int) {
|
|
||||||
if (!(item->flags() & (Qt::ItemIsSelectable))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->undoStack().push(new CreateCommand(this, item->text(0), scenePos));
|
|
||||||
|
|
||||||
modelMenu->close();
|
|
||||||
});
|
|
||||||
|
|
||||||
//Setup filtering
|
|
||||||
connect(txtBox, &QLineEdit::textChanged, [treeView](const QString &text) {
|
|
||||||
QTreeWidgetItemIterator categoryIt(treeView, QTreeWidgetItemIterator::HasChildren);
|
|
||||||
while (*categoryIt)
|
|
||||||
(*categoryIt++)->setHidden(true);
|
|
||||||
QTreeWidgetItemIterator it(treeView, QTreeWidgetItemIterator::NoChildren);
|
|
||||||
while (*it) {
|
|
||||||
auto modelName = (*it)->text(0);
|
|
||||||
const bool match = (modelName.contains(text, Qt::CaseInsensitive));
|
|
||||||
(*it)->setHidden(!match);
|
|
||||||
if (match) {
|
|
||||||
QTreeWidgetItem *parent = (*it)->parent();
|
|
||||||
while (parent) {
|
|
||||||
parent->setHidden(false);
|
|
||||||
parent = parent->parent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// make sure the text box gets focus so the user doesn't have to click on it
|
|
||||||
txtBox->setFocus();
|
|
||||||
|
|
||||||
// QMenu's instance auto-destruction
|
|
||||||
modelMenu->setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
|
|
||||||
return modelMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowGraphicsScene::save() const
|
|
||||||
{
|
|
||||||
QString fileName = QFileDialog::getSaveFileName(nullptr,
|
|
||||||
tr("Open Flow Scene"),
|
|
||||||
QDir::homePath(),
|
|
||||||
tr("Flow Scene Files (*.flow)"));
|
|
||||||
|
|
||||||
if (!fileName.isEmpty()) {
|
|
||||||
if (!fileName.endsWith("flow", Qt::CaseInsensitive))
|
|
||||||
fileName += ".flow";
|
|
||||||
|
|
||||||
QFile file(fileName);
|
|
||||||
if (file.open(QIODevice::WriteOnly)) {
|
|
||||||
file.write(QJsonDocument(_graphModel.save()).toJson());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFlowGraphicsScene::load()
|
|
||||||
{
|
|
||||||
QString fileName = QFileDialog::getOpenFileName(nullptr,
|
|
||||||
tr("Open Flow Scene"),
|
|
||||||
QDir::homePath(),
|
|
||||||
tr("Flow Scene Files (*.flow)"));
|
|
||||||
|
|
||||||
if (!QFileInfo::exists(fileName))
|
|
||||||
return;
|
|
||||||
|
|
||||||
QFile file(fileName);
|
|
||||||
|
|
||||||
if (!file.open(QIODevice::ReadOnly))
|
|
||||||
return;
|
|
||||||
|
|
||||||
clearScene();
|
|
||||||
|
|
||||||
QByteArray const wholeFile = file.readAll();
|
|
||||||
|
|
||||||
_graphModel.load(QJsonDocument::fromJson(wholeFile).object());
|
|
||||||
|
|
||||||
Q_EMIT sceneLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
#include "DefaultHorizontalNodeGeometry.hpp"
|
|
||||||
|
|
||||||
#include "AbstractGraphModel.hpp"
|
|
||||||
#include "NodeData.hpp"
|
|
||||||
|
|
||||||
#include <QPoint>
|
|
||||||
#include <QRect>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
DefaultHorizontalNodeGeometry::DefaultHorizontalNodeGeometry(AbstractGraphModel &graphModel)
|
|
||||||
: AbstractNodeGeometry(graphModel)
|
|
||||||
, _portSize(20)
|
|
||||||
, _portSpasing(10)
|
|
||||||
, _fontMetrics(QFont())
|
|
||||||
, _boldFontMetrics(QFont())
|
|
||||||
{
|
|
||||||
QFont f({ "Arial", 10 });
|
|
||||||
f.setBold(true);
|
|
||||||
_boldFontMetrics = QFontMetrics(f);
|
|
||||||
|
|
||||||
_portSize = _fontMetrics.height();
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize DefaultHorizontalNodeGeometry::size(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
return _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefaultHorizontalNodeGeometry::recomputeSize(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
unsigned int height = maxVerticalPortsExtent(nodeId);
|
|
||||||
|
|
||||||
if (auto w = _graphModel.nodeData<QWidget *>(nodeId, NodeRole::Widget)) {
|
|
||||||
height = std::max(height, static_cast<unsigned int>(w->height()));
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectF const capRect = captionRect(nodeId);
|
|
||||||
|
|
||||||
height += capRect.height();
|
|
||||||
|
|
||||||
height += _portSpasing; // space above caption
|
|
||||||
height += _portSpasing; // space below caption
|
|
||||||
|
|
||||||
unsigned int inPortWidth = maxPortsTextAdvance(nodeId, PortType::In);
|
|
||||||
unsigned int outPortWidth = maxPortsTextAdvance(nodeId, PortType::Out);
|
|
||||||
|
|
||||||
unsigned int width = inPortWidth + outPortWidth + 4 * _portSpasing;
|
|
||||||
|
|
||||||
if (auto w = _graphModel.nodeData<QWidget *>(nodeId, NodeRole::Widget)) {
|
|
||||||
width += w->width();
|
|
||||||
}
|
|
||||||
|
|
||||||
width = std::max(width, static_cast<unsigned int>(capRect.width()) + 2 * _portSpasing);
|
|
||||||
|
|
||||||
QSize size(width, height);
|
|
||||||
|
|
||||||
_graphModel.setNodeData(nodeId, NodeRole::Size, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF DefaultHorizontalNodeGeometry::portPosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const portIndex) const
|
|
||||||
{
|
|
||||||
unsigned int const step = _portSize + _portSpasing;
|
|
||||||
|
|
||||||
QPointF result;
|
|
||||||
|
|
||||||
double totalHeight = 15.0;
|
|
||||||
|
|
||||||
totalHeight += captionRect(nodeId).height();
|
|
||||||
totalHeight += _portSpasing;
|
|
||||||
|
|
||||||
totalHeight += step * portIndex;
|
|
||||||
totalHeight += step / 2.0;
|
|
||||||
|
|
||||||
QSize size = _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
|
|
||||||
switch (portType) {
|
|
||||||
case PortType::In: {
|
|
||||||
double x = 0.0;
|
|
||||||
|
|
||||||
result = QPointF(x, totalHeight);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PortType::Out: {
|
|
||||||
double x = size.width();
|
|
||||||
|
|
||||||
result = QPointF(x, totalHeight);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF DefaultHorizontalNodeGeometry::portTextPosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const portIndex) const
|
|
||||||
{
|
|
||||||
QPointF p = portPosition(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
QRectF rect = portTextRect(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
p.setY(p.y() + rect.height() / 4.0);
|
|
||||||
|
|
||||||
QSize size = _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
|
|
||||||
switch (portType) {
|
|
||||||
case PortType::In:
|
|
||||||
p.setX(_portSpasing);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PortType::Out:
|
|
||||||
p.setX(size.width() - _portSpasing - rect.width());
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectF DefaultHorizontalNodeGeometry::captionRect(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
if (!_graphModel.nodeData<bool>(nodeId, NodeRole::CaptionVisible))
|
|
||||||
return QRect();
|
|
||||||
|
|
||||||
QString name = _graphModel.nodeData<QString>(nodeId, NodeRole::Caption);
|
|
||||||
|
|
||||||
return _boldFontMetrics.boundingRect(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF DefaultHorizontalNodeGeometry::captionPosition(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
QSize size = _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
return QPointF(0.5 * (size.width() - captionRect(nodeId).width()),
|
|
||||||
0.5 * _portSpasing + captionRect(nodeId).height());
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF DefaultHorizontalNodeGeometry::widgetPosition(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
QSize size = _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
|
|
||||||
unsigned int captionHeight = captionRect(nodeId).height();
|
|
||||||
|
|
||||||
if (auto w = _graphModel.nodeData<QWidget *>(nodeId, NodeRole::Widget)) {
|
|
||||||
// If the widget wants to use as much vertical space as possible,
|
|
||||||
// place it immediately after the caption.
|
|
||||||
if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) {
|
|
||||||
return QPointF(2.0 * _portSpasing + maxPortsTextAdvance(nodeId, PortType::In),
|
|
||||||
captionHeight);
|
|
||||||
} else {
|
|
||||||
return QPointF(2.0 * _portSpasing + maxPortsTextAdvance(nodeId, PortType::In),
|
|
||||||
(captionHeight + size.height() - w->height()) / 2.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QPointF();
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect DefaultHorizontalNodeGeometry::resizeHandleRect(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
QSize size = _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
|
|
||||||
unsigned int rectSize = 7;
|
|
||||||
|
|
||||||
return QRect(size.width() - _portSpasing, size.height() - _portSpasing, rectSize, rectSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectF DefaultHorizontalNodeGeometry::portTextRect(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const portIndex) const
|
|
||||||
{
|
|
||||||
QString s;
|
|
||||||
if (_graphModel.portData<bool>(nodeId, portType, portIndex, PortRole::CaptionVisible)) {
|
|
||||||
s = _graphModel.portData<QString>(nodeId, portType, portIndex, PortRole::Caption);
|
|
||||||
} else {
|
|
||||||
auto portData = _graphModel.portData(nodeId, portType, portIndex, PortRole::DataType);
|
|
||||||
|
|
||||||
s = portData.value<NodeDataType>().name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _fontMetrics.boundingRect(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int DefaultHorizontalNodeGeometry::maxVerticalPortsExtent(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
PortCount nInPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::InPortCount);
|
|
||||||
|
|
||||||
PortCount nOutPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::OutPortCount);
|
|
||||||
|
|
||||||
unsigned int maxNumOfEntries = std::max(nInPorts, nOutPorts);
|
|
||||||
unsigned int step = _portSize + _portSpasing;
|
|
||||||
|
|
||||||
return step * maxNumOfEntries;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int DefaultHorizontalNodeGeometry::maxPortsTextAdvance(NodeId const nodeId,
|
|
||||||
PortType const portType) const
|
|
||||||
{
|
|
||||||
unsigned int width = 0;
|
|
||||||
|
|
||||||
size_t const n = _graphModel
|
|
||||||
.nodeData(nodeId,
|
|
||||||
(portType == PortType::Out) ? NodeRole::OutPortCount
|
|
||||||
: NodeRole::InPortCount)
|
|
||||||
.toUInt();
|
|
||||||
|
|
||||||
for (PortIndex portIndex = 0ul; portIndex < n; ++portIndex) {
|
|
||||||
QString name;
|
|
||||||
|
|
||||||
if (_graphModel.portData<bool>(nodeId, portType, portIndex, PortRole::CaptionVisible)) {
|
|
||||||
name = _graphModel.portData<QString>(nodeId, portType, portIndex, PortRole::Caption);
|
|
||||||
} else {
|
|
||||||
NodeDataType portData = _graphModel.portData<NodeDataType>(nodeId,
|
|
||||||
portType,
|
|
||||||
portIndex,
|
|
||||||
PortRole::DataType);
|
|
||||||
|
|
||||||
name = portData.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,284 +0,0 @@
|
||||||
#include "DefaultNodePainter.hpp"
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include <QtCore/QMargins>
|
|
||||||
|
|
||||||
#include "AbstractGraphModel.hpp"
|
|
||||||
#include "AbstractNodeGeometry.hpp"
|
|
||||||
#include "BasicGraphicsScene.hpp"
|
|
||||||
#include "ConnectionGraphicsObject.hpp"
|
|
||||||
#include "ConnectionIdUtils.hpp"
|
|
||||||
#include "NodeGraphicsObject.hpp"
|
|
||||||
#include "NodeState.hpp"
|
|
||||||
#include "StyleCollection.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
void DefaultNodePainter::paint(QPainter *painter, NodeGraphicsObject &ngo) const
|
|
||||||
{
|
|
||||||
// TODO?
|
|
||||||
//AbstractNodeGeometry & geometry = ngo.nodeScene()->nodeGeometry();
|
|
||||||
//geometry.recomputeSizeIfFontChanged(painter->font());
|
|
||||||
|
|
||||||
drawNodeRect(painter, ngo);
|
|
||||||
|
|
||||||
drawConnectionPoints(painter, ngo);
|
|
||||||
|
|
||||||
drawFilledConnectionPoints(painter, ngo);
|
|
||||||
|
|
||||||
drawNodeCaption(painter, ngo);
|
|
||||||
|
|
||||||
drawEntryLabels(painter, ngo);
|
|
||||||
|
|
||||||
drawResizeRect(painter, ngo);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefaultNodePainter::drawNodeRect(QPainter *painter, NodeGraphicsObject &ngo) const
|
|
||||||
{
|
|
||||||
AbstractGraphModel &model = ngo.graphModel();
|
|
||||||
|
|
||||||
NodeId const nodeId = ngo.nodeId();
|
|
||||||
|
|
||||||
AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry();
|
|
||||||
|
|
||||||
QSize size = geometry.size(nodeId);
|
|
||||||
|
|
||||||
QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style));
|
|
||||||
|
|
||||||
NodeStyle nodeStyle(json.object());
|
|
||||||
|
|
||||||
auto color = ngo.isSelected() ? nodeStyle.SelectedBoundaryColor : nodeStyle.NormalBoundaryColor;
|
|
||||||
|
|
||||||
// if (ngo.nodeState().hovered()) {
|
|
||||||
// QPen p(color, nodeStyle.HoveredPenWidth);
|
|
||||||
// painter->setPen(p);
|
|
||||||
// } else {
|
|
||||||
// QPen p(color, nodeStyle.PenWidth);
|
|
||||||
// painter->setPen(p);
|
|
||||||
// }
|
|
||||||
QPen pen = painter->pen();
|
|
||||||
pen.setBrush(color);
|
|
||||||
pen.setWidth(2);
|
|
||||||
painter->setPen(pen);
|
|
||||||
painter->setBrush(nodeStyle.GradientColor0);
|
|
||||||
|
|
||||||
QRectF boundary(0, 0, size.width(), size.height());
|
|
||||||
painter->drawRect(boundary);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefaultNodePainter::drawConnectionPoints(QPainter *painter, NodeGraphicsObject &ngo) const
|
|
||||||
{
|
|
||||||
AbstractGraphModel &model = ngo.graphModel();
|
|
||||||
NodeId const nodeId = ngo.nodeId();
|
|
||||||
AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry();
|
|
||||||
|
|
||||||
QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style));
|
|
||||||
NodeStyle nodeStyle(json.object());
|
|
||||||
|
|
||||||
auto const &connectionStyle = StyleCollection::connectionStyle();
|
|
||||||
|
|
||||||
float diameter = nodeStyle.ConnectionPointDiameter;
|
|
||||||
auto reducedDiameter = diameter * 0.6;
|
|
||||||
|
|
||||||
for (PortType portType : {PortType::Out, PortType::In}) {
|
|
||||||
size_t const n = model
|
|
||||||
.nodeData(nodeId,
|
|
||||||
(portType == PortType::Out) ? NodeRole::OutPortCount
|
|
||||||
: NodeRole::InPortCount)
|
|
||||||
.toUInt();
|
|
||||||
|
|
||||||
for (PortIndex portIndex = 0; portIndex < n; ++portIndex) {
|
|
||||||
QPointF p = geometry.portPosition(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
auto const &dataType = model.portData(nodeId, portType, portIndex, PortRole::DataType)
|
|
||||||
.value<NodeDataType>();
|
|
||||||
|
|
||||||
double r = 1.0;
|
|
||||||
|
|
||||||
NodeState const &state = ngo.nodeState();
|
|
||||||
|
|
||||||
if (auto const *cgo = state.connectionForReaction()) {
|
|
||||||
PortType requiredPort = cgo->connectionState().requiredPort();
|
|
||||||
|
|
||||||
if (requiredPort == portType) {
|
|
||||||
ConnectionId possibleConnectionId = makeCompleteConnectionId(cgo->connectionId(),
|
|
||||||
nodeId,
|
|
||||||
portIndex);
|
|
||||||
|
|
||||||
bool const possible = model.connectionPossible(possibleConnectionId);
|
|
||||||
|
|
||||||
auto cp = cgo->sceneTransform().map(cgo->endPoint(requiredPort));
|
|
||||||
cp = ngo.sceneTransform().inverted().map(cp);
|
|
||||||
|
|
||||||
auto diff = cp - p;
|
|
||||||
double dist = std::sqrt(QPointF::dotProduct(diff, diff));
|
|
||||||
|
|
||||||
if (possible) {
|
|
||||||
double const thres = 40.0;
|
|
||||||
r = (dist < thres) ? (2.0 - dist / thres) : 1.0;
|
|
||||||
} else {
|
|
||||||
double const thres = 80.0;
|
|
||||||
r = (dist < thres) ? (dist / thres) : 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionStyle.useDataDefinedColors()) {
|
|
||||||
painter->setBrush(connectionStyle.normalColor(dataType.id));
|
|
||||||
} else {
|
|
||||||
painter->setBrush(nodeStyle.ConnectionPointColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
painter->drawEllipse(p, reducedDiameter * r, reducedDiameter * r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ngo.nodeState().connectionForReaction()) {
|
|
||||||
ngo.nodeState().resetConnectionForReaction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefaultNodePainter::drawFilledConnectionPoints(QPainter *painter, NodeGraphicsObject &ngo) const
|
|
||||||
{
|
|
||||||
AbstractGraphModel &model = ngo.graphModel();
|
|
||||||
NodeId const nodeId = ngo.nodeId();
|
|
||||||
AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry();
|
|
||||||
|
|
||||||
QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style));
|
|
||||||
NodeStyle nodeStyle(json.object());
|
|
||||||
|
|
||||||
auto diameter = nodeStyle.ConnectionPointDiameter;
|
|
||||||
|
|
||||||
for (PortType portType : {PortType::Out, PortType::In}) {
|
|
||||||
size_t const n = model
|
|
||||||
.nodeData(nodeId,
|
|
||||||
(portType == PortType::Out) ? NodeRole::OutPortCount
|
|
||||||
: NodeRole::InPortCount)
|
|
||||||
.toUInt();
|
|
||||||
|
|
||||||
for (PortIndex portIndex = 0; portIndex < n; ++portIndex) {
|
|
||||||
QPointF p = geometry.portPosition(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
auto const &connected = model.connections(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
if (!connected.empty()) {
|
|
||||||
auto const &dataType = model
|
|
||||||
.portData(nodeId, portType, portIndex, PortRole::DataType)
|
|
||||||
.value<NodeDataType>();
|
|
||||||
|
|
||||||
auto const &connectionStyle = StyleCollection::connectionStyle();
|
|
||||||
if (connectionStyle.useDataDefinedColors()) {
|
|
||||||
QColor const c = connectionStyle.normalColor(dataType.id);
|
|
||||||
painter->setPen(c);
|
|
||||||
painter->setBrush(c);
|
|
||||||
} else {
|
|
||||||
painter->setPen(nodeStyle.FilledConnectionPointColor);
|
|
||||||
painter->setBrush(nodeStyle.FilledConnectionPointColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
painter->drawEllipse(p, diameter * 0.4, diameter * 0.4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefaultNodePainter::drawNodeCaption(QPainter *painter, NodeGraphicsObject &ngo) const
|
|
||||||
{
|
|
||||||
AbstractGraphModel &model = ngo.graphModel();
|
|
||||||
NodeId const nodeId = ngo.nodeId();
|
|
||||||
AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry();
|
|
||||||
|
|
||||||
if (!model.nodeData(nodeId, NodeRole::CaptionVisible).toBool())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString const name = model.nodeData(nodeId, NodeRole::Caption).toString();
|
|
||||||
|
|
||||||
QFont f({ "Arial", 10 });
|
|
||||||
f.setBold(true);
|
|
||||||
|
|
||||||
QPointF position = geometry.captionPosition(nodeId);
|
|
||||||
|
|
||||||
QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style));
|
|
||||||
NodeStyle nodeStyle(json.object());
|
|
||||||
|
|
||||||
|
|
||||||
painter->setBrush(QBrush("#f7aa1b"));
|
|
||||||
QFontMetrics metrics(f);
|
|
||||||
auto fontRect = metrics.boundingRect(name);
|
|
||||||
|
|
||||||
QSize sizeH = geometry.size(nodeId);
|
|
||||||
QRectF titleRect;
|
|
||||||
|
|
||||||
int w = sizeH.width();
|
|
||||||
// titleRect.setX(2);
|
|
||||||
// titleRect.setY(2);
|
|
||||||
titleRect.setWidth(w);
|
|
||||||
titleRect.setHeight(fontRect.height() + position.ry());
|
|
||||||
QPen pen = painter->pen();
|
|
||||||
pen.setWidth(0);
|
|
||||||
painter->setPen(pen);
|
|
||||||
painter->drawRect(titleRect);
|
|
||||||
|
|
||||||
painter->setFont(f);
|
|
||||||
painter->setPen(Qt::black);
|
|
||||||
painter->drawText(position, name);
|
|
||||||
|
|
||||||
f.setBold(false);
|
|
||||||
painter->setFont(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefaultNodePainter::drawEntryLabels(QPainter *painter, NodeGraphicsObject &ngo) const
|
|
||||||
{
|
|
||||||
AbstractGraphModel &model = ngo.graphModel();
|
|
||||||
NodeId const nodeId = ngo.nodeId();
|
|
||||||
AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry();
|
|
||||||
|
|
||||||
QJsonDocument json = QJsonDocument::fromVariant(model.nodeData(nodeId, NodeRole::Style));
|
|
||||||
NodeStyle nodeStyle(json.object());
|
|
||||||
|
|
||||||
for (PortType portType : {PortType::Out, PortType::In}) {
|
|
||||||
unsigned int n = model.nodeData<unsigned int>(nodeId,
|
|
||||||
(portType == PortType::Out)
|
|
||||||
? NodeRole::OutPortCount
|
|
||||||
: NodeRole::InPortCount);
|
|
||||||
|
|
||||||
for (PortIndex portIndex = 0; portIndex < n; ++portIndex) {
|
|
||||||
auto const &connected = model.connections(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
QPointF p = geometry.portTextPosition(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
if (connected.empty())
|
|
||||||
painter->setPen(nodeStyle.FontColorFaded);
|
|
||||||
else
|
|
||||||
painter->setPen(nodeStyle.FontColor);
|
|
||||||
|
|
||||||
QString s;
|
|
||||||
|
|
||||||
if (model.portData<bool>(nodeId, portType, portIndex, PortRole::CaptionVisible)) {
|
|
||||||
s = model.portData<QString>(nodeId, portType, portIndex, PortRole::Caption);
|
|
||||||
} else {
|
|
||||||
auto portData = model.portData(nodeId, portType, portIndex, PortRole::DataType);
|
|
||||||
|
|
||||||
s = portData.value<NodeDataType>().name;
|
|
||||||
}
|
|
||||||
|
|
||||||
painter->drawText(p, s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefaultNodePainter::drawResizeRect(QPainter *painter, NodeGraphicsObject &ngo) const
|
|
||||||
{
|
|
||||||
AbstractGraphModel &model = ngo.graphModel();
|
|
||||||
NodeId const nodeId = ngo.nodeId();
|
|
||||||
AbstractNodeGeometry &geometry = ngo.nodeScene()->nodeGeometry();
|
|
||||||
|
|
||||||
if (model.nodeFlags(nodeId) & NodeFlag::Resizable) {
|
|
||||||
painter->setBrush(Qt::gray);
|
|
||||||
|
|
||||||
painter->drawEllipse(geometry.resizeHandleRect(nodeId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,300 +0,0 @@
|
||||||
#include "DefaultVerticalNodeGeometry.hpp"
|
|
||||||
|
|
||||||
#include "AbstractGraphModel.hpp"
|
|
||||||
#include "NodeData.hpp"
|
|
||||||
|
|
||||||
#include <QPoint>
|
|
||||||
#include <QRect>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
DefaultVerticalNodeGeometry::DefaultVerticalNodeGeometry(AbstractGraphModel &graphModel)
|
|
||||||
: AbstractNodeGeometry(graphModel)
|
|
||||||
, _portSize(20)
|
|
||||||
, _portSpasing(10)
|
|
||||||
, _fontMetrics(QFont())
|
|
||||||
, _boldFontMetrics(QFont())
|
|
||||||
{
|
|
||||||
QFont f({ "Arial", 10 });
|
|
||||||
f.setBold(true);
|
|
||||||
_boldFontMetrics = QFontMetrics(f);
|
|
||||||
|
|
||||||
_portSize = _fontMetrics.height();
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize DefaultVerticalNodeGeometry::size(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
return _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefaultVerticalNodeGeometry::recomputeSize(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
unsigned int height = _portSpasing; // maxHorizontalPortsExtent(nodeId);
|
|
||||||
|
|
||||||
if (auto w = _graphModel.nodeData<QWidget *>(nodeId, NodeRole::Widget)) {
|
|
||||||
height = std::max(height, static_cast<unsigned int>(w->height()));
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectF const capRect = captionRect(nodeId);
|
|
||||||
|
|
||||||
height += capRect.height();
|
|
||||||
|
|
||||||
height += _portSpasing;
|
|
||||||
height += _portSpasing;
|
|
||||||
|
|
||||||
PortCount nInPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::InPortCount);
|
|
||||||
PortCount nOutPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::OutPortCount);
|
|
||||||
|
|
||||||
// Adding double step (top and bottom) to reserve space for port captions.
|
|
||||||
|
|
||||||
height += portCaptionsHeight(nodeId, PortType::In);
|
|
||||||
height += portCaptionsHeight(nodeId, PortType::Out);
|
|
||||||
|
|
||||||
unsigned int inPortWidth = maxPortsTextAdvance(nodeId, PortType::In);
|
|
||||||
unsigned int outPortWidth = maxPortsTextAdvance(nodeId, PortType::Out);
|
|
||||||
|
|
||||||
unsigned int totalInPortsWidth = nInPorts > 0
|
|
||||||
? inPortWidth * nInPorts + _portSpasing * (nInPorts - 1)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
unsigned int totalOutPortsWidth = nOutPorts > 0 ? outPortWidth * nOutPorts
|
|
||||||
+ _portSpasing * (nOutPorts - 1)
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
unsigned int width = std::max(totalInPortsWidth, totalOutPortsWidth);
|
|
||||||
|
|
||||||
if (auto w = _graphModel.nodeData<QWidget *>(nodeId, NodeRole::Widget)) {
|
|
||||||
width = std::max(width, static_cast<unsigned int>(w->width()));
|
|
||||||
}
|
|
||||||
|
|
||||||
width = std::max(width, static_cast<unsigned int>(capRect.width()));
|
|
||||||
|
|
||||||
width += _portSpasing;
|
|
||||||
width += _portSpasing;
|
|
||||||
|
|
||||||
QSize size(width, height);
|
|
||||||
|
|
||||||
_graphModel.setNodeData(nodeId, NodeRole::Size, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF DefaultVerticalNodeGeometry::portPosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const portIndex) const
|
|
||||||
{
|
|
||||||
QPointF result;
|
|
||||||
|
|
||||||
QSize size = _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
|
|
||||||
switch (portType) {
|
|
||||||
case PortType::In: {
|
|
||||||
unsigned int inPortWidth = maxPortsTextAdvance(nodeId, PortType::In) + _portSpasing;
|
|
||||||
|
|
||||||
PortCount nInPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::InPortCount);
|
|
||||||
|
|
||||||
double x = (size.width() - (nInPorts - 1) * inPortWidth) / 2.0 + portIndex * inPortWidth;
|
|
||||||
|
|
||||||
double y = 0.0;
|
|
||||||
|
|
||||||
result = QPointF(x, y);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PortType::Out: {
|
|
||||||
unsigned int outPortWidth = maxPortsTextAdvance(nodeId, PortType::Out) + _portSpasing;
|
|
||||||
PortCount nOutPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::OutPortCount);
|
|
||||||
|
|
||||||
double x = (size.width() - (nOutPorts - 1) * outPortWidth) / 2.0 + portIndex * outPortWidth;
|
|
||||||
|
|
||||||
double y = size.height();
|
|
||||||
|
|
||||||
result = QPointF(x, y);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF DefaultVerticalNodeGeometry::portTextPosition(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const portIndex) const
|
|
||||||
{
|
|
||||||
QPointF p = portPosition(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
QRectF rect = portTextRect(nodeId, portType, portIndex);
|
|
||||||
|
|
||||||
p.setX(p.x() - rect.width() / 2.0);
|
|
||||||
|
|
||||||
QSize size = _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
|
|
||||||
switch (portType) {
|
|
||||||
case PortType::In:
|
|
||||||
p.setY(5.0 + rect.height());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PortType::Out:
|
|
||||||
p.setY(size.height() - 5.0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectF DefaultVerticalNodeGeometry::captionRect(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
if (!_graphModel.nodeData<bool>(nodeId, NodeRole::CaptionVisible))
|
|
||||||
return QRect();
|
|
||||||
|
|
||||||
QString name = _graphModel.nodeData<QString>(nodeId, NodeRole::Caption);
|
|
||||||
|
|
||||||
return _boldFontMetrics.boundingRect(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF DefaultVerticalNodeGeometry::captionPosition(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
QSize size = _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
|
|
||||||
unsigned int step = portCaptionsHeight(nodeId, PortType::In);
|
|
||||||
step += _portSpasing;
|
|
||||||
|
|
||||||
auto rect = captionRect(nodeId);
|
|
||||||
|
|
||||||
return QPointF(0.5 * (size.width() - rect.width()), step + rect.height());
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF DefaultVerticalNodeGeometry::widgetPosition(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
QSize size = _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
|
|
||||||
unsigned int captionHeight = captionRect(nodeId).height();
|
|
||||||
|
|
||||||
if (auto w = _graphModel.nodeData<QWidget *>(nodeId, NodeRole::Widget)) {
|
|
||||||
// If the widget wants to use as much vertical space as possible,
|
|
||||||
// place it immediately after the caption.
|
|
||||||
if (w->sizePolicy().verticalPolicy() & QSizePolicy::ExpandFlag) {
|
|
||||||
return QPointF(_portSpasing + maxPortsTextAdvance(nodeId, PortType::In), captionHeight);
|
|
||||||
} else {
|
|
||||||
return QPointF(_portSpasing + maxPortsTextAdvance(nodeId, PortType::In),
|
|
||||||
(captionHeight + size.height() - w->height()) / 2.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QPointF();
|
|
||||||
}
|
|
||||||
|
|
||||||
QRect DefaultVerticalNodeGeometry::resizeHandleRect(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
QSize size = _graphModel.nodeData<QSize>(nodeId, NodeRole::Size);
|
|
||||||
|
|
||||||
unsigned int rectSize = 7;
|
|
||||||
|
|
||||||
return QRect(size.width() - rectSize, size.height() - rectSize, rectSize, rectSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
QRectF DefaultVerticalNodeGeometry::portTextRect(NodeId const nodeId,
|
|
||||||
PortType const portType,
|
|
||||||
PortIndex const portIndex) const
|
|
||||||
{
|
|
||||||
QString s;
|
|
||||||
if (_graphModel.portData<bool>(nodeId, portType, portIndex, PortRole::CaptionVisible)) {
|
|
||||||
s = _graphModel.portData<QString>(nodeId, portType, portIndex, PortRole::Caption);
|
|
||||||
} else {
|
|
||||||
auto portData = _graphModel.portData(nodeId, portType, portIndex, PortRole::DataType);
|
|
||||||
|
|
||||||
s = portData.value<NodeDataType>().name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _fontMetrics.boundingRect(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int DefaultVerticalNodeGeometry::maxHorizontalPortsExtent(NodeId const nodeId) const
|
|
||||||
{
|
|
||||||
PortCount nInPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::InPortCount);
|
|
||||||
|
|
||||||
PortCount nOutPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::OutPortCount);
|
|
||||||
|
|
||||||
unsigned int maxNumOfEntries = std::max(nInPorts, nOutPorts);
|
|
||||||
unsigned int step = _portSize + _portSpasing;
|
|
||||||
|
|
||||||
return step * maxNumOfEntries;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int DefaultVerticalNodeGeometry::maxPortsTextAdvance(NodeId const nodeId,
|
|
||||||
PortType const portType) const
|
|
||||||
{
|
|
||||||
unsigned int width = 0;
|
|
||||||
|
|
||||||
size_t const n = _graphModel
|
|
||||||
.nodeData(nodeId,
|
|
||||||
(portType == PortType::Out) ? NodeRole::OutPortCount
|
|
||||||
: NodeRole::InPortCount)
|
|
||||||
.toUInt();
|
|
||||||
|
|
||||||
for (PortIndex portIndex = 0ul; portIndex < n; ++portIndex) {
|
|
||||||
QString name;
|
|
||||||
|
|
||||||
if (_graphModel.portData<bool>(nodeId, portType, portIndex, PortRole::CaptionVisible)) {
|
|
||||||
name = _graphModel.portData<QString>(nodeId, portType, portIndex, PortRole::Caption);
|
|
||||||
} else {
|
|
||||||
NodeDataType portData = _graphModel.portData<NodeDataType>(nodeId,
|
|
||||||
portType,
|
|
||||||
portIndex,
|
|
||||||
PortRole::DataType);
|
|
||||||
|
|
||||||
name = portData.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int DefaultVerticalNodeGeometry::portCaptionsHeight(NodeId const nodeId,
|
|
||||||
PortType const portType) const
|
|
||||||
{
|
|
||||||
unsigned int h = 0;
|
|
||||||
|
|
||||||
switch (portType) {
|
|
||||||
case PortType::In: {
|
|
||||||
PortCount nInPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::InPortCount);
|
|
||||||
for (PortIndex i = 0; i < nInPorts; ++i) {
|
|
||||||
if (_graphModel.portData<bool>(nodeId, PortType::In, i, PortRole::CaptionVisible)) {
|
|
||||||
h += _portSpasing;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PortType::Out: {
|
|
||||||
PortCount nOutPorts = _graphModel.nodeData<PortCount>(nodeId, NodeRole::OutPortCount);
|
|
||||||
for (PortIndex i = 0; i < nOutPorts; ++i) {
|
|
||||||
if (_graphModel.portData<bool>(nodeId, PortType::Out, i, PortRole::CaptionVisible)) {
|
|
||||||
h += _portSpasing;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "Definitions.hpp"
|
|
||||||
|
|
@ -1,405 +0,0 @@
|
||||||
#include "GraphicsView.hpp"
|
|
||||||
|
|
||||||
#include "BasicGraphicsScene.hpp"
|
|
||||||
#include "ConnectionGraphicsObject.hpp"
|
|
||||||
#include "NodeGraphicsObject.hpp"
|
|
||||||
#include "StyleCollection.hpp"
|
|
||||||
#include "UndoCommands.hpp"
|
|
||||||
|
|
||||||
#include <QtWidgets/QGraphicsScene>
|
|
||||||
|
|
||||||
#include <QtGui/QBrush>
|
|
||||||
#include <QtGui/QPen>
|
|
||||||
|
|
||||||
#include <QtWidgets/QMenu>
|
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
#include <QtCore/QPointF>
|
|
||||||
#include <QtCore/QRectF>
|
|
||||||
|
|
||||||
#include <QtOpenGL>
|
|
||||||
#include <QtWidgets>
|
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
using QtNodes::BasicGraphicsScene;
|
|
||||||
using QtNodes::GraphicsView;
|
|
||||||
|
|
||||||
GraphicsView::GraphicsView(QWidget *parent)
|
|
||||||
: QGraphicsView(parent)
|
|
||||||
, _clearSelectionAction(Q_NULLPTR)
|
|
||||||
, _deleteSelectionAction(Q_NULLPTR)
|
|
||||||
, _duplicateSelectionAction(Q_NULLPTR)
|
|
||||||
, _copySelectionAction(Q_NULLPTR)
|
|
||||||
, _pasteAction(Q_NULLPTR)
|
|
||||||
{
|
|
||||||
setDragMode(QGraphicsView::ScrollHandDrag);
|
|
||||||
setRenderHint(QPainter::Antialiasing);
|
|
||||||
|
|
||||||
auto const &flowViewStyle = StyleCollection::flowViewStyle();
|
|
||||||
|
|
||||||
setBackgroundBrush(flowViewStyle.BackgroundColor);
|
|
||||||
|
|
||||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
|
|
||||||
setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
|
|
||||||
|
|
||||||
setCacheMode(QGraphicsView::CacheBackground);
|
|
||||||
setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
|
|
||||||
|
|
||||||
setScaleRange(0.3, 2);
|
|
||||||
|
|
||||||
// Sets the scene rect to its maximum possible ranges to avoid autu scene range
|
|
||||||
// re-calculation when expanding the all QGraphicsItems common rect.
|
|
||||||
int maxSize = 32767;
|
|
||||||
setSceneRect(-maxSize, -maxSize, (maxSize * 2), (maxSize * 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsView::GraphicsView(BasicGraphicsScene *scene, QWidget *parent)
|
|
||||||
: GraphicsView(parent)
|
|
||||||
{
|
|
||||||
setScene(scene);
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction *GraphicsView::clearSelectionAction() const
|
|
||||||
{
|
|
||||||
return _clearSelectionAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction *GraphicsView::deleteSelectionAction() const
|
|
||||||
{
|
|
||||||
return _deleteSelectionAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::setScene(BasicGraphicsScene *scene)
|
|
||||||
{
|
|
||||||
QGraphicsView::setScene(scene);
|
|
||||||
|
|
||||||
{
|
|
||||||
// setup actions
|
|
||||||
delete _clearSelectionAction;
|
|
||||||
_clearSelectionAction = new QAction(QStringLiteral("Clear Selection"), this);
|
|
||||||
_clearSelectionAction->setShortcut(Qt::Key_Escape);
|
|
||||||
|
|
||||||
connect(_clearSelectionAction, &QAction::triggered, scene, &QGraphicsScene::clearSelection);
|
|
||||||
|
|
||||||
addAction(_clearSelectionAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
delete _deleteSelectionAction;
|
|
||||||
_deleteSelectionAction = new QAction(QStringLiteral("Delete Selection"), this);
|
|
||||||
_deleteSelectionAction->setShortcutContext(Qt::ShortcutContext::WidgetShortcut);
|
|
||||||
_deleteSelectionAction->setShortcut(QKeySequence(QKeySequence::Delete));
|
|
||||||
connect(_deleteSelectionAction,
|
|
||||||
&QAction::triggered,
|
|
||||||
this,
|
|
||||||
&GraphicsView::onDeleteSelectedObjects);
|
|
||||||
|
|
||||||
addAction(_deleteSelectionAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
delete _duplicateSelectionAction;
|
|
||||||
_duplicateSelectionAction = new QAction(QStringLiteral("Duplicate Selection"), this);
|
|
||||||
_duplicateSelectionAction->setShortcutContext(Qt::ShortcutContext::WidgetShortcut);
|
|
||||||
_duplicateSelectionAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_D));
|
|
||||||
connect(_duplicateSelectionAction,
|
|
||||||
&QAction::triggered,
|
|
||||||
this,
|
|
||||||
&GraphicsView::onDuplicateSelectedObjects);
|
|
||||||
|
|
||||||
addAction(_duplicateSelectionAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
delete _copySelectionAction;
|
|
||||||
_copySelectionAction = new QAction(QStringLiteral("Copy Selection"), this);
|
|
||||||
_copySelectionAction->setShortcutContext(Qt::ShortcutContext::WidgetShortcut);
|
|
||||||
_copySelectionAction->setShortcut(QKeySequence(QKeySequence::Copy));
|
|
||||||
connect(_copySelectionAction,
|
|
||||||
&QAction::triggered,
|
|
||||||
this,
|
|
||||||
&GraphicsView::onCopySelectedObjects);
|
|
||||||
|
|
||||||
addAction(_copySelectionAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
delete _pasteAction;
|
|
||||||
_pasteAction = new QAction(QStringLiteral("Copy Selection"), this);
|
|
||||||
_pasteAction->setShortcutContext(Qt::ShortcutContext::WidgetShortcut);
|
|
||||||
_pasteAction->setShortcut(QKeySequence(QKeySequence::Paste));
|
|
||||||
connect(_pasteAction, &QAction::triggered, this, &GraphicsView::onPasteObjects);
|
|
||||||
|
|
||||||
addAction(_pasteAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto undoAction = scene->undoStack().createUndoAction(this, tr("&Undo"));
|
|
||||||
undoAction->setShortcuts(QKeySequence::Undo);
|
|
||||||
addAction(undoAction);
|
|
||||||
|
|
||||||
auto redoAction = scene->undoStack().createRedoAction(this, tr("&Redo"));
|
|
||||||
redoAction->setShortcuts(QKeySequence::Redo);
|
|
||||||
addAction(redoAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::centerScene()
|
|
||||||
{
|
|
||||||
if (scene()) {
|
|
||||||
scene()->setSceneRect(QRectF());
|
|
||||||
|
|
||||||
QRectF sceneRect = scene()->sceneRect();
|
|
||||||
|
|
||||||
if (sceneRect.width() > this->rect().width() || sceneRect.height() > this->rect().height()) {
|
|
||||||
fitInView(sceneRect, Qt::KeepAspectRatio);
|
|
||||||
}
|
|
||||||
|
|
||||||
centerOn(sceneRect.center());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::contextMenuEvent(QContextMenuEvent *event)
|
|
||||||
{
|
|
||||||
if (itemAt(event->pos())) {
|
|
||||||
QGraphicsView::contextMenuEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const scenePos = mapToScene(event->pos());
|
|
||||||
|
|
||||||
QMenu *menu = nodeScene()->createSceneMenu(scenePos);
|
|
||||||
|
|
||||||
if (menu) {
|
|
||||||
menu->exec(event->globalPos());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::wheelEvent(QWheelEvent *event)
|
|
||||||
{
|
|
||||||
QPoint delta = event->angleDelta();
|
|
||||||
|
|
||||||
if (delta.y() == 0) {
|
|
||||||
event->ignore();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
double const d = delta.y() / std::abs(delta.y());
|
|
||||||
|
|
||||||
if (d > 0.0)
|
|
||||||
scaleUp();
|
|
||||||
else
|
|
||||||
scaleDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
double GraphicsView::getScale() const
|
|
||||||
{
|
|
||||||
return transform().m11();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::setScaleRange(double minimum, double maximum)
|
|
||||||
{
|
|
||||||
if (maximum < minimum)
|
|
||||||
std::swap(minimum, maximum);
|
|
||||||
minimum = std::max(0.0, minimum);
|
|
||||||
maximum = std::max(0.0, maximum);
|
|
||||||
|
|
||||||
_scaleRange = {minimum, maximum};
|
|
||||||
|
|
||||||
setupScale(transform().m11());
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::setScaleRange(ScaleRange range)
|
|
||||||
{
|
|
||||||
setScaleRange(range.minimum, range.maximum);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::scaleUp()
|
|
||||||
{
|
|
||||||
double const step = 1.2;
|
|
||||||
double const factor = std::pow(step, 1.0);
|
|
||||||
|
|
||||||
if (_scaleRange.maximum > 0) {
|
|
||||||
QTransform t = transform();
|
|
||||||
t.scale(factor, factor);
|
|
||||||
if (t.m11() >= _scaleRange.maximum) {
|
|
||||||
setupScale(t.m11());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scale(factor, factor);
|
|
||||||
Q_EMIT scaleChanged(transform().m11());
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::scaleDown()
|
|
||||||
{
|
|
||||||
double const step = 1.2;
|
|
||||||
double const factor = std::pow(step, -1.0);
|
|
||||||
|
|
||||||
if (_scaleRange.minimum > 0) {
|
|
||||||
QTransform t = transform();
|
|
||||||
t.scale(factor, factor);
|
|
||||||
if (t.m11() <= _scaleRange.minimum) {
|
|
||||||
setupScale(t.m11());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scale(factor, factor);
|
|
||||||
Q_EMIT scaleChanged(transform().m11());
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::setupScale(double scale)
|
|
||||||
{
|
|
||||||
scale = std::max(_scaleRange.minimum, std::min(_scaleRange.maximum, scale));
|
|
||||||
|
|
||||||
if (scale <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (scale == transform().m11())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QTransform matrix;
|
|
||||||
matrix.scale(scale, scale);
|
|
||||||
setTransform(matrix, false);
|
|
||||||
|
|
||||||
Q_EMIT scaleChanged(scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::onDeleteSelectedObjects()
|
|
||||||
{
|
|
||||||
nodeScene()->undoStack().push(new DeleteCommand(nodeScene()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::onDuplicateSelectedObjects()
|
|
||||||
{
|
|
||||||
QPointF const pastePosition = scenePastePosition();
|
|
||||||
|
|
||||||
nodeScene()->undoStack().push(new CopyCommand(nodeScene()));
|
|
||||||
nodeScene()->undoStack().push(new PasteCommand(nodeScene(), pastePosition));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::onCopySelectedObjects()
|
|
||||||
{
|
|
||||||
nodeScene()->undoStack().push(new CopyCommand(nodeScene()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::onPasteObjects()
|
|
||||||
{
|
|
||||||
QPointF const pastePosition = scenePastePosition();
|
|
||||||
nodeScene()->undoStack().push(new PasteCommand(nodeScene(), pastePosition));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::keyPressEvent(QKeyEvent *event)
|
|
||||||
{
|
|
||||||
switch (event->key()) {
|
|
||||||
case Qt::Key_Shift:
|
|
||||||
setDragMode(QGraphicsView::RubberBandDrag);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
QGraphicsView::keyPressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::keyReleaseEvent(QKeyEvent *event)
|
|
||||||
{
|
|
||||||
switch (event->key()) {
|
|
||||||
case Qt::Key_Shift:
|
|
||||||
setDragMode(QGraphicsView::ScrollHandDrag);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
QGraphicsView::keyReleaseEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
QGraphicsView::mousePressEvent(event);
|
|
||||||
if (event->button() == Qt::LeftButton) {
|
|
||||||
_clickPos = mapToScene(event->pos());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::mouseMoveEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
QGraphicsView::mouseMoveEvent(event);
|
|
||||||
if (scene()->mouseGrabberItem() == nullptr && event->buttons() == Qt::LeftButton) {
|
|
||||||
// Make sure shift is not being pressed
|
|
||||||
if ((event->modifiers() & Qt::ShiftModifier) == 0) {
|
|
||||||
QPointF difference = _clickPos - mapToScene(event->pos());
|
|
||||||
setSceneRect(sceneRect().translated(difference.x(), difference.y()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::drawBackground(QPainter *painter, const QRectF &r)
|
|
||||||
{
|
|
||||||
QGraphicsView::drawBackground(painter, r);
|
|
||||||
|
|
||||||
auto drawGrid = [&](double gridStep) {
|
|
||||||
QRect windowRect = rect();
|
|
||||||
QPointF tl = mapToScene(windowRect.topLeft());
|
|
||||||
QPointF br = mapToScene(windowRect.bottomRight());
|
|
||||||
|
|
||||||
double left = std::floor(tl.x() / gridStep - 0.5);
|
|
||||||
double right = std::floor(br.x() / gridStep + 1.0);
|
|
||||||
double bottom = std::floor(tl.y() / gridStep - 0.5);
|
|
||||||
double top = std::floor(br.y() / gridStep + 1.0);
|
|
||||||
|
|
||||||
// vertical lines
|
|
||||||
for (int xi = int(left); xi <= int(right); ++xi) {
|
|
||||||
QLineF line(xi * gridStep, bottom * gridStep, xi * gridStep, top * gridStep);
|
|
||||||
|
|
||||||
painter->drawLine(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// horizontal lines
|
|
||||||
for (int yi = int(bottom); yi <= int(top); ++yi) {
|
|
||||||
QLineF line(left * gridStep, yi * gridStep, right * gridStep, yi * gridStep);
|
|
||||||
painter->drawLine(line);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto const &flowViewStyle = StyleCollection::flowViewStyle();
|
|
||||||
|
|
||||||
QPen pfine(flowViewStyle.FineGridColor, 1.0);
|
|
||||||
|
|
||||||
painter->setPen(pfine);
|
|
||||||
drawGrid(15);
|
|
||||||
|
|
||||||
QPen p(flowViewStyle.CoarseGridColor, 1.0);
|
|
||||||
|
|
||||||
painter->setPen(p);
|
|
||||||
drawGrid(150);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsView::showEvent(QShowEvent *event)
|
|
||||||
{
|
|
||||||
QGraphicsView::showEvent(event);
|
|
||||||
|
|
||||||
centerScene();
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicGraphicsScene *GraphicsView::nodeScene()
|
|
||||||
{
|
|
||||||
return dynamic_cast<BasicGraphicsScene *>(scene());
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF GraphicsView::scenePastePosition()
|
|
||||||
{
|
|
||||||
QPoint origin = mapFromGlobal(QCursor::pos());
|
|
||||||
|
|
||||||
QRect const viewRect = rect();
|
|
||||||
if (!viewRect.contains(origin))
|
|
||||||
origin = viewRect.center();
|
|
||||||
|
|
||||||
return mapToScene(origin);
|
|
||||||
}
|
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
#include "GraphicsViewStyle.hpp"
|
|
||||||
|
|
||||||
#include <QtCore/QFile>
|
|
||||||
#include <QtCore/QJsonArray>
|
|
||||||
#include <QtCore/QJsonObject>
|
|
||||||
#include <QtCore/QJsonValueRef>
|
|
||||||
|
|
||||||
#include "StyleCollection.hpp"
|
|
||||||
|
|
||||||
using QtNodes::GraphicsViewStyle;
|
|
||||||
|
|
||||||
inline void initResources()
|
|
||||||
{
|
|
||||||
Q_INIT_RESOURCE(resources);
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsViewStyle::GraphicsViewStyle()
|
|
||||||
{
|
|
||||||
// Explicit resources inialization for preventing the static initialization
|
|
||||||
// order fiasco: https://isocpp.org/wiki/faq/ctors#static-init-order
|
|
||||||
initResources();
|
|
||||||
|
|
||||||
// This configuration is stored inside the compiled unit and is loaded statically
|
|
||||||
loadJsonFile(":DefaultStyle.json");
|
|
||||||
}
|
|
||||||
|
|
||||||
GraphicsViewStyle::GraphicsViewStyle(QString jsonText)
|
|
||||||
{
|
|
||||||
loadJsonText(jsonText);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsViewStyle::setStyle(QString jsonText)
|
|
||||||
{
|
|
||||||
GraphicsViewStyle style(jsonText);
|
|
||||||
|
|
||||||
StyleCollection::setGraphicsViewStyle(style);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef STYLE_DEBUG
|
|
||||||
#define FLOW_VIEW_STYLE_CHECK_UNDEFINED_VALUE(v, variable) \
|
|
||||||
{ \
|
|
||||||
if (v.type() == QJsonValue::Undefined || v.type() == QJsonValue::Null) \
|
|
||||||
qWarning() << "Undefined value for parameter:" << #variable; \
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define FLOW_VIEW_STYLE_CHECK_UNDEFINED_VALUE(v, variable)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FLOW_VIEW_STYLE_READ_COLOR(values, variable) \
|
|
||||||
{ \
|
|
||||||
auto valueRef = values[#variable]; \
|
|
||||||
FLOW_VIEW_STYLE_CHECK_UNDEFINED_VALUE(valueRef, variable) \
|
|
||||||
if (valueRef.isArray()) { \
|
|
||||||
auto colorArray = valueRef.toArray(); \
|
|
||||||
std::vector<int> rgb; \
|
|
||||||
rgb.reserve(3); \
|
|
||||||
for (auto it = colorArray.begin(); it != colorArray.end(); ++it) { \
|
|
||||||
rgb.push_back((*it).toInt()); \
|
|
||||||
} \
|
|
||||||
variable = QColor(rgb[0], rgb[1], rgb[2]); \
|
|
||||||
} else { \
|
|
||||||
variable = QColor(valueRef.toString()); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define FLOW_VIEW_STYLE_WRITE_COLOR(values, variable) \
|
|
||||||
{ \
|
|
||||||
values[#variable] = variable.name(); \
|
|
||||||
}
|
|
||||||
|
|
||||||
void GraphicsViewStyle::loadJson(QJsonObject const &json)
|
|
||||||
{
|
|
||||||
QJsonValue nodeStyleValues = json["GraphicsViewStyle"];
|
|
||||||
|
|
||||||
QJsonObject obj = nodeStyleValues.toObject();
|
|
||||||
|
|
||||||
FLOW_VIEW_STYLE_READ_COLOR(obj, BackgroundColor);
|
|
||||||
FLOW_VIEW_STYLE_READ_COLOR(obj, FineGridColor);
|
|
||||||
FLOW_VIEW_STYLE_READ_COLOR(obj, CoarseGridColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject GraphicsViewStyle::toJson() const
|
|
||||||
{
|
|
||||||
QJsonObject obj;
|
|
||||||
|
|
||||||
FLOW_VIEW_STYLE_WRITE_COLOR(obj, BackgroundColor);
|
|
||||||
FLOW_VIEW_STYLE_WRITE_COLOR(obj, FineGridColor);
|
|
||||||
FLOW_VIEW_STYLE_WRITE_COLOR(obj, CoarseGridColor);
|
|
||||||
|
|
||||||
QJsonObject root;
|
|
||||||
root["GraphicsViewStyle"] = obj;
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
@ -1,152 +0,0 @@
|
||||||
#include "NodeConnectionInteraction.hpp"
|
|
||||||
|
|
||||||
#include "AbstractNodeGeometry.hpp"
|
|
||||||
#include "BasicGraphicsScene.hpp"
|
|
||||||
#include "ConnectionGraphicsObject.hpp"
|
|
||||||
#include "ConnectionIdUtils.hpp"
|
|
||||||
#include "NodeGraphicsObject.hpp"
|
|
||||||
#include "UndoCommands.hpp"
|
|
||||||
|
|
||||||
#include <QtCore/QDebug>
|
|
||||||
|
|
||||||
#include <QUndoStack>
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
NodeConnectionInteraction::NodeConnectionInteraction(NodeGraphicsObject &ngo,
|
|
||||||
ConnectionGraphicsObject &cgo,
|
|
||||||
BasicGraphicsScene &scene)
|
|
||||||
: _ngo(ngo)
|
|
||||||
, _cgo(cgo)
|
|
||||||
, _scene(scene)
|
|
||||||
{}
|
|
||||||
|
|
||||||
bool NodeConnectionInteraction::canConnect(PortIndex *portIndex) const
|
|
||||||
{
|
|
||||||
// 1. Connection requires a port.
|
|
||||||
|
|
||||||
PortType requiredPort = _cgo.connectionState().requiredPort();
|
|
||||||
|
|
||||||
if (requiredPort == PortType::None) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeId connectedNodeId = getNodeId(oppositePort(requiredPort), _cgo.connectionId());
|
|
||||||
|
|
||||||
// 2. Forbid connecting the node to itself.
|
|
||||||
|
|
||||||
if (_ngo.nodeId() == connectedNodeId)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 3. Connection loose end is above the node port.
|
|
||||||
|
|
||||||
QPointF connectionPoint = _cgo.sceneTransform().map(_cgo.endPoint(requiredPort));
|
|
||||||
|
|
||||||
*portIndex = nodePortIndexUnderScenePoint(requiredPort, connectionPoint);
|
|
||||||
|
|
||||||
if (*portIndex == InvalidPortIndex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Model allows connection.
|
|
||||||
|
|
||||||
AbstractGraphModel &model = _ngo.nodeScene()->graphModel();
|
|
||||||
|
|
||||||
ConnectionId connectionId = makeCompleteConnectionId(_cgo.connectionId(), // incomplete
|
|
||||||
_ngo.nodeId(), // missing node id
|
|
||||||
*portIndex); // missing port index
|
|
||||||
|
|
||||||
return model.connectionPossible(connectionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodeConnectionInteraction::tryConnect() const
|
|
||||||
{
|
|
||||||
// 1. Check conditions from 'canConnect'.
|
|
||||||
|
|
||||||
PortIndex targetPortIndex = InvalidPortIndex;
|
|
||||||
if (!canConnect(&targetPortIndex)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Create new connection.
|
|
||||||
|
|
||||||
ConnectionId incompleteConnectionId = _cgo.connectionId();
|
|
||||||
|
|
||||||
ConnectionId newConnectionId = makeCompleteConnectionId(incompleteConnectionId,
|
|
||||||
_ngo.nodeId(),
|
|
||||||
targetPortIndex);
|
|
||||||
|
|
||||||
_ngo.nodeScene()->resetDraftConnection();
|
|
||||||
|
|
||||||
_ngo.nodeScene()->undoStack().push(new ConnectCommand(_ngo.nodeScene(), newConnectionId));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NodeConnectionInteraction::disconnect(PortType portToDisconnect) const
|
|
||||||
{
|
|
||||||
ConnectionId connectionId = _cgo.connectionId();
|
|
||||||
|
|
||||||
_scene.undoStack().push(new DisconnectCommand(&_scene, connectionId));
|
|
||||||
|
|
||||||
AbstractNodeGeometry &geometry = _scene.nodeGeometry();
|
|
||||||
|
|
||||||
QPointF scenePos = geometry.portScenePosition(_ngo.nodeId(),
|
|
||||||
portToDisconnect,
|
|
||||||
getPortIndex(portToDisconnect, connectionId),
|
|
||||||
_ngo.sceneTransform());
|
|
||||||
|
|
||||||
// Converted to "draft" connection with the new incomplete id.
|
|
||||||
ConnectionId incompleteConnectionId = makeIncompleteConnectionId(connectionId, portToDisconnect);
|
|
||||||
|
|
||||||
// Grabs the mouse
|
|
||||||
auto const &draftConnection = _scene.makeDraftConnection(incompleteConnectionId);
|
|
||||||
|
|
||||||
QPointF const looseEndPos = draftConnection->mapFromScene(scenePos);
|
|
||||||
draftConnection->setEndPoint(portToDisconnect, looseEndPos);
|
|
||||||
|
|
||||||
// Repaint connection points.
|
|
||||||
NodeId connectedNodeId = getNodeId(oppositePort(portToDisconnect), connectionId);
|
|
||||||
_scene.nodeGraphicsObject(connectedNodeId)->update();
|
|
||||||
|
|
||||||
NodeId disconnectedNodeId = getNodeId(portToDisconnect, connectionId);
|
|
||||||
_scene.nodeGraphicsObject(disconnectedNodeId)->update();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------ util functions below
|
|
||||||
|
|
||||||
PortType NodeConnectionInteraction::connectionRequiredPort() const
|
|
||||||
{
|
|
||||||
auto const &state = _cgo.connectionState();
|
|
||||||
|
|
||||||
return state.requiredPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
QPointF NodeConnectionInteraction::nodePortScenePosition(PortType portType,
|
|
||||||
PortIndex portIndex) const
|
|
||||||
{
|
|
||||||
AbstractNodeGeometry &geometry = _scene.nodeGeometry();
|
|
||||||
|
|
||||||
QPointF p = geometry.portScenePosition(_ngo.nodeId(),
|
|
||||||
portType,
|
|
||||||
portIndex,
|
|
||||||
_ngo.sceneTransform());
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
PortIndex NodeConnectionInteraction::nodePortIndexUnderScenePoint(PortType portType,
|
|
||||||
QPointF const &scenePoint) const
|
|
||||||
{
|
|
||||||
AbstractNodeGeometry &geometry = _scene.nodeGeometry();
|
|
||||||
|
|
||||||
QTransform sceneTransform = _ngo.sceneTransform();
|
|
||||||
|
|
||||||
QPointF nodePoint = sceneTransform.inverted().map(scenePoint);
|
|
||||||
|
|
||||||
return geometry.checkPortHit(_ngo.nodeId(), portType, nodePoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
#include "NodeDelegateModel.hpp"
|
|
||||||
|
|
||||||
#include "StyleCollection.hpp"
|
|
||||||
|
|
||||||
namespace QtNodes {
|
|
||||||
|
|
||||||
NodeDelegateModel::NodeDelegateModel()
|
|
||||||
: _nodeStyle(StyleCollection::nodeStyle())
|
|
||||||
{
|
|
||||||
// Derived classes can initialize specific style here
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject NodeDelegateModel::save() const
|
|
||||||
{
|
|
||||||
QJsonObject modelJson;
|
|
||||||
|
|
||||||
modelJson["model-name"] = name();
|
|
||||||
|
|
||||||
return modelJson;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeDelegateModel::load(QJsonObject const &)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionPolicy NodeDelegateModel::portConnectionPolicy(PortType portType, PortIndex) const
|
|
||||||
{
|
|
||||||
auto result = ConnectionPolicy::One;
|
|
||||||
switch (portType) {
|
|
||||||
case PortType::In:
|
|
||||||
result = ConnectionPolicy::One;
|
|
||||||
break;
|
|
||||||
case PortType::Out:
|
|
||||||
result = ConnectionPolicy::Many;
|
|
||||||
break;
|
|
||||||
case PortType::None:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeStyle const &NodeDelegateModel::nodeStyle() const
|
|
||||||
{
|
|
||||||
return _nodeStyle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NodeDelegateModel::setNodeStyle(NodeStyle const &style)
|
|
||||||
{
|
|
||||||
_nodeStyle = style;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace QtNodes
|
|
||||||