You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/LyShine/Code/Source/UiLayoutGridComponent.cpp

698 lines
30 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include "UiLayoutGridComponent.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <LyShine/Bus/UiElementBus.h>
#include <LyShine/UiSerializeHelpers.h>
#include "UiSerialize.h"
////////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC MEMBER FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
UiLayoutGridComponent::UiLayoutGridComponent()
: m_padding(UiLayoutInterface::Padding())
, m_spacing(5.0f, 5.0f)
, m_cellSize(30.0f, 30.0f)
, m_horizontalOrder(UiLayoutInterface::HorizontalOrder::LeftToRight)
, m_verticalOrder(UiLayoutInterface::VerticalOrder::TopToBottom)
, m_startingDirection(StartingDirection::HorizontalOrder)
, m_childHAlignment(IDraw2d::HAlign::Left)
, m_childVAlignment(IDraw2d::VAlign::Top)
, m_origOffsetsInitialized(false)
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
UiLayoutGridComponent::~UiLayoutGridComponent()
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::ApplyLayoutWidth()
{
// Set the child widths. The positioning will be applied in ApplyLayoutHeight after the grid's
// height is valid. This is because a grid needs to know its width or height to layout its
// children depending on fill direction
UiTransform2dInterface::Anchors anchors(0.0f, 0.0f, 0.0f, 0.0f);
UiTransform2dInterface::Offsets offsets(0.0f, 0.0f, m_cellSize.GetX(), m_cellSize.GetY());
AZStd::vector<AZ::EntityId> childEntityIds;
EBUS_EVENT_ID_RESULT(childEntityIds, GetEntityId(), UiElementBus, GetChildEntityIds);
for (auto child : childEntityIds)
{
// Set the anchors
EBUS_EVENT_ID(child, UiTransform2dBus, SetAnchors, anchors, false, false);
// Set the offsets
EBUS_EVENT_ID(child, UiTransform2dBus, SetOffsets, offsets);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::ApplyLayoutHeight()
{
int numChildren = 0;
EBUS_EVENT_ID_RESULT(numChildren, GetEntityId(), UiElementBus, GetNumChildElements);
if (numChildren > 0)
{
// Get the layout rect inside the padding
AZ::Vector2 layoutRectSize(0.0f, 0.0f);
UiLayoutHelpers::GetSizeInsidePadding(GetEntityId(), m_padding, layoutRectSize);
// Calculate occupied width/height
AZ::Vector2 childrenRectSize = GetChildrenBoundingRectSize(m_cellSize, numChildren);
// Calculate alignment
float hAlignmentOffset = UiLayoutHelpers::GetHorizontalAlignmentOffset(m_childHAlignment, layoutRectSize.GetX(), childrenRectSize.GetX());
float vAlignmentOffset = UiLayoutHelpers::GetVerticalAlignmentOffset(m_childVAlignment, layoutRectSize.GetY(), childrenRectSize.GetY());
int numColumns = 0, numRows = 0;
switch (m_startingDirection)
{
case StartingDirection::HorizontalOrder:
numColumns = static_cast<int>(floorf((layoutRectSize.GetX() + m_spacing.GetX()) / (m_cellSize.GetX() + m_spacing.GetX())));
numColumns = max(numColumns, 1);
break;
case StartingDirection::VerticalOrder:
numRows = static_cast<int>(floorf((layoutRectSize.GetY() + m_spacing.GetY()) / (m_cellSize.GetY() + m_spacing.GetY())));
numRows = max(numRows, 1);
break;
default:
AZ_Assert(0, "Unrecognized Direction type in UiLayoutGridComponent");
break;
}
UiTransform2dInterface::Anchors anchors(0.0f, 0.0f, 0.0f, 0.0f);
UiTransform2dInterface::Offsets offsets(0.0f, 0.0f, 0.0f, 0.0f);
AZStd::vector<AZ::EntityId> childEntityIds;
EBUS_EVENT_ID_RESULT(childEntityIds, GetEntityId(), UiElementBus, GetChildEntityIds);
int childIndex = 0;
int columnIndex = 0;
int rowIndex = 0;
for (auto child : childEntityIds)
{
// Set the anchors
EBUS_EVENT_ID(child, UiTransform2dBus, SetAnchors, anchors, false, false);
// Set the offsets
switch (m_startingDirection)
{
case StartingDirection::HorizontalOrder:
columnIndex = childIndex % numColumns;
rowIndex = childIndex / numColumns;
break;
case StartingDirection::VerticalOrder:
columnIndex = childIndex / numRows;
rowIndex = childIndex % numRows;
break;
default:
AZ_Assert(0, "Unrecognized Direction type in UiLayoutGridComponent");
break;
}
switch (m_horizontalOrder)
{
case UiLayoutInterface::HorizontalOrder::LeftToRight:
offsets.m_left = m_padding.m_left + columnIndex * (m_cellSize.GetX() + m_spacing.GetX());
offsets.m_right = offsets.m_left + m_cellSize.GetX();
break;
case UiLayoutInterface::HorizontalOrder::RightToLeft:
offsets.m_right = m_padding.m_left + childrenRectSize.GetX() - columnIndex * (m_cellSize.GetX() + m_spacing.GetX());
offsets.m_left = offsets.m_right - m_cellSize.GetX();
break;
default:
AZ_Assert(0, "Unrecognized HorizontalOrder type in UiLayoutGridComponent");
break;
}
switch (m_verticalOrder)
{
case UiLayoutInterface::VerticalOrder::TopToBottom:
offsets.m_top = m_padding.m_top + rowIndex * (m_cellSize.GetY() + m_spacing.GetY());
offsets.m_bottom = offsets.m_top + m_cellSize.GetY();
break;
case UiLayoutInterface::VerticalOrder::BottomToTop:
offsets.m_bottom = m_padding.m_top + childrenRectSize.GetY() - rowIndex * (m_cellSize.GetY() + m_spacing.GetY());
offsets.m_top = offsets.m_bottom - m_cellSize.GetY();
break;
default:
AZ_Assert(0, "Unrecognized VerticalOrder type in UiLayoutGridComponent");
break;
}
offsets.m_left += hAlignmentOffset;
offsets.m_right += hAlignmentOffset;
offsets.m_top += vAlignmentOffset;
offsets.m_bottom += vAlignmentOffset;
EBUS_EVENT_ID(child, UiTransform2dBus, SetOffsets, offsets);
childIndex++;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool UiLayoutGridComponent::IsUsingLayoutCellsToCalculateLayout()
{
return false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool UiLayoutGridComponent::GetIgnoreDefaultLayoutCells()
{
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::SetIgnoreDefaultLayoutCells([[maybe_unused]] bool ignoreDefaultLayoutCells)
{
// Layout cells are not used by this layout component
}
////////////////////////////////////////////////////////////////////////////////////////////////////
IDraw2d::HAlign UiLayoutGridComponent::GetHorizontalChildAlignment()
{
return m_childHAlignment;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::SetHorizontalChildAlignment(IDraw2d::HAlign alignment)
{
m_childHAlignment = alignment;
InvalidateLayout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
IDraw2d::VAlign UiLayoutGridComponent::GetVerticalChildAlignment()
{
return m_childVAlignment;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::SetVerticalChildAlignment(IDraw2d::VAlign alignment)
{
m_childVAlignment = alignment;
InvalidateLayout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool UiLayoutGridComponent::IsControllingChild(AZ::EntityId childId)
{
return UiLayoutHelpers::IsControllingChild(GetEntityId(), childId);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Vector2 UiLayoutGridComponent::GetSizeToFitChildElements(const AZ::Vector2& childElementSize, int numChildElements)
{
AZ::Vector2 size(0.0f, 0.0f);
// Initialize original offsets
if (!m_origOffsetsInitialized)
{
m_origOffsetsInitialized = true;
EBUS_EVENT_ID_RESULT(m_origOffsets, GetEntityId(), UiTransform2dBus, GetOffsets);
}
if (numChildElements > 0)
{
// Calculate a layout rect size that is used to determine the number of rows and columns.
// Since the element size may change after this call, use the original offsets to get the layout rect size
UiTransform2dInterface::Offsets realOffsets;
EBUS_EVENT_ID_RESULT(realOffsets, GetEntityId(), UiTransform2dBus, GetOffsets);
EBUS_EVENT_ID(GetEntityId(), UiTransform2dBus, SetOffsets, m_origOffsets);
size = GetChildrenBoundingRectSize(childElementSize, numChildElements);
// Add padding
size.SetX(size.GetX() + m_padding.m_left + m_padding.m_right);
size.SetY(size.GetY() + m_padding.m_top + m_padding.m_bottom);
// In order for the number of rows and columns to remain the same after resizing to this new size, the
// new size must match the size retrieved from GetCanvasSpacePointsNoScaleRotate. To accommodate
// for slight variations, add a small value to ensure that the child element positions won't change
const float epsilon = 0.01f;
size += AZ::Vector2(epsilon, epsilon);
EBUS_EVENT_ID(GetEntityId(), UiTransform2dBus, SetOffsets, realOffsets);
}
return size;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
UiLayoutInterface::Padding UiLayoutGridComponent::GetPadding()
{
return m_padding;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::SetPadding(UiLayoutInterface::Padding padding)
{
m_padding = padding;
InvalidateLayout();
InvalidateParentLayout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Vector2 UiLayoutGridComponent::GetSpacing()
{
return m_spacing;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::SetSpacing(AZ::Vector2 spacing)
{
m_spacing = spacing;
InvalidateLayout();
InvalidateParentLayout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Vector2 UiLayoutGridComponent::GetCellSize()
{
return m_cellSize;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::SetCellSize(AZ::Vector2 size)
{
m_cellSize = size;
InvalidateLayout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
UiLayoutInterface::HorizontalOrder UiLayoutGridComponent::GetHorizontalOrder()
{
return m_horizontalOrder;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::SetHorizontalOrder(UiLayoutInterface::HorizontalOrder order)
{
m_horizontalOrder = order;
InvalidateLayout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
UiLayoutInterface::VerticalOrder UiLayoutGridComponent::GetVerticalOrder()
{
return m_verticalOrder;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::SetVerticalOrder(UiLayoutInterface::VerticalOrder order)
{
m_verticalOrder = order;
InvalidateLayout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
UiLayoutGridComponent::StartingDirection UiLayoutGridComponent::GetStartingDirection()
{
return m_startingDirection;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::SetStartingDirection(UiLayoutGridComponent::StartingDirection direction)
{
m_startingDirection = direction;
InvalidateLayout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
float UiLayoutGridComponent::GetMinWidth()
{
return 0.0f;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
float UiLayoutGridComponent::GetMinHeight()
{
return 0.0f;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
float UiLayoutGridComponent::GetTargetWidth(float maxWidth)
{
int numChildElements = 0;
EBUS_EVENT_ID_RESULT(numChildElements, GetEntityId(), UiElementBus, GetNumChildElements);
if (numChildElements == 0)
{
return 0.0f;
}
// Calculate number of columns
int numColumns = 0;
if (LyShine::IsUiLayoutCellSizeSpecified(maxWidth))
{
const int paddingWidth = m_padding.m_left + m_padding.m_right;
const float availableWidthForCells = maxWidth - paddingWidth;
if (availableWidthForCells > 0.0f)
{
const float cellAndSpacingWidth = m_cellSize.GetX() + m_spacing.GetX();
const int numAvailableColumns = cellAndSpacingWidth > 0.0f ? static_cast<int>((availableWidthForCells + m_spacing.GetX()) / cellAndSpacingWidth) : 1;
numColumns = AZ::GetMin(numAvailableColumns, numChildElements);
}
if (numColumns == 0)
{
return 0.0f;
}
}
else
{
// Since element width/height is unknown at this point, make target width resemble a square grid
numColumns = static_cast<int>(ceil(sqrt(numChildElements)));
}
float width = m_padding.m_left + m_padding.m_right + (numColumns * m_cellSize.GetX()) + ((numColumns - 1) * m_spacing.GetX());
// In order for the number of columns to remain the same after resizing to this new size, the
// new size must match the size retrieved from GetCanvasSpacePointsNoScaleRotate. To accommodate
// for slight variations, add a small value to ensure that the same number of cells fit per row
// after the element has been resized to this target size
const float epsilon = 0.01f;
width += epsilon;
return width;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
float UiLayoutGridComponent::GetTargetHeight(float /*maxHeight*/)
{
int numChildElements = 0;
EBUS_EVENT_ID_RESULT(numChildElements, GetEntityId(), UiElementBus, GetNumChildElements);
if (numChildElements == 0)
{
return 0.0f;
}
// Check how many elements fit in a row
AZ::Vector2 rectSize(0.0f, 0.0f);
EBUS_EVENT_ID_RESULT(rectSize, GetEntityId(), UiTransformBus, GetCanvasSpaceSizeNoScaleRotate);
// At least one child must fit in each row
int numElementsPerRow = 1;
float additionalElementWidth = m_spacing.GetX() + m_cellSize.GetX();
if (additionalElementWidth > 0.0f)
{
float availableWidthForAdditionalElements = AZ::GetMax(0.0f, rectSize.GetX() - (m_padding.m_left + m_padding.m_right + m_cellSize.GetX()));
numElementsPerRow += static_cast<int>(availableWidthForAdditionalElements / additionalElementWidth);
}
else
{
numElementsPerRow = numChildElements;
}
// Calculate number of rows
int numRows = static_cast<int>(ceil(static_cast<float>(numChildElements) / numElementsPerRow));
float height = m_padding.m_top + m_padding.m_bottom + (numRows * m_cellSize.GetY()) + ((numRows - 1) * m_spacing.GetY());
return height;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
float UiLayoutGridComponent::GetExtraWidthRatio()
{
return 1.0f;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
float UiLayoutGridComponent::GetExtraHeightRatio()
{
return 1.0f;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::OnCanvasSpaceRectChanged([[maybe_unused]] AZ::EntityId entityId, const UiTransformInterface::Rect& oldRect, const UiTransformInterface::Rect& newRect)
{
// If old rect equals new rect, size changed due to initialization
bool sizeChanged = (oldRect == newRect) || (!oldRect.GetSize().IsClose(newRect.GetSize(), 0.05f));
if (sizeChanged)
{
InvalidateLayout();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// PUBLIC STATIC MEMBER FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<UiLayoutGridComponent, AZ::Component>()
->Version(2, &VersionConverter)
->Field("Padding", &UiLayoutGridComponent::m_padding)
->Field("Spacing", &UiLayoutGridComponent::m_spacing)
->Field("CellSize", &UiLayoutGridComponent::m_cellSize)
->Field("HorizontalOrder", &UiLayoutGridComponent::m_horizontalOrder)
->Field("VerticalOrder", &UiLayoutGridComponent::m_verticalOrder)
->Field("StartingWith", &UiLayoutGridComponent::m_startingDirection)
->Field("ChildHAlignment", &UiLayoutGridComponent::m_childHAlignment)
->Field("ChildVAlignment", &UiLayoutGridComponent::m_childVAlignment);
AZ::EditContext* ec = serializeContext->GetEditContext();
if (ec)
{
auto editInfo = ec->Class<UiLayoutGridComponent>("LayoutGrid", "A layout component that arranges its children in a grid");
editInfo->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "UI")
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/UiLayoutGrid.png")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/UiLayoutGrid.png")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("UI", 0x27ff46b0))
->Attribute(AZ::Edit::Attributes::AutoExpand, true);
editInfo->DataElement(AZ::Edit::UIHandlers::LayoutPadding, &UiLayoutGridComponent::m_padding, "Padding", "The layout padding")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show) // needed because sub-elements are hidden
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateParentLayout)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::CheckLayoutFitterAndRefreshEditorTransformProperties);
editInfo->DataElement(0, &UiLayoutGridComponent::m_spacing, "Spacing", "The spacing between children")
->Attribute(AZ::Edit::Attributes::LabelForX, "Horizontal")
->Attribute(AZ::Edit::Attributes::LabelForY, "Vertical")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show) // needed because sub-elements are hidden
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateParentLayout)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::CheckLayoutFitterAndRefreshEditorTransformProperties);
editInfo->DataElement(0, &UiLayoutGridComponent::m_cellSize, "Cell size", "The size of the cells")
->Attribute(AZ::Edit::Attributes::LabelForX, "Width")
->Attribute(AZ::Edit::Attributes::LabelForY, "Height")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show) // needed because sub-elements are hidden
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateParentLayout)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::CheckLayoutFitterAndRefreshEditorTransformProperties);
// Order group
{
editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Order")
->Attribute(AZ::Edit::Attributes::AutoExpand, true);
editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiLayoutGridComponent::m_horizontalOrder, "Horizontal",
"Which direction the rows fill")
->EnumAttribute(UiLayoutInterface::HorizontalOrder::LeftToRight, "Left to right")
->EnumAttribute(UiLayoutInterface::HorizontalOrder::RightToLeft, "Right to left")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout);
editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiLayoutGridComponent::m_verticalOrder, "Vertical",
"Which direction the columns fill")
->EnumAttribute(UiLayoutInterface::VerticalOrder::TopToBottom, "Top to bottom")
->EnumAttribute(UiLayoutInterface::VerticalOrder::BottomToTop, "Bottom to top")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout);
editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiLayoutGridComponent::m_startingDirection, "Starting with",
"Start filling horizontally or vertically")
->EnumAttribute(UiLayoutGridInterface::StartingDirection::HorizontalOrder, "Horizontal")
->EnumAttribute(UiLayoutGridInterface::StartingDirection::VerticalOrder, "Vertical")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout);
}
// Alignment
{
editInfo->ClassElement(AZ::Edit::ClassElements::Group, "Child Alignment")
->Attribute(AZ::Edit::Attributes::AutoExpand, true);
editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiLayoutGridComponent::m_childHAlignment, "Horizontal",
"How to align the children if they don't take up all the available width")
->EnumAttribute(IDraw2d::HAlign::Left, "Left")
->EnumAttribute(IDraw2d::HAlign::Center, "Center")
->EnumAttribute(IDraw2d::HAlign::Right, "Right")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout);
editInfo->DataElement(AZ::Edit::UIHandlers::ComboBox, &UiLayoutGridComponent::m_childVAlignment, "Vertical",
"How to align the children if they don't take up all the available height")
->EnumAttribute(IDraw2d::VAlign::Top, "Top")
->EnumAttribute(IDraw2d::VAlign::Center, "Center")
->EnumAttribute(IDraw2d::VAlign::Bottom, "Bottom")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &UiLayoutGridComponent::InvalidateLayout);
}
}
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Enum<(int)UiLayoutGridInterface::StartingDirection::HorizontalOrder>("eUiLayoutGridStartingDirection_HorizontalOrder")
->Enum<(int)UiLayoutGridInterface::StartingDirection::VerticalOrder>("eUiLayoutGridStartingDirection_VerticalOrder");
behaviorContext->EBus<UiLayoutGridBus>("UiLayoutGridBus")
->Event("GetPadding", &UiLayoutGridBus::Events::GetPadding)
->Event("SetPadding", &UiLayoutGridBus::Events::SetPadding)
->Event("GetSpacing", &UiLayoutGridBus::Events::GetSpacing)
->Event("SetSpacing", &UiLayoutGridBus::Events::SetSpacing)
->Event("GetCellSize", &UiLayoutGridBus::Events::GetCellSize)
->Event("SetCellSize", &UiLayoutGridBus::Events::SetCellSize)
->Event("GetHorizontalOrder", &UiLayoutGridBus::Events::GetHorizontalOrder)
->Event("SetHorizontalOrder", &UiLayoutGridBus::Events::SetHorizontalOrder)
->Event("GetVerticalOrder", &UiLayoutGridBus::Events::GetVerticalOrder)
->Event("SetVerticalOrder", &UiLayoutGridBus::Events::SetVerticalOrder)
->Event("GetStartingDirection", &UiLayoutGridBus::Events::GetStartingDirection)
->Event("SetStartingDirection", &UiLayoutGridBus::Events::SetStartingDirection);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// PROTECTED MEMBER FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::Activate()
{
UiLayoutBus::Handler::BusConnect(m_entity->GetId());
UiLayoutControllerBus::Handler::BusConnect(m_entity->GetId());
UiLayoutGridBus::Handler::BusConnect(m_entity->GetId());
UiLayoutCellDefaultBus::Handler::BusConnect(m_entity->GetId());
UiTransformChangeNotificationBus::Handler::BusConnect(m_entity->GetId());
// If this is the first time the entity has been activated this has no effect since the canvas
// is not known. But if a LayoutGrid component has just been pasted onto an existing entity
// we need to invalidate the layout in case that affects things.
InvalidateLayout();
InvalidateParentLayout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::Deactivate()
{
UiLayoutBus::Handler::BusDisconnect();
UiLayoutControllerBus::Handler::BusDisconnect();
UiLayoutGridBus::Handler::BusDisconnect();
UiLayoutCellDefaultBus::Handler::BusDisconnect();
UiTransformChangeNotificationBus::Handler::BusDisconnect();
// We could be about to remove this component and then reactivate the entity
// which could affect the layout if there is a parent layout component
InvalidateLayout();
InvalidateParentLayout();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Vector2 UiLayoutGridComponent::GetChildrenBoundingRectSize(const AZ::Vector2 childElementSize, int numChildElements)
{
AZ::Vector2 size(0.0f, 0.0f);
if (numChildElements > 0)
{
// Get the layout rect inside the padding
AZ::Vector2 layoutRectSize(0.0f, 0.0f);
UiLayoutHelpers::GetSizeInsidePadding(GetEntityId(), m_padding, layoutRectSize);
// Calculate number of rows and columns
int numColumns = 0;
int numRows = 0;
switch (m_startingDirection)
{
case StartingDirection::HorizontalOrder:
numColumns = static_cast<int>(floorf((layoutRectSize.GetX() + m_spacing.GetX()) / (childElementSize.GetX() + m_spacing.GetX())));
Limit(numColumns, 1, numChildElements);
numRows = static_cast<int>(ceil(static_cast<float>(numChildElements) / numColumns));
numRows = max(numRows, 1);
break;
case StartingDirection::VerticalOrder:
numRows = static_cast<int>(floorf((layoutRectSize.GetY() + m_spacing.GetY()) / (childElementSize.GetY() + m_spacing.GetY())));
Limit(numRows, 1, numChildElements);
numColumns = static_cast<int>(ceil(static_cast<float>(numChildElements) / numRows));
numColumns = max(numColumns, 1);
break;
default:
AZ_Assert(0, "Unrecognized Direction type in UiLayoutGridComponent");
break;
}
// Calculate the minimum size to cover the rows and columns with spacing
size.SetX((numColumns * childElementSize.GetX()) + (m_spacing.GetX() * (numColumns - 1)));
size.SetY((numRows * childElementSize.GetY()) + (m_spacing.GetY() * (numRows - 1)));
}
return size;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::InvalidateLayout()
{
UiLayoutHelpers::InvalidateLayout(GetEntityId());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::InvalidateParentLayout()
{
UiLayoutHelpers::InvalidateParentLayout(GetEntityId());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void UiLayoutGridComponent::CheckLayoutFitterAndRefreshEditorTransformProperties() const
{
UiLayoutHelpers::CheckFitterAndRefreshEditorTransformProperties(GetEntityId());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// PRIVATE STATIC MEMBER FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
bool UiLayoutGridComponent::VersionConverter(AZ::SerializeContext& context,
AZ::SerializeContext::DataElementNode& classElement)
{
// conversion from version 1:
// - Need to convert Vec2 to AZ::Vector2
if (classElement.GetVersion() <= 1)
{
if (!LyShine::ConvertSubElementFromVec2ToVector2(context, classElement, "Spacing"))
{
return false;
}
if (!LyShine::ConvertSubElementFromVec2ToVector2(context, classElement, "CellSize"))
{
return false;
}
}
return true;
}