ConnectionComponent.cpp 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129
  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 <qcursor.h>
  9. #include <qgraphicsscene.h>
  10. #include <qgraphicsview.h>
  11. #include <QToolTip>
  12. #include <AzCore/Serialization/EditContext.h>
  13. #include <AzQtComponents/Components/ToastNotification.h>
  14. #include <Components/Connections/ConnectionComponent.h>
  15. #include <Components/Connections/ConnectionLayerControllerComponent.h>
  16. #include <Components/Connections/ConnectionVisualComponent.h>
  17. #include <Components/StylingComponent.h>
  18. #include <GraphCanvas/Components/Nodes/NodeBus.h>
  19. #include <GraphCanvas/Components/Slots/SlotBus.h>
  20. #include <GraphCanvas/Components/Slots/Extender/ExtenderSlotBus.h>
  21. #include <GraphCanvas/Editor/AssetEditorBus.h>
  22. #include <GraphCanvas/Editor/GraphModelBus.h>
  23. #include <GraphCanvas/Utils/GraphUtils.h>
  24. #include <GraphCanvas/Utils/ConversionUtils.h>
  25. #include <GraphCanvas/Widgets/GraphCanvasGraphicsView/GraphCanvasGraphicsView.h>
  26. namespace GraphCanvas
  27. {
  28. ///////////////////////////////
  29. // ConnectionEndpointAnimator
  30. ///////////////////////////////
  31. ConnectionComponent::ConnectionEndpointAnimator::ConnectionEndpointAnimator()
  32. : m_isAnimating(false)
  33. , m_timer(0.0f)
  34. , m_maxTime(0.25f)
  35. {
  36. }
  37. bool ConnectionComponent::ConnectionEndpointAnimator::IsAnimating() const
  38. {
  39. return m_isAnimating;
  40. }
  41. void ConnectionComponent::ConnectionEndpointAnimator::AnimateToEndpoint(const QPointF& startPoint, const Endpoint& endPoint, float maxTime)
  42. {
  43. if (m_isAnimating)
  44. {
  45. m_startPosition = m_currentPosition;
  46. }
  47. else
  48. {
  49. m_isAnimating = true;
  50. m_startPosition = startPoint;
  51. }
  52. m_targetEndpoint = endPoint;
  53. m_timer = 0.0f;
  54. m_maxTime = AZ::GetMax(maxTime, 0.001f);
  55. m_currentPosition = m_startPosition;
  56. }
  57. QPointF ConnectionComponent::ConnectionEndpointAnimator::GetAnimatedPosition() const
  58. {
  59. return m_currentPosition;
  60. }
  61. bool ConnectionComponent::ConnectionEndpointAnimator::Tick(float deltaTime)
  62. {
  63. m_timer += deltaTime;
  64. QPointF targetPosition;
  65. SlotUIRequestBus::EventResult(targetPosition, m_targetEndpoint.m_slotId, &SlotUIRequests::GetConnectionPoint);
  66. if (m_timer >= m_maxTime)
  67. {
  68. m_isAnimating = false;
  69. m_currentPosition = targetPosition;
  70. }
  71. else
  72. {
  73. float lerpPercent = (m_timer / m_maxTime);
  74. m_currentPosition.setX(AZ::Lerp(static_cast<float>(m_startPosition.x()), static_cast<float>(targetPosition.x()), lerpPercent));
  75. m_currentPosition.setY(AZ::Lerp(static_cast<float>(m_startPosition.y()), static_cast<float>(targetPosition.y()), lerpPercent));
  76. }
  77. return m_isAnimating;
  78. }
  79. ////////////////////////
  80. // ConnectionComponent
  81. ////////////////////////
  82. void ConnectionComponent::Reflect(AZ::ReflectContext* context)
  83. {
  84. Endpoint::Reflect(context);
  85. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  86. if (!serializeContext)
  87. {
  88. return;
  89. }
  90. serializeContext->Class<ConnectionComponent, AZ::Component>()
  91. ->Version(3)
  92. ->Field("Source", &ConnectionComponent::m_sourceEndpoint)
  93. ->Field("Target", &ConnectionComponent::m_targetEndpoint)
  94. ->Field("Tooltip", &ConnectionComponent::m_tooltip)
  95. ->Field("UserData", &ConnectionComponent::m_userData)
  96. ;
  97. AZ::EditContext* editContext = serializeContext->GetEditContext();
  98. if (!editContext)
  99. {
  100. return;
  101. }
  102. editContext->Class<ConnectionComponent>("Position", "The connection's position in the scene")
  103. ->ClassElement(AZ::Edit::ClassElements::EditorData, "Connection's class attributes")
  104. ->DataElement(AZ::Edit::UIHandlers::Default, &ConnectionComponent::m_tooltip, "Tooltip", "The connection's tooltip")
  105. ;
  106. }
  107. AZ::Entity* ConnectionComponent::CreateBaseConnectionEntity(const Endpoint& sourceEndpoint, const Endpoint& targetEndpoint, bool createModelConnection, const AZStd::string& selectorClass)
  108. {
  109. // Create this Connection's entity.
  110. AZ::Entity* entity = aznew AZ::Entity("Connection");
  111. entity->CreateComponent<ConnectionComponent>(sourceEndpoint, targetEndpoint, createModelConnection);
  112. entity->CreateComponent<StylingComponent>(Styling::Elements::Connection, AZ::EntityId(), selectorClass);
  113. entity->CreateComponent<ConnectionLayerControllerComponent>();
  114. return entity;
  115. }
  116. AZ::Entity* ConnectionComponent::CreateGeneralConnection(const Endpoint& sourceEndpoint, const Endpoint& targetEndpoint, bool createModelConnection, const AZStd::string& substyle)
  117. {
  118. AZ::Entity* entity = CreateBaseConnectionEntity(sourceEndpoint, targetEndpoint, createModelConnection, substyle);
  119. entity->CreateComponent<ConnectionVisualComponent>();
  120. return entity;
  121. }
  122. ConnectionComponent::ConnectionComponent()
  123. : m_dragContext(DragContext::Unknown)
  124. {
  125. }
  126. ConnectionComponent::ConnectionComponent(const Endpoint& sourceEndpoint, const Endpoint& targetEndpoint, bool createModelConnection)
  127. : AZ::Component()
  128. {
  129. m_sourceEndpoint = sourceEndpoint;
  130. m_targetEndpoint = targetEndpoint;
  131. AZ_Warning("GraphCanvas", m_targetEndpoint.IsValid() || m_sourceEndpoint.IsValid(), "Either source or target endpoint must be valid when creating a connection.");
  132. if (createModelConnection && m_sourceEndpoint.IsValid() && m_targetEndpoint.IsValid())
  133. {
  134. m_dragContext = DragContext::TryConnection;
  135. }
  136. else
  137. {
  138. m_dragContext = DragContext::Unknown;
  139. }
  140. }
  141. void ConnectionComponent::Activate()
  142. {
  143. ConnectionRequestBus::Handler::BusConnect(GetEntityId());
  144. SceneMemberRequestBus::Handler::BusConnect(GetEntityId());
  145. if (m_sourceEndpoint.IsValid() && m_targetEndpoint.IsValid() && m_dragContext != DragContext::TryConnection)
  146. {
  147. m_dragContext = DragContext::Connected;
  148. }
  149. StateController<RootGraphicsItemDisplayState>* displayStateController = nullptr;
  150. RootGraphicsItemRequestBus::EventResult(displayStateController, GetEntityId(), &RootGraphicsItemRequests::GetDisplayStateStateController);
  151. m_connectionStateStateSetter.AddStateController(displayStateController);
  152. }
  153. void ConnectionComponent::Deactivate()
  154. {
  155. StopMove();
  156. SceneMemberRequestBus::Handler::BusDisconnect();
  157. ConnectionRequestBus::Handler::BusDisconnect();
  158. CleanupToast();
  159. }
  160. void ConnectionComponent::OnSlotRemovedFromNode(const AZ::EntityId& slotId)
  161. {
  162. if (m_dragContext != DragContext::Connected)
  163. {
  164. if (slotId == m_sourceEndpoint.GetSlotId()
  165. || slotId == m_targetEndpoint.GetSlotId())
  166. {
  167. OnEscape();
  168. }
  169. }
  170. }
  171. AZ::EntityId ConnectionComponent::GetSourceSlotId() const
  172. {
  173. return m_sourceEndpoint.GetSlotId();
  174. }
  175. AZ::EntityId ConnectionComponent::GetSourceNodeId() const
  176. {
  177. return m_sourceEndpoint.GetNodeId();
  178. }
  179. Endpoint ConnectionComponent::GetSourceEndpoint() const
  180. {
  181. return m_sourceEndpoint;
  182. }
  183. QPointF ConnectionComponent::GetSourcePosition() const
  184. {
  185. if (m_sourceAnimator.IsAnimating())
  186. {
  187. return m_sourceAnimator.GetAnimatedPosition();
  188. }
  189. else if (m_sourceEndpoint.IsValid())
  190. {
  191. QPointF connectionPoint;
  192. SlotUIRequestBus::EventResult(connectionPoint, m_sourceEndpoint.m_slotId, &SlotUIRequests::GetConnectionPoint);
  193. return connectionPoint;
  194. }
  195. else
  196. {
  197. return m_mousePoint;
  198. }
  199. }
  200. void ConnectionComponent::StartSourceMove()
  201. {
  202. SlotRequestBus::Event(GetSourceEndpoint().GetSlotId(), &SlotRequests::RemoveConnectionId, GetEntityId(), GetTargetEndpoint());
  203. m_previousEndPoint = m_sourceEndpoint;
  204. m_sourceEndpoint = Endpoint();
  205. m_dragContext = DragContext::MoveSource;
  206. StartMove();
  207. }
  208. void ConnectionComponent::SnapSourceDisplayTo(const Endpoint& sourceEndpoint)
  209. {
  210. if (!sourceEndpoint.IsValid())
  211. {
  212. AZ_Error("GraphCanvas", false, "Trying to display a connection to an unknown source Endpoint");
  213. return;
  214. }
  215. bool canDisplaySource = false;
  216. SlotRequestBus::EventResult(canDisplaySource, m_targetEndpoint.GetSlotId(), &SlotRequests::CanDisplayConnectionTo, sourceEndpoint);
  217. if (!canDisplaySource)
  218. {
  219. return;
  220. }
  221. if (m_sourceEndpoint.IsValid())
  222. {
  223. SlotRequestBus::Event(m_sourceEndpoint.GetSlotId(), &SlotRequests::RemoveConnectionId, GetEntityId(), m_targetEndpoint);
  224. }
  225. Endpoint oldEndpoint = m_sourceEndpoint;
  226. m_sourceEndpoint = sourceEndpoint;
  227. ConnectionNotificationBus::Event(GetEntityId(), &ConnectionNotifications::OnSourceSlotIdChanged, oldEndpoint.GetSlotId(), m_sourceEndpoint.GetSlotId());
  228. SlotRequestBus::Event(m_sourceEndpoint.GetSlotId(), &SlotRequests::AddConnectionId, GetEntityId(), m_targetEndpoint);
  229. }
  230. void ConnectionComponent::AnimateSourceDisplayTo(const Endpoint& sourceEndpoint, float connectionTime)
  231. {
  232. QPointF startPosition = GetSourcePosition();
  233. SnapSourceDisplayTo(sourceEndpoint);
  234. m_sourceAnimator.AnimateToEndpoint(startPosition, sourceEndpoint, connectionTime);
  235. if (!AZ::TickBus::Handler::BusIsConnected())
  236. {
  237. AZ::TickBus::Handler::BusConnect();
  238. }
  239. }
  240. AZ::EntityId ConnectionComponent::GetTargetSlotId() const
  241. {
  242. return m_targetEndpoint.GetSlotId();
  243. }
  244. AZ::EntityId ConnectionComponent::GetTargetNodeId() const
  245. {
  246. return m_targetEndpoint.GetNodeId();
  247. }
  248. Endpoint ConnectionComponent::GetTargetEndpoint() const
  249. {
  250. return m_targetEndpoint;
  251. }
  252. QPointF ConnectionComponent::GetTargetPosition() const
  253. {
  254. if (m_targetAnimator.IsAnimating())
  255. {
  256. return m_targetAnimator.GetAnimatedPosition();
  257. }
  258. else if (m_targetEndpoint.IsValid())
  259. {
  260. QPointF connectionPoint;
  261. SlotUIRequestBus::EventResult(connectionPoint, m_targetEndpoint.m_slotId, &SlotUIRequests::GetConnectionPoint);
  262. return connectionPoint;
  263. }
  264. else
  265. {
  266. return m_mousePoint;
  267. }
  268. }
  269. void ConnectionComponent::StartTargetMove()
  270. {
  271. SlotRequestBus::Event(GetTargetEndpoint().GetSlotId(), &SlotRequests::RemoveConnectionId, GetEntityId(), GetSourceEndpoint());
  272. m_previousEndPoint = m_targetEndpoint;
  273. m_targetEndpoint = Endpoint();
  274. m_dragContext = DragContext::MoveTarget;
  275. StartMove();
  276. }
  277. void ConnectionComponent::SnapTargetDisplayTo(const Endpoint& targetEndpoint)
  278. {
  279. if (!targetEndpoint.IsValid())
  280. {
  281. AZ_Error("GraphCanvas", false, "Trying to display a connection to an unknown source Endpoint");
  282. return;
  283. }
  284. bool canDisplayTarget = false;
  285. SlotRequestBus::EventResult(canDisplayTarget, m_sourceEndpoint.GetSlotId(), &SlotRequests::CanDisplayConnectionTo, targetEndpoint);
  286. if (!canDisplayTarget)
  287. {
  288. return;
  289. }
  290. if (m_targetEndpoint.IsValid())
  291. {
  292. SlotRequestBus::Event(m_targetEndpoint.GetSlotId(), &SlotRequests::RemoveConnectionId, GetEntityId(), m_sourceEndpoint);
  293. }
  294. Endpoint oldTarget = m_targetEndpoint;
  295. m_targetEndpoint = targetEndpoint;
  296. ConnectionNotificationBus::Event(GetEntityId(), &ConnectionNotifications::OnTargetSlotIdChanged, oldTarget.GetSlotId(), m_targetEndpoint.GetSlotId());
  297. SlotRequestBus::Event(m_targetEndpoint.GetSlotId(), &SlotRequests::AddConnectionId, GetEntityId(), m_sourceEndpoint);
  298. }
  299. void ConnectionComponent::AnimateTargetDisplayTo(const Endpoint& targetEndpoint, float connectionTime)
  300. {
  301. QPointF startPosition = GetTargetPosition();
  302. SnapTargetDisplayTo(targetEndpoint);
  303. m_targetAnimator.AnimateToEndpoint(startPosition, targetEndpoint, connectionTime);
  304. if (!AZ::TickBus::Handler::BusIsConnected())
  305. {
  306. AZ::TickBus::Handler::BusConnect();
  307. }
  308. }
  309. bool ConnectionComponent::ContainsEndpoint(const Endpoint& endpoint) const
  310. {
  311. bool containsEndpoint = false;
  312. if (m_sourceEndpoint == endpoint)
  313. {
  314. containsEndpoint = (m_dragContext != DragContext::MoveSource);
  315. }
  316. else if (m_targetEndpoint == endpoint)
  317. {
  318. containsEndpoint = (m_dragContext != DragContext::MoveTarget);
  319. }
  320. return containsEndpoint;
  321. }
  322. void ConnectionComponent::ChainProposalCreation(const QPointF& scenePos, const QPoint& screenPos, AZ::EntityId groupTarget)
  323. {
  324. UpdateMovePosition(scenePos);
  325. SetGroupTarget(groupTarget);
  326. const bool chainAddition = true;
  327. FinalizeMove(scenePos, screenPos, chainAddition);
  328. }
  329. void ConnectionComponent::OnEscape()
  330. {
  331. StopMove();
  332. bool keepConnection = OnConnectionMoveCancelled();
  333. if (!keepConnection)
  334. {
  335. AZStd::unordered_set<AZ::EntityId> deletion;
  336. deletion.insert(GetEntityId());
  337. SceneRequestBus::Event(m_graphId, &SceneRequests::Delete, deletion);
  338. }
  339. }
  340. void ConnectionComponent::SetScene(const GraphId& graphId)
  341. {
  342. CleanupToast();
  343. m_graphId = graphId;
  344. if (!m_sourceEndpoint.IsValid())
  345. {
  346. StartSourceMove();
  347. }
  348. else if (!m_targetEndpoint.IsValid())
  349. {
  350. StartTargetMove();
  351. }
  352. else if (m_dragContext == DragContext::TryConnection)
  353. {
  354. OnConnectionMoveComplete(QPointF(), QPoint(), AZ::EntityId());
  355. }
  356. SceneMemberNotificationBus::Event(GetEntityId(), &SceneMemberNotifications::OnSceneSet, m_graphId);
  357. }
  358. void ConnectionComponent::ClearScene(const AZ::EntityId& /*oldSceneId*/)
  359. {
  360. AZ_Warning("Graph Canvas", m_graphId.IsValid(), "This connection (ID: %s) is not in a scene (ID: %s)!", GetEntityId().ToString().data(), m_graphId.ToString().data());
  361. AZ_Warning("Graph Canvas", GetEntityId().IsValid(), "This connection (ID: %s) doesn't have an Entity!", GetEntityId().ToString().data());
  362. if (!m_graphId.IsValid() || !GetEntityId().IsValid())
  363. {
  364. return;
  365. }
  366. SceneMemberNotificationBus::Event(GetEntityId(), &SceneMemberNotifications::OnRemovedFromScene, m_graphId);
  367. m_graphId.SetInvalid();
  368. }
  369. void ConnectionComponent::SignalMemberSetupComplete()
  370. {
  371. SceneMemberNotificationBus::Event(GetEntityId(), &SceneMemberNotifications::OnMemberSetupComplete);
  372. }
  373. AZ::EntityId ConnectionComponent::GetScene() const
  374. {
  375. return m_graphId;
  376. }
  377. AZStd::string ConnectionComponent::GetTooltip() const
  378. {
  379. return m_tooltip;
  380. }
  381. void ConnectionComponent::SetTooltip(const AZStd::string& tooltip)
  382. {
  383. m_tooltip = tooltip;
  384. }
  385. AZStd::any* ConnectionComponent::GetUserData()
  386. {
  387. return &m_userData;
  388. }
  389. void ConnectionComponent::OnTick(float deltaTime, AZ::ScriptTimePoint)
  390. {
  391. bool sourceAnimating = m_sourceAnimator.IsAnimating() && m_sourceAnimator.Tick(deltaTime);
  392. bool targetAnimating = m_targetAnimator.IsAnimating() && m_targetAnimator.Tick(deltaTime);
  393. ConnectionUIRequestBus::Event(GetEntityId(), &ConnectionUIRequests::UpdateConnectionPath);
  394. if (!sourceAnimating && !targetAnimating)
  395. {
  396. AZ::TickBus::Handler::BusDisconnect();
  397. }
  398. }
  399. void ConnectionComponent::OnFocusLost()
  400. {
  401. OnEscape();
  402. }
  403. void ConnectionComponent::OnNodeIsBeingEdited(bool isBeingEdited)
  404. {
  405. if (isBeingEdited)
  406. {
  407. OnEscape();
  408. }
  409. }
  410. void ConnectionComponent::SetGroupTarget(AZ::EntityId groupTarget)
  411. {
  412. if (groupTarget != m_groupTarget)
  413. {
  414. m_groupTarget = groupTarget;
  415. if (m_groupTarget.IsValid())
  416. {
  417. StateController<RootGraphicsItemDisplayState>* displayStateController = nullptr;
  418. RootGraphicsItemRequestBus::EventResult(displayStateController, m_groupTarget, &RootGraphicsItemRequests::GetDisplayStateStateController);
  419. m_forcedGroupDisplayStateStateSetter.AddStateController(displayStateController);
  420. m_forcedGroupDisplayStateStateSetter.SetState(RootGraphicsItemDisplayState::Inspection);
  421. StateController<AZStd::string>* layerStateController = nullptr;
  422. LayerControllerRequestBus::EventResult(layerStateController, m_groupTarget, &LayerControllerRequests::GetLayerModifierController);
  423. m_forcedLayerStateSetter.AddStateController(layerStateController);
  424. m_forcedLayerStateSetter.SetState("dropTarget");
  425. }
  426. else
  427. {
  428. m_forcedGroupDisplayStateStateSetter.ResetStateSetter();
  429. m_forcedLayerStateSetter.ResetStateSetter();
  430. }
  431. }
  432. }
  433. void ConnectionComponent::FinalizeMove()
  434. {
  435. DragContext dragContext = m_dragContext;
  436. m_dragContext = DragContext::Connected;
  437. if (dragContext == DragContext::MoveSource)
  438. {
  439. ConnectionNotificationBus::Event(GetEntityId(), &ConnectionNotifications::OnSourceSlotIdChanged, m_previousEndPoint.GetSlotId(), m_sourceEndpoint.GetSlotId());
  440. SlotRequestBus::Event(GetSourceEndpoint().GetSlotId(), &SlotRequests::AddConnectionId, GetEntityId(), GetTargetEndpoint());
  441. }
  442. else
  443. {
  444. ConnectionNotificationBus::Event(GetEntityId(), &ConnectionNotifications::OnTargetSlotIdChanged, m_previousEndPoint.GetSlotId(), m_targetEndpoint.GetSlotId());
  445. SlotRequestBus::Event(GetTargetEndpoint().GetSlotId(), &SlotRequests::AddConnectionId, GetEntityId(), GetSourceEndpoint());
  446. }
  447. const bool isValidConnection = true;
  448. ConnectionNotificationBus::Event(GetEntityId(), &ConnectionNotifications::OnMoveFinalized, isValidConnection);
  449. }
  450. void ConnectionComponent::OnConnectionMoveStart()
  451. {
  452. SceneRequestBus::Event(m_graphId, &SceneRequests::SignalConnectionDragBegin);
  453. ConnectionNotificationBus::Event(GetEntityId(), &ConnectionNotifications::OnMoveBegin);
  454. GraphModelRequestBus::Event(m_graphId, &GraphModelRequests::DisconnectConnection, GetEntityId());
  455. if (m_dragContext == DragContext::MoveSource)
  456. {
  457. NodeRequestBus::Event(GetTargetNodeId(), &NodeRequests::SignalConnectionMoveBegin, GetEntityId());
  458. }
  459. else if (m_dragContext == DragContext::MoveTarget)
  460. {
  461. NodeRequestBus::Event(GetSourceNodeId(), &NodeRequests::SignalConnectionMoveBegin, GetEntityId());
  462. }
  463. }
  464. bool ConnectionComponent::OnConnectionMoveCancelled()
  465. {
  466. bool keepConnection = false;
  467. if (m_previousEndPoint.IsValid())
  468. {
  469. if (m_dragContext == DragContext::MoveSource)
  470. {
  471. m_sourceEndpoint = m_previousEndPoint;
  472. }
  473. else
  474. {
  475. m_targetEndpoint = m_previousEndPoint;
  476. }
  477. bool acceptConnection = GraphUtils::CreateModelConnection(m_graphId, GetEntityId(), m_sourceEndpoint, m_targetEndpoint);
  478. AZ_Error("GraphCanvas", acceptConnection, "Cancelled a move, and was unable to reconnect to the previous connection.");
  479. if (acceptConnection)
  480. {
  481. keepConnection = true;
  482. FinalizeMove();
  483. }
  484. }
  485. if (!keepConnection)
  486. {
  487. const bool isValidConnection = false;
  488. ConnectionNotificationBus::Event(GetEntityId(), &ConnectionNotifications::OnMoveFinalized, isValidConnection);
  489. }
  490. return keepConnection;
  491. }
  492. ConnectionComponent::ConnectionMoveResult ConnectionComponent::OnConnectionMoveComplete(const QPointF& scenePos, const QPoint& screenPos, AZ::EntityId groupTarget)
  493. {
  494. ConnectionMoveResult connectionResult = ConnectionMoveResult::DeleteConnection;
  495. bool acceptConnection = GraphUtils::CreateModelConnection(m_graphId, GetEntityId(), m_sourceEndpoint, m_targetEndpoint);
  496. if (acceptConnection)
  497. {
  498. connectionResult = ConnectionMoveResult::ConnectionMove;
  499. }
  500. else if (!acceptConnection && !m_previousEndPoint.IsValid() && m_dragContext != DragContext::TryConnection && AllowNodeCreation())
  501. {
  502. Endpoint knownEndpoint = m_sourceEndpoint;
  503. if (!knownEndpoint.IsValid())
  504. {
  505. knownEndpoint = m_targetEndpoint;
  506. }
  507. GraphCanvas::Endpoint otherEndpoint;
  508. EditorId editorId;
  509. SceneRequestBus::EventResult(editorId, m_graphId, &SceneRequests::GetEditorId);
  510. AssetEditorRequestBus::EventResult(otherEndpoint, editorId, &AssetEditorRequests::CreateNodeForProposalWithGroup, GetEntityId(), knownEndpoint, scenePos, screenPos, groupTarget);
  511. if (otherEndpoint.IsValid())
  512. {
  513. if (!m_sourceEndpoint.IsValid())
  514. {
  515. m_sourceEndpoint = otherEndpoint;
  516. }
  517. else if (!m_targetEndpoint.IsValid())
  518. {
  519. m_targetEndpoint = otherEndpoint;
  520. }
  521. acceptConnection = GraphUtils::CreateModelConnection(m_graphId, GetEntityId(), m_sourceEndpoint, m_targetEndpoint);
  522. if (acceptConnection)
  523. {
  524. connectionResult = ConnectionMoveResult::NodeCreation;
  525. }
  526. }
  527. }
  528. return connectionResult;
  529. }
  530. bool ConnectionComponent::AllowNodeCreation() const
  531. {
  532. return true;
  533. }
  534. void ConnectionComponent::CleanupToast()
  535. {
  536. if (m_toastId.IsValid())
  537. {
  538. ViewId viewId;
  539. SceneRequestBus::EventResult(viewId, m_graphId, &SceneRequests::GetViewId);
  540. auto viewHandler = ViewRequestBus::FindFirstHandler(viewId);
  541. if (viewHandler == nullptr)
  542. {
  543. return;
  544. }
  545. viewHandler->HideToastNotification(m_toastId);
  546. m_toastId.SetInvalid();
  547. }
  548. }
  549. void ConnectionComponent::StartMove()
  550. {
  551. QGraphicsItem* connectionGraphicsItem = nullptr;
  552. SceneMemberUIRequestBus::EventResult(connectionGraphicsItem, GetEntityId(), &SceneMemberUIRequests::GetRootGraphicsItem);
  553. if (connectionGraphicsItem)
  554. {
  555. connectionGraphicsItem->setSelected(false);
  556. connectionGraphicsItem->setOpacity(0.5f);
  557. m_eventFilter = aznew ConnectionEventFilter((*this));
  558. ViewId viewId;
  559. SceneRequestBus::EventResult(viewId, m_graphId, &SceneRequests::GetViewId);
  560. ViewNotificationBus::Handler::BusConnect(viewId);
  561. QGraphicsScene* graphicsScene = nullptr;
  562. SceneRequestBus::EventResult(graphicsScene, m_graphId, &SceneRequests::AsQGraphicsScene);
  563. if (graphicsScene)
  564. {
  565. graphicsScene->addItem(m_eventFilter);
  566. if (!graphicsScene->views().empty())
  567. {
  568. QGraphicsView* view = graphicsScene->views().front();
  569. m_mousePoint = view->mapToScene(view->mapFromGlobal(QCursor::pos()));
  570. }
  571. }
  572. connectionGraphicsItem->installSceneEventFilter(m_eventFilter);
  573. connectionGraphicsItem->grabMouse();
  574. SceneNotificationBus::Handler::BusConnect(m_graphId);
  575. StyledEntityRequestBus::Event(GetEntityId(), &StyledEntityRequests::AddSelectorState, Styling::States::Dragging);
  576. AZ::EntityId nodeId;
  577. StateController<RootGraphicsItemDisplayState>* stateController = nullptr;
  578. if (m_dragContext == DragContext::MoveSource)
  579. {
  580. nodeId = GetTargetEndpoint().GetNodeId();
  581. }
  582. else
  583. {
  584. nodeId = GetSourceEndpoint().GetNodeId();
  585. }
  586. RootGraphicsItemRequestBus::EventResult(stateController, nodeId, &RootGraphicsItemRequests::GetDisplayStateStateController);
  587. m_nodeDisplayStateStateSetter.AddStateController(stateController);
  588. m_nodeDisplayStateStateSetter.SetState(RootGraphicsItemDisplayState::Inspection);
  589. ConnectionUIRequestBus::Event(GetEntityId(), &ConnectionUIRequests::SetAltDeletionEnabled, false);
  590. NodeNotificationBus::Handler::BusConnect(nodeId);
  591. OnConnectionMoveStart();
  592. }
  593. }
  594. void ConnectionComponent::StopMove()
  595. {
  596. QGraphicsItem* connectionGraphicsItem = nullptr;
  597. SceneMemberUIRequestBus::EventResult(connectionGraphicsItem, GetEntityId(), &SceneMemberUIRequests::GetRootGraphicsItem);
  598. if (connectionGraphicsItem)
  599. {
  600. connectionGraphicsItem->removeSceneEventFilter(m_eventFilter);
  601. delete m_eventFilter;
  602. m_eventFilter = nullptr;
  603. connectionGraphicsItem->setOpacity(1.0f);
  604. connectionGraphicsItem->ungrabMouse();
  605. StyledEntityRequestBus::Event(GetEntityId(), &StyledEntityRequests::RemoveSelectorState, Styling::States::Dragging);
  606. }
  607. SceneNotificationBus::Handler::BusDisconnect();;
  608. NodeNotificationBus::Handler::BusDisconnect();
  609. ViewNotificationBus::Handler::BusDisconnect();
  610. if (m_dragContext == DragContext::MoveSource)
  611. {
  612. SlotRequestBus::Event(GetSourceEndpoint().GetSlotId(), &SlotRequests::RemoveProposedConnection, GetEntityId(), GetTargetEndpoint());
  613. StyledEntityRequestBus::Event(GetSourceEndpoint().GetSlotId(), &StyledEntityRequests::RemoveSelectorState, Styling::States::ValidDrop);
  614. }
  615. else
  616. {
  617. SlotRequestBus::Event(GetTargetEndpoint().GetSlotId(), &SlotRequests::RemoveProposedConnection, GetEntityId(), GetSourceEndpoint());
  618. StyledEntityRequestBus::Event(GetTargetEndpoint().GetSlotId(), &StyledEntityRequests::RemoveSelectorState, Styling::States::ValidDrop);
  619. }
  620. m_nodeDisplayStateStateSetter.ResetStateSetter();
  621. SceneRequestBus::Event(m_graphId, &SceneRequests::SignalConnectionDragEnd);
  622. ConnectionUIRequestBus::Event(GetEntityId(), &ConnectionUIRequests::SetAltDeletionEnabled, true);
  623. SetGroupTarget(AZ::EntityId());
  624. }
  625. bool ConnectionComponent::UpdateProposal(Endpoint& activePoint, const Endpoint& proposalPoint, AZStd::function< void(const AZ::EntityId&, const AZ::EntityId&)> endpointChangedFunctor)
  626. {
  627. bool retVal = false;
  628. if (activePoint != proposalPoint)
  629. {
  630. retVal = true;
  631. QGraphicsItem* connectionGraphicsItem = nullptr;
  632. SceneMemberUIRequestBus::EventResult(connectionGraphicsItem, GetEntityId(), &SceneMemberUIRequests::GetRootGraphicsItem);
  633. if (activePoint.IsValid())
  634. {
  635. StateController<RootGraphicsItemDisplayState>* stateController = nullptr;
  636. RootGraphicsItemRequestBus::EventResult(stateController, activePoint.GetNodeId(), &RootGraphicsItemRequests::GetDisplayStateStateController);
  637. m_nodeDisplayStateStateSetter.RemoveStateController(stateController);
  638. StyledEntityRequestBus::Event(activePoint.m_slotId, &StyledEntityRequests::RemoveSelectorState, Styling::States::ValidDrop);
  639. if (connectionGraphicsItem)
  640. {
  641. connectionGraphicsItem->setOpacity(0.5f);
  642. }
  643. }
  644. AZ::EntityId oldId = activePoint.GetSlotId();
  645. activePoint = proposalPoint;
  646. endpointChangedFunctor(oldId, activePoint.GetSlotId());
  647. if (activePoint.IsValid())
  648. {
  649. StateController<RootGraphicsItemDisplayState>* stateController = nullptr;
  650. RootGraphicsItemRequestBus::EventResult(stateController, activePoint.GetNodeId(), &RootGraphicsItemRequests::GetDisplayStateStateController);
  651. m_nodeDisplayStateStateSetter.AddStateController(stateController);
  652. StyledEntityRequestBus::Event(activePoint.m_slotId, &StyledEntityRequests::AddSelectorState, Styling::States::ValidDrop);
  653. if (connectionGraphicsItem)
  654. {
  655. connectionGraphicsItem->setOpacity(1.0f);
  656. }
  657. }
  658. }
  659. return retVal || !activePoint.IsValid();
  660. }
  661. ConnectionComponent::ConnectionCandidate ConnectionComponent::FindConnectionCandidateAt(const QPointF& scenePos) const
  662. {
  663. Endpoint knownEndpoint = m_targetEndpoint;
  664. if (m_dragContext == DragContext::MoveTarget)
  665. {
  666. knownEndpoint = m_sourceEndpoint;
  667. }
  668. EditorId editorId;
  669. SceneRequestBus::EventResult(editorId, m_graphId, &SceneRequests::GetEditorId);
  670. double snapDist = 10.0;
  671. AssetEditorSettingsRequestBus::EventResult(snapDist, editorId, &AssetEditorSettingsRequests::GetSnapDistance);
  672. QPointF dist(snapDist, snapDist);
  673. QRectF rect(scenePos - dist, scenePos + dist);
  674. // These are returnned sorted. So we just need to return the first match we find.
  675. AZStd::vector<Endpoint> endpoints;
  676. SceneRequestBus::EventResult(endpoints, m_graphId, &SceneRequests::GetEndpointsInRect, rect);
  677. ConnectionCandidate candidate;
  678. for (Endpoint endpoint : endpoints)
  679. {
  680. // Skip over ourselves.
  681. if (endpoint == knownEndpoint)
  682. {
  683. continue;
  684. }
  685. if (!GraphUtils::IsSlotVisible(endpoint.GetSlotId()))
  686. {
  687. continue;
  688. }
  689. // For our tool tips we really only want to focus in on the first element
  690. if (!candidate.m_testedTarget.IsValid())
  691. {
  692. candidate.m_testedTarget = endpoint;
  693. }
  694. bool canCreateConnection = false;
  695. if (m_dragContext == DragContext::MoveSource && endpoint == m_sourceEndpoint)
  696. {
  697. canCreateConnection = true;
  698. }
  699. else if (m_dragContext == DragContext::MoveTarget && endpoint == m_targetEndpoint)
  700. {
  701. canCreateConnection = true;
  702. }
  703. else if (m_dragContext == DragContext::MoveTarget && endpoint == m_sourceEndpoint
  704. || m_dragContext == DragContext::MoveSource && endpoint == m_targetEndpoint)
  705. {
  706. continue;
  707. }
  708. else
  709. {
  710. // If we are checking against an extender slot. We need to go through special flow.
  711. // Since the extender will create a new slot for us to connect to.
  712. if (auto extenderHandler = ExtenderSlotRequestBus::FindFirstHandler(endpoint.GetSlotId()))
  713. {
  714. Endpoint newConnectionEndpoint = extenderHandler->ExtendForConnectionProposal(GetEntityId(), knownEndpoint);
  715. if (newConnectionEndpoint.IsValid())
  716. {
  717. canCreateConnection = true;
  718. endpoint = newConnectionEndpoint;
  719. }
  720. }
  721. else
  722. {
  723. SlotRequestBus::EventResult(canCreateConnection, endpoint.GetSlotId(), &SlotRequests::CanCreateConnectionTo, knownEndpoint);
  724. }
  725. }
  726. if (canCreateConnection)
  727. {
  728. candidate.m_connectableTarget = endpoint;
  729. break;
  730. }
  731. }
  732. return candidate;
  733. }
  734. void ConnectionComponent::UpdateMovePosition(const QPointF& position)
  735. {
  736. if (m_dragContext == DragContext::MoveSource
  737. || m_dragContext == DragContext::MoveTarget)
  738. {
  739. m_mousePoint = position;
  740. ConnectionCandidate connectionCandidate = FindConnectionCandidateAt(m_mousePoint);
  741. bool updateConnection = false;
  742. if (m_dragContext == DragContext::MoveSource)
  743. {
  744. AZStd::function<void(const AZ::EntityId&, const AZ::EntityId)> updateFunction = [this](const AZ::EntityId& oldId, const AZ::EntityId& newId)
  745. {
  746. SlotRequestBus::Event(oldId, &SlotRequests::RemoveProposedConnection, this->GetEntityId(), this->GetTargetEndpoint());
  747. SlotRequestBus::Event(newId, &SlotRequests::DisplayProposedConnection, this->GetEntityId(), this->GetTargetEndpoint());
  748. ConnectionNotificationBus::Event(this->GetEntityId(), &ConnectionNotifications::OnSourceSlotIdChanged, oldId, newId);
  749. };
  750. updateConnection = UpdateProposal(m_sourceEndpoint, connectionCandidate.m_connectableTarget, updateFunction);
  751. }
  752. else
  753. {
  754. AZStd::function<void(const AZ::EntityId&, const AZ::EntityId)> updateFunction = [this](const AZ::EntityId& oldId, const AZ::EntityId& newId)
  755. {
  756. SlotRequestBus::Event(oldId, &SlotRequests::RemoveProposedConnection, this->GetEntityId(), this->GetSourceEndpoint());
  757. SlotRequestBus::Event(newId, &SlotRequests::DisplayProposedConnection, this->GetEntityId(), this->GetSourceEndpoint());
  758. ConnectionNotificationBus::Event(this->GetEntityId(), &ConnectionNotifications::OnTargetSlotIdChanged, oldId, newId);
  759. };
  760. updateConnection = UpdateProposal(m_targetEndpoint, connectionCandidate.m_connectableTarget, updateFunction);
  761. }
  762. if (connectionCandidate.m_connectableTarget.IsValid())
  763. {
  764. Endpoint invalidEndpoint;
  765. DisplayConnectionToolTip(position, invalidEndpoint);
  766. // If we have a valid target. We want to not manage our group target.
  767. SetGroupTarget(AZ::EntityId());
  768. }
  769. else
  770. {
  771. DisplayConnectionToolTip(position, connectionCandidate.m_testedTarget);
  772. AZ::EntityId groupTarget;
  773. SceneRequestBus::EventResult(groupTarget, GetScene(), &SceneRequests::FindTopmostGroupAtPoint, m_mousePoint);
  774. SetGroupTarget(groupTarget);
  775. }
  776. if (updateConnection)
  777. {
  778. ConnectionUIRequestBus::Event(GetEntityId(), &ConnectionUIRequests::UpdateConnectionPath);
  779. }
  780. }
  781. }
  782. void ConnectionComponent::FinalizeMove(const QPointF& scenePos, const QPoint& screenPos, bool chainAddition)
  783. {
  784. if (m_dragContext == DragContext::Connected)
  785. {
  786. AZ_Error("Graph Canvas", false, "Receiving MouseRelease event in invalid drag state.");
  787. return;
  788. }
  789. DisplayConnectionToolTip(scenePos, Endpoint());
  790. DragContext chainContext = m_dragContext;
  791. AZ::EntityId groupTarget = m_groupTarget;
  792. StopMove();
  793. // Have to copy the GraphId here because deletion of the Entity this component is attached to deletes this component.
  794. GraphId graphId = m_graphId;
  795. ConnectionMoveResult connectionResult = OnConnectionMoveComplete(scenePos, screenPos, groupTarget);
  796. if (connectionResult == ConnectionMoveResult::DeleteConnection)
  797. {
  798. // If the previous endpoint is not valid, this implies a new connection is being created
  799. bool preventUndoState = !m_previousEndPoint.IsValid();
  800. if (preventUndoState)
  801. {
  802. GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestPushPreventUndoStateUpdate);
  803. }
  804. AZ::EntityId connectionId = GetEntityId();
  805. // OnMoveFinalized might end up deleting the connection if it was from an ExtenderSlot, so no member methods should be called after either of these methods.
  806. //
  807. // The SceneRequests::Delete will delete the Entity this component is attached.
  808. // Therefore it is invalid to access the members of this component after the call.
  809. const bool isValidConnection = false;
  810. ConnectionNotificationBus::Event(connectionId, &ConnectionNotifications::OnMoveFinalized, isValidConnection);
  811. AZStd::unordered_set<AZ::EntityId> deletion;
  812. deletion.insert(connectionId);
  813. SceneRequestBus::Event(graphId, &SceneRequests::Delete, deletion);
  814. if (preventUndoState)
  815. {
  816. GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestPopPreventUndoStateUpdate);
  817. }
  818. }
  819. else
  820. {
  821. FinalizeMove();
  822. GraphModelRequestBus::Event(graphId, &GraphModelRequests::RequestUndoPoint);
  823. if (chainAddition && connectionResult == ConnectionMoveResult::NodeCreation)
  824. {
  825. AZ::EntityId graphId2;
  826. SceneMemberRequestBus::EventResult(graphId2, GetEntityId(), &SceneMemberRequests::GetScene);
  827. AZ::EntityId nodeId;
  828. SlotType slotType = SlotTypes::Invalid;
  829. ConnectionType connectionType = CT_Invalid;
  830. if (chainContext == DragContext::MoveSource)
  831. {
  832. nodeId = GetSourceNodeId();
  833. slotType = SlotTypes::ExecutionSlot;
  834. connectionType = CT_Input;
  835. }
  836. else if (chainContext == DragContext::MoveTarget)
  837. {
  838. nodeId = GetTargetNodeId();
  839. slotType = SlotTypes::ExecutionSlot;
  840. connectionType = CT_Output;
  841. }
  842. SceneRequestBus::Event(graphId2, &SceneRequests::HandleProposalDaisyChainWithGroup, nodeId, slotType, connectionType, screenPos, scenePos, groupTarget);
  843. }
  844. }
  845. }
  846. void ConnectionComponent::DisplayConnectionToolTip(const QPointF& /*scenePoint*/, const Endpoint& connectionTarget)
  847. {
  848. if (m_endpointTooltip != connectionTarget)
  849. {
  850. GraphId graphId;
  851. SceneMemberRequestBus::EventResult(graphId, GetEntityId(), &SceneMemberRequests::GetScene);
  852. ViewId viewId;
  853. SceneRequestBus::EventResult(viewId, graphId, &SceneRequests::GetViewId);
  854. auto viewHandler = ViewRequestBus::FindFirstHandler(viewId);
  855. if (viewHandler == nullptr)
  856. {
  857. return;
  858. }
  859. CleanupToast();
  860. m_endpointTooltip = connectionTarget;
  861. // No endpoint I'm going to treat like a valid connection
  862. m_validationResult = ConnectionValidationTooltip();
  863. m_validationResult.m_isValid = true;
  864. // If our tooltip target is the same as both our target and source, this means we are trying to connect to the point we started from.
  865. // This just looks weird, so we won't do it.
  866. if (m_endpointTooltip.IsValid())
  867. {
  868. // If we are pointing at an extender slot. Don't investigate the reason for a failure.
  869. if (ExtenderSlotRequestBus::FindFirstHandler(m_endpointTooltip.GetSlotId()) != nullptr)
  870. {
  871. return;
  872. }
  873. if (m_dragContext == DragContext::MoveTarget)
  874. {
  875. if (m_sourceEndpoint != m_endpointTooltip)
  876. {
  877. m_validationResult = GraphUtils::GetModelConnnectionValidityToolTip(graphId, m_sourceEndpoint, m_endpointTooltip);
  878. }
  879. }
  880. else
  881. {
  882. if (m_targetEndpoint != m_endpointTooltip)
  883. {
  884. m_validationResult = GraphUtils::GetModelConnnectionValidityToolTip(graphId, m_endpointTooltip, m_targetEndpoint);
  885. }
  886. }
  887. }
  888. if (!m_validationResult.m_isValid)
  889. {
  890. EditorId editorId;
  891. SceneRequestBus::EventResult(editorId, graphId, &SceneRequests::GetEditorId);
  892. QPointF connectionPoint;
  893. SlotUIRequestBus::EventResult(connectionPoint, m_endpointTooltip.GetSlotId(), &SlotUIRequests::GetConnectionPoint);
  894. AZ::Vector2 globalConnectionVector = ConversionUtils::QPointToVector(connectionPoint);
  895. globalConnectionVector = viewHandler->MapToGlobal(globalConnectionVector);
  896. QPointF globalConnectionPoint = ConversionUtils::AZToQPoint(globalConnectionVector);
  897. QPointF anchorPoint(0.0f, 0.0f);
  898. AzQtComponents::ToastConfiguration toastConfiguration(AzQtComponents::ToastType::Error, "Unable to connect to slot", m_validationResult.m_failureReason.c_str());
  899. toastConfiguration.m_closeOnClick = false;
  900. m_toastId = viewHandler->ShowToastAtPoint(globalConnectionPoint.toPoint(), anchorPoint, toastConfiguration);
  901. }
  902. }
  903. }
  904. }