GraphController.cpp 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. // AZ
  9. #include <AzCore/Debug/Trace.h>
  10. #include <AzCore/Math/Aabb.h>
  11. #include <AzCore/Math/Vector2.h>
  12. #include <AzCore/Math/Vector3.h>
  13. #include <AzCore/Math/Vector4.h>
  14. #include <AzCore/Serialization/Utils.h>
  15. #include <AzCore/std/smart_ptr/make_shared.h>
  16. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  17. // Qt
  18. #include <QGraphicsItem>
  19. #include <QGraphicsLinearLayout>
  20. // Graph Canvas
  21. #include <GraphCanvas/Components/GeometryBus.h>
  22. #include <GraphCanvas/Components/GridBus.h>
  23. #include <GraphCanvas/Components/Nodes/NodeBus.h>
  24. #include <GraphCanvas/Components/Nodes/NodeLayoutBus.h>
  25. #include <GraphCanvas/Components/Nodes/NodeTitleBus.h>
  26. #include <GraphCanvas/Components/Nodes/Wrapper/WrapperNodeBus.h>
  27. #include <GraphCanvas/Components/Slots/Extender/ExtenderSlotBus.h>
  28. #include <GraphCanvas/Components/ViewBus.h>
  29. #include <GraphCanvas/GraphCanvasBus.h>
  30. #include <GraphCanvas/Types/EntitySaveData.h>
  31. // Graph Model
  32. #include <GraphModel/Integration/BooleanDataInterface.h>
  33. #include <GraphModel/Integration/FloatDataInterface.h>
  34. #include <GraphModel/Integration/GraphCanvasMetadata.h>
  35. #include <GraphModel/Integration/GraphController.h>
  36. #include <GraphModel/Integration/Helpers.h>
  37. #include <GraphModel/Integration/IntegerDataInterface.h>
  38. #include <GraphModel/Integration/IntegrationBus.h>
  39. #include <GraphModel/Integration/StringDataInterface.h>
  40. #include <GraphModel/Integration/ThumbnailImageItem.h>
  41. #include <GraphModel/Integration/VectorDataInterface.inl>
  42. #include <GraphModel/Model/Connection.h>
  43. #include <GraphModel/Model/DataType.h>
  44. #include <GraphModel/Model/Graph.h>
  45. #include <GraphModel/Model/GraphContext.h>
  46. #include <GraphModel/Model/Node.h>
  47. namespace GraphModelIntegration
  48. {
  49. // Index of the thumbnail image we embed in our nodes (just after the title header)
  50. static const int NODE_THUMBNAIL_INDEX = 1;
  51. // Helpers static function definitions
  52. AZStd::string Helpers::GetTitlePaletteOverride(void* nodePtr, const AZ::TypeId& typeId)
  53. {
  54. AZ::SerializeContext* serializeContext = nullptr;
  55. AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  56. AZ_Assert(serializeContext, "Failed to acquire application serialize context.");
  57. AZStd::string paletteOverride;
  58. const AZ::SerializeContext::ClassData* derivedClassData = serializeContext->FindClassData(typeId);
  59. if (!derivedClassData)
  60. {
  61. return paletteOverride;
  62. }
  63. // Use the EnumHierarchy API to retrive a list of TypeIds that this class derives from,
  64. // starting with the actual type and going backwards
  65. AZStd::vector<AZ::TypeId> typeIds;
  66. if (derivedClassData->m_azRtti)
  67. {
  68. derivedClassData->m_azRtti->EnumHierarchy(&RttiEnumHierarchyHelper, &typeIds);
  69. }
  70. // Look through all the derived TypeIds to see if the TitlePaletteOverride attribute
  71. // was set in the EditContext at any level
  72. for (auto currentTypeId : typeIds)
  73. {
  74. auto classData = serializeContext->FindClassData(currentTypeId);
  75. if (classData)
  76. {
  77. if (classData->m_editData)
  78. {
  79. const AZ::Edit::ElementData* elementData = classData->m_editData->FindElementData(AZ::Edit::ClassElements::EditorData);
  80. if (elementData)
  81. {
  82. if (auto titlePaletteAttribute = elementData->FindAttribute(Attributes::TitlePaletteOverride))
  83. {
  84. AZ::AttributeReader nameReader(nodePtr, titlePaletteAttribute);
  85. nameReader.Read<AZStd::string>(paletteOverride);
  86. }
  87. }
  88. }
  89. }
  90. }
  91. return paletteOverride;
  92. }
  93. void Helpers::RttiEnumHierarchyHelper(const AZ::TypeId& typeId, void* userData)
  94. {
  95. AZStd::vector<AZ::TypeId>* typeIds = reinterpret_cast<AZStd::vector<AZ::TypeId>*>(userData);
  96. typeIds->push_back(typeId);
  97. }
  98. ////////////////////////////////////////////////////////////////////////////////////
  99. // GraphElementMap
  100. void GraphController::GraphElementMap::Add(AZ::EntityId graphCanvasId, GraphModel::GraphElementPtr graphElement)
  101. {
  102. Remove(graphCanvasId);
  103. Remove(graphElement);
  104. m_graphElementToUi[graphElement.get()] = graphCanvasId;
  105. m_uiToGraphElement[graphCanvasId] = graphElement;
  106. }
  107. void GraphController::GraphElementMap::Remove(AZ::EntityId graphCanvasId)
  108. {
  109. const auto iter = m_uiToGraphElement.find(graphCanvasId);
  110. if (iter != m_uiToGraphElement.end())
  111. {
  112. m_graphElementToUi.erase(iter->second.get());
  113. m_uiToGraphElement.erase(iter);
  114. }
  115. }
  116. void GraphController::GraphElementMap::Remove(GraphModel::ConstGraphElementPtr graphElement)
  117. {
  118. const auto iter = m_graphElementToUi.find(graphElement.get());
  119. if (iter != m_graphElementToUi.end())
  120. {
  121. m_uiToGraphElement.erase(iter->second);
  122. m_graphElementToUi.erase(iter);
  123. }
  124. }
  125. GraphModel::GraphElementPtr GraphController::GraphElementMap::Find(AZ::EntityId graphCanvasId)
  126. {
  127. const auto iter = m_uiToGraphElement.find(graphCanvasId);
  128. return iter != m_uiToGraphElement.end() ? iter->second : nullptr;
  129. }
  130. GraphModel::ConstGraphElementPtr GraphController::GraphElementMap::Find(AZ::EntityId graphCanvasId) const
  131. {
  132. const auto iter = m_uiToGraphElement.find(graphCanvasId);
  133. return iter != m_uiToGraphElement.end() ? iter->second : nullptr;
  134. }
  135. AZ::EntityId GraphController::GraphElementMap::Find(GraphModel::ConstGraphElementPtr graphElement) const
  136. {
  137. const auto iter = m_graphElementToUi.find(graphElement.get());
  138. return iter != m_graphElementToUi.end() ? iter->second : AZ::EntityId();
  139. }
  140. ////////////////////////////////////////////////////////////////////////////////////
  141. // GraphElementMapCollection
  142. const GraphController::GraphElementMap* GraphController::GraphElementMapCollection::GetMapFor(
  143. GraphModel::ConstGraphElementPtr graphElement) const
  144. {
  145. using namespace GraphModel;
  146. if (azrtti_istypeof<Node>(graphElement.get()))
  147. {
  148. return &m_nodeMap;
  149. }
  150. if (azrtti_istypeof<Slot>(graphElement.get()))
  151. {
  152. return &m_slotMap;
  153. }
  154. if (azrtti_istypeof<Connection>(graphElement.get()))
  155. {
  156. return &m_connectionMap;
  157. }
  158. AZ_Assert(false, "Could not determine correct GraphElementMap");
  159. return nullptr;
  160. }
  161. GraphController::GraphElementMap* GraphController::GraphElementMapCollection::GetMapFor(GraphModel::ConstGraphElementPtr graphElement)
  162. {
  163. // Non-const overload implementation
  164. const GraphElementMapCollection* constThis = this;
  165. return const_cast<GraphController::GraphElementMap*>(constThis->GetMapFor(graphElement));
  166. }
  167. void GraphController::GraphElementMapCollection::Add(AZ::EntityId graphCanvasId, GraphModel::GraphElementPtr graphElement)
  168. {
  169. using namespace GraphModel;
  170. if (graphElement)
  171. {
  172. GetMapFor(graphElement)->Add(graphCanvasId, graphElement);
  173. }
  174. }
  175. void GraphController::GraphElementMapCollection::Remove(AZ::EntityId graphCanvasId)
  176. {
  177. for (GraphElementMap* map : m_allMaps)
  178. {
  179. map->Remove(graphCanvasId);
  180. }
  181. }
  182. void GraphController::GraphElementMapCollection::Remove(GraphModel::ConstGraphElementPtr graphElement)
  183. {
  184. GetMapFor(graphElement)->Remove(graphElement);
  185. }
  186. AZ::EntityId GraphController::GraphElementMapCollection::Find(GraphModel::ConstGraphElementPtr graphElement) const
  187. {
  188. return GetMapFor(graphElement)->Find(graphElement);
  189. }
  190. ////////////////////////////////////////////////////////////////////////////////////
  191. // GraphController
  192. GraphCanvas::ConnectionType ToGraphCanvasConnectionType(const GraphModel::SlotDirection& direction)
  193. {
  194. switch (direction)
  195. {
  196. case GraphModel::SlotDirection::Input:
  197. return GraphCanvas::ConnectionType::CT_Input;
  198. case GraphModel::SlotDirection::Output:
  199. return GraphCanvas::ConnectionType::CT_Output;
  200. default:
  201. break;
  202. }
  203. AZ_Assert(false, "Invalid SlotDirection");
  204. return GraphCanvas::ConnectionType::CT_Invalid;
  205. }
  206. GraphCanvas::SlotGroup ToGraphCanvasSlotGroup(const GraphModel::SlotType& slotType)
  207. {
  208. switch (slotType)
  209. {
  210. case GraphModel::SlotType::Data:
  211. return GraphCanvas::SlotGroups::DataGroup;
  212. case GraphModel::SlotType::Event:
  213. return GraphCanvas::SlotGroups::ExecutionGroup;
  214. case GraphModel::SlotType::Property:
  215. return GraphCanvas::SlotGroups::PropertyGroup;
  216. default:
  217. break;
  218. }
  219. AZ_Assert(false, "Invalid SlotType");
  220. return GraphCanvas::SlotGroups::Invalid;
  221. }
  222. GraphController::GraphController(GraphModel::GraphPtr graph, AZ::EntityId graphCanvasSceneId)
  223. : m_graph(graph)
  224. , m_graphCanvasSceneId(graphCanvasSceneId)
  225. {
  226. AZ::ComponentApplicationBus::BroadcastResult(m_serializeContext, &AZ::ComponentApplicationRequests::GetSerializeContext);
  227. AZ_Assert(m_serializeContext, "Failed to acquire application serialize context.");
  228. GraphCanvas::GraphModelRequestBus::Handler::BusConnect(m_graphCanvasSceneId);
  229. GraphCanvas::SceneNotificationBus::Handler::BusConnect(m_graphCanvasSceneId);
  230. GraphControllerRequestBus::Handler::BusConnect(m_graphCanvasSceneId);
  231. CreateFullGraphUi();
  232. }
  233. GraphController::~GraphController()
  234. {
  235. GraphControllerRequestBus::Handler::BusDisconnect();
  236. GraphCanvas::SceneNotificationBus::Handler::BusDisconnect();
  237. GraphCanvas::GraphModelRequestBus::Handler::BusDisconnect();
  238. }
  239. GraphModel::GraphPtr GraphController::GetGraph()
  240. {
  241. return m_graph;
  242. }
  243. const GraphModel::GraphPtr GraphController::GetGraph() const
  244. {
  245. return m_graph;
  246. }
  247. const AZ::EntityId GraphController::GetGraphCanvasSceneId() const
  248. {
  249. return m_graphCanvasSceneId;
  250. }
  251. void GraphController::CreateFullGraphUi()
  252. {
  253. using namespace GraphModel;
  254. // This notification is needed by the graph canvassing component prior to repopulating the entire scene.
  255. GraphCanvas::SceneRequestBus::Event(GetGraphCanvasSceneId(), &GraphCanvas::SceneRequests::SignalLoadStart);
  256. GraphCanvasMetadata* graphCanvasMetadata = GetGraphMetadata();
  257. // Connect the EntitySaveDataRequestBus using a router. This will allow the graph controller to inject custom selection save
  258. // data for all graph canvas objects. This should only be connected while serializing save data for the current graph.
  259. GraphCanvas::EntitySaveDataRequestBus::Router::BusRouterConnect();
  260. // Create UI for all the Nodes
  261. for (const auto& pair : m_graph->GetNodes())
  262. {
  263. const NodeId nodeId = pair.first;
  264. NodePtr node = pair.second;
  265. AZStd::shared_ptr<GraphCanvas::EntitySaveDataContainer> container;
  266. auto metadataIter = graphCanvasMetadata->m_nodeMetadata.find(nodeId);
  267. if (metadataIter != graphCanvasMetadata->m_nodeMetadata.end() && metadataIter->second)
  268. {
  269. container = metadataIter->second;
  270. }
  271. AZ::EntityId nodeUiId = CreateNodeUi(nodeId, node, AZ::Vector2::CreateZero());
  272. if (container)
  273. {
  274. GraphCanvas::EntitySaveDataRequestBus::Event(nodeUiId, &GraphCanvas::EntitySaveDataRequests::ReadSaveData, *container);
  275. }
  276. }
  277. // Wrap any nodes stored in the node wrappings
  278. for (auto& pair : m_graph->GetNodeWrappings())
  279. {
  280. GraphModel::NodePtr node = m_graph->GetNode(pair.first);
  281. GraphModel::NodePtr wrapperNode = m_graph->GetNode(pair.second.first);
  282. AZ::u32 layoutOrder = pair.second.second;
  283. WrapNodeUi(wrapperNode, node, layoutOrder);
  284. }
  285. // Create UI for all the Connections
  286. for (ConnectionPtr connection : m_graph->GetConnections())
  287. {
  288. CreateConnectionUi(connection);
  289. }
  290. // Load graph canvas metadata for the scene. This will recreate all of the utility types like comments, bookmarks, groups, etc.
  291. if (graphCanvasMetadata->m_sceneMetadata)
  292. {
  293. GraphCanvas::EntitySaveDataRequestBus::Event(
  294. GetGraphCanvasSceneId(), &GraphCanvas::EntitySaveDataRequests::ReadSaveData, *graphCanvasMetadata->m_sceneMetadata);
  295. }
  296. // Disconnect the EntitySaveDataRequestBus after save data serialization as completed
  297. GraphCanvas::EntitySaveDataRequestBus::Router::BusRouterDisconnect();
  298. // After the graph has been reconstructed, this signal will inform the scene, node groups, and other types to update their state
  299. // after all of the graph elements are in place. This is necessary for node groups to reclaim nodes that were contained within them
  300. // when the graph was saved.
  301. GraphCanvas::SceneRequestBus::Event(GetGraphCanvasSceneId(), &GraphCanvas::SceneRequests::SignalLoadEnd);
  302. }
  303. AZ::Entity* GraphController::CreateSlotUi(GraphModel::SlotPtr slot, AZ::EntityId nodeUiId)
  304. {
  305. using namespace GraphModel;
  306. GraphCanvas::SlotConfiguration slotConfig;
  307. slotConfig.m_name = !slot->GetDisplayName().empty() ? slot->GetDisplayName() : slot->GetName();
  308. slotConfig.m_tooltip = slot->GetDescription();
  309. slotConfig.m_connectionType = ToGraphCanvasConnectionType(slot->GetSlotDirection());
  310. slotConfig.m_slotGroup = ToGraphCanvasSlotGroup(slot->GetSlotType());
  311. const AZ::EntityId stylingParent = nodeUiId;
  312. AZ::Entity* graphCanvasSlotEntity = nullptr;
  313. switch (slot->GetSlotType())
  314. {
  315. case SlotType::Data:
  316. {
  317. GraphCanvas::DataSlotConfiguration dataConfig(slotConfig);
  318. dataConfig.m_dataSlotType = GraphCanvas::DataSlotType::Value;
  319. dataConfig.m_typeId = slot->GetDataType()->GetTypeUuid();
  320. GraphCanvas::GraphCanvasRequestBus::BroadcastResult(
  321. graphCanvasSlotEntity, &GraphCanvas::GraphCanvasRequests::CreateSlot, stylingParent, dataConfig);
  322. }
  323. break;
  324. case SlotType::Event:
  325. {
  326. GraphCanvas::ExecutionSlotConfiguration eventConfig(slotConfig);
  327. GraphCanvas::GraphCanvasRequestBus::BroadcastResult(
  328. graphCanvasSlotEntity, &GraphCanvas::GraphCanvasRequests::CreateSlot, stylingParent, eventConfig);
  329. }
  330. break;
  331. case SlotType::Property:
  332. {
  333. GraphCanvas::GraphCanvasRequestBus::BroadcastResult(
  334. graphCanvasSlotEntity, &GraphCanvas::GraphCanvasRequests::CreatePropertySlot, stylingParent, 0, slotConfig);
  335. }
  336. break;
  337. default:
  338. AZ_Assert(false, "Invalid SlotType");
  339. }
  340. AZ_Assert(graphCanvasSlotEntity, "Unable to create GraphCanvas Slot");
  341. graphCanvasSlotEntity->Init();
  342. graphCanvasSlotEntity->Activate();
  343. m_elementMap.Add(graphCanvasSlotEntity->GetId(), slot);
  344. GraphCanvas::NodeRequestBus::Event(nodeUiId, &GraphCanvas::NodeRequests::AddSlot, graphCanvasSlotEntity->GetId());
  345. return graphCanvasSlotEntity;
  346. }
  347. AZ::EntityId GraphController::CreateNodeUi(
  348. [[maybe_unused]] GraphModel::NodeId nodeId, GraphModel::NodePtr node, const AZ::Vector2& scenePosition)
  349. {
  350. using namespace GraphModel;
  351. // Create the node...
  352. const char* nodeStyle = "";
  353. const AZ::Entity* graphCanvasNode = nullptr;
  354. const NodeType& nodeType = node->GetNodeType();
  355. switch (nodeType)
  356. {
  357. case NodeType::GeneralNode:
  358. GraphCanvas::GraphCanvasRequestBus::BroadcastResult(
  359. graphCanvasNode, &GraphCanvas::GraphCanvasRequests::CreateGeneralNodeAndActivate, nodeStyle);
  360. break;
  361. case NodeType::WrapperNode:
  362. GraphCanvas::GraphCanvasRequestBus::BroadcastResult(
  363. graphCanvasNode, &GraphCanvas::GraphCanvasRequests::CreateWrapperNodeAndActivate, nodeStyle);
  364. break;
  365. }
  366. AZ_Assert(graphCanvasNode, "Unable to create GraphCanvas Node");
  367. const AZ::EntityId nodeUiId = graphCanvasNode->GetId();
  368. m_elementMap.Add(nodeUiId, node);
  369. GraphCanvas::NodeTitleRequestBus::Event(nodeUiId, &GraphCanvas::NodeTitleRequests::SetTitle, node->GetTitle());
  370. GraphCanvas::NodeTitleRequestBus::Event(nodeUiId, &GraphCanvas::NodeTitleRequests::SetSubTitle, node->GetSubTitle());
  371. // Set the palette override for this node if one has been specified
  372. AZStd::string paletteOverride = Helpers::GetTitlePaletteOverride(node.get(), azrtti_typeid(node.get()));
  373. if (!paletteOverride.empty())
  374. {
  375. GraphCanvas::NodeTitleRequestBus::Event(nodeUiId, &GraphCanvas::NodeTitleRequests::SetPaletteOverride, paletteOverride);
  376. }
  377. // Create the slots...
  378. // Note that SlotDefinitions are stored in a list in the order defined by the author.
  379. // That's why we loop through SlotDefinitions instead of the actual Slots, which are stored in a map.
  380. for (SlotDefinitionPtr slotDefinition : node->GetSlotDefinitions())
  381. {
  382. if (!slotDefinition->IsVisibleOnNode())
  383. {
  384. continue;
  385. }
  386. const AZStd::string& slotName = slotDefinition->GetName();
  387. GraphCanvas::ExtenderId extenderId;
  388. if (slotDefinition->SupportsExtendability())
  389. {
  390. for (GraphModel::SlotPtr slot : node->GetExtendableSlots(slotName))
  391. {
  392. CreateSlotUi(slot, nodeUiId);
  393. }
  394. // Keep a mapping of the extenderId/SlotName for this node
  395. extenderId = AZ_CRC(slotName);
  396. auto it = m_nodeExtenderIds.find(nodeUiId);
  397. if (it != m_nodeExtenderIds.end())
  398. {
  399. it->second[extenderId] = slotName;
  400. }
  401. else
  402. {
  403. AZStd::unordered_map<GraphCanvas::ExtenderId, GraphModel::SlotName> newNodeMap;
  404. newNodeMap[extenderId] = slotName;
  405. m_nodeExtenderIds[nodeUiId] = newNodeMap;
  406. }
  407. }
  408. else
  409. {
  410. CreateSlotUi(node->GetSlot(slotName), nodeUiId);
  411. }
  412. // For an extendable slot, we also need to create the extension slot that allows
  413. // the user to add more slots
  414. if (slotDefinition->SupportsExtendability())
  415. {
  416. GraphCanvas::ExtenderSlotConfiguration extenderConfig;
  417. extenderConfig.m_extenderId = extenderId;
  418. extenderConfig.m_name = slotDefinition->GetExtensionLabel();
  419. extenderConfig.m_tooltip = slotDefinition->GetExtensionTooltip();
  420. extenderConfig.m_connectionType = ToGraphCanvasConnectionType(slotDefinition->GetSlotDirection());
  421. extenderConfig.m_slotGroup = ToGraphCanvasSlotGroup(slotDefinition->GetSlotType());
  422. const AZ::EntityId stylingParent = nodeUiId;
  423. AZ::Entity* extensionEntity = nullptr;
  424. GraphCanvas::GraphCanvasRequestBus::BroadcastResult(
  425. extensionEntity, &GraphCanvas::GraphCanvasRequests::CreateSlot, stylingParent, extenderConfig);
  426. extensionEntity->Init();
  427. extensionEntity->Activate();
  428. GraphCanvas::NodeRequestBus::Event(nodeUiId, &GraphCanvas::NodeRequests::AddSlot, extensionEntity->GetId());
  429. }
  430. }
  431. GraphCanvas::SceneRequestBus::Event(GetGraphCanvasSceneId(), &GraphCanvas::SceneRequests::AddNode, nodeUiId, scenePosition, false);
  432. return nodeUiId;
  433. }
  434. void GraphController::CreateConnectionUi(GraphModel::ConnectionPtr connection)
  435. {
  436. AZ::EntityId sourceNodeUiId = m_elementMap.Find(connection->GetSourceNode());
  437. AZ::EntityId targetNodeUiId = m_elementMap.Find(connection->GetTargetNode());
  438. AZ::EntityId sourceSlotUiId = m_elementMap.Find(connection->GetSourceSlot());
  439. AZ::EntityId targetSlotUiId = m_elementMap.Find(connection->GetTargetSlot());
  440. m_isCreatingConnectionUi = true;
  441. AZ::EntityId connectionUiId;
  442. GraphCanvas::SceneRequestBus::EventResult(
  443. connectionUiId,
  444. GetGraphCanvasSceneId(),
  445. &GraphCanvas::SceneRequests::CreateConnectionBetween,
  446. GraphCanvas::Endpoint(sourceNodeUiId, sourceSlotUiId),
  447. GraphCanvas::Endpoint(targetNodeUiId, targetSlotUiId));
  448. m_elementMap.Add(connectionUiId, connection);
  449. m_isCreatingConnectionUi = false;
  450. }
  451. GraphCanvas::NodeId GraphController::AddNode(GraphModel::NodePtr node, AZ::Vector2& sceneDropPosition)
  452. {
  453. AZ_Assert(node, "Node was null");
  454. const GraphModel::NodeId nodeId = m_graph->AddNode(node);
  455. AZ::EntityId graphCanvasNodeId = CreateNodeUi(nodeId, node, sceneDropPosition);
  456. GraphCanvas::SceneMemberUIRequestBus::Event(graphCanvasNodeId, &GraphCanvas::SceneMemberUIRequests::SetSelected, true);
  457. SaveMetadata(graphCanvasNodeId);
  458. // Offset the sceneDropPosition so if multiple nodes are dragged into the scene at the same time, the don't stack exactly on top of
  459. // each other
  460. AZ::EntityId gridId;
  461. GraphCanvas::SceneRequestBus::EventResult(gridId, GetGraphCanvasSceneId(), &GraphCanvas::SceneRequests::GetGrid);
  462. AZ::Vector2 offset = AZ::Vector2::CreateZero();
  463. GraphCanvas::GridRequestBus::EventResult(offset, gridId, &GraphCanvas::GridRequests::GetMinorPitch);
  464. sceneDropPosition += offset;
  465. return graphCanvasNodeId;
  466. }
  467. bool GraphController::RemoveNode(GraphModel::NodePtr node)
  468. {
  469. const AZ::EntityId nodeUiId = m_elementMap.Find(node);
  470. if (nodeUiId.IsValid())
  471. {
  472. AzToolsFramework::EntityIdSet entityIds = { nodeUiId };
  473. GraphCanvas::SceneRequestBus::Event(GetGraphCanvasSceneId(), &GraphCanvas::SceneRequests::Delete, entityIds);
  474. return true;
  475. }
  476. return false;
  477. }
  478. AZ::Vector2 GraphController::GetPosition(GraphModel::NodePtr node) const
  479. {
  480. AZ::Vector2 position = AZ::Vector2::CreateZero();
  481. const AZ::EntityId nodeUiId = m_elementMap.Find(node);
  482. if (nodeUiId.IsValid())
  483. {
  484. GraphCanvas::GeometryRequestBus::EventResult(position, nodeUiId, &GraphCanvas::GeometryRequests::GetPosition);
  485. }
  486. return position;
  487. }
  488. void GraphController::WrapNodeInternal(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node, AZ::u32 layoutOrder)
  489. {
  490. AZ::EntityId wrapperNodeUiId = m_elementMap.Find(wrapperNode);
  491. if (!wrapperNodeUiId.IsValid())
  492. {
  493. // The parent WrapperNode needs to be added to the scene before we can wrap a child node
  494. return;
  495. }
  496. GraphControllerNotificationBus::Event(
  497. m_graphCanvasSceneId, &GraphControllerNotifications::PreOnGraphModelNodeWrapped, wrapperNode, node);
  498. AZ::EntityId nodeUiId = m_elementMap.Find(node);
  499. if (!nodeUiId.IsValid())
  500. {
  501. // If the node to be wrapped hasn't been added to the scene yet,
  502. // add it before wrapping it
  503. AZ::Vector2 dropPosition(0, 0);
  504. nodeUiId = AddNode(node, dropPosition);
  505. }
  506. m_graph->WrapNode(wrapperNode, node, layoutOrder);
  507. WrapNodeUi(wrapperNode, node, layoutOrder);
  508. GraphControllerNotificationBus::Event(
  509. m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelNodeWrapped, wrapperNode, node);
  510. }
  511. void GraphController::WrapNode(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node)
  512. {
  513. WrapNodeInternal(wrapperNode, node);
  514. }
  515. void GraphController::WrapNodeOrdered(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node, AZ::u32 layoutOrder)
  516. {
  517. WrapNodeInternal(wrapperNode, node, layoutOrder);
  518. }
  519. void GraphController::UnwrapNode(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node)
  520. {
  521. AZ::EntityId wrapperNodeUiId = m_elementMap.Find(wrapperNode);
  522. AZ::EntityId nodeUiId = m_elementMap.Find(node);
  523. if (!wrapperNodeUiId.IsValid() || !nodeUiId.IsValid())
  524. {
  525. return;
  526. }
  527. m_graph->UnwrapNode(node);
  528. // Unwrap the node from the parent WrapperNode
  529. GraphCanvas::WrappedNodeConfiguration configuration;
  530. GraphCanvas::WrapperNodeRequestBus::Event(wrapperNodeUiId, &GraphCanvas::WrapperNodeRequests::UnwrapNode, nodeUiId);
  531. GraphControllerNotificationBus::Event(
  532. m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelNodeUnwrapped, wrapperNode, node);
  533. }
  534. bool GraphController::IsNodeWrapped(GraphModel::NodePtr node) const
  535. {
  536. return m_graph->IsNodeWrapped(node);
  537. }
  538. void GraphController::WrapNodeUi(GraphModel::NodePtr wrapperNode, GraphModel::NodePtr node, AZ::u32 layoutOrder)
  539. {
  540. AZ::EntityId wrapperNodeUiId = m_elementMap.Find(wrapperNode);
  541. AZ::EntityId nodeUiId = m_elementMap.Find(node);
  542. if (!wrapperNodeUiId.IsValid() || !nodeUiId.IsValid())
  543. {
  544. return;
  545. }
  546. // Wrap the node in the parent WrapperNode with the given layout order
  547. GraphCanvas::WrappedNodeConfiguration configuration;
  548. configuration.m_layoutOrder = layoutOrder;
  549. GraphCanvas::WrapperNodeRequestBus::Event(wrapperNodeUiId, &GraphCanvas::WrapperNodeRequests::WrapNode, nodeUiId, configuration);
  550. }
  551. void GraphController::SetWrapperNodeActionString(GraphModel::NodePtr node, const char* actionString)
  552. {
  553. AZ::EntityId nodeUiId = m_elementMap.Find(node);
  554. if (!nodeUiId.IsValid())
  555. {
  556. return;
  557. }
  558. GraphCanvas::WrapperNodeRequestBus::Event(nodeUiId, &GraphCanvas::WrapperNodeRequests::SetActionString, actionString);
  559. }
  560. GraphModel::ConnectionPtr GraphController::AddConnection(GraphModel::SlotPtr sourceSlot, GraphModel::SlotPtr targetSlot)
  561. {
  562. GraphModel::ConnectionPtr newConnection = CreateConnection(sourceSlot, targetSlot);
  563. if (newConnection)
  564. {
  565. CreateConnectionUi(newConnection);
  566. }
  567. return newConnection;
  568. }
  569. GraphModel::ConnectionPtr GraphController::AddConnectionBySlotId(
  570. GraphModel::NodePtr sourceNode,
  571. const GraphModel::SlotId& sourceSlotId,
  572. GraphModel::NodePtr targetNode,
  573. const GraphModel::SlotId& targetSlotId)
  574. {
  575. GraphModel::SlotPtr sourceSlot = sourceNode->GetSlot(sourceSlotId);
  576. GraphModel::SlotPtr targetSlot = targetNode->GetSlot(targetSlotId);
  577. return AddConnection(sourceSlot, targetSlot);
  578. }
  579. bool GraphController::AreSlotsConnected(
  580. GraphModel::NodePtr sourceNode,
  581. const GraphModel::SlotId& sourceSlotId,
  582. GraphModel::NodePtr targetNode,
  583. const GraphModel::SlotId& targetSlotId) const
  584. {
  585. if (!sourceNode || !targetNode)
  586. {
  587. return false;
  588. }
  589. const GraphModel::SlotPtr sourceSlot = sourceNode->GetSlot(sourceSlotId);
  590. const GraphModel::SlotPtr targetSlot = targetNode->GetSlot(targetSlotId);
  591. if (!sourceSlot || !targetSlot)
  592. {
  593. return false;
  594. }
  595. // Check all connections on the source slot to see if they match the target node and slot
  596. for (const auto& connection : sourceSlot->GetConnections())
  597. {
  598. if (connection->GetTargetNode() == targetNode && connection->GetTargetSlot() == targetSlot)
  599. {
  600. return true;
  601. }
  602. }
  603. return false;
  604. }
  605. bool GraphController::RemoveConnection(GraphModel::ConnectionPtr connection)
  606. {
  607. const AZ::EntityId connectionUiId = m_elementMap.Find(connection);
  608. if (connectionUiId.IsValid())
  609. {
  610. AZStd::unordered_set<AZ::EntityId> deleteIds = { connectionUiId };
  611. // This general Delete method will in turn call SceneRequests::RemoveConnection,
  612. // but just calling RemoveConnection by itself won't actually delete the ConnectionComponent itself.
  613. GraphCanvas::SceneRequestBus::Event(GetGraphCanvasSceneId(), &GraphCanvas::SceneRequests::Delete, deleteIds);
  614. return true;
  615. }
  616. return false;
  617. }
  618. GraphModel::SlotId GraphController::ExtendSlot(GraphModel::NodePtr node, const GraphModel::SlotName& slotName)
  619. {
  620. GraphModel::SlotPtr newSlot = node->AddExtendedSlot(slotName);
  621. if (newSlot)
  622. {
  623. AZ::EntityId nodeUiId = m_elementMap.Find(node);
  624. CreateSlotUi(newSlot, nodeUiId);
  625. return newSlot->GetSlotId();
  626. }
  627. return GraphModel::SlotId();
  628. }
  629. GraphModel::NodePtr GraphController::GetNodeById(const GraphCanvas::NodeId& nodeId)
  630. {
  631. return m_elementMap.Find<GraphModel::Node>(nodeId);
  632. }
  633. GraphModel::NodePtrList GraphController::GetNodesFromGraphNodeIds(const AZStd::vector<GraphCanvas::NodeId>& nodeIds)
  634. {
  635. GraphModel::NodePtrList nodeList;
  636. nodeList.reserve(nodeIds.size());
  637. for (const auto& nodeId : nodeIds)
  638. {
  639. if (GraphModel::NodePtr nodePtr = m_elementMap.Find<GraphModel::Node>(nodeId))
  640. {
  641. nodeList.push_back(nodePtr);
  642. }
  643. }
  644. return nodeList;
  645. }
  646. GraphCanvas::NodeId GraphController::GetNodeIdByNode(GraphModel::NodePtr node) const
  647. {
  648. const GraphCanvas::NodeId nodeId = m_elementMap.Find(node);
  649. return nodeId.IsValid() ? nodeId : GraphCanvas::NodeId();
  650. }
  651. GraphCanvas::SlotId GraphController::GetSlotIdBySlot(GraphModel::SlotPtr slot) const
  652. {
  653. const GraphCanvas::SlotId slotId = m_elementMap.Find(slot);
  654. return slotId.IsValid() ? slotId : GraphCanvas::SlotId();
  655. }
  656. GraphModel::NodePtrList GraphController::GetNodes()
  657. {
  658. const auto& nodeMap = m_graph->GetNodes();
  659. GraphModel::NodePtrList nodes;
  660. nodes.reserve(nodeMap.size());
  661. for (auto& pair : nodeMap)
  662. {
  663. nodes.push_back(pair.second);
  664. }
  665. return nodes;
  666. }
  667. GraphModel::NodePtrList GraphController::GetSelectedNodes()
  668. {
  669. AzToolsFramework::EntityIdList selectedNodeIds;
  670. GraphCanvas::SceneRequestBus::EventResult(selectedNodeIds, m_graphCanvasSceneId, &GraphCanvas::SceneRequests::GetSelectedItems);
  671. return GetNodesFromGraphNodeIds(selectedNodeIds);
  672. }
  673. void GraphController::SetSelected(GraphModel::NodePtrList nodes, bool selected)
  674. {
  675. for (const auto& node : nodes)
  676. {
  677. const AZ::EntityId nodeId = m_elementMap.Find(node);
  678. if (nodeId.IsValid())
  679. {
  680. GraphCanvas::SceneMemberUIRequestBus::Event(nodeId, &GraphCanvas::SceneMemberUIRequests::SetSelected, selected);
  681. SaveMetadata(nodeId);
  682. }
  683. }
  684. }
  685. void GraphController::ClearSelection()
  686. {
  687. GraphCanvas::SceneRequestBus::Event(m_graphCanvasSceneId, &GraphCanvas::SceneRequests::ClearSelection);
  688. }
  689. void GraphController::EnableNode(GraphModel::NodePtr node)
  690. {
  691. const AZ::EntityId nodeId = m_elementMap.Find(node);
  692. if (nodeId.IsValid())
  693. {
  694. GraphCanvas::SceneRequestBus::Event(m_graphCanvasSceneId, &GraphCanvas::SceneRequests::Enable, nodeId);
  695. }
  696. }
  697. void GraphController::DisableNode(GraphModel::NodePtr node)
  698. {
  699. const AZ::EntityId nodeId = m_elementMap.Find(node);
  700. if (nodeId.IsValid())
  701. {
  702. GraphCanvas::SceneRequestBus::Event(m_graphCanvasSceneId, &GraphCanvas::SceneRequests::Disable, nodeId);
  703. }
  704. }
  705. void GraphController::CenterOnNodes(GraphModel::NodePtrList nodes)
  706. {
  707. AZStd::vector<AZ::Vector3> points;
  708. points.reserve(nodes.size() * 2);
  709. // Find all the position points for our nodes are are selecting
  710. // The Aabb class has functionality for creating a box from a series of points
  711. // so we are using that/Vector3 and just ignoring the Z value
  712. for (const auto& node : nodes)
  713. {
  714. const AZ::EntityId nodeId = m_elementMap.Find(node);
  715. AZ::Vector2 position = AZ::Vector2::CreateZero();
  716. GraphCanvas::GeometryRequestBus::EventResult(position, nodeId, &GraphCanvas::GeometryRequests::GetPosition);
  717. // Add the top-left corner position of the node
  718. points.emplace_back(position);
  719. // Add the bottom-right corner position of the node as well, so that
  720. // when we center the view, it will contain the entire node
  721. QGraphicsItem* nodeItem = nullptr;
  722. GraphCanvas::SceneMemberUIRequestBus::EventResult(nodeItem, nodeId, &GraphCanvas::SceneMemberUIRequests::GetRootGraphicsItem);
  723. if (nodeItem)
  724. {
  725. const QRectF nodeRect = nodeItem->boundingRect();
  726. points.emplace_back(position + AZ::Vector2(aznumeric_cast<float>(nodeRect.width()), aznumeric_cast<float>(nodeRect.height())));
  727. }
  728. }
  729. // Create a bounding box using all of our points so that we can center around
  730. // all of the nodes
  731. const AZ::Aabb boundingBox = AZ::Aabb::CreatePoints(points.data(), (int)points.size());
  732. const AZ::Vector3 topLeft = boundingBox.GetMin();
  733. QRectF boundingRect(topLeft.GetX(), topLeft.GetY(), boundingBox.GetXExtent(), boundingBox.GetYExtent());
  734. // Center the view on our desired area
  735. GraphCanvas::ViewId viewId;
  736. GraphCanvas::SceneRequestBus::EventResult(viewId, m_graphCanvasSceneId, &GraphCanvas::SceneRequests::GetViewId);
  737. GraphCanvas::ViewRequestBus::Event(viewId, &GraphCanvas::ViewRequests::CenterOnArea, boundingRect);
  738. }
  739. AZ::Vector2 GraphController::GetMajorPitch() const
  740. {
  741. AZ::EntityId gridId;
  742. AZ::Vector2 gridMajorPitch;
  743. GraphCanvas::SceneRequestBus::EventResult(gridId, m_graphCanvasSceneId, &GraphCanvas::SceneRequests::GetGrid);
  744. GraphCanvas::GridRequestBus::EventResult(gridMajorPitch, gridId, &GraphCanvas::GridRequests::GetMajorPitch);
  745. return gridMajorPitch;
  746. }
  747. void GraphController::OnNodeAdded(const AZ::EntityId& nodeUiId, bool)
  748. {
  749. if (const GraphModel::NodePtr node = m_elementMap.Find<GraphModel::Node>(nodeUiId))
  750. {
  751. GraphControllerNotificationBus::Event(m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelNodeAdded, node);
  752. }
  753. }
  754. void GraphController::OnNodeRemoved(const AZ::EntityId& nodeUiId)
  755. {
  756. if (const GraphModel::NodePtr node = m_elementMap.Find<GraphModel::Node>(nodeUiId))
  757. {
  758. GraphControllerNotificationBus::Event(m_graphCanvasSceneId, &GraphControllerNotifications::PreOnGraphModelNodeRemoved, node);
  759. // Remove any thumbnail reference for this node when it is removed from the graph
  760. // The ThumbnailItem will be deleted by the Node layout itself
  761. m_nodeThumbnails.erase(node->GetId());
  762. // When a node gets removed, we need to remove all of its slots
  763. // from our m_elementMap as well
  764. for (const auto& it : node->GetSlots())
  765. {
  766. m_elementMap.Remove(it.second);
  767. }
  768. m_graph->RemoveNode(node);
  769. m_elementMap.Remove(node);
  770. GraphControllerNotificationBus::Event(m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelNodeRemoved, node);
  771. }
  772. }
  773. void GraphController::OnConnectionAdded(const AZ::EntityId& connectionUiId)
  774. {
  775. if (const GraphModel::ConnectionPtr connection = m_elementMap.Find<GraphModel::Connection>(connectionUiId))
  776. {
  777. GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetSourceNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
  778. GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetTargetNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
  779. }
  780. }
  781. void GraphController::OnConnectionRemoved(const AZ::EntityId& connectionUiId)
  782. {
  783. if (const GraphModel::ConnectionPtr connection = m_elementMap.Find<GraphModel::Connection>(connectionUiId))
  784. {
  785. GraphControllerNotificationBus::Event(
  786. m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelConnectionRemoved, connection);
  787. GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetSourceNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
  788. GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetTargetNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
  789. m_graph->RemoveConnection(connection);
  790. m_elementMap.Remove(connection);
  791. }
  792. }
  793. void GraphController::OnEntitiesSerialized(GraphCanvas::GraphSerialization& serializationTarget)
  794. {
  795. GraphModelSerialization serialization;
  796. // Create mappings of the serialized nodes/slots so that we can properly associate
  797. // the GraphCanvas nodes/slots that get deserialized later with the GraphModel counterparts
  798. const auto& nodeWrappings = m_graph->GetNodeWrappings();
  799. for (const auto& nodeEntity : serializationTarget.GetGraphData().m_nodes)
  800. {
  801. const GraphCanvas::NodeId nodeUiId = nodeEntity->GetId();
  802. const GraphModel::NodePtr node = m_elementMap.Find<GraphModel::Node>(nodeUiId);
  803. if (!node)
  804. {
  805. continue;
  806. }
  807. // Keep a mapping of the serialized GraphCanvas nodeId with the serialized GraphModel node
  808. // The node is being serialized to an object stream and deserialized as needed. This prevents any objects or smart pointers from
  809. // lingering after related systems have been destroyed.
  810. GraphModelSerialization::SerializedNodeBuffer serializedNodeBuffer;
  811. AZ::IO::ByteContainerStream<decltype(serializedNodeBuffer)> serializedNodeStream(&serializedNodeBuffer);
  812. AZ::Utils::SaveObjectToStream(serializedNodeStream, AZ::ObjectStream::ST_BINARY, &node);
  813. serialization.m_serializedNodes[nodeUiId] = serializedNodeBuffer;
  814. // Keep a mapping of the serialized GraphCanvas slotIds with their serialized GraphModel slots
  815. serialization.m_serializedSlotMappings[nodeUiId] = GraphModelSerialization::SerializedSlotMapping();
  816. for (const auto& slotPair : node->GetSlots())
  817. {
  818. const GraphModel::SlotId& slotId = slotPair.first;
  819. const GraphModel::SlotPtr slot = slotPair.second;
  820. const AZ::EntityId slotUiId = m_elementMap.Find(slot);
  821. if (slotUiId.IsValid())
  822. {
  823. serialization.m_serializedSlotMappings[nodeUiId][slotId] = slotUiId;
  824. }
  825. }
  826. // Keep track of any serialized wrapped nodes, since these will need to be
  827. // handled separately after the deserialization is complete
  828. if (const auto it = nodeWrappings.find(node->GetId()); it != nodeWrappings.end())
  829. {
  830. GraphModel::NodePtr wrapperNode = m_graph->GetNode(it->second.first);
  831. AZ::u32 layoutOrder = it->second.second;
  832. AZ::EntityId wrapperNodeUiId = m_elementMap.Find(wrapperNode);
  833. AZ_Assert(wrapperNodeUiId.IsValid(), "Invalid wrapper node reference for node [%d]", wrapperNode->GetId());
  834. serialization.m_serializedNodeWrappings[nodeUiId] = AZStd::make_pair(wrapperNodeUiId, layoutOrder);
  835. }
  836. }
  837. GraphManagerRequestBus::Broadcast(&GraphManagerRequests::SetSerializedMappings, serialization);
  838. }
  839. void GraphController::OnEntitiesDeserialized(const GraphCanvas::GraphSerialization& serializationSource)
  840. {
  841. GraphModelSerialization serialization;
  842. GraphManagerRequestBus::BroadcastResult(serialization, &GraphManagerRequests::GetSerializedMappings);
  843. for (const auto& it : serialization.m_serializedNodes)
  844. {
  845. const auto& serializedNodeId = it.first;
  846. const auto serializedNodeBuffer = it.second;
  847. // Recreate the notes previously serialized to the stream.
  848. GraphModel::NodePtr newNode;
  849. AZ::IO::ByteContainerStream<decltype(serializedNodeBuffer)> serializedNodeStream(&serializedNodeBuffer);
  850. AZ::Utils::LoadObjectFromStreamInPlace(serializedNodeStream, newNode);
  851. // Load the new node into our graph
  852. m_graph->PostLoadSetup(newNode);
  853. // Re-map our new node to the deserialized GraphCanvas node
  854. AZ::EntityId newNodeUiId = serializationSource.FindRemappedEntityId(serializedNodeId);
  855. m_elementMap.Add(newNodeUiId, newNode);
  856. auto slotMapIt = serialization.m_serializedSlotMappings.find(serializedNodeId);
  857. if (slotMapIt == serialization.m_serializedSlotMappings.end())
  858. {
  859. continue;
  860. }
  861. const GraphModelSerialization::SerializedSlotMapping& serializedNodeSlots = slotMapIt->second;
  862. for (auto slotPair : newNode->GetSlots())
  863. {
  864. GraphModel::SlotId& slotId = slotPair.first;
  865. GraphModel::SlotPtr slot = slotPair.second;
  866. auto slotIt = serializedNodeSlots.find(slotId);
  867. if (slotIt == serializedNodeSlots.end())
  868. {
  869. continue;
  870. }
  871. GraphCanvas::SlotId serializedSlotUiId = slotIt->second;
  872. GraphCanvas::SlotId newSlotUiId = serializationSource.FindRemappedEntityId(serializedSlotUiId);
  873. if (!newSlotUiId.IsValid())
  874. {
  875. continue;
  876. }
  877. // Re-map our new slot to the deserialized GraphCanvas slot
  878. m_elementMap.Add(newSlotUiId, slot);
  879. }
  880. }
  881. }
  882. void GraphController::OnEntitiesDeserializationComplete(const GraphCanvas::GraphSerialization& serializationSource)
  883. {
  884. GraphModelSerialization serialization;
  885. GraphManagerRequestBus::BroadcastResult(serialization, &GraphManagerRequests::GetSerializedMappings);
  886. // We need to handle the wrapped nodes after all the nodes have been deserialized
  887. // so that the wrapper nodes will be active/ready to accept the nodes being
  888. // wrapped onto them.
  889. for (auto it : serialization.m_serializedNodeWrappings)
  890. {
  891. GraphCanvas::NodeId serializedNodeId = it.first;
  892. GraphCanvas::NodeId wrapperNodeId = it.second.first;
  893. AZ::u32 layoutOrder = it.second.second;
  894. AZ::EntityId newNodeId = serializationSource.FindRemappedEntityId(serializedNodeId);
  895. AZ::EntityId newWrapperNodeId = serializationSource.FindRemappedEntityId(wrapperNodeId);
  896. GraphModel::NodePtr newNode = m_elementMap.Find<GraphModel::Node>(newNodeId);
  897. GraphModel::NodePtr newWrapperNode = m_elementMap.Find<GraphModel::Node>(newWrapperNodeId);
  898. if (newNode && newWrapperNode)
  899. {
  900. WrapNodeInternal(newWrapperNode, newNode, layoutOrder);
  901. }
  902. }
  903. }
  904. void GraphController::OnNodeIsBeingEdited(bool isBeingEditeed)
  905. {
  906. if (isBeingEditeed)
  907. {
  908. GraphCanvas::GraphModelRequestBus::Event(
  909. m_graphCanvasSceneId, &GraphCanvas::GraphModelRequests::RequestPushPreventUndoStateUpdate);
  910. }
  911. else
  912. {
  913. GraphCanvas::GraphModelRequestBus::Event(
  914. m_graphCanvasSceneId, &GraphCanvas::GraphModelRequests::RequestPopPreventUndoStateUpdate);
  915. GraphCanvas::GraphModelRequestBus::Event(m_graphCanvasSceneId, &GraphCanvas::GraphModelRequests::RequestUndoPoint);
  916. }
  917. }
  918. void GraphController::OnSelectionChanged()
  919. {
  920. bool loading = false;
  921. GraphCanvas::SceneRequestBus::EventResult(loading, GetGraphCanvasSceneId(), &GraphCanvas::SceneRequests::IsLoading);
  922. bool pasting = false;
  923. GraphCanvas::SceneRequestBus::EventResult(pasting, GetGraphCanvasSceneId(), &GraphCanvas::SceneRequests::IsPasting);
  924. if (loading || pasting)
  925. {
  926. return;
  927. }
  928. // Save selection save data and other metadata every time the selection changes unless this is during a reload or a face operation
  929. SaveMetadata(GetGraphCanvasSceneId());
  930. GraphCanvas::SceneRequestBus::Event(
  931. GetGraphCanvasSceneId(),
  932. [&](GraphCanvas::SceneRequests* scene)
  933. {
  934. for (const auto& element : scene->GetNodes())
  935. {
  936. SaveMetadata(element);
  937. }
  938. for (const auto& element : scene->GetConnections())
  939. {
  940. SaveMetadata(element);
  941. }
  942. });
  943. }
  944. void GraphController::WriteSaveData(GraphCanvas::EntitySaveDataContainer& saveDataContainer) const
  945. {
  946. // Store selection save data for the current graph element
  947. const AZ::EntityId nodeUiId = *GraphCanvas::EntitySaveDataRequestBus::GetCurrentBusId();
  948. // Save data will only be stored for selected items to minimize file size
  949. bool selected = false;
  950. GraphCanvas::SceneMemberUIRequestBus::EventResult(selected, nodeUiId, &GraphCanvas::SceneMemberUIRequests::IsSelected);
  951. if (selected)
  952. {
  953. auto data = saveDataContainer.FindCreateSaveData<GraphCanvasSelectionData>();
  954. data->m_selected = selected;
  955. }
  956. }
  957. void GraphController::ReadSaveData(const GraphCanvas::EntitySaveDataContainer& saveDataContainer)
  958. {
  959. // Restore selection and position data for the current graph element
  960. const AZ::EntityId nodeUiId = *GraphCanvas::EntitySaveDataRequestBus::GetCurrentBusId();
  961. if (auto data = saveDataContainer.FindSaveData<GraphCanvasSelectionData>())
  962. {
  963. GraphCanvas::SceneMemberUIRequestBus::Event(nodeUiId, &GraphCanvas::SceneMemberUIRequests::SetSelected, data->m_selected);
  964. }
  965. if (auto data = saveDataContainer.FindSaveData<GraphCanvas::GeometrySaveData>())
  966. {
  967. GraphCanvas::GeometryRequestBus::Event(nodeUiId, &GraphCanvas::GeometryRequests::SetPosition, data->m_position);
  968. }
  969. }
  970. GraphModel::ConnectionPtr GraphController::CreateConnection(GraphModel::SlotPtr sourceSlot, GraphModel::SlotPtr targetSlot)
  971. {
  972. if (!sourceSlot || !targetSlot)
  973. {
  974. return nullptr;
  975. }
  976. // Remove existing connections on target slot
  977. for (GraphModel::ConnectionPtr connection : targetSlot->GetConnections())
  978. {
  979. RemoveConnection(connection);
  980. // No need to clean up the maps here because the OnConnectionRemoved() callback will handle that
  981. }
  982. GraphModel::ConnectionPtr connection = m_graph->AddConnection(sourceSlot, targetSlot);
  983. GraphControllerNotificationBus::Event(
  984. m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelConnectionAdded, connection);
  985. GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetSourceNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
  986. GraphCanvas::NodeUIRequestBus::Event(m_elementMap.Find(connection->GetTargetNode()), &GraphCanvas::NodeUIRequests::AdjustSize);
  987. return connection;
  988. }
  989. bool GraphController::CreateConnection(
  990. const AZ::EntityId& connectionUiId, const GraphCanvas::Endpoint& sourcePoint, const GraphCanvas::Endpoint& targetPoint)
  991. {
  992. using namespace GraphModel;
  993. if (m_isCreatingConnectionUi)
  994. {
  995. // We're already creating the connection further up the callstack
  996. return true;
  997. }
  998. if (!sourcePoint.IsValid() || !targetPoint.IsValid())
  999. {
  1000. return false;
  1001. }
  1002. SlotPtr sourceSlot = m_elementMap.Find<Slot>(sourcePoint.GetSlotId());
  1003. SlotPtr targetSlot = m_elementMap.Find<Slot>(targetPoint.GetSlotId());
  1004. // Handle the cases where this Connection already exists in our model
  1005. ConnectionPtr connection = m_elementMap.Find<Connection>(connectionUiId);
  1006. if (connection)
  1007. {
  1008. // If the connection being created has the same source and target as the existing connection, then either:
  1009. // 1. The user cancelled the action after disconnecting from the slot
  1010. // OR
  1011. // 2. The user reconnected to the same slot after disconnecting it
  1012. // So in either case, we can just return true since the model should remain the same and
  1013. // GraphCanvas has already done the right thing display wise
  1014. if (connection->GetSourceSlot() == sourceSlot && connection->GetTargetSlot() == targetSlot)
  1015. {
  1016. return true;
  1017. }
  1018. // Otherwise, the user has disconnected an existing connection from a slot and
  1019. // has connected it to a different slot, so we need to remove the pre-existing
  1020. // Connection from our model. GraphCanvas has already deleted the previous connection
  1021. // from the UI when GraphCanvas::GraphModelRequestBus::DisconnectConnection is invoked.
  1022. else
  1023. {
  1024. OnConnectionRemoved(connectionUiId);
  1025. }
  1026. }
  1027. ConnectionPtr newConnection = CreateConnection(sourceSlot, targetSlot);
  1028. if (newConnection)
  1029. {
  1030. m_elementMap.Add(connectionUiId, newConnection);
  1031. return true;
  1032. }
  1033. return false;
  1034. }
  1035. bool GraphController::CheckForLoopback(GraphModel::NodePtr sourceNode, GraphModel::NodePtr targetNode) const
  1036. {
  1037. // TODO: In the future, we could add support here for the client to choose if
  1038. // loopbacks should be supported or not.
  1039. // If at any point the target and source nodes are the same,
  1040. // then we've detected a connection loop
  1041. if (targetNode == sourceNode)
  1042. {
  1043. return true;
  1044. }
  1045. for (auto slotIt : sourceNode->GetSlots())
  1046. {
  1047. GraphModel::SlotPtr slot = slotIt.second;
  1048. // We only care about input slots because we are crawling upstream
  1049. if (slot->GetSlotDirection() != GraphModel::SlotDirection::Input)
  1050. {
  1051. continue;
  1052. }
  1053. // Check for loopback on any of the connected input slots
  1054. for (GraphModel::ConnectionPtr connection : slot->GetConnections())
  1055. {
  1056. if (CheckForLoopback(connection->GetSourceNode(), targetNode))
  1057. {
  1058. return true;
  1059. }
  1060. }
  1061. }
  1062. return false;
  1063. }
  1064. bool GraphController::IsValidConnection(const GraphCanvas::Endpoint& sourcePoint, const GraphCanvas::Endpoint& targetPoint) const
  1065. {
  1066. if (!sourcePoint.IsValid() || !targetPoint.IsValid())
  1067. {
  1068. return false;
  1069. }
  1070. auto sourceSlot = m_elementMap.Find<GraphModel::Slot>(sourcePoint.GetSlotId());
  1071. auto targetSlot = m_elementMap.Find<GraphModel::Slot>(targetPoint.GetSlotId());
  1072. // Make sure both slots are in our element map
  1073. if (!sourceSlot || !targetSlot)
  1074. {
  1075. return false;
  1076. }
  1077. bool dataTypesMatch = false;
  1078. GraphModel::DataTypePtr sourceSlotDataType = sourceSlot->GetDataType();
  1079. GraphModel::DataTypePtr targetSlotDataType = targetSlot->GetDataType();
  1080. if (sourceSlotDataType == nullptr && targetSlotDataType == nullptr)
  1081. {
  1082. // If both data types are null, this means the slots are both event types,
  1083. // so this is considered valid
  1084. AZ_Assert(
  1085. sourceSlot->GetSlotType() == GraphModel::SlotType::Event, "Source slot has a null data type but is not an Event type slot");
  1086. AZ_Assert(
  1087. targetSlot->GetSlotType() == GraphModel::SlotType::Event, "Target slot has a null data type but is not an Event type slot");
  1088. dataTypesMatch = true;
  1089. }
  1090. else if (sourceSlotDataType == nullptr || targetSlotDataType == nullptr)
  1091. {
  1092. // If one of the data types is null but the other isn't, then this is invalid
  1093. dataTypesMatch = false;
  1094. }
  1095. else
  1096. {
  1097. // The source slot data type must be supported by the target slot
  1098. dataTypesMatch = targetSlot->IsSupportedDataType(sourceSlotDataType);
  1099. }
  1100. return dataTypesMatch && !CheckForLoopback(sourceSlot->GetParentNode(), targetSlot->GetParentNode());
  1101. }
  1102. //! Helper function to create a GraphCanvas::NodePropertyDisplay and a data interface for editing input pin values
  1103. //! \typename DataInterfaceType One of the data interface types. Ex: BooleanDataInterface
  1104. //! \typename CreateDisplayFunctionType Function pointer type should be filled automatically
  1105. //! \param inputSlot the input slot
  1106. //! \param createDisplayFunction GraphCanvasRequests EBus function for creating the NodePropertyDisplay
  1107. template<typename DataInterfaceType, typename CreateDisplayFunctionType>
  1108. GraphCanvas::NodePropertyDisplay* CreatePropertyDisplay(GraphModel::SlotPtr inputSlot, CreateDisplayFunctionType createDisplayFunction)
  1109. {
  1110. GraphCanvas::NodePropertyDisplay* dataDisplay = nullptr;
  1111. if (inputSlot)
  1112. {
  1113. GraphCanvas::DataInterface* dataInterface = aznew DataInterfaceType(inputSlot);
  1114. GraphCanvas::GraphCanvasRequestBus::BroadcastResult(
  1115. dataDisplay, createDisplayFunction, static_cast<DataInterfaceType*>(dataInterface));
  1116. if (!dataDisplay)
  1117. {
  1118. delete dataInterface;
  1119. }
  1120. }
  1121. return dataDisplay;
  1122. }
  1123. GraphCanvas::NodePropertyDisplay* GraphController::CreatePropertySlotPropertyDisplay(
  1124. [[maybe_unused]] const AZ::Crc32& propertyId,
  1125. [[maybe_unused]] const GraphCanvas::NodeId& nodeUiId,
  1126. const GraphCanvas::SlotId& slotUiId) const
  1127. {
  1128. // CONST CAST WARNING: The CreatePropertySlotPropertyDisplay graph canvas interface is const, but probably shouldn't be, because it
  1129. // expects a non-const NodePropertyDisplay*. We need non-const version of m_elementMap in order to create a non-const
  1130. // NodePropertyDisplay
  1131. GraphModel::SlotPtr inputSlot = const_cast<GraphController*>(this)->m_elementMap.Find<GraphModel::Slot>(slotUiId);
  1132. GraphCanvas::NodePropertyDisplay* display = CreateSlotPropertyDisplay(inputSlot);
  1133. if (display)
  1134. {
  1135. display->SetNodeId(nodeUiId);
  1136. display->SetSlotId(slotUiId);
  1137. }
  1138. return display;
  1139. }
  1140. GraphCanvas::NodePropertyDisplay* GraphController::CreateDataSlotPropertyDisplay(
  1141. [[maybe_unused]] const AZ::Uuid& dataTypeUuid,
  1142. [[maybe_unused]] const GraphCanvas::NodeId& nodeUiId,
  1143. const GraphCanvas::SlotId& slotUiId) const
  1144. {
  1145. #if defined(AZ_ENABLE_TRACING)
  1146. GraphModel::DataTypePtr dataType = m_graph->GetContext()->GetDataType(dataTypeUuid);
  1147. AZ_Assert(
  1148. dataType->GetTypeUuid() == dataTypeUuid,
  1149. "Creating property display for mismatched type. dataTypeUuid=%s. Slot TypeName=%s TypeID=%s.",
  1150. dataTypeUuid.ToString<AZStd::string>().c_str(),
  1151. dataType->GetCppName().c_str(),
  1152. dataType->GetTypeUuidString().c_str());
  1153. #endif // AZ_ENABLE_TRACING
  1154. // CONST CAST WARNING: The CreateDataSlotPropertyDisplay graph canvas interface is const, but probably shouldn't be, because it
  1155. // expects a non-const NodePropertyDisplay*. We need non-const version of m_elementMap in order to create a non-const
  1156. // NodePropertyDisplay
  1157. GraphModel::SlotPtr inputSlot = const_cast<GraphController*>(this)->m_elementMap.Find<GraphModel::Slot>(slotUiId);
  1158. GraphCanvas::NodePropertyDisplay* display = CreateSlotPropertyDisplay(inputSlot);
  1159. if (display)
  1160. {
  1161. display->SetNodeId(nodeUiId);
  1162. display->SetSlotId(slotUiId);
  1163. }
  1164. return display;
  1165. }
  1166. GraphCanvas::NodePropertyDisplay* GraphController::CreateSlotPropertyDisplay(GraphModel::SlotPtr inputSlot) const
  1167. {
  1168. if (!inputSlot || !inputSlot->IsVisibleOnNode() || !inputSlot->IsEditableOnNode())
  1169. {
  1170. return nullptr;
  1171. }
  1172. AZ_Assert(
  1173. inputSlot->GetSlotDirection() == GraphModel::SlotDirection::Input, "Property value displays are only meant for input slots");
  1174. const AZ::Uuid dataTypeUuid = inputSlot->GetDataType()->GetTypeUuid();
  1175. if (dataTypeUuid == azrtti_typeid<bool>())
  1176. {
  1177. return CreatePropertyDisplay<BooleanDataInterface>(
  1178. inputSlot, &GraphCanvas::GraphCanvasRequests::CreateBooleanNodePropertyDisplay);
  1179. }
  1180. if (dataTypeUuid == azrtti_typeid<int>())
  1181. {
  1182. return CreatePropertyDisplay<IntegerDataInterface<int>>(
  1183. inputSlot, &GraphCanvas::GraphCanvasRequests::CreateNumericNodePropertyDisplay);
  1184. }
  1185. if (dataTypeUuid == azrtti_typeid<unsigned int>())
  1186. {
  1187. return CreatePropertyDisplay<IntegerDataInterface<unsigned int>>(
  1188. inputSlot, &GraphCanvas::GraphCanvasRequests::CreateNumericNodePropertyDisplay);
  1189. }
  1190. if (dataTypeUuid == azrtti_typeid<long>())
  1191. {
  1192. return CreatePropertyDisplay<IntegerDataInterface<long>>(
  1193. inputSlot, &GraphCanvas::GraphCanvasRequests::CreateNumericNodePropertyDisplay);
  1194. }
  1195. if (dataTypeUuid == azrtti_typeid<unsigned long>())
  1196. {
  1197. return CreatePropertyDisplay<IntegerDataInterface<unsigned long>>(
  1198. inputSlot, &GraphCanvas::GraphCanvasRequests::CreateNumericNodePropertyDisplay);
  1199. }
  1200. if (dataTypeUuid == azrtti_typeid<float>())
  1201. {
  1202. return CreatePropertyDisplay<FloatDataInterface>(
  1203. inputSlot, &GraphCanvas::GraphCanvasRequests::CreateNumericNodePropertyDisplay);
  1204. }
  1205. if (dataTypeUuid == azrtti_typeid<AZ::Vector2>())
  1206. {
  1207. return CreatePropertyDisplay<VectorDataInterface<AZ::Vector2, 2>>(
  1208. inputSlot, &GraphCanvas::GraphCanvasRequests::CreateVectorNodePropertyDisplay);
  1209. }
  1210. if (dataTypeUuid == azrtti_typeid<AZ::Vector3>())
  1211. {
  1212. return CreatePropertyDisplay<VectorDataInterface<AZ::Vector3, 3>>(
  1213. inputSlot, &GraphCanvas::GraphCanvasRequests::CreateVectorNodePropertyDisplay);
  1214. }
  1215. if (dataTypeUuid == azrtti_typeid<AZ::Vector4>())
  1216. {
  1217. return CreatePropertyDisplay<VectorDataInterface<AZ::Vector4, 4>>(
  1218. inputSlot, &GraphCanvas::GraphCanvasRequests::CreateVectorNodePropertyDisplay);
  1219. }
  1220. if (dataTypeUuid == azrtti_typeid<AZStd::string>())
  1221. {
  1222. return CreatePropertyDisplay<StringDataInterface>(
  1223. inputSlot, &GraphCanvas::GraphCanvasRequests::CreateStringNodePropertyDisplay);
  1224. }
  1225. return nullptr;
  1226. }
  1227. void GraphController::RequestUndoPoint()
  1228. {
  1229. if (m_preventUndoStateUpdateCount <= 0)
  1230. {
  1231. m_preventUndoStateUpdateCount = 0;
  1232. GraphControllerNotificationBus::Event(m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelRequestUndoPoint);
  1233. IntegrationBus::Broadcast(&IntegrationBusInterface::SignalSceneDirty, m_graphCanvasSceneId);
  1234. }
  1235. }
  1236. void GraphController::RequestPushPreventUndoStateUpdate()
  1237. {
  1238. ++m_preventUndoStateUpdateCount;
  1239. }
  1240. void GraphController::RequestPopPreventUndoStateUpdate()
  1241. {
  1242. if (m_preventUndoStateUpdateCount > 0)
  1243. {
  1244. --m_preventUndoStateUpdateCount;
  1245. }
  1246. }
  1247. void GraphController::TriggerUndo()
  1248. {
  1249. GraphControllerNotificationBus::Event(m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelTriggerUndo);
  1250. }
  1251. void GraphController::TriggerRedo()
  1252. {
  1253. GraphControllerNotificationBus::Event(m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelTriggerRedo);
  1254. }
  1255. void GraphController::EnableNodes(const AZStd::unordered_set<GraphCanvas::NodeId>& nodeIds)
  1256. {
  1257. AZ_UNUSED(nodeIds);
  1258. }
  1259. void GraphController::DisableNodes(const AZStd::unordered_set<GraphCanvas::NodeId>& nodeIds)
  1260. {
  1261. AZ_UNUSED(nodeIds);
  1262. }
  1263. AZStd::string GraphController::GetDataTypeString(const AZ::Uuid& typeId)
  1264. {
  1265. return m_graph->GetContext()->GetDataType(typeId)->GetDisplayName();
  1266. }
  1267. GraphCanvasMetadata* GraphController::GetGraphMetadata()
  1268. {
  1269. GraphCanvasMetadata* graphCanvasMetadata = &m_graph->GetUiMetadata();
  1270. AZ_Assert(graphCanvasMetadata, "GraphCanvasMetadata not initialized");
  1271. return graphCanvasMetadata;
  1272. }
  1273. void GraphController::OnSaveDataDirtied(const AZ::EntityId& savedElement)
  1274. {
  1275. SaveMetadata(savedElement);
  1276. }
  1277. void GraphController::ResetSlotToDefaultValue(const GraphCanvas::Endpoint& endpoint)
  1278. {
  1279. if (auto slot = m_elementMap.Find<GraphModel::Slot>(endpoint.GetSlotId()))
  1280. {
  1281. GraphCanvas::ScopedGraphUndoBatch undoBatch(m_graphCanvasSceneId);
  1282. slot->SetValue(slot->GetDefaultValue());
  1283. }
  1284. }
  1285. void GraphController::RemoveSlot(const GraphCanvas::Endpoint& endpoint)
  1286. {
  1287. GraphCanvas::ScopedGraphUndoBatch undoBatch(m_graphCanvasSceneId);
  1288. const GraphCanvas::NodeId& nodeId = endpoint.GetNodeId();
  1289. const GraphCanvas::SlotId& slotId = endpoint.GetSlotId();
  1290. auto node = m_elementMap.Find<GraphModel::Node>(nodeId);
  1291. auto slot = m_elementMap.Find<GraphModel::Slot>(slotId);
  1292. if (node && slot)
  1293. {
  1294. node->DeleteSlot(slot);
  1295. // We need to actually remove the slot, the GraphModelRequestBus::RemoveSlot is a request, not a notification that the slot has
  1296. // been removed
  1297. GraphCanvas::NodeRequestBus::Event(nodeId, &GraphCanvas::NodeRequests::RemoveSlot, slotId);
  1298. }
  1299. }
  1300. bool GraphController::IsSlotRemovable(const GraphCanvas::Endpoint& endpoint) const
  1301. {
  1302. auto node = m_elementMap.Find<GraphModel::Node>(endpoint.GetNodeId());
  1303. auto slot = m_elementMap.Find<GraphModel::Slot>(endpoint.GetSlotId());
  1304. return node && slot && node->CanDeleteSlot(slot);
  1305. }
  1306. GraphCanvas::SlotId GraphController::RequestExtension(
  1307. const GraphCanvas::NodeId& nodeId, const GraphCanvas::ExtenderId& extenderId, GraphModelRequests::ExtensionRequestReason)
  1308. {
  1309. GraphCanvas::ScopedGraphUndoBatch undoBatch(m_graphCanvasSceneId);
  1310. GraphModel::NodePtr node = m_elementMap.Find<GraphModel::Node>(nodeId);
  1311. if (!node)
  1312. {
  1313. return GraphCanvas::SlotId{};
  1314. }
  1315. auto it = m_nodeExtenderIds.find(nodeId);
  1316. if (it == m_nodeExtenderIds.end())
  1317. {
  1318. return GraphCanvas::SlotId{};
  1319. }
  1320. auto extenderIt = it->second.find(extenderId);
  1321. if (extenderIt == it->second.end())
  1322. {
  1323. return GraphCanvas::SlotId{};
  1324. }
  1325. // The extension request will usually result in a new slot being added, unless
  1326. // the maximum allowed slots for that definition has been reached, or the
  1327. // Node has overriden the extension handling and rejected the new slot
  1328. const GraphModel::SlotName& slotName = extenderIt->second;
  1329. const GraphModel::SlotId& newSlotId = ExtendSlot(node, slotName);
  1330. GraphModel::SlotPtr newSlot = node->GetSlot(newSlotId);
  1331. if (!newSlot)
  1332. {
  1333. return GraphCanvas::SlotId{};
  1334. }
  1335. return m_elementMap.Find(newSlot);
  1336. }
  1337. bool GraphController::ShouldWrapperAcceptDrop(const GraphCanvas::NodeId& wrapperNode, const QMimeData* mimeData) const
  1338. {
  1339. AZ_UNUSED(wrapperNode);
  1340. AZ_UNUSED(mimeData);
  1341. return false;
  1342. }
  1343. void GraphController::AddWrapperDropTarget(const GraphCanvas::NodeId& wrapperNode)
  1344. {
  1345. AZ_UNUSED(wrapperNode);
  1346. }
  1347. void GraphController::RemoveWrapperDropTarget(const GraphCanvas::NodeId& wrapperNode)
  1348. {
  1349. AZ_UNUSED(wrapperNode);
  1350. }
  1351. void GraphController::SaveMetadata(const AZ::EntityId& graphCanvasElement)
  1352. {
  1353. bool loading = false;
  1354. GraphCanvas::SceneRequestBus::EventResult(loading, GetGraphCanvasSceneId(), &GraphCanvas::SceneRequests::IsLoading);
  1355. bool pasting = false;
  1356. GraphCanvas::SceneRequestBus::EventResult(pasting, GetGraphCanvasSceneId(), &GraphCanvas::SceneRequests::IsPasting);
  1357. if (loading || pasting)
  1358. {
  1359. return;
  1360. }
  1361. using namespace GraphModel;
  1362. GraphCanvasMetadata* graphCanvasMetadata = GetGraphMetadata();
  1363. // Connect the EntitySaveDataRequestBus using a router. This will allow the graph controller to inject custom selection save
  1364. // data for all graph canvas objects. This should only be connected while serializing save data for the current graph.
  1365. GraphCanvas::EntitySaveDataRequestBus::Router::BusRouterConnect();
  1366. // Save into m_nodeMetadata
  1367. if (const auto node = m_elementMap.Find<Node>(graphCanvasElement))
  1368. {
  1369. auto container = AZStd::make_shared<GraphCanvas::EntitySaveDataContainer>();
  1370. GraphCanvas::EntitySaveDataRequestBus::Event(
  1371. graphCanvasElement, &GraphCanvas::EntitySaveDataRequests::WriteSaveData, *container);
  1372. const NodeId nodeId = node->GetId();
  1373. graphCanvasMetadata->m_nodeMetadata[nodeId] = container;
  1374. GraphControllerNotificationBus::Event(m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelGraphModified, node);
  1375. }
  1376. // Save into m_sceneMetadata
  1377. else if (graphCanvasElement == GetGraphCanvasSceneId())
  1378. {
  1379. auto container = AZStd::make_shared<GraphCanvas::EntitySaveDataContainer>();
  1380. GraphCanvas::EntitySaveDataRequestBus::Event(
  1381. graphCanvasElement, &GraphCanvas::EntitySaveDataRequests::WriteSaveData, *container);
  1382. graphCanvasMetadata->m_sceneMetadata = container;
  1383. GraphControllerNotificationBus::Event(m_graphCanvasSceneId, &GraphControllerNotifications::OnGraphModelGraphModified, nullptr);
  1384. }
  1385. // Disconnect the EntitySaveDataRequestBus after save data serialization as completed
  1386. GraphCanvas::EntitySaveDataRequestBus::Router::BusRouterDisconnect();
  1387. }
  1388. QGraphicsLinearLayout* GraphController::GetLayoutFromNode(GraphModel::NodePtr node)
  1389. {
  1390. AZ::EntityId nodeUiId = m_elementMap.Find(node);
  1391. QGraphicsLayout* layout = nullptr;
  1392. GraphCanvas::NodeLayoutRequestBus::EventResult(layout, nodeUiId, &GraphCanvas::NodeLayoutRequests::GetLayout);
  1393. if (layout)
  1394. {
  1395. // We can't do qobject_cast or dynamic_cast here since it doesn't derive from QObject
  1396. // and it's a Qt class that wasn't compiled with rtti. This layout is created by
  1397. // GraphCanvas though so we can rely on knowing the type.
  1398. QGraphicsLinearLayout* linearLayout = static_cast<QGraphicsLinearLayout*>(layout);
  1399. return linearLayout;
  1400. }
  1401. return nullptr;
  1402. }
  1403. void GraphController::SetThumbnailImageOnNode(GraphModel::NodePtr node, const QPixmap& image)
  1404. {
  1405. auto it = m_nodeThumbnails.find(node->GetId());
  1406. if (it != m_nodeThumbnails.end())
  1407. {
  1408. // Update the image if the thumbnail already existed
  1409. ThumbnailImageItem* item = azrtti_cast<ThumbnailImageItem*>(it->second);
  1410. AZ_Assert(item, "Mismatch trying to set default image on a custom ThumbnailItem");
  1411. item->UpdateImage(image);
  1412. }
  1413. else
  1414. {
  1415. // Find the layout for this Node so we can insert our thumbnail
  1416. QGraphicsLinearLayout* layout = GetLayoutFromNode(node);
  1417. if (!layout)
  1418. {
  1419. return;
  1420. }
  1421. // Create a new thumbnail item if we didn't have one before
  1422. // The layout takes ownership of the item when inserted
  1423. ThumbnailImageItem* newItem = new ThumbnailImageItem(image);
  1424. layout->insertItem(NODE_THUMBNAIL_INDEX, newItem);
  1425. m_nodeThumbnails[node->GetId()] = newItem;
  1426. }
  1427. }
  1428. void GraphController::SetThumbnailOnNode(GraphModel::NodePtr node, ThumbnailItem* item)
  1429. {
  1430. // Remove any existing thumbnail on this node if one already exists
  1431. auto it = m_nodeThumbnails.find(node->GetId());
  1432. if (it != m_nodeThumbnails.end())
  1433. {
  1434. RemoveThumbnailFromNode(node);
  1435. }
  1436. QGraphicsLinearLayout* layout = GetLayoutFromNode(node);
  1437. if (!layout)
  1438. {
  1439. AZ_Assert(false, "Couldn't find a layout for the node");
  1440. return;
  1441. }
  1442. // Add the custom thumbnail item to the node
  1443. layout->insertItem(NODE_THUMBNAIL_INDEX, item);
  1444. m_nodeThumbnails[node->GetId()] = item;
  1445. }
  1446. void GraphController::RemoveThumbnailFromNode(GraphModel::NodePtr node)
  1447. {
  1448. auto it = m_nodeThumbnails.find(node->GetId());
  1449. if (it != m_nodeThumbnails.end())
  1450. {
  1451. // Remove the thumbnail from our local tracking
  1452. ThumbnailItem* item = it->second;
  1453. m_nodeThumbnails.erase(it);
  1454. QGraphicsLinearLayout* layout = GetLayoutFromNode(node);
  1455. if (!layout)
  1456. {
  1457. AZ_Assert(false, "Couldn't find a layout for the node");
  1458. return;
  1459. }
  1460. // Remove our item from the node layout, which releases ownership from the layout
  1461. layout->removeItem(item);
  1462. // If this was one of our ThumbnailImageItem's, then we need to delete it ourselves
  1463. // since we allocated it. If someone created their own custom ThumbnailItem and
  1464. // set it using SetThumbnailOnNode, they are in charge of deleting it after
  1465. // calling RemoveThumbnailFromNode.
  1466. ThumbnailImageItem* imageItem = azrtti_cast<ThumbnailImageItem*>(item);
  1467. if (imageItem)
  1468. {
  1469. delete item;
  1470. }
  1471. }
  1472. }
  1473. } // namespace GraphModelIntegration