3
0

UiLayoutGridComponent.cpp 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  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 "UiLayoutGridComponent.h"
  9. #include <AzCore/Serialization/SerializeContext.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzCore/RTTI/BehaviorContext.h>
  12. #include <LyShine/Bus/UiElementBus.h>
  13. #include <LyShine/UiSerializeHelpers.h>
  14. #include "UiSerialize.h"
  15. ////////////////////////////////////////////////////////////////////////////////////////////////////
  16. // PUBLIC MEMBER FUNCTIONS
  17. ////////////////////////////////////////////////////////////////////////////////////////////////////
  18. ////////////////////////////////////////////////////////////////////////////////////////////////////
  19. UiLayoutGridComponent::UiLayoutGridComponent()
  20. : m_padding(UiLayoutInterface::Padding())
  21. , m_spacing(5.0f, 5.0f)
  22. , m_cellSize(30.0f, 30.0f)
  23. , m_horizontalOrder(UiLayoutInterface::HorizontalOrder::LeftToRight)
  24. , m_verticalOrder(UiLayoutInterface::VerticalOrder::TopToBottom)
  25. , m_startingDirection(StartingDirection::HorizontalOrder)
  26. , m_childHAlignment(IDraw2d::HAlign::Left)
  27. , m_childVAlignment(IDraw2d::VAlign::Top)
  28. , m_origOffsetsInitialized(false)
  29. {
  30. }
  31. ////////////////////////////////////////////////////////////////////////////////////////////////////
  32. UiLayoutGridComponent::~UiLayoutGridComponent()
  33. {
  34. }
  35. ////////////////////////////////////////////////////////////////////////////////////////////////////
  36. void UiLayoutGridComponent::ApplyLayoutWidth()
  37. {
  38. // Set the child widths. The positioning will be applied in ApplyLayoutHeight after the grid's
  39. // height is valid. This is because a grid needs to know its width or height to layout its
  40. // children depending on fill direction
  41. UiTransform2dInterface::Anchors anchors(0.0f, 0.0f, 0.0f, 0.0f);
  42. UiTransform2dInterface::Offsets offsets(0.0f, 0.0f, m_cellSize.GetX(), m_cellSize.GetY());
  43. AZStd::vector<AZ::EntityId> childEntityIds;
  44. UiElementBus::EventResult(childEntityIds, GetEntityId(), &UiElementBus::Events::GetChildEntityIds);
  45. for (auto child : childEntityIds)
  46. {
  47. // Set the anchors
  48. UiTransform2dBus::Event(child, &UiTransform2dBus::Events::SetAnchors, anchors, false, false);
  49. // Set the offsets
  50. UiTransform2dBus::Event(child, &UiTransform2dBus::Events::SetOffsets, offsets);
  51. }
  52. }
  53. ////////////////////////////////////////////////////////////////////////////////////////////////////
  54. void UiLayoutGridComponent::ApplyLayoutHeight()
  55. {
  56. int numChildren = 0;
  57. UiElementBus::EventResult(numChildren, GetEntityId(), &UiElementBus::Events::GetNumChildElements);
  58. if (numChildren > 0)
  59. {
  60. // Get the layout rect inside the padding
  61. AZ::Vector2 layoutRectSize(0.0f, 0.0f);
  62. UiLayoutHelpers::GetSizeInsidePadding(GetEntityId(), m_padding, layoutRectSize);
  63. // Calculate occupied width/height
  64. AZ::Vector2 childrenRectSize = GetChildrenBoundingRectSize(m_cellSize, numChildren);
  65. // Calculate alignment
  66. float hAlignmentOffset = UiLayoutHelpers::GetHorizontalAlignmentOffset(m_childHAlignment, layoutRectSize.GetX(), childrenRectSize.GetX());
  67. float vAlignmentOffset = UiLayoutHelpers::GetVerticalAlignmentOffset(m_childVAlignment, layoutRectSize.GetY(), childrenRectSize.GetY());
  68. int numColumns = 0, numRows = 0;
  69. switch (m_startingDirection)
  70. {
  71. case StartingDirection::HorizontalOrder:
  72. numColumns = static_cast<int>(floorf((layoutRectSize.GetX() + m_spacing.GetX()) / (m_cellSize.GetX() + m_spacing.GetX())));
  73. numColumns = max(numColumns, 1);
  74. break;
  75. case StartingDirection::VerticalOrder:
  76. numRows = static_cast<int>(floorf((layoutRectSize.GetY() + m_spacing.GetY()) / (m_cellSize.GetY() + m_spacing.GetY())));
  77. numRows = max(numRows, 1);
  78. break;
  79. default:
  80. AZ_Assert(0, "Unrecognized Direction type in UiLayoutGridComponent");
  81. break;
  82. }
  83. UiTransform2dInterface::Anchors anchors(0.0f, 0.0f, 0.0f, 0.0f);
  84. UiTransform2dInterface::Offsets offsets(0.0f, 0.0f, 0.0f, 0.0f);
  85. AZStd::vector<AZ::EntityId> childEntityIds;
  86. UiElementBus::EventResult(childEntityIds, GetEntityId(), &UiElementBus::Events::GetChildEntityIds);
  87. int childIndex = 0;
  88. int columnIndex = 0;
  89. int rowIndex = 0;
  90. for (auto child : childEntityIds)
  91. {
  92. // Set the anchors
  93. UiTransform2dBus::Event(child, &UiTransform2dBus::Events::SetAnchors, anchors, false, false);
  94. // Set the offsets
  95. switch (m_startingDirection)
  96. {
  97. case StartingDirection::HorizontalOrder:
  98. columnIndex = childIndex % numColumns;
  99. rowIndex = childIndex / numColumns;
  100. break;
  101. case StartingDirection::VerticalOrder:
  102. columnIndex = childIndex / numRows;
  103. rowIndex = childIndex % numRows;
  104. break;
  105. default:
  106. AZ_Assert(0, "Unrecognized Direction type in UiLayoutGridComponent");
  107. break;
  108. }
  109. switch (m_horizontalOrder)
  110. {
  111. case UiLayoutInterface::HorizontalOrder::LeftToRight:
  112. offsets.m_left = m_padding.m_left + columnIndex * (m_cellSize.GetX() + m_spacing.GetX());
  113. offsets.m_right = offsets.m_left + m_cellSize.GetX();
  114. break;
  115. case UiLayoutInterface::HorizontalOrder::RightToLeft:
  116. offsets.m_right = m_padding.m_left + childrenRectSize.GetX() - columnIndex * (m_cellSize.GetX() + m_spacing.GetX());
  117. offsets.m_left = offsets.m_right - m_cellSize.GetX();
  118. break;
  119. default:
  120. AZ_Assert(0, "Unrecognized HorizontalOrder type in UiLayoutGridComponent");
  121. break;
  122. }
  123. switch (m_verticalOrder)
  124. {
  125. case UiLayoutInterface::VerticalOrder::TopToBottom:
  126. offsets.m_top = m_padding.m_top + rowIndex * (m_cellSize.GetY() + m_spacing.GetY());
  127. offsets.m_bottom = offsets.m_top + m_cellSize.GetY();
  128. break;
  129. case UiLayoutInterface::VerticalOrder::BottomToTop:
  130. offsets.m_bottom = m_padding.m_top + childrenRectSize.GetY() - rowIndex * (m_cellSize.GetY() + m_spacing.GetY());
  131. offsets.m_top = offsets.m_bottom - m_cellSize.GetY();
  132. break;
  133. default:
  134. AZ_Assert(0, "Unrecognized VerticalOrder type in UiLayoutGridComponent");
  135. break;
  136. }
  137. offsets.m_left += hAlignmentOffset;
  138. offsets.m_right += hAlignmentOffset;
  139. offsets.m_top += vAlignmentOffset;
  140. offsets.m_bottom += vAlignmentOffset;
  141. UiTransform2dBus::Event(child, &UiTransform2dBus::Events::SetOffsets, offsets);
  142. childIndex++;
  143. }
  144. }
  145. }
  146. ////////////////////////////////////////////////////////////////////////////////////////////////////
  147. bool UiLayoutGridComponent::IsUsingLayoutCellsToCalculateLayout()
  148. {
  149. return false;
  150. }
  151. ////////////////////////////////////////////////////////////////////////////////////////////////////
  152. bool UiLayoutGridComponent::GetIgnoreDefaultLayoutCells()
  153. {
  154. return true;
  155. }
  156. ////////////////////////////////////////////////////////////////////////////////////////////////////
  157. void UiLayoutGridComponent::SetIgnoreDefaultLayoutCells([[maybe_unused]] bool ignoreDefaultLayoutCells)
  158. {
  159. // Layout cells are not used by this layout component
  160. }
  161. ////////////////////////////////////////////////////////////////////////////////////////////////////
  162. IDraw2d::HAlign UiLayoutGridComponent::GetHorizontalChildAlignment()
  163. {
  164. return m_childHAlignment;
  165. }
  166. ////////////////////////////////////////////////////////////////////////////////////////////////////
  167. void UiLayoutGridComponent::SetHorizontalChildAlignment(IDraw2d::HAlign alignment)
  168. {
  169. m_childHAlignment = alignment;
  170. InvalidateLayout();
  171. }
  172. ////////////////////////////////////////////////////////////////////////////////////////////////////
  173. IDraw2d::VAlign UiLayoutGridComponent::GetVerticalChildAlignment()
  174. {
  175. return m_childVAlignment;
  176. }
  177. ////////////////////////////////////////////////////////////////////////////////////////////////////
  178. void UiLayoutGridComponent::SetVerticalChildAlignment(IDraw2d::VAlign alignment)
  179. {
  180. m_childVAlignment = alignment;
  181. InvalidateLayout();
  182. }
  183. ////////////////////////////////////////////////////////////////////////////////////////////////////
  184. bool UiLayoutGridComponent::IsControllingChild(AZ::EntityId childId)
  185. {
  186. return UiLayoutHelpers::IsControllingChild(GetEntityId(), childId);
  187. }
  188. ////////////////////////////////////////////////////////////////////////////////////////////////////
  189. AZ::Vector2 UiLayoutGridComponent::GetSizeToFitChildElements(const AZ::Vector2& childElementSize, int numChildElements)
  190. {
  191. AZ::Vector2 size(0.0f, 0.0f);
  192. // Initialize original offsets
  193. if (!m_origOffsetsInitialized)
  194. {
  195. m_origOffsetsInitialized = true;
  196. UiTransform2dBus::EventResult(m_origOffsets, GetEntityId(), &UiTransform2dBus::Events::GetOffsets);
  197. }
  198. if (numChildElements > 0)
  199. {
  200. // Calculate a layout rect size that is used to determine the number of rows and columns.
  201. // Since the element size may change after this call, use the original offsets to get the layout rect size
  202. UiTransform2dInterface::Offsets realOffsets;
  203. UiTransform2dBus::EventResult(realOffsets, GetEntityId(), &UiTransform2dBus::Events::GetOffsets);
  204. UiTransform2dBus::Event(GetEntityId(), &UiTransform2dBus::Events::SetOffsets, m_origOffsets);
  205. size = GetChildrenBoundingRectSize(childElementSize, numChildElements);
  206. // Add padding
  207. size.SetX(size.GetX() + m_padding.m_left + m_padding.m_right);
  208. size.SetY(size.GetY() + m_padding.m_top + m_padding.m_bottom);
  209. // In order for the number of rows and columns to remain the same after resizing to this new size, the
  210. // new size must match the size retrieved from GetCanvasSpacePointsNoScaleRotate. To accommodate
  211. // for slight variations, add a small value to ensure that the child element positions won't change
  212. const float epsilon = 0.01f;
  213. size += AZ::Vector2(epsilon, epsilon);
  214. UiTransform2dBus::Event(GetEntityId(), &UiTransform2dBus::Events::SetOffsets, realOffsets);
  215. }
  216. return size;
  217. }
  218. ////////////////////////////////////////////////////////////////////////////////////////////////////
  219. UiLayoutInterface::Padding UiLayoutGridComponent::GetPadding()
  220. {
  221. return m_padding;
  222. }
  223. ////////////////////////////////////////////////////////////////////////////////////////////////////
  224. void UiLayoutGridComponent::SetPadding(UiLayoutInterface::Padding padding)
  225. {
  226. m_padding = padding;
  227. InvalidateLayout();
  228. InvalidateParentLayout();
  229. }
  230. ////////////////////////////////////////////////////////////////////////////////////////////////////
  231. AZ::Vector2 UiLayoutGridComponent::GetSpacing()
  232. {
  233. return m_spacing;
  234. }
  235. ////////////////////////////////////////////////////////////////////////////////////////////////////
  236. void UiLayoutGridComponent::SetSpacing(AZ::Vector2 spacing)
  237. {
  238. m_spacing = spacing;
  239. InvalidateLayout();
  240. InvalidateParentLayout();
  241. }
  242. ////////////////////////////////////////////////////////////////////////////////////////////////////
  243. AZ::Vector2 UiLayoutGridComponent::GetCellSize()
  244. {
  245. return m_cellSize;
  246. }
  247. ////////////////////////////////////////////////////////////////////////////////////////////////////
  248. void UiLayoutGridComponent::SetCellSize(AZ::Vector2 size)
  249. {
  250. m_cellSize = size;
  251. InvalidateLayout();
  252. }
  253. ////////////////////////////////////////////////////////////////////////////////////////////////////
  254. UiLayoutInterface::HorizontalOrder UiLayoutGridComponent::GetHorizontalOrder()
  255. {
  256. return m_horizontalOrder;
  257. }
  258. ////////////////////////////////////////////////////////////////////////////////////////////////////
  259. void UiLayoutGridComponent::SetHorizontalOrder(UiLayoutInterface::HorizontalOrder order)
  260. {
  261. m_horizontalOrder = order;
  262. InvalidateLayout();
  263. }
  264. ////////////////////////////////////////////////////////////////////////////////////////////////////
  265. UiLayoutInterface::VerticalOrder UiLayoutGridComponent::GetVerticalOrder()
  266. {
  267. return m_verticalOrder;
  268. }
  269. ////////////////////////////////////////////////////////////////////////////////////////////////////
  270. void UiLayoutGridComponent::SetVerticalOrder(UiLayoutInterface::VerticalOrder order)
  271. {
  272. m_verticalOrder = order;
  273. InvalidateLayout();
  274. }
  275. ////////////////////////////////////////////////////////////////////////////////////////////////////
  276. UiLayoutGridComponent::StartingDirection UiLayoutGridComponent::GetStartingDirection()
  277. {
  278. return m_startingDirection;
  279. }
  280. ////////////////////////////////////////////////////////////////////////////////////////////////////
  281. void UiLayoutGridComponent::SetStartingDirection(UiLayoutGridComponent::StartingDirection direction)
  282. {
  283. m_startingDirection = direction;
  284. InvalidateLayout();
  285. }
  286. ////////////////////////////////////////////////////////////////////////////////////////////////////
  287. float UiLayoutGridComponent::GetMinWidth()
  288. {
  289. return 0.0f;
  290. }
  291. ////////////////////////////////////////////////////////////////////////////////////////////////////
  292. float UiLayoutGridComponent::GetMinHeight()
  293. {
  294. return 0.0f;
  295. }
  296. ////////////////////////////////////////////////////////////////////////////////////////////////////
  297. float UiLayoutGridComponent::GetTargetWidth(float maxWidth)
  298. {
  299. int numChildElements = 0;
  300. UiElementBus::EventResult(numChildElements, GetEntityId(), &UiElementBus::Events::GetNumChildElements);
  301. if (numChildElements == 0)
  302. {
  303. return 0.0f;
  304. }
  305. // Calculate number of columns
  306. int numColumns = 0;
  307. if (LyShine::IsUiLayoutCellSizeSpecified(maxWidth))
  308. {
  309. const int paddingWidth = m_padding.m_left + m_padding.m_right;
  310. const float availableWidthForCells = maxWidth - paddingWidth;
  311. if (availableWidthForCells > 0.0f)
  312. {
  313. const float cellAndSpacingWidth = m_cellSize.GetX() + m_spacing.GetX();
  314. const int numAvailableColumns = cellAndSpacingWidth > 0.0f ? static_cast<int>((availableWidthForCells + m_spacing.GetX()) / cellAndSpacingWidth) : 1;
  315. numColumns = AZ::GetMin(numAvailableColumns, numChildElements);
  316. }
  317. if (numColumns == 0)
  318. {
  319. return 0.0f;
  320. }
  321. }
  322. else
  323. {
  324. // Since element width/height is unknown at this point, make target width resemble a square grid
  325. numColumns = static_cast<int>(ceil(sqrt(numChildElements)));
  326. }
  327. float width = m_padding.m_left + m_padding.m_right + (numColumns * m_cellSize.GetX()) + ((numColumns - 1) * m_spacing.GetX());
  328. // In order for the number of columns to remain the same after resizing to this new size, the
  329. // new size must match the size retrieved from GetCanvasSpacePointsNoScaleRotate. To accommodate
  330. // for slight variations, add a small value to ensure that the same number of cells fit per row
  331. // after the element has been resized to this target size
  332. const float epsilon = 0.01f;
  333. width += epsilon;
  334. return width;
  335. }
  336. ////////////////////////////////////////////////////////////////////////////////////////////////////
  337. float UiLayoutGridComponent::GetTargetHeight(float /*maxHeight*/)
  338. {
  339. int numChildElements = 0;
  340. UiElementBus::EventResult(numChildElements, GetEntityId(), &UiElementBus::Events::GetNumChildElements);
  341. if (numChildElements == 0)
  342. {
  343. return 0.0f;
  344. }
  345. // Check how many elements fit in a row
  346. AZ::Vector2 rectSize(0.0f, 0.0f);
  347. UiTransformBus::EventResult(rectSize, GetEntityId(), &UiTransformBus::Events::GetCanvasSpaceSizeNoScaleRotate);
  348. // At least one child must fit in each row
  349. int numElementsPerRow = 1;
  350. float additionalElementWidth = m_spacing.GetX() + m_cellSize.GetX();
  351. if (additionalElementWidth > 0.0f)
  352. {
  353. float availableWidthForAdditionalElements = AZ::GetMax(0.0f, rectSize.GetX() - (m_padding.m_left + m_padding.m_right + m_cellSize.GetX()));
  354. numElementsPerRow += static_cast<int>(availableWidthForAdditionalElements / additionalElementWidth);
  355. }
  356. else
  357. {
  358. numElementsPerRow = numChildElements;
  359. }
  360. // Calculate number of rows
  361. int numRows = static_cast<int>(ceil(static_cast<float>(numChildElements) / numElementsPerRow));
  362. float height = m_padding.m_top + m_padding.m_bottom + (numRows * m_cellSize.GetY()) + ((numRows - 1) * m_spacing.GetY());
  363. return height;
  364. }
  365. ////////////////////////////////////////////////////////////////////////////////////////////////////
  366. float UiLayoutGridComponent::GetExtraWidthRatio()
  367. {
  368. return 1.0f;
  369. }
  370. ////////////////////////////////////////////////////////////////////////////////////////////////////
  371. float UiLayoutGridComponent::GetExtraHeightRatio()
  372. {
  373. return 1.0f;
  374. }
  375. ////////////////////////////////////////////////////////////////////////////////////////////////////
  376. void UiLayoutGridComponent::OnCanvasSpaceRectChanged([[maybe_unused]] AZ::EntityId entityId, const UiTransformInterface::Rect& oldRect, const UiTransformInterface::Rect& newRect)
  377. {
  378. // If old rect equals new rect, size changed due to initialization
  379. bool sizeChanged = (oldRect == newRect) || (!oldRect.GetSize().IsClose(newRect.GetSize(), 0.05f));
  380. if (sizeChanged)
  381. {
  382. InvalidateLayout();
  383. }
  384. }
  385. ////////////////////////////////////////////////////////////////////////////////////////////////////
  386. // PUBLIC STATIC MEMBER FUNCTIONS
  387. ////////////////////////////////////////////////////////////////////////////////////////////////////
  388. void UiLayoutGridComponent::Reflect(AZ::ReflectContext* context)
  389. {
  390. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  391. if (serializeContext)
  392. {
  393. serializeContext->Class<UiLayoutGridComponent, AZ::Component>()
  394. ->Version(2, &VersionConverter)
  395. ->Field("Padding", &UiLayoutGridComponent::m_padding)
  396. ->Field("Spacing", &UiLayoutGridComponent::m_spacing)
  397. ->Field("CellSize", &UiLayoutGridComponent::m_cellSize)
  398. ->Field("HorizontalOrder", &UiLayoutGridComponent::m_horizontalOrder)
  399. ->Field("VerticalOrder", &UiLayoutGridComponent::m_verticalOrder)
  400. ->Field("StartingWith", &UiLayoutGridComponent::m_startingDirection)
  401. ->Field("ChildHAlignment", &UiLayoutGridComponent::m_childHAlignment)
  402. ->Field("ChildVAlignment", &UiLayoutGridComponent::m_childVAlignment);
  403. AZ::EditContext* ec = serializeContext->GetEditContext();
  404. if (ec)
  405. {
  406. auto editInfo = ec->Class<UiLayoutGridComponent>("LayoutGrid", "A layout component that arranges its children in a grid");
  407. editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  408. ->Attribute(AZ::Edit::Attributes::Category, "UI")
  409. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiLayoutGrid.png")
  410. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiLayoutGrid.png")
  411. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0))
  412. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  413. editInfo->DataElement(AZ::Edit::UIHandlers::LayoutPadding, &UiLayoutGridComponent::m_padding, "Padding", "The layout padding")
  414. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show) // needed because sub-elements are hidden
  415. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout)
  416. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateParentLayout)
  417. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::CheckLayoutFitterAndRefreshEditorTransformProperties);
  418. editInfo->DataElement(0, &UiLayoutGridComponent::m_spacing, "Spacing", "The spacing between children")
  419. ->Attribute(AZ::Edit::Attributes::LabelForX, "Horizontal")
  420. ->Attribute(AZ::Edit::Attributes::LabelForY, "Vertical")
  421. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show) // needed because sub-elements are hidden
  422. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout)
  423. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateParentLayout)
  424. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::CheckLayoutFitterAndRefreshEditorTransformProperties);
  425. editInfo->DataElement(0, &UiLayoutGridComponent::m_cellSize, "Cell size", "The size of the cells")
  426. ->Attribute(AZ::Edit::Attributes::LabelForX, "Width")
  427. ->Attribute(AZ::Edit::Attributes::LabelForY, "Height")
  428. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show) // needed because sub-elements are hidden
  429. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout)
  430. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateParentLayout)
  431. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::CheckLayoutFitterAndRefreshEditorTransformProperties);
  432. // Order group
  433. {
  434. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Order")
  435. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  436. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiLayoutGridComponent::m_horizontalOrder, "Horizontal",
  437. "Which direction the rows fill")
  438. ->EnumAttribute(UiLayoutInterface::HorizontalOrder::LeftToRight, "Left to right")
  439. ->EnumAttribute(UiLayoutInterface::HorizontalOrder::RightToLeft, "Right to left")
  440. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout);
  441. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiLayoutGridComponent::m_verticalOrder, "Vertical",
  442. "Which direction the columns fill")
  443. ->EnumAttribute(UiLayoutInterface::VerticalOrder::TopToBottom, "Top to bottom")
  444. ->EnumAttribute(UiLayoutInterface::VerticalOrder::BottomToTop, "Bottom to top")
  445. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout);
  446. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiLayoutGridComponent::m_startingDirection, "Starting with",
  447. "Start filling horizontally or vertically")
  448. ->EnumAttribute(UiLayoutGridInterface::StartingDirection::HorizontalOrder, "Horizontal")
  449. ->EnumAttribute(UiLayoutGridInterface::StartingDirection::VerticalOrder, "Vertical")
  450. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout);
  451. }
  452. // Alignment
  453. {
  454. editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Child Alignment")
  455. ->Attribute(AZ::Edit::Attributes::AutoExpand, true);
  456. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiLayoutGridComponent::m_childHAlignment, "Horizontal",
  457. "How to align the children if they don't take up all the available width")
  458. ->EnumAttribute(IDraw2d::HAlign::Left, "Left")
  459. ->EnumAttribute(IDraw2d::HAlign::Center, "Center")
  460. ->EnumAttribute(IDraw2d::HAlign::Right, "Right")
  461. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout);
  462. editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiLayoutGridComponent::m_childVAlignment, "Vertical",
  463. "How to align the children if they don't take up all the available height")
  464. ->EnumAttribute(IDraw2d::VAlign::Top, "Top")
  465. ->EnumAttribute(IDraw2d::VAlign::Center, "Center")
  466. ->EnumAttribute(IDraw2d::VAlign::Bottom, "Bottom")
  467. ->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout);
  468. }
  469. }
  470. }
  471. AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
  472. if (behaviorContext)
  473. {
  474. behaviorContext->Enum<(int)UiLayoutGridInterface::StartingDirection::HorizontalOrder>("eUiLayoutGridStartingDirection_HorizontalOrder")
  475. ->Enum<(int)UiLayoutGridInterface::StartingDirection::VerticalOrder>("eUiLayoutGridStartingDirection_VerticalOrder");
  476. behaviorContext->EBus<UiLayoutGridBus>("UiLayoutGridBus")
  477. ->Event("GetPadding", &UiLayoutGridBus::Events::GetPadding)
  478. ->Event("SetPadding", &UiLayoutGridBus::Events::SetPadding)
  479. ->Event("GetSpacing", &UiLayoutGridBus::Events::GetSpacing)
  480. ->Event("SetSpacing", &UiLayoutGridBus::Events::SetSpacing)
  481. ->Event("GetCellSize", &UiLayoutGridBus::Events::GetCellSize)
  482. ->Event("SetCellSize", &UiLayoutGridBus::Events::SetCellSize)
  483. ->Event("GetHorizontalOrder", &UiLayoutGridBus::Events::GetHorizontalOrder)
  484. ->Event("SetHorizontalOrder", &UiLayoutGridBus::Events::SetHorizontalOrder)
  485. ->Event("GetVerticalOrder", &UiLayoutGridBus::Events::GetVerticalOrder)
  486. ->Event("SetVerticalOrder", &UiLayoutGridBus::Events::SetVerticalOrder)
  487. ->Event("GetStartingDirection", &UiLayoutGridBus::Events::GetStartingDirection)
  488. ->Event("SetStartingDirection", &UiLayoutGridBus::Events::SetStartingDirection);
  489. }
  490. }
  491. ////////////////////////////////////////////////////////////////////////////////////////////////////
  492. // PROTECTED MEMBER FUNCTIONS
  493. ////////////////////////////////////////////////////////////////////////////////////////////////////
  494. ////////////////////////////////////////////////////////////////////////////////////////////////////
  495. void UiLayoutGridComponent::Activate()
  496. {
  497. UiLayoutBus::Handler::BusConnect(m_entity->GetId());
  498. UiLayoutControllerBus::Handler::BusConnect(m_entity->GetId());
  499. UiLayoutGridBus::Handler::BusConnect(m_entity->GetId());
  500. UiLayoutCellDefaultBus::Handler::BusConnect(m_entity->GetId());
  501. UiTransformChangeNotificationBus::Handler::BusConnect(m_entity->GetId());
  502. // If this is the first time the entity has been activated this has no effect since the canvas
  503. // is not known. But if a LayoutGrid component has just been pasted onto an existing entity
  504. // we need to invalidate the layout in case that affects things.
  505. InvalidateLayout();
  506. InvalidateParentLayout();
  507. }
  508. ////////////////////////////////////////////////////////////////////////////////////////////////////
  509. void UiLayoutGridComponent::Deactivate()
  510. {
  511. UiLayoutBus::Handler::BusDisconnect();
  512. UiLayoutControllerBus::Handler::BusDisconnect();
  513. UiLayoutGridBus::Handler::BusDisconnect();
  514. UiLayoutCellDefaultBus::Handler::BusDisconnect();
  515. UiTransformChangeNotificationBus::Handler::BusDisconnect();
  516. // We could be about to remove this component and then reactivate the entity
  517. // which could affect the layout if there is a parent layout component
  518. InvalidateLayout();
  519. InvalidateParentLayout();
  520. }
  521. ////////////////////////////////////////////////////////////////////////////////////////////////////
  522. AZ::Vector2 UiLayoutGridComponent::GetChildrenBoundingRectSize(const AZ::Vector2 childElementSize, int numChildElements)
  523. {
  524. AZ::Vector2 size(0.0f, 0.0f);
  525. if (numChildElements > 0)
  526. {
  527. // Get the layout rect inside the padding
  528. AZ::Vector2 layoutRectSize(0.0f, 0.0f);
  529. UiLayoutHelpers::GetSizeInsidePadding(GetEntityId(), m_padding, layoutRectSize);
  530. // Calculate number of rows and columns
  531. int numColumns = 0;
  532. int numRows = 0;
  533. switch (m_startingDirection)
  534. {
  535. case StartingDirection::HorizontalOrder:
  536. numColumns = static_cast<int>(floorf((layoutRectSize.GetX() + m_spacing.GetX()) / (childElementSize.GetX() + m_spacing.GetX())));
  537. Limit(numColumns, 1, numChildElements);
  538. numRows = static_cast<int>(ceil(static_cast<float>(numChildElements) / numColumns));
  539. numRows = max(numRows, 1);
  540. break;
  541. case StartingDirection::VerticalOrder:
  542. numRows = static_cast<int>(floorf((layoutRectSize.GetY() + m_spacing.GetY()) / (childElementSize.GetY() + m_spacing.GetY())));
  543. Limit(numRows, 1, numChildElements);
  544. numColumns = static_cast<int>(ceil(static_cast<float>(numChildElements) / numRows));
  545. numColumns = max(numColumns, 1);
  546. break;
  547. default:
  548. AZ_Assert(0, "Unrecognized Direction type in UiLayoutGridComponent");
  549. break;
  550. }
  551. // Calculate the minimum size to cover the rows and columns with spacing
  552. size.SetX((numColumns * childElementSize.GetX()) + (m_spacing.GetX() * (numColumns - 1)));
  553. size.SetY((numRows * childElementSize.GetY()) + (m_spacing.GetY() * (numRows - 1)));
  554. }
  555. return size;
  556. }
  557. ////////////////////////////////////////////////////////////////////////////////////////////////////
  558. void UiLayoutGridComponent::InvalidateLayout()
  559. {
  560. UiLayoutHelpers::InvalidateLayout(GetEntityId());
  561. }
  562. ////////////////////////////////////////////////////////////////////////////////////////////////////
  563. void UiLayoutGridComponent::InvalidateParentLayout()
  564. {
  565. UiLayoutHelpers::InvalidateParentLayout(GetEntityId());
  566. }
  567. ////////////////////////////////////////////////////////////////////////////////////////////////////
  568. void UiLayoutGridComponent::CheckLayoutFitterAndRefreshEditorTransformProperties() const
  569. {
  570. UiLayoutHelpers::CheckFitterAndRefreshEditorTransformProperties(GetEntityId());
  571. }
  572. ////////////////////////////////////////////////////////////////////////////////////////////////////
  573. // PRIVATE STATIC MEMBER FUNCTIONS
  574. ////////////////////////////////////////////////////////////////////////////////////////////////////
  575. ////////////////////////////////////////////////////////////////////////////////////////////////////
  576. bool UiLayoutGridComponent::VersionConverter(AZ::SerializeContext& context,
  577. AZ::SerializeContext::DataElementNode& classElement)
  578. {
  579. // conversion from version 1:
  580. // - Need to convert Vec2 to AZ::Vector2
  581. if (classElement.GetVersion() <= 1)
  582. {
  583. if (!LyShine::ConvertSubElementFromVec2ToVector2(context, classElement, "Spacing"))
  584. {
  585. return false;
  586. }
  587. if (!LyShine::ConvertSubElementFromVec2ToVector2(context, classElement, "CellSize"))
  588. {
  589. return false;
  590. }
  591. }
  592. return true;
  593. }