ConnectionComponent.cpp 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130
  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. }