CollapsedNodeGroupComponent.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  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. #include <QGraphicsWidget>
  9. #include <QTimer>
  10. #include <QSequentialAnimationGroup>
  11. #include <AzCore/std/containers/unordered_map.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <Components/Nodes/Group/CollapsedNodeGroupComponent.h>
  14. #include <Components/LayerControllerComponent.h>
  15. #include <Components/Nodes/General/GeneralNodeLayoutComponent.h>
  16. #include <GraphCanvas/Components/Connections/ConnectionBus.h>
  17. #include <GraphCanvas/Components/Nodes/Comment/CommentBus.h>
  18. #include <GraphCanvas/Components/Nodes/NodeTitleBus.h>
  19. #include <GraphCanvas/Components/Slots/SlotBus.h>
  20. #include <GraphCanvas/GraphCanvasBus.h>
  21. #include <GraphCanvas/Editor/GraphModelBus.h>
  22. #include <GraphCanvas/Utils/ConversionUtils.h>
  23. namespace GraphCanvas
  24. {
  25. //////////////////////////
  26. // RedirectedSlotWatcher
  27. //////////////////////////
  28. RedirectedSlotWatcher::~RedirectedSlotWatcher()
  29. {
  30. NodeNotificationBus::MultiHandler::BusDisconnect();
  31. }
  32. void RedirectedSlotWatcher::ConfigureWatcher(const AZ::EntityId& collapsedGroupId)
  33. {
  34. m_collapsedGroupId = collapsedGroupId;
  35. NodeNotificationBus::MultiHandler::BusDisconnect();
  36. }
  37. void RedirectedSlotWatcher::RegisterEndpoint(const Endpoint& sourceEndpoint, const Endpoint& remappedEndpoint)
  38. {
  39. m_endpointMapping[sourceEndpoint] = remappedEndpoint;
  40. NodeNotificationBus::MultiHandler::BusConnect(sourceEndpoint.GetNodeId());
  41. }
  42. void RedirectedSlotWatcher::OnNodeAboutToBeDeleted()
  43. {
  44. const AZ::EntityId* nodeRemoved = NodeNotificationBus::GetCurrentBusId();
  45. if (nodeRemoved)
  46. {
  47. auto mapIter = m_endpointMapping.begin();
  48. while (mapIter != m_endpointMapping.end())
  49. {
  50. if (mapIter->first.GetNodeId() == (*nodeRemoved))
  51. {
  52. NodeRequestBus::Event(m_collapsedGroupId, &NodeRequests::RemoveSlot, mapIter->second.GetSlotId());
  53. mapIter = m_endpointMapping.erase(mapIter);
  54. }
  55. else
  56. {
  57. ++mapIter;
  58. }
  59. }
  60. NodeNotificationBus::MultiHandler::BusDisconnect((*nodeRemoved));
  61. }
  62. }
  63. void RedirectedSlotWatcher::OnSlotRemovedFromNode(const AZ::EntityId& slotId)
  64. {
  65. const AZ::EntityId* nodeSource = NodeNotificationBus::GetCurrentBusId();
  66. if (nodeSource)
  67. {
  68. Endpoint sourceEndpoint((*nodeSource), slotId);
  69. auto endpointIter = m_endpointMapping.find(sourceEndpoint);
  70. if (endpointIter != m_endpointMapping.end())
  71. {
  72. NodeRequestBus::Event(m_collapsedGroupId, &NodeRequests::RemoveSlot, endpointIter->second.GetSlotId());
  73. m_endpointMapping.erase(endpointIter);
  74. bool maintainConnection = false;
  75. for (const auto& mapPair : m_endpointMapping)
  76. {
  77. if (mapPair.first.GetNodeId() == (*nodeSource))
  78. {
  79. maintainConnection = true;
  80. break;
  81. }
  82. }
  83. if (!maintainConnection)
  84. {
  85. NodeNotificationBus::MultiHandler::BusDisconnect((*nodeSource));
  86. }
  87. }
  88. }
  89. }
  90. ////////////////////////////////
  91. // CollapsedNodeGroupComponent
  92. ////////////////////////////////
  93. constexpr int k_collapsingAnimationTimeMS = 175;
  94. constexpr int k_fadeInTimeMS = 50;
  95. // 0.9f found through the scientific process of it looking right.
  96. constexpr float k_endpointanimationTimeSec = (k_collapsingAnimationTimeMS/1000.0f) * 0.9f;
  97. // General frame delay to ensure that qt has updated and refreshed its display so that everything looks right.
  98. // 3 is just a magic number found through visual testing.
  99. constexpr int k_qtFrameDelay = 3;
  100. void CollapsedNodeGroupComponent::Reflect(AZ::ReflectContext* context)
  101. {
  102. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  103. if (serializeContext)
  104. {
  105. serializeContext->Class<SlotRedirectionConfiguration>()
  106. ->Version(1)
  107. ->Field("Name", &SlotRedirectionConfiguration::m_name)
  108. ->Field("TargetId", &SlotRedirectionConfiguration::m_targetEndpoint)
  109. ;
  110. serializeContext->Class<CollapsedNodeGroupComponent, GraphCanvasPropertyComponent>()
  111. ->Version(1)
  112. ;
  113. }
  114. }
  115. AZ::Entity* CollapsedNodeGroupComponent::CreateCollapsedNodeGroupEntity(const CollapsedNodeGroupConfiguration& config)
  116. {
  117. AZ::Entity* nodeEntity = GeneralNodeLayoutComponent::CreateGeneralNodeEntity(".collapsedGroup", config);
  118. nodeEntity->CreateComponent<CollapsedNodeGroupComponent>(config);
  119. return nodeEntity;
  120. }
  121. CollapsedNodeGroupComponent::CollapsedNodeGroupComponent()
  122. : GraphCanvasPropertyComponent()
  123. , m_animationDelayCounter(0)
  124. , m_isExpandingOccluderAnimation(false)
  125. , m_occluderDestructionCounter(0)
  126. , m_unhideOnAnimationComplete(false)
  127. , m_deleteObjects(true)
  128. , m_positionDirty(false)
  129. , m_ignorePositionChanges(true)
  130. {
  131. // Two part animation.
  132. QSequentialAnimationGroup* opacityGroup = new QSequentialAnimationGroup();
  133. QPropertyAnimation* delayAnimation = new QPropertyAnimation();
  134. delayAnimation->setDuration((k_collapsingAnimationTimeMS - k_fadeInTimeMS));
  135. opacityGroup->addAnimation(delayAnimation);
  136. m_opacityAnimation = new QPropertyAnimation();
  137. m_opacityAnimation->setDuration(k_fadeInTimeMS);
  138. m_opacityAnimation->setPropertyName("opacity");
  139. m_opacityAnimation->setStartValue(1.0f);
  140. m_opacityAnimation->setEndValue(0.25f);
  141. opacityGroup->addAnimation(m_opacityAnimation);
  142. opacityGroup->setLoopCount(1);
  143. m_occluderAnimation.addAnimation(opacityGroup);
  144. m_sizeAnimation = new QPropertyAnimation();
  145. m_sizeAnimation->setDuration(k_collapsingAnimationTimeMS);
  146. m_sizeAnimation->setPropertyName("size");
  147. m_occluderAnimation.addAnimation(m_sizeAnimation);
  148. m_positionAnimation = new QPropertyAnimation();
  149. m_positionAnimation->setDuration(k_collapsingAnimationTimeMS);
  150. m_positionAnimation->setPropertyName("pos");
  151. m_occluderAnimation.addAnimation(m_positionAnimation);
  152. QObject::connect(&m_occluderAnimation, &QAnimationGroup::finished, [this]()
  153. {
  154. OnAnimationFinished();
  155. });
  156. }
  157. CollapsedNodeGroupComponent::CollapsedNodeGroupComponent(const CollapsedNodeGroupConfiguration& config)
  158. : CollapsedNodeGroupComponent()
  159. {
  160. m_nodeGroupId = config.m_nodeGroupId;
  161. }
  162. CollapsedNodeGroupComponent::~CollapsedNodeGroupComponent()
  163. {
  164. }
  165. void CollapsedNodeGroupComponent::Init()
  166. {
  167. GraphCanvasPropertyComponent::Init();
  168. m_memberHiddenStateSetter.AddStateController(&m_ignorePositionChanges);
  169. m_memberDraggedStateSetter.AddStateController(&m_ignorePositionChanges);
  170. }
  171. void CollapsedNodeGroupComponent::Activate()
  172. {
  173. GraphCanvasPropertyComponent::Activate();
  174. AZ::EntityId entityId = GetEntityId();
  175. m_redirectedSlotWatcher.ConfigureWatcher(entityId);
  176. CollapsedNodeGroupRequestBus::Handler::BusConnect(entityId);
  177. VisualNotificationBus::Handler::BusConnect(entityId);
  178. NodeNotificationBus::Handler::BusConnect(entityId);
  179. SceneMemberNotificationBus::Handler::BusConnect(entityId);
  180. GeometryNotificationBus::Handler::BusConnect(entityId);
  181. }
  182. void CollapsedNodeGroupComponent::Deactivate()
  183. {
  184. GraphCanvasPropertyComponent::Deactivate();
  185. GroupableSceneMemberNotificationBus::Handler::BusDisconnect();
  186. CommentNotificationBus::Handler::BusDisconnect();
  187. GeometryNotificationBus::Handler::BusDisconnect();
  188. SceneMemberNotificationBus::Handler::BusDisconnect();
  189. NodeNotificationBus::Handler::BusDisconnect();
  190. VisualNotificationBus::Handler::BusDisconnect();
  191. CollapsedNodeGroupRequestBus::Handler::BusDisconnect();
  192. AZ::SystemTickBus::Handler::BusDisconnect();
  193. }
  194. void CollapsedNodeGroupComponent::OnSystemTick()
  195. {
  196. // Delay count for Qt to catch up with the visuals so I can animate in a visually pleasing way.
  197. if (m_animationDelayCounter > 0)
  198. {
  199. m_animationDelayCounter--;
  200. if (m_animationDelayCounter <= 0)
  201. {
  202. AnimateOccluder(m_isExpandingOccluderAnimation);
  203. m_isExpandingOccluderAnimation = false;
  204. m_animationDelayCounter = 0;
  205. UpdateSystemTickBus();
  206. }
  207. }
  208. if (m_occluderDestructionCounter > 0)
  209. {
  210. m_occluderDestructionCounter--;
  211. if (m_occluderDestructionCounter <= 0)
  212. {
  213. AZ::EntityId graphId;
  214. SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
  215. if (m_effectId.IsValid())
  216. {
  217. SceneRequestBus::Event(graphId, &SceneRequests::CancelGraphicsEffect, m_effectId);
  218. m_effectId.SetInvalid();
  219. }
  220. m_occluderDestructionCounter = 0;
  221. UpdateSystemTickBus();
  222. CollapsedNodeGroupNotificationBus::Event(GetEntityId(), &CollapsedNodeGroupNotifications::OnExpansionComplete);
  223. AZStd::unordered_set<NodeId> deleteIds = { GetEntityId() };
  224. SceneRequestBus::Event(graphId, &SceneRequests::Delete, deleteIds);
  225. }
  226. }
  227. }
  228. void CollapsedNodeGroupComponent::OnAddedToScene(const GraphId& graphId)
  229. {
  230. SceneNotificationBus::Handler::BusConnect(graphId);
  231. m_containedSubGraphs.Clear();
  232. AZStd::string comment;
  233. CommentRequestBus::EventResult(comment, m_nodeGroupId, &CommentRequests::GetComment);
  234. NodeTitleRequestBus::Event(GetEntityId(), &NodeTitleRequests::SetTitle, comment);
  235. NodeTitleRequestBus::Event(GetEntityId(), &NodeTitleRequests::SetSubTitle, "Collapsed Node Group");
  236. AZ::Color color;
  237. NodeGroupRequestBus::EventResult(color, m_nodeGroupId, &NodeGroupRequests::GetGroupColor);
  238. OnBackgroundColorChanged(color);
  239. AZStd::vector< NodeId > groupedElements;
  240. NodeGroupRequestBus::Event(m_nodeGroupId, &NodeGroupRequests::FindGroupedElements, groupedElements);
  241. AZStd::vector< NodeId > elementsToManage;
  242. elementsToManage.reserve(groupedElements.size());
  243. AZStd::vector< NodeId > elementsToSearch = groupedElements;
  244. while (!elementsToSearch.empty())
  245. {
  246. AZ::EntityId searchedElement = elementsToSearch.front();
  247. elementsToSearch.erase(elementsToSearch.begin());
  248. if (GraphUtils::IsNodeGroup(searchedElement))
  249. {
  250. QGraphicsItem* graphicsItem = nullptr;
  251. SceneMemberUIRequestBus::EventResult(graphicsItem, searchedElement, &SceneMemberUIRequests::GetRootGraphicsItem);
  252. if (graphicsItem->isVisible())
  253. {
  254. elementsToManage.emplace_back(searchedElement);
  255. AZStd::vector<NodeId> subGroupedElements;
  256. NodeGroupRequestBus::Event(searchedElement, &NodeGroupRequests::FindGroupedElements, subGroupedElements);
  257. if (!subGroupedElements.empty())
  258. {
  259. elementsToSearch.insert(elementsToSearch.end(), subGroupedElements.begin(), subGroupedElements.end());
  260. elementsToManage.reserve(elementsToManage.size() + subGroupedElements.size());
  261. }
  262. }
  263. }
  264. else
  265. {
  266. elementsToManage.emplace_back(searchedElement);
  267. }
  268. }
  269. SubGraphParsingConfig config;
  270. config.m_ignoredGraphMembers.insert(GetEntityId());
  271. config.m_createNonConnectableSubGraph = true;
  272. m_containedSubGraphs = GraphUtils::ParseSceneMembersIntoSubGraphs(elementsToManage, config);
  273. ConstructGrouping(graphId);
  274. SetupGroupPosition(graphId);
  275. CommentNotificationBus::Handler::BusConnect(m_nodeGroupId);
  276. bool isLoading = false;
  277. SceneRequestBus::EventResult(isLoading, graphId, &SceneRequests::IsLoading);
  278. bool isPasting = false;
  279. SceneRequestBus::EventResult(isPasting, graphId, &SceneRequests::IsPasting);
  280. GroupableSceneMemberNotificationBus::Handler::BusConnect(GetEntityId());
  281. if (!isLoading && !isPasting)
  282. {
  283. CreateOccluder(graphId, m_nodeGroupId);
  284. // Node won't be the correct size right away, need to wait for Qt to tick an update.
  285. TriggerCollapseAnimation();
  286. }
  287. }
  288. void CollapsedNodeGroupComponent::OnRemovedFromScene(const GraphId& graphId)
  289. {
  290. if (m_effectId.IsValid())
  291. {
  292. SceneRequestBus::Event(graphId, &SceneRequests::CancelGraphicsEffect, m_effectId);
  293. m_effectId.SetInvalid();
  294. }
  295. if (m_deleteObjects)
  296. {
  297. GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestPushPreventUndoStateUpdate);
  298. SceneRequestBus::Event(graphId, &SceneRequests::Delete, m_containedSubGraphs.m_nonConnectableGraph.m_containedNodes);
  299. for (const GraphSubGraph& subGraph : m_containedSubGraphs.m_subGraphs)
  300. {
  301. SceneRequestBus::Event(graphId, &SceneRequests::Delete, subGraph.m_containedNodes);
  302. }
  303. AZStd::unordered_set< AZ::EntityId > deletionIds = { m_nodeGroupId };
  304. SceneRequestBus::Event(graphId, &SceneRequests::Delete, deletionIds);
  305. GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestPopPreventUndoStateUpdate);
  306. }
  307. SceneNotificationBus::Handler::BusDisconnect();
  308. AZ::SystemTickBus::Handler::BusDisconnect();
  309. }
  310. void CollapsedNodeGroupComponent::OnBoundsChanged()
  311. {
  312. if (AZ::SystemTickBus::Handler::BusIsConnected())
  313. {
  314. AZ::EntityId graphId;
  315. SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
  316. SetupGroupPosition(graphId);
  317. if (m_animationDelayCounter != 0)
  318. {
  319. m_animationDelayCounter = k_qtFrameDelay;
  320. }
  321. }
  322. }
  323. void CollapsedNodeGroupComponent::OnPositionChanged(const AZ::EntityId& /*targetEntity*/, const AZ::Vector2& position)
  324. {
  325. if (!m_ignorePositionChanges.GetState())
  326. {
  327. MoveGroupedElementsBy(position - m_previousPosition);
  328. m_previousPosition = position;
  329. }
  330. else
  331. {
  332. m_positionDirty = true;
  333. }
  334. }
  335. void CollapsedNodeGroupComponent::OnSceneMemberDragBegin()
  336. {
  337. m_memberDraggedStateSetter.SetState(true);
  338. }
  339. void CollapsedNodeGroupComponent::OnSceneMemberDragComplete()
  340. {
  341. m_memberDraggedStateSetter.ReleaseState();
  342. // This is a quick implementation of this. Really this shouldn't be necessary as I can just
  343. // calculate the offset when the group is broken and just apply the changes then.
  344. //
  345. // But for simplicity now, I'll do this the quick way and just update everything after each move.
  346. if (m_positionDirty)
  347. {
  348. m_positionDirty = false;
  349. AZ::Vector2 position;
  350. GeometryRequestBus::EventResult(position, GetEntityId(), &GeometryRequests::GetPosition);
  351. MoveGroupedElementsBy(position - m_previousPosition);
  352. m_previousPosition = position;
  353. }
  354. }
  355. void CollapsedNodeGroupComponent::OnCommentChanged(const AZStd::string& comment)
  356. {
  357. NodeTitleRequestBus::Event(GetEntityId(), &NodeTitleRequests::SetTitle, comment);
  358. NodeUIRequestBus::Event(GetEntityId(), &NodeUIRequests::AdjustSize);
  359. }
  360. void CollapsedNodeGroupComponent::OnBackgroundColorChanged(const AZ::Color& color)
  361. {
  362. QColor titleColor = ConversionUtils::AZToQColor(color);
  363. NodeTitleRequestBus::Event(GetEntityId(), &NodeTitleRequests::SetColorPaletteOverride, titleColor);
  364. }
  365. bool CollapsedNodeGroupComponent::OnMouseDoubleClick(const QGraphicsSceneMouseEvent* /*mouseEvent*/)
  366. {
  367. ExpandGroup();
  368. return true;
  369. }
  370. void CollapsedNodeGroupComponent::ExpandGroup()
  371. {
  372. GraphId graphId;
  373. SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
  374. ReverseGrouping(graphId);
  375. }
  376. AZ::EntityId CollapsedNodeGroupComponent::GetSourceGroup() const
  377. {
  378. return m_nodeGroupId;
  379. }
  380. AZStd::vector< Endpoint > CollapsedNodeGroupComponent::GetRedirectedEndpoints() const
  381. {
  382. AZStd::vector< Endpoint > redirectedEndpoints;
  383. for (const auto& redirectionConfigurations : m_redirections)
  384. {
  385. AZStd::unordered_set< Endpoint > remappedEndpoints = GraphUtils::RemapEndpointForModel(redirectionConfigurations.m_targetEndpoint);
  386. redirectedEndpoints.insert(redirectedEndpoints.begin(), remappedEndpoints.begin(), remappedEndpoints.end());
  387. }
  388. return redirectedEndpoints;
  389. }
  390. void CollapsedNodeGroupComponent::ForceEndpointRedirection(const AZStd::vector<Endpoint>& endpoints)
  391. {
  392. m_forcedRedirections.insert(endpoints.begin(), endpoints.end());
  393. }
  394. void CollapsedNodeGroupComponent::OnGroupChanged()
  395. {
  396. AZ::EntityId groupId;
  397. GroupableSceneMemberRequestBus::EventResult(groupId, GetEntityId(), &GroupableSceneMemberRequests::GetGroupId);
  398. if (groupId.IsValid())
  399. {
  400. NodeGroupRequestBus::Event(groupId, &NodeGroupRequests::AddElementToGroup, m_nodeGroupId);
  401. }
  402. else
  403. {
  404. GroupableSceneMemberRequestBus::Event(m_nodeGroupId, &GroupableSceneMemberRequests::RemoveFromGroup);
  405. }
  406. }
  407. void CollapsedNodeGroupComponent::OnSceneMemberHidden()
  408. {
  409. m_memberHiddenStateSetter.SetState(true);
  410. }
  411. void CollapsedNodeGroupComponent::OnSceneMemberShown()
  412. {
  413. m_memberHiddenStateSetter.ReleaseState();
  414. }
  415. void CollapsedNodeGroupComponent::SetupGroupPosition(const GraphId& /*graphId*/)
  416. {
  417. StateSetter<bool> ignorePositionSetter;
  418. ignorePositionSetter.AddStateController(&m_ignorePositionChanges);
  419. ignorePositionSetter.SetState(true);
  420. QPointF centerPoint;
  421. QGraphicsItem* blockItem = nullptr;
  422. SceneMemberUIRequestBus::EventResult(blockItem, m_nodeGroupId, &SceneMemberUIRequests::GetRootGraphicsItem);
  423. if (blockItem)
  424. {
  425. centerPoint = blockItem->sceneBoundingRect().center();
  426. }
  427. // Want to adjust the position of the node to make it a little more centered.
  428. // But the scene component will re-position it to the passed in location
  429. // before it attempts this part(and the node needs a frame to adjust it's sizing to be correct)
  430. //
  431. // So, fire off a single shot timer and hope we never create/delete this thing.
  432. QGraphicsItem* graphicsItem = nullptr;
  433. SceneMemberUIRequestBus::EventResult(graphicsItem, GetEntityId(), &SceneMemberUIRequests::GetRootGraphicsItem);
  434. QRectF boundingRect = graphicsItem->sceneBoundingRect();
  435. qreal width = boundingRect.width();
  436. qreal height = boundingRect.height();
  437. // Want the Collapsed Node Group to appear centered over top of the Node Group.
  438. AZ::Vector2 offset(aznumeric_cast<float>(width * 0.5f), aznumeric_cast<float>(height * 0.5f));
  439. m_previousPosition = ConversionUtils::QPointToVector(centerPoint);
  440. m_previousPosition -= offset;
  441. graphicsItem->setPos(ConversionUtils::AZToQPoint(m_previousPosition));
  442. GeometryRequestBus::Event(GetEntityId(), &GeometryRequests::SetPosition, m_previousPosition);
  443. // Reget the position we set since we might have snapped to a grid.
  444. GeometryRequestBus::EventResult(m_previousPosition, GetEntityId(), &GeometryRequests::GetPosition);
  445. m_positionDirty = false;
  446. }
  447. void CollapsedNodeGroupComponent::CreateOccluder(const GraphId& graphId, const AZ::EntityId& initialElement)
  448. {
  449. if (m_effectId.IsValid())
  450. {
  451. GraphCanvas::SceneRequestBus::Event(graphId, &GraphCanvas::SceneRequests::CancelGraphicsEffect, m_effectId);
  452. }
  453. QGraphicsItem* graphicsItem = nullptr;
  454. VisualRequestBus::EventResult(graphicsItem, initialElement, &VisualRequests::AsGraphicsItem);
  455. if (graphicsItem == nullptr)
  456. {
  457. return;
  458. }
  459. AZ::Color groupColor;
  460. NodeGroupRequestBus::EventResult(groupColor, m_nodeGroupId, &NodeGroupRequests::GetGroupColor);
  461. OccluderConfiguration configuration;
  462. configuration.m_renderColor = ConversionUtils::AZToQColor(groupColor);
  463. configuration.m_bounds = graphicsItem->sceneBoundingRect();
  464. configuration.m_zValue = LayerUtils::AlwaysOnTopZValue();
  465. SceneRequestBus::EventResult(m_effectId, graphId, &SceneRequests::CreateOccluder, configuration);
  466. }
  467. void CollapsedNodeGroupComponent::AnimateOccluder(bool isExpanding)
  468. {
  469. m_unhideOnAnimationComplete = isExpanding;
  470. AZ::EntityId graphId;
  471. SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
  472. if (!m_effectId.IsValid())
  473. {
  474. if (isExpanding)
  475. {
  476. CreateOccluder(graphId, GetEntityId());
  477. }
  478. else
  479. {
  480. CreateOccluder(graphId, m_nodeGroupId);
  481. }
  482. }
  483. if (!m_effectId.IsValid())
  484. {
  485. OnAnimationFinished();
  486. }
  487. QGraphicsItem* blockItem = nullptr;
  488. VisualRequestBus::EventResult(blockItem, m_nodeGroupId, &VisualRequests::AsGraphicsItem);
  489. if (blockItem == nullptr)
  490. {
  491. OnAnimationFinished();
  492. return;
  493. }
  494. QGraphicsItem* graphicsItem = nullptr;
  495. SceneMemberUIRequestBus::EventResult(graphicsItem, GetEntityId(), &SceneMemberUIRequests::GetRootGraphicsItem);
  496. if (graphicsItem == nullptr)
  497. {
  498. OnAnimationFinished();
  499. return;
  500. }
  501. QGraphicsItem* occluderItem = nullptr;
  502. GraphicsEffectRequestBus::EventResult(occluderItem, m_effectId, &GraphicsEffectRequests::AsQGraphicsItem);
  503. if (occluderItem)
  504. {
  505. QRectF startRect = occluderItem->sceneBoundingRect();
  506. QRectF targetRect;
  507. if (isExpanding)
  508. {
  509. targetRect = blockItem->sceneBoundingRect();
  510. }
  511. else
  512. {
  513. targetRect = graphicsItem->sceneBoundingRect();
  514. }
  515. QGraphicsObject* occluderObject = static_cast<QGraphicsObject*>(occluderItem);
  516. m_sizeAnimation->setTargetObject(occluderObject);
  517. m_sizeAnimation->setStartValue(startRect.size());
  518. m_sizeAnimation->setEndValue(targetRect.size());
  519. m_positionAnimation->setTargetObject(occluderObject);
  520. m_positionAnimation->setStartValue(startRect.topLeft());
  521. m_positionAnimation->setEndValue(targetRect.topLeft());
  522. m_opacityAnimation->setTargetObject(occluderObject);
  523. m_occluderAnimation.start();
  524. }
  525. }
  526. void CollapsedNodeGroupComponent::ConstructGrouping(const GraphId& graphId)
  527. {
  528. bool isLoading = false;
  529. SceneRequestBus::EventResult(isLoading, graphId, &SceneRequests::IsLoading);
  530. bool isPasting = false;
  531. SceneRequestBus::EventResult(isPasting, graphId, &SceneRequests::IsPasting);
  532. // Keeps track of a mapping from the raw slot Id to the corresponding slot endpoint that we created.
  533. AZStd::unordered_map< SlotId, SlotId > internalSlotMappings;
  534. OrderedEndpointSet sourceEndpointOrdering;
  535. AZStd::unordered_multimap< Endpoint, ConnectionId > sourceEndpointRemapping;
  536. OrderedEndpointSet targetEndpointOrdering;
  537. AZStd::unordered_multimap< Endpoint, ConnectionId > targetEndpointRemapping;
  538. for (const NodeId& nodeId : m_containedSubGraphs.m_nonConnectableGraph.m_containedNodes)
  539. {
  540. SceneRequestBus::Event(graphId, &SceneRequests::Hide, nodeId);
  541. }
  542. for (const Endpoint& forcedEndpoint : m_forcedRedirections)
  543. {
  544. ConnectionType connectionType = ConnectionType::CT_Invalid;
  545. SlotRequestBus::EventResult(connectionType, forcedEndpoint.GetSlotId(), &SlotRequests::GetConnectionType);
  546. if (connectionType == ConnectionType::CT_Input)
  547. {
  548. targetEndpointOrdering.insert(EndpointOrderingStruct::ConstructOrderingInformation(forcedEndpoint));
  549. }
  550. else if (connectionType == ConnectionType::CT_Output)
  551. {
  552. sourceEndpointOrdering.insert(EndpointOrderingStruct::ConstructOrderingInformation(forcedEndpoint));
  553. }
  554. }
  555. for (const GraphSubGraph& subGraph : m_containedSubGraphs.m_subGraphs)
  556. {
  557. for (const ConnectionId& connectionId : subGraph.m_entryConnections)
  558. {
  559. Endpoint targetEndpoint;
  560. ConnectionRequestBus::EventResult(targetEndpoint, connectionId, &ConnectionRequests::GetTargetEndpoint);
  561. targetEndpointOrdering.insert(EndpointOrderingStruct::ConstructOrderingInformation(targetEndpoint));
  562. targetEndpointRemapping.insert(AZStd::make_pair(targetEndpoint, connectionId));
  563. }
  564. for (const ConnectionId& connectionId : subGraph.m_exitConnections)
  565. {
  566. Endpoint sourceEndpoint;
  567. ConnectionRequestBus::EventResult(sourceEndpoint, connectionId, &ConnectionRequests::GetSourceEndpoint);
  568. sourceEndpointOrdering.insert(EndpointOrderingStruct::ConstructOrderingInformation(sourceEndpoint));
  569. sourceEndpointRemapping.insert(AZStd::make_pair(sourceEndpoint, connectionId));
  570. }
  571. for (const NodeId& nodeId : subGraph.m_containedNodes)
  572. {
  573. SceneRequestBus::Event(graphId, &SceneRequests::Hide, nodeId);
  574. }
  575. for (const ConnectionId& connectionId : subGraph.m_innerConnections)
  576. {
  577. SceneRequestBus::Event(graphId, &SceneRequests::Hide, connectionId);
  578. }
  579. }
  580. for (const EndpointOrderingStruct& targetEndpointStruct : targetEndpointOrdering)
  581. {
  582. auto slotIter = internalSlotMappings.find(targetEndpointStruct.m_endpoint.GetSlotId());
  583. if (slotIter == internalSlotMappings.end())
  584. {
  585. SlotId redirectionSlotId = CreateSlotRedirection(graphId, targetEndpointStruct.m_endpoint);
  586. auto insertIter = internalSlotMappings.insert(AZStd::make_pair(targetEndpointStruct.m_endpoint.GetSlotId(), redirectionSlotId));
  587. if (insertIter.second)
  588. {
  589. SlotRequestBus::Event(redirectionSlotId, &SlotRequests::RemapSlotForModel, targetEndpointStruct.m_endpoint);
  590. slotIter = insertIter.first;
  591. }
  592. }
  593. if (slotIter == internalSlotMappings.end())
  594. {
  595. continue;
  596. }
  597. Endpoint mappedTargetEndpoint = Endpoint(GetEntityId(), slotIter->second);
  598. auto iterPair = targetEndpointRemapping.equal_range(targetEndpointStruct.m_endpoint);
  599. for (auto mapIter = iterPair.first; mapIter != iterPair.second; ++mapIter)
  600. {
  601. if (isLoading || isPasting)
  602. {
  603. ConnectionRequestBus::Event(mapIter->second, &ConnectionRequests::SnapTargetDisplayTo, mappedTargetEndpoint);
  604. }
  605. else
  606. {
  607. ConnectionRequestBus::Event(mapIter->second, &ConnectionRequests::AnimateTargetDisplayTo, mappedTargetEndpoint, k_endpointanimationTimeSec);
  608. }
  609. }
  610. }
  611. for (const EndpointOrderingStruct& sourceEndpointStruct : sourceEndpointOrdering)
  612. {
  613. auto slotIter = internalSlotMappings.find(sourceEndpointStruct.m_endpoint.GetSlotId());
  614. if (slotIter == internalSlotMappings.end())
  615. {
  616. SlotId redirectionSlotId = CreateSlotRedirection(graphId, sourceEndpointStruct.m_endpoint);
  617. auto insertIter = internalSlotMappings.insert(AZStd::make_pair(sourceEndpointStruct.m_endpoint.GetSlotId(), redirectionSlotId));
  618. if (insertIter.second)
  619. {
  620. SlotRequestBus::Event(redirectionSlotId, &SlotRequests::RemapSlotForModel, sourceEndpointStruct.m_endpoint);
  621. slotIter = insertIter.first;
  622. }
  623. }
  624. if (slotIter == internalSlotMappings.end())
  625. {
  626. continue;
  627. }
  628. Endpoint mappedSourceEndpoint = Endpoint(GetEntityId(), slotIter->second);
  629. auto iterPair = sourceEndpointRemapping.equal_range(sourceEndpointStruct.m_endpoint);
  630. for (auto mapIter = iterPair.first; mapIter != iterPair.second; ++mapIter)
  631. {
  632. if (isLoading || isPasting)
  633. {
  634. ConnectionRequestBus::Event(mapIter->second, &ConnectionRequests::SnapSourceDisplayTo, mappedSourceEndpoint);
  635. }
  636. else
  637. {
  638. ConnectionRequestBus::Event(mapIter->second, &ConnectionRequests::AnimateSourceDisplayTo, mappedSourceEndpoint, k_endpointanimationTimeSec);
  639. }
  640. }
  641. }
  642. SceneRequestBus::Event(graphId, &SceneRequests::Hide, m_nodeGroupId);
  643. }
  644. void CollapsedNodeGroupComponent::ReverseGrouping(const GraphId& graphId)
  645. {
  646. AZStd::vector< SlotId > slotIds;
  647. NodeRequestBus::EventResult(slotIds, GetEntityId(), &NodeRequests::GetSlotIds);
  648. for (const SlotId& slotId : slotIds)
  649. {
  650. ConnectionType connectionType = CT_Invalid;
  651. SlotRequestBus::EventResult(connectionType, slotId, &SlotRequests::GetConnectionType);
  652. if (connectionType != CT_Invalid && connectionType != CT_None)
  653. {
  654. AZStd::vector< Endpoint > redirectedEndpoints;
  655. SlotRequestBus::EventResult(redirectedEndpoints, slotId, &SlotRequests::GetRemappedModelEndpoints);
  656. AZ_Assert(redirectedEndpoints.size() == 1, "A single slot being redirected to multiple slots is not currently supported.");
  657. if (redirectedEndpoints.size() == 0)
  658. {
  659. continue;
  660. }
  661. Endpoint redirectedEndpoint = redirectedEndpoints.front();
  662. AZStd::vector< ConnectionId > connectionIds;
  663. SlotRequestBus::EventResult(connectionIds, slotId, &SlotRequests::GetConnections);
  664. for (const ConnectionId& connectionId : connectionIds)
  665. {
  666. if (connectionType == CT_Input)
  667. {
  668. ConnectionRequestBus::Event(connectionId, &ConnectionRequests::AnimateTargetDisplayTo, redirectedEndpoint, k_endpointanimationTimeSec);
  669. }
  670. else if (connectionType == CT_Output)
  671. {
  672. ConnectionRequestBus::Event(connectionId, &ConnectionRequests::AnimateSourceDisplayTo, redirectedEndpoint, k_endpointanimationTimeSec);
  673. }
  674. }
  675. }
  676. }
  677. CreateOccluder(graphId, GetEntityId());
  678. TriggerExpandAnimation();
  679. SceneRequestBus::Event(graphId, &SceneRequests::Hide, GetEntityId());
  680. }
  681. void CollapsedNodeGroupComponent::TriggerExpandAnimation()
  682. {
  683. m_animationDelayCounter = k_qtFrameDelay;
  684. m_isExpandingOccluderAnimation = true;
  685. VisualRequestBus::Event(GetEntityId(), &VisualRequests::SetVisible, false);
  686. UpdateSystemTickBus();
  687. }
  688. void CollapsedNodeGroupComponent::TriggerCollapseAnimation()
  689. {
  690. m_animationDelayCounter = k_qtFrameDelay;
  691. m_isExpandingOccluderAnimation = false;
  692. VisualRequestBus::Event(GetEntityId(), &VisualRequests::SetVisible, false);
  693. UpdateSystemTickBus();
  694. }
  695. void CollapsedNodeGroupComponent::MoveGroupedElementsBy(const AZ::Vector2& offset)
  696. {
  697. GraphId graphId;
  698. SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
  699. GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestPushPreventUndoStateUpdate);
  700. // Update the NodeGroup
  701. {
  702. AZ::Vector2 position;
  703. GeometryRequestBus::EventResult(position, m_nodeGroupId, &GeometryRequests::GetPosition);
  704. position += offset;
  705. // TODO: Potentially fix the collapsed node groups
  706. GeometryRequestBus::Event(m_nodeGroupId, &GeometryRequests::SetPosition, position);
  707. }
  708. GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestPopPreventUndoStateUpdate);
  709. }
  710. void CollapsedNodeGroupComponent::MoveSubGraphBy(const GraphSubGraph& subGraph, const AZ::Vector2& offset)
  711. {
  712. for (const NodeId& nodeId : subGraph.m_containedNodes)
  713. {
  714. AZ::Vector2 position;
  715. GeometryRequestBus::EventResult(position, nodeId, &GeometryRequests::GetPosition);
  716. position += offset;
  717. GeometryRequestBus::Event(nodeId, &GeometryRequests::SetPosition, position);
  718. }
  719. }
  720. void CollapsedNodeGroupComponent::OnAnimationFinished()
  721. {
  722. GraphId graphId;
  723. SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
  724. if (m_unhideOnAnimationComplete)
  725. {
  726. {
  727. ScopedGraphUndoBlocker undoBlocker(graphId);
  728. SceneRequestBus::Event(graphId, &SceneRequests::Show, m_nodeGroupId);
  729. for (const NodeId& nodeId : m_containedSubGraphs.m_nonConnectableGraph.m_containedNodes)
  730. {
  731. SceneRequestBus::Event(graphId, &SceneRequests::Show, nodeId);
  732. }
  733. for (const GraphSubGraph& subGraph : m_containedSubGraphs.m_subGraphs)
  734. {
  735. for (const NodeId& nodeId : subGraph.m_containedNodes)
  736. {
  737. SceneRequestBus::Event(graphId, &SceneRequests::Show, nodeId);
  738. }
  739. for (const ConnectionId& connectionId : subGraph.m_innerConnections)
  740. {
  741. SceneRequestBus::Event(graphId, &SceneRequests::Show, connectionId);
  742. }
  743. }
  744. AZ::EntityId groupId;
  745. GroupableSceneMemberRequestBus::EventResult(groupId, m_nodeGroupId, &GroupableSceneMemberRequests::GetGroupId);
  746. if (groupId.IsValid())
  747. {
  748. const bool growOnly = true;
  749. NodeGroupRequestBus::Event(groupId, &NodeGroupRequests::ResizeGroupToElements, growOnly);
  750. }
  751. m_deleteObjects = false;
  752. GroupableSceneMemberNotificationBus::Handler::BusDisconnect();
  753. // Want to delay removing the occluder, because the wrapper nodes sometime deform slightly and need a tick to visually
  754. // update.
  755. m_occluderDestructionCounter = k_qtFrameDelay;
  756. UpdateSystemTickBus();
  757. }
  758. GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestUndoPoint);
  759. }
  760. else
  761. {
  762. if (m_effectId.IsValid())
  763. {
  764. SceneRequestBus::Event(graphId, &SceneRequests::CancelGraphicsEffect, m_effectId);
  765. m_effectId.SetInvalid();
  766. }
  767. VisualRequestBus::Event(GetEntityId(), &VisualRequests::SetVisible, true);
  768. SceneMemberUIRequestBus::Event(GetEntityId(), &SceneMemberUIRequests::SetSelected, true);
  769. GraphUtils::SanityCheckEnabledState(GetEntityId());
  770. }
  771. }
  772. SlotId CollapsedNodeGroupComponent::CreateSlotRedirection(const GraphId& /*graphId*/, const Endpoint& endpoint)
  773. {
  774. m_redirections.emplace_back();
  775. SlotRedirectionConfiguration& configuration = m_redirections.back();
  776. configuration.m_targetEndpoint = endpoint;
  777. return InitializeRedirectionSlot(configuration);
  778. }
  779. SlotId CollapsedNodeGroupComponent::InitializeRedirectionSlot(const SlotRedirectionConfiguration& configuration)
  780. {
  781. SlotId retVal;
  782. SlotConfiguration* cloneConfiguration = nullptr;
  783. SlotRequestBus::EventResult(cloneConfiguration, configuration.m_targetEndpoint.GetSlotId(), &SlotRequests::CloneSlotConfiguration);
  784. if (cloneConfiguration)
  785. {
  786. if (!configuration.m_name.empty())
  787. {
  788. cloneConfiguration->m_name = configuration.m_name;
  789. }
  790. else
  791. {
  792. AZStd::string nodeTitle;
  793. NodeTitleRequestBus::EventResult(nodeTitle, configuration.m_targetEndpoint.GetNodeId(), &NodeTitleRequests::GetTitle);
  794. AZStd::string displayName = AZStd::string::format("%s:%s", nodeTitle.c_str(), cloneConfiguration->m_name.c_str());
  795. cloneConfiguration->m_name = displayName;
  796. }
  797. AZ::Entity* slotEntity = nullptr;
  798. GraphCanvasRequestBus::BroadcastResult(slotEntity, &GraphCanvasRequests::CreateSlot, GetEntityId(), (*cloneConfiguration));
  799. if (slotEntity)
  800. {
  801. slotEntity->Init();
  802. slotEntity->Activate();
  803. GraphCanvas::NodeRequestBus::Event(GetEntityId(), &GraphCanvas::NodeRequests::AddSlot, slotEntity->GetId());
  804. retVal = slotEntity->GetId();
  805. }
  806. delete cloneConfiguration;
  807. }
  808. Endpoint redirectedSlot(GetEntityId(), retVal);
  809. if (redirectedSlot.IsValid())
  810. {
  811. m_redirectedSlotWatcher.RegisterEndpoint(configuration.m_targetEndpoint, redirectedSlot);
  812. }
  813. return retVal;
  814. }
  815. void CollapsedNodeGroupComponent::UpdateSystemTickBus()
  816. {
  817. if (m_animationDelayCounter > 0 || m_occluderDestructionCounter > 0)
  818. {
  819. AZ::SystemTickBus::Handler::BusConnect();
  820. }
  821. else
  822. {
  823. AZ::SystemTickBus::Handler::BusDisconnect();
  824. }
  825. }
  826. }