3
0

UiSliderComponent.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900
  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 "UiSliderComponent.h"
  9. #include "Sprite.h"
  10. #include <AzCore/Component/TickBus.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/Serialization/EditContext.h>
  13. #include <AzCore/RTTI/BehaviorContext.h>
  14. #include <LyShine/Bus/UiCanvasBus.h>
  15. #include <LyShine/Bus/UiElementBus.h>
  16. #include <LyShine/Bus/UiTransform2dBus.h>
  17. #include <LyShine/Bus/UiTransformBus.h>
  18. #include <LyShine/Bus/UiVisualBus.h>
  19. #include <LyShine/ISprite.h>
  20. #include <LyShine/UiSerializeHelpers.h>
  21. #include "UiSerialize.h"
  22. #include "UiNavigationHelpers.h"
  23. ////////////////////////////////////////////////////////////////////////////////////////////////////
  24. //! UiSliderNotificationBus Behavior context handler class
  25. class UiSliderNotificationBusBehaviorHandler
  26. : public UiSliderNotificationBus::Handler
  27. , public AZ::BehaviorEBusHandler
  28. {
  29. public:
  30. AZ_EBUS_BEHAVIOR_BINDER(UiSliderNotificationBusBehaviorHandler, "{13540E5E-5987-4BD9-AC7A-F771F8AD0206}", AZ::SystemAllocator,
  31. OnSliderValueChanging, OnSliderValueChanged);
  32. void OnSliderValueChanging(float value) override
  33. {
  34. Call(FN_OnSliderValueChanging, value);
  35. }
  36. void OnSliderValueChanged(float value) override
  37. {
  38. Call(FN_OnSliderValueChanged, value);
  39. }
  40. };
  41. ////////////////////////////////////////////////////////////////////////////////////////////////////
  42. // PUBLIC MEMBER FUNCTIONS
  43. ////////////////////////////////////////////////////////////////////////////////////////////////////
  44. ////////////////////////////////////////////////////////////////////////////////////////////////////
  45. UiSliderComponent::UiSliderComponent()
  46. : m_value(0.0f)
  47. , m_minValue(0.0f)
  48. , m_maxValue(100.0f)
  49. , m_stepValue(0.0f)
  50. , m_isDragging(false)
  51. , m_isActive(false)
  52. , m_onValueChanged()
  53. , m_onValueChanging()
  54. , m_valueChangedActionName()
  55. , m_valueChangingActionName()
  56. , m_trackEntity()
  57. , m_fillEntity()
  58. , m_manipulatorEntity()
  59. {
  60. }
  61. ////////////////////////////////////////////////////////////////////////////////////////////////////
  62. UiSliderComponent::~UiSliderComponent()
  63. {
  64. }
  65. ////////////////////////////////////////////////////////////////////////////////////////////////////
  66. float UiSliderComponent::GetValue()
  67. {
  68. return m_value;
  69. }
  70. ////////////////////////////////////////////////////////////////////////////////////////////////////
  71. void UiSliderComponent::SetValue(float value)
  72. {
  73. if (m_minValue < m_maxValue)
  74. {
  75. m_value = AZ::GetClamp(value, m_minValue, m_maxValue);
  76. }
  77. else // ( m_minValue >= m_maxValue )
  78. {
  79. m_value = AZ::GetClamp(value, m_maxValue, m_minValue);
  80. }
  81. if (m_stepValue)
  82. {
  83. m_value += (m_stepValue / 2.0f); ///< Add half the step size to the value before the fmodf so that we round to nearest step rather than flooring to previous step
  84. m_value = m_value - fmodf(m_value, m_stepValue);
  85. }
  86. float valueRange = fabsf(m_maxValue - m_minValue);
  87. float unitValue = valueRange > 0.0f ? (fabsf(m_value - m_minValue) / valueRange) : 0.0f;
  88. if (m_fillEntity.IsValid())
  89. {
  90. // Offsets.
  91. {
  92. UiTransform2dInterface::Offsets offsets;
  93. UiTransform2dBus::EventResult(offsets, m_fillEntity, &UiTransform2dBus::Events::GetOffsets);
  94. offsets.m_left = offsets.m_right = 0.0f;
  95. UiTransform2dBus::Event(m_fillEntity, &UiTransform2dBus::Events::SetOffsets, offsets);
  96. }
  97. // Anchors.
  98. {
  99. UiTransform2dInterface::Anchors anchors;
  100. UiTransform2dBus::EventResult(anchors, m_fillEntity, &UiTransform2dBus::Events::GetAnchors);
  101. anchors.m_left = 0.0f;
  102. anchors.m_right = unitValue;
  103. UiTransform2dBus::Event(m_fillEntity, &UiTransform2dBus::Events::SetAnchors, anchors, false, true);
  104. }
  105. }
  106. if (m_manipulatorEntity.IsValid())
  107. {
  108. // Anchors.
  109. {
  110. UiTransform2dInterface::Anchors anchors;
  111. UiTransform2dBus::EventResult(anchors, m_manipulatorEntity, &UiTransform2dBus::Events::GetAnchors);
  112. anchors.m_left = anchors.m_right = unitValue;
  113. UiTransform2dBus::Event(m_manipulatorEntity, &UiTransform2dBus::Events::SetAnchors, anchors, false, true);
  114. }
  115. }
  116. }
  117. ////////////////////////////////////////////////////////////////////////////////////////////////////
  118. float UiSliderComponent::GetMinValue()
  119. {
  120. return m_minValue;
  121. }
  122. ////////////////////////////////////////////////////////////////////////////////////////////////////
  123. void UiSliderComponent::SetMinValue(float value)
  124. {
  125. m_minValue = value;
  126. }
  127. ////////////////////////////////////////////////////////////////////////////////////////////////////
  128. float UiSliderComponent::GetMaxValue()
  129. {
  130. return m_maxValue;
  131. }
  132. ////////////////////////////////////////////////////////////////////////////////////////////////////
  133. void UiSliderComponent::SetMaxValue(float value)
  134. {
  135. m_maxValue = value;
  136. }
  137. ////////////////////////////////////////////////////////////////////////////////////////////////////
  138. float UiSliderComponent::GetStepValue()
  139. {
  140. return m_stepValue;
  141. }
  142. ////////////////////////////////////////////////////////////////////////////////////////////////////
  143. void UiSliderComponent::SetStepValue(float step)
  144. {
  145. m_stepValue = step;
  146. }
  147. ////////////////////////////////////////////////////////////////////////////////////////////////////
  148. UiSliderComponent::ValueChangeCallback UiSliderComponent::GetValueChangingCallback()
  149. {
  150. return m_onValueChanging;
  151. }
  152. ////////////////////////////////////////////////////////////////////////////////////////////////////
  153. void UiSliderComponent::SetValueChangingCallback(ValueChangeCallback onChange)
  154. {
  155. m_onValueChanging = onChange;
  156. }
  157. ////////////////////////////////////////////////////////////////////////////////////////////////////
  158. const LyShine::ActionName& UiSliderComponent::GetValueChangingActionName()
  159. {
  160. return m_valueChangingActionName;
  161. }
  162. ////////////////////////////////////////////////////////////////////////////////////////////////////
  163. void UiSliderComponent::SetValueChangingActionName(const LyShine::ActionName& actionName)
  164. {
  165. m_valueChangingActionName = actionName;
  166. }
  167. ////////////////////////////////////////////////////////////////////////////////////////////////////
  168. UiSliderComponent::ValueChangeCallback UiSliderComponent::GetValueChangedCallback()
  169. {
  170. return m_onValueChanged;
  171. }
  172. ////////////////////////////////////////////////////////////////////////////////////////////////////
  173. void UiSliderComponent::SetValueChangedCallback(ValueChangeCallback onChange)
  174. {
  175. m_onValueChanged = onChange;
  176. }
  177. ////////////////////////////////////////////////////////////////////////////////////////////////////
  178. const LyShine::ActionName& UiSliderComponent::GetValueChangedActionName()
  179. {
  180. return m_valueChangedActionName;
  181. }
  182. ////////////////////////////////////////////////////////////////////////////////////////////////////
  183. void UiSliderComponent::SetValueChangedActionName(const LyShine::ActionName& actionName)
  184. {
  185. m_valueChangedActionName = actionName;
  186. }
  187. ////////////////////////////////////////////////////////////////////////////////////////////////////
  188. void UiSliderComponent::SetTrackEntity(AZ::EntityId entityId)
  189. {
  190. m_trackEntity = entityId;
  191. }
  192. ////////////////////////////////////////////////////////////////////////////////////////////////////
  193. AZ::EntityId UiSliderComponent::GetTrackEntity()
  194. {
  195. return m_trackEntity;
  196. }
  197. ////////////////////////////////////////////////////////////////////////////////////////////////////
  198. void UiSliderComponent::SetFillEntity(AZ::EntityId entityId)
  199. {
  200. m_fillEntity = entityId;
  201. }
  202. ////////////////////////////////////////////////////////////////////////////////////////////////////
  203. AZ::EntityId UiSliderComponent::GetFillEntity()
  204. {
  205. return m_fillEntity;
  206. }
  207. ////////////////////////////////////////////////////////////////////////////////////////////////////
  208. void UiSliderComponent::SetManipulatorEntity(AZ::EntityId entityId)
  209. {
  210. m_manipulatorEntity = entityId;
  211. }
  212. ////////////////////////////////////////////////////////////////////////////////////////////////////
  213. AZ::EntityId UiSliderComponent::GetManipulatorEntity()
  214. {
  215. return m_manipulatorEntity;
  216. }
  217. ////////////////////////////////////////////////////////////////////////////////////////////////////
  218. void UiSliderComponent::InGamePostActivate()
  219. {
  220. SetValue(m_value);
  221. }
  222. ////////////////////////////////////////////////////////////////////////////////////////////////////
  223. bool UiSliderComponent::HandlePressed(AZ::Vector2 point, bool& shouldStayActive)
  224. {
  225. bool handled = UiInteractableComponent::HandlePressed(point, shouldStayActive);
  226. if (handled)
  227. {
  228. m_isDragging = false;
  229. }
  230. return handled;
  231. }
  232. ////////////////////////////////////////////////////////////////////////////////////////////////////
  233. bool UiSliderComponent::HandleReleased(AZ::Vector2 point)
  234. {
  235. if (m_isPressed && m_isHandlingEvents)
  236. {
  237. float value = GetValueFromPoint(point);
  238. SetValue(value);
  239. UiInteractableComponent::TriggerReleasedAction();
  240. DoChangedActions();
  241. }
  242. m_isPressed = false;
  243. m_isDragging = false;
  244. m_pressedPoint = AZ::Vector2(0.0f, 0.0f);
  245. return m_isHandlingEvents;
  246. }
  247. /////////////////////////////////////////////////////////////////
  248. bool UiSliderComponent::HandleEnterPressed(bool& shouldStayActive)
  249. {
  250. bool handled = UiInteractableComponent::HandleEnterPressed(shouldStayActive);
  251. if (handled)
  252. {
  253. // the slider will stay active after released
  254. shouldStayActive = true;
  255. m_isActive = true;
  256. }
  257. return handled;
  258. }
  259. /////////////////////////////////////////////////////////////////
  260. bool UiSliderComponent::HandleAutoActivation()
  261. {
  262. if (!m_isHandlingEvents)
  263. {
  264. return false;
  265. }
  266. m_isActive = true;
  267. return true;
  268. }
  269. /////////////////////////////////////////////////////////////////
  270. bool UiSliderComponent::HandleKeyInputBegan(const AzFramework::InputChannel::Snapshot& inputSnapshot, AzFramework::ModifierKeyMask activeModifierKeys)
  271. {
  272. if (!m_isHandlingEvents)
  273. {
  274. return false;
  275. }
  276. // don't accept key input while in pressed state
  277. if (m_isPressed)
  278. {
  279. return false;
  280. }
  281. bool result = false;
  282. const UiNavigationHelpers::Command command = UiNavigationHelpers::MapInputChannelIdToUiNavigationCommand(inputSnapshot.m_channelId, activeModifierKeys);
  283. if (command == UiNavigationHelpers::Command::Up ||
  284. command == UiNavigationHelpers::Command::Down ||
  285. command == UiNavigationHelpers::Command::Left ||
  286. command == UiNavigationHelpers::Command::Right)
  287. {
  288. const float keySteps = 10.0f;
  289. float delta = (m_stepValue != 0.0f) ? m_stepValue : (m_maxValue - m_minValue) / keySteps;
  290. UiTransformInterface::RectPoints points;
  291. UiTransformBus::Event(GetEntityId(), &UiTransformBus::Events::GetViewportSpacePoints, points);
  292. AZ::Vector2 dir = points.TopRight() - points.TopLeft();
  293. bool isHorizontal = fabs(dir.GetX()) >= fabs(dir.GetY());
  294. bool isVertical = fabs(dir.GetX()) <= fabs(dir.GetY());
  295. float newValue = m_value;
  296. if (isHorizontal && (command == UiNavigationHelpers::Command::Left || command == UiNavigationHelpers::Command::Right))
  297. {
  298. newValue += (command == UiNavigationHelpers::Command::Left ? -delta : delta);
  299. result = true;
  300. }
  301. else if (isVertical && (command == UiNavigationHelpers::Command::Up || command == UiNavigationHelpers::Command::Down))
  302. {
  303. newValue += (command == UiNavigationHelpers::Command::Down ? -delta : delta);
  304. result = true;
  305. }
  306. if (m_minValue < m_maxValue)
  307. {
  308. newValue = AZ::GetClamp(newValue, m_minValue, m_maxValue);
  309. }
  310. else // ( m_minValue >= m_maxValue )
  311. {
  312. newValue = AZ::GetClamp(newValue, m_maxValue, m_minValue);
  313. }
  314. if (newValue != m_value)
  315. {
  316. SetValue(newValue);
  317. AZ::Vector2 point(-1.0f, 1.0f);
  318. DoChangingActions();
  319. DoChangedActions();
  320. }
  321. }
  322. return result;
  323. }
  324. /////////////////////////////////////////////////////////////////
  325. void UiSliderComponent::InputPositionUpdate(AZ::Vector2 point)
  326. {
  327. if (m_isPressed && m_trackEntity.IsValid())
  328. {
  329. // if we are not yet in the dragging state do some tests to see if we should be
  330. if (!m_isDragging)
  331. {
  332. bool handOffDone = false;
  333. bool dragDetected = CheckForDragOrHandOffToParent(GetEntityId(), m_pressedPoint, point, 0.0f, handOffDone);
  334. if (dragDetected)
  335. {
  336. if (handOffDone)
  337. {
  338. // the drag was handed off to a parent, this slider is no longer active
  339. m_isPressed = false;
  340. }
  341. else
  342. {
  343. // the drag was valid for this slider, we are now dragging
  344. m_isDragging = true;
  345. }
  346. }
  347. }
  348. // if we are now in the dragging state do the drag of the slider
  349. if (m_isDragging)
  350. {
  351. float value = GetValueFromPoint(point);
  352. SetValue(value);
  353. DoChangingActions();
  354. }
  355. }
  356. }
  357. /////////////////////////////////////////////////////////////////
  358. bool UiSliderComponent::DoesSupportDragHandOff(AZ::Vector2 startPoint)
  359. {
  360. // this component does support hand-off, so long as the start point is in its bounds
  361. bool isPointInRect = false;
  362. UiTransformBus::EventResult(isPointInRect, GetEntityId(), &UiTransformBus::Events::IsPointInRect, startPoint);
  363. return isPointInRect;
  364. }
  365. /////////////////////////////////////////////////////////////////
  366. bool UiSliderComponent::OfferDragHandOff(AZ::EntityId currentActiveInteractable, AZ::Vector2 startPoint, AZ::Vector2 currentPoint, float dragThreshold)
  367. {
  368. bool handedOffToParent = false;
  369. bool dragDetected = CheckForDragOrHandOffToParent(currentActiveInteractable, startPoint, currentPoint, dragThreshold, handedOffToParent);
  370. if (dragDetected)
  371. {
  372. if (!handedOffToParent)
  373. {
  374. // a drag was detected and it was not handed off to a parent, so this slider is now taking the handoff
  375. m_isPressed = true;
  376. m_pressedPoint = startPoint;
  377. m_isDragging = true;
  378. // tell the canvas that this is now the active interactable
  379. UiInteractableActiveNotificationBus::Event(
  380. currentActiveInteractable, &UiInteractableActiveNotificationBus::Events::ActiveChanged, GetEntityId(), false);
  381. }
  382. }
  383. return dragDetected;
  384. }
  385. ////////////////////////////////////////////////////////////////////////////////////////////////////
  386. void UiSliderComponent::LostActiveStatus()
  387. {
  388. UiInteractableComponent::LostActiveStatus();
  389. if (m_isDragging)
  390. {
  391. if (m_isHandlingEvents)
  392. {
  393. DoChangedActions();
  394. }
  395. m_isDragging = false;
  396. }
  397. m_isActive = false;
  398. }
  399. ////////////////////////////////////////////////////////////////////////////////////////////////////
  400. // PROTECTED MEMBER FUNCTIONS
  401. ////////////////////////////////////////////////////////////////////////////////////////////////////
  402. ////////////////////////////////////////////////////////////////////////////////////////////////////
  403. void UiSliderComponent::Activate()
  404. {
  405. UiInteractableComponent::Activate();
  406. UiSliderBus::Handler::BusConnect(GetEntityId());
  407. UiInitializationBus::Handler::BusConnect(GetEntityId());
  408. }
  409. ////////////////////////////////////////////////////////////////////////////////////////////////////
  410. void UiSliderComponent::Deactivate()
  411. {
  412. UiInteractableComponent::Deactivate();
  413. UiSliderBus::Handler::BusDisconnect(GetEntityId());
  414. UiInitializationBus::Handler::BusDisconnect(GetEntityId());
  415. }
  416. ////////////////////////////////////////////////////////////////////////////////////////////////////
  417. bool UiSliderComponent::IsAutoActivationSupported()
  418. {
  419. return true;
  420. }
  421. ////////////////////////////////////////////////////////////////////////////////////////////////////
  422. UiInteractableStatesInterface::State UiSliderComponent::ComputeInteractableState()
  423. {
  424. UiInteractableStatesInterface::State state = UiInteractableStatesInterface::StateNormal;
  425. if (!m_isHandlingEvents)
  426. {
  427. state = UiInteractableStatesInterface::StateDisabled;
  428. }
  429. else if (m_isPressed || m_isActive)
  430. {
  431. // Use pressed state regardless of mouse position
  432. state = UiInteractableStatesInterface::StatePressed;
  433. }
  434. else if (m_isHover)
  435. {
  436. state = UiInteractableStatesInterface::StateHover;
  437. }
  438. return state;
  439. }
  440. ////////////////////////////////////////////////////////////////////////////////////////////////////
  441. // PROTECTED STATIC MEMBER FUNCTIONS
  442. ////////////////////////////////////////////////////////////////////////////////////////////////////
  443. ////////////////////////////////////////////////////////////////////////////////////////////////////
  444. void UiSliderComponent::Reflect(AZ::ReflectContext* context)
  445. {
  446. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  447. if (serializeContext)
  448. {
  449. serializeContext->Class<UiSliderComponent, UiInteractableComponent>()
  450. ->Version(3, &VersionConverter)
  451. // Elements group
  452. ->Field("TrackEntity", &UiSliderComponent::m_trackEntity)
  453. ->Field("FillEntity", &UiSliderComponent::m_fillEntity)
  454. ->Field("ManipulatorEntity", &UiSliderComponent::m_manipulatorEntity)
  455. // Value group
  456. ->Field("Value", &UiSliderComponent::m_value)
  457. ->Field("MinValue", &UiSliderComponent::m_minValue)
  458. ->Field("MaxValue", &UiSliderComponent::m_maxValue)
  459. ->Field("StepValue", &UiSliderComponent::m_stepValue)
  460. // Actions group
  461. ->Field("ValueChangingActionName", &UiSliderComponent::m_valueChangingActionName)
  462. ->Field("ValueChangedActionName", &UiSliderComponent::m_valueChangedActionName);
  463. AZ::EditContext* ec = serializeContext->GetEditContext();
  464. if (ec)
  465. {
  466. auto editInfo = ec->Class<UiSliderComponent>("Slider", "An interactable component for modifying a floating point value with a slider.");
  467. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  468. ->Attribute(AZ::Edit::Attributes::Category, "UI")
  469. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiSlider.png")
  470. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiSlider.png")
  471. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0))
  472. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  473. // Elements group
  474. {
  475. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Elements")
  476. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  477. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiSliderComponent::m_trackEntity, "Track", "The child element used to define the range of movement.")
  478. ->Attribute(AZ::Edit::Attributes::EnumValues, &UiSliderComponent::PopulateChildEntityList);
  479. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiSliderComponent::m_fillEntity, "Fill", "The child element used to show the filled part of the range.")
  480. ->Attribute(AZ::Edit::Attributes::EnumValues, &UiSliderComponent::PopulateChildEntityList);
  481. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiSliderComponent::m_manipulatorEntity, "Manipulator", "The child element used as a handle.")
  482. ->Attribute(AZ::Edit::Attributes::EnumValues, &UiSliderComponent::PopulateChildEntityList);
  483. }
  484. // Value group
  485. {
  486. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Value")
  487. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  488. editInfo->DataElement(0, &UiSliderComponent::m_value, "Value", "The initial value of the slider.");
  489. editInfo->DataElement(0, &UiSliderComponent::m_minValue, "Min", "The minimum slider value.");
  490. editInfo->DataElement(0, &UiSliderComponent::m_maxValue, "Max", "The maximum slider value.");
  491. editInfo->DataElement(0, &UiSliderComponent::m_stepValue, "Stepping", "The smallest increment allowed between values. Use zero for no restriction.");
  492. }
  493. // Actions group
  494. {
  495. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Actions")
  496. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  497. editInfo->DataElement(0, &UiSliderComponent::m_valueChangingActionName, "Change", "The action triggered while the value is changing.");
  498. editInfo->DataElement(0, &UiSliderComponent::m_valueChangedActionName, "End change", "The action triggered when the value is done changing.");
  499. }
  500. }
  501. }
  502. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  503. if (behaviorContext)
  504. {
  505. behaviorContext->EBus<UiSliderBus>("UiSliderBus")
  506. ->Event("GetValue", &UiSliderBus::Events::GetValue)
  507. ->Event("SetValue", &UiSliderBus::Events::SetValue)
  508. ->Event("GetMinValue", &UiSliderBus::Events::GetMinValue)
  509. ->Event("SetMinValue", &UiSliderBus::Events::SetMinValue)
  510. ->Event("GetMaxValue", &UiSliderBus::Events::GetMaxValue)
  511. ->Event("SetMaxValue", &UiSliderBus::Events::SetMaxValue)
  512. ->Event("GetStepValue", &UiSliderBus::Events::GetStepValue)
  513. ->Event("SetStepValue", &UiSliderBus::Events::SetStepValue)
  514. ->Event("GetTrackEntity", &UiSliderBus::Events::GetTrackEntity)
  515. ->Event("SetTrackEntity", &UiSliderBus::Events::SetTrackEntity)
  516. ->Event("GetFillEntity", &UiSliderBus::Events::GetFillEntity)
  517. ->Event("SetFillEntity", &UiSliderBus::Events::SetFillEntity)
  518. ->Event("GetManipulatorEntity", &UiSliderBus::Events::GetManipulatorEntity)
  519. ->Event("SetManipulatorEntity", &UiSliderBus::Events::SetManipulatorEntity)
  520. ->Event("GetValueChangingActionName", &UiSliderBus::Events::GetValueChangingActionName)
  521. ->Event("SetValueChangingActionName", &UiSliderBus::Events::SetValueChangingActionName)
  522. ->Event("GetValueChangedActionName", &UiSliderBus::Events::GetValueChangedActionName)
  523. ->Event("SetValueChangedActionName", &UiSliderBus::Events::SetValueChangedActionName)
  524. ->VirtualProperty("Value", "GetValue", "SetValue")
  525. ->VirtualProperty("MinValue", "GetMinValue", "SetMinValue")
  526. ->VirtualProperty("MaxValue", "GetMaxValue", "SetMaxValue")
  527. ->VirtualProperty("StepValue", "GetStepValue", "SetStepValue");
  528. behaviorContext->Class<UiSliderComponent>()->RequestBus("UiSliderBus");
  529. behaviorContext->EBus<UiSliderNotificationBus>("UiSliderNotificationBus")
  530. ->Handler<UiSliderNotificationBusBehaviorHandler>();
  531. }
  532. }
  533. ////////////////////////////////////////////////////////////////////////////////////////////////////
  534. // PRIVATE MEMBER FUNCTIONS
  535. ////////////////////////////////////////////////////////////////////////////////////////////////////
  536. ////////////////////////////////////////////////////////////////////////////////////////////////////
  537. UiSliderComponent::EntityComboBoxVec UiSliderComponent::PopulateChildEntityList()
  538. {
  539. EntityComboBoxVec result;
  540. // add a first entry for "None"
  541. result.push_back(AZStd::make_pair(AZ::EntityId(AZ::EntityId()), "<None>"));
  542. // Get a list of all child elements
  543. LyShine::EntityArray matchingElements;
  544. UiElementBus::Event(
  545. GetEntityId(),
  546. &UiElementBus::Events::FindDescendantElements,
  547. []([[maybe_unused]] const AZ::Entity* entity)
  548. {
  549. return true;
  550. },
  551. matchingElements);
  552. // add their names to the StringList and their IDs to the id list
  553. for (auto childEntity : matchingElements)
  554. {
  555. result.push_back(AZStd::make_pair(AZ::EntityId(childEntity->GetId()), childEntity->GetName()));
  556. }
  557. return result;
  558. }
  559. ////////////////////////////////////////////////////////////////////////////////////////////////////
  560. float UiSliderComponent::GetValueFromPoint(AZ::Vector2 point)
  561. {
  562. // Get m_value from point.
  563. float value = 0.0f;
  564. UiTransformInterface::RectPoints points;
  565. UiTransformBus::Event(m_trackEntity, &UiTransformBus::Events::GetCanvasSpacePointsNoScaleRotate, points);
  566. // apply scale and rotation to points
  567. UiTransformBus::Event(m_trackEntity, &UiTransformBus::Events::RotateAndScalePoints, points);
  568. const AZ::Vector2& tl = points.TopLeft();
  569. const AZ::Vector2& tr = points.TopRight();
  570. AZ::Vector2 A = tr - tl;
  571. float magA = A.GetLength();
  572. AZ::Vector2 normA = A.GetNormalized();
  573. AZ::Vector2 B = point - tl;
  574. float magBalongA = B.Dot(normA);
  575. float range = fabsf(m_maxValue - m_minValue);
  576. float unitValue = (magBalongA / magA);
  577. if (m_minValue < m_maxValue)
  578. {
  579. value = m_minValue + (range * unitValue);
  580. }
  581. else // ( m_minValue >= m_maxValue )
  582. {
  583. value = m_minValue - (range * unitValue);
  584. }
  585. return value;
  586. }
  587. ////////////////////////////////////////////////////////////////////////////////////////////////////
  588. // calculate how much we have dragged along the axis of the slider
  589. float UiSliderComponent::GetValidDragDistanceInPixels(AZ::Vector2 startPoint, AZ::Vector2 endPoint)
  590. {
  591. const float validDragRatio = 0.5f;
  592. // convert the drag vector to local space
  593. AZ::Matrix4x4 transformFromViewport;
  594. UiTransformBus::Event(m_trackEntity, &UiTransformBus::Events::GetTransformFromViewport, transformFromViewport);
  595. AZ::Vector2 dragVec = endPoint - startPoint;
  596. AZ::Vector3 dragVec3(dragVec.GetX(), dragVec.GetY(), 0.0f);
  597. AZ::Vector3 localDragVec = transformFromViewport.Multiply3x3(dragVec3);
  598. // the slider component only supports drag along the x axis so zero the y axis
  599. localDragVec.SetY(0.0f);
  600. // convert back to viewport space
  601. AZ::Matrix4x4 transformToViewport;
  602. UiTransformBus::Event(m_trackEntity, &UiTransformBus::Events::GetTransformToViewport, transformToViewport);
  603. AZ::Vector3 validDragVec = transformToViewport.Multiply3x3(localDragVec);
  604. float validDistance = validDragVec.GetLengthSq();
  605. float totalDistance = dragVec.GetLengthSq();
  606. // if they are not dragging mostly in a valid direction then ignore the drag
  607. if (validDistance / totalDistance < validDragRatio)
  608. {
  609. validDistance = 0.0f;
  610. }
  611. // return the valid drag distance
  612. return validDistance;
  613. }
  614. ////////////////////////////////////////////////////////////////////////////////////////////////////
  615. bool UiSliderComponent::CheckForDragOrHandOffToParent(AZ::EntityId currentActiveInteractable, AZ::Vector2 startPoint, AZ::Vector2 currentPoint, float childDragThreshold, bool& handOffDone)
  616. {
  617. bool result = false;
  618. AZ::EntityId parentDraggable;
  619. UiElementBus::EventResult(parentDraggable, GetEntityId(), &UiElementBus::Events::FindParentInteractableSupportingDrag, startPoint);
  620. // if this interactable is inside another interactable that supports drag then we use
  621. // a threshold value before starting a drag on this interactable
  622. const float normalDragThreshold = 0.0f;
  623. const float containedDragThreshold = 5.0f;
  624. float dragThreshold = normalDragThreshold;
  625. if (childDragThreshold > 0.0f)
  626. {
  627. dragThreshold = childDragThreshold;
  628. }
  629. else if (parentDraggable.IsValid())
  630. {
  631. dragThreshold = containedDragThreshold;
  632. }
  633. // calculate how much we have dragged along the axis of the slider
  634. float validDragDistance = GetValidDragDistanceInPixels(startPoint, currentPoint);
  635. if (validDragDistance > dragThreshold)
  636. {
  637. // we dragged above the threshold value along axis of slider
  638. result = true;
  639. }
  640. else if (parentDraggable.IsValid())
  641. {
  642. // offer the parent draggable the chance to become the active interactable
  643. UiInteractableBus::EventResult(
  644. handOffDone,
  645. parentDraggable,
  646. &UiInteractableBus::Events::OfferDragHandOff,
  647. currentActiveInteractable,
  648. startPoint,
  649. currentPoint,
  650. containedDragThreshold);
  651. if (handOffDone)
  652. {
  653. // interaction has been handed off to a container entity
  654. result = true;
  655. }
  656. }
  657. return result;
  658. }
  659. ////////////////////////////////////////////////////////////////////////////////////////////////////
  660. void UiSliderComponent::DoChangedActions()
  661. {
  662. if (m_onValueChanged)
  663. {
  664. m_onValueChanged(GetEntityId(), m_value);
  665. }
  666. // Tell any action listeners about the event
  667. if (!m_valueChangedActionName.empty())
  668. {
  669. AZ::EntityId canvasEntityId;
  670. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  671. UiCanvasNotificationBus::Event(canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_valueChangedActionName);
  672. }
  673. UiSliderNotificationBus::Event(GetEntityId(), &UiSliderNotificationBus::Events::OnSliderValueChanged, m_value);
  674. }
  675. ////////////////////////////////////////////////////////////////////////////////////////////////////
  676. void UiSliderComponent::DoChangingActions()
  677. {
  678. if (m_onValueChanging)
  679. {
  680. m_onValueChanging(GetEntityId(), m_value);
  681. }
  682. // Tell any action listeners about the event
  683. if (!m_valueChangingActionName.empty())
  684. {
  685. AZ::EntityId canvasEntityId;
  686. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  687. UiCanvasNotificationBus::Event(canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_valueChangingActionName);
  688. }
  689. UiSliderNotificationBus::Event(GetEntityId(), &UiSliderNotificationBus::Events::OnSliderValueChanging, m_value);
  690. }
  691. ////////////////////////////////////////////////////////////////////////////////////////////////////
  692. // PRIVATE STATIC MEMBER FUNCTIONS
  693. ////////////////////////////////////////////////////////////////////////////////////////////////////
  694. ////////////////////////////////////////////////////////////////////////////////////////////////////
  695. bool UiSliderComponent::VersionConverter(AZ::SerializeContext& context,
  696. AZ::SerializeContext::DataElementNode& classElement)
  697. {
  698. // conversion from version 1 to 2:
  699. // - Need to convert AZStd::string sprites to AzFramework::SimpleAssetReference<LmbrCentral::TextureAsset>
  700. if (classElement.GetVersion() < 2)
  701. {
  702. if (!LyShine::ConvertSubElementFromAzStringToAssetRef<LmbrCentral::TextureAsset>(context, classElement, "SelectedSprite"))
  703. {
  704. return false;
  705. }
  706. if (!LyShine::ConvertSubElementFromAzStringToAssetRef<LmbrCentral::TextureAsset>(context, classElement, "DisabledSprite"))
  707. {
  708. return false;
  709. }
  710. }
  711. // Conversion from version 2 to 3:
  712. if (classElement.GetVersion() < 3)
  713. {
  714. // find the base class (AZ::Component)
  715. // NOTE: in very old versions there may not be a base class because the base class was not serialized
  716. int componentBaseClassIndex = classElement.FindElement(AZ_CRC("BaseClass1", 0xd4925735));
  717. // If there was a base class, make a copy and remove it
  718. AZ::SerializeContext::DataElementNode componentBaseClassNode;
  719. if (componentBaseClassIndex != -1)
  720. {
  721. // make a local copy of the component base class node
  722. componentBaseClassNode = classElement.GetSubElement(componentBaseClassIndex);
  723. // remove the component base class from the button
  724. classElement.RemoveElement(componentBaseClassIndex);
  725. }
  726. // Add a new base class (UiInteractableComponent)
  727. int interactableBaseClassIndex = classElement.AddElement<UiInteractableComponent>(context, "BaseClass1");
  728. AZ::SerializeContext::DataElementNode& interactableBaseClassNode = classElement.GetSubElement(interactableBaseClassIndex);
  729. // if there was previously a base class...
  730. if (componentBaseClassIndex != -1)
  731. {
  732. // copy the component base class into the new interactable base class
  733. // Since AZ::Component is now the base class of UiInteractableComponent
  734. interactableBaseClassNode.AddElement(componentBaseClassNode);
  735. }
  736. // Move the selected/hover state to the base class
  737. if (!UiSerialize::MoveToInteractableStateActions(context, classElement, "HoverStateActions",
  738. "SelectedColor", "SelectedAlpha", "SelectedSprite"))
  739. {
  740. return false;
  741. }
  742. // Move the disabled state to the base class
  743. if (!UiSerialize::MoveToInteractableStateActions(context, classElement, "DisabledStateActions",
  744. "DisabledColor", "DisabledAlpha", "DisabledSprite"))
  745. {
  746. return false;
  747. }
  748. }
  749. return true;
  750. }