UiRadioButtonComponent.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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 "UiRadioButtonComponent.h"
  9. #include "UiRadioButtonGroupComponent.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/Bus/UiCanvasBus.h>
  15. #include <LyShine/Bus/UiElementBus.h>
  16. #include <LyShine/Bus/UiTransformBus.h>
  17. #include <LyShine/Bus/UiRadioButtonGroupBus.h>
  18. #include <LyShine/Bus/UiRadioButtonGroupCommunicationBus.h>
  19. #include <LyShine/UiSerializeHelpers.h>
  20. ////////////////////////////////////////////////////////////////////////////////////////////////////
  21. //! UiRadioButtonNotificationBus Behavior context handler class
  22. class UiRadioButtonNotificationBusBehaviorHandler
  23. : public UiRadioButtonNotificationBus::Handler
  24. , public AZ::BehaviorEBusHandler
  25. {
  26. public:
  27. AZ_EBUS_BEHAVIOR_BINDER(UiRadioButtonNotificationBusBehaviorHandler, "{182D0EB2-DAD6-4CFC-98E9-185863A78637}", AZ::SystemAllocator,
  28. OnRadioButtonStateChange);
  29. void OnRadioButtonStateChange(bool checked) override
  30. {
  31. Call(FN_OnRadioButtonStateChange, checked);
  32. }
  33. };
  34. ////////////////////////////////////////////////////////////////////////////////////////////////////
  35. // PUBLIC MEMBER FUNCTIONS
  36. ////////////////////////////////////////////////////////////////////////////////////////////////////
  37. ////////////////////////////////////////////////////////////////////////////////////////////////////
  38. UiRadioButtonComponent::UiRadioButtonComponent()
  39. : m_isOn(false)
  40. {
  41. }
  42. ////////////////////////////////////////////////////////////////////////////////////////////////////
  43. UiRadioButtonComponent::~UiRadioButtonComponent()
  44. {
  45. }
  46. ////////////////////////////////////////////////////////////////////////////////////////////////////
  47. bool UiRadioButtonComponent::GetState()
  48. {
  49. return m_isOn;
  50. }
  51. ////////////////////////////////////////////////////////////////////////////////////////////////////
  52. AZ::EntityId UiRadioButtonComponent::GetGroup()
  53. {
  54. return m_group;
  55. }
  56. ////////////////////////////////////////////////////////////////////////////////////////////////////
  57. void UiRadioButtonComponent::SetCheckedEntity(AZ::EntityId entityId)
  58. {
  59. m_optionalCheckedEntity = entityId;
  60. }
  61. ////////////////////////////////////////////////////////////////////////////////////////////////////
  62. AZ::EntityId UiRadioButtonComponent::GetCheckedEntity()
  63. {
  64. return m_optionalCheckedEntity;
  65. }
  66. ////////////////////////////////////////////////////////////////////////////////////////////////////
  67. void UiRadioButtonComponent::SetUncheckedEntity(AZ::EntityId entityId)
  68. {
  69. m_optionalUncheckedEntity = entityId;
  70. }
  71. ////////////////////////////////////////////////////////////////////////////////////////////////////
  72. AZ::EntityId UiRadioButtonComponent::GetUncheckedEntity()
  73. {
  74. return m_optionalUncheckedEntity;
  75. }
  76. ////////////////////////////////////////////////////////////////////////////////////////////////////
  77. const LyShine::ActionName& UiRadioButtonComponent::GetTurnOnActionName()
  78. {
  79. return m_turnOnActionName;
  80. }
  81. ////////////////////////////////////////////////////////////////////////////////////////////////////
  82. void UiRadioButtonComponent::SetTurnOnActionName(const LyShine::ActionName& actionName)
  83. {
  84. m_turnOnActionName = actionName;
  85. }
  86. ////////////////////////////////////////////////////////////////////////////////////////////////////
  87. const LyShine::ActionName& UiRadioButtonComponent::GetTurnOffActionName()
  88. {
  89. return m_turnOffActionName;
  90. }
  91. ////////////////////////////////////////////////////////////////////////////////////////////////////
  92. void UiRadioButtonComponent::SetTurnOffActionName(const LyShine::ActionName& actionName)
  93. {
  94. m_turnOffActionName = actionName;
  95. }
  96. ////////////////////////////////////////////////////////////////////////////////////////////////////
  97. const LyShine::ActionName& UiRadioButtonComponent::GetChangedActionName()
  98. {
  99. return m_changedActionName;
  100. }
  101. ////////////////////////////////////////////////////////////////////////////////////////////////////
  102. void UiRadioButtonComponent::SetChangedActionName(const LyShine::ActionName& actionName)
  103. {
  104. m_changedActionName = actionName;
  105. }
  106. ////////////////////////////////////////////////////////////////////////////////////////////////////
  107. void UiRadioButtonComponent::SetState(bool isOn, bool sendNotifications)
  108. {
  109. bool oldState = m_isOn;
  110. m_isOn = isOn;
  111. // if we changed state, send events and set the correct entity to display
  112. if (m_isOn != oldState)
  113. {
  114. if (m_optionalCheckedEntity.IsValid())
  115. {
  116. UiElementBus::Event(m_optionalCheckedEntity, &UiElementBus::Events::SetIsEnabled, m_isOn);
  117. }
  118. if (m_optionalUncheckedEntity.IsValid())
  119. {
  120. UiElementBus::Event(m_optionalUncheckedEntity, &UiElementBus::Events::SetIsEnabled, !m_isOn);
  121. }
  122. if (sendNotifications)
  123. {
  124. // Tell any action listeners about the event
  125. if (m_isOn && !m_turnOnActionName.empty())
  126. {
  127. AZ::EntityId canvasEntityId;
  128. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  129. UiCanvasNotificationBus::Event(
  130. canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_turnOnActionName);
  131. }
  132. if (!m_isOn && !m_turnOffActionName.empty())
  133. {
  134. AZ::EntityId canvasEntityId;
  135. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  136. UiCanvasNotificationBus::Event(
  137. canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_turnOffActionName);
  138. }
  139. if (!m_changedActionName.empty())
  140. {
  141. AZ::EntityId canvasEntityId;
  142. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  143. UiCanvasNotificationBus::Event(
  144. canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_changedActionName);
  145. }
  146. UiRadioButtonNotificationBus::Event(GetEntityId(), &UiRadioButtonNotificationBus::Events::OnRadioButtonStateChange, m_isOn);
  147. }
  148. }
  149. }
  150. ////////////////////////////////////////////////////////////////////////////////////////////////////
  151. void UiRadioButtonComponent::SetGroup(AZ::EntityId group)
  152. {
  153. m_group = group;
  154. }
  155. ////////////////////////////////////////////////////////////////////////////////////////////////////
  156. void UiRadioButtonComponent::InGamePostActivate()
  157. {
  158. // Add this radio button to its group
  159. UiRadioButtonGroupCommunicationBus::Event(m_group, &UiRadioButtonGroupCommunicationBus::Events::RegisterRadioButton, GetEntityId());
  160. // Request to be set to its default state
  161. // If the default state is on
  162. if (m_isOn)
  163. {
  164. // Let the group know about it
  165. UiRadioButtonGroupBus::Event(m_group, &UiRadioButtonGroupBus::Events::SetState, GetEntityId(), true);
  166. }
  167. // Else if the default state is off
  168. else
  169. {
  170. // We need to make sure the on/off entities are displaying correctly, no need to go through group
  171. if (m_optionalCheckedEntity.IsValid())
  172. {
  173. UiElementBus::Event(m_optionalCheckedEntity, &UiElementBus::Events::SetIsEnabled, false);
  174. }
  175. if (m_optionalUncheckedEntity.IsValid())
  176. {
  177. UiElementBus::Event(m_optionalUncheckedEntity, &UiElementBus::Events::SetIsEnabled, true);
  178. }
  179. }
  180. }
  181. ////////////////////////////////////////////////////////////////////////////////////////////////////
  182. bool UiRadioButtonComponent::HandleReleased(AZ::Vector2 point)
  183. {
  184. bool isInRect = false;
  185. UiTransformBus::EventResult(isInRect, GetEntityId(), &UiTransformBus::Events::IsPointInRect, point);
  186. if (isInRect)
  187. {
  188. return HandleReleasedCommon(point);
  189. }
  190. else
  191. {
  192. m_isPressed = false;
  193. return m_isHandlingEvents;
  194. }
  195. }
  196. ////////////////////////////////////////////////////////////////////////////////////////////////////
  197. bool UiRadioButtonComponent::HandleEnterReleased()
  198. {
  199. AZ::Vector2 point(-1.0f, -1.0f);
  200. return HandleReleasedCommon(point);
  201. }
  202. ////////////////////////////////////////////////////////////////////////////////////////////////////
  203. // PROTECTED MEMBER FUNCTIONS
  204. ////////////////////////////////////////////////////////////////////////////////////////////////////
  205. ////////////////////////////////////////////////////////////////////////////////////////////////////
  206. void UiRadioButtonComponent::Activate()
  207. {
  208. UiInteractableComponent::Activate();
  209. UiRadioButtonBus::Handler::BusConnect(GetEntityId());
  210. UiRadioButtonCommunicationBus::Handler::BusConnect(GetEntityId());
  211. UiInitializationBus::Handler::BusConnect(GetEntityId());
  212. }
  213. ////////////////////////////////////////////////////////////////////////////////////////////////////
  214. void UiRadioButtonComponent::Deactivate()
  215. {
  216. UiRadioButtonGroupCommunicationBus::Event(m_group, &UiRadioButtonGroupCommunicationBus::Events::UnregisterRadioButton, GetEntityId());
  217. UiInteractableComponent::Deactivate();
  218. UiRadioButtonBus::Handler::BusDisconnect(GetEntityId());
  219. UiRadioButtonCommunicationBus::Handler::BusDisconnect(GetEntityId());
  220. UiInitializationBus::Handler::BusDisconnect(GetEntityId());
  221. }
  222. ////////////////////////////////////////////////////////////////////////////////////////////////////
  223. // PROTECTED STATIC MEMBER FUNCTIONS
  224. ////////////////////////////////////////////////////////////////////////////////////////////////////
  225. void UiRadioButtonComponent::Reflect(AZ::ReflectContext* context)
  226. {
  227. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  228. if (serializeContext)
  229. {
  230. serializeContext->Class<UiRadioButtonComponent, UiInteractableComponent>()
  231. ->Version(1)
  232. // Elements group
  233. ->Field("OptionalCheckedEntity", &UiRadioButtonComponent::m_optionalCheckedEntity)
  234. ->Field("OptionalUncheckedEntity", &UiRadioButtonComponent::m_optionalUncheckedEntity)
  235. ->Field("Group", &UiRadioButtonComponent::m_group)
  236. // Value group
  237. ->Field("IsChecked", &UiRadioButtonComponent::m_isOn)
  238. // Actions group
  239. ->Field("ChangedActionName", &UiRadioButtonComponent::m_changedActionName)
  240. ->Field("TurnOnActionName", &UiRadioButtonComponent::m_turnOnActionName)
  241. ->Field("TurnOffActionName", &UiRadioButtonComponent::m_turnOffActionName);
  242. AZ::EditContext* ec = serializeContext->GetEditContext();
  243. if (ec)
  244. {
  245. auto editInfo = ec->Class<UiRadioButtonComponent>("RadioButton", "An interactable component for RadioButton behavior.");
  246. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  247. ->Attribute(AZ::Edit::Attributes::Category, "UI")
  248. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiRadioButton.png")
  249. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiRadioButton.png")
  250. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0))
  251. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  252. // Elements group
  253. {
  254. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Elements")
  255. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  256. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiRadioButtonComponent::m_optionalCheckedEntity, "On", "The child element to show when RadioButton is in on state.")
  257. ->Attribute(AZ::Edit::Attributes::EnumValues, &UiRadioButtonComponent::PopulateChildEntityList);
  258. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiRadioButtonComponent::m_optionalUncheckedEntity, "Off", "The child element to show when RadioButton is in off state.")
  259. ->Attribute(AZ::Edit::Attributes::EnumValues, &UiRadioButtonComponent::PopulateChildEntityList);
  260. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiRadioButtonComponent::m_group, "Group", "The group this radio button belongs to.")
  261. ->Attribute(AZ::Edit::Attributes::EnumValues, &UiRadioButtonComponent::PopulateGroupsEntityList);
  262. }
  263. // Value group
  264. {
  265. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Value")
  266. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  267. editInfo->DataElement(0, &UiRadioButtonComponent::m_isOn, "Checked", "The initial state of the radio button.");
  268. }
  269. // Actions group
  270. {
  271. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Actions")
  272. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  273. editInfo->DataElement(0, &UiRadioButtonComponent::m_changedActionName, "Change", "The action triggered when value changes either way.");
  274. editInfo->DataElement(0, &UiRadioButtonComponent::m_turnOnActionName, "On", "The action triggered when turned on.");
  275. editInfo->DataElement(0, &UiRadioButtonComponent::m_turnOffActionName, "Off", "The action triggered when turned off.");
  276. }
  277. }
  278. }
  279. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  280. if (behaviorContext)
  281. {
  282. behaviorContext->EBus<UiRadioButtonBus>("UiRadioButtonBus")
  283. ->Event("GetState", &UiRadioButtonBus::Events::GetState)
  284. ->Event("GetGroup", &UiRadioButtonBus::Events::GetGroup)
  285. ->Event("GetCheckedEntity", &UiRadioButtonBus::Events::GetCheckedEntity)
  286. ->Event("SetCheckedEntity", &UiRadioButtonBus::Events::SetCheckedEntity)
  287. ->Event("GetUncheckedEntity", &UiRadioButtonBus::Events::GetUncheckedEntity)
  288. ->Event("SetUncheckedEntity", &UiRadioButtonBus::Events::SetUncheckedEntity)
  289. ->Event("GetTurnOnActionName", &UiRadioButtonBus::Events::GetTurnOnActionName)
  290. ->Event("SetTurnOnActionName", &UiRadioButtonBus::Events::SetTurnOnActionName)
  291. ->Event("GetTurnOffActionName", &UiRadioButtonBus::Events::GetTurnOffActionName)
  292. ->Event("SetTurnOffActionName", &UiRadioButtonBus::Events::SetTurnOffActionName)
  293. ->Event("GetChangedActionName", &UiRadioButtonBus::Events::GetChangedActionName)
  294. ->Event("SetChangedActionName", &UiRadioButtonBus::Events::SetChangedActionName);
  295. behaviorContext->EBus<UiRadioButtonNotificationBus>("UiRadioButtonNotificationBus")
  296. ->Handler<UiRadioButtonNotificationBusBehaviorHandler>();
  297. }
  298. }
  299. ////////////////////////////////////////////////////////////////////////////////////////////////////
  300. // PRIVATE MEMBER FUNCTIONS
  301. ////////////////////////////////////////////////////////////////////////////////////////////////////
  302. ////////////////////////////////////////////////////////////////////////////////////////////////////
  303. UiRadioButtonComponent::EntityComboBoxVec UiRadioButtonComponent::PopulateChildEntityList()
  304. {
  305. EntityComboBoxVec result;
  306. // add a first entry for "None"
  307. result.push_back(AZStd::make_pair(AZ::EntityId(AZ::EntityId()), "<None>"));
  308. // Get a list of all child elements
  309. LyShine::EntityArray matchingElements;
  310. UiElementBus::Event(
  311. GetEntityId(),
  312. &UiElementBus::Events::FindDescendantElements,
  313. []([[maybe_unused]] const AZ::Entity* entity)
  314. {
  315. return true;
  316. },
  317. matchingElements);
  318. // add their names to the StringList and their IDs to the id list
  319. for (auto childEntity : matchingElements)
  320. {
  321. result.push_back(AZStd::make_pair(AZ::EntityId(childEntity->GetId()), childEntity->GetName()));
  322. }
  323. return result;
  324. }
  325. ////////////////////////////////////////////////////////////////////////////////////////////////////
  326. UiRadioButtonComponent::EntityComboBoxVec UiRadioButtonComponent::PopulateGroupsEntityList()
  327. {
  328. EntityComboBoxVec result;
  329. // add a first entry for "None"
  330. result.push_back(AZStd::make_pair(AZ::EntityId(AZ::EntityId()), "<None>"));
  331. // Get a list of all elements in the canvas with the radio button group component
  332. AZ::EntityId canvasEntityId;
  333. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  334. LyShine::EntityArray matchingElements;
  335. UiCanvasBus::Event(
  336. canvasEntityId,
  337. &UiCanvasBus::Events::FindElements,
  338. [](const AZ::Entity* entity)
  339. {
  340. return UiRadioButtonGroupBus::FindFirstHandler(entity->GetId()) != nullptr;
  341. },
  342. matchingElements);
  343. // Sort the elements by name
  344. AZStd::sort(matchingElements.begin(), matchingElements.end(),
  345. [](const AZ::Entity* e1, const AZ::Entity* e2) { return e1->GetName() < e2->GetName(); });
  346. // add their names to the StringList and their IDs to the id list
  347. for (auto childEntity : matchingElements)
  348. {
  349. result.push_back(AZStd::make_pair(AZ::EntityId(childEntity->GetId()), childEntity->GetName()));
  350. }
  351. return result;
  352. }
  353. ////////////////////////////////////////////////////////////////////////////////////////////////////
  354. bool UiRadioButtonComponent::HandleReleasedCommon([[maybe_unused]] const AZ::Vector2& point)
  355. {
  356. if (m_isHandlingEvents)
  357. {
  358. UiInteractableComponent::TriggerReleasedAction();
  359. UiRadioButtonGroupCommunicationBus::Event(
  360. m_group, &UiRadioButtonGroupCommunicationBus::Events::RequestRadioButtonStateChange, GetEntityId(), !m_isOn);
  361. }
  362. m_isPressed = false;
  363. return m_isHandlingEvents;
  364. }