134 lines
4.3 KiB
C++
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
|