Files
XCEngine/engine/include/XCEngine/UI/Layout/UITabStripLayout.h

134 lines
4.3 KiB
C++

#pragma once
#include <XCEngine/UI/Layout/LayoutTypes.h>
#include <algorithm>
#include <cstddef>
#include <vector>
namespace XCEngine {
namespace UI {
namespace Layout {
struct UITabStripMetrics {
float headerHeight = 34.0f;
float tabMinWidth = 72.0f;
float tabHorizontalPadding = 12.0f;
float tabGap = 1.0f;
};
struct UITabStripMeasureItem {
float desiredHeaderLabelWidth = 0.0f;
UISize desiredContentSize = {};
UISize minimumContentSize = {};
};
struct UITabStripMeasureResult {
UISize desiredSize = {};
UISize minimumSize = {};
};
struct UITabStripLayoutResult {
UIRect headerRect = {};
UIRect contentRect = {};
std::vector<UIRect> tabHeaderRects = {};
};
inline float MeasureUITabStripHeaderWidth(
float labelWidth,
const UITabStripMetrics& metrics) {
return (std::max)(
metrics.tabMinWidth,
(std::max)(0.0f, labelWidth) + (std::max)(0.0f, metrics.tabHorizontalPadding) * 2.0f);
}
inline UITabStripMeasureResult MeasureUITabStrip(
const std::vector<UITabStripMeasureItem>& items,
const UITabStripMetrics& metrics) {
UITabStripMeasureResult result = {};
const float gap = (std::max)(0.0f, metrics.tabGap);
float desiredHeaderWidth = 0.0f;
float minimumHeaderWidth = 0.0f;
float desiredContentWidth = 0.0f;
float desiredContentHeight = 0.0f;
float minimumContentWidth = 0.0f;
float minimumContentHeight = 0.0f;
for (std::size_t index = 0; index < items.size(); ++index) {
const UITabStripMeasureItem& item = items[index];
desiredHeaderWidth += MeasureUITabStripHeaderWidth(
item.desiredHeaderLabelWidth,
metrics);
minimumHeaderWidth += metrics.tabMinWidth;
if (index > 0u) {
desiredHeaderWidth += gap;
minimumHeaderWidth += gap;
}
desiredContentWidth = (std::max)(desiredContentWidth, item.desiredContentSize.width);
desiredContentHeight = (std::max)(desiredContentHeight, item.desiredContentSize.height);
minimumContentWidth = (std::max)(minimumContentWidth, item.minimumContentSize.width);
minimumContentHeight = (std::max)(minimumContentHeight, item.minimumContentSize.height);
}
const float headerHeight = items.empty() ? 0.0f : (std::max)(0.0f, metrics.headerHeight);
result.desiredSize = UISize(
(std::max)(desiredHeaderWidth, desiredContentWidth),
headerHeight + desiredContentHeight);
result.minimumSize = UISize(
(std::max)(minimumHeaderWidth, minimumContentWidth),
headerHeight + minimumContentHeight);
return result;
}
inline UITabStripLayoutResult ArrangeUITabStrip(
const UIRect& bounds,
const std::vector<float>& desiredHeaderWidths,
const UITabStripMetrics& metrics) {
UITabStripLayoutResult result = {};
const float headerHeight = desiredHeaderWidths.empty()
? 0.0f
: (std::min)((std::max)(0.0f, metrics.headerHeight), bounds.height);
result.headerRect = UIRect(bounds.x, bounds.y, bounds.width, headerHeight);
result.contentRect = UIRect(
bounds.x,
bounds.y + headerHeight,
bounds.width,
(std::max)(0.0f, bounds.height - headerHeight));
result.tabHeaderRects.resize(desiredHeaderWidths.size());
if (desiredHeaderWidths.empty()) {
return result;
}
const float gap = (std::max)(0.0f, metrics.tabGap);
const float totalGapWidth = gap * static_cast<float>(desiredHeaderWidths.size() - 1u);
const float availableTabsWidth = (std::max)(0.0f, bounds.width - totalGapWidth);
float totalDesiredWidth = 0.0f;
for (float width : desiredHeaderWidths) {
totalDesiredWidth += (std::max)(0.0f, width);
}
const float scale = totalDesiredWidth > 0.0f && totalDesiredWidth > availableTabsWidth
? availableTabsWidth / totalDesiredWidth
: 1.0f;
float cursorX = bounds.x;
for (std::size_t index = 0; index < desiredHeaderWidths.size(); ++index) {
const float width = totalDesiredWidth > 0.0f
? (std::max)(0.0f, desiredHeaderWidths[index]) * scale
: availableTabsWidth / static_cast<float>(desiredHeaderWidths.size());
result.tabHeaderRects[index] = UIRect(cursorX, bounds.y, width, headerHeight);
cursorX += width + gap;
}
return result;
}
} // namespace Layout
} // namespace UI
} // namespace XCEngine