UiInteractableComponent.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  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 "UiInteractableComponent.h"
  9. #include <AzCore/Math/Crc.h>
  10. #include <AzCore/Serialization/SerializeContext.h>
  11. #include <AzCore/Serialization/EditContext.h>
  12. #include <AzCore/RTTI/BehaviorContext.h>
  13. #include <AzCore/std/sort.h>
  14. #include <LyShine/ISprite.h>
  15. #include <LyShine/UiSerializeHelpers.h>
  16. #include <LyShine/Bus/UiCanvasBus.h>
  17. #include <LyShine/Bus/UiElementBus.h>
  18. ////////////////////////////////////////////////////////////////////////////////////////////////////
  19. //! UiInteractableNotificationBus Behavior context handler class
  20. class BehaviorUiInteractableNotificationBusHandler
  21. : public UiInteractableNotificationBus::Handler
  22. , public AZ::BehaviorEBusHandler
  23. {
  24. public:
  25. AZ_EBUS_BEHAVIOR_BINDER(BehaviorUiInteractableNotificationBusHandler, "{BBF912EB-8F45-4869-B1F0-19CDA9D16231}", AZ::SystemAllocator,
  26. OnHoverStart, OnHoverEnd, OnPressed, OnReleased, OnReceivedHoverByNavigatingFromDescendant);
  27. void OnHoverStart() override
  28. {
  29. Call(FN_OnHoverStart);
  30. }
  31. void OnHoverEnd() override
  32. {
  33. Call(FN_OnHoverEnd);
  34. }
  35. void OnPressed() override
  36. {
  37. Call(FN_OnPressed);
  38. }
  39. void OnReleased() override
  40. {
  41. Call(FN_OnReleased);
  42. }
  43. void OnReceivedHoverByNavigatingFromDescendant(AZ::EntityId descendantEntityId) override
  44. {
  45. Call(FN_OnReceivedHoverByNavigatingFromDescendant, descendantEntityId);
  46. }
  47. };
  48. ////////////////////////////////////////////////////////////////////////////////////////////////////
  49. UiInteractableComponent::UiInteractableComponent()
  50. : m_isAutoActivationEnabled(false)
  51. , m_isHandlingEvents(true)
  52. , m_isHandlingMultiTouchEvents(true)
  53. , m_isHover(false)
  54. , m_isPressed(false)
  55. , m_pressedPoint(0.0f, 0.0f)
  56. , m_pressedMultiTouchIndex(0)
  57. , m_state(UiInteractableStatesInterface::StateNormal)
  58. , m_hoverStartActionCallback(nullptr)
  59. , m_hoverEndActionCallback(nullptr)
  60. , m_pressedActionCallback(nullptr)
  61. , m_releasedActionCallback(nullptr)
  62. {
  63. // Must be called in the same order as the states defined in UiInteractableStatesInterface
  64. m_stateActionManager.AddState(nullptr); // normal state has no state actions
  65. m_stateActionManager.AddState(&m_hoverStateActions);
  66. m_stateActionManager.AddState(&m_pressedStateActions);
  67. m_stateActionManager.AddState(&m_disabledStateActions);
  68. }
  69. ////////////////////////////////////////////////////////////////////////////////////////////////////
  70. UiInteractableComponent::~UiInteractableComponent()
  71. {
  72. if (m_isPressed && m_entity)
  73. {
  74. UiInteractableActiveNotificationBus::Event(GetEntityId(), &UiInteractableActiveNotificationBus::Events::ActiveCancelled);
  75. m_isPressed = false;
  76. }
  77. }
  78. ////////////////////////////////////////////////////////////////////////////////////////////////////
  79. bool UiInteractableComponent::CanHandleEvent([[maybe_unused]] AZ::Vector2 point)
  80. {
  81. return m_isHandlingEvents;
  82. }
  83. ////////////////////////////////////////////////////////////////////////////////////////////////////
  84. bool UiInteractableComponent::HandlePressed(AZ::Vector2 point, bool& shouldStayActive)
  85. {
  86. bool handled = false;
  87. if (m_isHandlingEvents)
  88. {
  89. m_isPressed = true;
  90. m_pressedPoint = point;
  91. shouldStayActive = false;
  92. handled = true;
  93. TriggerPressedAction();
  94. }
  95. return handled;
  96. }
  97. ////////////////////////////////////////////////////////////////////////////////////////////////////
  98. bool UiInteractableComponent::HandleReleased([[maybe_unused]] AZ::Vector2 point)
  99. {
  100. m_isPressed = false;
  101. TriggerReleasedAction();
  102. return m_isHandlingEvents;
  103. }
  104. ////////////////////////////////////////////////////////////////////////////////////////////////////
  105. bool UiInteractableComponent::HandleMultiTouchPressed(AZ::Vector2 point, int multiTouchIndex)
  106. {
  107. m_pressedMultiTouchIndex = multiTouchIndex;
  108. bool shouldStayActive = false;
  109. return m_isHandlingMultiTouchEvents && HandlePressed(point, shouldStayActive);
  110. }
  111. ////////////////////////////////////////////////////////////////////////////////////////////////////
  112. bool UiInteractableComponent::HandleMultiTouchReleased(AZ::Vector2 point, int multiTouchIndex)
  113. {
  114. AZ_UNUSED(multiTouchIndex);
  115. bool handled = m_isHandlingMultiTouchEvents && HandleReleased(point);
  116. m_pressedMultiTouchIndex = 0;
  117. return handled;
  118. }
  119. ////////////////////////////////////////////////////////////////////////////////////////////////////
  120. bool UiInteractableComponent::HandleEnterPressed(bool& shouldStayActive)
  121. {
  122. bool handled = false;
  123. if (m_isHandlingEvents)
  124. {
  125. m_isPressed = true;
  126. m_pressedPoint = AZ::Vector2(-1.0f, -1.0f);
  127. shouldStayActive = false;
  128. handled = true;
  129. TriggerPressedAction();
  130. }
  131. return handled;
  132. }
  133. ////////////////////////////////////////////////////////////////////////////////////////////////////
  134. bool UiInteractableComponent::HandleEnterReleased()
  135. {
  136. m_isPressed = false;
  137. TriggerReleasedAction();
  138. return m_isHandlingEvents;
  139. }
  140. ////////////////////////////////////////////////////////////////////////////////////////////////////
  141. void UiInteractableComponent::InputPositionUpdate(AZ::Vector2 point)
  142. {
  143. if (m_isPressed)
  144. {
  145. AZ::EntityId parentDraggable;
  146. UiElementBus::EventResult(parentDraggable, GetEntityId(), &UiElementBus::Events::FindParentInteractableSupportingDrag, point);
  147. if (parentDraggable.IsValid())
  148. {
  149. const float containedDragThreshold = 5.0f;
  150. // offer the parent draggable the chance to become the active interactable
  151. bool handOff = false;
  152. UiInteractableBus::EventResult(
  153. handOff,
  154. parentDraggable,
  155. &UiInteractableBus::Events::OfferDragHandOff,
  156. GetEntityId(),
  157. m_pressedPoint,
  158. point,
  159. containedDragThreshold);
  160. if (handOff)
  161. {
  162. // interaction has been handed off to a container entity
  163. m_isPressed = false;
  164. }
  165. }
  166. }
  167. }
  168. ////////////////////////////////////////////////////////////////////////////////////////////////////
  169. void UiInteractableComponent::MultiTouchPositionUpdate(AZ::Vector2 point, int multiTouchIndex)
  170. {
  171. AZ_UNUSED(multiTouchIndex);
  172. if (m_isHandlingMultiTouchEvents)
  173. {
  174. InputPositionUpdate(point);
  175. }
  176. }
  177. ////////////////////////////////////////////////////////////////////////////////////////////////////
  178. void UiInteractableComponent::LostActiveStatus()
  179. {
  180. m_isPressed = false;
  181. }
  182. ////////////////////////////////////////////////////////////////////////////////////////////////////
  183. void UiInteractableComponent::HandleHoverStart()
  184. {
  185. m_isHover = true;
  186. TriggerHoverStartAction();
  187. }
  188. ////////////////////////////////////////////////////////////////////////////////////////////////////
  189. void UiInteractableComponent::HandleHoverEnd()
  190. {
  191. m_isHover = false;
  192. TriggerHoverEndAction();
  193. }
  194. ////////////////////////////////////////////////////////////////////////////////////////////////////
  195. void UiInteractableComponent::HandleReceivedHoverByNavigatingFromDescendant(AZ::EntityId descendantEntityId)
  196. {
  197. TriggerReceivedHoverByNavigatingFromDescendantAction(descendantEntityId);
  198. }
  199. ////////////////////////////////////////////////////////////////////////////////////////////////////
  200. bool UiInteractableComponent::IsPressed()
  201. {
  202. return m_isPressed;
  203. }
  204. ////////////////////////////////////////////////////////////////////////////////////////////////////
  205. bool UiInteractableComponent::IsHandlingEvents()
  206. {
  207. return m_isHandlingEvents;
  208. }
  209. ////////////////////////////////////////////////////////////////////////////////////////////////////
  210. void UiInteractableComponent::SetIsHandlingEvents(bool isHandlingEvents)
  211. {
  212. m_isHandlingEvents = isHandlingEvents;
  213. }
  214. ////////////////////////////////////////////////////////////////////////////////////////////////////
  215. bool UiInteractableComponent::IsHandlingMultiTouchEvents()
  216. {
  217. return m_isHandlingMultiTouchEvents;
  218. }
  219. ////////////////////////////////////////////////////////////////////////////////////////////////////
  220. void UiInteractableComponent::SetIsHandlingMultiTouchEvents(bool isHandlingMultiTouchEvents)
  221. {
  222. m_isHandlingMultiTouchEvents = isHandlingMultiTouchEvents;
  223. }
  224. ////////////////////////////////////////////////////////////////////////////////////////////////////
  225. bool UiInteractableComponent::GetIsAutoActivationEnabled()
  226. {
  227. return m_isAutoActivationEnabled;
  228. }
  229. ////////////////////////////////////////////////////////////////////////////////////////////////////
  230. void UiInteractableComponent::SetIsAutoActivationEnabled(bool isEnabled)
  231. {
  232. m_isAutoActivationEnabled = isEnabled;
  233. }
  234. ////////////////////////////////////////////////////////////////////////////////////////////////////
  235. const LyShine::ActionName& UiInteractableComponent::GetHoverStartActionName()
  236. {
  237. return m_hoverStartActionName;
  238. }
  239. ////////////////////////////////////////////////////////////////////////////////////////////////////
  240. void UiInteractableComponent::SetHoverStartActionName(const LyShine::ActionName& actionName)
  241. {
  242. m_hoverStartActionName = actionName;
  243. }
  244. ////////////////////////////////////////////////////////////////////////////////////////////////////
  245. const LyShine::ActionName& UiInteractableComponent::GetHoverEndActionName()
  246. {
  247. return m_hoverEndActionName;
  248. }
  249. ////////////////////////////////////////////////////////////////////////////////////////////////////
  250. void UiInteractableComponent::SetHoverEndActionName(const LyShine::ActionName& actionName)
  251. {
  252. m_hoverEndActionName = actionName;
  253. }
  254. ////////////////////////////////////////////////////////////////////////////////////////////////////
  255. const LyShine::ActionName& UiInteractableComponent::GetPressedActionName()
  256. {
  257. return m_pressedActionName;
  258. }
  259. ////////////////////////////////////////////////////////////////////////////////////////////////////
  260. void UiInteractableComponent::SetPressedActionName(const LyShine::ActionName& actionName)
  261. {
  262. m_pressedActionName = actionName;
  263. }
  264. ////////////////////////////////////////////////////////////////////////////////////////////////////
  265. const LyShine::ActionName& UiInteractableComponent::GetReleasedActionName()
  266. {
  267. return m_releasedActionName;
  268. }
  269. ////////////////////////////////////////////////////////////////////////////////////////////////////
  270. void UiInteractableComponent::SetReleasedActionName(const LyShine::ActionName& actionName)
  271. {
  272. m_releasedActionName = actionName;
  273. }
  274. ////////////////////////////////////////////////////////////////////////////////////////////////////
  275. void UiInteractableComponent::SetOutsideReleasedActionName(const LyShine::ActionName& actionName)
  276. {
  277. m_outsideReleasedActionName = actionName;
  278. }
  279. ////////////////////////////////////////////////////////////////////////////////////////////////////
  280. const LyShine::ActionName& UiInteractableComponent::GetOutsideReleasedActionName() const
  281. {
  282. return m_outsideReleasedActionName;
  283. }
  284. ////////////////////////////////////////////////////////////////////////////////////////////////////
  285. UiInteractableActionsInterface::OnActionCallback UiInteractableComponent::GetHoverStartActionCallback()
  286. {
  287. return m_hoverStartActionCallback;
  288. }
  289. ////////////////////////////////////////////////////////////////////////////////////////////////////
  290. void UiInteractableComponent::SetHoverStartActionCallback(OnActionCallback onActionCallback)
  291. {
  292. m_hoverStartActionCallback = onActionCallback;
  293. }
  294. ////////////////////////////////////////////////////////////////////////////////////////////////////
  295. UiInteractableActionsInterface::OnActionCallback UiInteractableComponent::GetHoverEndActionCallback()
  296. {
  297. return m_hoverEndActionCallback;
  298. }
  299. ////////////////////////////////////////////////////////////////////////////////////////////////////
  300. void UiInteractableComponent::SetHoverEndActionCallback(OnActionCallback onActionCallback)
  301. {
  302. m_hoverEndActionCallback = onActionCallback;
  303. }
  304. ////////////////////////////////////////////////////////////////////////////////////////////////////
  305. UiInteractableActionsInterface::OnActionCallback UiInteractableComponent::GetPressedActionCallback()
  306. {
  307. return m_pressedActionCallback;
  308. }
  309. ////////////////////////////////////////////////////////////////////////////////////////////////////
  310. void UiInteractableComponent::SetPressedActionCallback(OnActionCallback onActionCallback)
  311. {
  312. m_pressedActionCallback = onActionCallback;
  313. }
  314. ////////////////////////////////////////////////////////////////////////////////////////////////////
  315. UiInteractableActionsInterface::OnActionCallback UiInteractableComponent::GetReleasedActionCallback()
  316. {
  317. return m_releasedActionCallback;
  318. }
  319. ////////////////////////////////////////////////////////////////////////////////////////////////////
  320. void UiInteractableComponent::SetReleasedActionCallback(OnActionCallback onActionCallback)
  321. {
  322. m_releasedActionCallback = onActionCallback;
  323. }
  324. ////////////////////////////////////////////////////////////////////////////////////////////////////
  325. void UiInteractableComponent::Update(float /* deltaTime */)
  326. {
  327. // This currently happens every frame. Needs optimization to just happen on events
  328. UiInteractableStatesInterface::State state = ComputeInteractableState();
  329. if (state != m_state)
  330. {
  331. m_stateActionManager.ResetAllOverrides();
  332. m_stateActionManager.ApplyStateActions(state);
  333. m_state = state;
  334. }
  335. }
  336. ////////////////////////////////////////////////////////////////////////////////////////////////////
  337. void UiInteractableComponent::OnUiElementFixup(AZ::EntityId canvasEntityId, AZ::EntityId /*parentEntityId*/)
  338. {
  339. bool isElementEnabled = false;
  340. UiElementBus::EventResult(isElementEnabled, GetEntityId(), &UiElementBus::Events::GetAreElementAndAncestorsEnabled);
  341. if (isElementEnabled)
  342. {
  343. UiCanvasUpdateNotificationBus::Handler::BusConnect(canvasEntityId);
  344. }
  345. }
  346. ////////////////////////////////////////////////////////////////////////////////////////////////////
  347. void UiInteractableComponent::OnUiElementAndAncestorsEnabledChanged(bool areElementAndAncestorsEnabled)
  348. {
  349. if (areElementAndAncestorsEnabled)
  350. {
  351. AZ::EntityId canvasEntityId;
  352. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  353. if (canvasEntityId.IsValid())
  354. {
  355. UiCanvasUpdateNotificationBus::Handler::BusConnect(canvasEntityId);
  356. }
  357. }
  358. else
  359. {
  360. UiCanvasUpdateNotificationBus::Handler::BusDisconnect();
  361. }
  362. }
  363. ////////////////////////////////////////////////////////////////////////////////////////////////////
  364. // PUBLIC STATIC MEMBER FUNCTIONS
  365. ////////////////////////////////////////////////////////////////////////////////////////////////////
  366. ////////////////////////////////////////////////////////////////////////////////////////////////////
  367. void UiInteractableComponent::Reflect(AZ::ReflectContext* context)
  368. {
  369. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  370. if (serializeContext)
  371. {
  372. serializeContext->Class<UiInteractableComponent, AZ::Component>()
  373. ->Version(2, &VersionConverter)
  374. ->Field("IsHandlingEvents", &UiInteractableComponent::m_isHandlingEvents)
  375. ->Field("IsHandlingMultiTouchEvents", &UiInteractableComponent::m_isHandlingMultiTouchEvents)
  376. ->Field("HoverStateActions", &UiInteractableComponent::m_hoverStateActions)
  377. ->Field("PressedStateActions", &UiInteractableComponent::m_pressedStateActions)
  378. ->Field("DisabledStateActions", &UiInteractableComponent::m_disabledStateActions)
  379. ->Field("NavigationSettings", &UiInteractableComponent::m_navigationSettings)
  380. ->Field("IsAutoActivationEnabled", &UiInteractableComponent::m_isAutoActivationEnabled)
  381. ->Field("HoverStartActionName", &UiInteractableComponent::m_hoverStartActionName)
  382. ->Field("HoverEndActionName", &UiInteractableComponent::m_hoverEndActionName)
  383. ->Field("OutsideReleasedActionName", &UiInteractableComponent::m_outsideReleasedActionName)
  384. ->Field("PressedActionName", &UiInteractableComponent::m_pressedActionName)
  385. ->Field("ReleasedActionName", &UiInteractableComponent::m_releasedActionName);
  386. AZ::EditContext* ec = serializeContext->GetEditContext();
  387. if (ec)
  388. {
  389. auto editInfo = ec->Class<UiInteractableComponent>("Interactable", "Common settings for all interactable components");
  390. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  391. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  392. editInfo->DataElement("CheckBox", &UiInteractableComponent::m_isHandlingEvents, "Input enabled",
  393. "When checked, this interactable will handle events.\n"
  394. "When unchecked, this interactable is drawn in the Disabled state.")
  395. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC_CE("RefreshEntireTree"));
  396. editInfo->DataElement("CheckBox", &UiInteractableComponent::m_isHandlingMultiTouchEvents, "Multi-touch input enabled",
  397. "When checked, this interactable will handle all multi-touch input events.\n"
  398. "When unchecked, this interactable will handle only primary touch input events.\n"
  399. "Will be ignored if the parent UICanvasComponent does not support multi-touch.")
  400. ->Attribute(AZ::Edit::Attributes::Visibility, &UiInteractableComponent::IsHandlingEvents);
  401. // Navigation
  402. editInfo->DataElement(0, &UiInteractableComponent::m_navigationSettings, "Navigation",
  403. "How to navigate from this interactbale to the next interactable");
  404. editInfo->DataElement(0, &UiInteractableComponent::m_isAutoActivationEnabled, "Auto activate",
  405. "When checked, this interactable will automatically become active when navigated to with a gamepad/keyboard.\n"
  406. "When unchecked, a button press is required to activate/deactivate this interactable.")
  407. ->Attribute(AZ::Edit::Attributes::Visibility, &UiInteractableComponent::IsAutoActivationSupported);
  408. // States Group
  409. {
  410. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "States")
  411. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  412. editInfo->DataElement(0, &UiInteractableComponent::m_hoverStateActions, "Hover", "The hover/selected state actions")
  413. ->Attribute(AZ::Edit::Attributes::AddNotify, &UiInteractableComponent::OnHoverStateActionsChanged);
  414. editInfo->DataElement(0, &UiInteractableComponent::m_pressedStateActions, "Pressed", "The pressed state actions")
  415. ->Attribute(AZ::Edit::Attributes::AddNotify, &UiInteractableComponent::OnPressedStateActionsChanged);
  416. editInfo->DataElement(0, &UiInteractableComponent::m_disabledStateActions, "Disabled", "The disabled state actions")
  417. ->Attribute(AZ::Edit::Attributes::AddNotify, &UiInteractableComponent::OnDisabledStateActionsChanged);
  418. }
  419. // Actions Group
  420. {
  421. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Actions")
  422. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  423. editInfo->DataElement(0, &UiInteractableComponent::m_hoverStartActionName, "Hover start", "Action triggered on hover start");
  424. editInfo->DataElement(0, &UiInteractableComponent::m_hoverEndActionName, "Hover end", "Action triggered on hover end");
  425. editInfo->DataElement(0, &UiInteractableComponent::m_pressedActionName, "Pressed", "Action triggered on press");
  426. editInfo->DataElement(0, &UiInteractableComponent::m_releasedActionName, "Released", "Action triggered on release");
  427. editInfo->DataElement(0, &UiInteractableComponent::m_outsideReleasedActionName, "Outside Released", "Action triggered on release outside of element");
  428. }
  429. }
  430. }
  431. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  432. if (behaviorContext)
  433. {
  434. behaviorContext->EBus<UiInteractableBus>("UiInteractableBus")
  435. ->Event("IsHandlingEvents", &UiInteractableBus::Events::IsHandlingEvents)
  436. ->Event("SetIsHandlingEvents", &UiInteractableBus::Events::SetIsHandlingEvents)
  437. ->Event("IsHandlingMultiTouchEvents", &UiInteractableBus::Events::IsHandlingMultiTouchEvents)
  438. ->Event("SetIsHandlingMultiTouchEvents", &UiInteractableBus::Events::SetIsHandlingMultiTouchEvents)
  439. ->Event("GetIsAutoActivationEnabled", &UiInteractableBus::Events::GetIsAutoActivationEnabled)
  440. ->Event("LostActiveStatus", &UiInteractableBus::Events::LostActiveStatus)
  441. ->Event("SetIsAutoActivationEnabled", &UiInteractableBus::Events::SetIsAutoActivationEnabled);
  442. behaviorContext->EBus<UiInteractableActionsBus>("UiInteractableActionsBus")
  443. ->Event("GetHoverStartActionName", &UiInteractableActionsBus::Events::GetHoverStartActionName)
  444. ->Event("SetHoverStartActionName", &UiInteractableActionsBus::Events::SetHoverStartActionName)
  445. ->Event("GetHoverEndActionName", &UiInteractableActionsBus::Events::GetHoverEndActionName)
  446. ->Event("SetHoverEndActionName", &UiInteractableActionsBus::Events::SetHoverEndActionName)
  447. ->Event("GetPressedActionName", &UiInteractableActionsBus::Events::GetPressedActionName)
  448. ->Event("SetPressedActionName", &UiInteractableActionsBus::Events::SetPressedActionName)
  449. ->Event("GetReleasedActionName", &UiInteractableActionsBus::Events::GetReleasedActionName)
  450. ->Event("SetReleasedActionName", &UiInteractableActionsBus::Events::SetReleasedActionName);
  451. behaviorContext->Enum<(int)UiInteractableStatesInterface::StateNormal>("eUiInteractableState_Normal")
  452. ->Enum<(int)UiInteractableStatesInterface::StateHover>("eUiInteractableState_Hover")
  453. ->Enum<(int)UiInteractableStatesInterface::StatePressed>("eUiInteractableState_Pressed")
  454. ->Enum<(int)UiInteractableStatesInterface::StateDisabled>("eUiInteractableState_Disabled");
  455. behaviorContext->EBus<UiInteractableStatesBus>("UiInteractableStatesBus")
  456. ->Event("GetStateColor", &UiInteractableStatesBus::Events::GetStateColor)
  457. ->Event("SetStateColor", &UiInteractableStatesBus::Events::SetStateColor)
  458. ->Event("HasStateColor", &UiInteractableStatesBus::Events::HasStateColor)
  459. ->Event("GetStateAlpha", &UiInteractableStatesBus::Events::GetStateAlpha)
  460. ->Event("SetStateAlpha", &UiInteractableStatesBus::Events::SetStateAlpha)
  461. ->Event("HasStateAlpha", &UiInteractableStatesBus::Events::HasStateAlpha)
  462. ->Event("GetStateSpritePathname", &UiInteractableStatesBus::Events::GetStateSpritePathname)
  463. ->Event("SetStateSpritePathname", &UiInteractableStatesBus::Events::SetStateSpritePathname)
  464. ->Event("HasStateSprite", &UiInteractableStatesBus::Events::HasStateSprite)
  465. ->Event("GetStateFontPathname", &UiInteractableStatesBus::Events::GetStateFontPathname)
  466. ->Event("GetStateFontEffectIndex", &UiInteractableStatesBus::Events::GetStateFontEffectIndex)
  467. ->Event("SetStateFont", &UiInteractableStatesBus::Events::SetStateFont)
  468. ->Event("HasStateFont", &UiInteractableStatesBus::Events::HasStateFont);
  469. behaviorContext->EBus<UiInteractableNotificationBus>("UiInteractableNotificationBus")
  470. ->Handler<BehaviorUiInteractableNotificationBusHandler>();
  471. }
  472. UiInteractableStateAction::Reflect(context);
  473. UiInteractableStateColor::Reflect(context);
  474. UiInteractableStateAlpha::Reflect(context);
  475. UiInteractableStateSprite::Reflect(context);
  476. UiInteractableStateFont::Reflect(context);
  477. }
  478. ////////////////////////////////////////////////////////////////////////////////////////////////////
  479. // PROTECTED MEMBER FUNCTIONS
  480. ////////////////////////////////////////////////////////////////////////////////////////////////////
  481. ////////////////////////////////////////////////////////////////////////////////////////////////////
  482. void UiInteractableComponent::Init()
  483. {
  484. m_stateActionManager.Init(GetEntityId());
  485. }
  486. ////////////////////////////////////////////////////////////////////////////////////////////////////
  487. void UiInteractableComponent::Activate()
  488. {
  489. m_stateActionManager.Activate();
  490. m_navigationSettings.Activate(m_entity->GetId(), GetNavigableInteractables);
  491. UiInteractableBus::Handler::BusConnect(GetEntityId());
  492. UiInteractableActionsBus::Handler::BusConnect(GetEntityId());
  493. UiElementNotificationBus::Handler::BusConnect(GetEntityId());
  494. // The first time the component is activated the owning canvas will not be known. However if
  495. // the element is fixed up and then we deactivate and reactivate, OnUiElementFixup will
  496. // not get called again. So we need to connect to the UiCanvasUpdateNotificationBus here.
  497. // This assumes than on an element activate it will activate the UiElementComponent before
  498. // this component. We can rely on this because all UI components depend on UiElementService
  499. // as a required service.
  500. AZ::EntityId canvasEntityId;
  501. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  502. if (canvasEntityId.IsValid())
  503. {
  504. bool isElementEnabled = false;
  505. UiElementBus::EventResult(isElementEnabled, GetEntityId(), &UiElementBus::Events::GetAreElementAndAncestorsEnabled);
  506. if (isElementEnabled)
  507. {
  508. UiCanvasUpdateNotificationBus::Handler::BusConnect(canvasEntityId);
  509. }
  510. }
  511. }
  512. ////////////////////////////////////////////////////////////////////////////////////////////////////
  513. void UiInteractableComponent::Deactivate()
  514. {
  515. m_stateActionManager.Deactivate();
  516. m_navigationSettings.Deactivate();
  517. UiInteractableBus::Handler::BusDisconnect();
  518. UiCanvasUpdateNotificationBus::Handler::BusDisconnect();
  519. UiElementNotificationBus::Handler::BusDisconnect();
  520. UiInteractableActionsBus::Handler::BusDisconnect();
  521. }
  522. ////////////////////////////////////////////////////////////////////////////////////////////////////
  523. UiInteractableStatesInterface::State UiInteractableComponent::ComputeInteractableState()
  524. {
  525. UiInteractableStatesInterface::State state = UiInteractableStatesInterface::StateNormal;
  526. if (!m_isHandlingEvents)
  527. {
  528. // not handling events, use disabled state
  529. state = UiInteractableStatesInterface::StateDisabled;
  530. }
  531. else if (m_isPressed && m_isHover)
  532. {
  533. // We only use the pressed state when the button is pressed AND the mouse is over it
  534. state = UiInteractableStatesInterface::StatePressed;
  535. }
  536. else if (m_isHover || m_isPressed)
  537. {
  538. // we use the hover state for normal hover but also if the button is pressed but
  539. // the mouse is outside the button
  540. state = UiInteractableStatesInterface::StateHover;
  541. }
  542. return state;
  543. }
  544. ////////////////////////////////////////////////////////////////////////////////////////////////////
  545. void UiInteractableComponent::OnHoverStateActionsChanged()
  546. {
  547. m_stateActionManager.InitInteractableEntityForStateActions(m_hoverStateActions);
  548. }
  549. ////////////////////////////////////////////////////////////////////////////////////////////////////
  550. void UiInteractableComponent::OnPressedStateActionsChanged()
  551. {
  552. m_stateActionManager.InitInteractableEntityForStateActions(m_pressedStateActions);
  553. }
  554. ////////////////////////////////////////////////////////////////////////////////////////////////////
  555. void UiInteractableComponent::OnDisabledStateActionsChanged()
  556. {
  557. m_stateActionManager.InitInteractableEntityForStateActions(m_disabledStateActions);
  558. }
  559. ////////////////////////////////////////////////////////////////////////////////////////////////////
  560. void UiInteractableComponent::TriggerHoverStartAction()
  561. {
  562. // if a C++ callback is registered for hover start then call it
  563. if (m_hoverStartActionCallback)
  564. {
  565. m_hoverStartActionCallback(GetEntityId());
  566. }
  567. UiInteractableNotificationBus::Event(GetEntityId(), &UiInteractableNotificationBus::Events::OnHoverStart);
  568. // Tell any action listeners about the event
  569. if (!m_hoverStartActionName.empty())
  570. {
  571. AZ::EntityId canvasEntityId;
  572. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  573. UiCanvasNotificationBus::Event(canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_hoverStartActionName);
  574. }
  575. }
  576. ////////////////////////////////////////////////////////////////////////////////////////////////////
  577. void UiInteractableComponent::TriggerHoverEndAction()
  578. {
  579. // if a C++ callback is registered for hover end then call it
  580. if (m_hoverEndActionCallback)
  581. {
  582. m_hoverEndActionCallback(GetEntityId());
  583. }
  584. UiInteractableNotificationBus::Event(GetEntityId(), &UiInteractableNotificationBus::Events::OnHoverEnd);
  585. // Tell any action listeners about the event
  586. if (!m_hoverEndActionName.empty())
  587. {
  588. AZ::EntityId canvasEntityId;
  589. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  590. UiCanvasNotificationBus::Event(canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_hoverEndActionName);
  591. }
  592. }
  593. ////////////////////////////////////////////////////////////////////////////////////////////////////
  594. void UiInteractableComponent::TriggerPressedAction()
  595. {
  596. // if a C++ callback is registered for pressed then call it
  597. if (m_pressedActionCallback)
  598. {
  599. m_pressedActionCallback(GetEntityId());
  600. }
  601. // Queue the event to prevent deletions during the input event
  602. UiInteractableNotificationBus::QueueEvent(GetEntityId(), &UiInteractableNotificationBus::Events::OnPressed);
  603. // Tell any action listeners about the event
  604. if (!m_pressedActionName.empty())
  605. {
  606. AZ::EntityId canvasEntityId;
  607. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  608. // Queue the event to prevent deletions during the input event
  609. UiCanvasNotificationBus::QueueEvent(canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_pressedActionName);
  610. UiCanvasNotificationBus::QueueEvent(canvasEntityId, &UiCanvasNotificationBus::Events::OnActionMultitouch, GetEntityId(), m_pressedActionName, m_pressedPoint, m_pressedMultiTouchIndex);
  611. }
  612. }
  613. ////////////////////////////////////////////////////////////////////////////////////////////////////
  614. void UiInteractableComponent::TriggerReleasedAction(bool releasedOutside)
  615. {
  616. // if a C++ callback is registered for released then call it
  617. if (m_releasedActionCallback)
  618. {
  619. m_releasedActionCallback(GetEntityId());
  620. }
  621. // Queue the event to prevent deletions during the input event
  622. UiInteractableNotificationBus::QueueEvent(GetEntityId(), &UiInteractableNotificationBus::Events::OnReleased);
  623. if (releasedOutside && !m_outsideReleasedActionName.empty())
  624. {
  625. AZ::EntityId canvasEntityId;
  626. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  627. // Queue the event to prevent deletions during the input event
  628. UiCanvasNotificationBus::QueueEvent(canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_outsideReleasedActionName);
  629. UiCanvasNotificationBus::QueueEvent(canvasEntityId, &UiCanvasNotificationBus::Events::OnActionMultitouch, GetEntityId(), m_outsideReleasedActionName, m_pressedPoint, m_pressedMultiTouchIndex);
  630. return;
  631. }
  632. // Tell any action listeners about the event
  633. if (!m_releasedActionName.empty())
  634. {
  635. AZ::EntityId canvasEntityId;
  636. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  637. // Queue the event to prevent deletions during the input event
  638. UiCanvasNotificationBus::QueueEvent(
  639. canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_releasedActionName);
  640. UiCanvasNotificationBus::QueueEvent(canvasEntityId, &UiCanvasNotificationBus::Events::OnActionMultitouch, GetEntityId(), m_releasedActionName, m_pressedPoint, m_pressedMultiTouchIndex);
  641. }
  642. }
  643. ////////////////////////////////////////////////////////////////////////////////////////////////////
  644. void UiInteractableComponent::TriggerReceivedHoverByNavigatingFromDescendantAction(AZ::EntityId descendantEntityId)
  645. {
  646. UiInteractableNotificationBus::Event(
  647. GetEntityId(), &UiInteractableNotificationBus::Events::OnReceivedHoverByNavigatingFromDescendant, descendantEntityId);
  648. }
  649. ////////////////////////////////////////////////////////////////////////////////////////////////////
  650. bool UiInteractableComponent::IsAutoActivationSupported()
  651. {
  652. return false;
  653. }
  654. ////////////////////////////////////////////////////////////////////////////////////////////////////
  655. // PRIVATE STATIC MEMBER FUNCTIONS
  656. ////////////////////////////////////////////////////////////////////////////////////////////////////
  657. ////////////////////////////////////////////////////////////////////////////////////////////////////
  658. LyShine::EntityArray UiInteractableComponent::GetNavigableInteractables(AZ::EntityId entityId)
  659. {
  660. // Get a list of all navigable elements
  661. AZ::EntityId canvasEntityId;
  662. UiElementBus::EventResult(canvasEntityId, entityId, &UiElementBus::Events::GetCanvasEntityId);
  663. LyShine::EntityArray navigableElements;
  664. UiCanvasBus::Event(
  665. canvasEntityId,
  666. &UiCanvasBus::Events::FindElements,
  667. [entityId](const AZ::Entity* entity)
  668. {
  669. bool navigable = false;
  670. if (entity->GetId() != entityId)
  671. {
  672. if (UiInteractableBus::FindFirstHandler(entity->GetId()))
  673. {
  674. UiNavigationInterface::NavigationMode navigationMode = UiNavigationInterface::NavigationMode::None;
  675. UiNavigationBus::EventResult(navigationMode, entity->GetId(), &UiNavigationBus::Events::GetNavigationMode);
  676. navigable = (navigationMode != UiNavigationInterface::NavigationMode::None);
  677. }
  678. }
  679. return navigable;
  680. },
  681. navigableElements);
  682. return navigableElements;
  683. }
  684. ////////////////////////////////////////////////////////////////////////////////////////////////////
  685. bool UiInteractableComponent::VersionConverter(AZ::SerializeContext& context,
  686. AZ::SerializeContext::DataElementNode& classElement)
  687. {
  688. // conversion from version 1:
  689. // - Need to move the navigation settings into a sub element UiNavigationSettings
  690. if (classElement.GetVersion() <= 1)
  691. {
  692. int navModeIndex = classElement.FindElement(AZ_CRC_CE("NavigationMode"));
  693. int navUpIndex = classElement.FindElement(AZ_CRC_CE("OnUpEntity"));
  694. int navDownIndex = classElement.FindElement(AZ_CRC_CE("OnDownEntity"));
  695. int navLeftIndex = classElement.FindElement(AZ_CRC_CE("OnLeftEntity"));
  696. int navRightIndex = classElement.FindElement(AZ_CRC_CE("OnRightEntity"));
  697. if (navModeIndex == -1 || navUpIndex == -1 || navDownIndex == -1 || navLeftIndex == -1 || navRightIndex == -1)
  698. {
  699. AZ_Error("Serialization", false, "UiInteractableComponent version conversion failed finding navigation fields");
  700. return false;
  701. }
  702. // Add the new UiNavigationSettings node
  703. int navSettingsIndex = classElement.AddElement<UiNavigationSettings>(context, "NavigationSettings");
  704. if (navSettingsIndex == -1)
  705. {
  706. AZ_Error("Serialization", false, "UiInteractableComponent version conversion failed when adding navigation settings");
  707. return false;
  708. }
  709. AZ::SerializeContext::DataElementNode& navSettingsNode = classElement.GetSubElement(navSettingsIndex);
  710. // for each of the fields to move, make a copy of the existing node and add it to the nav settings
  711. AZ::SerializeContext::DataElementNode navModeNode = classElement.GetSubElement(navModeIndex);
  712. navSettingsNode.AddElement(navModeNode);
  713. AZ::SerializeContext::DataElementNode navUpNode = classElement.GetSubElement(navUpIndex);
  714. navSettingsNode.AddElement(navUpNode);
  715. AZ::SerializeContext::DataElementNode navDownNode = classElement.GetSubElement(navDownIndex);
  716. navSettingsNode.AddElement(navDownNode);
  717. AZ::SerializeContext::DataElementNode navLeftNode = classElement.GetSubElement(navLeftIndex);
  718. navSettingsNode.AddElement(navLeftNode);
  719. AZ::SerializeContext::DataElementNode navRightNode = classElement.GetSubElement(navRightIndex);
  720. navSettingsNode.AddElement(navRightNode);
  721. // Remove the old nodes in reverse order since removing an index invalidates all indices after it
  722. classElement.RemoveElement(navRightIndex);
  723. classElement.RemoveElement(navLeftIndex);
  724. classElement.RemoveElement(navDownIndex);
  725. classElement.RemoveElement(navUpIndex);
  726. classElement.RemoveElement(navModeIndex);
  727. }
  728. return true;
  729. }