UiCheckboxComponent.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  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 "UiCheckboxComponent.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/UiTransformBus.h>
  17. #include <LyShine/Bus/UiVisualBus.h>
  18. #include <LyShine/ISprite.h>
  19. #include <LyShine/UiSerializeHelpers.h>
  20. #include "UiSerialize.h"
  21. ////////////////////////////////////////////////////////////////////////////////////////////////////
  22. //! UiCheckboxNotificationBus Behavior context handler class
  23. class UiCheckboxNotificationBusBehaviorHandler
  24. : public UiCheckboxNotificationBus::Handler
  25. , public AZ::BehaviorEBusHandler
  26. {
  27. public:
  28. AZ_EBUS_BEHAVIOR_BINDER(UiCheckboxNotificationBusBehaviorHandler, "{718A00EF-119B-4616-9235-F55790640A1E}", AZ::SystemAllocator,
  29. OnCheckboxStateChange);
  30. void OnCheckboxStateChange(bool checked) override
  31. {
  32. Call(FN_OnCheckboxStateChange, checked);
  33. }
  34. };
  35. ////////////////////////////////////////////////////////////////////////////////////////////////////
  36. // PUBLIC MEMBER FUNCTIONS
  37. ////////////////////////////////////////////////////////////////////////////////////////////////////
  38. ////////////////////////////////////////////////////////////////////////////////////////////////////
  39. UiCheckboxComponent::UiCheckboxComponent()
  40. : m_isOn(false)
  41. , m_optionalCheckedEntity()
  42. , m_optionalUncheckedEntity()
  43. , m_onChange()
  44. , m_turnOnActionName()
  45. , m_turnOffActionName()
  46. , m_changedActionName()
  47. {
  48. }
  49. ////////////////////////////////////////////////////////////////////////////////////////////////////
  50. UiCheckboxComponent::~UiCheckboxComponent()
  51. {
  52. }
  53. ////////////////////////////////////////////////////////////////////////////////////////////////////
  54. bool UiCheckboxComponent::GetState()
  55. {
  56. return m_isOn;
  57. }
  58. ////////////////////////////////////////////////////////////////////////////////////////////////////
  59. void UiCheckboxComponent::SetState(bool isOn)
  60. {
  61. m_isOn = isOn;
  62. if (m_optionalCheckedEntity.IsValid())
  63. {
  64. UiElementBus::Event(m_optionalCheckedEntity, &UiElementBus::Events::SetIsEnabled, m_isOn);
  65. }
  66. if (m_optionalUncheckedEntity.IsValid())
  67. {
  68. UiElementBus::Event(m_optionalUncheckedEntity, &UiElementBus::Events::SetIsEnabled, !m_isOn);
  69. }
  70. }
  71. ////////////////////////////////////////////////////////////////////////////////////////////////////
  72. bool UiCheckboxComponent::ToggleState()
  73. {
  74. SetState(!m_isOn);
  75. return m_isOn;
  76. }
  77. ////////////////////////////////////////////////////////////////////////////////////////////////////
  78. UiCheckboxComponent::StateChangeCallback UiCheckboxComponent::GetStateChangeCallback()
  79. {
  80. return m_onChange;
  81. }
  82. ////////////////////////////////////////////////////////////////////////////////////////////////////
  83. void UiCheckboxComponent::SetStateChangeCallback(UiCheckboxComponent::StateChangeCallback onChange)
  84. {
  85. m_onChange = onChange;
  86. }
  87. ////////////////////////////////////////////////////////////////////////////////////////////////////
  88. void UiCheckboxComponent::SetCheckedEntity(AZ::EntityId entityId)
  89. {
  90. m_optionalCheckedEntity = entityId;
  91. }
  92. ////////////////////////////////////////////////////////////////////////////////////////////////////
  93. AZ::EntityId UiCheckboxComponent::GetCheckedEntity()
  94. {
  95. return m_optionalCheckedEntity;
  96. }
  97. ////////////////////////////////////////////////////////////////////////////////////////////////////
  98. void UiCheckboxComponent::SetUncheckedEntity(AZ::EntityId entityId)
  99. {
  100. m_optionalUncheckedEntity = entityId;
  101. }
  102. ////////////////////////////////////////////////////////////////////////////////////////////////////
  103. AZ::EntityId UiCheckboxComponent::GetUncheckedEntity()
  104. {
  105. return m_optionalUncheckedEntity;
  106. }
  107. ////////////////////////////////////////////////////////////////////////////////////////////////////
  108. const LyShine::ActionName& UiCheckboxComponent::GetTurnOnActionName()
  109. {
  110. return m_turnOnActionName;
  111. }
  112. ////////////////////////////////////////////////////////////////////////////////////////////////////
  113. void UiCheckboxComponent::SetTurnOnActionName(const LyShine::ActionName& actionName)
  114. {
  115. m_turnOnActionName = actionName;
  116. }
  117. ////////////////////////////////////////////////////////////////////////////////////////////////////
  118. const LyShine::ActionName& UiCheckboxComponent::GetTurnOffActionName()
  119. {
  120. return m_turnOffActionName;
  121. }
  122. ////////////////////////////////////////////////////////////////////////////////////////////////////
  123. void UiCheckboxComponent::SetTurnOffActionName(const LyShine::ActionName& actionName)
  124. {
  125. m_turnOffActionName = actionName;
  126. }
  127. ////////////////////////////////////////////////////////////////////////////////////////////////////
  128. const LyShine::ActionName& UiCheckboxComponent::GetChangedActionName()
  129. {
  130. return m_changedActionName;
  131. }
  132. ////////////////////////////////////////////////////////////////////////////////////////////////////
  133. void UiCheckboxComponent::SetChangedActionName(const LyShine::ActionName& actionName)
  134. {
  135. m_changedActionName = actionName;
  136. }
  137. ////////////////////////////////////////////////////////////////////////////////////////////////////
  138. void UiCheckboxComponent::InGamePostActivate()
  139. {
  140. SetState(m_isOn);
  141. }
  142. ////////////////////////////////////////////////////////////////////////////////////////////////////
  143. bool UiCheckboxComponent::HandleReleased(AZ::Vector2 point)
  144. {
  145. bool isInRect = false;
  146. UiTransformBus::EventResult(isInRect, GetEntityId(), &UiTransformBus::Events::IsPointInRect, point);
  147. if (isInRect)
  148. {
  149. return HandleReleasedCommon(point);
  150. }
  151. else
  152. {
  153. m_isPressed = false;
  154. return m_isHandlingEvents;
  155. }
  156. }
  157. ////////////////////////////////////////////////////////////////////////////////////////////////////
  158. bool UiCheckboxComponent::HandleEnterReleased()
  159. {
  160. AZ::Vector2 point(-1.0f, -1.0f);
  161. return HandleReleasedCommon(point);
  162. }
  163. ////////////////////////////////////////////////////////////////////////////////////////////////////
  164. // PROTECTED MEMBER FUNCTIONS
  165. ////////////////////////////////////////////////////////////////////////////////////////////////////
  166. ////////////////////////////////////////////////////////////////////////////////////////////////////
  167. void UiCheckboxComponent::Activate()
  168. {
  169. UiInteractableComponent::Activate();
  170. UiCheckboxBus::Handler::BusConnect(GetEntityId());
  171. UiInitializationBus::Handler::BusConnect(GetEntityId());
  172. }
  173. ////////////////////////////////////////////////////////////////////////////////////////////////////
  174. void UiCheckboxComponent::Deactivate()
  175. {
  176. UiInteractableComponent::Deactivate();
  177. UiCheckboxBus::Handler::BusDisconnect(GetEntityId());
  178. UiInitializationBus::Handler::BusDisconnect(GetEntityId());
  179. }
  180. ////////////////////////////////////////////////////////////////////////////////////////////////////
  181. // PROTECTED STATIC MEMBER FUNCTIONS
  182. ////////////////////////////////////////////////////////////////////////////////////////////////////
  183. void UiCheckboxComponent::Reflect(AZ::ReflectContext* context)
  184. {
  185. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  186. if (serializeContext)
  187. {
  188. serializeContext->Class<UiCheckboxComponent, UiInteractableComponent>()
  189. ->Version(3, &VersionConverter)
  190. // Elements group
  191. ->Field("OptionalCheckedEntity", &UiCheckboxComponent::m_optionalCheckedEntity)
  192. ->Field("OptionalUncheckedEntity", &UiCheckboxComponent::m_optionalUncheckedEntity)
  193. // Value group
  194. ->Field("IsChecked", &UiCheckboxComponent::m_isOn)
  195. // Actions group
  196. ->Field("ChangedActionName", &UiCheckboxComponent::m_changedActionName)
  197. ->Field("TurnOnActionName", &UiCheckboxComponent::m_turnOnActionName)
  198. ->Field("TurnOffActionName", &UiCheckboxComponent::m_turnOffActionName);
  199. AZ::EditContext* ec = serializeContext->GetEditContext();
  200. if (ec)
  201. {
  202. auto editInfo = ec->Class<UiCheckboxComponent>("Checkbox", "An interactable component for Checkbox/Toggle behavior.");
  203. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  204. ->Attribute(AZ::Edit::Attributes::Category, "UI")
  205. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiCheckbox.png")
  206. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiCheckbox.png")
  207. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0))
  208. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  209. // Elements group
  210. {
  211. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Elements")
  212. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  213. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiCheckboxComponent::m_optionalCheckedEntity, "On", "The child element to show when Checkbox is in on state.")
  214. ->Attribute(AZ::Edit::Attributes::EnumValues, &UiCheckboxComponent::PopulateChildEntityList);
  215. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiCheckboxComponent::m_optionalUncheckedEntity, "Off", "The child element to show when Checkbox is in off state.")
  216. ->Attribute(AZ::Edit::Attributes::EnumValues, &UiCheckboxComponent::PopulateChildEntityList);
  217. }
  218. // Value group
  219. {
  220. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Value")
  221. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  222. editInfo->DataElement(0, &UiCheckboxComponent::m_isOn, "Checked", "The initial state of the Checkbox.");
  223. }
  224. // Actions group
  225. {
  226. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Actions")
  227. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  228. editInfo->DataElement(0, &UiCheckboxComponent::m_changedActionName, "Change", "The action triggered when value changes either way.");
  229. editInfo->DataElement(0, &UiCheckboxComponent::m_turnOnActionName, "On", "The action triggered when turned on.");
  230. editInfo->DataElement(0, &UiCheckboxComponent::m_turnOffActionName, "Off", "The action triggered when turned off.");
  231. }
  232. }
  233. }
  234. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  235. if (behaviorContext)
  236. {
  237. behaviorContext->EBus<UiCheckboxBus>("UiCheckboxBus")
  238. ->Event("GetState", &UiCheckboxBus::Events::GetState)
  239. ->Event("SetState", &UiCheckboxBus::Events::SetState)
  240. ->Event("ToggleState", &UiCheckboxBus::Events::ToggleState)
  241. ->Event("GetCheckedEntity", &UiCheckboxBus::Events::GetCheckedEntity)
  242. ->Event("SetCheckedEntity", &UiCheckboxBus::Events::SetCheckedEntity)
  243. ->Event("GetUncheckedEntity", &UiCheckboxBus::Events::GetUncheckedEntity)
  244. ->Event("SetUncheckedEntity", &UiCheckboxBus::Events::SetUncheckedEntity)
  245. ->Event("GetTurnOnActionName", &UiCheckboxBus::Events::GetTurnOnActionName)
  246. ->Event("SetTurnOnActionName", &UiCheckboxBus::Events::SetTurnOnActionName)
  247. ->Event("GetTurnOffActionName", &UiCheckboxBus::Events::GetTurnOffActionName)
  248. ->Event("SetTurnOffActionName", &UiCheckboxBus::Events::SetTurnOffActionName)
  249. ->Event("GetChangedActionName", &UiCheckboxBus::Events::GetChangedActionName)
  250. ->Event("SetChangedActionName", &UiCheckboxBus::Events::SetChangedActionName);
  251. behaviorContext->EBus<UiCheckboxNotificationBus>("UiCheckboxNotificationBus")
  252. ->Handler<UiCheckboxNotificationBusBehaviorHandler>();
  253. }
  254. }
  255. ////////////////////////////////////////////////////////////////////////////////////////////////////
  256. // PRIVATE MEMBER FUNCTIONS
  257. ////////////////////////////////////////////////////////////////////////////////////////////////////
  258. ////////////////////////////////////////////////////////////////////////////////////////////////////
  259. UiCheckboxComponent::EntityComboBoxVec UiCheckboxComponent::PopulateChildEntityList()
  260. {
  261. EntityComboBoxVec result;
  262. // add a first entry for "None"
  263. result.push_back(AZStd::make_pair(AZ::EntityId(AZ::EntityId()), "<None>"));
  264. // Get a list of all child elements
  265. LyShine::EntityArray matchingElements;
  266. UiElementBus::Event(
  267. GetEntityId(),
  268. &UiElementBus::Events::FindDescendantElements,
  269. []([[maybe_unused]] const AZ::Entity* entity)
  270. {
  271. return true;
  272. },
  273. matchingElements);
  274. // add their names to the StringList and their IDs to the id list
  275. for (auto childEntity : matchingElements)
  276. {
  277. result.push_back(AZStd::make_pair(AZ::EntityId(childEntity->GetId()), childEntity->GetName()));
  278. }
  279. return result;
  280. }
  281. ////////////////////////////////////////////////////////////////////////////////////////////////////
  282. bool UiCheckboxComponent::HandleReleasedCommon(const AZ::Vector2& point)
  283. {
  284. if (m_isHandlingEvents)
  285. {
  286. SetState(!m_isOn);
  287. if (m_onChange)
  288. {
  289. m_onChange(GetEntityId(), point, m_isOn);
  290. }
  291. UiInteractableComponent::TriggerReleasedAction();
  292. // Tell any action listeners about the event
  293. if (m_isOn && !m_turnOnActionName.empty())
  294. {
  295. AZ::EntityId canvasEntityId;
  296. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  297. UiCanvasNotificationBus::Event(canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_turnOnActionName);
  298. }
  299. if (!m_isOn && !m_turnOffActionName.empty())
  300. {
  301. AZ::EntityId canvasEntityId;
  302. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  303. UiCanvasNotificationBus::Event(canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_turnOffActionName);
  304. }
  305. if (!m_changedActionName.empty())
  306. {
  307. AZ::EntityId canvasEntityId;
  308. UiElementBus::EventResult(canvasEntityId, GetEntityId(), &UiElementBus::Events::GetCanvasEntityId);
  309. UiCanvasNotificationBus::Event(canvasEntityId, &UiCanvasNotificationBus::Events::OnAction, GetEntityId(), m_changedActionName);
  310. }
  311. UiCheckboxNotificationBus::Event(GetEntityId(), &UiCheckboxNotificationBus::Events::OnCheckboxStateChange, m_isOn);
  312. }
  313. m_isPressed = false;
  314. return m_isHandlingEvents;
  315. }
  316. ////////////////////////////////////////////////////////////////////////////////////////////////////
  317. // PRIVATE STATIC MEMBER FUNCTIONS
  318. ////////////////////////////////////////////////////////////////////////////////////////////////////
  319. ////////////////////////////////////////////////////////////////////////////////////////////////////
  320. bool UiCheckboxComponent::VersionConverter(AZ::SerializeContext& context,
  321. AZ::SerializeContext::DataElementNode& classElement)
  322. {
  323. // conversion from version 1 to 2:
  324. // - Need to convert AZStd::string sprites to AzFramework::SimpleAssetReference<LmbrCentral::TextureAsset>
  325. if (classElement.GetVersion() < 2)
  326. {
  327. if (!LyShine::ConvertSubElementFromAzStringToAssetRef<LmbrCentral::TextureAsset>(context, classElement, "SelectedSprite"))
  328. {
  329. return false;
  330. }
  331. if (!LyShine::ConvertSubElementFromAzStringToAssetRef<LmbrCentral::TextureAsset>(context, classElement, "DisabledSprite"))
  332. {
  333. return false;
  334. }
  335. }
  336. // Conversion from version 2 to 3:
  337. if (classElement.GetVersion() < 3)
  338. {
  339. // find the base class (AZ::Component)
  340. // NOTE: in very old versions there may not be a base class because the base class was not serialized
  341. int componentBaseClassIndex = classElement.FindElement(AZ_CRC("BaseClass1", 0xd4925735));
  342. // If there was a base class, make a copy and remove it
  343. AZ::SerializeContext::DataElementNode componentBaseClassNode;
  344. if (componentBaseClassIndex != -1)
  345. {
  346. // make a local copy of the component base class node
  347. componentBaseClassNode = classElement.GetSubElement(componentBaseClassIndex);
  348. // remove the component base class from the button
  349. classElement.RemoveElement(componentBaseClassIndex);
  350. }
  351. // Add a new base class (UiInteractableComponent)
  352. int interactableBaseClassIndex = classElement.AddElement<UiInteractableComponent>(context, "BaseClass1");
  353. AZ::SerializeContext::DataElementNode& interactableBaseClassNode = classElement.GetSubElement(interactableBaseClassIndex);
  354. // if there was previously a base class...
  355. if (componentBaseClassIndex != -1)
  356. {
  357. // copy the component base class into the new interactable base class
  358. // Since AZ::Component is now the base class of UiInteractableComponent
  359. interactableBaseClassNode.AddElement(componentBaseClassNode);
  360. }
  361. // Move the selected/hover state to the base class
  362. if (!UiSerialize::MoveToInteractableStateActions(context, classElement, "HoverStateActions",
  363. "SelectedColor", "SelectedAlpha", "SelectedSprite"))
  364. {
  365. return false;
  366. }
  367. // Move the disabled state to the base class
  368. if (!UiSerialize::MoveToInteractableStateActions(context, classElement, "DisabledStateActions",
  369. "DisabledColor", "DisabledAlpha", "DisabledSprite"))
  370. {
  371. return false;
  372. }
  373. }
  374. return true;
  375. }