UiTooltipDisplayComponent.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  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 "UiTooltipDisplayComponent.h"
  9. #include <AzCore/Serialization/SerializeContext.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzCore/RTTI/BehaviorContext.h>
  12. #include <AzCore/std/sort.h>
  13. #include <AzCore/Time/ITime.h>
  14. #include <LyShine/Bus/UiTransform2dBus.h>
  15. #include <LyShine/Bus/UiElementBus.h>
  16. #include <LyShine/Bus/UiTextBus.h>
  17. #include <LyShine/Bus/UiCanvasBus.h>
  18. #include <LyShine/Bus/UiTooltipDataPopulatorBus.h>
  19. #include <LyShine/UiSerializeHelpers.h>
  20. ////////////////////////////////////////////////////////////////////////////////////////////////////
  21. // PUBLIC MEMBER FUNCTIONS
  22. ////////////////////////////////////////////////////////////////////////////////////////////////////
  23. ////////////////////////////////////////////////////////////////////////////////////////////////////
  24. UiTooltipDisplayComponent::UiTooltipDisplayComponent()
  25. : m_triggerMode(TriggerMode::OnHover)
  26. , m_autoPosition(true)
  27. , m_autoPositionMode(AutoPositionMode::OffsetFromMouse)
  28. , m_offset(0.0f, -10.0f)
  29. , m_autoSize(true)
  30. , m_delayTime(0.5f)
  31. , m_displayTime(5.0f)
  32. , m_state(State::Hidden)
  33. , m_stateStartTime(-1.0f)
  34. , m_curDelayTime(-1.0f)
  35. , m_timeSinceLastShown(-1.0f)
  36. , m_maxWrapTextWidth(-1.0f)
  37. , m_showSequence(nullptr)
  38. , m_hideSequence(nullptr)
  39. {
  40. }
  41. ////////////////////////////////////////////////////////////////////////////////////////////////////
  42. UiTooltipDisplayComponent::~UiTooltipDisplayComponent()
  43. {
  44. }
  45. ////////////////////////////////////////////////////////////////////////////////////////////////////
  46. UiTooltipDisplayInterface::TriggerMode UiTooltipDisplayComponent::GetTriggerMode()
  47. {
  48. return m_triggerMode;
  49. }
  50. ////////////////////////////////////////////////////////////////////////////////////////////////////
  51. void UiTooltipDisplayComponent::SetTriggerMode(TriggerMode triggerMode)
  52. {
  53. m_triggerMode = triggerMode;
  54. }
  55. ////////////////////////////////////////////////////////////////////////////////////////////////////
  56. bool UiTooltipDisplayComponent::GetAutoPosition()
  57. {
  58. return m_autoPosition;
  59. }
  60. ////////////////////////////////////////////////////////////////////////////////////////////////////
  61. void UiTooltipDisplayComponent::SetAutoPosition(bool autoPosition)
  62. {
  63. m_autoPosition = autoPosition;
  64. }
  65. ////////////////////////////////////////////////////////////////////////////////////////////////////
  66. UiTooltipDisplayInterface::AutoPositionMode UiTooltipDisplayComponent::GetAutoPositionMode()
  67. {
  68. return m_autoPositionMode;
  69. }
  70. ////////////////////////////////////////////////////////////////////////////////////////////////////
  71. void UiTooltipDisplayComponent::SetAutoPositionMode(AutoPositionMode autoPositionMode)
  72. {
  73. m_autoPositionMode = autoPositionMode;
  74. }
  75. ////////////////////////////////////////////////////////////////////////////////////////////////////
  76. const AZ::Vector2& UiTooltipDisplayComponent::GetOffset()
  77. {
  78. return m_offset;
  79. }
  80. ////////////////////////////////////////////////////////////////////////////////////////////////////
  81. void UiTooltipDisplayComponent::SetOffset(const AZ::Vector2& offset)
  82. {
  83. m_offset = offset;
  84. }
  85. ////////////////////////////////////////////////////////////////////////////////////////////////////
  86. bool UiTooltipDisplayComponent::GetAutoSize()
  87. {
  88. return m_autoSize;
  89. }
  90. ////////////////////////////////////////////////////////////////////////////////////////////////////
  91. void UiTooltipDisplayComponent::SetAutoSize(bool autoSize)
  92. {
  93. m_autoSize = autoSize;
  94. }
  95. ////////////////////////////////////////////////////////////////////////////////////////////////////
  96. AZ::EntityId UiTooltipDisplayComponent::GetTextEntity()
  97. {
  98. return m_textEntity;
  99. }
  100. ////////////////////////////////////////////////////////////////////////////////////////////////////
  101. void UiTooltipDisplayComponent::SetTextEntity(AZ::EntityId textEntity)
  102. {
  103. m_textEntity = textEntity;
  104. }
  105. ////////////////////////////////////////////////////////////////////////////////////////////////////
  106. float UiTooltipDisplayComponent::GetDelayTime()
  107. {
  108. return m_delayTime;
  109. }
  110. ////////////////////////////////////////////////////////////////////////////////////////////////////
  111. void UiTooltipDisplayComponent::SetDelayTime(float delayTime)
  112. {
  113. m_delayTime = delayTime;
  114. }
  115. ////////////////////////////////////////////////////////////////////////////////////////////////////
  116. float UiTooltipDisplayComponent::GetDisplayTime()
  117. {
  118. return m_displayTime;
  119. }
  120. ////////////////////////////////////////////////////////////////////////////////////////////////////
  121. void UiTooltipDisplayComponent::SetDisplayTime(float displayTime)
  122. {
  123. m_displayTime = displayTime;
  124. }
  125. ////////////////////////////////////////////////////////////////////////////////////////////////////
  126. void UiTooltipDisplayComponent::PrepareToShow(AZ::EntityId tooltipElement)
  127. {
  128. m_tooltipElement = tooltipElement;
  129. // We should be in the hidden state
  130. AZ_Assert(((m_state == State::Hiding) || (m_state == State::Hidden)), "State is not hidden when attempting to show tooltip");
  131. if (m_state != State::Hidden)
  132. {
  133. EndTransitionState();
  134. SetState(State::Hidden);
  135. }
  136. m_curDelayTime = m_delayTime;
  137. SetState(State::DelayBeforeShow);
  138. }
  139. ////////////////////////////////////////////////////////////////////////////////////////////////////
  140. void UiTooltipDisplayComponent::Hide()
  141. {
  142. switch (m_state)
  143. {
  144. case State::Showing:
  145. {
  146. // Since sequences can't have keys that represent current values,
  147. // only play the hide animation if the show animation has completed.
  148. const AZ::TimeMs realTimeMs = AZ::GetRealElapsedTimeMs();
  149. m_timeSinceLastShown = AZ::TimeMsToSeconds(realTimeMs);
  150. EndTransitionState();
  151. SetState(State::Hidden);
  152. }
  153. break;
  154. case State::Shown:
  155. {
  156. const AZ::TimeMs realTimeMs = AZ::GetRealElapsedTimeMs();
  157. m_timeSinceLastShown = AZ::TimeMsToSeconds(realTimeMs);
  158. // Check if there is a hide animation to play
  159. IUiAnimationSystem* animSystem = nullptr;
  160. PrepareSequenceForPlay(m_hideSequenceName, m_hideSequence, animSystem);
  161. if (m_hideSequence)
  162. {
  163. SetState(State::Hiding);
  164. animSystem->PlaySequence(m_hideSequence, nullptr, false, false);
  165. }
  166. else
  167. {
  168. SetState(State::Hidden);
  169. }
  170. }
  171. break;
  172. case State::DelayBeforeShow:
  173. {
  174. SetState(State::Hidden);
  175. }
  176. break;
  177. default:
  178. break;
  179. }
  180. }
  181. ////////////////////////////////////////////////////////////////////////////////////////////////////
  182. void UiTooltipDisplayComponent::Update()
  183. {
  184. if (m_state == State::DelayBeforeShow)
  185. {
  186. // Check if it's time to show the tooltip
  187. const AZ::TimeMs realTimeMs = AZ::GetRealElapsedTimeMs();
  188. const float currentTime = AZ::TimeMsToSeconds(realTimeMs);
  189. if ((currentTime - m_stateStartTime) >= m_curDelayTime)
  190. {
  191. // Make sure nothing has changed with the hover interactable
  192. if (m_tooltipElement.IsValid() && UiTooltipDataPopulatorBus::FindFirstHandler(m_tooltipElement))
  193. {
  194. Show();
  195. }
  196. else
  197. {
  198. Hide();
  199. }
  200. }
  201. }
  202. else if (m_state == State::Shown)
  203. {
  204. // Check if it's time to hide the tooltip
  205. if (m_displayTime >= 0.0f)
  206. {
  207. const AZ::TimeMs realTimeMs = AZ::GetRealElapsedTimeMs();
  208. const float currentTime = AZ::TimeMsToSeconds(realTimeMs);
  209. if ((currentTime - m_stateStartTime) >= m_displayTime)
  210. {
  211. // Hide tooltip
  212. Hide();
  213. }
  214. }
  215. }
  216. }
  217. ////////////////////////////////////////////////////////////////////////////////////////////////////
  218. void UiTooltipDisplayComponent::InGamePostActivate()
  219. {
  220. SetState(State::Hidden);
  221. }
  222. ////////////////////////////////////////////////////////////////////////////////////////////////////
  223. void UiTooltipDisplayComponent::OnUiAnimationEvent(EUiAnimationEvent uiAnimationEvent, IUiAnimSequence* animSequence)
  224. {
  225. if (m_state == State::Showing && animSequence == m_showSequence)
  226. {
  227. if ((uiAnimationEvent == eUiAnimationEvent_Stopped) || (uiAnimationEvent == eUiAnimationEvent_Aborted))
  228. {
  229. SetState(State::Shown);
  230. }
  231. }
  232. else if (m_state == State::Hiding && animSequence == m_hideSequence)
  233. {
  234. if ((uiAnimationEvent == eUiAnimationEvent_Stopped) || (uiAnimationEvent == eUiAnimationEvent_Aborted))
  235. {
  236. SetState(State::Hidden);
  237. }
  238. }
  239. }
  240. ////////////////////////////////////////////////////////////////////////////////////////////////////
  241. UiTooltipDisplayComponent::State UiTooltipDisplayComponent::GetState()
  242. {
  243. return m_state;
  244. }
  245. ////////////////////////////////////////////////////////////////////////////////////////////////////
  246. // PUBLIC STATIC MEMBER FUNCTIONS
  247. ////////////////////////////////////////////////////////////////////////////////////////////////////
  248. void UiTooltipDisplayComponent::Reflect(AZ::ReflectContext* context)
  249. {
  250. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  251. if (serializeContext)
  252. {
  253. serializeContext->Class<UiTooltipDisplayComponent, AZ::Component>()
  254. ->Version(2, &VersionConverter)
  255. ->Field("TriggerMode", &UiTooltipDisplayComponent::m_triggerMode)
  256. ->Field("AutoPosition", &UiTooltipDisplayComponent::m_autoPosition)
  257. ->Field("AutoPositionMode", &UiTooltipDisplayComponent::m_autoPositionMode)
  258. ->Field("Offset", &UiTooltipDisplayComponent::m_offset)
  259. ->Field("AutoSize", &UiTooltipDisplayComponent::m_autoSize)
  260. ->Field("Text", &UiTooltipDisplayComponent::m_textEntity)
  261. ->Field("DelayTime", &UiTooltipDisplayComponent::m_delayTime)
  262. ->Field("DisplayTime", &UiTooltipDisplayComponent::m_displayTime)
  263. ->Field("ShowSequence", &UiTooltipDisplayComponent::m_showSequenceName)
  264. ->Field("HideSequence", &UiTooltipDisplayComponent::m_hideSequenceName);
  265. AZ::EditContext* ec = serializeContext->GetEditContext();
  266. if (ec)
  267. {
  268. auto editInfo = ec->Class<UiTooltipDisplayComponent>("TooltipDisplay", "A component that handles how the tooltip element is to be displayed.");
  269. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  270. ->Attribute(AZ::Edit::Attributes::Category, "UI")
  271. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiTooltipDisplay.png")
  272. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiTooltipDisplay.png")
  273. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("UI"))
  274. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  275. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiTooltipDisplayComponent::m_triggerMode, "Trigger Mode",
  276. "Sets the way the tooltip is triggered to display.")
  277. ->EnumAttribute(UiTooltipDisplayInterface::TriggerMode::OnHover, "On Hover")
  278. ->EnumAttribute(UiTooltipDisplayInterface::TriggerMode::OnPress, "On Press")
  279. ->EnumAttribute(UiTooltipDisplayInterface::TriggerMode::OnClick, "On Click");
  280. editInfo->DataElement(0, &UiTooltipDisplayComponent::m_autoPosition, "Auto position",
  281. "Whether the element will automatically be positioned.")
  282. ->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC_CE("RefreshEntireTree"));
  283. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiTooltipDisplayComponent::m_autoPositionMode, "Positioning",
  284. "Sets the positioning behavior of the element.")
  285. ->EnumAttribute(UiTooltipDisplayInterface::AutoPositionMode::OffsetFromMouse, "Offset from mouse")
  286. ->EnumAttribute(UiTooltipDisplayInterface::AutoPositionMode::OffsetFromElement, "Offset from element")
  287. ->Attribute(AZ::Edit::Attributes::Visibility, &UiTooltipDisplayComponent::m_autoPosition);
  288. editInfo->DataElement(0, &UiTooltipDisplayComponent::m_offset, "Offset",
  289. "The offset to use when positioning the element.")
  290. ->Attribute(AZ::Edit::Attributes::Visibility, &UiTooltipDisplayComponent::m_autoPosition);
  291. editInfo->DataElement(0, &UiTooltipDisplayComponent::m_autoSize, "Auto size",
  292. "Whether the element will automatically be sized so that the text element's size is the same as the size of the tooltip string.\n"
  293. "If auto size is on, the text element's anchors should be apart.");
  294. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiTooltipDisplayComponent::m_textEntity, "Text",
  295. "The UI element to hold the main tooltip text. Also used for auto sizing.")
  296. ->Attribute(AZ::Edit::Attributes::EnumValues, &UiTooltipDisplayComponent::PopulateTextEntityList);
  297. editInfo->DataElement(0, &UiTooltipDisplayComponent::m_delayTime, "Delay Time",
  298. "The amount of time to wait before displaying the element.");
  299. editInfo->DataElement(0, &UiTooltipDisplayComponent::m_displayTime, "Display Time",
  300. "The amount of time the element is to be displayed.");
  301. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiTooltipDisplayComponent::m_showSequenceName, "Show Sequence",
  302. "The sequence to be played when the element is about to show.")
  303. ->Attribute(AZ::Edit::Attributes::StringList, &UiTooltipDisplayComponent::PopulateSequenceList);
  304. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiTooltipDisplayComponent::m_hideSequenceName, "Hide Sequence",
  305. "The sequence to be played when the element is about to hide.")
  306. ->Attribute(AZ::Edit::Attributes::StringList, &UiTooltipDisplayComponent::PopulateSequenceList);
  307. }
  308. }
  309. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  310. if (behaviorContext)
  311. {
  312. behaviorContext->Enum<(int)UiTooltipDisplayInterface::AutoPositionMode::OffsetFromMouse>("eUiTooltipDisplayAutoPositionMode_OffsetFromMouse")
  313. ->Enum<(int)UiTooltipDisplayInterface::AutoPositionMode::OffsetFromElement>("eUiTooltipDisplayAutoPositionMode_OffsetFromElement");
  314. behaviorContext->Enum<(int)UiTooltipDisplayInterface::TriggerMode::OnHover>("eUiTooltipDisplayTriggerMode_OnHover")
  315. ->Enum<(int)UiTooltipDisplayInterface::TriggerMode::OnPress>("eUiTooltipDisplayTriggerMode_OnPress")
  316. ->Enum<(int)UiTooltipDisplayInterface::TriggerMode::OnPress>("eUiTooltipDisplayTriggerMode_OnClick");
  317. behaviorContext->EBus<UiTooltipDisplayBus>("UiTooltipDisplayBus")
  318. ->Event("GetTriggerMode", &UiTooltipDisplayBus::Events::GetTriggerMode)
  319. ->Event("SetTriggerMode", &UiTooltipDisplayBus::Events::SetTriggerMode)
  320. ->Event("GetAutoPosition", &UiTooltipDisplayBus::Events::GetAutoPosition)
  321. ->Event("SetAutoPosition", &UiTooltipDisplayBus::Events::SetAutoPosition)
  322. ->Event("GetAutoPositionMode", &UiTooltipDisplayBus::Events::GetAutoPositionMode)
  323. ->Event("SetAutoPositionMode", &UiTooltipDisplayBus::Events::SetAutoPositionMode)
  324. ->Event("GetOffset", &UiTooltipDisplayBus::Events::GetOffset)
  325. ->Event("SetOffset", &UiTooltipDisplayBus::Events::SetOffset)
  326. ->Event("GetAutoSize", &UiTooltipDisplayBus::Events::GetAutoSize)
  327. ->Event("SetAutoSize", &UiTooltipDisplayBus::Events::SetAutoSize)
  328. ->Event("GetTextEntity", &UiTooltipDisplayBus::Events::GetTextEntity)
  329. ->Event("SetTextEntity", &UiTooltipDisplayBus::Events::SetTextEntity)
  330. ->Event("GetDelayTime", &UiTooltipDisplayBus::Events::GetDelayTime)
  331. ->Event("SetDelayTime", &UiTooltipDisplayBus::Events::SetDelayTime)
  332. ->Event("GetDisplayTime", &UiTooltipDisplayBus::Events::GetDisplayTime)
  333. ->Event("SetDisplayTime", &UiTooltipDisplayBus::Events::SetDisplayTime)
  334. ->VirtualProperty("DelayTime", "GetDelayTime", "SetDelayTime")
  335. ->VirtualProperty("DisplayTime", "GetDisplayTime", "SetDisplayTime");
  336. behaviorContext->Class<UiTooltipDisplayComponent>()->RequestBus("UiTooltipDisplayBus");
  337. }
  338. }
  339. ////////////////////////////////////////////////////////////////////////////////////////////////////
  340. // PROTECTED MEMBER FUNCTIONS
  341. ////////////////////////////////////////////////////////////////////////////////////////////////////
  342. ////////////////////////////////////////////////////////////////////////////////////////////////////
  343. void UiTooltipDisplayComponent::Activate()
  344. {
  345. UiTooltipDisplayBus::Handler::BusConnect(GetEntityId());
  346. UiInitializationBus::Handler::BusConnect(GetEntityId());
  347. }
  348. ////////////////////////////////////////////////////////////////////////////////////////////////////
  349. void UiTooltipDisplayComponent::Deactivate()
  350. {
  351. UiTooltipDisplayBus::Handler::BusDisconnect(GetEntityId());
  352. UiInitializationBus::Handler::BusDisconnect(GetEntityId());
  353. // Stop listening for animation actions. The sequences may have been deleted at this point,
  354. // so first check if they still exist.
  355. IUiAnimationSystem* animSystem = nullptr;
  356. IUiAnimSequence* sequence;
  357. if (m_listeningForAnimationEvents)
  358. {
  359. m_listeningForAnimationEvents = false;
  360. sequence = GetSequence(m_showSequenceName, animSystem);
  361. if (sequence)
  362. {
  363. animSystem->RemoveUiAnimationListener(sequence, this);
  364. }
  365. sequence = GetSequence(m_hideSequenceName, animSystem);
  366. if (sequence)
  367. {
  368. animSystem->RemoveUiAnimationListener(sequence, this);
  369. }
  370. }
  371. }
  372. ////////////////////////////////////////////////////////////////////////////////////////////////////
  373. void UiTooltipDisplayComponent::SetState(State state)
  374. {
  375. m_state = state;
  376. const AZ::TimeMs realTimeMs = AZ::GetRealElapsedTimeMs();
  377. m_stateStartTime = AZ::TimeMsToSeconds(realTimeMs);
  378. switch (m_state)
  379. {
  380. case State::Hiding:
  381. UiTooltipDisplayNotificationBus::Event(m_tooltipElement, &UiTooltipDisplayNotificationBus::Events::OnHiding);
  382. break;
  383. case State::Hidden:
  384. UiTooltipDisplayNotificationBus::Event(m_tooltipElement, &UiTooltipDisplayNotificationBus::Events::OnHidden);
  385. UiElementBus::Event(GetEntityId(), &UiElementBus::Events::SetIsEnabled, false);
  386. break;
  387. case State::Showing:
  388. case State::Shown:
  389. UiElementBus::Event(GetEntityId(), &UiElementBus::Events::SetIsEnabled, true);
  390. if (m_state == State::Showing)
  391. {
  392. UiTooltipDisplayNotificationBus::Event(m_tooltipElement, &UiTooltipDisplayNotificationBus::Events::OnShowing);
  393. }
  394. else
  395. {
  396. UiTooltipDisplayNotificationBus::Event(m_tooltipElement, &UiTooltipDisplayNotificationBus::Events::OnShown);
  397. }
  398. break;
  399. default:
  400. break;
  401. }
  402. }
  403. ////////////////////////////////////////////////////////////////////////////////////////////////////
  404. void UiTooltipDisplayComponent::EndTransitionState()
  405. {
  406. if (m_state == State::Showing)
  407. {
  408. if (m_showSequence)
  409. {
  410. IUiAnimationSystem* animSystem = GetAnimationSystem();
  411. if (animSystem)
  412. {
  413. animSystem->StopSequence(m_showSequence);
  414. }
  415. }
  416. SetState(State::Shown);
  417. }
  418. else if (m_state == State::Hiding)
  419. {
  420. if (m_hideSequence)
  421. {
  422. IUiAnimationSystem* animSystem = GetAnimationSystem();
  423. if (animSystem)
  424. {
  425. animSystem->StopSequence(m_hideSequence);
  426. }
  427. }
  428. SetState(State::Hidden);
  429. }
  430. }
  431. ////////////////////////////////////////////////////////////////////////////////////////////////////
  432. void UiTooltipDisplayComponent::Show()
  433. {
  434. // It is assumed that current state is DelayBeforeShow
  435. if (m_autoSize && m_textEntity.IsValid())
  436. {
  437. // Initialize max wrap text width
  438. if (m_maxWrapTextWidth < 0.0f)
  439. {
  440. UiTransformInterface::Rect textRect;
  441. UiTransformBus::Event(m_textEntity, &UiTransformBus::Events::GetCanvasSpaceRectNoScaleRotate, textRect);
  442. m_maxWrapTextWidth = textRect.GetWidth();
  443. }
  444. // If wrapping is on, reset display element to its original width
  445. UiTextInterface::WrapTextSetting wrapSetting = UiTextInterface::WrapTextSetting::NoWrap;
  446. UiTextBus::EventResult(wrapSetting, m_textEntity, &UiTextBus::Events::GetWrapText);
  447. if (wrapSetting != UiTextInterface::WrapTextSetting::NoWrap)
  448. {
  449. UiTransformInterface::Rect textRect;
  450. UiTransformBus::Event(m_textEntity, &UiTransformBus::Events::GetCanvasSpaceRectNoScaleRotate, textRect);
  451. AZ::Vector2 delta(m_maxWrapTextWidth - textRect.GetWidth(), 0.0f);
  452. ResizeElementByDelta(GetEntityId(), delta);
  453. }
  454. }
  455. // Assign tooltip data to the tooltip display element
  456. UiTooltipDataPopulatorBus::Event(m_tooltipElement, &UiTooltipDataPopulatorBus::Events::PushDataToDisplayElement, GetEntityId());
  457. // Auto-resize display element so that the text element is the same size as the size of its text string
  458. if (m_autoSize && m_textEntity.IsValid())
  459. {
  460. AutoResize();
  461. }
  462. // Auto-position display element
  463. if (m_autoPosition)
  464. {
  465. AutoPosition();
  466. }
  467. // Check if there is a show animation to play
  468. IUiAnimationSystem* animSystem = nullptr;
  469. PrepareSequenceForPlay(m_showSequenceName, m_showSequence, animSystem);
  470. if (m_showSequence)
  471. {
  472. SetState(State::Showing);
  473. animSystem->PlaySequence(m_showSequence, nullptr, false, false);
  474. }
  475. else
  476. {
  477. SetState(State::Shown);
  478. }
  479. }
  480. ////////////////////////////////////////////////////////////////////////////////////////////////////
  481. void UiTooltipDisplayComponent::AutoResize()
  482. {
  483. // Get the text string size
  484. AZ::Vector2 stringSize(-1.0f, -1.0f);
  485. UiTextBus::EventResult(stringSize, m_textEntity, &UiTextBus::Events::GetTextSize);
  486. if (stringSize.GetX() < 0.0f || stringSize.GetY() < 0.0f)
  487. {
  488. return;
  489. }
  490. // Get the text element's rect
  491. UiTransformInterface::Rect textRect;
  492. UiTransformBus::Event(m_textEntity, &UiTransformBus::Events::GetCanvasSpaceRectNoScaleRotate, textRect);
  493. // Get the difference between the text element's size and the string size
  494. AZ::Vector2 delta(stringSize.GetX() - textRect.GetWidth(), stringSize.GetY() - textRect.GetHeight());
  495. UiTextInterface::WrapTextSetting wrapSetting;
  496. UiTextBus::EventResult(wrapSetting, m_textEntity, &UiTextBus::Events::GetWrapText);
  497. if (wrapSetting != UiTextInterface::WrapTextSetting::NoWrap)
  498. {
  499. // In order for the wrapping to remain the same after the resize, the
  500. // text element width would need to match the string width exactly. To accommodate
  501. // for slight variation in size, add a small value to ensure that the string will fit
  502. // inside the text element's bounds. The downside to this is there may be extra space
  503. // at the bottom, but this is unlikely.
  504. const float epsilon = 0.01f;
  505. delta.SetX(delta.GetX() + epsilon);
  506. }
  507. // Resize the display element by the difference
  508. ResizeElementByDelta(GetEntityId(), delta);
  509. }
  510. ////////////////////////////////////////////////////////////////////////////////////////////////////
  511. void UiTooltipDisplayComponent::ResizeElementByDelta(AZ::EntityId entityId, const AZ::Vector2& delta)
  512. {
  513. if ((delta.GetX() != 0.0f) || (delta.GetY() != 0.0f))
  514. {
  515. // Resize the element based on the difference
  516. UiTransform2dInterface::Offsets offsets;
  517. UiTransform2dBus::EventResult(offsets, entityId, &UiTransform2dBus::Events::GetOffsets);
  518. AZ::Vector2 pivot;
  519. UiTransformBus::EventResult(pivot, entityId, &UiTransformBus::Events::GetPivot);
  520. if (delta.GetX() != 0.0f)
  521. {
  522. float leftOffsetDelta = delta.GetX() * pivot.GetX();
  523. offsets.m_left -= leftOffsetDelta;
  524. offsets.m_right += delta.GetX() - leftOffsetDelta;
  525. }
  526. if (delta.GetY() != 0.0f)
  527. {
  528. float topOffsetDelta = delta.GetY() * pivot.GetY();
  529. offsets.m_top -= topOffsetDelta;
  530. offsets.m_bottom += delta.GetY() - topOffsetDelta;
  531. }
  532. UiTransform2dBus::Event(entityId, &UiTransform2dBus::Events::SetOffsets, offsets);
  533. }
  534. }
  535. ////////////////////////////////////////////////////////////////////////////////////////////////////
  536. void UiTooltipDisplayComponent::AutoPosition()
  537. {
  538. bool havePosition = false;
  539. AZ::Vector2 position(-1.0f, -1.0f);
  540. if (m_autoPositionMode == UiTooltipDisplayInterface::AutoPositionMode::OffsetFromMouse)
  541. {
  542. // Get current mouse position
  543. AZ::EntityId canvasId;
  544. UiElementBus::EventResult(canvasId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  545. UiCanvasBus::EventResult(position, canvasId, &UiCanvasBus::Events::GetMousePosition);
  546. if (position.GetX() >= 0.0f && position.GetY() >= 0.0f)
  547. {
  548. // Check if the mouse is hovering over the tooltip element
  549. UiTransformBus::EventResult(havePosition, m_tooltipElement, &UiTransformBus::Events::IsPointInRect, position);
  550. }
  551. }
  552. if (!havePosition)
  553. {
  554. // Get the pivot position of the tooltip element
  555. UiTransformBus::EventResult(position, m_tooltipElement, &UiTransformBus::Events::GetViewportSpacePivot);
  556. }
  557. MoveToPosition(position, m_offset);
  558. }
  559. ////////////////////////////////////////////////////////////////////////////////////////////////////
  560. void UiTooltipDisplayComponent::MoveToPosition(const AZ::Vector2& point, const AZ::Vector2& offsetFromPoint)
  561. {
  562. // Move the display element so that its pivot is a certain distance from the point
  563. UiTransform2dInterface::Offsets offsets;
  564. UiTransform2dBus::EventResult(offsets, GetEntityId(), &UiTransform2dBus::Events::GetOffsets);
  565. AZ::Vector2 pivot;
  566. UiTransformBus::EventResult(pivot, GetEntityId(), &UiTransformBus::Events::GetViewportSpacePivot);
  567. AZ::Vector2 moveDist = (point + offsetFromPoint) - pivot;
  568. offsets += moveDist;
  569. UiTransform2dBus::Event(GetEntityId(), &UiTransform2dBus::Events::SetOffsets, offsets);
  570. // Make sure that the display element stays within the bounds of its parent
  571. // Get parent rect
  572. UiTransformInterface::Rect parentRect;
  573. AZ::Entity* parentEntity = nullptr;
  574. UiElementBus::EventResult(parentEntity, GetEntityId(), &UiElementBus::Events::GetParent);
  575. if (parentEntity)
  576. {
  577. UiTransformBus::Event(parentEntity->GetId(), &UiTransformBus::Events::GetCanvasSpaceRectNoScaleRotate, parentRect);
  578. }
  579. else
  580. {
  581. AZ::EntityId canvasEntityId;
  582. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  583. AZ::Vector2 size;
  584. UiCanvasBus::EventResult(size, canvasEntityId, &UiCanvasBus::Events::GetCanvasSize);
  585. parentRect.Set(0.0f, size.GetX(), 0.0f, size.GetY());
  586. }
  587. // If the display element exceeds the top/bottom bounds of its parent, the offset
  588. // is flipped and applied to the top/bottom of the display element.
  589. // If positioning is to be relative to the tooltip element though, this step is skipped
  590. // to preserve the original position as much as possible.
  591. if (m_autoPositionMode != UiTooltipDisplayInterface::AutoPositionMode::OffsetFromElement)
  592. {
  593. CheckBoundsAndChangeYPosition(parentRect, point.GetY(), fabs(m_offset.GetY()));
  594. }
  595. ConstrainToBounds(parentRect);
  596. }
  597. ////////////////////////////////////////////////////////////////////////////////////////////////////
  598. void UiTooltipDisplayComponent::CheckBoundsAndChangeYPosition(const UiTransformInterface::Rect& boundsRect, float yPoint, float yOffsetFromPoint)
  599. {
  600. // Get display element rect
  601. UiTransformInterface::Rect rect = GetAxisAlignedRect();
  602. float yMoveDist = 0.0f;
  603. // Check upper and lower bounds
  604. if (rect.top < boundsRect.top)
  605. {
  606. // Move top of display element to an offset below the point
  607. yMoveDist = (yPoint + yOffsetFromPoint) - rect.top;
  608. }
  609. else if (rect.bottom > boundsRect.bottom)
  610. {
  611. // Move bottom of display element to an offset above the point
  612. yMoveDist = (yPoint - yOffsetFromPoint) - rect.bottom;
  613. }
  614. if (yMoveDist != 0.0f)
  615. {
  616. UiTransform2dInterface::Offsets offsets;
  617. UiTransform2dBus::EventResult(offsets, GetEntityId(), &UiTransform2dBus::Events::GetOffsets);
  618. offsets.m_top += yMoveDist;
  619. offsets.m_bottom += yMoveDist;
  620. UiTransform2dBus::Event(GetEntityId(), &UiTransform2dBus::Events::SetOffsets, offsets);
  621. }
  622. }
  623. ////////////////////////////////////////////////////////////////////////////////////////////////////
  624. void UiTooltipDisplayComponent::ConstrainToBounds(const UiTransformInterface::Rect& boundsRect)
  625. {
  626. // Get display element rect
  627. UiTransformInterface::Rect rect = GetAxisAlignedRect();
  628. AZ::Vector2 moveDist(0.0f, 0.0f);
  629. // Check left and right bounds
  630. if (rect.left < boundsRect.left)
  631. {
  632. moveDist.SetX(boundsRect.left - rect.left);
  633. }
  634. else if (rect.right > boundsRect.right)
  635. {
  636. moveDist.SetX(boundsRect.right - rect.right);
  637. }
  638. // Check upper and lower bounds
  639. if (rect.top < boundsRect.top)
  640. {
  641. moveDist.SetY(boundsRect.top - rect.top);
  642. }
  643. else if (rect.bottom > boundsRect.bottom)
  644. {
  645. moveDist.SetY(boundsRect.bottom - rect.bottom);
  646. }
  647. if (moveDist.GetX() != 0.0f || moveDist.GetY() != 0.0f)
  648. {
  649. UiTransform2dInterface::Offsets offsets;
  650. UiTransform2dBus::EventResult(offsets, GetEntityId(), &UiTransform2dBus::Events::GetOffsets);
  651. offsets += moveDist;
  652. UiTransform2dBus::Event(GetEntityId(), &UiTransform2dBus::Events::SetOffsets, offsets);
  653. }
  654. }
  655. ////////////////////////////////////////////////////////////////////////////////////////////////////
  656. UiTransformInterface::Rect UiTooltipDisplayComponent::GetAxisAlignedRect()
  657. {
  658. UiTransformInterface::RectPoints points;
  659. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetCanvasSpacePointsNoScaleRotate, points);
  660. AZ::Matrix4x4 transform;
  661. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetLocalTransform, transform);
  662. points = points.Transform(transform);
  663. UiTransformInterface::Rect rect;
  664. rect.left = points.GetAxisAlignedTopLeft().GetX();
  665. rect.right = points.GetAxisAlignedBottomRight().GetX();
  666. rect.top = points.GetAxisAlignedTopLeft().GetY();
  667. rect.bottom = points.GetAxisAlignedBottomRight().GetY();
  668. return rect;
  669. }
  670. ////////////////////////////////////////////////////////////////////////////////////////////////////
  671. IUiAnimationSystem* UiTooltipDisplayComponent::GetAnimationSystem()
  672. {
  673. AZ::EntityId canvasId;
  674. UiElementBus::EventResult(canvasId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  675. IUiAnimationSystem* animSystem = nullptr;
  676. UiCanvasBus::EventResult(animSystem, canvasId, &UiCanvasBus::Events::GetAnimationSystem);
  677. return animSystem;
  678. }
  679. ////////////////////////////////////////////////////////////////////////////////////////////////////
  680. IUiAnimSequence* UiTooltipDisplayComponent::GetSequence(const AZStd::string& sequenceName, IUiAnimationSystem*& animSystemOut)
  681. {
  682. IUiAnimSequence* sequence = nullptr;
  683. if (!sequenceName.empty() && (sequenceName != "<None>"))
  684. {
  685. IUiAnimationSystem* animSystem = GetAnimationSystem();
  686. if (animSystem)
  687. {
  688. sequence = animSystem->FindSequence(sequenceName.c_str());
  689. animSystemOut = animSystem;
  690. }
  691. }
  692. return sequence;
  693. }
  694. ////////////////////////////////////////////////////////////////////////////////////////////////////
  695. void UiTooltipDisplayComponent::PrepareSequenceForPlay(const AZStd::string& sequenceName, IUiAnimSequence*& sequence, IUiAnimationSystem*& animSystemOut)
  696. {
  697. IUiAnimationSystem* animSystem = nullptr;
  698. if (!sequence)
  699. {
  700. sequence = GetSequence(sequenceName, animSystem);
  701. if (sequence)
  702. {
  703. m_listeningForAnimationEvents = true;
  704. animSystem->AddUiAnimationListener(sequence, this);
  705. }
  706. }
  707. else
  708. {
  709. animSystem = GetAnimationSystem();
  710. if (!animSystem)
  711. {
  712. sequence = nullptr;
  713. }
  714. }
  715. if (sequence)
  716. {
  717. animSystemOut = animSystem;
  718. }
  719. }
  720. ////////////////////////////////////////////////////////////////////////////////////////////////////
  721. UiTooltipDisplayComponent::EntityComboBoxVec UiTooltipDisplayComponent::PopulateTextEntityList()
  722. {
  723. EntityComboBoxVec result;
  724. // add a first entry for "None"
  725. result.push_back(AZStd::make_pair(AZ::EntityId(AZ::EntityId()), "<None>"));
  726. // allow the destination to be the same entity as the source by
  727. // adding this entity (if it has a text component)
  728. if (UiTextBus::FindFirstHandler(GetEntityId()))
  729. {
  730. result.push_back(AZStd::make_pair(AZ::EntityId(GetEntityId()), GetEntity()->GetName()));
  731. }
  732. // Get a list of all descendant elements that support the UiTextBus
  733. LyShine::EntityArray matchingElements;
  734. UiElementBus::Event(
  735. GetEntityId(),
  736. &UiElementBus::Events::FindDescendantElements,
  737. [](const AZ::Entity* entity)
  738. {
  739. return UiTextBus::FindFirstHandler(entity->GetId()) != nullptr;
  740. },
  741. matchingElements);
  742. // add their names to the StringList and their IDs to the id list
  743. for (auto childEntity : matchingElements)
  744. {
  745. result.push_back(AZStd::make_pair(AZ::EntityId(childEntity->GetId()), childEntity->GetName()));
  746. }
  747. return result;
  748. }
  749. ////////////////////////////////////////////////////////////////////////////////////////////////////
  750. UiTooltipDisplayComponent::SequenceComboBoxVec UiTooltipDisplayComponent::PopulateSequenceList()
  751. {
  752. SequenceComboBoxVec result;
  753. // add a first entry for "None"
  754. result.push_back("<None>");
  755. IUiAnimationSystem* animSystem = GetAnimationSystem();
  756. if (animSystem)
  757. {
  758. for (int i = 0; i < animSystem->GetNumSequences(); i++)
  759. {
  760. IUiAnimSequence* sequence = animSystem->GetSequence(i);
  761. if (sequence)
  762. {
  763. result.push_back(sequence->GetName());
  764. }
  765. }
  766. }
  767. // Sort the sequence names
  768. SequenceComboBoxVec::iterator iterBegin = result.begin();
  769. ++iterBegin;
  770. if (iterBegin < result.end())
  771. {
  772. AZStd::sort(iterBegin, result.end(),
  773. [](const AZStd::string s1, const AZStd::string s2) { return s1 < s2; });
  774. }
  775. return result;
  776. }
  777. ////////////////////////////////////////////////////////////////////////////////////////////////////
  778. // PRIVATE STATIC MEMBER FUNCTIONS
  779. ////////////////////////////////////////////////////////////////////////////////////////////////////
  780. ////////////////////////////////////////////////////////////////////////////////////////////////////
  781. bool UiTooltipDisplayComponent::VersionConverter(AZ::SerializeContext& context,
  782. AZ::SerializeContext::DataElementNode& classElement)
  783. {
  784. // conversion from version 1:
  785. // - Need to convert Vec2 to AZ::Vector2
  786. if (classElement.GetVersion() <= 1)
  787. {
  788. if (!LyShine::ConvertSubElementFromVec2ToVector2(context, classElement, "Offset"))
  789. {
  790. return false;
  791. }
  792. }
  793. return true;
  794. }