#include "GridLayout.h" #include #include namespace UI { GridLayout::GridLayout(float width, float itemSpacing, float rowHeight) : reasonableColumnWidth(std::max(1.0f, width)), spacing(std::max(0.0f, itemSpacing)), maximumRowHeight(std::max(0.0f, rowHeight)) { } void GridLayout::SetReasonableColumnWidth(float width) { reasonableColumnWidth = std::max(1.0f, width); MarkDirty(DirtyFlags::Layout); } float GridLayout::GetReasonableColumnWidth() const { return reasonableColumnWidth; } void GridLayout::SetSpacing(float value) { spacing = std::max(0.0f, value); MarkDirty(DirtyFlags::Layout); } float GridLayout::GetSpacing() const { return spacing; } std::size_t GridLayout::GetColumnCount() const { return columnCount; } std::size_t GridLayout::CalculateColumnCount(float availableWidth) const { float contentWidth = std::max(0.0f, availableWidth - padding.HorizontalTotal()); if (contentWidth <= 0.0f) { return 1; } float columns = (contentWidth + spacing) / (reasonableColumnWidth + spacing); return std::max(1, static_cast(std::round(columns))); } float GridLayout::CalculateColumnWidth(float availableWidth) const { float contentWidth = std::max(0.0f, availableWidth - padding.HorizontalTotal()); return std::max(0.0f, (contentWidth - spacing * static_cast(columnCount - 1)) / static_cast(columnCount)); } void GridLayout::Measure(Size availableSpace) { columnCount = CalculateColumnCount(availableSpace.width); float columnWidth = CalculateColumnWidth(availableSpace.width); float totalHeight = padding.VerticalTotal(); float rowHeight = 0.0f; std::size_t column = 0; std::size_t rowCount = 0; for (auto& child : children) { if (!child->IsVisible()) { continue; } child->Measure(Size(columnWidth, availableSpace.height)); rowHeight = std::max(rowHeight, child->GetDesiredSize().height); column++; if (column == columnCount) { column = 0; rowCount++; } } if (maximumRowHeight > 0.0f) { rowHeight = maximumRowHeight; } if (column != 0) { rowCount++; } if (rowCount > 0) { totalHeight += rowHeight * static_cast(rowCount); totalHeight += spacing * static_cast(rowCount - 1); } float width = std::max(availableSpace.width, columnWidth * static_cast(columnCount) + spacing * static_cast(columnCount - 1) + padding.HorizontalTotal()); desiredSize.width = std::clamp(width + margin.HorizontalTotal(), minSize.width, maxSize.width); desiredSize.height = std::clamp(totalHeight + margin.VerticalTotal(), minSize.height, maxSize.height); } void GridLayout::ArrangeChildInCell(Widget* child, Rect cellBounds) const { Size desired = child->GetDesiredSize(); Thickness childMargin = child->GetMargin(); float availableWidth = std::max(0.0f, cellBounds.Width - childMargin.HorizontalTotal()); float availableHeight = std::max(0.0f, cellBounds.Height - childMargin.VerticalTotal()); float scale = 1.0f; if (desired.width > 0.0f && desired.height > 0.0f) { scale = std::min(availableWidth / desired.width, availableHeight / desired.height); } float width = desired.width * scale; float height = desired.height * scale; child->Arrange(Rect( cellBounds.X + childMargin.Left + (availableWidth - width) * 0.5f, cellBounds.Y + childMargin.Top + (availableHeight - height) * 0.5f, width, height )); } void GridLayout::Arrange(Rect finalBounds) { ArrangeCore(finalBounds); columnCount = CalculateColumnCount(localBounds.Width); float columnWidth = CalculateColumnWidth(localBounds.Width); std::vector rowHeights; float rowHeight = 0.0f; for (std::size_t index = 0; index < children.size(); index++) { if (!children[index]->IsVisible()) { continue; } rowHeight = std::max(rowHeight, children[index]->GetDesiredSize().height); } if (maximumRowHeight > 0.0f) { rowHeight = maximumRowHeight; } std::size_t rowCount = children.empty() ? 0 : (children.size() + columnCount - 1) / columnCount; rowHeights.resize(rowCount, rowHeight); for (std::size_t index = 0; index < children.size(); index++) { if (!children[index]->IsVisible()) { continue; } std::size_t row = index / columnCount; if (row >= rowHeights.size()) { continue; } } for (std::size_t index = 0; index < children.size(); index++) { if (!children[index]->IsVisible()) { continue; } std::size_t row = index / columnCount; std::size_t column = index % columnCount; float y = padding.Top; for (std::size_t previousRow = 0; previousRow < row; previousRow++) { y += rowHeights[previousRow] + spacing; } ArrangeChildInCell(children[index].get(), Rect( padding.Left + static_cast(column) * (columnWidth + spacing), y, columnWidth, rowHeights[row] )); } ClearDirty(DirtyFlags::Layout); } }