3
0

UiLayoutFitterComponent.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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 "UiLayoutFitterComponent.h"
  9. #include "UiLayoutHelpers.h"
  10. #include <LyShine/Bus/UiLayoutCellBus.h>
  11. #include <LyShine/Bus/UiLayoutCellDefaultBus.h>
  12. #include <LyShine/Bus/UiTransformBus.h>
  13. #include <LyShine/Bus/UiTransform2dBus.h>
  14. #include <LyShine/Bus/UiEditorChangeNotificationBus.h>
  15. #include <AzCore/Serialization/SerializeContext.h>
  16. #include <AzCore/Serialization/EditContext.h>
  17. #include <AzCore/RTTI/BehaviorContext.h>
  18. ////////////////////////////////////////////////////////////////////////////////////////////////////
  19. // PUBLIC MEMBER FUNCTIONS
  20. ////////////////////////////////////////////////////////////////////////////////////////////////////
  21. ////////////////////////////////////////////////////////////////////////////////////////////////////
  22. UiLayoutFitterComponent::UiLayoutFitterComponent()
  23. : m_horizontalFit(false)
  24. , m_verticalFit(false)
  25. {
  26. }
  27. ////////////////////////////////////////////////////////////////////////////////////////////////////
  28. UiLayoutFitterComponent::~UiLayoutFitterComponent()
  29. {
  30. }
  31. ////////////////////////////////////////////////////////////////////////////////////////////////////
  32. void UiLayoutFitterComponent::ApplyLayoutWidth()
  33. {
  34. if (m_horizontalFit)
  35. {
  36. float targetWidth = UiLayoutHelpers::GetLayoutElementTargetWidth(GetEntityId());
  37. // Recalculate the new horizontal offsets using the pivot
  38. UiTransform2dInterface::Offsets offsets;
  39. UiTransform2dBus::EventResult(offsets, GetEntityId(), &UiTransform2dBus::Events::GetOffsets);
  40. UiTransform2dInterface::Anchors anchors;
  41. UiTransform2dBus::EventResult(anchors, GetEntityId(), &UiTransform2dBus::Events::GetAnchors);
  42. // If anchors are separate
  43. if (anchors.m_left != anchors.m_right)
  44. {
  45. // Bring them back together in their midpoint
  46. float midPoint = (anchors.m_left + anchors.m_right) / 2.0f;
  47. anchors.m_left = anchors.m_right = midPoint;
  48. UiTransform2dBus::Event(GetEntityId(), &UiTransform2dBus::Events::SetAnchors, anchors, false, true);
  49. }
  50. float oldWidth = offsets.m_right - offsets.m_left;
  51. float widthDiff = targetWidth - oldWidth;
  52. if (widthDiff != 0.0f)
  53. {
  54. AZ::Vector2 pivot;
  55. UiTransformBus::EventResult(pivot, GetEntityId(), &UiTransformBus::Events::GetPivot);
  56. offsets.m_left -= widthDiff * pivot.GetX();
  57. offsets.m_right += widthDiff * (1.0f - pivot.GetX());
  58. UiTransform2dBus::Event(GetEntityId(), &UiTransform2dBus::Events::SetOffsets, offsets);
  59. }
  60. }
  61. }
  62. ////////////////////////////////////////////////////////////////////////////////////////////////////
  63. void UiLayoutFitterComponent::ApplyLayoutHeight()
  64. {
  65. if (m_verticalFit)
  66. {
  67. float targetHeight = UiLayoutHelpers::GetLayoutElementTargetHeight(GetEntityId());
  68. // Recalculate the new vertical offsets using the pivot
  69. UiTransform2dInterface::Offsets offsets;
  70. UiTransform2dBus::EventResult(offsets, GetEntityId(), &UiTransform2dBus::Events::GetOffsets);
  71. UiTransform2dInterface::Anchors anchors;
  72. UiTransform2dBus::EventResult(anchors, GetEntityId(), &UiTransform2dBus::Events::GetAnchors);
  73. // If anchors are separate
  74. if (anchors.m_top != anchors.m_bottom)
  75. {
  76. // Bring them back together in their midpoint
  77. float midPoint = (anchors.m_top + anchors.m_bottom) / 2.0f;
  78. anchors.m_top = anchors.m_bottom = midPoint;
  79. UiTransform2dBus::Event(GetEntityId(), &UiTransform2dBus::Events::SetAnchors, anchors, false, true);
  80. }
  81. float oldHeight = offsets.m_bottom - offsets.m_top;
  82. float heightDiff = targetHeight - oldHeight;
  83. if (heightDiff != 0.0f)
  84. {
  85. AZ::Vector2 pivot;
  86. UiTransformBus::EventResult(pivot, GetEntityId(), &UiTransformBus::Events::GetPivot);
  87. offsets.m_top -= heightDiff * pivot.GetY();
  88. offsets.m_bottom += heightDiff * (1.0f - pivot.GetY());
  89. UiTransform2dBus::Event(GetEntityId(), &UiTransform2dBus::Events::SetOffsets, offsets);
  90. }
  91. }
  92. }
  93. ////////////////////////////////////////////////////////////////////////////////////////////////////
  94. bool UiLayoutFitterComponent::GetHorizontalFit()
  95. {
  96. return m_horizontalFit;
  97. }
  98. ////////////////////////////////////////////////////////////////////////////////////////////////////
  99. void UiLayoutFitterComponent::SetHorizontalFit(bool horizontalFit)
  100. {
  101. m_horizontalFit = horizontalFit;
  102. CheckFitterAndInvalidateLayout();
  103. }
  104. ////////////////////////////////////////////////////////////////////////////////////////////////////
  105. bool UiLayoutFitterComponent::GetVerticalFit()
  106. {
  107. return m_verticalFit;
  108. }
  109. ////////////////////////////////////////////////////////////////////////////////////////////////////
  110. void UiLayoutFitterComponent::SetVerticalFit(bool verticalFit)
  111. {
  112. m_verticalFit = verticalFit;
  113. CheckFitterAndInvalidateLayout();
  114. }
  115. ////////////////////////////////////////////////////////////////////////////////////////////////////
  116. UiLayoutFitterInterface::FitType UiLayoutFitterComponent::GetFitType()
  117. {
  118. if (m_horizontalFit && m_verticalFit)
  119. {
  120. return UiLayoutFitterInterface::FitType::HorizontalAndVertical;
  121. }
  122. else if (m_horizontalFit)
  123. {
  124. return UiLayoutFitterInterface::FitType::HorizontalOnly;
  125. }
  126. else if (m_verticalFit)
  127. {
  128. return UiLayoutFitterInterface::FitType::VerticalOnly;
  129. }
  130. else
  131. {
  132. return UiLayoutFitterInterface::FitType::None;
  133. }
  134. }
  135. ////////////////////////////////////////////////////////////////////////////////////////////////////
  136. // PUBLIC STATIC MEMBER FUNCTIONS
  137. ////////////////////////////////////////////////////////////////////////////////////////////////////
  138. void UiLayoutFitterComponent::Reflect(AZ::ReflectContext* context)
  139. {
  140. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  141. if (serializeContext)
  142. {
  143. serializeContext->Class<UiLayoutFitterComponent, AZ::Component>()
  144. ->Version(0)
  145. ->Field("HorizontalFit", &UiLayoutFitterComponent::m_horizontalFit)
  146. ->Field("VerticalFit", &UiLayoutFitterComponent::m_verticalFit);
  147. AZ::EditContext* ec = serializeContext->GetEditContext();
  148. if (ec)
  149. {
  150. auto editInfo = ec->Class<UiLayoutFitterComponent>("LayoutFitter", "A component that resizes its element to its content.");
  151. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  152. ->Attribute(AZ::Edit::Attributes::Category, "UI")
  153. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiLayoutFitter.png")
  154. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiLayoutFitter.png")
  155. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0))
  156. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  157. editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiLayoutFitterComponent::m_horizontalFit, "Horizontal Fit",
  158. "When checked, this element will be resized according to the target width of its content.")
  159. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutFitterComponent::CheckFitterAndInvalidateLayout)
  160. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutFitterComponent::RefreshEditorTransformProperties);
  161. editInfo->DataElement(AZ::Edit::UIHandlers::CheckBox, &UiLayoutFitterComponent::m_verticalFit, "Vertical Fit",
  162. "When checked, this element will be resized according to the target height of its content.")
  163. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutFitterComponent::CheckFitterAndInvalidateLayout)
  164. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutFitterComponent::RefreshEditorTransformProperties);
  165. }
  166. }
  167. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  168. if (behaviorContext)
  169. {
  170. behaviorContext->EBus<UiLayoutFitterBus>("UiLayoutFitterBus")
  171. ->Event("GetHorizontalFit", &UiLayoutFitterBus::Events::GetHorizontalFit)
  172. ->Event("SetHorizontalFit", &UiLayoutFitterBus::Events::SetHorizontalFit)
  173. ->Event("GetVerticalFit", &UiLayoutFitterBus::Events::GetVerticalFit)
  174. ->Event("SetVerticalFit", &UiLayoutFitterBus::Events::SetVerticalFit);
  175. }
  176. }
  177. ////////////////////////////////////////////////////////////////////////////////////////////////////
  178. // PROTECTED MEMBER FUNCTIONS
  179. ////////////////////////////////////////////////////////////////////////////////////////////////////
  180. ////////////////////////////////////////////////////////////////////////////////////////////////////
  181. void UiLayoutFitterComponent::Activate()
  182. {
  183. UiLayoutControllerBus::Handler::BusConnect(m_entity->GetId());
  184. UiLayoutFitterBus::Handler::BusConnect(m_entity->GetId());
  185. // If this is the first time the entity has been activated this has no effect since the canvas
  186. // is not known. But if a LayoutFitter component has just been pasted onto an existing entity
  187. // we need to invalidate the layout in case that affects things.
  188. CheckFitterAndInvalidateLayout();
  189. }
  190. ////////////////////////////////////////////////////////////////////////////////////////////////////
  191. void UiLayoutFitterComponent::Deactivate()
  192. {
  193. UiLayoutControllerBus::Handler::BusDisconnect();
  194. UiLayoutFitterBus::Handler::BusDisconnect();
  195. }
  196. ////////////////////////////////////////////////////////////////////////////////////////////////////
  197. unsigned int UiLayoutFitterComponent::GetPriority() const
  198. {
  199. // Priority should be lower (called earlier) than components that modify their children size
  200. // Default prioritiy is 100
  201. return 50;
  202. }
  203. ////////////////////////////////////////////////////////////////////////////////////////////////////
  204. void UiLayoutFitterComponent::CheckFitterAndInvalidateLayout()
  205. {
  206. if (m_horizontalFit || m_verticalFit)
  207. {
  208. UiLayoutHelpers::InvalidateLayout(GetEntityId());
  209. }
  210. }
  211. ////////////////////////////////////////////////////////////////////////////////////////////////////
  212. void UiLayoutFitterComponent::RefreshEditorTransformProperties()
  213. {
  214. UiEditorChangeNotificationBus::Broadcast(&UiEditorChangeNotificationBus::Events::OnEditorTransformPropertiesNeedRefresh);
  215. }