├── .clang-format ├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Example ├── CMakeLists.txt ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h └── mainwindow.ui ├── LICENSE.txt ├── NOTICE.txt ├── NodeView ├── NodeView_global.h ├── connection.h ├── expandablecontainer.h ├── fulllinesocket.h ├── minimapdraglabel.h ├── node.h ├── nodegroup.h ├── nodeview.h ├── nodeviewminimap.h ├── sidesocketnode.h ├── simplenode.h ├── socket.h ├── stylemanager.h ├── textlesssocket.h ├── trianglesocketshape.h └── widgetsocket.h ├── README.md ├── build.py ├── conanfile.py ├── resources ├── Minus_16.png ├── Minus_9.png ├── Plus_16.png ├── Plus_9.png └── images.qrc └── src ├── connection.cpp ├── expandablecontainer.cpp ├── fullLinesocket.cpp ├── minimapdraglabel.cpp ├── node.cpp ├── nodegroup.cpp ├── nodeview.cpp ├── nodeviewminimap.cpp ├── sidesocketnode.cpp ├── simplenode.cpp ├── socket.cpp ├── stylemanager.cpp ├── textlesssocket.cpp ├── trianglesocketshape.cpp └── widgetsocket.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | Standard: c++17 3 | UseTab: Never 4 | ColumnLimit: 120 5 | IndentWidth: 4 6 | BreakBeforeBraces: Attach 7 | NamespaceIndentation: None 8 | AlwaysBreakTemplateDeclarations: true 9 | SpacesInParentheses: true 10 | SpaceBeforeParens: Never 11 | BreakConstructorInitializersBeforeComma: true 12 | PointerAlignment: Left 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/moc_* 2 | build 3 | /Example/moc_mainwindow.cpp 4 | /Example/ui_mainwindow.h 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | cmake_minimum_required( VERSION 3.20 FATAL_ERROR ) 4 | 5 | project( NodeView ) 6 | 7 | find_package( thinkboxcmlibrary REQUIRED ) 8 | include( ThinkboxCMLibrary ) 9 | 10 | set( SUPPORTED_MAYA_VERSIONS 2022 2023 ) 11 | 12 | option( MAYA_VERSION "The version of QtMaya to build the library against." 2022 ) 13 | option( BUILD_EXAMPLES "Build the example NodeView project" OFF ) 14 | 15 | if( NOT MAYA_VERSION IN_LIST SUPPORTED_MAYA_VERSIONS ) 16 | message( FATAL_ERROR "ERROR: Cannot build for unsupported Maya version ${MAYA_VERSION}" ) 17 | endif() 18 | 19 | set( CMAKE_AUTOMOC ON ) 20 | set( CMAKE_AUTORCC ON ) 21 | set( CMAKE_AUTOUIC ON ) 22 | 23 | add_library( nodeview STATIC ) 24 | 25 | target_compile_definitions( nodeview PUBLIC FRANTIC_BUILD_FOR_MAYA ) 26 | 27 | file( GLOB_RECURSE H_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} 28 | "NodeView/*.h" 29 | "NodeView/*.hpp" 30 | ) 31 | 32 | file( GLOB_RECURSE CXX_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} 33 | "src/*.cpp" 34 | ) 35 | 36 | target_sources( nodeview PRIVATE ${H_FILES} ${CXX_FILES} "resources/images.qrc" ) 37 | 38 | 39 | target_include_directories( nodeview PUBLIC 40 | $ 41 | $ ) 42 | 43 | find_package( Qt5 COMPONENTS Core Gui Widgets REQUIRED ) 44 | 45 | target_include_directories( nodeview PUBLIC ${Qt5_INCLUDE_DIRS} ) 46 | 47 | target_link_libraries( nodeview INTERFACE Qt5::Core ) 48 | target_link_libraries( nodeview INTERFACE Qt5::Gui ) 49 | target_link_libraries( nodeview INTERFACE Qt5::Widgets ) 50 | 51 | frantic_common_platform_setup( nodeview ) 52 | frantic_default_source_groups( nodeview HEADERDIR NodeView SOURCEDIR src ) 53 | 54 | # Disable optimization for the RelWithDebInfo configuration on Windows. 55 | # This allows breakpoints to be hit reliably when debugging in Visual Studio. 56 | if( WIN32 ) 57 | target_compile_options( nodeview PRIVATE "$<$:/O2>$<$:/Od>" ) 58 | endif() 59 | 60 | install( DIRECTORY NodeView 61 | DESTINATION include 62 | FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" 63 | ) 64 | install( TARGETS nodeview 65 | RUNTIME DESTINATION bin 66 | LIBRARY DESTINATION lib 67 | ARCHIVE DESTINATION lib 68 | ) 69 | 70 | if( BUILD_EXAMPLES ) 71 | add_subdirectory( Example ) 72 | endif() 73 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to NodeView 2 | 3 | Thanks for your interest in contributing to NodeView! ❤️ 4 | 5 | This document describes how to set up a development environment and submit your contributions. Please read it carefully 6 | and let us know if it's not up-to-date (even better, submit a PR with your corrections ;-)). 7 | 8 | - [Prerequisites](#prerequisites) 9 | - [Building](#building) 10 | - [Testing](#testing) 11 | - [Publishing to Local Conan Cache](#publishing-to-local-conan-cache) 12 | - [Building Multiple Configurations](#building-multiple-configurations) 13 | - [Pull Requests](#pull-requests) 14 | - [Pull Request Checklist](#pull-request-checklist) 15 | - [Step 1: Open Issue](#step-1-open-issue) 16 | - [Step 2: Design (optional)](#step-2-design-optional) 17 | - [Step 3: Work your Magic](#step-3-work-your-magic) 18 | - [Step 4: Commit](#step-4-commit) 19 | - [Step 5: Pull Request](#step-5-pull-request) 20 | - [Step 6: Merge](#step-6-merge) 21 | 22 | ## Prerequisites 23 | 24 | You will need Python 3.10 or later and the C++ compiler for your platform installed before you are able to build NodeView. The compilers used for each platform are as follows: 25 | 26 | * Windows: Visual C++ 14.1 (Visual Studio 2017) or later 27 | * macOS: Clang 10.0 or later 28 | * Linux: GCC 7 or later 29 | 30 | You will then need to install Conan. You can do this by running: 31 | 32 | ```bash 33 | pip install conan conan_package_tools 34 | ``` 35 | 36 | Conan is a C++ package manager that is used to install the 3rd party dependencies. 37 | 38 | NodeView uses the C++17 standard. 39 | 40 | You will need to build the following dependencies to your local Conan cache: 41 | 42 | * https://github.com/aws/thinkbox-cm-library 43 | 44 | ## Building 45 | 46 | From the project root directory run the following commands to install the dependencies and build NodeView: 47 | 48 | ```bash 49 | conan install . --install-folder build 50 | conan build . --build-folder build 51 | ``` 52 | 53 | If you wish to generate a development environment without building the package immediately, you can add `--configure` to the `conan build` command. 54 | 55 | If you are using Windows, once run, you can open `build/NodeView.sln` in Visual Studio to use Visual Studio for development and debugging. 56 | 57 | 58 | ### Testing 59 | 60 | You can run the project's unit tests after [building](#building) the project by running the following command from the project root directory on Windows: 61 | 62 | ```bash 63 | build/UnitTests/Release/test_thinkboxlibrary.exe 64 | ``` 65 | 66 | ### Publishing to Local Conan Cache 67 | 68 | If you need to publish build artifacts to your local Conan cache manually, after completing the [building](#building) steps, you can run the following commands to package NodeView and publish it to your local Conan cache: 69 | 70 | ```bash 71 | conan package . --install-folder build --build-folder build --package-folder build/package 72 | conan export-pkg . --package-folder build/package 73 | ``` 74 | 75 | ### Building Multiple Configurations 76 | 77 | To quickly build all supported configurations on the current platform you can run: 78 | 79 | ``` 80 | python build.py 81 | ``` 82 | 83 | This will build the configurations and publish them to your local conan cache. You can add the `--dry-run` flag to preview the configurations that will be built without building them. 84 | 85 | ### Pull Requests 86 | 87 | #### Pull Request Checklist 88 | 89 | - Testing 90 | - Unit test added (prefer not to modify an existing test, otherwise, it's probably a breaking change) 91 | - Title and Description 92 | - __Change type__: title prefixed with **fix**, **feat** and module name in parens, which will appear in changelog 93 | - __Title__: use lower-case and doesn't end with a period 94 | - __Breaking?__: last paragraph: "BREAKING CHANGE: " 95 | - __Issues__: Indicate issues fixed via: "**Fixes #xxx**" or "**Closes #xxx**" 96 | 97 | #### Step 1: Open Issue 98 | 99 | If there isn't one already, open an issue describing what you intend to contribute. It's useful to communicate in 100 | advance, because sometimes, someone is already working in this space, so maybe it's worth collaborating with them 101 | instead of duplicating the efforts. 102 | 103 | #### Step 2: Design (optional) 104 | 105 | In some cases, it is useful to seek for feedback by iterating on a design document. This is useful 106 | when you plan a big change or feature, or you want advice on what would be the best path forward. 107 | 108 | Sometimes, the GitHub issue is sufficient for such discussions, and can be sufficient to get 109 | clarity on what you plan to do. Sometimes, a design document would work better, so people can provide 110 | iterative feedback. 111 | 112 | In such cases, use the GitHub issue description to collect **requirements** and 113 | **use cases** for your feature. 114 | 115 | #### Step 3: Work your Magic 116 | 117 | Work your magic. Here are some guidelines: 118 | 119 | - Coding style: 120 | - Code should conform to the style defined in .clang-format 121 | - Every change requires a unit test 122 | - Try to maintain a single feature/bugfix per pull request. It's okay to introduce a little bit of housekeeping 123 | changes along the way, but try to avoid conflating multiple features. Eventually all these are going to go into a 124 | single commit, so you can use that to frame your scope. 125 | 126 | #### Step 4: Commit 127 | 128 | Create a commit with the proposed changes: 129 | 130 | - Commit title and message (and PR title and description) must adhere to [conventionalcommits](https://www.conventionalcommits.org). 131 | - The title must begin with `feat: title`, `fix: title`, `refactor: title` or 132 | `chore: title`. 133 | - Title should be lowercase. 134 | - No period at the end of the title. 135 | 136 | - Commit message should describe _motivation_. Think about your code reviewers and what information they need in 137 | order to understand what you did. If it's a big commit (hopefully not), try to provide some good entry points so 138 | it will be easier to follow. 139 | 140 | - Commit message should indicate which issues are fixed: `fixes #` or `closes #`. 141 | 142 | - Shout out to collaborators. 143 | 144 | - If not obvious (i.e. from unit tests), describe how you verified that your change works. 145 | 146 | - If this commit includes breaking changes, they must be listed at the end in the following format (notice how multiple breaking changes should be formatted): 147 | 148 | ``` 149 | BREAKING CHANGE: Description of what broke and how to achieve this behavior now 150 | - **module-name:** Another breaking change 151 | - **module-name:** Yet another breaking change 152 | ``` 153 | 154 | #### Step 5: Pull Request 155 | 156 | - Push to a personal GitHub fork. 157 | - Submit a Pull Request on GitHub. A reviewer will later be assigned by the maintainers. 158 | - Please follow the PR checklist written above. We trust our contributors to self-check, and this helps that process! 159 | - Discuss review comments and iterate until you get at least one "Approve". When iterating, push new commits to the 160 | same branch. Usually all these are going to be squashed when you merge to master. The commit messages should be hints 161 | for you when you finalize your merge commit message. 162 | - Make sure to update the PR title/description if things change. The PR title/description are going to be used as the 163 | commit title/message and will appear in the CHANGELOG, so maintain them all the way throughout the process. 164 | 165 | #### Step 6: Merge 166 | 167 | - Make sure your PR builds successfully 168 | - Once approved and tested, a maintainer will squash-merge to master and will use your PR title/description as the 169 | commit message. 170 | -------------------------------------------------------------------------------- /Example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | cmake_minimum_required( VERSION 3.20 FATAL_ERROR ) 4 | 5 | project( NodeViewExample ) 6 | 7 | find_package( thinkboxcmlibrary REQUIRED ) 8 | include( ThinkboxCMLibrary ) 9 | 10 | set( CMAKE_AUTOMOC ON ) 11 | set( CMAKE_AUTORCC ON ) 12 | set( CMAKE_AUTOUIC ON ) 13 | 14 | set( H_FILES mainwindow.h ) 15 | set( CXX_FILES main.cpp mainwindow.cpp ) 16 | 17 | add_executable( nodeviewexample ) 18 | target_sources( nodeviewexample PRIVATE ${H_FILES} ${CXX_FILES} ${MOC_SOURCE_FILES} ${UI_HEADER_FILES} mainwindow.ui ) 19 | 20 | find_package( Qt5 COMPONENTS Core Gui Widgets REQUIRED ) 21 | 22 | target_include_directories( nodeviewexample PUBLIC ${Qt5_INCLUDE_DIRS} ) 23 | 24 | target_link_libraries( nodeviewexample INTERFACE Qt5::Core ) 25 | target_link_libraries( nodeviewexample INTERFACE Qt5::Gui ) 26 | target_link_libraries( nodeviewexample INTERFACE Qt5::Widgets ) 27 | 28 | target_link_libraries( nodeviewexample PRIVATE nodeview ) 29 | 30 | set_target_properties( nodeviewexample PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${NodeView_BINARY_DIR} ) 31 | 32 | install( TARGETS nodeviewexample 33 | RUNTIME DESTINATION bin 34 | LIBRARY DESTINATION lib 35 | ARCHIVE DESTINATION lib ) 36 | -------------------------------------------------------------------------------- /Example/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "mainwindow.h" 4 | #include 5 | 6 | int main( int argc, char* argv[] ) { 7 | QApplication::setStyle( "plastique" ); 8 | QApplication::setPalette( QPalette( QColor( 64, 64, 64 ) ) ); 9 | QApplication a( argc, argv ); 10 | MainWindow w; 11 | w.show(); 12 | 13 | // w.pan(-1000,0); 14 | 15 | return a.exec(); 16 | } 17 | -------------------------------------------------------------------------------- /Example/mainwindow.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #ifndef MAINWINDOW_H 4 | #define MAINWINDOW_H 5 | 6 | #include 7 | 8 | namespace Ui { 9 | class MainWindow; 10 | } 11 | 12 | class Node; 13 | class Socket; 14 | class Connection; 15 | class QListWidgetItem; 16 | 17 | class MainWindow : public QMainWindow { 18 | Q_OBJECT 19 | 20 | public: 21 | explicit MainWindow( QWidget* parent = 0 ); 22 | ~MainWindow(); 23 | 24 | private slots: 25 | 26 | void onChangePixmap(); 27 | void onAddNode(); 28 | 29 | void onAddSideNode(); 30 | void onAddSimpleNode(); 31 | 32 | void onAddInputSocketBtnClicked(); 33 | 34 | void onAddOutputSocketBtnClicked(); 35 | 36 | void onAddFullLineOutputSocketBtnClicked(); 37 | 38 | void onAddFullLineInputSocketBtnClicked(); 39 | 40 | void onChangeOutlineColorBtnClicked(); 41 | 42 | void onChangeFillColorBtnClicked(); 43 | 44 | void onChangeTextColorBtnClicked(); 45 | 46 | void onChangeBackgroundColorBtnClicked(); 47 | 48 | void onChangeForegroundColorBtnClicked(); 49 | 50 | void onChangeBorderColorBtnClicked(); 51 | 52 | void onChangeDetailFontBtnClicked(); 53 | 54 | void onChangeDetailColorBtnClicked(); 55 | 56 | void onChangeBorderColorHighlightBtnClicked(); 57 | 58 | void onChangeBackgroundHighlightColorBtnClicked(); 59 | 60 | void onChangeForegroundHighlightColorBtnClicked(); 61 | 62 | void onChangeMagmaBackgroundColorBtnClicked(); 63 | 64 | void onDropShadowBoxToggled( bool checked ); 65 | 66 | void onChangeGridColorBtnClicked(); 67 | 68 | void onConnectionCreated( Socket* sock ); 69 | 70 | void onDeleteInputSocketBtnClicked(); 71 | 72 | void onDeleteOutputSocketBtnClicked(); 73 | 74 | void onGridLinesBoxToggled( bool checked ); 75 | 76 | void onNodeAddedOrDeleted(); 77 | 78 | void onNodeNameEditReturnPressed(); 79 | 80 | void onDetailTextEditTextChanged(); 81 | 82 | void onEnableDetailText( bool enabled ); 83 | 84 | void onNodeSelected(); 85 | 86 | void onConnectionSelected(); 87 | 88 | void onSelectionChanged(); 89 | 90 | void onConnectionAnimationEnabledChanged(); 91 | 92 | void onConnectionAnimationDurationChanged(); 93 | 94 | void onConnectionAnimationLightnessChanged(); 95 | 96 | void onConnectionArrowPositionChanged(); 97 | 98 | void onConnectionArrowSizeChanged(); 99 | 100 | void onConnectionCurvatureChanged(); 101 | 102 | void onConnectionDragDistanceChanged(); 103 | 104 | void onConnectionChangeColor(); 105 | 106 | void onConnectionBrushChanged(); 107 | 108 | void onConnectionPenChanged(); 109 | 110 | void onConnectionSelectedColorChanged(); 111 | 112 | void onConnectionSelectedBrushChanged(); 113 | 114 | void onConnectionSelectedPenChanged(); 115 | 116 | void onScroll(); 117 | 118 | void onSnapToGridBoxToggled( bool checked ); 119 | 120 | void onCreationPointBoxToggled( bool checked ); 121 | 122 | void onSocketNameEditReturnPressed(); 123 | 124 | void onSocketItemClicked( QListWidgetItem* item ); 125 | 126 | void onSocketTypeToggled( bool checked ); 127 | 128 | void onMoveMiniButtonClicked(); 129 | 130 | void onZoomChanged(); 131 | 132 | void onGridSizeChanged(); 133 | 134 | void onRadiusXChanged(); 135 | 136 | void onRadiusYChanged(); 137 | 138 | void onEnablePixmapButtonToggled( bool checked ); 139 | 140 | void onPixmapPosToggled( bool checked ); 141 | 142 | void onSetPixmapSize(); 143 | 144 | void onMinWidthChanged(); 145 | 146 | void onChangeSocketBuffer(); 147 | 148 | void onSocketShapeSizeChanged( int value ); 149 | 150 | void onDeleteAll(); 151 | 152 | void onJustifyChanged( bool checked ); 153 | 154 | void onElideChanged( bool checked ); 155 | 156 | void printCheck(); 157 | 158 | void minimapChanged( bool checked ); 159 | 160 | void onChangeTitleTextColor(); 161 | 162 | void onChangeTitleTextFont(); 163 | 164 | void onSocketAnimtionEnabledChanged(); 165 | 166 | void onSocketDurationChanged(); 167 | 168 | void onSocketLightnessChanged(); 169 | 170 | void onMultipleConnectionsChanged(); 171 | 172 | void onChangeTriangleOrientation( bool checked ); 173 | 174 | void onStyleSheetChanged(); 175 | 176 | void onDetailTitleTextChanged(); 177 | 178 | void onDetailTitleFontChanged(); 179 | 180 | void onDetailTitleLocationChanged( bool checked ); 181 | 182 | void onExpansionButtonLocationChanged( bool checked ); 183 | 184 | void onDetailTitleColorChanged(); 185 | 186 | void onNodeObjectNameChanged(); 187 | 188 | void onSocketObjectNameChanged(); 189 | 190 | void onConnectionObjectNameChanged(); 191 | 192 | void onSocketAlignmentChanged( bool checked ); 193 | 194 | void onDragEnabledChanged( bool checked ); 195 | 196 | void onLabelStyleChanged( bool checked ); 197 | 198 | int randInt( int low, int high ); 199 | 200 | void NodeCountTest( int Count, int Radius ); 201 | 202 | void OnPerformNodeCountTest(); 203 | 204 | void ConnectionTest( int Count, int Radius ); 205 | 206 | void OnPerformConnectionTest(); 207 | 208 | void MeshTest( int Count1, int Count2, int Radius ); 209 | 210 | void OnPerformMeshTest(); 211 | 212 | private: 213 | Ui::MainWindow* ui; 214 | Node* m_selectedNode; 215 | Socket* m_selectedSocket; 216 | Connection* m_selectedConnection; 217 | }; 218 | 219 | #endif // MAINWINDOW_H 220 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | NodeView 2 | Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | -------------------------------------------------------------------------------- /NodeView/NodeView_global.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #if defined( FRANTIC_BUILD_FOR_MAYA ) 6 | #include 7 | #else 8 | #include 9 | #endif 10 | 11 | #ifndef FRANTIC_BUILD_FOR_MAYA 12 | #if defined( NodeView_EXPORTS ) 13 | #define NodeView_API Q_DECL_EXPORT 14 | #else 15 | #define NodeView_API Q_DECL_IMPORT 16 | #endif 17 | #else 18 | // Maya build is static 19 | #define NodeView_API 20 | #endif 21 | -------------------------------------------------------------------------------- /NodeView/connection.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/NodeView_global.h" 6 | #include "NodeView/stylemanager.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | class Socket; 13 | class NodeView; 14 | 15 | class QPainterPath; 16 | class QPropertyAnimation; 17 | 18 | /*! 19 | * \brief The Connection class is used to join two [Nodes](\ref Node) together within the NodeView view. To change the 20 | * color and thickness of the Connection, as well as other properties you need to use the function setPen(QPen). 21 | */ 22 | class NodeView_API Connection : public QObject, public QGraphicsPathItem, public Styleable { 23 | Q_OBJECT 24 | 25 | Q_PROPERTY( int animationDuration READ animationDuration WRITE setAnimationDuration DESIGNABLE true ) 26 | Q_PROPERTY( int animationLightness READ animationLightness WRITE setAnimationLightness DESIGNABLE true ) 27 | Q_PROPERTY( QBrush brush READ brush WRITE setBrush DESIGNABLE true ) 28 | Q_PROPERTY( QColor color READ color WRITE setColor DESIGNABLE true ) 29 | Q_PROPERTY( QPen pen READ pen WRITE setPen DESIGNABLE true ) 30 | Q_PROPERTY( QBrush selectedBrush READ selectedBrush WRITE setSelectedBrush DESIGNABLE true ) 31 | Q_PROPERTY( QPen selectedPen READ selectedPen WRITE setSelectedPen DESIGNABLE true ) 32 | Q_PROPERTY( qreal arrowPositionPercent READ arrowPositionPercent WRITE setArrowPositionPercent DESIGNABLE true ) 33 | Q_PROPERTY( qreal arrowSize READ arrowSize WRITE setArrowSize DESIGNABLE true ) 34 | Q_PROPERTY( qreal curvature READ curvature WRITE setCurvature DESIGNABLE true ) 35 | 36 | private: 37 | /*! 38 | * \brief Calculates the path that the Connection will take. This is used by the drawing functions. 39 | * \return 40 | */ 41 | void calculatePath(); 42 | 43 | Socket* m_source; /*!< The source Socket. */ 44 | Socket* m_dest; /*!< The destination Socket. */ 45 | NodeView* m_graphWidget; 46 | 47 | QPropertyAnimation* m_animation; 48 | 49 | QPointF m_dragPoint; /*!< The dragging point of the Connection. */ 50 | QPainterPath m_path; /*!< The path object that describes the Connection shape. */ 51 | QPen m_selectedPen; 52 | QBrush m_selectedBrush; 53 | 54 | qreal m_arrowPosition; 55 | qreal m_arrowSize; 56 | qreal m_curvature; 57 | qreal m_dragDistance; 58 | 59 | bool m_animationEnabled; 60 | int m_animationDuration; 61 | int m_animationLightness; 62 | 63 | QPointF m_prevStart; 64 | QPointF m_prevEnd; 65 | bool m_forceRecalc; 66 | 67 | static int NextZConnection; 68 | 69 | public: 70 | /*! 71 | * \brief Definition of type ID for Conneciton objects. All QGrpahicsItems need a type ID. 72 | */ 73 | enum { Type = UserType + 3 }; 74 | 75 | /*! 76 | * \brief Constructor 77 | * \param parent The parent QGraphicsItem. 78 | * \param scene The QGraphicsScene that the Connection resides in. 79 | */ 80 | Connection( NodeView* graphWidget, QGraphicsItem* parent = 0, QGraphicsScene* scene = 0 ); 81 | 82 | /*! 83 | * \brief Deconstructor 84 | */ 85 | virtual ~Connection(); 86 | 87 | /*! 88 | * \brief Returns the amount of time in miliseconds that the animation will run for. 89 | * \return Int that is the number of miliseconds the animation will run for. 90 | */ 91 | int animationDuration() const; 92 | 93 | /*! 94 | * \brief Returns true if animation is currently enabled. 95 | * \return Bool which states if animation is enabled for 96 | */ 97 | bool animationEnabled() const; 98 | 99 | /*! 100 | * \brief Returns the level that the animation will lighten the color of the Connection. 101 | * \return Int the is the level of lightness the color will change too. Default is 175. 102 | */ 103 | int animationLightness() const; 104 | 105 | /*! 106 | * \brief Returns the point in the Connection line that the arrow should appear. This will be a value between 0 107 | * and 1. \return The qreal that contains the location of the arrow. 108 | */ 109 | qreal arrowPositionPercent() const; 110 | 111 | /*! 112 | * \brief Returns the size of the arrow. 113 | * \return The qreal that represents the current size of the arrow. 114 | */ 115 | qreal arrowSize() const; 116 | 117 | /*! 118 | * \brief Returns the color of the Connection. 119 | * \return QColor that the Connection is set too. 120 | */ 121 | QColor color() const; 122 | 123 | /*! 124 | * \brief Returns the currently set cruvature for the Connection. A value of 0 means that the line has no curvature 125 | * (a straight line); Anything else and there will be a curve in the line. \return the qreal that represents the 126 | * current level of curvature in the Connection. 127 | */ 128 | qreal curvature() const; 129 | 130 | /*! 131 | * \brief Returns the currently set drag distance. The drag distance is how far away the mouse has to move from the 132 | * Connection before a drag event is fired. 133 | * \return The currently set drag distance as a qreal. 134 | */ 135 | qreal dragDistance() const; 136 | 137 | /*! 138 | * \brief The destination Socket of the Connection. 139 | * \return The Socket object that is the destination of the Connection. 140 | */ 141 | Socket* destinationSocket() const; 142 | 143 | /*! 144 | * \brief Removes the destination Socket from this Connection and sets its drag point for drawing. This is used when 145 | * pulling a currently connected Connection off of a Socket. 146 | * \param dragPoint QPointF that you want the drag point to be for drawing. 147 | */ 148 | void removeDestinationSocket( const QPointF& dragPoint ); 149 | 150 | /*! 151 | * \brief Removes the source Socket from this Connection and sets its drag point for drawing. This is used when 152 | * pulling a currently connected Connection off of a Socket. 153 | * \param dragPoint QPointF that you want the drag point to be for drawing. 154 | */ 155 | void removeSourceSocket( const QPointF& dragPoint ); 156 | 157 | static void resetZDepth() { NextZConnection = INT_MIN; } 158 | 159 | /*! 160 | * \brief Returns the brush that will be used to draw the arrow when the Connection is selected. 161 | * \return The QBrush that will be returned. 162 | */ 163 | QBrush selectedBrush() const; 164 | 165 | /*! 166 | * \brief Returns the color of the Connection when selected. 167 | * \return QColor that the Connection is set too. 168 | */ 169 | QColor selectedColor() const; 170 | 171 | /*! 172 | * \brief Returns the pen that will be used to draw the Connection when it is selected. 173 | * \return The QPen that will be returned. 174 | */ 175 | QPen selectedPen() const; 176 | 177 | /*! 178 | * \brief Sets the animation duration. 179 | * \param duration int in miliseconds that you want the animation duration to be. 180 | */ 181 | void setAnimationDuration( int duration ); 182 | 183 | /*! 184 | * \brief Sets if the animation is to be run 185 | * \param bool reperesenting if you want the animation to run 186 | * \note may give incorrect results if changed while animation is running 187 | */ 188 | void setAnimationEnabled( bool enabled ); 189 | 190 | /*! 191 | * \brief Sets the animtion lightness. A value of 100 will keep it the same. A valye greater than 100 will cause the 192 | * color to become lighter. A value less than 100 but greater thn 0 will make the color darker. Values less than 0 193 | * will cause undefined results. 194 | * \param lightness int that will change the color of the animation. 195 | */ 196 | void setAnimationLightness( int lightness ); 197 | 198 | /*! 199 | * \brief Sets the arrow position to be the position passed in. This should be a value between 0 and 1. 200 | * \param position The qreal percentage that you want the arrow to be positioned at. 201 | */ 202 | void setArrowPositionPercent( const qreal position ); 203 | 204 | /*! 205 | * \brief Sets the size of the arrow that will appear on the Connection line. 206 | * \param size The qreal that you want each side of the arrow to be. 207 | */ 208 | void setArrowSize( const qreal size ); 209 | 210 | /*! 211 | * \brief Sets the color for the Connection.` 212 | * \param color QColor that you want the connection to be. 213 | */ 214 | void setColor( const QColor& color ); 215 | 216 | /*! 217 | * \brief Sets the curvature of the Connection line. 218 | * \param curvature The qreal that you want the curvature to be. 0 will produce a straight line. Values between 0 219 | * and 1 will produce nice soft curves. Values greater than 1 will cause the line to double back and form a "z" like 220 | * shape. 221 | */ 222 | void setCurvature( qreal curvature ); 223 | 224 | /*! 225 | * \brief Sets the destination Socket of the Connection. 226 | * \param destination The Socket that will be the desination of the Connection. 227 | */ 228 | void setDestinationSocket( Socket* destination ); 229 | 230 | /*! 231 | * \brief Sets the drag distance for this Connection. 232 | * \param dragDistance int distacne that you want to be exceeded before the Connection will be detached from a 233 | * Socket. 234 | */ 235 | void setDragDistance( qreal dragDistance ); 236 | 237 | /*! 238 | * \brief Sets the point outside of a Socket that will draw the Connection too while it is being dragged by the 239 | * mouse. \param dragPoint The QPointF that contains the coordinate to draw the Connection to. 240 | */ 241 | void setDragPoint( const QPointF& dragPoint ); 242 | 243 | /*! 244 | * \brief Overloaded from QObject. Sets the object name for this Connection object and infroms the style manager 245 | * that it requires styling. \param name The name that you want the object to have. 246 | */ 247 | void setObjectName( const QString& name ); 248 | 249 | /*! 250 | * \brief Sets the passed brush to be the one used when filling in the arrow of the Connection when it is selected. 251 | * \param brush The QBrush that you want the selected Connection to use. 252 | */ 253 | void setSelectedBrush( const QBrush& brush ); 254 | 255 | /*! 256 | * \brief Sets the color for the Connection when selected.` 257 | * \param color QColor that you want the connection to be. 258 | */ 259 | void setSelectedColor( const QColor& color ); 260 | 261 | /*! 262 | * \brief Sets the passed pen to be the one used to draw the Connection when it is selected. 263 | * \param pen The QPen that you want the selected Connection to use 264 | */ 265 | void setSelectedPen( const QPen& pen ); 266 | 267 | /*! 268 | * \brief Sets the starting Socket for this Connection 269 | * \param source The Socket that the Connection will start from. 270 | */ 271 | void setSourceSocket( Socket* source ); 272 | 273 | /*! 274 | * \brief Overloaded from QGraphicsPathItem. Returns the shape of the Connection. 275 | * \return QPainterPath that represents the Connections shape. 276 | */ 277 | virtual QPainterPath shape() const; 278 | 279 | /*! 280 | * \brief This is the source Socket of the Connection. 281 | * \return The Socket that is the source of the Connection. 282 | */ 283 | Socket* sourceSocket() const; 284 | 285 | /*! 286 | * \brief type 287 | * \return 288 | */ 289 | virtual int type() const; 290 | 291 | /*! 292 | * \brief Updates the Connection and performs a redraw. 293 | */ 294 | void updatePosition(); 295 | 296 | signals: 297 | /*! 298 | * \brief This signal is emitted when the Connection requires to be styled by the style manager. 299 | */ 300 | void requiresStyling( QObject* item ); 301 | 302 | protected: 303 | /*! 304 | * \brief hoverEnterEvent 305 | * \param event 306 | */ 307 | virtual void hoverEnterEvent( QGraphicsSceneHoverEvent* event ); 308 | 309 | /*! 310 | * \brief hoverLeaveEvent 311 | * \param event 312 | */ 313 | virtual void hoverLeaveEvent( QGraphicsSceneHoverEvent* event ); 314 | 315 | virtual QVariant itemChange( GraphicsItemChange change, const QVariant& value ); 316 | 317 | /*! 318 | * \brief mouseMoveEvent 319 | * \param event 320 | */ 321 | virtual void mouseMoveEvent( QGraphicsSceneMouseEvent* event ); 322 | 323 | /*! 324 | * \brief mouseReleaseEvent 325 | * \param event 326 | */ 327 | virtual void mouseReleaseEvent( QGraphicsSceneMouseEvent* event ); 328 | 329 | /*! 330 | * \brief paint 331 | * \param painter 332 | * \param option 333 | * \param widget 334 | */ 335 | virtual void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0 ); 336 | }; 337 | -------------------------------------------------------------------------------- /NodeView/expandablecontainer.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/NodeView_global.h" 6 | #include "NodeView/stylemanager.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class Connection; 15 | class NodeView; 16 | 17 | /*! 18 | * \brief [Sockets](\ref Socket) are used to make connections between [Nodes](\ref Node) using Connection objects. 19 | */ 20 | class NodeView_API ExpandableContainer : public QGraphicsItemGroup { 21 | public: 22 | /*! 23 | * \brief Definition of type ID for Socket objects. All QGrpahicsItems need a type ID. 24 | */ 25 | enum { Type = UserType + 95 }; 26 | 27 | /*! 28 | * \brief Locations of where the expansion button can appear 29 | */ 30 | enum ExpansionButtonPosition { ButtonLeft, ButtonRight }; 31 | 32 | /*! 33 | * \brief Title justify for the title text 34 | */ 35 | enum TitleLocation { TitleLeft, TitleRight, TitleCenter }; 36 | 37 | private: 38 | QGraphicsItem* m_containedItem; 39 | QGraphicsSimpleTextItem m_label; 40 | QString m_labelText; 41 | bool m_contentsVisible; 42 | QGraphicsPixmapItem m_picture; 43 | int m_width; 44 | 45 | TitleLocation m_titleLoc; 46 | ExpansionButtonPosition m_expansionPos; 47 | int m_leftAdjust; 48 | int m_rightAdjust; 49 | 50 | public: 51 | /*! 52 | * \brief Constructor. 53 | * \param type Whether this is an input or output Socket. 54 | * \param label The label that will appear on the Socket. 55 | * \param parent The parent item of the Socket. 56 | */ 57 | ExpandableContainer( QGraphicsItem* containedItem = NULL, const QString label = "", QGraphicsItem* parent = 0, 58 | ExpansionButtonPosition expPos = ButtonRight, TitleLocation titleLoc = TitleLeft ); 59 | 60 | /*! 61 | * \brief Returns the side at which the expansion button will appear 62 | * \return returns the location according to the enum ExpansionButtonPosition 63 | */ 64 | ExpansionButtonPosition buttonPosition(); 65 | 66 | /*! 67 | * \brief Expands/shrinks the central area of the container 68 | * \param expand or contract (true = expand) 69 | */ 70 | void expandArea( bool expanded ); 71 | 72 | /*! 73 | * \brief Returns the currently set font. 74 | * \return The QFont obeject that describes the currently set font being used for the text. 75 | */ 76 | QFont font() const; 77 | 78 | /*! 79 | * \brief Returns the current item contained in the container 80 | * \return The QGraphicsItem contained 81 | */ 82 | QGraphicsItem* getContainedItem(); 83 | 84 | /*! 85 | * \brief Returns true if the expnasion button is currently under the mouses pointer 86 | * \return true if expansion button is currently under the mouse 87 | */ 88 | bool isPixmapUnderMouse() const; 89 | 90 | /*! 91 | * \brief Returns the text from the label(contents) of the container 92 | * \return the text from the label of the container 93 | */ 94 | QString label() const; 95 | 96 | /*! 97 | * \brief Sets the location of the button either top left or top right 98 | * \param The position to put the button in. 99 | */ 100 | void setButtonLocation( ExpansionButtonPosition expansionPos ); 101 | 102 | /*! 103 | * \brief Sets the font used by the text label. 104 | * \param font The QFont object that desbribes the font desired to be used for the text in the Socket. 105 | */ 106 | void setFont( const QFont font ); 107 | 108 | /*! 109 | * \brief Sets the text for the label (contents) of the container 110 | * \param QString The string you want to the label to be set to 111 | */ 112 | void setLabel( const QString label ); 113 | 114 | /*! 115 | * \brief Sets the amount that the inner data is pushed in from the sides (adjustment for sockets) 116 | * \param int The amuount to push the data in. 117 | */ 118 | void setLeftAdjust( int adjust ); 119 | 120 | /*! 121 | * \brief Sets teh brush that will be used by the text in the title 122 | * \param The brush to be used 123 | */ 124 | void setTitleBrush( QBrush brush ); 125 | 126 | /*! 127 | * \brief Justify the title within the container 128 | * \param The direction in which you wish the title to be justified 129 | */ 130 | void setTitleLocation( TitleLocation titleLoc ); 131 | 132 | /*! 133 | * \brief Sets the Brush for the label (title) of the container 134 | * \param QBrush The Brush you want to the label to use 135 | */ 136 | QBrush titleBrush() const; 137 | 138 | /*! 139 | * \brief Set the maximum width the expandable container can take 140 | * \param width 141 | */ 142 | void setWidth( int width ); 143 | 144 | /*! 145 | * \brief Title height retrieves the height of the "title section" (button and title text) 146 | * \return The height of the tile section 147 | */ 148 | qreal titleHeight(); 149 | 150 | /*! 151 | * \brief Retrieve the justification placed on the title 152 | * \return The position of the title 153 | */ 154 | TitleLocation titleLocation(); 155 | 156 | /*! 157 | * \brief type 158 | * \return 159 | */ 160 | int type() const; 161 | 162 | /*! 163 | * \brief update 164 | */ 165 | void update(); 166 | 167 | protected: 168 | /*! 169 | * \brief mouseDoubleClickEvent 170 | * \param event 171 | */ 172 | void mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event ); 173 | 174 | /*! 175 | * \brief mousePressEvent 176 | * \param event 177 | */ 178 | void mousePressEvent( QGraphicsSceneMouseEvent* event ); 179 | 180 | /*! 181 | * \brief mouseReleaseEvent 182 | * \param event 183 | */ 184 | void mouseReleaseEvent( QGraphicsSceneMouseEvent* event ); 185 | 186 | /*! 187 | * \brief constructGroup 188 | */ 189 | void constructGroup(); 190 | }; 191 | -------------------------------------------------------------------------------- /NodeView/fulllinesocket.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/NodeView_global.h" 6 | #include "NodeView/socket.h" 7 | 8 | #include 9 | 10 | class NodeView; 11 | 12 | /*! 13 | * \brief A subclass of QGraphicsPolygonItem that is used 14 | * to represent a node within a NodeView view. 15 | */ 16 | class NodeView_API FullLineSocket : public Socket { 17 | Q_OBJECT 18 | 19 | public: 20 | enum { Type = UserType + 123 }; 21 | 22 | enum LabelPos { Left, Right, Center }; 23 | 24 | protected: 25 | LabelPos m_labelPos; 26 | 27 | public: 28 | /*! 29 | * \brief Constructor. 30 | * \param type Whether this is an input or output Socket. 31 | * \param label The label that will appear on the Socket. 32 | * \param parent The parent item of the Socket. 33 | */ 34 | FullLineSocket( NodeView* graphWidget, const SocketType type = Invalid, const QString label = "", 35 | QGraphicsItem* parent = 0 ); 36 | 37 | /*! 38 | * \brief Returns the position of the label within the node 39 | * \return the position of the label within the node 40 | */ 41 | LabelPos labelPos(); 42 | 43 | /*! 44 | * \brief Sets the position of the label within the node 45 | * \param LabelPos The position of the label within the node 46 | */ 47 | void setLabelPos( LabelPos pos ); 48 | 49 | /*! 50 | * \brief type 51 | * \return 52 | */ 53 | virtual int type() const; 54 | 55 | protected: 56 | /*! 57 | * \brief constructGroup 58 | */ 59 | virtual void constructGroup(); 60 | 61 | /*! 62 | * \brief mouseMoveEvent 63 | * \param event 64 | */ 65 | virtual void mouseMoveEvent( QGraphicsSceneMouseEvent* event ); 66 | 67 | /*! 68 | * \brief mousePressEvent 69 | * \param event 70 | */ 71 | virtual void mousePressEvent( QGraphicsSceneMouseEvent* event ); 72 | 73 | /*! 74 | * \brief mouseReleaseEvent 75 | * \param event 76 | */ 77 | virtual void mouseReleaseEvent( QGraphicsSceneMouseEvent* event ); 78 | 79 | /*! 80 | * \brief resizeGroup 81 | * \param width 82 | */ 83 | virtual void resizeGroup( qreal width ); 84 | }; 85 | -------------------------------------------------------------------------------- /NodeView/minimapdraglabel.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | class NodeView; 9 | class NodeViewMiniMap; 10 | 11 | class MinimapDragLabel : public QWidget { 12 | Q_OBJECT 13 | 14 | private: 15 | NodeViewMiniMap* m_miniMap; 16 | QPoint m_currentPos; 17 | bool m_drag; 18 | 19 | public: 20 | /*! 21 | * \brief MinimapDragLabel The dragable area of a NodeViewMiniMap which appears in the central most corner of the 22 | * minimap 23 | * \param NodeViewMiniMap The minimap that the drag label is attached to 24 | * \param QWidget The Parent object of the minimap drag label 25 | */ 26 | explicit MinimapDragLabel( NodeViewMiniMap* miniMap, QWidget* parent = 0 ); 27 | 28 | protected: 29 | /*! 30 | * \brief enterEvent 31 | * \param event 32 | */ 33 | void enterEvent( QEvent* event ); 34 | 35 | /*! 36 | * \brief leaveEvent 37 | * \param event 38 | */ 39 | void leaveEvent( QEvent* event ); 40 | 41 | /*! 42 | * \brief mouseMoveEvent 43 | * \param event 44 | */ 45 | void mouseMoveEvent( QMouseEvent* event ); 46 | 47 | /*! 48 | * \brief mousePressEvent 49 | * \param event 50 | */ 51 | void mousePressEvent( QMouseEvent* event ); 52 | 53 | /*! 54 | * \brief mouseReleaseEvent 55 | * \param event 56 | */ 57 | void mouseReleaseEvent( QMouseEvent* event ); 58 | }; 59 | -------------------------------------------------------------------------------- /NodeView/nodegroup.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/NodeView_global.h" 6 | #include "NodeView/stylemanager.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define BASE_RECT QRectF( -10, -10, 153, 33 ) 16 | 17 | class Node; 18 | class Connection; 19 | class Socket; 20 | class NodeView; 21 | 22 | QT_BEGIN_NAMESPACE 23 | class QGraphicsSceneMouseEvent; 24 | QT_END_NAMESPACE 25 | 26 | /*! 27 | * \brief A subclass of QGraphicsPolygonItem that is used 28 | * to represent a node within a NodeView view. 29 | */ 30 | class NodeView_API NodeGroup : public QObject, public QGraphicsPolygonItem { 31 | Q_OBJECT 32 | public: 33 | enum { Type = UserType + 32 }; 34 | 35 | enum TitleJustify { TitleLeft, TitleRight, TitleCenter }; 36 | 37 | protected: 38 | NodeView* m_graphWidget; 39 | 40 | QPen m_outlinePen; 41 | QPen m_selectedPen; 42 | QBrush m_backgroundBrush; 43 | QBrush m_selectedBrush; 44 | QBrush m_titleBarBrush; 45 | QBrush m_titleBarSelectedBrush; 46 | QRectF m_mainRect; 47 | 48 | QPointF m_curPos; 49 | QPointF m_topCorner; 50 | QGraphicsSimpleTextItem m_titleTextItem; 51 | QString m_titleText; 52 | Qt::TextElideMode m_elidedTitle; 53 | TitleJustify m_titleJustify; 54 | 55 | protected: 56 | QList m_containedNodes; 57 | 58 | QRectF m_boundingRect; 59 | 60 | static int NextZNode; 61 | 62 | public: 63 | /*! 64 | * \brief Constructor 65 | * \param graphWidget The NodeView class that the Node will exist whthin. 66 | * \param parent The QGraphicsItem parent item of the Node. 67 | * \param scene The QGraphicsScene that the Node will be 68 | * displayed within. 69 | */ 70 | NodeGroup( NodeView* graphWidget, QGraphicsItem* parent = 0, QGraphicsScene* scene = 0 ); 71 | 72 | void addNode( Node* node ); 73 | 74 | /*! 75 | * \brief Deconstructor 76 | */ 77 | virtual ~NodeGroup(); 78 | 79 | QBrush backgroundBrush() const; 80 | 81 | /*! 82 | * \brief Gets the bounding rectangle of the Node 83 | * \return The bounding rectangle of the Node as a QRectF. 84 | */ 85 | virtual QRectF boundingRect() const; 86 | 87 | QList containedNodes(); 88 | 89 | Qt::TextElideMode elidedTitle(); 90 | 91 | QPen outlinePen() const; 92 | 93 | void updateBoundingRect(); 94 | 95 | QBrush selectedBrush() const; 96 | 97 | QPen selectedPen() const; 98 | 99 | void setBackgroundBrush( const QBrush& brush ); 100 | 101 | void setElidedTitle( Qt::TextElideMode mode ); 102 | 103 | void setOutlinePen( const QPen& pen ); 104 | 105 | void setSelectedBrush( const QBrush& brush ); 106 | 107 | void setSelectedPen( const QPen& pen ); 108 | 109 | void setTitleBarBrush( const QBrush& brush ); 110 | 111 | void setTitleBarSelectedBrush( const QBrush& brush ); 112 | 113 | void setTitleJustify( TitleJustify justify ); 114 | 115 | void setTitleTextBrush( const QBrush& brush ); 116 | 117 | void setTitleTextFont( const QFont& font ); 118 | 119 | void setTitleTextPen( const QPen& pen ); 120 | 121 | void setTitleText( const QString& text ); 122 | 123 | /*! 124 | * \brief This is the shape of the Node as described by a [QPainterPath](http: 125 | * //qt-project.org/doc/qt-4.8/qpainterpath.html) object. 126 | * \return The QPainterPath that contains the shape of the 127 | * Node. 128 | */ 129 | QPainterPath shape() const; 130 | 131 | QBrush titleBarBrush() const; 132 | 133 | QBrush titleBarSelectedBrush() const; 134 | 135 | TitleJustify titleJustify() const; 136 | 137 | QString titleText() const; 138 | 139 | QBrush titleTextBrush() const; 140 | 141 | QFont titleTextFont() const; 142 | 143 | /*! 144 | * \brief This returns the user defined type of a Node 145 | * QGraphicsItem. All 146 | * QGrpahicsItems need to have a type in order to properly 147 | * function within a QGraphicsScene. 148 | * \return The Node object type identifier. 149 | */ 150 | virtual int type() const; 151 | 152 | void updateArea(); 153 | 154 | /*! 155 | * \brief This function performs all the drawing aspects of the Node. Reimplement this function to change the 156 | * appearence of the Node. 157 | * \param painter The QPainter object that will perform the 158 | * drawing. \param option \param widget 159 | */ 160 | virtual void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ); 161 | 162 | protected: 163 | QVariant itemChange( GraphicsItemChange change, const QVariant& value ); 164 | 165 | void mouseReleaseEvent( QGraphicsSceneMouseEvent* event ); 166 | 167 | virtual void mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event ); 168 | signals: 169 | void doubleClicked( QGraphicsSceneMouseEvent* event ); 170 | public slots: 171 | 172 | void updateGroup(); 173 | void removeNode( Node* node ); 174 | }; 175 | -------------------------------------------------------------------------------- /NodeView/nodeview.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/NodeView_global.h" 6 | #include "NodeView/stylemanager.h" 7 | 8 | #include 9 | #include 10 | 11 | class Node; 12 | class Socket; 13 | class Connection; 14 | class NodeGroup; 15 | class NodeViewMiniMap; 16 | 17 | /*! 18 | * \brief The NodeView class is a subclass of QGraphicsView and is used to display and control a node based user 19 | * interface. 20 | */ 21 | class NodeView_API NodeView : public QGraphicsView { 22 | Q_OBJECT 23 | 24 | Q_PROPERTY( QColor backgroundColor READ backgroundColor WRITE setBackgroundColor DESIGNABLE true ) 25 | Q_PROPERTY( QColor gridColor READ gridColor WRITE setGridColor DESIGNABLE true ) 26 | Q_PROPERTY( bool gridLines READ gridLines WRITE setGridLines DESIGNABLE true ) 27 | Q_PROPERTY( int gridSize READ gridSize WRITE setGridSize DESIGNABLE true ) 28 | Q_PROPERTY( bool snapToGrid READ snapToGrid WRITE setSnapToGrid DESIGNABLE true ) 29 | Q_ENUMS( MinimapPosition ) 30 | 31 | public: 32 | /*! 33 | * \brief The PixmapPosition enum is used to define where you want the pixel map item to appear in the title bar 34 | * area of the Node. 35 | */ 36 | enum MinimapPosition { TopLeft, TopRight, BottomLeft, BottomRight }; 37 | 38 | private: 39 | int m_timerId; /*!< The identification number of the refresh timer for the NodeView view. */ 40 | bool m_snapToGrid; /*!< Private member variable that indicates whether Node objects in the NodeView view should snap 41 | to a grid or not. */ 42 | bool m_gridLines; 43 | int m_gridSize; 44 | QList m_nodes; /*!< The list of [Nodes](\ref Node) currently in the NodeView view. */ 45 | QPixmap m_backgroundMap; /*!< A dynamically generated pixel map of the background grid. The grid is drawn this way 46 | for performance reasons. */ 47 | QPainter 48 | m_backgroundPainter; /*!< The QPainter object that dynamically generates the m_backgroundMap member variable. */ 49 | QPoint m_panStartPos; 50 | QColor m_gridColor; 51 | QColor m_backgroundColor; 52 | 53 | QPoint m_creationPoint; 54 | QGraphicsEllipseItem m_creationPointMarker; 55 | 56 | MinimapPosition m_miniMapPosition; 57 | NodeViewMiniMap* m_miniMap; 58 | StyleManager m_styleManager; 59 | 60 | bool m_midClickMoved; 61 | 62 | public: 63 | /*! 64 | * \brief Constructor 65 | * \param parent The parent QWidget of this NodeView instance. 66 | */ 67 | NodeView( QWidget* parent = 0 ); 68 | 69 | ~NodeView(); 70 | 71 | /*! 72 | * \brief Returns the color that the background is currently set to. 73 | * \return Returns the background color of the NodeView view as a QColor. 74 | * \see setBackgroundColor() 75 | */ 76 | QColor backgroundColor() const; 77 | 78 | QColor creationPointColor() const; 79 | 80 | /*! 81 | * \brief Removes and deletes the Node passed from the NodeView view. 82 | * \param node The Node that you want to remove and delete. 83 | */ 84 | void deleteNode( Node* node ); 85 | 86 | /*! 87 | * \brief Removes and deletes all [Nodes](\ref Node) from the NodeView view. 88 | */ 89 | void deleteAllNodes(); 90 | 91 | /*! 92 | * \brief Gets the pointer to the Node at position pos. 93 | * \param pos The position of the Node wanted in QPointF. 94 | * \return The Node that is at position, pos. 95 | */ 96 | Node* nodeByPosition( const QPointF& pos ) const; 97 | 98 | /*! 99 | * \brief Member function that returns the total number of Node instances in the NodeView view. 100 | * \return The total number of Node instances in the MagamaFLUX view. 101 | * \see Node 102 | */ 103 | int nodeCount() const; 104 | 105 | /*! 106 | * \brief Gets the count of QGraphicsItems currently selected in the MagamaFLUX view. 107 | * \return The number of QGraphicsItems currently selected. 108 | */ 109 | int selectionCount() const; 110 | 111 | /*! 112 | * \brief Returns the currently set grid color. 113 | * \return This is the QColor that the grid is currently set to. 114 | */ 115 | QColor gridColor() const; 116 | 117 | /*! 118 | * \brief Returns true if grid lines are enabled, false otherwise. 119 | * \return Bool describing if the grid lines are turned on or not. 120 | */ 121 | bool gridLines() const; 122 | 123 | /*! 124 | * \brief Returns the currently set width and height for the grid lines. 125 | * \return The int value the is the grids current size. 126 | */ 127 | int gridSize() const; 128 | 129 | /*! 130 | * \brief Hides the mini map for the NodeView view. 131 | */ 132 | void hideMiniMap(); 133 | 134 | /*! 135 | * \brief Returns whether or not the creation point marker is visible, if it is all new nodes will be created at 136 | * that point \return Whether or not the creation point marker is visible. 137 | */ 138 | bool isCreationPointMarkerVisible() const; 139 | 140 | /*! 141 | * \brief Returns the style sheet that is currently being used to style the NodeView view. 142 | * \return The QString containing the JSON style sheet. 143 | */ 144 | QString nodeViewStyleSheet() const; 145 | 146 | /*! 147 | * \brief Returns the position of the Minimap 148 | * \return The position of the minimap 149 | */ 150 | MinimapPosition minimapPosition() const; 151 | 152 | /*! 153 | * \brief Returns whether or not the minimap is visible 154 | * \return Whether or not the minimap is visible 155 | */ 156 | bool minimapVisible(); 157 | 158 | /*! 159 | * \brief Moves the minimap to the correct corner 160 | */ 161 | void moveMiniMap(); 162 | 163 | QList nodes(); 164 | 165 | // QList nodeGroups();//will need to rework some stuff for this then add it 166 | 167 | /*! 168 | * \brief Move the currently visable section of the NodeView view. 169 | * \param dx The amount to move the view in the X dimension. 170 | * \param dy The amount to move the view in the Y dimension. 171 | * \see zoom() 172 | */ 173 | void pan( qreal dx, qreal dy ); 174 | 175 | /*! 176 | * \brief Called to redraw the view by an outside source. 177 | */ 178 | void redrawView(); 179 | 180 | /*! 181 | * \brief Gets and returns a QList of all the currently selected [connections](\ref Connection) in the NodeView 182 | * view. 183 | * \return The [Connections](\ref Connection) that are currently selected. 184 | */ 185 | QList selectedConnections() const; 186 | 187 | /*! 188 | * \brief Gets and returns a QList of all the currently selected [Nodes](\ref Node) in the NodeView view. 189 | * \return The [Nodes](\ref Node) that are currently selected. 190 | */ 191 | QList selectedNodes() const; 192 | 193 | QList selectedNodeGroups() const; 194 | 195 | /*! 196 | * \brief Sets the background color of the NodeView view. 197 | * \param color The desired color of the background for the NodeView view. 198 | * \see backgroundColor() 199 | */ 200 | void setBackgroundColor( const QColor& color ); 201 | 202 | void setCreationPointMarkerColor( const QColor& color ); 203 | 204 | /*! 205 | * \brief setCreationPointMarkerVisible 206 | * \param visible 207 | */ 208 | void setCreationPointMarkerVisible( bool visible ); 209 | 210 | /*! 211 | * \brief Sets the color of the grid lines in the NodeView view. 212 | * \param color The desired color of the grid lines. 213 | */ 214 | void setGridColor( const QColor& color ); 215 | 216 | /*! 217 | * \brief Turns the grid lines on or off. 218 | * \param enabled Pass 'true' if you want to turn grid lines on, 'false' to turn them off. 219 | */ 220 | void setGridLines( bool enabled ); 221 | 222 | /*! 223 | * \brief Sets the grid size to be the passed value. This affects both the snap-to-grid size and grid lines. 224 | * \param size The int size that you want the grid to be. 225 | */ 226 | void setGridSize( int size ); 227 | 228 | /*! 229 | * \brief This feature will cause any Node in the scene to snap in place to the background grid if enabled. 230 | * \param enable Pass true if you want to enable snapToGrid, or false to disable snapToGrid. 231 | * \see snapToGrid() 232 | */ 233 | void setSnapToGrid( bool enable ); 234 | 235 | /*! 236 | * \brief Sets the style sheet to be used for styling Nodes, Sockets, and Connections. 237 | * \param styleSheet The style sheet you want to use in JSON format. 238 | */ 239 | void setNodeViewStyleMap( const QVariantMap& styleMap ); 240 | 241 | /*! 242 | * \brief Sets the style sheet to be used for styling Nodes, Sockets, and Connections. 243 | * \param styleSheet The style sheet you want to use in JSON format. 244 | */ 245 | void setNodeViewStyleSheet( const QString& styleSheet ); 246 | 247 | /*! 248 | * \brief Sets the MiniMaps Position then moves the minimap 249 | * \param pos The corner in which you want the minimap 250 | */ 251 | void setMiniMapPosition( const MinimapPosition pos ); 252 | 253 | /*! 254 | * \brief Set a specific zoom level for the NodeView 255 | * \param zoomPercent The value to set the zoom to default = 1.0 256 | */ 257 | void setZoom( qreal zoomPercent ); 258 | 259 | /*! 260 | * \brief Shows the mini map for the NodeView view. 261 | */ 262 | void showMiniMap(); 263 | 264 | /*! 265 | * \brief Returns a bool that describes the current state of snapToGrid feature. 266 | * \return The bool value of whether the snapToGrid feature is currently turned on. 267 | * \see setSnapToGrid() 268 | */ 269 | bool snapToGrid() const; 270 | 271 | /*! 272 | * \brief Scales the currently visable section of the NodeView view in or out by factor given. 273 | * \param zoomFactor The amount to zoom the NodeView view in or out. 274 | * \see pan() 275 | */ 276 | void zoom( qreal zoomFactor ); 277 | 278 | /*! 279 | * \brief Sets the zoom level to be the minimal to include the bounding box of all items 280 | */ 281 | void zoomToItemBoundingRect(); 282 | 283 | /*! 284 | * \brief Sets the zoom level to be the minimal to include the entire scene rect 285 | */ 286 | void zoomToSceneRect(); 287 | 288 | public slots: 289 | /*! 290 | * \brief Adds an allready created Node to the NodeView view. 291 | * \param node The Node you want added. 292 | */ 293 | void addNode( Node* node ); 294 | 295 | /*! 296 | * \brief Creates a Node with the passed perameters and adds it to the NodeView view. 297 | * \param inSockets The number of input sockets you want the node to have. 298 | * \param outSockets The number of output sockets you want the node to have. 299 | * \param nodeType The Node type for style sheet purposes. 300 | * \return The pointer to the node that was created. 301 | */ 302 | Node* createNode( int inSockets = 0, int outSockets = 0, const QString& nodeType = "" ); 303 | 304 | /*! 305 | * \brief Deletes the currently selected [Nodes](\ref Node) from the NodeView view. 306 | */ 307 | void deleteSelectedNodes(); 308 | 309 | signals: 310 | 311 | /*! 312 | * \brief This signal is emitted when a Connection has been made between two [Nodes](\ref Node). 313 | * \param sock The Input Socket of the new Connection. 314 | */ 315 | void connectionCreated( Socket* sock ); 316 | 317 | /*! 318 | * \brief This signal is emitted when a Connection is deleted from the NodeView view. 319 | */ 320 | void connectionDeleted(); 321 | 322 | /*! 323 | * \brief This signal is emitted when a Connection is currently under the mouse cursor. 324 | * \param conn The Connection that is under the mouse cursor 325 | */ 326 | void connectionMouseOver( Connection* conn ); 327 | 328 | /*! 329 | * \brief This signal is emitted when a Connection is selected within the NodeView view. 330 | * \param conn The Connection that is selected. 331 | */ 332 | void connectionSelected( Connection* conn ); 333 | 334 | /*! 335 | * \brief This signal is emitted when a Connection has been started from a Socket, but not yet completed. 336 | */ 337 | void connectionStarted(); 338 | 339 | /*! 340 | * \brief This signal is emitted when a Connection has been dropped on empty space in the view. 341 | * \param pos The QPointF that represents the position in the scene where the Connection was dropped. 342 | */ 343 | void connectToEmpty( Socket* socket, QPointF pos ); 344 | 345 | /*! 346 | * \brief disconnectToEmpty 347 | * \param socket 348 | * \param pos 349 | */ 350 | void disconnectToEmpty( Socket* socket, QPointF pos ); 351 | 352 | /*! 353 | * \brief This signal is emitted when the users performs a 'double click' of a mouse button within the NodeView 354 | * view. 355 | * \param event The data that was generated by the doubleClick event. 356 | */ 357 | void doubleClick( QMouseEvent* event ); 358 | 359 | /*! 360 | * \brief This signal is emitted when the NodeView view loses focus to another window or widget. 361 | * \param event The data that was generated by the focusOut event. 362 | */ 363 | void focusOut( QFocusEvent* event ); 364 | 365 | /*! 366 | * \brief This signal is emitted when the user moves the mouse around after a mousePress event has ocurred within 367 | * the NodeView view. 368 | * \param event The data that was generated by the mouseMove event. 369 | */ 370 | void mouseMove( QMouseEvent* event ); 371 | 372 | /*! 373 | * \brief This signal is emitted when the a mouse button is pressed down within the NodeView view. 374 | * \param event The data that was generated by the mousePress event. 375 | */ 376 | void mousePress( QMouseEvent* event ); 377 | 378 | /*! 379 | * \brief This signal is emitted when the user releases a mouse button after already pressing a mouse button within 380 | * the NodeView view. 381 | * \param event The data that was generated by the mouseRelease event. 382 | */ 383 | void mouseRelease( QMouseEvent* event ); 384 | 385 | /*! 386 | * \brief This signal is emitted whenever a Node is added to the NodeView view. 387 | * \param node The Node that is being added to the NodeView view. 388 | */ 389 | void nodeAdded( Node* node ); 390 | 391 | /*! 392 | * \brief This signal is emitted whenever a Node or Nodes are deleted from the NodeView view. 393 | */ 394 | void nodeDeleted(); 395 | 396 | /*! 397 | * \brief This signal is emitted when the user uses the mouse scroll wheel within the NodeView view. 398 | * \param event The data that was generated by the scrollWheel event. 399 | */ 400 | void scrollWheel( QWheelEvent* event ); 401 | 402 | /*! 403 | * \brief This signal is emitted whenever the selection within the NodeView view changes. 404 | */ 405 | void selectionChanged(); 406 | 407 | /*! 408 | * \brief This signal is emitted when a Connection is deleted from the NodeView view. 409 | */ 410 | void zoomChanged(); 411 | 412 | protected slots: 413 | /*! 414 | * \brief This function is called by a Connection object in the MagamFLUX when the Connection object handles a mouse 415 | * hover event. 416 | * \param conn This is the Connection that is currently being hovered over by the mouse. 417 | * \see connectionMouseOver 418 | */ 419 | void onConnectionMouseOver( Connection* conn ); 420 | 421 | protected: 422 | void registerStyleableItem( Styleable* styleItem ); 423 | 424 | friend class Connection; 425 | friend class Socket; 426 | friend class Node; 427 | 428 | protected: 429 | virtual void drawBackground( QPainter* painter, const QRectF& rect ); 430 | 431 | /*! 432 | * \brief focusOutEvent 433 | * \param event 434 | */ 435 | virtual void focusOutEvent( QFocusEvent* event ); 436 | 437 | /*! 438 | * \brief keyPressEvent 439 | * \param event 440 | */ 441 | virtual void keyPressEvent( QKeyEvent* event ); 442 | 443 | /*! 444 | * \brief mouseDoubleClickEvent 445 | * \param event 446 | */ 447 | virtual void mouseDoubleClickEvent( QMouseEvent* event ); 448 | 449 | /*! 450 | * \brief mouseMoveEvent 451 | * \param event 452 | */ 453 | virtual void mouseMoveEvent( QMouseEvent* event ); 454 | 455 | /*! 456 | * \brief mousePressEvent 457 | * \param event 458 | */ 459 | virtual void mousePressEvent( QMouseEvent* event ); 460 | 461 | /*! 462 | * \brief mouseReleaseEvent 463 | * \param event 464 | */ 465 | virtual void mouseReleaseEvent( QMouseEvent* event ); 466 | 467 | /*! 468 | * \brief scaleView 469 | * \param scaleFactor 470 | */ 471 | virtual void scaleView( qreal scaleFactor ); 472 | 473 | /*! 474 | * \brief wheelEvent 475 | * \param event 476 | */ 477 | virtual void wheelEvent( QWheelEvent* event ); 478 | 479 | private slots: 480 | void onConnectionCreated(); 481 | void onConnectionDeleted(); 482 | void onConnectionStarted(); 483 | void onConnectToEmpty( Socket* socket, QPointF pos ); 484 | void onDisconnectToEmpty( Socket* socket, QPointF pos ); 485 | void onSelectionChanged(); 486 | }; 487 | -------------------------------------------------------------------------------- /NodeView/nodeviewminimap.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | class NodeView; 9 | class MinimapDragLabel; 10 | 11 | class NodeViewMiniMap : public QGraphicsView { 12 | Q_OBJECT 13 | 14 | private: 15 | bool m_mouseDrag; 16 | MinimapDragLabel* m_dragArea; 17 | NodeView* m_magmaView; 18 | 19 | public: 20 | /*! 21 | * \brief The NodeViewMiniMap is a minimap that can be displayed onto a NodeView 22 | * \param The MagmaFlux it can be displayed onto 23 | * \param The parent object of the NodeViewMiniMap 24 | */ 25 | explicit NodeViewMiniMap( NodeView* magmaView, QWidget* parent = 0 ); 26 | 27 | /*! 28 | * \brief magmaView 29 | * \return 30 | */ 31 | NodeView* magmaView(); 32 | 33 | /*! 34 | * \brief Move the dragArea of the minimap to the correct corner 35 | */ 36 | void moveDragArea(); 37 | 38 | protected: 39 | /*! 40 | * \brief drawForeground 41 | * \param painter 42 | * \param rect 43 | */ 44 | virtual void drawForeground( QPainter* painter, const QRectF& rect ); 45 | 46 | /*! 47 | * \brief mouseMoveEvent 48 | * \param event 49 | */ 50 | virtual void mouseMoveEvent( QMouseEvent* event ); 51 | 52 | /*! 53 | * \brief mousePressEvent 54 | * \param event 55 | */ 56 | virtual void mousePressEvent( QMouseEvent* event ); 57 | 58 | /*! 59 | * \brief mouseReleaseEvent 60 | * \param event 61 | */ 62 | virtual void mouseReleaseEvent( QMouseEvent* event ); 63 | 64 | /*! 65 | * \brief resizeEvent 66 | * \param event 67 | */ 68 | virtual void resizeEvent( QResizeEvent* event ); 69 | 70 | private: 71 | QList calculateUnusedAreas(); 72 | 73 | private slots: 74 | void adjust(); 75 | }; -------------------------------------------------------------------------------- /NodeView/sidesocketnode.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/NodeView_global.h" 6 | #include "NodeView/node.h" 7 | 8 | class NodeView; 9 | class ExpandableContainer; 10 | 11 | /*! 12 | * \brief A subclass of QGraphicsPolygonItem that is used 13 | * to represent a node within a NodeView view. 14 | */ 15 | class NodeView_API SideSocketNode : public Node { 16 | Q_OBJECT 17 | 18 | public: 19 | enum { Type = UserType + 5 }; 20 | 21 | /*! 22 | * \brief Constructor 23 | * \param graphWidget The NodeView class that the Node will exist whthin. 24 | * \param parent The QGraphicsItem parent item of the Node. 25 | * \param scene The QGraphicsScene that the Node will be 26 | * displayed within. 27 | */ 28 | SideSocketNode( NodeView* graphWidget, QGraphicsItem* parent = 0, QGraphicsScene* scene = 0 ); 29 | 30 | /*! 31 | * \brief Adds a new input Socket to the Node. 32 | * \return The index of the new input Socket created. 33 | * \see getInputSocket(), getInputSocketCount(), deleteInputSocket() 34 | */ 35 | virtual int addInputSocket(); 36 | 37 | /*! 38 | * \brief Adds a new output Socket to the Node. 39 | * \return The index of the new output Socket created. 40 | * \see getOutputSocket(), getOutputSocketCount(), deleteOutputSocket() 41 | */ 42 | virtual int addOutputSocket(); 43 | 44 | /*! 45 | * \brief Returns the currently set detail text in HTML format. 46 | * \return The QString containing the detail text. 47 | */ 48 | virtual QString detailText() const; 49 | 50 | /*! 51 | * \brief Returns the default color of the detail text. 52 | * \return The QColor that the is the default color for unformatted detail text. 53 | */ 54 | virtual QColor detailTextColor() const; 55 | 56 | /*! 57 | * \brief Returns the font currently being used for the detail text. 58 | * \return The QFont the the detail text is using. 59 | */ 60 | virtual QFont detailTextFont() const; 61 | 62 | /*! 63 | * \brief Returns the expandable container contained 64 | * \return the ExpandableContainer contained in the node 65 | */ 66 | ExpandableContainer* expandableArea(); 67 | 68 | /*! 69 | * \brief Returns the title of the expandabelContainer within 70 | * \return THe title of the expandable title 71 | */ 72 | QString expandableTitle() const; 73 | 74 | /*! 75 | * \brief Returns the font currently being used for the expandableContainer. 76 | * \return The QFont the the expandableContainer is using. 77 | */ 78 | QFont expandableTitleFont() const; 79 | 80 | /*! 81 | * \brief Expand the expandable container 82 | * \param Expanded or closed 83 | */ 84 | void expandArea( bool expanded ); 85 | 86 | /*! 87 | * \brief Sets the text to be displayed as the detail text. This can contain HTML formatting structures. 88 | * \param text The QString that contains the text to be displayed. 89 | */ 90 | virtual void setDetailText( const QString& text ); 91 | 92 | /*! 93 | * \brief Sets the default text color for unformatted text in the detail text. 94 | * \param color The QColor that you want the unformatted text to be. 95 | */ 96 | virtual void setDetailTextColor( const QColor& color ); 97 | 98 | /*! 99 | * \brief Sets if the detail text should be displayed or not. 100 | * \param enabled If true, the detail text will be shown, otherwise it will be hidden. 101 | */ 102 | virtual void setDetailTextEnabled( bool enabled ); 103 | 104 | /*! 105 | * \brief Sets the text font for the detail text. 106 | * \param font The QFont that you want the font to be. 107 | */ 108 | virtual void setDetailTextFont( const QFont& font ); 109 | 110 | /*! 111 | * \brief Sets the ExpandableContainer contained 112 | * \param ExpandableContainer The expandableContainer you want the node to contain 113 | */ 114 | void setExpandableArea( ExpandableContainer* container ); 115 | 116 | /*! 117 | * \brief Sets the text to be displayed as the Title of the Expandable container text. 118 | * \param text The QString that contains the text to be displayed. 119 | */ 120 | void setExpandableTitle( const QString& text ); 121 | 122 | void setAreaExpanded(); 123 | 124 | /*! 125 | * \brief Sets the text font for the Expandable Container title text. 126 | * \param font The QFont that you want the font to be. 127 | */ 128 | void setExpandableTitleFont( const QFont& font ); 129 | 130 | /*! 131 | * \brief This returns the user defined type of a Node 132 | * QGraphicsItem. All 133 | * QGrpahicsItems need to have a type in order to properly 134 | * function within a QGraphicsScene. 135 | * \return The Node object type identifier. 136 | */ 137 | virtual int type() const; 138 | 139 | protected: 140 | /*! 141 | * \brief This function is called whenever a new input or output Socket is added/removed from the Node. It 142 | * calculates its new size and puts all the components in the proper position. 143 | */ 144 | virtual void resizeNode(); 145 | 146 | /*! 147 | * \brief resizeTitleBar 148 | */ 149 | virtual void resizeTitleBar(); 150 | 151 | private: 152 | bool m_areaExpanded; 153 | ExpandableContainer* m_container; 154 | }; 155 | -------------------------------------------------------------------------------- /NodeView/simplenode.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/NodeView_global.h" 6 | #include "NodeView/node.h" 7 | 8 | class NodeView_API SimpleNode : public Node { 9 | Q_OBJECT 10 | 11 | protected: 12 | qreal m_minHeight; 13 | 14 | public: 15 | enum { Type = UserType + 7 }; 16 | 17 | SimpleNode( NodeView* graphWidget, QGraphicsItem* parent = 0, QGraphicsScene* scene = 0 ); 18 | 19 | /*! 20 | * \brief Adds a new input Socket to the Node. 21 | * \return The index of the new input Socket created. 22 | * \see getInputSocket(), getInputSocketCount(), deleteInputSocket() 23 | */ 24 | virtual int addInputSocket(); 25 | 26 | /*! 27 | * \brief Adds a new output Socket to the Node. 28 | * \return The index of the new output Socket created. 29 | * \see getOutputSocket(), getOutputSocketCount(), deleteOutputSocket() 30 | */ 31 | virtual int addOutputSocket(); 32 | 33 | /*! 34 | * \brief Gets the bounding rectangle of the Node 35 | * \return The bounding rectangle of the Node as a QRectF. 36 | */ 37 | QRectF boundingRect() const; 38 | 39 | qreal minimumHeight() const; 40 | 41 | void setMinimumHeight( qreal height ); 42 | 43 | /*! 44 | * \brief shape 45 | * \return 46 | */ 47 | QPainterPath shape() const; 48 | 49 | protected: 50 | /*! 51 | * \brief resizeNode 52 | */ 53 | virtual void resizeNode(); 54 | }; 55 | -------------------------------------------------------------------------------- /NodeView/socket.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/NodeView_global.h" 6 | #include "NodeView/stylemanager.h" 7 | #include "NodeView/trianglesocketshape.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class Connection; 15 | class NodeView; 16 | 17 | class QPropertyAnimation; 18 | 19 | /*! 20 | * \brief [Sockets](\ref Socket) are used to make connections between [Nodes](\ref Node) using Connection objects. 21 | */ 22 | class NodeView_API Socket : public QObject, public QGraphicsItemGroup, public Styleable { 23 | Q_OBJECT 24 | 25 | Q_PROPERTY( int animationDuration READ animationDuration WRITE setAnimationDuration DESIGNABLE true ) 26 | Q_PROPERTY( int animationLightness READ animationLightness WRITE setAnimationLightness DESIGNABLE true ) 27 | Q_PROPERTY( QColor fillColor READ fillColor WRITE setFillColor DESIGNABLE true ) 28 | Q_PROPERTY( QFont font READ font WRITE setFont DESIGNABLE true ) 29 | Q_PROPERTY( QColor labelColor READ labelColor WRITE setLabelColor DESIGNABLE true ) 30 | Q_PROPERTY( bool multipleConnections READ multipleConnections WRITE setMultipleConnections DESIGNABLE true ) 31 | Q_PROPERTY( QColor outlineColor READ outlineColor WRITE setOutlineColor DESIGNABLE true ) 32 | Q_PROPERTY( QBrush socketBrush READ socketBrush WRITE setSocketBrush DESIGNABLE true ) 33 | Q_PROPERTY( QPen socketPen READ socketPen WRITE setSocketPen DESIGNABLE true ) 34 | Q_PROPERTY( SocketShape socketShape READ socketShape WRITE setSocketShape DESIGNABLE true ) 35 | Q_PROPERTY( qreal socketShapeSize READ socketShapeSize WRITE setSocketShapeSize DESIGNABLE true ) 36 | Q_PROPERTY( bool animationEnabled READ animationEnabled WRITE setAnimationEnabled DESIGNABLE true ) 37 | Q_ENUMS( SocketShape ) 38 | 39 | public: 40 | /*! 41 | * \brief Definition of type ID for Socket objects. All QGrpahicsItems need a type ID. 42 | */ 43 | enum { Type = UserType + 2 }; 44 | 45 | /*! 46 | * \brief This describes the shape of the Socket connection point. 47 | */ 48 | enum SocketShape { Circle, Square, Triangle, None }; 49 | 50 | /*! 51 | * \brief This describes the relative alignment of the text and socket shape 52 | */ 53 | enum VerticalAlignment { Top, Center, Bottom }; 54 | 55 | /*! 56 | * \brief This describes what will happen if the text on the label is too large to fully display on a single line 57 | */ 58 | enum LabelStyle { Elide, Wrap }; 59 | 60 | /*! 61 | * \brief This describes whether the object will be an input or output socket. This will determine the orientation 62 | * and behavior of the Socket. 63 | */ 64 | enum SocketType { Input, Output, Invalid }; 65 | 66 | protected: 67 | SocketShape m_socketShape; /*!< The shape of the Socket connection point. */ 68 | qreal m_socketShapeSize; 69 | QAbstractGraphicsShapeItem* m_shape; 70 | SocketType m_socketType; /*!< Whether this is an input or output Socket. */ 71 | QGraphicsTextItem* m_label; 72 | VerticalAlignment m_verticalAlignment; 73 | // private: 74 | 75 | QPropertyAnimation* m_animation; 76 | int m_animationDuration; 77 | int m_animationLightness; 78 | bool m_animationEnabled; 79 | bool m_dragEnabled; 80 | LabelStyle m_labelStyle; 81 | 82 | QColor m_fillColor; /*!< The fill color of the Socket. */ 83 | QColor m_outlineColor; /*!< The outline color of the Socket */ 84 | 85 | // QColor m_textColor; /*!< The color of the text of the Socket. */ 86 | bool m_multiConnections; 87 | 88 | QString m_labelText; 89 | 90 | NodeView* m_graphWidget; 91 | 92 | QList m_connections; 93 | Connection* m_tempConnection; 94 | 95 | public: 96 | /*! 97 | * \brief Constructor. 98 | * \param type Whether this is an input or output Socket. 99 | * \param label The label that will appear on the Socket. 100 | * \param parent The parent item of the Socket. 101 | */ 102 | Socket( NodeView* graphWidget, const SocketType type = Invalid, const QString label = "", 103 | QGraphicsItem* parent = 0 ); 104 | 105 | /*! 106 | * \brief 107 | */ 108 | virtual ~Socket(); 109 | 110 | /*! 111 | * \brief Adds the Connection object to the list of this [Sockets](\ref Socket) connections 112 | * \param connection The QSharedPointer that referenecs the Connection object that you want to add. 113 | */ 114 | void addConnection( Connection* connection ); 115 | 116 | /*! 117 | * \brief Returns the amount of time in miliseconds that the animation will run for. 118 | * \return Int that is the number of miliseconds the animation will run for. 119 | */ 120 | int animationDuration() const; 121 | 122 | /*! 123 | * \brief Returns true if animation is currently enabled. If the animation is enabled the sockets color will change 124 | * when hovered over 125 | * \return Bool which states if animation is enabled for 126 | */ 127 | bool animationEnabled() const; 128 | 129 | /*! 130 | * \brief Returns the level that the animation will lighten the color of the Socket. 131 | * \return Int the is the level of lightness the color will change too. Default is 175. 132 | */ 133 | int animationLightness() const; 134 | 135 | /*! 136 | * \brief Removes all connections from this Socket. 137 | */ 138 | void clearConnections(); 139 | 140 | /*! 141 | * \brief Returns the list of [Connections](\ref Connection) that this Socket has. 142 | * \return The QList of connections that this Socket has. 143 | */ 144 | QList connections() const; 145 | 146 | /*! 147 | * \brief Creates a Connection between this Socket and the one passed. This is the perfered way to create 148 | * connections between two [Sockets](\ref Socket). \param socket The pointer to the Socket that you want to connect 149 | * to. \return True, if the connection was successful, false if not. 150 | */ 151 | bool createConnection( Socket* socket ); 152 | 153 | /*! 154 | * \brief Returns whether or not a connection can be started from this socket 155 | * \return whether or not a connection can be started from this socket 156 | */ 157 | bool dragEnabled(); 158 | 159 | /*! 160 | * \brief This returns the QColor object that describes the fill color of the Socket shape connector. 161 | * \return The color of the Socket shape. 162 | */ 163 | QColor fillColor() const; 164 | 165 | /*! 166 | * \brief Returns the currently set font. 167 | * \return The QFont obeject that describes the currently set font being used for the text. 168 | */ 169 | QFont font() const; 170 | 171 | /*! 172 | * \brief Returns true if the Socket connection point is under the mouse cursor, false if it is not. 173 | * \return 174 | */ 175 | bool isConnectionPointUnderMouse( QGraphicsSceneMouseEvent* event ) const; 176 | 177 | /*! 178 | * \brief Returns a boolean value stating whether the Socket is valid or not. 179 | * \return Boolean value describing the [Sockets](\ref Socket) validity. 180 | */ 181 | bool isValid() const; 182 | 183 | /*! 184 | * \brief Returns the QPen that is currently set to the label of the Socket. This will affect the outline color and 185 | * style of the Socket label. 186 | * \return The QPen that is assigned to the Socket label. 187 | */ 188 | virtual QColor labelColor() const; 189 | 190 | /*! 191 | * \brief Returns the LabelStyle that is currently being used by the label when the text is too long to fit on a 192 | * single line 193 | * \return The LabelStyle that is being used 194 | */ 195 | LabelStyle labelStyle() const; 196 | 197 | int maxTextLength(); 198 | 199 | /*! 200 | * \brief Returns a boolean value stating whether multiple connections are enabled or not. For Output [Sockets](\ref 201 | * Socket) this is enabled by default. For Input [Sockets](\ref Socket) this is disabled by default. 202 | * \return Boolean value describing if multi ple connections are enabled or not. 203 | */ 204 | bool multipleConnections() const; 205 | 206 | /*! 207 | * \brief This function returns the outline color of the text and connection point shape. 208 | * \return The QColor object that describes the outline color. 209 | */ 210 | QColor outlineColor() const; 211 | 212 | /*! 213 | * \brief The starting position of the Socket on the Node. 214 | * \return The position as a QPointF. 215 | */ 216 | QPointF position() const; 217 | 218 | /*! 219 | * \brief Rebuilds the socket giving it a maximum size that it is allowed to take up 220 | * \return The position as a QPointF. 221 | */ 222 | void rebuildSocket( qreal width ); 223 | 224 | /*! 225 | * \brief Sets the animation duration. 226 | * \param duration int in miliseconds that you want the animation duration to be. 227 | */ 228 | void setAnimationDuration( int duration ); 229 | 230 | /*! 231 | * \brief Sets if the animation is to be run 232 | * \param bool reperesenting if you want the animation to run 233 | * \note may give incorrect results if changed while animation is running 234 | */ 235 | void setAnimationEnabled( bool enabled ); 236 | 237 | /*! 238 | * \brief Sets the animtion lightness. A value of 100 will keep it the same. A valye greater than 100 will cause the 239 | * color to become lighter. A value less than 100 but greater thn 0 will make the color darker. Values less than 0 240 | * will cause undefined results. 241 | * \param lightness int that will change the color of the animation. 242 | */ 243 | void setAnimationLightness( int lightness ); 244 | 245 | /*! 246 | * \brief Sets if connections can be started from this node 247 | * \param bool reperesenting if you want connections to be allowed to be started 248 | */ 249 | void setDragEnabled( bool enabled ); 250 | 251 | /*! 252 | * \brief Sets the fill color for this Socket. 253 | * \param fillColor The QColor object that describes the desired fill color. 254 | */ 255 | void setFillColor( const QColor& fillColor ); 256 | 257 | /*! 258 | * \brief Sets the font used by the text label. 259 | * \param font The QFont object that desbribes the font desired to be used for the text in the Socket. 260 | */ 261 | virtual void setFont( const QFont& font ); 262 | 263 | /*! 264 | * \brief Sets the Socket label pen to be the QPen passed. This will change the outline color and style of the text 265 | * in the label. \param pen The QPen with the settings you want the label text to have. 266 | */ 267 | void setLabelColor( const QColor& color ); 268 | 269 | /*! 270 | * \brief Sets the Socket label Style to be the LabelStyle passed. This will change the behaviour when the text of 271 | * the label will not fit on a single line \param style the LabelStyle you want the label to have 272 | */ 273 | void setLabelStyle( const LabelStyle style ); 274 | 275 | /*! 276 | * \brief Sets whether this Socket allows multiple connections or not. 277 | * \param enabled Pass 'true' if you want to enable multiple connections, 'false' if you want to disable them. 278 | */ 279 | void setMultipleConnections( bool enabled ); 280 | 281 | /*! 282 | * \brief Overloaded from QObject. Sets the object name for this Socket object and infroms the style manager that it 283 | * requires styling. 284 | * \param name The name that you want the object to have. 285 | */ 286 | virtual void setObjectName( const QString& name ); 287 | 288 | /*! 289 | * \brief Sets the outline color for this Socket. 290 | * \param outlineColor The QColor object that describes the desired outline color 291 | */ 292 | void setOutlineColor( const QColor& outlineColor ); 293 | 294 | /*! 295 | * \brief Sets the brush to be used to fill in the Socket shape. 296 | * \param brush The QBrush that you want the Socket shape to use. 297 | */ 298 | void setSocketBrush( const QBrush& brush ); 299 | 300 | /*! 301 | * \brief Sets the name of the Socket object. The name is displayed beside the shape connection point. 302 | * \param name The QString object that contains the name. 303 | */ 304 | virtual void setSocketName( const QString& name ); 305 | 306 | /*! 307 | * \brief Sets the pen to be used to outline the Socket shape. 308 | * \param pen The QPen that you want to Socket shape to use. 309 | */ 310 | void setSocketPen( const QPen& pen ); 311 | 312 | /*! 313 | * \brief Member function that allows you to change the connection point shape of the Socket. 314 | * \param shape The SocketShape value that describes the shape you want for the connection point. 315 | */ 316 | void setSocketShape( const SocketShape shape ); 317 | 318 | /*! 319 | * \brief Sets the socket shape size to the passed value. 320 | * \param size The size in a qreal that you want the socket shape to be. 321 | */ 322 | void setSocketShapeSize( const qreal size ); 323 | 324 | /*! 325 | * \brief Sets the orientation of the Socket if it has a triangle shape 326 | * \param dir the direction that the point of the triangle should be facing 327 | */ 328 | void setTriangleOrientation( TriangleSocketShape::Orientation dir ); 329 | 330 | /*! 331 | * \brief Sets the vertical alignment of the sockets text in comparison with the socket shape 332 | * \param Align where they are aligned to 333 | */ 334 | void setVerticalAlignment( VerticalAlignment align ); 335 | 336 | /*! 337 | * \brief shape 338 | * \return 339 | */ 340 | virtual QPainterPath shape() const; 341 | 342 | /*! 343 | * \brief Returns the brush being used to fill in the Socket shape. 344 | * \return The QBrush being returned. 345 | */ 346 | QBrush socketBrush() const; 347 | 348 | /*! 349 | * \brief The location point of the Socket connection point. 350 | * \return The QPointF that contains the location of the Socket connection point. 351 | */ 352 | QPointF socketLocation() const; 353 | 354 | /*! 355 | * \brief Gets the name of the Socket. 356 | * \return The QString that contains the name of the Socket. 357 | */ 358 | virtual QString socketName() const; 359 | 360 | /*! 361 | * \brief Returns the pen used to outline the Socket shape. 362 | * \return The QPen being returned. 363 | */ 364 | QPen socketPen() const; 365 | 366 | /*! 367 | * \brief Gets the current connection point shape that the Socket has. 368 | * \return The connection point shap type of the Socket. 369 | */ 370 | SocketShape socketShape() const; 371 | 372 | /*! 373 | * \brief Returns the currently set socket shape size. 374 | * \return The qreal that the socket shape size is currently set to. 375 | */ 376 | qreal socketShapeSize() const; 377 | 378 | /*! 379 | * \brief Gets whether a Socket is an Output or Input Socket. 380 | * \return The SocketType of the Socket. 381 | */ 382 | SocketType socketType() const; 383 | 384 | /*! 385 | * \brief Returns the direction in which the socket is facing if it is a triangular socket. 386 | * \return The direction in which the socket is facing if it is a triangular socket otherwise Orientation::None 387 | */ 388 | TriangleSocketShape::Orientation triangleOrientation(); 389 | 390 | /*! 391 | * \brief type 392 | * \return 393 | */ 394 | virtual int type() const; 395 | 396 | /*! 397 | * \brief Schedules a redraw of the Socekt. 398 | * \param rect The are to be redrawn. 399 | */ 400 | virtual void update( const QRectF& rect = QRectF() ); 401 | 402 | /*! 403 | * \brief This updates the connection drawing points for redrawing the connection lines when a Node is being moved 404 | * around. 405 | */ 406 | void updateConnections(); 407 | 408 | /*! 409 | * \brief Returns the alignment of the socket shape and text 410 | * \return The alignment of the socket shape and text 411 | */ 412 | VerticalAlignment verticalAlignment(); 413 | 414 | public slots: 415 | /*! 416 | * \brief removeConnection 417 | * \param conn 418 | */ 419 | void removeConnection( QObject* conn ); 420 | 421 | signals: 422 | /*! 423 | * \brief This signal is emitted when a Connection has been made between two [Sockets](\ref Socket). 424 | */ 425 | void connectionCreated(); 426 | 427 | /*! 428 | * \brief This signal is emitted when a Connection is deleted or disconnected from the Socket. 429 | */ 430 | void connectionDeleted(); 431 | 432 | /*! 433 | * \brief This signal is emitted when a Connection had been started to be dragged from a Socket but not yet 434 | * completed by Connecting to another Socket. 435 | */ 436 | void connectionStarted(); 437 | 438 | /*! 439 | * \brief This signal is emitted when a Connection has been dropped on empty space in the view. 440 | * \param pos The QPointF that represents the position in the scene where the Connection was dropped. 441 | */ 442 | void connectToEmpty( Socket* socket, QPointF pos ); 443 | 444 | /*! 445 | * \brief This signal is emitted when the Socket requires to be styled by the style manager. 446 | */ 447 | void requiresStyling( QObject* item ); 448 | 449 | protected: 450 | /*! 451 | * \brief constructGroup 452 | */ 453 | virtual void constructGroup(); 454 | /*! 455 | * \brief hoverEnterEvent 456 | * \param event 457 | */ 458 | virtual void hoverEnterEvent( QGraphicsSceneHoverEvent* event ); 459 | 460 | /*! 461 | * \brief hoverLeaveEvent 462 | * \param event 463 | */ 464 | virtual void hoverLeaveEvent( QGraphicsSceneHoverEvent* event ); 465 | 466 | /*! 467 | * \brief mouseMoveEvent 468 | * \param event 469 | */ 470 | virtual void mouseMoveEvent( QGraphicsSceneMouseEvent* event ); 471 | 472 | /*! 473 | * \brief mousePressEvent 474 | * \param event 475 | */ 476 | virtual void mousePressEvent( QGraphicsSceneMouseEvent* event ); 477 | 478 | /*! 479 | * \brief mouseReleaseEvent 480 | * \param event 481 | */ 482 | virtual void mouseReleaseEvent( QGraphicsSceneMouseEvent* event ); 483 | 484 | /*! 485 | * \brief resizeGroup 486 | * \param width 487 | */ 488 | virtual void resizeGroup( qreal width ); 489 | }; 490 | -------------------------------------------------------------------------------- /NodeView/stylemanager.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #ifndef STYLEMANAGER_H 4 | #define STYLEMANAGER_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class QGradient; 11 | 12 | #if QT_VERSION >= 0x050301 13 | // these are Qt5 features that aren't available in Krakatoa for Maya 2015 & 2016 14 | class QJsonObject; 15 | class QJsonArray; 16 | #endif 17 | 18 | class Styleable { 19 | public: 20 | virtual ~Styleable(){}; 21 | 22 | protected: 23 | virtual void requiresStyling( QObject* item ) = 0; 24 | }; 25 | 26 | class StyleManager : public QObject { 27 | Q_OBJECT 28 | private: 29 | QString m_styleSheet; 30 | QVariantMap m_styleMap; 31 | QSet m_styleItems; 32 | 33 | public: 34 | /*! 35 | * \brief StyleManager 36 | * \param parent 37 | */ 38 | explicit StyleManager( QObject* parent = 0 ); 39 | 40 | /*! 41 | * \brief registerStyleableItem 42 | * \param styleItem 43 | */ 44 | void registerStyleableItem( Styleable* styleItem ); 45 | 46 | /*! 47 | * \brief setStyleMap 48 | * \param map 49 | */ 50 | void setStyleMap( const QVariantMap map ); 51 | 52 | /*! 53 | * \brief setStyleSheet 54 | * \param style 55 | */ 56 | void setStyleSheet( const QString style ); 57 | 58 | /*! 59 | * \brief styleSheet 60 | * \return 61 | */ 62 | QString styleSheet() const; 63 | 64 | public slots: 65 | /*! 66 | * \brief onRequiresStyling 67 | */ 68 | void onRequiresStyling( QObject* item ); 69 | 70 | /*! 71 | * \brief onStyleItemDestroyed 72 | */ 73 | void onStyleItemDestroyed( QObject* item ); 74 | 75 | private: 76 | /*! 77 | * \brief applyStyleSheet 78 | */ 79 | void applyStyleSheet(); 80 | 81 | /*! 82 | * \brief applyStyleSheet 83 | * \param styleItem 84 | */ 85 | void applyStyleSheet( QObject* styleItem ); 86 | 87 | // void applyStyleToConnection(Connection *conn); 88 | 89 | // void applyStyleToNode(Node *node); 90 | 91 | // void applyStyleToSocket(Socket *sock); 92 | 93 | #if QT_VERSION >= 0x050301 94 | /*! 95 | * \brief parseConnection 96 | * \param conn 97 | * \return 98 | */ 99 | QVariantMap parseConnection( const QJsonObject& conn ); 100 | 101 | /*! 102 | * \brief parseJSON 103 | */ 104 | void parseJSON(); 105 | 106 | /*! 107 | * \brief parseNode 108 | * \param node 109 | * \return 110 | */ 111 | QVariantMap parseNode( const QJsonObject& node ); 112 | 113 | /*! 114 | * \brief parseQBrush 115 | * \param qBrush 116 | * \return 117 | */ 118 | QBrush parseQBrush( const QJsonObject& qBrush ); 119 | 120 | /*! 121 | * \brief parseQColor 122 | * \param qColor 123 | * \return 124 | */ 125 | QColor parseQColor( const QJsonObject& qColor ); 126 | 127 | /*! 128 | * \brief parseQFont 129 | * \param qFont 130 | * \return 131 | */ 132 | QFont parseQFont( const QJsonObject& qFont ); 133 | 134 | // const QGradient *parseQGradient(const QScriptValue &qGradient); 135 | 136 | /*! 137 | * \brief parseQPen 138 | * \param qPen 139 | * \return 140 | */ 141 | QPen parseQPen( const QJsonObject& qPen ); 142 | 143 | /*! 144 | * \brief parseQPixmap 145 | * \param qPixmap 146 | * \return 147 | */ 148 | QPixmap parseQPixmap( const QString& qPixmap ); 149 | 150 | /*! 151 | * \brief parseQSize 152 | * \param qSize 153 | * \return 154 | */ 155 | QSize parseQSize( const QJsonArray& qSize ); 156 | 157 | /*! 158 | * \brief parseSocket 159 | * \param sock 160 | * \return 161 | */ 162 | QVariantMap parseSocket( const QJsonObject& sock ); 163 | #endif 164 | }; 165 | 166 | #endif // STYLEMANAGER_H 167 | -------------------------------------------------------------------------------- /NodeView/textlesssocket.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/NodeView_global.h" 6 | #include "NodeView/socket.h" 7 | 8 | #include 9 | 10 | class NodeView; 11 | 12 | /*! 13 | * \brief A subclass of QGraphicsPolygonItem that is used 14 | * to represent a node within a NodeView view. 15 | */ 16 | class NodeView_API TextlessSocket : public Socket { 17 | Q_OBJECT 18 | 19 | public: 20 | enum { Type = UserType + 6 }; 21 | 22 | /*! 23 | * \brief Constructor. 24 | * \param type Whether this is an input or output Socket. 25 | * \param parent The parent item of the Socket. 26 | */ 27 | TextlessSocket( NodeView* graphWidget, const SocketType type = Invalid, QGraphicsItem* parent = 0 ); 28 | 29 | /*! 30 | * \brief Does nothing since there is no text (will be removed when we Redo the system 31 | * \return an empty QBrush 32 | */ 33 | virtual QBrush labelBrush() const; 34 | 35 | /*! 36 | * \brief Does nothing since there is no text (will be removed when we Redo the system 37 | * \return an empty QPen 38 | */ 39 | virtual QPen labelPen() const; 40 | 41 | /*! 42 | * \brief Does nothing since there is no text (will be removed when we Redo the system 43 | */ 44 | virtual void setFont( const QFont font ); 45 | 46 | /*! 47 | * \brief Does nothing since there is no text (will be removed when we Redo the system 48 | */ 49 | virtual void setLabelBrush( const QBrush brush ); 50 | 51 | /*! 52 | * \brief Does nothing since there is no text (will be removed when we Redo the system 53 | */ 54 | virtual void setLabelPen( const QPen pen ); 55 | 56 | protected: 57 | /*! 58 | * \brief constructGroup 59 | */ 60 | virtual void constructGroup(); 61 | 62 | /*! 63 | * \brief resizeGroup 64 | * \param width 65 | */ 66 | virtual void resizeGroup( qreal width ); 67 | }; 68 | -------------------------------------------------------------------------------- /NodeView/trianglesocketshape.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/NodeView_global.h" 6 | 7 | #include 8 | 9 | class NodeView_API TriangleSocketShape : public QGraphicsPolygonItem { 10 | public: 11 | enum { Type = UserType + 25 }; 12 | 13 | enum Orientation { North, East, South, West, None }; 14 | 15 | protected: 16 | Orientation m_orientation; 17 | QRectF m_rect; 18 | 19 | public: 20 | /*! 21 | * \brief A shape for that allows us to have triangular shaped Sockets 22 | * \param parent 23 | */ 24 | TriangleSocketShape( QGraphicsItem* parent = 0 ); 25 | 26 | /*! 27 | * \brief Returns the orientation in which the triangle has a point facing 28 | * \return The Orientaiton 29 | */ 30 | Orientation orientation() const; 31 | 32 | /*! 33 | * \brief QRectF that the Triangle will be drawn within 34 | * \return The QRectf 35 | */ 36 | QRectF rect() const; 37 | 38 | /*! 39 | * \brief Sets the orientation in which the triangle has a point facing 40 | * \param orientation 41 | */ 42 | void setOrientation( Orientation orientation ); 43 | 44 | /*! 45 | * \brief Sets the QrectF that the Triangle will be drawn within 46 | * \param The QRectF 47 | */ 48 | void setRect( const QRectF& rect ); 49 | 50 | /*! 51 | * \brief Sets the QrectF that the Triangle will be drawn within 52 | * \param x X Coordinate of the left side of the rect 53 | * \param y Y Coordiante of the top side of the rect 54 | * \param width Width of the rect 55 | * \param height Height of the rect 56 | */ 57 | void setRect( qreal x, qreal y, qreal width, qreal height ); 58 | 59 | /*! 60 | * \brief type 61 | * \return 62 | */ 63 | virtual int type() const; 64 | 65 | protected: 66 | /*! 67 | * \brief generateTriangle 68 | */ 69 | void generateTriangle(); 70 | }; 71 | -------------------------------------------------------------------------------- /NodeView/widgetsocket.h: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #pragma once 4 | 5 | #include "NodeView/socket.h" 6 | 7 | #include 8 | 9 | class QWidget; 10 | class NodeView; 11 | 12 | class WidgetSocket : public Socket { 13 | Q_OBJECT 14 | protected: 15 | QGraphicsProxyWidget m_proxyWidget; 16 | 17 | public: 18 | /*! 19 | * \brief WidgetSocket 20 | * \param graphWidget 21 | * \param widget 22 | * \param type 23 | * \param label 24 | * \param parent 25 | */ 26 | explicit WidgetSocket( NodeView* graphWidget, QWidget* widget = 0, const SocketType type = Invalid, 27 | const QString label = "", QGraphicsItem* parent = 0 ); 28 | 29 | /*! 30 | * \brief Sets the Widget contained within the Socket 31 | * \param The widget contained within the socket 32 | */ 33 | void setWidget( QWidget* widget ); 34 | 35 | /*! 36 | * \brief update 37 | * \param rect 38 | */ 39 | void update( const QRectF& rect ); 40 | 41 | /*! 42 | * \brief Return a pointer to the widget within the socket 43 | * \return A pointer to the widget within the socket 44 | */ 45 | QWidget* widget() const; 46 | 47 | protected: 48 | /*! 49 | * \brief constructGroup 50 | */ 51 | virtual void constructGroup(); 52 | }; 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NodeView 2 | 3 | ## Overview 4 | 5 | NodeView is a Qt component for displaying graph structures in a GUI. 6 | 7 | ## Table of Contents 8 | 9 | - [Reporting Bugs/Feature Requests](#reporting-bugs/feature-requests) 10 | - [Security issue notifications](#security-issue-notifications) 11 | - [Contributing](#contributing) 12 | - [Code of Conduct](#code-of-conduct) 13 | - [Licensing](#licensing) 14 | 15 | ## Reporting Bugs/Feature Requests 16 | 17 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 18 | 19 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 20 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 21 | 22 | - A reproducible test case or series of steps 23 | - The version of our code being used 24 | - Any modifications you've made relevant to the bug 25 | - Anything unusual about your environment or deployment 26 | 27 | ## Security issue notifications 28 | 29 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 30 | 31 | ## Contributing 32 | 33 | Contributions to NodeView are encouraged. If you want to fix a problem, or want to enhance the library in any way, then 34 | we are happy to accept your contribution. Information on contributing to NodeView can be found 35 | [in CONTRIBUTING.md](CONTRIBUTING.md). 36 | 37 | ## Code of Conduct 38 | 39 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 40 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 41 | opensource-codeofconduct@amazon.com with any additional questions or comments. 42 | 43 | ## Licensing 44 | 45 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 46 | 47 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 48 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | from typing import Any 4 | from cpt.packager import ConanMultiPackager 5 | 6 | import argparse 7 | import platform 8 | import pprint 9 | 10 | 11 | VALID_MAYA_CONFIGS: dict[tuple[str, str], set[str]] = { 12 | ('Visual Studio', '16'): { '2022', '2023' }, 13 | ('gcc', '7'): { '2022', '2023' }, 14 | ('gcc', '9'): { '2022', '2023' }, 15 | ('apple-clang', '10.0'): { '2022', '2023' } 16 | } 17 | 18 | COMMON_PACKAGER_ARGS: dict[str, Any] = { 19 | 'build_types': ['Release'], 20 | 'archs': ['x86_64'], 21 | 'build_policy': 'missing' 22 | } 23 | 24 | WINDOWS_PACKAGER_ARGS: dict[str, Any] = { 25 | 'visual_versions': ['16'], 26 | 'visual_runtimes': ['MD'] 27 | } 28 | 29 | LINUX_PACKAGER_ARGS: dict[str, Any] = { 30 | 'gcc_versions': ['7'] 31 | } 32 | 33 | MACOS_PACKAGER_ARGS: dict[str, Any] = { 34 | 'apple_clang_versions': ['10.0'] 35 | } 36 | 37 | 38 | def parse_arguments() -> argparse.Namespace: 39 | parser = argparse.ArgumentParser() 40 | parser.add_argument('-u', '--username', default=None, help='The Conan username to use for the built package.') 41 | parser.add_argument('-c', '--channel', default=None, help='The Conan channel to use for the built package.') 42 | parser.add_argument('-o', '--option', action='append', dest='options', help='Specify package options to be used by the build.') 43 | parser.add_argument('--dry-run', action='store_true', help='Print the configurations that would be built without actually building them.') 44 | return parser.parse_args() 45 | 46 | def main() -> None: 47 | args = parse_arguments() 48 | 49 | packager_args = { 50 | 'username': args.username, 51 | 'channel': args.channel, 52 | 'options': args.options 53 | } 54 | packager_args.update(COMMON_PACKAGER_ARGS) 55 | 56 | if platform.system() == 'Windows': 57 | packager_args.update(WINDOWS_PACKAGER_ARGS) 58 | elif platform.system() == 'Linux': 59 | packager_args.update(LINUX_PACKAGER_ARGS) 60 | elif platform.system() == 'Darwin': 61 | packager_args.update(MACOS_PACKAGER_ARGS) 62 | else: 63 | raise Exception('Platform not supported.') 64 | 65 | builder = ConanMultiPackager(**packager_args) 66 | builder.add_common_builds(build_all_options_values=[ 67 | 'maya_version' 68 | ], pure_c=False) 69 | builder.remove_build_if( 70 | lambda build: build.options['nodeview:maya_version'] not in VALID_MAYA_CONFIGS[ 71 | (build.settings['compiler'], build.settings['compiler.version']) 72 | ]) 73 | # Remove the legacy libstdc++ build. 74 | if platform.system() == 'Linux': 75 | builder.remove_build_if(lambda build: build.settings['compiler.libcxx'] == 'libstdc++') 76 | 77 | if args.dry_run: 78 | pprint.pprint(builder.builds, indent=4) 79 | else: 80 | builder.run() 81 | 82 | 83 | if __name__ == '__main__': 84 | main() 85 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: Apache-2.0 3 | import os 4 | from typing import Any 5 | from conans import ConanFile, CMake 6 | 7 | 8 | VALID_MAYA_CONFIGS: dict[tuple[str, str], set[str]] = { 9 | ('Visual Studio', '16'): { '2022', '2023' }, 10 | ('gcc', '7'): { '2022', '2023' }, 11 | ('gcc', '9'): { '2022', '2023' }, 12 | ('apple-clang', '10.0'): { '2022', '2023' } 13 | } 14 | 15 | SETTINGS: dict[str, Any] = { 16 | 'os': ['Windows', 'Linux', 'Macos'], 17 | 'compiler': { 18 | 'Visual Studio': {'version': ['16']}, 19 | 'gcc': {'version': ['7', '9']}, 20 | 'apple-clang': {'version': ['10.0']} 21 | }, 22 | 'build_type': None, 23 | 'arch': 'x86_64' 24 | } 25 | 26 | TOOL_REQUIRES: list[str] = [ 27 | 'cmake/3.24.1', 28 | 'thinkboxcmlibrary/1.0.0' 29 | ] 30 | 31 | 32 | MAYA_QT_VERSIONS: dict[str, str] = { 33 | '2022': '5.15.2', 34 | '2023': '5.15.2' 35 | } 36 | 37 | 38 | class NodeViewConan(ConanFile): 39 | name: str = 'nodeview' 40 | version: str = '1.0.0' 41 | license: str = 'Apache-2.0' 42 | description: str = 'Shared code for Thinkbox\'s Maya plugins' 43 | settings: dict[str, Any] = SETTINGS 44 | tool_requires: list[str] = TOOL_REQUIRES 45 | generators: str | list[str] = 'cmake_find_package' 46 | options: dict[str, Any] = { 47 | 'maya_version': ['2022', '2023'], 48 | 'build_examples': [True, False] 49 | } 50 | default_options: dict[str, Any] = { 51 | 'qt:shared': True, 52 | 'qt:openssl': False, 53 | 'qt:with_pcre2': False, 54 | 'qt:with_harfbuzz': False, 55 | 'qt:with_sqlite3': False, 56 | 'qt:with_pq': False, 57 | 'qt:with_odbc': False, 58 | 'qt:with_openal': False, 59 | 'qt:with_zstd': False, 60 | 'qt:with_md4c': False 61 | } 62 | 63 | def requirements(self) -> None: 64 | self.requires(f'qt/{MAYA_QT_VERSIONS[str(self.options.maya_version)]}') 65 | 66 | def configure(self) -> None: 67 | if self.options.maya_version == None: 68 | self.options.maya_version = '2022' 69 | if self.options.build_examples == None: 70 | self.options.build_examples = False 71 | 72 | def validate(self) -> None: 73 | compiler = str(self.settings.compiler) 74 | compiler_version = str(self.settings.compiler.version) 75 | compiler_tuple = (compiler, compiler_version) 76 | maya_version = str(self.options.maya_version) 77 | if maya_version not in VALID_MAYA_CONFIGS[compiler_tuple]: 78 | raise Exception(f'{str(compiler_tuple)} is not a valid configuration for Maya {maya_version}') 79 | 80 | def build(self) -> None: 81 | cmake = CMake(self) 82 | cmake.configure(defs={ 83 | 'MAYA_VERSION': self.options.maya_version, 84 | 'BUILD_EXAMPLES': 'ON' if self.options.build_examples else 'OFF' 85 | }) 86 | cmake.build() 87 | 88 | def export_sources(self) -> None: 89 | self.copy('**.h', src='', dst='') 90 | self.copy('**.hpp', src='', dst='') 91 | self.copy('**.cpp', src='', dst='') 92 | self.copy('**.cmake', src='', dst='') 93 | self.copy('*', src='resources', dst='resources') 94 | self.copy('*', src='') 95 | self.copy('CMakeLists.txt', src='', dst='') 96 | self.copy('NOTICE.txt', src='', dst='') 97 | self.copy('LICENSE.txt', src='', dst='') 98 | 99 | def imports(self) -> None: 100 | # Copy DLLs to the Example binary directory 101 | self.copy('*.dll', dst='Release', src='bin') 102 | 103 | def package(self) -> None: 104 | cmake = CMake(self) 105 | cmake.install() 106 | 107 | with open(os.path.join(self.source_folder, 'NOTICE.txt'), 'r', encoding='utf8') as notice_file: 108 | notice_contents = notice_file.readlines() 109 | with open(os.path.join(self.source_folder, 'LICENSE.txt'), 'r', encoding='utf8') as license_file: 110 | license_contents = license_file.readlines() 111 | os.makedirs(os.path.join(self.package_folder, 'licenses'), exist_ok=True) 112 | with open(os.path.join(self.package_folder, 'licenses', 'LICENSE'), 'w', encoding='utf8') as cat_license_file: 113 | cat_license_file.writelines(notice_contents) 114 | cat_license_file.writelines(license_contents) 115 | 116 | def deploy(self) -> None: 117 | self.copy('*', dst='bin', src='bin') 118 | self.copy('*', dst='lib', src='lib') 119 | self.copy('*', dst='include', src='include') 120 | 121 | def package_info(self) -> None: 122 | self.cpp_info.libs = ['nodeview'] 123 | -------------------------------------------------------------------------------- /resources/Minus_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/thinkbox-node-view/7a7e1b80a2648a9c27392c0481f0474a3c33d4af/resources/Minus_16.png -------------------------------------------------------------------------------- /resources/Minus_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/thinkbox-node-view/7a7e1b80a2648a9c27392c0481f0474a3c33d4af/resources/Minus_9.png -------------------------------------------------------------------------------- /resources/Plus_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/thinkbox-node-view/7a7e1b80a2648a9c27392c0481f0474a3c33d4af/resources/Plus_16.png -------------------------------------------------------------------------------- /resources/Plus_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws/thinkbox-node-view/7a7e1b80a2648a9c27392c0481f0474a3c33d4af/resources/Plus_9.png -------------------------------------------------------------------------------- /resources/images.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Minus_9.png 4 | Plus_9.png 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/connection.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/connection.h" 4 | #include "NodeView/node.h" 5 | #include "NodeView/nodeview.h" 6 | #include "NodeView/socket.h" 7 | 8 | #include 9 | 10 | #include 11 | #if QT_VERSION >= 0x050000 12 | #ifndef Qt5 13 | #define Qt5 14 | #endif 15 | #else 16 | #ifndef Qt4 17 | #define Qt4 18 | #endif 19 | #endif 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef Qt5 26 | #include 27 | #endif 28 | static const double Pi = 3.14159265358979323846264338327950288419717; 29 | static double TwoPi = 2.0 * Pi; 30 | int Connection::NextZConnection = INT_MIN + 1; 31 | /***********************Public Members***********************/ 32 | 33 | Connection::Connection( NodeView* graphWidget, QGraphicsItem* parent, QGraphicsScene* scene ) 34 | #ifdef Qt5 35 | : QGraphicsPathItem( parent ) 36 | , m_source( 0 ) 37 | , m_dest( 0 ) 38 | , m_graphWidget( graphWidget ) { 39 | scene->addItem( this ); 40 | #else // Qt4 41 | : QGraphicsPathItem( parent, scene ) 42 | , m_source( 0 ) 43 | , m_dest( 0 ) 44 | , m_graphWidget( graphWidget ) { 45 | #endif 46 | 47 | setPen( QPen( Qt::darkGray, 3, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin ) ); 48 | setBrush( QBrush( Qt::darkGray ) ); 49 | 50 | Connection::NextZConnection++; 51 | if( Connection::NextZConnection == 0 ) 52 | Connection::NextZConnection = INT_MIN + 1; 53 | setZValue( Connection::NextZConnection ); 54 | 55 | setFlag( ItemIsSelectable ); 56 | setAcceptHoverEvents( true ); 57 | m_arrowSize = 15; 58 | m_arrowPosition = 0.5; 59 | m_curvature = 0.5; 60 | m_dragDistance = 20.0; 61 | 62 | setSelectedBrush( QBrush( Qt::white ) ); 63 | setSelectedPen( QPen( Qt::white, 3, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin ) ); 64 | 65 | m_animation = new QPropertyAnimation( this, "color", this ); 66 | m_animationDuration = 250; 67 | m_animationLightness = 175; 68 | m_animationEnabled = true; 69 | 70 | m_graphWidget->registerStyleableItem( this ); 71 | } 72 | 73 | Connection::~Connection() { 74 | // if (this->m_source != 0) 75 | // this->m_source->removeConnection(QSharedPointer(this)); 76 | // if (this->m_dest != 0) 77 | // this->m_dest->removeConnection(QSharedPointer(this)); 78 | // m_source->removeConnection(this); 79 | // m_dest->removeConnection(this); 80 | // qDebug() << "Connection Destroyed"; 81 | } 82 | 83 | int Connection::animationDuration() const { return this->m_animationDuration; } 84 | 85 | bool Connection::animationEnabled() const { return this->m_animationEnabled; } 86 | 87 | int Connection::animationLightness() const { return this->m_animationLightness; } 88 | 89 | qreal Connection::arrowPositionPercent() const { return this->m_arrowPosition; } 90 | 91 | qreal Connection::arrowSize() const { return this->m_arrowSize; } 92 | 93 | QColor Connection::color() const { return this->pen().color(); } 94 | 95 | qreal Connection::curvature() const { return this->m_curvature; } 96 | 97 | Socket* Connection::destinationSocket() const { return this->m_dest; } 98 | 99 | qreal Connection::dragDistance() const { return this->m_dragDistance; } 100 | 101 | void Connection::removeDestinationSocket( const QPointF& dragPoint ) { 102 | disconnect( this, SIGNAL( destroyed( QObject* ) ), this->m_dest, SLOT( removeConnection( QObject* ) ) ); 103 | this->m_dest->removeConnection( this ); 104 | this->m_dest = 0; 105 | this->setDragPoint( dragPoint ); 106 | this->updatePosition(); 107 | } 108 | 109 | void Connection::removeSourceSocket( const QPointF& dragPoint ) { 110 | disconnect( this, SIGNAL( destroyed( QObject* ) ), this->m_source, SLOT( removeConnection( QObject* ) ) ); 111 | this->m_source->removeConnection( this ); 112 | this->m_source = 0; 113 | this->setDragPoint( dragPoint ); 114 | this->updatePosition(); 115 | } 116 | 117 | QBrush Connection::selectedBrush() const { return this->m_selectedBrush; } 118 | 119 | QColor Connection::selectedColor() const { return this->selectedPen().color(); } 120 | 121 | QPen Connection::selectedPen() const { return this->m_selectedPen; } 122 | 123 | void Connection::setAnimationDuration( int duration ) { this->m_animationDuration = duration; } 124 | 125 | void Connection::setAnimationEnabled( bool enabled ) { this->m_animationEnabled = enabled; } 126 | 127 | void Connection::setAnimationLightness( int lightness ) { this->m_animationLightness = lightness; } 128 | 129 | void Connection::setArrowPositionPercent( const qreal position ) { 130 | if( position > 1 ) 131 | this->m_arrowPosition = 1.0; 132 | else if( position < 0 ) 133 | this->m_arrowPosition = 0.0; 134 | else 135 | this->m_arrowPosition = position; 136 | this->updatePosition(); 137 | } 138 | 139 | void Connection::setArrowSize( const qreal size ) { 140 | this->m_arrowSize = size; 141 | this->updatePosition(); 142 | } 143 | 144 | void Connection::setColor( const QColor& color ) { 145 | QBrush brush = this->brush(); 146 | QPen pen = this->pen(); 147 | 148 | brush.setColor( color ); 149 | pen.setColor( color ); 150 | 151 | this->setBrush( brush ); 152 | this->setPen( pen ); 153 | } 154 | 155 | void Connection::setCurvature( qreal curvature ) { 156 | this->m_curvature = curvature; 157 | this->updatePosition(); 158 | } 159 | 160 | void Connection::setDestinationSocket( Socket* destination ) { 161 | this->m_dest = destination; 162 | connect( this, SIGNAL( destroyed( QObject* ) ), destination, SLOT( removeConnection( QObject* ) ) ); 163 | destination->addConnection( this ); 164 | this->updatePosition(); 165 | } 166 | 167 | void Connection::setDragDistance( qreal dragDistance ) { this->m_dragDistance = dragDistance; } 168 | 169 | void Connection::setDragPoint( const QPointF& dragPoint ) { 170 | this->m_dragPoint = dragPoint; 171 | this->updatePosition(); 172 | } 173 | 174 | void Connection::setObjectName( const QString& name ) { 175 | QObject::setObjectName( name ); 176 | emit requiresStyling( this ); 177 | } 178 | 179 | void Connection::setSelectedBrush( const QBrush& brush ) { 180 | this->m_selectedBrush = brush; 181 | if( this->isSelected() ) 182 | this->update(); 183 | } 184 | 185 | void Connection::setSelectedColor( const QColor& color ) { 186 | QBrush brush = this->selectedBrush(); 187 | QPen pen = this->selectedPen(); 188 | 189 | brush.setColor( color ); 190 | pen.setColor( color ); 191 | 192 | this->setSelectedBrush( brush ); 193 | this->setSelectedPen( pen ); 194 | } 195 | 196 | void Connection::setSelectedPen( const QPen& pen ) { 197 | this->m_selectedPen = pen; 198 | if( this->isSelected() ) 199 | this->update(); 200 | } 201 | 202 | void Connection::setSourceSocket( Socket* source ) { 203 | this->m_source = source; 204 | connect( this, SIGNAL( destroyed( QObject* ) ), source, SLOT( removeConnection( QObject* ) ) ); 205 | source->addConnection( this ); 206 | this->updatePosition(); 207 | } 208 | 209 | QPainterPath Connection::shape() const { 210 | QPainterPathStroker stroker; 211 | 212 | stroker.setWidth( this->pen().width() * 3 ); 213 | return stroker.createStroke( this->path() ); 214 | } 215 | 216 | Socket* Connection::sourceSocket() const { return m_source; } 217 | 218 | int Connection::type() const { return Type; } 219 | 220 | void Connection::updatePosition() { 221 | this->calculatePath(); 222 | this->setPath( this->m_path ); 223 | this->update( this->boundingRect() ); 224 | } 225 | 226 | /***********************Protected Members********************/ 227 | 228 | void Connection::hoverEnterEvent( QGraphicsSceneHoverEvent* event ) { 229 | Q_UNUSED( event ); 230 | 231 | if( this->m_animationEnabled ) { 232 | this->m_graphWidget->onConnectionMouseOver( this ); 233 | 234 | if( this->m_animation->state() == QAbstractAnimation::Stopped ) { 235 | this->m_animation->setStartValue( this->color() ); 236 | this->m_animation->setEndValue( this->color().lighter( this->m_animationLightness ) ); 237 | this->m_animation->setDuration( this->m_animationDuration ); 238 | } else if( this->m_animation->state() == QAbstractAnimation::Running ) 239 | this->m_animation->pause(); 240 | 241 | this->m_animation->setDirection( QPropertyAnimation::Forward ); 242 | 243 | if( this->m_animation->state() == QAbstractAnimation::Paused ) 244 | this->m_animation->resume(); 245 | else 246 | this->m_animation->start(); 247 | } 248 | } 249 | 250 | void Connection::hoverLeaveEvent( QGraphicsSceneHoverEvent* event ) { 251 | Q_UNUSED( event ); 252 | 253 | if( this->m_animationEnabled ) { 254 | if( this->m_animation->state() == QAbstractAnimation::Running ) 255 | this->m_animation->pause(); 256 | 257 | this->m_animation->setDirection( QPropertyAnimation::Backward ); 258 | 259 | if( this->m_animation->state() == QAbstractAnimation::Paused ) 260 | this->m_animation->resume(); 261 | else 262 | this->m_animation->start(); 263 | } 264 | } 265 | 266 | QVariant Connection::itemChange( GraphicsItemChange change, const QVariant& value ) { 267 | switch( change ) { 268 | case ItemSelectedChange: 269 | if( value == true ) { 270 | Connection::NextZConnection++; 271 | if( Connection::NextZConnection == 0 ) 272 | Connection::NextZConnection = INT_MIN + 1; 273 | setZValue( Connection::NextZConnection ); 274 | } 275 | break; 276 | default: 277 | break; 278 | } 279 | 280 | return QGraphicsItem::itemChange( change, value ); 281 | } 282 | 283 | void Connection::mouseMoveEvent( QGraphicsSceneMouseEvent* event ) { 284 | if( QLineF( event->screenPos(), event->buttonDownScreenPos( Qt::LeftButton ) ).length() < this->m_dragDistance && 285 | ( this->m_source != 0 && this->m_dest != 0 ) ) 286 | return; 287 | 288 | if( this->m_source != 0 && this->m_dest != 0 ) { 289 | qreal distanceToStart = QLineF( event->scenePos(), this->m_source->socketLocation() ).length(); 290 | qreal distanceToEnd = QLineF( event->scenePos(), this->m_dest->socketLocation() ).length(); 291 | 292 | foreach( Connection* con, m_graphWidget->selectedConnections() ) { 293 | if( con != this ) { 294 | con->setSelected( false ); 295 | } 296 | } 297 | 298 | if( distanceToStart > distanceToEnd ) { 299 | this->removeDestinationSocket( event->scenePos() ); 300 | } else { 301 | this->removeSourceSocket( event->scenePos() ); 302 | } 303 | } else { 304 | this->setDragPoint( event->scenePos() ); 305 | } 306 | 307 | QGraphicsPathItem::mouseMoveEvent( event ); 308 | } 309 | 310 | void Connection::mouseReleaseEvent( QGraphicsSceneMouseEvent* event ) { 311 | if( this->m_source == 0 || this->m_dest == 0 ) { 312 | Socket* connectedSocket = 0; 313 | 314 | if( this->m_source != 0 ) 315 | connectedSocket = this->m_source; 316 | else if( this->m_dest != 0 ) 317 | connectedSocket = this->m_dest; 318 | 319 | QList items = this->scene()->collidingItems( this ); 320 | bool found = false; 321 | foreach( QGraphicsItem* item, items ) { 322 | Socket* socket = qgraphicsitem_cast( item ); 323 | 324 | if( connectedSocket && socket != 0 && socket != connectedSocket && 325 | socket->isConnectionPointUnderMouse( event ) ) { 326 | connectedSocket->createConnection( socket ); 327 | found = true; 328 | break; 329 | } 330 | } 331 | if( !found ) { 332 | 333 | this->m_graphWidget->onDisconnectToEmpty( connectedSocket, event->scenePos() ); 334 | } 335 | this->deleteLater(); 336 | } 337 | 338 | QGraphicsPathItem::mouseReleaseEvent( event ); 339 | } 340 | 341 | void Connection::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) { 342 | Q_UNUSED( option ); 343 | Q_UNUSED( widget ); 344 | 345 | // Draw the outline of the path and the arrow head 346 | if( this->isSelected() ) { 347 | painter->setPen( this->m_selectedPen ); 348 | } else { 349 | painter->setPen( this->pen() ); 350 | } 351 | 352 | painter->drawPath( this->path() ); 353 | 354 | // Fill in the arrow head 355 | if( this->path().toSubpathPolygons().count() > 1 ) { 356 | QPolygonF arrowHead = this->path().toSubpathPolygons()[1]; 357 | 358 | if( this->isSelected() ) { 359 | painter->setBrush( this->m_selectedBrush ); 360 | } else { 361 | painter->setBrush( this->brush() ); 362 | } 363 | painter->drawPolygon( arrowHead ); 364 | } 365 | } 366 | 367 | /***********************Private Members**********************/ 368 | 369 | void Connection::calculatePath() { 370 | QPointF endPoint; 371 | QPointF startPoint; /* = m_source->mapToScene(m_source->socketLocation());*/ 372 | 373 | if( this->m_source != 0 ) 374 | startPoint = this->m_source->socketLocation(); 375 | /*this->m_source->mapToScene(this->m_source->socketLocation());*/ // this->mapFromItem(this->m_source->parentItem(), 376 | // this->m_source->socketLocation()); 377 | // //this->m_source->mapToScene(this->m_source->socketLocation()); 378 | else 379 | startPoint = this->m_dragPoint; 380 | 381 | if( this->m_dest != 0 ) 382 | endPoint = this->m_dest->socketLocation(); // this->mapFromItem(this->m_dest, this->m_dest->socketLocation()); 383 | else 384 | endPoint = this->m_dragPoint; 385 | 386 | if( endPoint == m_prevEnd && startPoint == m_prevStart && m_forceRecalc == false ) 387 | return; 388 | 389 | m_prevEnd = endPoint; 390 | m_prevStart = startPoint; 391 | 392 | // QPointF startPoint = m_source->mapToScene(m_source->boundingRect().center()); 393 | // QPainterPath *path = new QPainterPath(startPoint); 394 | this->m_path = QPainterPath( startPoint ); 395 | this->m_path.setFillRule( Qt::WindingFill ); 396 | 397 | // Draws the curved path between two nodes. 398 | QPointF sourceAdjust = startPoint; 399 | QPointF destAdjust = endPoint; 400 | 401 | if( m_curvature > 0 ) { 402 | qreal adjust = QLineF( startPoint, endPoint ).dx() * this->m_curvature; 403 | 404 | sourceAdjust.setX( startPoint.x() + adjust ); 405 | destAdjust.setX( endPoint.x() - adjust ); 406 | this->m_path.cubicTo( sourceAdjust, destAdjust, endPoint ); 407 | } else { 408 | this->m_path.lineTo( endPoint ); 409 | } 410 | 411 | // Draws the arrow in the center of the path. 412 | qreal pathLength = this->m_path.length(); 413 | if( pathLength > this->m_arrowSize * 2 && this->m_arrowSize > 0 ) { 414 | qreal arrowLengthPercent = ( this->m_arrowSize / 2 ) / pathLength; 415 | QPolygonF arrowHead; 416 | QPointF arrowStart = this->m_path.pointAtPercent( 417 | this->m_arrowPosition + arrowLengthPercent > 1 ? 1 : this->m_arrowPosition + arrowLengthPercent ); 418 | QPointF arrowEnd = this->m_path.pointAtPercent( 419 | this->m_arrowPosition - arrowLengthPercent < 0 ? 0 : this->m_arrowPosition - arrowLengthPercent ); 420 | QLineF line( arrowStart, arrowEnd ); 421 | 422 | double angle = ::acos( line.dx() / line.length() ); 423 | if( line.dy() >= 0 ) 424 | angle = TwoPi - angle; 425 | 426 | QPointF arrowP1 = 427 | line.p1() + QPointF( sin( angle + Pi / 3 ) * this->m_arrowSize, cos( angle + Pi / 3 ) * this->m_arrowSize ); 428 | QPointF arrowP2 = line.p1() + QPointF( sin( angle + Pi - Pi / 3 ) * this->m_arrowSize, 429 | cos( angle + Pi - Pi / 3 ) * this->m_arrowSize ); 430 | 431 | arrowHead << line.p1() << arrowP1 << arrowP2; 432 | this->m_path.moveTo( line.p1() ); 433 | this->m_path.addPolygon( arrowHead ); 434 | this->m_path.closeSubpath(); 435 | } 436 | } 437 | -------------------------------------------------------------------------------- /src/expandablecontainer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/expandablecontainer.h" 4 | #include "NodeView/connection.h" 5 | #include "NodeView/node.h" 6 | #include "NodeView/nodeview.h" 7 | #include "NodeView/sidesocketnode.h" 8 | 9 | #include 10 | #if QT_VERSION >= 0x050000 11 | #ifndef Qt5 12 | #define Qt5 13 | #endif 14 | #else 15 | #ifndef Qt4 16 | #define Qt4 17 | #endif 18 | #endif 19 | 20 | #ifdef Qt5 21 | #include 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | /***********************Public Members***********************/ 29 | 30 | ExpandableContainer::ExpandableContainer( QGraphicsItem* containedItem, const QString label, QGraphicsItem* parent, 31 | ExpansionButtonPosition expPos, TitleLocation titleLoc ) 32 | : QGraphicsItemGroup( parent ) { 33 | m_labelText = label; 34 | m_label.setParentItem( this ); 35 | m_label.setText( m_labelText ); 36 | m_titleLoc = titleLoc; 37 | m_expansionPos = expPos; 38 | setToolTip( label ); 39 | m_containedItem = containedItem; 40 | m_contentsVisible = true; 41 | m_width = 5000; 42 | m_picture.setPixmap( QPixmap( ":Images/Minus_9.png" ) ); 43 | m_picture.setParentItem( this ); 44 | m_picture.setShapeMode( QGraphicsPixmapItem::BoundingRectShape ); 45 | m_leftAdjust = m_rightAdjust = 0; 46 | } 47 | 48 | ExpandableContainer::ExpansionButtonPosition ExpandableContainer::buttonPosition() { return this->m_expansionPos; } 49 | 50 | void ExpandableContainer::expandArea( bool expanded ) { 51 | this->m_contentsVisible = expanded; 52 | if( this->m_contentsVisible ) 53 | this->m_picture.setPixmap( QPixmap( ":Images/Minus_9.png" ) ); 54 | else 55 | this->m_picture.setPixmap( QPixmap( ":Images/Plus_9.png" ) ); 56 | } 57 | 58 | QFont ExpandableContainer::font() const { return this->m_label.font(); } 59 | 60 | QGraphicsItem* ExpandableContainer::getContainedItem() { return this->m_containedItem; } 61 | 62 | QString ExpandableContainer::label() const { return this->m_labelText; } 63 | 64 | void ExpandableContainer::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event ) { 65 | if( this->isPixmapUnderMouse() || this->m_label.isUnderMouse() ) 66 | event->accept(); 67 | else 68 | QGraphicsItemGroup::mouseDoubleClickEvent( event ); 69 | } 70 | 71 | void ExpandableContainer::mouseReleaseEvent( QGraphicsSceneMouseEvent* event ) { 72 | Q_UNUSED( event ); 73 | if( this->m_contentsVisible ) { 74 | this->expandArea( false ); 75 | } else { 76 | this->expandArea( true ); 77 | } 78 | dynamic_cast( this->parentItem() )->setAreaExpanded(); 79 | dynamic_cast( this->parentItem() )->redrawNode(); 80 | } 81 | 82 | void ExpandableContainer::setButtonLocation( ExpansionButtonPosition expansionPos ) { 83 | this->m_expansionPos = expansionPos; 84 | this->update(); 85 | } 86 | 87 | void ExpandableContainer::setFont( const QFont font ) { 88 | this->m_label.setFont( font ); 89 | this->update(); 90 | } 91 | 92 | void ExpandableContainer::setLabel( const QString label ) { 93 | this->m_labelText = label; 94 | this->update(); 95 | } 96 | 97 | void ExpandableContainer::setLeftAdjust( int adjust ) { this->m_leftAdjust = adjust; } 98 | 99 | void ExpandableContainer::setTitleBrush( QBrush brush ) { this->m_label.setBrush( brush ); } 100 | 101 | void ExpandableContainer::setTitleLocation( TitleLocation titleLoc ) { 102 | this->m_titleLoc = titleLoc; 103 | this->update(); 104 | } 105 | 106 | void ExpandableContainer::setWidth( int width ) { this->m_width = width; } 107 | 108 | QBrush ExpandableContainer::titleBrush() const { return this->m_label.brush(); } 109 | 110 | qreal ExpandableContainer::titleHeight() { 111 | return qMax( this->m_label.boundingRect().height(), m_picture.boundingRect().height() ); 112 | } 113 | 114 | ExpandableContainer::TitleLocation ExpandableContainer::titleLocation() { return this->m_titleLoc; } 115 | 116 | int ExpandableContainer::type() const { return Type; } 117 | 118 | void ExpandableContainer::update() { constructGroup(); } 119 | 120 | /***********************Protected Members********************/ 121 | 122 | bool ExpandableContainer::isPixmapUnderMouse() const { 123 | return this->m_picture.isUnderMouse(); 124 | 125 | return false; 126 | } 127 | 128 | void ExpandableContainer::mousePressEvent( QGraphicsSceneMouseEvent* event ) { 129 | if( this->isPixmapUnderMouse() || this->m_label.isUnderMouse() ) 130 | event->accept(); 131 | else 132 | QGraphicsItemGroup::mousePressEvent( event ); 133 | } 134 | 135 | /***********************Private Members**********************/ 136 | 137 | void ExpandableContainer::constructGroup() { 138 | this->m_label.setText( this->m_labelText ); 139 | qreal height = titleHeight(); 140 | QRectF boundingRect = this->m_label.boundingRect(); 141 | boundingRect.setWidth( this->m_width ); 142 | boundingRect.setHeight( 143 | qMax( height, height + this->m_contentsVisible ? this->m_containedItem->boundingRect().height() : 0 ) ); 144 | 145 | QFontMetricsF fm( this->m_label.font() ); 146 | 147 | this->removeFromGroup( this->m_containedItem ); 148 | this->removeFromGroup( &( this->m_label ) ); 149 | 150 | this->addToGroup( &( this->m_label ) ); 151 | this->addToGroup( &( this->m_picture ) ); 152 | QString elidedText = fm.elidedText( this->m_labelText, Qt::ElideRight, boundingRect.width() - 20 ); 153 | 154 | this->m_label.setText( elidedText ); 155 | if( this->m_expansionPos == ButtonRight ) { 156 | 157 | if( this->m_titleLoc == TitleLeft ) { 158 | this->m_label.setPos( this->m_label.mapToParent( 0, 0 ) ); 159 | } else if( this->m_titleLoc == TitleRight ) { 160 | qreal pos = boundingRect.right() - m_picture.boundingRect().width() - 3 - fm.width( elidedText ); 161 | this->m_label.setPos( pos, 0 ); 162 | } else { 163 | qreal pos = boundingRect.right() - m_picture.boundingRect().width() - 3 - fm.width( elidedText ); 164 | pos = pos / 2; 165 | this->m_label.setPos( pos, 0 ); 166 | } 167 | 168 | this->m_picture.setPos( boundingRect.right() - m_picture.boundingRect().width(), m_label.boundingRect().top() ); 169 | 170 | } else { 171 | this->m_picture.setPos( 0, 0 ); 172 | 173 | if( this->m_titleLoc == TitleLeft ) { 174 | this->m_label.setPos( this->m_picture.mapToParent( m_picture.boundingRect().width() + 3, 0 ) ); 175 | } else if( this->m_titleLoc == TitleRight ) { 176 | qreal pos = boundingRect.right() - fm.width( elidedText ); 177 | this->m_label.setPos( this->m_picture.mapToParent( pos, boundingRect.top() ) ); 178 | } else { 179 | qreal pos = boundingRect.right() - fm.width( elidedText ) - m_picture.boundingRect().width() - 3; 180 | pos = pos / 2 + m_picture.boundingRect().width() - 3; 181 | this->m_label.setPos( pos, 0 ); 182 | } 183 | 184 | // this->m_label.setPos(m_picture.boundingRect().right(),m_picture.boundingRect().top()); 185 | } 186 | 187 | if( this->m_contentsVisible ) { 188 | this->m_containedItem->setVisible( true ); 189 | this->m_containedItem->setPos( 190 | this->mapToParent( this->m_leftAdjust, qMax( this->m_picture.boundingRect().bottom(), 191 | this->m_label.boundingRect().bottom() ) ) ); 192 | this->addToGroup( this->m_containedItem ); 193 | } else { 194 | this->m_containedItem->setVisible( false ); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/fullLinesocket.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/fulllinesocket.h" 4 | #include "NodeView/connection.h" 5 | #include "NodeView/nodeview.h" 6 | #include "NodeView/trianglesocketshape.h" 7 | 8 | #include 9 | #include 10 | 11 | /***********************Public Members***********************/ 12 | 13 | FullLineSocket::FullLineSocket( NodeView* graphWidget, const SocketType type, const QString label, 14 | QGraphicsItem* parent ) 15 | : Socket( graphWidget, type, label, parent ) { 16 | setSocketShape( Socket::Square ); 17 | this->m_label->setZValue( this->m_shape->zValue() + 1 ); 18 | 19 | if( this->m_socketType == Socket::Input ) { 20 | this->m_labelPos = Left; 21 | } else { 22 | this->m_labelPos = Right; 23 | } 24 | constructGroup(); 25 | } 26 | 27 | FullLineSocket::LabelPos FullLineSocket::labelPos() { return this->m_labelPos; } 28 | 29 | void FullLineSocket::setLabelPos( LabelPos pos ) { 30 | this->m_labelPos = pos; 31 | this->constructGroup(); 32 | } 33 | 34 | int FullLineSocket::type() const { return Type; } 35 | 36 | void FullLineSocket::mouseMoveEvent( QGraphicsSceneMouseEvent* event ) { QGraphicsItemGroup::mouseMoveEvent( event ); } 37 | 38 | void FullLineSocket::mousePressEvent( QGraphicsSceneMouseEvent* event ) { 39 | QGraphicsItemGroup::mousePressEvent( event ); 40 | } 41 | 42 | void FullLineSocket::mouseReleaseEvent( QGraphicsSceneMouseEvent* event ) { 43 | QGraphicsItemGroup::mouseReleaseEvent( event ); 44 | } 45 | 46 | void FullLineSocket::resizeGroup( qreal width ) { 47 | this->m_label->setPlainText( this->m_labelText ); 48 | QRectF shapeRect; 49 | QRectF boundingRect = this->m_label->boundingRect(); 50 | QGraphicsRectItem* squareShape = 0; 51 | QGraphicsEllipseItem* circleShape = 0; 52 | TriangleSocketShape* triangleShape = 0; 53 | QFontMetricsF fm( this->m_label->font() ); 54 | 55 | this->removeFromGroup( this->m_shape ); 56 | this->removeFromGroup( this->m_label ); 57 | 58 | boundingRect.setWidth( width ); 59 | shapeRect.setWidth( boundingRect.width() ); 60 | this->m_label->setPlainText( fm.elidedText( this->m_labelText, Qt::ElideRight, boundingRect.width() ) ); 61 | 62 | if( this->m_socketShape == Square ) 63 | squareShape = qgraphicsitem_cast( this->m_shape ); 64 | else if( this->m_socketShape == Circle ) 65 | circleShape = qgraphicsitem_cast( this->m_shape ); 66 | else if( this->m_socketShape == Triangle ) 67 | triangleShape = qgraphicsitem_cast( this->m_shape ); 68 | 69 | if( this->m_socketType == Output ) { 70 | this->m_label->setPos( this->pos() ); 71 | 72 | shapeRect.setRect( boundingRect.topLeft().x() - boundingRect.width(), boundingRect.topRight().y(), 73 | boundingRect.width(), this->m_socketShapeSize ); 74 | 75 | if( squareShape ) 76 | squareShape->setRect( shapeRect ); 77 | else if( circleShape ) 78 | circleShape->setRect( shapeRect ); 79 | else if( triangleShape ) 80 | triangleShape->setRect( shapeRect ); 81 | } else { 82 | shapeRect.setRect( 0, 0, boundingRect.width(), this->m_socketShapeSize ); 83 | 84 | if( squareShape ) 85 | squareShape->setRect( shapeRect ); 86 | else if( circleShape ) 87 | circleShape->setRect( shapeRect ); 88 | else if( triangleShape ) 89 | triangleShape->setRect( shapeRect ); 90 | } 91 | 92 | if( this->m_labelPos == Left ) { 93 | this->m_label->setPos( 94 | this->m_shape->mapToParent( this->m_shape->boundingRect().topLeft().x() + fm.width( ' ' ), 95 | this->m_shape->boundingRect().center().y() - ( fm.height() / 2 ) ) ); 96 | } else if( this->m_labelPos == Right ) { 97 | this->m_label->setPos( this->m_shape->mapToParent( 98 | this->m_shape->boundingRect().topRight().x() - fm.width( ' ' ) - fm.width( this->m_labelText ), 99 | this->m_shape->boundingRect().center().y() - ( fm.height() / 2 ) ) ); 100 | } else { 101 | this->m_label->setPos( this->m_shape->mapToParent( 102 | this->m_shape->boundingRect().topRight().x() - fm.width( ' ' ) - 103 | ( ( qMin( fm.width( this->m_labelText ), boundingRect.width() - 3 * fm.width( ' ' ) ) + 104 | this->m_shape->boundingRect().width() ) / 105 | 2 ), 106 | this->m_shape->boundingRect().center().y() - ( fm.height() / 2 ) ) ); 107 | } 108 | 109 | this->addToGroup( this->m_shape ); 110 | this->addToGroup( this->m_label ); 111 | } 112 | 113 | void FullLineSocket::constructGroup() { 114 | this->m_label->setPlainText( this->m_labelText ); 115 | QRectF shapeRect; 116 | QRectF boundingRect = this->m_label->boundingRect(); 117 | QGraphicsRectItem* squareShape = 0; 118 | QGraphicsEllipseItem* circleShape = 0; 119 | TriangleSocketShape* triangleShape = 0; 120 | QFontMetricsF fm( this->m_label->font() ); 121 | 122 | this->removeFromGroup( this->m_shape ); 123 | this->removeFromGroup( this->m_label ); 124 | 125 | boundingRect.setWidth( this->parentItem()->shape().boundingRect().width() ); 126 | shapeRect.setWidth( boundingRect.width() ); 127 | this->m_label->setPlainText( fm.elidedText( this->m_labelText, Qt::ElideRight, boundingRect.width() ) ); 128 | 129 | if( this->m_socketShape == Square ) 130 | squareShape = qgraphicsitem_cast( this->m_shape ); 131 | else if( this->m_socketShape == Circle ) 132 | circleShape = qgraphicsitem_cast( this->m_shape ); 133 | else if( this->m_socketShape == Triangle ) 134 | triangleShape = qgraphicsitem_cast( this->m_shape ); 135 | 136 | if( this->m_socketType == Output ) { 137 | this->m_label->setPos( this->pos() ); 138 | 139 | shapeRect.setRect( boundingRect.topLeft().x() - boundingRect.width(), boundingRect.topRight().y(), 140 | boundingRect.width(), this->m_socketShapeSize ); 141 | 142 | if( squareShape ) 143 | squareShape->setRect( shapeRect ); 144 | else if( circleShape ) 145 | circleShape->setRect( shapeRect ); 146 | else if( triangleShape ) 147 | triangleShape->setRect( shapeRect ); 148 | } else { 149 | shapeRect.setRect( 0, 0, boundingRect.width(), this->m_socketShapeSize ); 150 | 151 | if( squareShape ) 152 | squareShape->setRect( shapeRect ); 153 | else if( circleShape ) 154 | circleShape->setRect( shapeRect ); 155 | else if( triangleShape ) 156 | triangleShape->setRect( shapeRect ); 157 | } 158 | 159 | if( this->m_labelPos == Left ) { 160 | this->m_label->setPos( 161 | this->m_shape->mapToParent( this->m_shape->boundingRect().topLeft().x() + fm.width( ' ' ), 162 | this->m_shape->boundingRect().center().y() - ( fm.height() / 2 ) ) ); 163 | } else if( this->m_labelPos == Right ) { 164 | this->m_label->setPos( this->m_shape->mapToParent( 165 | this->m_shape->boundingRect().topRight().x() - fm.width( ' ' ) - fm.width( this->m_labelText ), 166 | this->m_shape->boundingRect().center().y() - ( fm.height() / 2 ) ) ); 167 | } else { 168 | this->m_label->setPos( this->m_shape->mapToParent( 169 | this->m_shape->boundingRect().topRight().x() - fm.width( ' ' ) - 170 | ( ( qMin( fm.width( this->m_labelText ), boundingRect.width() - 3 * fm.width( ' ' ) ) + 171 | this->m_shape->boundingRect().width() ) / 172 | 2 ), 173 | this->m_shape->boundingRect().center().y() - ( fm.height() / 2 ) ) ); 174 | } 175 | 176 | this->addToGroup( this->m_shape ); 177 | this->addToGroup( this->m_label ); 178 | } 179 | -------------------------------------------------------------------------------- /src/minimapdraglabel.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/minimapdraglabel.h" 4 | #include "NodeView/nodeview.h" 5 | #include "NodeView/nodeviewminimap.h" 6 | 7 | #include 8 | #if QT_VERSION >= 0x050000 9 | #ifndef Qt5 10 | #define Qt5 11 | #endif 12 | #else 13 | #ifndef Qt4 14 | #define Qt4 15 | #endif 16 | #endif 17 | 18 | #ifdef Qt5 19 | #include 20 | #endif 21 | 22 | #include 23 | #include 24 | 25 | /***********************Public Members***********************/ 26 | 27 | MinimapDragLabel::MinimapDragLabel( NodeViewMiniMap* miniMap, QWidget* parent ) 28 | : QWidget( parent ) 29 | , m_miniMap( miniMap ) 30 | , m_drag( false ) { 31 | this->setFixedSize( 8, 8 ); 32 | this->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); 33 | } 34 | 35 | /***********************Protected Members********************/ 36 | void MinimapDragLabel::enterEvent( QEvent* event ) { 37 | QCursor cursor = QCursor( Qt::SizeFDiagCursor ); 38 | NodeView::MinimapPosition pos = this->m_miniMap->magmaView()->minimapPosition(); 39 | if( pos == NodeView::TopRight || pos == NodeView::BottomLeft ) 40 | cursor = QCursor( Qt::SizeBDiagCursor ); 41 | QApplication::setOverrideCursor( cursor ); 42 | 43 | QWidget::enterEvent( event ); 44 | } 45 | 46 | void MinimapDragLabel::leaveEvent( QEvent* event ) { 47 | QApplication::restoreOverrideCursor(); 48 | 49 | QWidget::leaveEvent( event ); 50 | } 51 | 52 | void MinimapDragLabel::mouseMoveEvent( QMouseEvent* event ) { 53 | if( this->m_miniMap != NULL && this->m_drag ) { 54 | 55 | QLine* line = new QLine( this->m_currentPos, event->globalPos() ); 56 | int deltaX = line->dx(); 57 | int deltaY = line->dy(); 58 | NodeView::MinimapPosition pos = this->m_miniMap->magmaView()->minimapPosition(); 59 | 60 | if( pos == NodeView::TopRight || pos == NodeView::BottomRight ) 61 | deltaX = 0 - deltaX; 62 | if( pos == NodeView::BottomLeft || pos == NodeView::BottomRight ) 63 | deltaY = 0 - deltaY; 64 | this->m_miniMap->setFixedSize( qMax( this->m_miniMap->width() + deltaX, this->width() ), 65 | qMax( this->m_miniMap->height() + deltaY, this->height() ) ); 66 | this->m_currentPos = event->globalPos(); 67 | } 68 | } 69 | 70 | void MinimapDragLabel::mousePressEvent( QMouseEvent* event ) { 71 | if( this->m_miniMap != NULL ) { 72 | this->m_drag = true; 73 | this->m_currentPos = event->globalPos(); 74 | } 75 | 76 | QWidget::mousePressEvent( event ); 77 | } 78 | 79 | void MinimapDragLabel::mouseReleaseEvent( QMouseEvent* event ) { 80 | this->m_drag = false; 81 | 82 | QWidget::mouseReleaseEvent( event ); 83 | } 84 | -------------------------------------------------------------------------------- /src/nodegroup.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/nodegroup.h" 4 | #include "NodeView/connection.h" 5 | #include "NodeView/fulllinesocket.h" 6 | #include "NodeView/node.h" 7 | #include "NodeView/nodeview.h" 8 | #include "NodeView/socket.h" 9 | #include "NodeView/widgetsocket.h" 10 | 11 | #include 12 | #if QT_VERSION >= 0x050000 13 | #ifndef Qt5 14 | #define Qt5 15 | #endif 16 | #else 17 | #ifndef Qt4 18 | #define Qt4 19 | #endif 20 | #endif 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /***********************Public Members***********************/ 31 | 32 | NodeGroup::NodeGroup( NodeView* graphWidget, QGraphicsItem* parent, QGraphicsScene* scene ) 33 | #ifdef Qt5 34 | : QGraphicsPolygonItem( parent ) 35 | , m_graphWidget( graphWidget ) { 36 | Q_UNUSED( scene ); 37 | #else 38 | : QGraphicsPolygonItem( parent, scene ) 39 | , m_graphWidget( graphWidget ) { 40 | #endif 41 | 42 | setFlag( ItemIsMovable ); 43 | setFlag( ItemSendsGeometryChanges ); 44 | setFlag( ItemIsSelectable ); 45 | setFlag( ItemIsFocusable ); 46 | // setCacheMode(DeviceCoordinateCache); 47 | 48 | setZValue( INT_MIN ); 49 | m_outlinePen = QPen( Qt::black ); 50 | m_outlinePen.setWidth( 3 ); 51 | m_selectedPen = QPen( Qt::darkGray ); 52 | m_selectedPen.setWidth( 3 ); 53 | m_backgroundBrush = QBrush( QColor( 200, 255, 200 ) ); 54 | m_selectedBrush = m_backgroundBrush; 55 | m_titleBarBrush = m_backgroundBrush; 56 | m_titleBarBrush.setColor( m_titleBarBrush.color().darker( 125 ) ); 57 | m_titleBarSelectedBrush = m_titleBarBrush; 58 | 59 | m_topCorner = QPointF( 0, 0 ); 60 | m_mainRect = QRectF( 0, 0, 100, 100 ); 61 | m_curPos = this->pos(); 62 | 63 | QFont titleFont; 64 | // titleFont.setFamily("Arial"); 65 | m_titleTextItem.setParentItem( this ); 66 | m_titleTextItem.setFont( titleFont ); 67 | m_titleText = "Title"; 68 | 69 | m_titleJustify = TitleCenter; 70 | m_elidedTitle = Qt::ElideNone; 71 | 72 | this->updateBoundingRect(); 73 | } 74 | 75 | NodeGroup::~NodeGroup() {} 76 | 77 | void NodeGroup::addNode( Node* node ) { 78 | this->m_containedNodes.append( node ); 79 | this->updateGroup(); 80 | QObject::connect( node, SIGNAL( deletingNode( Node* ) ), this, SLOT( removeNode( Node* ) ) ); 81 | QObject::connect( node, SIGNAL( itemUpdated() ), this, SLOT( updateGroup() ) ); 82 | } 83 | 84 | QBrush NodeGroup::backgroundBrush() const { return this->m_backgroundBrush; } 85 | 86 | QList NodeGroup::containedNodes() { return this->m_containedNodes; } 87 | 88 | Qt::TextElideMode NodeGroup::elidedTitle() { return this->m_elidedTitle; } 89 | 90 | void NodeGroup::updateBoundingRect() { 91 | QFontMetrics* fm = new QFontMetrics( m_titleTextItem.font() ); 92 | int height = fm->height(); 93 | if( this->m_mainRect.width() > 0 ) { 94 | QRectF boundingRect = this->m_mainRect; 95 | boundingRect.setY( boundingRect.y() - height - 6 ); 96 | m_boundingRect = boundingRect; 97 | } else { 98 | QRectF boundingRect = QRectF( this->m_topCorner.x(), this->m_topCorner.y() - 25, 50, 50 + height + 6 ); 99 | m_boundingRect = boundingRect; 100 | } 101 | } 102 | 103 | QRectF NodeGroup::boundingRect() const { return m_boundingRect; } 104 | 105 | QPen NodeGroup::outlinePen() const { return this->m_outlinePen; } 106 | 107 | void NodeGroup::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) { 108 | Q_UNUSED( widget ); 109 | Q_UNUSED( option ); 110 | 111 | QFontMetrics* fm = new QFontMetrics( m_titleTextItem.font() ); 112 | 113 | int width = 0; 114 | QString elidedTitle; 115 | if( this->m_mainRect.width() > 0 ) { 116 | elidedTitle = fm->elidedText( m_titleText, m_elidedTitle, m_mainRect.width() - 6 ); 117 | } else { 118 | elidedTitle = fm->elidedText( m_titleText, m_elidedTitle, 50 ); 119 | } 120 | m_titleTextItem.setText( elidedTitle ); 121 | width = fm->width( elidedTitle ); 122 | int left = 3; 123 | if( m_titleJustify == TitleCenter ) 124 | left = this->shape().boundingRect().width() / 2 + 3 - width / 2; 125 | else if( m_titleJustify == TitleRight ) 126 | left = this->shape().boundingRect().width() - width - 3; 127 | m_titleTextItem.setPos( this->shape().boundingRect().left() + left, this->shape().boundingRect().top() + 3 ); 128 | 129 | if( isSelected() ) { 130 | painter->setPen( this->m_selectedPen ); 131 | painter->setBrush( this->m_titleBarSelectedBrush ); 132 | } else { 133 | painter->setPen( this->m_outlinePen ); 134 | painter->setBrush( this->m_titleBarBrush ); 135 | } 136 | // Draws the node on the screen. 137 | painter->drawPath( this->shape() ); 138 | 139 | if( isSelected() ) { 140 | painter->setPen( this->m_selectedPen ); 141 | painter->setBrush( this->m_selectedBrush ); 142 | } else { 143 | painter->setPen( this->m_outlinePen ); 144 | painter->setBrush( this->m_backgroundBrush ); 145 | } 146 | 147 | QPainterPath path = QPainterPath(); 148 | if( this->m_mainRect.width() > 0 ) 149 | path.addRect( this->m_mainRect.x(), this->shape().boundingRect().bottom(), this->m_mainRect.width(), 150 | this->m_mainRect.height() ); 151 | else 152 | path.addRect( this->m_topCorner.x(), this->m_topCorner.y(), 50, 50 ); 153 | // Draws the node on the screen. 154 | painter->drawPath( path ); 155 | } 156 | 157 | QBrush NodeGroup::selectedBrush() const { return this->m_selectedBrush; } 158 | 159 | QPen NodeGroup::selectedPen() const { return this->m_selectedPen; } 160 | 161 | void NodeGroup::setBackgroundBrush( const QBrush& brush ) { 162 | this->m_backgroundBrush = brush; 163 | this->update(); 164 | } 165 | 166 | void NodeGroup::setElidedTitle( Qt::TextElideMode mode ) { this->m_elidedTitle = mode; } 167 | 168 | void NodeGroup::setOutlinePen( const QPen& pen ) { 169 | this->m_outlinePen = pen; 170 | this->update(); 171 | } 172 | 173 | void NodeGroup::setSelectedBrush( const QBrush& brush ) { 174 | this->m_selectedBrush = brush; 175 | this->update(); 176 | } 177 | 178 | void NodeGroup::setSelectedPen( const QPen& pen ) { 179 | this->m_selectedPen = pen; 180 | this->update(); 181 | } 182 | 183 | void NodeGroup::setTitleBarBrush( const QBrush& brush ) { 184 | this->m_titleBarBrush = brush; 185 | this->update(); 186 | } 187 | 188 | void NodeGroup::setTitleBarSelectedBrush( const QBrush& brush ) { this->m_titleBarSelectedBrush = brush; } 189 | 190 | void NodeGroup::setTitleJustify( TitleJustify justify ) { this->m_titleJustify = justify; } 191 | 192 | void NodeGroup::setTitleText( const QString& text ) { 193 | this->m_titleText = text; 194 | this->updateGroup(); 195 | } 196 | 197 | void NodeGroup::setTitleTextBrush( const QBrush& brush ) { 198 | this->m_titleTextItem.setBrush( brush ); 199 | this->update(); 200 | } 201 | 202 | void NodeGroup::setTitleTextFont( const QFont& font ) { 203 | this->m_titleTextItem.setFont( font ); 204 | this->updateGroup(); 205 | } 206 | 207 | void NodeGroup::setTitleTextPen( const QPen& pen ) { 208 | this->m_titleTextItem.setPen( pen ); 209 | this->updateGroup(); 210 | } 211 | 212 | QPainterPath NodeGroup::shape() const { 213 | QPainterPath path; 214 | QFontMetrics fm = QFontMetrics( this->m_titleTextItem.font() ); 215 | 216 | if( this->m_mainRect.width() > 0 ) 217 | path.addRect( this->m_mainRect.x(), this->m_mainRect.y() - ( fm.height() + 6 ), this->m_mainRect.width(), 218 | fm.height() + 6 ); 219 | else 220 | path.addRect( this->m_topCorner.x(), this->m_topCorner.y() - ( fm.height() + 6 ), 50, fm.height() + 6 ); 221 | return path; 222 | } 223 | 224 | QBrush NodeGroup::titleBarBrush() const { return this->m_titleBarBrush; } 225 | 226 | QBrush NodeGroup::titleBarSelectedBrush() const { return this->m_titleBarSelectedBrush; } 227 | 228 | NodeGroup::TitleJustify NodeGroup::titleJustify() const { return this->m_titleJustify; } 229 | 230 | QString NodeGroup::titleText() const { return this->m_titleText; } 231 | 232 | QBrush NodeGroup::titleTextBrush() const { return this->m_titleTextItem.brush(); } 233 | 234 | QFont NodeGroup::titleTextFont() const { return this->m_titleTextItem.font(); } 235 | 236 | int NodeGroup::type() const { return Type; } 237 | 238 | void NodeGroup::updateArea() { 239 | this->prepareGeometryChange(); 240 | QRectF workingRect; 241 | foreach( Node* contained, this->m_containedNodes ) { 242 | if( workingRect.isEmpty() ) { 243 | workingRect = this->mapFromItem( contained, contained->boundingRect() ).boundingRect(); 244 | } else { 245 | workingRect = 246 | workingRect.united( this->mapFromItem( contained, contained->boundingRect() ).boundingRect() ); 247 | } 248 | } 249 | if( workingRect.width() > 0 ) 250 | this->m_topCorner = workingRect.topLeft(); 251 | 252 | if( m_elidedTitle == Qt::ElideNone ) { 253 | QFontMetrics* fm = new QFontMetrics( m_titleTextItem.font() ); 254 | int textWidth = fm->width( m_titleText ); 255 | int rectWidth = workingRect.width(); 256 | if( textWidth > rectWidth ) { 257 | int widthDiff = textWidth - rectWidth; 258 | int mod = int( ( widthDiff / 2.0 ) + 0.5 ); 259 | workingRect.adjust( -mod, 0, mod, 0 ); 260 | } 261 | } 262 | workingRect.adjust( -3, -3, 3, 3 ); 263 | this->m_mainRect = workingRect; 264 | } 265 | 266 | void NodeGroup::mouseReleaseEvent( QGraphicsSceneMouseEvent* event ) { 267 | update(); 268 | 269 | foreach( Node* node, this->m_containedNodes ) { 270 | node->snapToGrid(); 271 | } 272 | 273 | QGraphicsItem::mouseReleaseEvent( event ); 274 | } 275 | 276 | QVariant NodeGroup::itemChange( GraphicsItemChange change, const QVariant& value ) { 277 | QPointF movement; 278 | switch( change ) { 279 | case ItemPositionHasChanged: 280 | movement = value.toPointF() - this->m_curPos; 281 | foreach( Node* inner, this->m_containedNodes ) { 282 | inner->setPos( inner->pos() + movement ); 283 | } 284 | this->m_curPos = value.toPointF(); 285 | break; 286 | default: 287 | break; 288 | } 289 | 290 | return QGraphicsItem::itemChange( change, value ); 291 | } 292 | 293 | void NodeGroup::mouseDoubleClickEvent( QGraphicsSceneMouseEvent* event ) { 294 | emit doubleClicked( event ); 295 | QGraphicsItem::mouseDoubleClickEvent( event ); 296 | } 297 | 298 | void NodeGroup::updateGroup() { 299 | this->updateArea(); 300 | this->updateBoundingRect(); 301 | this->update(); 302 | } 303 | 304 | void NodeGroup::removeNode( Node* node ) { 305 | this->m_containedNodes.removeAll( node ); 306 | this->updateGroup(); 307 | } 308 | -------------------------------------------------------------------------------- /src/nodeview.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/nodeview.h" 4 | #include "NodeView/connection.h" 5 | #include "NodeView/node.h" 6 | #include "NodeView/nodegroup.h" 7 | #include "NodeView/nodeviewminimap.h" 8 | #include "NodeView/socket.h" 9 | 10 | #include 11 | #if QT_VERSION >= 0x050000 12 | #ifndef Qt5 13 | #define Qt5 14 | #endif 15 | #else 16 | #ifndef Qt4 17 | #define Qt4 18 | #endif 19 | #endif 20 | 21 | #include 22 | 23 | //#include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | /***********************Public Members***********************/ 32 | 33 | NodeView::NodeView( QWidget* parent ) 34 | : QGraphicsView( parent ) 35 | , m_backgroundMap( 10, 10 ) 36 | , m_miniMap( NULL ) { 37 | // Setup memeber variables 38 | m_snapToGrid = true; 39 | m_gridLines = true; 40 | m_gridSize = 10; 41 | 42 | // Set up the basic properties for the QGraphicsView 43 | setCacheMode( CacheBackground ); 44 | setViewportUpdateMode( SmartViewportUpdate ); 45 | setRenderHints( QPainter::Antialiasing | QPainter::SmoothPixmapTransform ); 46 | setTransformationAnchor( AnchorUnderMouse ); 47 | setBackgroundBrush( QBrush( Qt::lightGray ) ); 48 | setRubberBandSelectionMode( Qt::IntersectsItemShape ); 49 | setDragMode( RubberBandDrag ); 50 | setGeometry( QRect( 0, 0, 300, 300 ) ); 51 | setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); 52 | setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); 53 | 54 | setSceneRect( -50000, -50000, 100000, 100000 ); 55 | 56 | // Setup the QGrpahicsScene 57 | QGraphicsScene* scene = new QGraphicsScene( this ); 58 | scene->setItemIndexMethod( QGraphicsScene::NoIndex ); 59 | connect( scene, SIGNAL( selectionChanged() ), this, SLOT( onSelectionChanged() ) ); 60 | setScene( scene ); 61 | 62 | // Set the window title 63 | setWindowTitle( tr( "NodeView" ) ); 64 | 65 | // Setup background 66 | setGridColor( QColor( Qt::gray ) ); 67 | 68 | // Setup Minimap layout 69 | QGridLayout* layout = new QGridLayout( this ); 70 | this->setLayout( layout ); 71 | layout->setContentsMargins( 0, 0, 0, 0 ); 72 | this->m_miniMapPosition = TopLeft; 73 | 74 | // Setup the creation point marker 75 | m_creationPoint = QPoint( 0, 0 ); 76 | m_creationPointMarker.setZValue( INT_MIN ); 77 | m_creationPointMarker.setRect( 0, 0, 5, 5 ); 78 | m_creationPointMarker.setBrush( QBrush( Qt::black ) ); 79 | this->scene()->addItem( &m_creationPointMarker ); 80 | 81 | setCreationPointMarkerVisible( false ); 82 | this->m_midClickMoved = false; 83 | } 84 | 85 | NodeView::~NodeView() {} 86 | 87 | QColor NodeView::backgroundColor() const { return this->m_backgroundColor; } 88 | 89 | QColor NodeView::creationPointColor() const { return this->m_creationPointMarker.brush().color(); } 90 | 91 | void NodeView::deleteAllNodes() { 92 | foreach( Node* node, this->m_nodes ) { 93 | this->scene()->removeItem( node ); 94 | this->m_nodes.removeAll( node ); 95 | node->deletingSelf(); 96 | node->deleteLater(); 97 | } 98 | 99 | Connection::resetZDepth(); 100 | Node::resetZDepth(); 101 | // qDeleteAll(this->m_nodes); 102 | emit nodeDeleted(); 103 | } 104 | 105 | void NodeView::deleteNode( Node* node ) { 106 | this->scene()->removeItem( node ); 107 | this->m_nodes.removeAll( node ); 108 | node->deletingSelf(); 109 | node->deleteLater(); 110 | emit nodeDeleted(); 111 | } 112 | 113 | Node* NodeView::nodeByPosition( const QPointF& pos ) const { 114 | QGraphicsScene* scene = this->scene(); 115 | #ifdef Qt5 116 | QGraphicsItem* item = scene->itemAt( pos, transform() ); 117 | #else 118 | QGraphicsItem* item = scene->itemAt( pos ); 119 | #endif 120 | Node* node = qgraphicsitem_cast( item ); 121 | 122 | return node; 123 | } 124 | 125 | int NodeView::nodeCount() const { return this->m_nodes.count(); } 126 | 127 | int NodeView::selectionCount() const { return this->scene()->selectedItems().count(); } 128 | 129 | QColor NodeView::gridColor() const { return this->m_gridColor; } 130 | 131 | bool NodeView::gridLines() const { return this->m_gridLines; } 132 | 133 | int NodeView::gridSize() const { return this->m_gridSize; } 134 | 135 | void NodeView::hideMiniMap() { 136 | if( this->m_miniMap != NULL ) 137 | this->m_miniMap->hide(); 138 | } 139 | 140 | bool NodeView::isCreationPointMarkerVisible() const { return this->m_creationPointMarker.isVisible(); } 141 | 142 | QString NodeView::nodeViewStyleSheet() const { return this->m_styleManager.styleSheet(); } 143 | 144 | NodeView::MinimapPosition NodeView::minimapPosition() const { return this->m_miniMapPosition; } 145 | 146 | bool NodeView::minimapVisible() { 147 | if( this->m_miniMap == NULL ) 148 | return false; 149 | return this->m_miniMap->isVisible(); 150 | } 151 | 152 | void NodeView::moveMiniMap() { 153 | if( this->m_miniMap == NULL ) 154 | return; 155 | 156 | QGridLayout* layout = qobject_cast( this->layout() ); 157 | QLayoutItem* item; 158 | 159 | // clear the old layout removing the spacer and minimap 160 | while( ( item = layout->takeAt( 0 ) ) != 0 ) { 161 | layout->removeItem( item ); 162 | } 163 | // read the minimap and a new spacer and set the layout direction to be the correct direction 164 | switch( this->m_miniMapPosition ) { 165 | case TopLeft: 166 | layout->addWidget( this->m_miniMap, 0, 0 ); 167 | layout->setRowStretch( 0, 0 ); 168 | layout->setColumnStretch( 0, 0 ); 169 | layout->setRowStretch( 1, 1 ); 170 | layout->setColumnStretch( 1, 1 ); 171 | break; 172 | case TopRight: 173 | layout->addWidget( this->m_miniMap, 0, 1 ); 174 | layout->setRowStretch( 0, 0 ); 175 | layout->setColumnStretch( 0, 1 ); 176 | layout->setRowStretch( 1, 1 ); 177 | layout->setColumnStretch( 1, 0 ); 178 | break; 179 | case BottomLeft: 180 | layout->addWidget( this->m_miniMap, 1, 0 ); 181 | layout->setRowStretch( 0, 1 ); 182 | layout->setColumnStretch( 0, 0 ); 183 | layout->setRowStretch( 1, 0 ); 184 | layout->setColumnStretch( 1, 1 ); 185 | break; 186 | case BottomRight: 187 | layout->addWidget( this->m_miniMap, 1, 1 ); 188 | layout->setRowStretch( 0, 1 ); 189 | layout->setColumnStretch( 0, 1 ); 190 | layout->setRowStretch( 1, 0 ); 191 | layout->setColumnStretch( 1, 0 ); 192 | break; 193 | } 194 | 195 | this->m_miniMap->moveDragArea(); 196 | } 197 | 198 | QList NodeView::nodes() { return this->m_nodes; } 199 | 200 | /* 201 | QList nodeGroups() 202 | { 203 | return this->m_nodeGroups; 204 | } 205 | */ 206 | 207 | void NodeView::pan( qreal dx, qreal dy ) { this->translate( dx, dy ); } 208 | 209 | void NodeView::redrawView() { this->update(); } 210 | 211 | QList NodeView::selectedConnections() const { 212 | QList selectedItems = this->scene()->selectedItems(); 213 | QList selectedConnections; 214 | Connection* conn; 215 | 216 | foreach( QGraphicsItem* item, selectedItems ) { 217 | conn = qgraphicsitem_cast( item ); 218 | if( conn ) 219 | selectedConnections.append( conn ); 220 | } 221 | 222 | return selectedConnections; 223 | } 224 | 225 | QList NodeView::selectedNodes() const { 226 | QList selectedItems = this->scene()->selectedItems(); 227 | QList selectedNodes; 228 | Node* node; 229 | 230 | foreach( QGraphicsItem* item, selectedItems ) { 231 | node = qgraphicsitem_cast( item ); 232 | if( node ) 233 | selectedNodes.append( node ); 234 | } 235 | 236 | return selectedNodes; 237 | } 238 | 239 | QList NodeView::selectedNodeGroups() const { 240 | QList selectedItems = this->scene()->selectedItems(); 241 | QList selectedNodeGroups; 242 | NodeGroup* nodegroup; 243 | 244 | foreach( QGraphicsItem* item, selectedItems ) { 245 | nodegroup = qgraphicsitem_cast( item ); 246 | if( nodegroup ) 247 | selectedNodeGroups.append( nodegroup ); 248 | } 249 | 250 | return selectedNodeGroups; 251 | } 252 | 253 | void NodeView::setBackgroundColor( const QColor& color ) { 254 | this->m_backgroundColor = color; 255 | 256 | this->setBackgroundBrush( QBrush( color ) ); 257 | } 258 | 259 | void NodeView::setCreationPointMarkerColor( const QColor& color ) { 260 | this->m_creationPointMarker.setBrush( QBrush( color ) ); 261 | 262 | // this->m_creationPointMarker.setVisible(visible); 263 | } 264 | 265 | void NodeView::setCreationPointMarkerVisible( bool visible ) { this->m_creationPointMarker.setVisible( visible ); } 266 | 267 | void NodeView::setGridColor( const QColor& color ) { 268 | this->m_gridColor = color; 269 | 270 | this->setCacheMode( CacheNone ); 271 | this->invalidateScene(); 272 | this->viewport()->update(); 273 | this->setCacheMode( CacheBackground ); 274 | } 275 | 276 | void NodeView::setGridLines( bool enabled ) { 277 | this->m_gridLines = enabled; 278 | 279 | this->setCacheMode( CacheNone ); 280 | this->invalidateScene(); 281 | this->viewport()->update(); 282 | this->setCacheMode( CacheBackground ); 283 | } 284 | 285 | void NodeView::setGridSize( int size ) { 286 | this->m_gridSize = size; 287 | 288 | if( this->m_snapToGrid ) { 289 | foreach( Node* node, this->m_nodes ) { 290 | node->snapToGrid(); 291 | } 292 | } 293 | this->setCacheMode( CacheNone ); 294 | this->invalidateScene(); 295 | this->viewport()->update(); 296 | this->setCacheMode( CacheBackground ); 297 | } 298 | 299 | void NodeView::setNodeViewStyleSheet( const QString& styleSheet ) { this->m_styleManager.setStyleSheet( styleSheet ); } 300 | 301 | void NodeView::setNodeViewStyleMap( const QVariantMap& styleMap ) { this->m_styleManager.setStyleMap( styleMap ); } 302 | 303 | void NodeView::setMiniMapPosition( const MinimapPosition pos ) { 304 | this->m_miniMapPosition = pos; 305 | 306 | this->moveMiniMap(); 307 | } 308 | 309 | void NodeView::setSnapToGrid( bool enable ) { 310 | this->m_snapToGrid = enable; 311 | if( enable ) { 312 | foreach( Node* node, this->m_nodes ) { 313 | node->snapToGrid(); 314 | } 315 | } 316 | } 317 | 318 | void NodeView::setZoom( qreal zoomPercent ) { 319 | qreal scaleFactor = zoomPercent / this->transform().m11(); 320 | this->scaleView( scaleFactor ); 321 | emit zoomChanged(); 322 | } 323 | 324 | void NodeView::showMiniMap() { 325 | if( this->m_miniMap == NULL ) 326 | this->m_miniMap = new NodeViewMiniMap( this, this ); 327 | 328 | this->m_miniMap->show(); 329 | moveMiniMap(); 330 | } 331 | 332 | bool NodeView::snapToGrid() const { return m_snapToGrid; } 333 | 334 | void NodeView::zoom( qreal zoomFactor ) { 335 | this->scaleView( zoomFactor ); 336 | emit zoomChanged(); 337 | } 338 | 339 | void NodeView::zoomToSceneRect() { 340 | this->fitInView( this->sceneRect(), Qt::KeepAspectRatio ); 341 | emit zoomChanged(); 342 | } 343 | 344 | void NodeView::zoomToItemBoundingRect() { 345 | QRectF boundingRect; 346 | foreach( Node* node, this->m_nodes ) { 347 | boundingRect |= node->sceneBoundingRect(); 348 | } 349 | 350 | this->fitInView( boundingRect, Qt::KeepAspectRatio ); 351 | emit zoomChanged(); 352 | } 353 | 354 | /***********************Public Slots*************************/ 355 | 356 | void NodeView::addNode( Node* node ) { 357 | node->setParent( this ); 358 | 359 | this->scene()->addItem( node ); 360 | this->m_nodes.append( node ); 361 | node->setPos( this->m_creationPoint ); 362 | 363 | emit nodeAdded( node ); 364 | } 365 | 366 | Node* NodeView::createNode( int inSockets, int outSockets, const QString& nodeType ) { 367 | Node* newNode = new Node( this ); 368 | 369 | newNode->beginModifyNode(); 370 | 371 | for( int i = 0; i < inSockets; i++ ) 372 | newNode->addInputSocket(); 373 | 374 | for( int i = 0; i < outSockets; i++ ) 375 | newNode->addOutputSocket(); 376 | 377 | if( nodeType != "" ) 378 | newNode->setObjectName( nodeType ); 379 | 380 | newNode->endModifyNode(); 381 | 382 | this->addNode( newNode ); 383 | newNode->setPos( this->m_creationPoint ); 384 | 385 | return newNode; 386 | } 387 | 388 | void NodeView::deleteSelectedNodes() { 389 | QList selectedItems = this->scene()->selectedItems(); 390 | this->scene()->clearSelection(); 391 | 392 | foreach( QGraphicsItem* item, selectedItems ) { 393 | Node* node = qgraphicsitem_cast( item ); 394 | if( node != 0 ) { 395 | // this->scene()->removeItem(node); 396 | // this->m_nodes.removeAt(this->m_nodes.indexOf(node)); 397 | // node->deletingSelf(); 398 | // node->deleteLater(); 399 | deleteNode( node ); 400 | } 401 | } 402 | 403 | emit nodeDeleted(); 404 | } 405 | 406 | /***********************Protected Slots**********************/ 407 | 408 | void NodeView::onConnectionMouseOver( Connection* conn ) { emit connectionMouseOver( conn ); } 409 | 410 | /***********************Protected Members********************/ 411 | 412 | void NodeView::registerStyleableItem( Styleable* styleItem ) { 413 | this->m_styleManager.registerStyleableItem( styleItem ); 414 | } 415 | 416 | /***********************Protected Members********************/ 417 | 418 | void NodeView::drawBackground( QPainter* painter, const QRectF& rect ) { 419 | QVector lines; 420 | QRectF sceneRect = this->mapToScene( this->rect() ).boundingRect(); 421 | qreal adjustFactor = qMin( -3 / ( this->transform().m11() ), -0.5 ); 422 | // qDebug()<transform().m11()<save(); 426 | 427 | painter->setBrush( this->backgroundBrush() ); 428 | painter->drawRect( sceneRect ); 429 | if( this->m_gridLines && ( this->transform().m11() * this->m_gridSize ) >= 5 ) { 430 | int left = rect.x() - int( rect.x() ) % this->m_gridSize; 431 | int top = rect.y() - int( rect.y() ) % this->m_gridSize; 432 | 433 | for( int x = left; x < rect.right(); x += this->m_gridSize ) { 434 | lines << QLineF( x, rect.y(), x, rect.bottom() ); 435 | } 436 | 437 | for( int y = top; y < rect.bottom(); y += this->m_gridSize ) { 438 | lines << QLineF( rect.x(), y, rect.right(), y ); 439 | } 440 | 441 | painter->setPen( this->m_gridColor ); 442 | painter->drawLines( lines ); 443 | } 444 | 445 | painter->restore(); 446 | } 447 | 448 | void NodeView::focusOutEvent( QFocusEvent* event ) { 449 | emit focusOut( event ); 450 | QGraphicsView::focusOutEvent( event ); 451 | } 452 | 453 | void NodeView::keyPressEvent( QKeyEvent* event ) { 454 | Connection* conn; 455 | 456 | switch( event->key() ) { 457 | case Qt::Key_Delete: 458 | case Qt::Key_Backspace: 459 | foreach( QGraphicsItem* item, this->scene()->selectedItems() ) { 460 | conn = qgraphicsitem_cast( item ); 461 | if( conn != 0 ) 462 | conn->deleteLater(); 463 | // conn->sourceSocket()->removeConnection(conn); 464 | } 465 | 466 | default: 467 | QGraphicsView::keyPressEvent( event ); 468 | } 469 | } 470 | 471 | void NodeView::mouseDoubleClickEvent( QMouseEvent* event ) { 472 | QGraphicsView::mouseDoubleClickEvent( event ); 473 | emit doubleClick( event ); 474 | } 475 | 476 | void NodeView::mouseMoveEvent( QMouseEvent* event ) { 477 | if( this->m_panStartPos != QPoint() ) { 478 | QPoint delta = event->pos() - this->m_panStartPos; 479 | this->horizontalScrollBar()->setValue( this->horizontalScrollBar()->value() - delta.x() ); 480 | this->verticalScrollBar()->setValue( this->verticalScrollBar()->value() - delta.y() ); 481 | this->m_panStartPos = event->pos(); 482 | this->m_midClickMoved = true; 483 | } else { 484 | emit mouseMove( event ); 485 | QGraphicsView::mouseMoveEvent( event ); 486 | } 487 | } 488 | 489 | void NodeView::mousePressEvent( QMouseEvent* event ) { 490 | 491 | if( event->button() == Qt::MiddleButton ) { 492 | event->accept(); 493 | this->m_panStartPos = event->pos(); 494 | this->m_midClickMoved = false; 495 | } else if( event->button() == Qt::RightButton ) { 496 | event->accept(); 497 | #ifdef Qt5 498 | } else if( event->button() == Qt::LeftButton && 499 | this->scene()->itemAt( this->mapToScene( event->pos() ), transform() ) == 0 ) { 500 | #else 501 | } else if( event->button() == Qt::LeftButton && this->scene()->itemAt( this->mapToScene( event->pos() ) ) == 0 ) { 502 | #endif 503 | 504 | // event->accept(); 505 | this->m_creationPoint = this->mapToScene( event->pos() ).toPoint(); 506 | this->m_creationPointMarker.setPos( this->m_creationPoint ); 507 | QGraphicsView::mousePressEvent( event ); 508 | } else 509 | QGraphicsView::mousePressEvent( event ); 510 | 511 | emit mousePress( event ); 512 | } 513 | 514 | void NodeView::mouseReleaseEvent( QMouseEvent* event ) { 515 | if( event->button() == Qt::MiddleButton ) { 516 | this->m_panStartPos = QPoint(); 517 | if( !this->m_midClickMoved ) { 518 | this->zoomToItemBoundingRect(); 519 | } 520 | } 521 | 522 | emit mouseRelease( event ); 523 | QGraphicsView::mouseReleaseEvent( event ); 524 | } 525 | 526 | void NodeView::scaleView( qreal scaleFactor ) { 527 | qreal factor = transform().scale( scaleFactor, scaleFactor ).mapRect( QRectF( 0, 0, 1, 1 ) ).width(); 528 | 529 | // factor < 0.07 || 530 | if( factor > 100 || factor < 0.001 ) 531 | return; 532 | 533 | scale( scaleFactor, scaleFactor ); 534 | 535 | m_creationPointMarker.setRect( 0, 0, ( 5.0 / transform().m11() ), ( 5.0 / transform().m11() ) ); 536 | // m_creationPoint.transform().m11() 537 | 538 | emit zoomChanged(); 539 | } 540 | 541 | void NodeView::wheelEvent( QWheelEvent* event ) { 542 | qreal scaleFactor = pow( 2.0, event->delta() / 480.0 ); 543 | 544 | scaleView( scaleFactor ); 545 | 546 | emit scrollWheel( event ); 547 | } 548 | 549 | /***********************Private Slots************************/ 550 | 551 | void NodeView::onConnectionCreated() { 552 | Socket* sock = qobject_cast( this->sender() ); 553 | emit connectionCreated( sock ); 554 | } 555 | 556 | void NodeView::onConnectionDeleted() { emit connectionDeleted(); } 557 | 558 | void NodeView::onConnectionStarted() { emit connectionStarted(); } 559 | 560 | void NodeView::onConnectToEmpty( Socket* socket, QPointF pos ) { emit connectToEmpty( socket, pos ); } 561 | 562 | void NodeView::onDisconnectToEmpty( Socket* socket, QPointF pos ) { emit disconnectToEmpty( socket, pos ); } 563 | 564 | void NodeView::onSelectionChanged() { 565 | QList selectedItems = this->scene()->selectedItems(); 566 | 567 | if( selectedItems.count() == 1 ) { 568 | Connection* conn = qgraphicsitem_cast( selectedItems[0] ); 569 | 570 | if( conn != 0 ) 571 | emit connectionSelected( conn ); 572 | } 573 | 574 | emit selectionChanged(); 575 | } 576 | -------------------------------------------------------------------------------- /src/nodeviewminimap.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/nodeviewminimap.h" 4 | #include "NodeView/minimapdraglabel.h" 5 | #include "NodeView/node.h" 6 | #include "NodeView/nodeview.h" 7 | 8 | #include 9 | #if QT_VERSION >= 0x050000 10 | #ifndef Qt5 11 | #define Qt5 12 | #endif 13 | #else 14 | #ifndef Qt4 15 | #define Qt4 16 | #endif 17 | #endif 18 | 19 | #ifdef Qt5 20 | #include 21 | #endif 22 | 23 | #include 24 | #include 25 | /***********************Public Members***********************/ 26 | 27 | NodeViewMiniMap::NodeViewMiniMap( NodeView* magmaView, QWidget* parent ) 28 | : QGraphicsView( parent ) 29 | , m_mouseDrag( false ) 30 | , m_magmaView( magmaView ) { 31 | setInteractive( false ); 32 | setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); 33 | setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); 34 | setStyleSheet( "background: rgba(150,150,150,128)" ); 35 | setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); 36 | setFixedSize( 200, 200 ); 37 | setRenderHint( QPainter::Antialiasing ); 38 | setWindowFlags( Qt::SubWindow ); 39 | // setWindowOpacity(0.2); 40 | if( m_magmaView != NULL ) { 41 | setScene( m_magmaView->scene() ); 42 | connect( (QObject*)m_magmaView->verticalScrollBar(), SIGNAL( valueChanged( int ) ), this->scene(), 43 | SLOT( invalidate() ) ); 44 | connect( (QObject*)m_magmaView->horizontalScrollBar(), SIGNAL( valueChanged( int ) ), this->scene(), 45 | SLOT( invalidate() ) ); 46 | connect( (QObject*)m_magmaView->verticalScrollBar(), SIGNAL( sliderMoved( int ) ), this->scene(), 47 | SLOT( invalidate() ) ); 48 | connect( (QObject*)m_magmaView->horizontalScrollBar(), SIGNAL( sliderMoved( int ) ), this->scene(), 49 | SLOT( invalidate() ) ); 50 | } 51 | QGridLayout* layout = new QGridLayout( this ); 52 | this->setLayout( layout ); 53 | layout->setContentsMargins( 0, 0, 0, 0 ); 54 | m_dragArea = new MinimapDragLabel( this, this ); 55 | moveDragArea(); 56 | connect( this->scene(), SIGNAL( changed( QList ) ), this, SLOT( adjust() ) ); 57 | } 58 | 59 | /***********************Protected Members********************/ 60 | 61 | void NodeViewMiniMap::drawForeground( QPainter* painter, const QRectF& rect ) { 62 | QGraphicsView::drawForeground( painter, rect ); 63 | 64 | if( this->m_magmaView != NULL ) { 65 | QRectF viewRect = this->m_magmaView->mapToScene( this->m_magmaView->viewport()->rect() ).boundingRect(); 66 | 67 | painter->setPen( Qt::black ); 68 | painter->drawRect( viewRect ); 69 | } 70 | } 71 | 72 | NodeView* NodeViewMiniMap::magmaView() { return this->m_magmaView; } 73 | 74 | void NodeViewMiniMap::mouseMoveEvent( QMouseEvent* event ) { 75 | if( this->m_magmaView != NULL && this->m_mouseDrag ) 76 | this->m_magmaView->centerOn( this->mapToScene( event->pos() ) ); 77 | 78 | QGraphicsView::mouseMoveEvent( event ); 79 | } 80 | 81 | void NodeViewMiniMap::mousePressEvent( QMouseEvent* event ) { 82 | if( this->m_magmaView != NULL ) { 83 | this->m_mouseDrag = true; 84 | this->m_magmaView->centerOn( this->mapToScene( event->pos() ) ); 85 | } 86 | 87 | QGraphicsView::mousePressEvent( event ); 88 | } 89 | 90 | void NodeViewMiniMap::mouseReleaseEvent( QMouseEvent* event ) { 91 | this->m_mouseDrag = false; 92 | 93 | QGraphicsView::mouseReleaseEvent( event ); 94 | } 95 | 96 | void NodeViewMiniMap::moveDragArea() { 97 | QGridLayout* layout = qobject_cast( this->layout() ); 98 | QLayoutItem* item; 99 | 100 | // clear the old layout removing the spacer and minimap 101 | while( ( item = layout->takeAt( 0 ) ) != 0 ) { 102 | layout->removeItem( item ); 103 | } 104 | 105 | switch( this->m_magmaView->minimapPosition() ) { 106 | case NodeView::TopLeft: 107 | layout->addWidget( this->m_dragArea, 1, 1 ); 108 | layout->setRowStretch( 0, 1 ); 109 | layout->setColumnStretch( 0, 1 ); 110 | layout->setRowStretch( 1, 0 ); 111 | layout->setColumnStretch( 1, 0 ); 112 | break; 113 | case NodeView::TopRight: 114 | layout->addWidget( this->m_dragArea, 1, 0 ); 115 | layout->setRowStretch( 0, 1 ); 116 | layout->setColumnStretch( 0, 0 ); 117 | layout->setRowStretch( 1, 0 ); 118 | layout->setColumnStretch( 1, 1 ); 119 | break; 120 | case NodeView::BottomLeft: 121 | layout->addWidget( this->m_dragArea, 0, 1 ); 122 | layout->setRowStretch( 0, 0 ); 123 | layout->setColumnStretch( 0, 1 ); 124 | layout->setRowStretch( 1, 1 ); 125 | layout->setColumnStretch( 1, 0 ); 126 | break; 127 | case NodeView::BottomRight: 128 | layout->addWidget( this->m_dragArea, 0, 0 ); 129 | layout->setRowStretch( 0, 0 ); 130 | layout->setColumnStretch( 0, 0 ); 131 | layout->setRowStretch( 1, 1 ); 132 | layout->setColumnStretch( 1, 1 ); 133 | break; 134 | } 135 | } 136 | 137 | void NodeViewMiniMap::resizeEvent( QResizeEvent* event ) { 138 | QRectF sceneRect; 139 | foreach( Node* node, this->m_magmaView->nodes() ) { 140 | if( sceneRect == QRectF() ) 141 | sceneRect = node->sceneBoundingRect(); 142 | else 143 | sceneRect |= node->sceneBoundingRect(); 144 | } 145 | this->fitInView( sceneRect, Qt::KeepAspectRatio ); 146 | 147 | QGraphicsView::resizeEvent( event ); 148 | } 149 | 150 | /******Private Slots*****/ 151 | void NodeViewMiniMap::adjust() { 152 | QRectF sceneRect; 153 | foreach( Node* node, this->m_magmaView->nodes() ) { 154 | if( sceneRect == QRectF() ) 155 | sceneRect = node->sceneBoundingRect(); 156 | else 157 | sceneRect |= node->sceneBoundingRect(); 158 | } 159 | this->fitInView( sceneRect, Qt::KeepAspectRatio ); 160 | } 161 | -------------------------------------------------------------------------------- /src/simplenode.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/simplenode.h" 4 | #include "NodeView/nodeview.h" 5 | #include "NodeView/socket.h" 6 | #include "NodeView/textlesssocket.h" 7 | 8 | #include 9 | #include 10 | 11 | SimpleNode::SimpleNode( NodeView* graphWidget, QGraphicsItem* parent, QGraphicsScene* scene ) 12 | : Node( graphWidget, parent, scene ) 13 | , m_minHeight( 0 ) { 14 | m_polygon = QPolygonF( QRectF( 0, 0, 10, 10 ) ); 15 | m_titleBarItem.hide(); 16 | m_titleTextItem.setParentItem( this ); 17 | redrawNode(); 18 | } 19 | 20 | int SimpleNode::addInputSocket() { 21 | TextlessSocket* sock = new TextlessSocket( this->m_graphWidget, Socket::Input, this ); 22 | m_inSockets.append( sock ); 23 | 24 | QObject::connect( sock, SIGNAL( connectionStarted() ), this->m_graphWidget, SLOT( onConnectionStarted() ) ); 25 | QObject::connect( sock, SIGNAL( connectionCreated() ), this->m_graphWidget, SLOT( onConnectionCreated() ) ); 26 | QObject::connect( sock, SIGNAL( connectionDeleted() ), this->m_graphWidget, SLOT( onConnectionDeleted() ) ); 27 | QObject::connect( sock, SIGNAL( connectToEmpty( Socket*, QPointF ) ), this->m_graphWidget, 28 | SLOT( onConnectToEmpty( Socket*, QPointF ) ) ); 29 | 30 | this->resizeNode(); 31 | 32 | return m_inSockets.count() - 1; 33 | } 34 | 35 | int SimpleNode::addOutputSocket() { 36 | TextlessSocket* sock = new TextlessSocket( this->m_graphWidget, Socket::Output, this ); 37 | m_outSockets.append( sock ); 38 | 39 | QObject::connect( sock, SIGNAL( connectionStarted() ), this->m_graphWidget, SLOT( onConnectionStarted() ) ); 40 | QObject::connect( sock, SIGNAL( connectionCreated() ), this->m_graphWidget, SLOT( onConnectionCreated() ) ); 41 | QObject::connect( sock, SIGNAL( connectionDeleted() ), this->m_graphWidget, SLOT( onConnectionDeleted() ) ); 42 | QObject::connect( sock, SIGNAL( connectToEmpty( Socket*, QPointF ) ), this->m_graphWidget, 43 | SLOT( onConnectToEmpty( Socket*, QPointF ) ) ); 44 | 45 | this->resizeNode(); 46 | 47 | return m_outSockets.count() - 1; 48 | } 49 | 50 | QRectF SimpleNode::boundingRect() const { 51 | QRectF boundingRect = this->m_polygon.boundingRect(); 52 | 53 | qreal penWidth = this->m_outlinePen.width(); 54 | 55 | qreal topAdjust = penWidth / 2; 56 | qreal bottomAdjust = penWidth / 2; 57 | 58 | foreach( Socket* socket, this->m_inSockets ) { 59 | bottomAdjust = qMax( bottomAdjust, socket->socketShapeSize() ); 60 | } 61 | 62 | foreach( Socket* socket, this->m_outSockets ) { 63 | topAdjust = qMax( topAdjust, socket->socketShapeSize() ); 64 | } 65 | 66 | boundingRect.adjust( -penWidth / 2, -bottomAdjust, penWidth / 2, topAdjust ); 67 | 68 | if( this->m_dropShadow ) { 69 | QRectF dropShadowRect = this->m_polygon.boundingRect().translated( 8, 8 ); 70 | return boundingRect.united( dropShadowRect ); 71 | } else 72 | return boundingRect; 73 | } 74 | 75 | qreal SimpleNode::minimumHeight() const { return m_minHeight; } 76 | 77 | void SimpleNode::setMinimumHeight( qreal height ) { m_minHeight = height; } 78 | 79 | void SimpleNode::resizeNode() { 80 | QRectF boundingRect = this->shape().boundingRect(); 81 | QPointF inStartPoint; 82 | QPointF outStartPoint; 83 | QFontMetricsF fm( this->m_titleTextItem.font() ); 84 | 85 | qreal width = boundingRect.width(); 86 | qreal height = fm.height(); 87 | qreal inSocketWidth = 0; 88 | qreal outSocketWidth = 0; 89 | 90 | foreach( Socket* outSocket, this->m_outSockets ) { 91 | outSocket->rebuildSocket( outSocket->socketShapeSize() ); 92 | } 93 | 94 | foreach( Socket* inSocket, this->m_inSockets ) { 95 | inSocket->rebuildSocket( inSocket->socketShapeSize() ); 96 | } 97 | 98 | foreach( Socket* outSocket, this->m_outSockets ) { 99 | outSocketWidth += outSocket->socketShapeSize() + 4; 100 | } 101 | 102 | foreach( Socket* inSocket, this->m_inSockets ) { 103 | inSocketWidth += inSocket->socketShapeSize() + 4; 104 | } 105 | 106 | m_titleTextItem.setText( m_titleText ); 107 | 108 | width = qMax( outSocketWidth, inSocketWidth ); 109 | width = qMax( fm.width( this->m_titleTextItem.text() ) + 10, width ); 110 | width = qMax( m_minWidth, width ); 111 | 112 | height = qMax( m_minHeight, height ); 113 | 114 | boundingRect.setWidth( width ); 115 | boundingRect.setHeight( height ); 116 | inStartPoint = boundingRect.center() - QPointF( inSocketWidth / 2, boundingRect.height() / 2 ); 117 | outStartPoint = boundingRect.center() + QPointF( -( outSocketWidth / 2 ), boundingRect.height() / 2 ); 118 | 119 | // Create a new polygon based on the new size of the node 120 | QPolygonF newPolygon( boundingRect ); 121 | this->prepareGeometryChange(); 122 | this->m_polygon = newPolygon; 123 | 124 | this->m_titleTextItem.setPos( boundingRect.topLeft() + 125 | QPointF( ( boundingRect.width() - fm.width( this->m_titleTextItem.text() ) ) / 2, 126 | ( boundingRect.height() - fm.height() ) / 2 ) ); 127 | 128 | // Set the starting points of the input sockets 129 | foreach( Socket* inSocket, this->m_inSockets ) { 130 | inSocket->setPos( 131 | inStartPoint - 132 | QPointF( 133 | 0, 134 | inSocket 135 | ->socketShapeSize() ) /*inStartPoint + QPoint(-(inSocket->socketShapeSize()/2), -inSocket->boundingRect().height())*/ ); 136 | inStartPoint += QPointF( inSocket->socketShapeSize() + 4, 0 ); 137 | } 138 | 139 | // Set the starting points of the output sockets 140 | foreach( Socket* outSocket, this->m_outSockets ) { 141 | outSocket->setPos( 142 | outStartPoint /*outStartPoint - QPoint(outSocket->boundingRect().width() - (outSocket->socketShapeSize()/2), outSocket->boundingRect().height())*/ ); 143 | outStartPoint += QPointF( outSocket->socketShapeSize() + 4, 0 ); 144 | } 145 | 146 | // foreach (Socket *outSocket, this->m_outSockets) { 147 | // outSocket->update(); 148 | // } 149 | // foreach (Socket *inSocket, this->m_inSockets) { 150 | // inSocket->update(); 151 | // } 152 | 153 | this->updateConnections(); 154 | } 155 | 156 | QPainterPath SimpleNode::shape() const { 157 | QPainterPath path; 158 | path.addRoundedRect( this->m_polygon.boundingRect(), this->m_cornerXRadius, this->m_cornerYRadius ); 159 | return path; 160 | } 161 | -------------------------------------------------------------------------------- /src/stylemanager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #if QT_VERSION >= 0x050301 15 | #include 16 | #include 17 | #include 18 | #endif 19 | 20 | /***********************Public Members***********************/ 21 | 22 | StyleManager::StyleManager( QObject* parent ) 23 | : QObject( parent ) { 24 | m_styleSheet = ""; 25 | } 26 | 27 | void StyleManager::registerStyleableItem( Styleable* styleItem ) { 28 | // if (!this->m_styleItems.contains(styleItem)) { 29 | connect( dynamic_cast( styleItem ), SIGNAL( requiresStyling( QObject* ) ), this, 30 | SLOT( onRequiresStyling( QObject* ) ) ); 31 | connect( dynamic_cast( styleItem ), SIGNAL( destroyed( QObject* ) ), this, 32 | SLOT( onStyleItemDestroyed( QObject* ) ) ); 33 | 34 | // this->m_styleItems.append(styleItem); 35 | this->m_styleItems.insert( dynamic_cast( styleItem ) ); 36 | this->applyStyleSheet( dynamic_cast( styleItem ) ); 37 | //} 38 | } 39 | 40 | void StyleManager::setStyleMap( const QVariantMap map ) { 41 | this->m_styleMap = map; 42 | this->applyStyleSheet(); 43 | } 44 | 45 | void StyleManager::setStyleSheet( const QString style ) { 46 | this->m_styleSheet = style; 47 | 48 | if( this->m_styleSheet != "" ) { 49 | this->m_styleMap.clear(); 50 | #if QT_VERSION >= 0x050301 51 | this->parseJSON(); 52 | #endif 53 | this->applyStyleSheet(); 54 | } 55 | } 56 | 57 | QString StyleManager::styleSheet() const { return this->m_styleSheet; } 58 | 59 | /***********************Public Slots*************************/ 60 | 61 | void StyleManager::onRequiresStyling( QObject* item ) { this->applyStyleSheet( item ); } 62 | 63 | void StyleManager::onStyleItemDestroyed( QObject* item ) { this->m_styleItems.remove( item ); } 64 | 65 | /***********************Private Members**********************/ 66 | void StyleManager::applyStyleSheet() { 67 | foreach( auto styleItem, this->m_styleItems ) 68 | this->applyStyleSheet( styleItem ); 69 | } 70 | 71 | void StyleManager::applyStyleSheet( QObject* item ) { 72 | QString itemKey = ( item->objectName().length() ) 73 | ? QString( item->metaObject()->className() ).append( "#" ).append( item->objectName() ) 74 | : QString( item->metaObject()->className() ); 75 | QVariantMap itemStyleMap; // = this->m_styleMap[itemKey].toMap(); 76 | 77 | if( this->m_styleMap.contains( itemKey ) ) 78 | itemStyleMap = this->m_styleMap[itemKey].toMap(); 79 | else if( this->m_styleMap.contains( QString( item->metaObject()->className() ) ) ) 80 | itemStyleMap = this->m_styleMap[QString( item->metaObject()->className() )].toMap(); 81 | if( !itemStyleMap.isEmpty() ) { 82 | QVariantMap itemStyleMap = this->m_styleMap[itemKey].toMap(); 83 | int propertyCount = item->metaObject()->propertyCount(); 84 | 85 | for( int i = 0; i < propertyCount; i++ ) { 86 | const char* propertyName = item->metaObject()->property( i ).name(); 87 | if( itemStyleMap.contains( QString( propertyName ) ) ) { 88 | item->setProperty( propertyName, itemStyleMap[QString( propertyName )] ); 89 | } 90 | } 91 | } 92 | } 93 | 94 | #if QT_VERSION >= 0x050301 95 | QVariantMap StyleManager::parseConnection( const QJsonObject& conn ) { 96 | QVariantMap map; 97 | 98 | if( conn["animationDuration"].isDouble() ) 99 | map["animationDuration"] = conn["animationDuration"].toInt(); 100 | 101 | if( conn["animationLightness"].isDouble() ) 102 | map["animationLightness"] = conn["animationLightness"].toInt(); 103 | 104 | if( conn["animationEnabled"].isBool() ) 105 | map["animationEnabled"] = conn["animationEnabled"].toBool(); 106 | 107 | if( conn["brush"].isObject() ) 108 | map["brush"] = this->parseQBrush( conn["brush"].toObject() ); 109 | 110 | if( conn["pen"].isObject() ) 111 | map["pen"] = this->parseQPen( conn["pen"].toObject() ); 112 | 113 | if( conn["selectedBrush"].isObject() ) 114 | map["selectedBrush"] = this->parseQBrush( conn["selectedBrush"].toObject() ); 115 | 116 | if( conn["selectedPen"].isObject() ) 117 | map["selectedPen"] = this->parseQPen( conn["selectedPen"].toObject() ); 118 | 119 | if( conn["arrowPositionPercent"].isDouble() ) 120 | map["arrowPositionPercent"] = static_cast( conn["arrowPositionPercent"].toDouble() ); 121 | 122 | if( conn["arrowSize"].isDouble() ) 123 | map["arrowSize"] = static_cast( conn["arrowSize"].toDouble() ); 124 | 125 | if( conn["curvature"].isDouble() ) 126 | map["curvature"] = static_cast( conn["curvature"].toDouble() ); 127 | 128 | return map; 129 | } 130 | 131 | void StyleManager::parseJSON() { 132 | auto parseError = QJsonParseError(); 133 | auto json = QJsonDocument::fromJson( m_styleSheet.toUtf8(), &parseError ); 134 | 135 | if( json.isObject() ) { 136 | auto styleSheetObject = json.object(); 137 | foreach( const auto& key, styleSheetObject.keys() ) { 138 | if( key.startsWith( "Node" ) ) 139 | this->m_styleMap[key] = this->parseNode( styleSheetObject[key].toObject() ); 140 | else if( key.startsWith( "Socket" ) ) 141 | this->m_styleMap[key] = this->parseSocket( styleSheetObject[key].toObject() ); 142 | else if( key.startsWith( "Connection" ) ) 143 | this->m_styleMap[key] = this->parseConnection( styleSheetObject[key].toObject() ); 144 | } 145 | } 146 | } 147 | 148 | QVariantMap StyleManager::parseNode( const QJsonObject& node ) { 149 | QVariantMap map; 150 | 151 | if( node["backgroundBrush"].isObject() ) 152 | map["backgroundBrush"] = this->parseQBrush( node["backgroundBrush"].toObject() ); 153 | 154 | if( node["selectedBrush"].isObject() ) 155 | map["selectedBrush"] = this->parseQBrush( node["selectedBrush"].toObject() ); 156 | 157 | if( node["titleBarBrush"].isObject() ) 158 | map["titleBarBrush"] = this->parseQBrush( node["titleBarBrush"].toObject() ); 159 | 160 | if( node["titleBarSelectedBrush"].isObject() ) 161 | map["titleBarSelectedBrush"] = this->parseQBrush( node["titleBarSelectedBrush"].toObject() ); 162 | 163 | if( node["titleTextBrush"].isObject() ) 164 | map["titleTextBrush"] = this->parseQBrush( node["titleTextBrush"].toObject() ); 165 | 166 | if( node["detailTextFont"].isObject() ) 167 | map["detailTextFont"] = this->parseQFont( node["detailTextFont"].toObject() ); 168 | 169 | if( node["titleTextFont"].isObject() ) 170 | map["titleTextFont"] = this->parseQFont( node["titleTextFont"].toObject() ); 171 | 172 | if( node["outlinePen"].isObject() ) 173 | map["outlinePen"] = this->parseQPen( node["outlinePen"].toObject() ); 174 | 175 | if( node["selectedPen"].isObject() ) 176 | map["selectedPen"] = this->parseQPen( node["selectedPen"].toObject() ); 177 | 178 | if( node["titleTextPen"].isObject() ) 179 | map["titleTextPen"] = this->parseQPen( node["titleTextPen"].toObject() ); 180 | 181 | if( node["detailTextColor"].isObject() ) 182 | map["detailTextColor"] = this->parseQColor( node["detailTextColor"].toObject() ); 183 | 184 | if( node["pixmap"].isObject() ) 185 | map["pixmap"] = this->parseQPixmap( node["pixmap"].toString() ); 186 | 187 | if( node["pixmapSize"].isObject() ) 188 | map["pixmapSize"] = this->parseQSize( node["pixmapSize"].toArray() ); 189 | 190 | if( node["detailTextEnabled"].isBool() ) 191 | map["detailTextEnabled"] = node["detailTextEnabled"].toBool(); 192 | 193 | if( node["dropShadow"].isBool() ) 194 | map["dropShadow"] = node["dropShadow"].toBool(); 195 | 196 | if( node["pixmapEnabled"].isBool() ) 197 | map["pixmapEnabled"] = node["pixmapEnabled"].toBool(); 198 | 199 | if( node["cornerXRadius"].isDouble() ) 200 | map["cornerXRadius"] = static_cast( node["cornerXRadius"].toDouble() ); 201 | 202 | if( node["cornerYRadius"].isDouble() ) 203 | map["cornerYRadius"] = static_cast( node["cornerYRadius"].toDouble() ); 204 | 205 | if( node["minimumWidth"].isDouble() ) 206 | map["minimumWidth"] = static_cast( node["minimumWidth"].toDouble() ); 207 | 208 | if( node["pixmapPosition"].isDouble() ) 209 | map["pixmapPosition"] = node["pixmapPosition"].toInt(); 210 | 211 | return map; 212 | } 213 | 214 | QBrush StyleManager::parseQBrush( const QJsonObject& qBrush ) { 215 | 216 | if( qBrush["color"].isObject() ) { 217 | QColor color = this->parseQColor( qBrush["color"].toObject() ); 218 | 219 | if( qBrush["brushStyle"].isDouble() ) { 220 | Qt::BrushStyle style = static_cast( qBrush["brushStyle"].toInt() ); 221 | return QBrush( color, style ); 222 | 223 | } else 224 | return QBrush( color ); 225 | 226 | } else if( qBrush["pixmap"].isObject() ) { 227 | QPixmap pixmap = this->parseQPixmap( qBrush["pixmap"].toString() ); 228 | return QBrush( pixmap ); 229 | 230 | } else { 231 | return QBrush(); 232 | } 233 | } 234 | 235 | QColor StyleManager::parseQColor( const QJsonObject& qColor ) { 236 | 237 | // Case for RGB 238 | if( qColor["rgb"].isArray() ) { 239 | qint32 rgb[3]; 240 | 241 | auto i = 0; 242 | foreach( const auto& value, qColor["rgb"].toArray() ) { 243 | if( value.isDouble() ) { 244 | rgb[i] = value.toInt(); 245 | i++; 246 | } else { 247 | return QColor(); 248 | } 249 | } 250 | return QColor::fromRgb( rgb[0], rgb[1], rgb[2] ); 251 | } 252 | 253 | // Case for RGBA 254 | if( qColor["rgba"].isArray() ) { 255 | qint32 rgba[4]; 256 | 257 | auto i = 0; 258 | foreach( const auto& value, qColor["rgba"].toArray() ) { 259 | if( value.isDouble() ) { 260 | rgba[i] = value.toInt(); 261 | i++; 262 | } else { 263 | return QColor(); 264 | } 265 | } 266 | return QColor::fromRgb( rgba[0], rgba[1], rgba[2], rgba[3] ); 267 | } 268 | 269 | // Case for HSV 270 | if( qColor["hsv"].isArray() ) { 271 | qint32 hsv[3]; 272 | 273 | auto i = 0; 274 | foreach( const auto& value, qColor["hsv"].toArray() ) { 275 | if( value.isDouble() ) { 276 | hsv[i] = value.toInt(); 277 | i++; 278 | } else { 279 | return QColor(); 280 | } 281 | } 282 | return QColor::fromHsv( hsv[0], hsv[1], hsv[2] ); 283 | } 284 | 285 | // Case for HSVA 286 | if( qColor["hsva"].isArray() ) { 287 | qint32 hsva[4]; 288 | 289 | auto i = 0; 290 | foreach( const auto& value, qColor["hsva"].toArray() ) { 291 | if( value.isDouble() ) { 292 | hsva[i] = value.toInt(); 293 | i++; 294 | } else { 295 | return QColor(); 296 | } 297 | } 298 | return QColor::fromHsv( hsva[0], hsva[1], hsva[2], hsva[3] ); 299 | } 300 | 301 | // Case for color string 302 | if( qColor["string"].isString() ) { 303 | return QColor( qColor["string"].toString() ); 304 | } 305 | 306 | return QColor(); 307 | } 308 | 309 | QFont StyleManager::parseQFont( const QJsonObject& qFont ) { 310 | QFont font; 311 | 312 | if( qFont["fontFamily"].isString() ) 313 | font.setFamily( qFont["fontFamily"].toString() ); 314 | 315 | if( qFont["pointSize"].isDouble() ) 316 | font.setPointSize( qFont["pointSize"].toInt() ); 317 | 318 | if( qFont["bold"].isBool() ) 319 | font.setBold( qFont["bold"].toBool() ); 320 | 321 | if( qFont["italic"].isBool() ) 322 | font.setItalic( qFont["italic"].toBool() ); 323 | 324 | if( qFont["underline"].isBool() ) 325 | font.setUnderline( qFont["underline"].toBool() ); 326 | 327 | if( qFont["strikeOut"].isBool() ) 328 | font.setUnderline( qFont["strikeOut"].toBool() ); 329 | 330 | return font; 331 | } 332 | 333 | QPen StyleManager::parseQPen( const QJsonObject& qPen ) { 334 | QPen pen; 335 | 336 | if( qPen["brush"].isObject() ) 337 | pen.setBrush( this->parseQBrush( qPen["brush"].toObject() ) ); 338 | 339 | if( qPen["color"].isObject() ) 340 | pen.setColor( this->parseQColor( qPen["color"].toObject() ) ); 341 | 342 | if( qPen["penStyle"].isDouble() ) 343 | pen.setStyle( static_cast( qPen["penStyle"].toInt() ) ); 344 | 345 | if( qPen["penCapStyle"].isDouble() ) 346 | pen.setCapStyle( static_cast( qPen["penCapStyle"].toInt() ) ); 347 | 348 | if( qPen["penJoinStyle"].isDouble() ) 349 | pen.setJoinStyle( static_cast( qPen["penJoinStyle"].toInt() ) ); 350 | 351 | if( qPen["width"].isDouble() ) 352 | pen.setWidth( qPen["width"].toInt() ); 353 | 354 | return pen; 355 | } 356 | 357 | QPixmap StyleManager::parseQPixmap( const QString& qPixmap ) { return QPixmap( qPixmap ); } 358 | 359 | QSize StyleManager::parseQSize( const QJsonArray& qSize ) { 360 | qint32 size[2]; 361 | 362 | auto i = 0; 363 | foreach( const auto& value, qSize ) { 364 | if( value.isDouble() ) { 365 | size[i] = value.toInt(); 366 | i++; 367 | } else { 368 | return QSize(); 369 | } 370 | } 371 | 372 | return QSize( size[0], size[1] ); 373 | } 374 | 375 | QVariantMap StyleManager::parseSocket( const QJsonObject& sock ) { 376 | QVariantMap map; 377 | 378 | if( sock["animationDuration"].isDouble() ) 379 | map["animationDuration"] = sock["animationDuration"].toInt(); 380 | 381 | if( sock["animationLightness"].isDouble() ) 382 | map["animationLightness"] = sock["animationLightness"].toInt(); 383 | 384 | if( sock["animationEnabled"].isBool() ) 385 | map["animationEnabled"] = sock["animationEnabled"].toBool(); 386 | 387 | if( sock["fillColor"].isObject() ) 388 | map["fillColor"] = this->parseQColor( sock["fillColor"].toObject() ); 389 | 390 | if( sock["outlineColor"].isObject() ) 391 | map["outlineColor"] = this->parseQColor( sock["outlineColor"].toObject() ); 392 | 393 | if( sock["labelBrush"].isObject() ) 394 | map["labelBrush"] = this->parseQBrush( sock["labelBrush"].toObject() ); 395 | 396 | if( sock["socketBrush"].isObject() ) 397 | map["socketBrush"] = this->parseQBrush( sock["socketBrush"].toObject() ); 398 | 399 | if( sock["font"].isObject() ) 400 | map["font"] = this->parseQFont( sock["font"].toObject() ); 401 | 402 | if( sock["socketPen"].isObject() ) 403 | map["socketPen"] = this->parseQPen( sock["socketPen"].toObject() ); 404 | 405 | if( sock["multipleConnections"].isBool() ) 406 | map["multipleConnections"] = sock["multipleConnections"].toBool(); 407 | 408 | if( sock["socketShapeSize"].isDouble() ) 409 | map["socketShapeSize"] = static_cast( sock["socketShapeSize"].toDouble() ); 410 | 411 | if( sock["socketShape"].isDouble() ) 412 | map["socketShape"] = sock["socketShape"].toInt(); 413 | 414 | return map; 415 | } 416 | #endif 417 | -------------------------------------------------------------------------------- /src/textlesssocket.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/textlesssocket.h" 4 | #include "NodeView/connection.h" 5 | #include "NodeView/nodeview.h" 6 | #include "NodeView/trianglesocketshape.h" 7 | 8 | #include 9 | #include 10 | 11 | /***********************Public Members***********************/ 12 | 13 | TextlessSocket::TextlessSocket( NodeView* graphWidget, const SocketType type, QGraphicsItem* parent ) 14 | : Socket( graphWidget, type, "Textless Socket", parent ) { 15 | this->m_label->hide(); 16 | } 17 | 18 | QBrush TextlessSocket::labelBrush() const { return QBrush(); } 19 | 20 | QPen TextlessSocket::labelPen() const { return QPen(); } 21 | 22 | void TextlessSocket::resizeGroup( qreal width ) { 23 | Q_UNUSED( width ); 24 | QRectF shapeRect; 25 | QGraphicsRectItem* squareShape = 0; 26 | QGraphicsEllipseItem* circleShape = 0; 27 | TriangleSocketShape* triangleShape = 0; 28 | 29 | // QRectF boundingRect = this->m_shape->boundingRect(); 30 | 31 | this->removeFromGroup( this->m_shape ); 32 | 33 | if( this->m_socketShape == Square ) 34 | squareShape = qgraphicsitem_cast( this->m_shape ); 35 | else if( this->m_socketShape == Circle ) 36 | circleShape = qgraphicsitem_cast( this->m_shape ); 37 | else if( this->m_socketShape == Triangle ) 38 | triangleShape = qgraphicsitem_cast( this->m_shape ); 39 | 40 | shapeRect.setRect( 0, 0, this->m_socketShapeSize, this->m_socketShapeSize ); 41 | 42 | if( squareShape ) { 43 | squareShape->setRect( shapeRect ); 44 | this->m_shape = squareShape; 45 | } else if( circleShape ) { 46 | circleShape->setRect( shapeRect ); 47 | this->m_shape = circleShape; 48 | } else if( triangleShape ) { 49 | triangleShape->setRect( shapeRect ); 50 | } 51 | // this->m_shape->setPos(0,0); 52 | 53 | this->addToGroup( this->m_shape ); 54 | } 55 | 56 | void TextlessSocket::setFont( const QFont font ) { Q_UNUSED( font ); } 57 | 58 | void TextlessSocket::setLabelBrush( const QBrush brush ) { Q_UNUSED( brush ); } 59 | 60 | void TextlessSocket::setLabelPen( const QPen pen ) { Q_UNUSED( pen ); } 61 | 62 | void TextlessSocket::constructGroup() { 63 | QRectF shapeRect; 64 | QGraphicsRectItem* squareShape = 0; 65 | QGraphicsEllipseItem* circleShape = 0; 66 | TriangleSocketShape* triangleShape = 0; 67 | 68 | // QRectF boundingRect = this->m_shape->boundingRect(); 69 | 70 | this->removeFromGroup( this->m_shape ); 71 | 72 | if( this->m_socketShape == Square ) 73 | squareShape = qgraphicsitem_cast( this->m_shape ); 74 | else if( this->m_socketShape == Circle ) 75 | circleShape = qgraphicsitem_cast( this->m_shape ); 76 | else if( this->m_socketShape == Triangle ) 77 | triangleShape = qgraphicsitem_cast( this->m_shape ); 78 | 79 | shapeRect.setRect( 0, 0, this->m_socketShapeSize, this->m_socketShapeSize ); 80 | 81 | if( squareShape ) { 82 | squareShape->setRect( shapeRect ); 83 | squareShape->setPos( this->pos() ); 84 | } else if( circleShape ) { 85 | circleShape->setRect( shapeRect ); 86 | circleShape->setPos( this->pos() ); 87 | } else if( triangleShape ) { 88 | triangleShape->setRect( shapeRect ); 89 | triangleShape->setPos( this->pos() ); 90 | } 91 | 92 | this->addToGroup( this->m_shape ); 93 | } 94 | -------------------------------------------------------------------------------- /src/trianglesocketshape.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/trianglesocketshape.h" 4 | 5 | TriangleSocketShape::TriangleSocketShape( QGraphicsItem* parent ) 6 | : QGraphicsPolygonItem( parent ) { 7 | m_orientation = North; 8 | } 9 | 10 | TriangleSocketShape::Orientation TriangleSocketShape::orientation() const { return this->m_orientation; } 11 | 12 | QRectF TriangleSocketShape::rect() const { return this->m_rect; } 13 | 14 | void TriangleSocketShape::setOrientation( Orientation orientation ) { 15 | this->m_orientation = orientation; 16 | this->generateTriangle(); 17 | } 18 | 19 | void TriangleSocketShape::setRect( const QRectF& rect ) { 20 | this->m_rect = rect; 21 | this->generateTriangle(); 22 | } 23 | 24 | void TriangleSocketShape::setRect( qreal x, qreal y, qreal width, qreal height ) { 25 | this->setRect( QRect( x, y, width, height ) ); 26 | } 27 | 28 | int TriangleSocketShape::type() const { return Type; } 29 | 30 | void TriangleSocketShape::generateTriangle() { 31 | QPointF p1; 32 | QPointF p2; 33 | QPointF p3; 34 | QPolygonF polygon; 35 | 36 | switch( this->m_orientation ) { 37 | case North: 38 | p1 = this->m_rect.bottomLeft(); 39 | p2 = this->m_rect.center(); 40 | p2.setY( this->m_rect.top() ); 41 | p3 = this->m_rect.bottomRight(); 42 | break; 43 | 44 | case East: 45 | p1 = this->m_rect.topLeft(); 46 | p2 = this->m_rect.center(); 47 | p2.setX( this->m_rect.right() ); 48 | p3 = this->m_rect.bottomLeft(); 49 | break; 50 | 51 | case South: 52 | p1 = this->m_rect.topLeft(); 53 | p2 = this->m_rect.center(); 54 | p2.setY( this->m_rect.bottom() ); 55 | p3 = this->m_rect.topRight(); 56 | break; 57 | 58 | case West: 59 | p1 = this->m_rect.topRight(); 60 | p2 = this->m_rect.center(); 61 | p2.setX( this->m_rect.left() ); 62 | p3 = this->m_rect.bottomRight(); 63 | break; 64 | case None: 65 | p1 = QPointF( 0, 0 ); 66 | p2 = QPointF( 0, 0 ); 67 | p2 = QPointF( 0, 0 ); 68 | break; 69 | } 70 | 71 | polygon << p1 << p2 << p3 << p1; 72 | this->setPolygon( polygon ); 73 | } 74 | -------------------------------------------------------------------------------- /src/widgetsocket.cpp: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: Apache-2.0 3 | #include "NodeView/widgetsocket.h" 4 | #include "NodeView/node.h" 5 | 6 | #include 7 | #include 8 | 9 | WidgetSocket::WidgetSocket( NodeView* graphWidget, QWidget* widget, const SocketType type, const QString label, 10 | QGraphicsItem* parent ) 11 | : Socket( graphWidget, type, label, parent ) { 12 | // m_proxyWidget.setParentItem(this); 13 | setWidget( widget ); 14 | setSocketShape( Socket::Square ); 15 | constructGroup(); 16 | } 17 | 18 | void WidgetSocket::setWidget( QWidget* widget ) { 19 | this->m_proxyWidget.setWidget( widget ); 20 | this->m_proxyWidget.show(); 21 | } 22 | 23 | void WidgetSocket::update( const QRectF& rect ) { 24 | if( this->isValid() ) { 25 | Node* parentNode = dynamic_cast( this->parentItem() ); 26 | this->constructGroup(); 27 | if( parentNode ) 28 | parentNode->redrawNode(); 29 | } 30 | QGraphicsItemGroup::update( rect ); 31 | } 32 | 33 | QWidget* WidgetSocket::widget() const { return this->m_proxyWidget.widget(); } 34 | 35 | void WidgetSocket::constructGroup() { 36 | this->m_label->setPlainText( this->m_labelText ); 37 | QRectF shapeRect; 38 | QRectF boundingRect = this->m_label->boundingRect(); 39 | QGraphicsRectItem* squareShape = 0; 40 | QGraphicsEllipseItem* circleShape = 0; 41 | QFontMetricsF fm( this->m_label->font() ); 42 | 43 | this->removeFromGroup( this->m_shape ); 44 | this->removeFromGroup( this->m_label ); 45 | this->removeFromGroup( &( this->m_proxyWidget ) ); 46 | 47 | if( ( boundingRect.width() + this->m_socketShapeSize + this->m_proxyWidget.widget()->sizeHint().width() + 3 ) > 48 | this->parentItem()->shape().boundingRect().width() ) { 49 | boundingRect.setWidth( this->parentItem()->shape().boundingRect().width() - this->m_socketShapeSize - 50 | this->m_proxyWidget.widget()->sizeHint().width() - 3 ); 51 | this->m_label->setPlainText( fm.elidedText( this->m_labelText, Qt::ElideRight, boundingRect.width() ) ); 52 | } 53 | 54 | if( this->m_socketShape == Square ) 55 | squareShape = qgraphicsitem_cast( this->m_shape ); 56 | else 57 | circleShape = qgraphicsitem_cast( this->m_shape ); 58 | 59 | if( this->m_socketType == Output ) { 60 | this->m_label->setPos( this->m_proxyWidget.pos() + QPointF( this->m_proxyWidget.widget()->width() + 3, 0 ) ); 61 | 62 | shapeRect.setRect( 0, 0, this->m_socketShapeSize, this->m_socketShapeSize ); 63 | 64 | if( squareShape ) { 65 | squareShape->setRect( shapeRect ); 66 | squareShape->setPos( this->m_label->pos().x() + 67 | this->m_label->boundingRect().width() /*fm.width(this->m_label->text())*/ + 68 | fm.width( ' ' ), 69 | this->m_label->pos().y() ); 70 | } else { 71 | circleShape->setRect( shapeRect ); 72 | circleShape->setPos( this->m_label->pos().x() + 73 | this->m_label->boundingRect().width() /*fm.width(this->m_label->text())*/ + 74 | fm.width( ' ' ), 75 | this->m_label->pos().y() ); 76 | } 77 | 78 | } else { 79 | shapeRect.setRect( 0, 0, this->m_socketShapeSize, this->m_socketShapeSize ); 80 | 81 | if( squareShape ) 82 | squareShape->setRect( shapeRect ); 83 | else 84 | circleShape->setRect( shapeRect ); 85 | 86 | this->m_label->setPos( 87 | this->m_shape->mapToParent( this->m_shape->boundingRect().topRight().x() + fm.width( ' ' ), 88 | this->m_shape->boundingRect().center().y() - ( fm.height() / 2 ) ) ); 89 | this->m_proxyWidget.setPos( this->m_label->pos() + QPointF( this->m_label->boundingRect().width() + 3, 0 ) ); 90 | } 91 | 92 | // if (this->m_socketType == Input) { 93 | // QPointF pos = this->m_label->pos() + QPointF(this->m_label->boundingRect().width(), 0); 94 | // this->m_proxyWidget.setPos(pos); 95 | // } else if (this->m_socketType == Output){ 96 | // QPointF pos = this->m_label->pos() - QPointF(this->m_proxyWidget.boundingRect().width(), 0); 97 | // this->m_proxyWidget.setPos(pos); 98 | // } 99 | 100 | this->addToGroup( this->m_shape ); 101 | this->addToGroup( this->m_label ); 102 | this->addToGroup( &( this->m_proxyWidget ) ); 103 | } 104 | --------------------------------------------------------------------------------