2026-04-10 00:41:28 +08:00
|
|
|
#include <XCEditor/Collections/UIEditorTabStrip.h>
|
2026-04-07 01:42:02 +08:00
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <utility>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
namespace XCEngine::UI::Editor::Widgets {
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
using ::XCEngine::UI::UIColor;
|
|
|
|
|
using ::XCEngine::UI::UIDrawList;
|
|
|
|
|
using ::XCEngine::UI::UIPoint;
|
|
|
|
|
using ::XCEngine::UI::UIRect;
|
|
|
|
|
using ::XCEngine::UI::Layout::ArrangeUITabStrip;
|
|
|
|
|
using ::XCEngine::UI::Layout::MeasureUITabStripHeaderWidth;
|
|
|
|
|
|
2026-04-10 21:05:07 +08:00
|
|
|
constexpr float kTabRounding = 0.0f;
|
|
|
|
|
constexpr float kStripRounding = 0.0f;
|
2026-04-11 17:07:37 +08:00
|
|
|
constexpr float kHeaderFontSize = 13.0f;
|
2026-04-07 01:42:02 +08:00
|
|
|
|
|
|
|
|
float ClampNonNegative(float value) {
|
|
|
|
|
return (std::max)(value, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsPointInsideRect(
|
|
|
|
|
const UIRect& rect,
|
|
|
|
|
const UIPoint& point) {
|
|
|
|
|
return point.x >= rect.x &&
|
|
|
|
|
point.x <= rect.x + rect.width &&
|
|
|
|
|
point.y >= rect.y &&
|
|
|
|
|
point.y <= rect.y + rect.height;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-10 00:41:28 +08:00
|
|
|
float ResolveStripRounding(const UIEditorTabStripMetrics& metrics) {
|
2026-04-10 21:05:07 +08:00
|
|
|
(void)metrics;
|
|
|
|
|
return kStripRounding;
|
2026-04-10 00:41:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float ResolveTabRounding(const UIEditorTabStripMetrics& metrics) {
|
2026-04-10 21:05:07 +08:00
|
|
|
(void)metrics;
|
|
|
|
|
return kTabRounding;
|
2026-04-10 00:41:28 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-07 01:42:02 +08:00
|
|
|
std::size_t ResolveSelectedIndex(
|
|
|
|
|
std::size_t itemCount,
|
|
|
|
|
std::size_t selectedIndex) {
|
|
|
|
|
if (itemCount == 0u) {
|
|
|
|
|
return UIEditorTabStripInvalidIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedIndex == UIEditorTabStripInvalidIndex || selectedIndex >= itemCount) {
|
|
|
|
|
return 0u;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return selectedIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float ResolveEstimatedLabelWidth(
|
|
|
|
|
const UIEditorTabStripItem& item,
|
|
|
|
|
const UIEditorTabStripMetrics& metrics) {
|
|
|
|
|
if (item.desiredHeaderLabelWidth > 0.0f) {
|
|
|
|
|
return item.desiredHeaderLabelWidth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return static_cast<float>(item.title.size()) * ClampNonNegative(metrics.estimatedGlyphWidth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float ResolveTabTextTop(
|
|
|
|
|
const UIRect& rect,
|
|
|
|
|
const UIEditorTabStripMetrics& metrics) {
|
|
|
|
|
return rect.y + (std::max)(0.0f, (rect.height - kHeaderFontSize) * 0.5f) + metrics.labelInsetY;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 17:37:13 +08:00
|
|
|
float ResolveTabTextLeft(
|
|
|
|
|
const UIRect& rect,
|
|
|
|
|
const UIEditorTabStripItem& item,
|
|
|
|
|
const UIEditorTabStripMetrics& metrics) {
|
|
|
|
|
const float padding =
|
|
|
|
|
(std::max)(
|
|
|
|
|
ClampNonNegative(metrics.layoutMetrics.tabHorizontalPadding),
|
|
|
|
|
ClampNonNegative(metrics.labelInsetX));
|
|
|
|
|
const float availableLeft = rect.x + padding;
|
|
|
|
|
const float availableRight = rect.x + rect.width - padding;
|
|
|
|
|
const float availableWidth = (std::max)(availableRight - availableLeft, 0.0f);
|
|
|
|
|
const float labelWidth = ResolveEstimatedLabelWidth(item, metrics);
|
|
|
|
|
return availableLeft + (std::max)(0.0f, (availableWidth - labelWidth) * 0.5f);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-07 01:42:02 +08:00
|
|
|
UIColor ResolveTabFillColor(
|
|
|
|
|
bool selected,
|
|
|
|
|
bool hovered,
|
|
|
|
|
const UIEditorTabStripPalette& palette) {
|
|
|
|
|
if (selected) {
|
|
|
|
|
return palette.tabSelectedColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hovered) {
|
|
|
|
|
return palette.tabHoveredColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return palette.tabColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UIColor ResolveTabBorderColor(
|
|
|
|
|
bool selected,
|
|
|
|
|
bool hovered,
|
|
|
|
|
bool focused,
|
|
|
|
|
const UIEditorTabStripPalette& palette) {
|
|
|
|
|
if (selected) {
|
2026-04-11 17:07:37 +08:00
|
|
|
(void)focused;
|
|
|
|
|
return palette.tabSelectedBorderColor;
|
2026-04-07 01:42:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hovered) {
|
|
|
|
|
return palette.tabHoveredBorderColor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return palette.tabBorderColor;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 17:07:37 +08:00
|
|
|
float ResolveTabBorderThickness(bool selected, bool focused, const UIEditorTabStripMetrics& metrics) {
|
2026-04-07 01:42:02 +08:00
|
|
|
if (selected) {
|
2026-04-11 17:07:37 +08:00
|
|
|
(void)focused;
|
|
|
|
|
return metrics.selectedBorderThickness;
|
2026-04-07 01:42:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return metrics.baseBorderThickness;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 17:07:37 +08:00
|
|
|
void AppendHeaderContentSeparator(
|
|
|
|
|
UIDrawList& drawList,
|
|
|
|
|
const UIEditorTabStripLayout& layout,
|
|
|
|
|
const UIEditorTabStripPalette& palette,
|
2026-04-07 01:42:02 +08:00
|
|
|
const UIEditorTabStripMetrics& metrics) {
|
2026-04-11 17:07:37 +08:00
|
|
|
if (layout.headerRect.width <= 0.0f ||
|
|
|
|
|
layout.headerRect.height <= 0.0f ||
|
|
|
|
|
layout.contentRect.height <= 0.0f) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float thickness = (std::max)(ClampNonNegative(metrics.baseBorderThickness), 1.0f);
|
|
|
|
|
const float separatorY = layout.contentRect.y;
|
|
|
|
|
const float separatorLeft = layout.headerRect.x;
|
|
|
|
|
const float separatorRight = layout.headerRect.x + layout.headerRect.width;
|
|
|
|
|
|
|
|
|
|
if (layout.selectedIndex == UIEditorTabStripInvalidIndex ||
|
|
|
|
|
layout.selectedIndex >= layout.tabHeaderRects.size()) {
|
|
|
|
|
drawList.AddFilledRect(
|
|
|
|
|
UIRect(
|
|
|
|
|
separatorLeft,
|
|
|
|
|
separatorY,
|
|
|
|
|
(std::max)(separatorRight - separatorLeft, 0.0f),
|
|
|
|
|
thickness),
|
|
|
|
|
palette.headerContentSeparatorColor);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UIRect& selectedRect = layout.tabHeaderRects[layout.selectedIndex];
|
|
|
|
|
const float gapLeft = (std::max)(separatorLeft, selectedRect.x + 1.0f);
|
|
|
|
|
const float gapRight = (std::min)(separatorRight, selectedRect.x + selectedRect.width - 1.0f);
|
|
|
|
|
|
|
|
|
|
if (gapLeft > separatorLeft) {
|
|
|
|
|
drawList.AddFilledRect(
|
|
|
|
|
UIRect(
|
|
|
|
|
separatorLeft,
|
|
|
|
|
separatorY,
|
|
|
|
|
(std::max)(gapLeft - separatorLeft, 0.0f),
|
|
|
|
|
thickness),
|
|
|
|
|
palette.headerContentSeparatorColor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gapRight < separatorRight) {
|
|
|
|
|
drawList.AddFilledRect(
|
|
|
|
|
UIRect(
|
|
|
|
|
gapRight,
|
|
|
|
|
separatorY,
|
|
|
|
|
(std::max)(separatorRight - gapRight, 0.0f),
|
|
|
|
|
thickness),
|
|
|
|
|
palette.headerContentSeparatorColor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AppendSelectedTabBottomBorderMask(
|
|
|
|
|
UIDrawList& drawList,
|
|
|
|
|
const UIRect& rect,
|
|
|
|
|
float thickness,
|
|
|
|
|
const UIEditorTabStripPalette& palette) {
|
|
|
|
|
if (rect.width <= 0.0f || rect.height <= 0.0f || thickness <= 0.0f) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const float maskHeight = (std::max)(1.0f, thickness + 1.0f);
|
|
|
|
|
drawList.AddFilledRect(
|
|
|
|
|
UIRect(
|
|
|
|
|
rect.x + 1.0f,
|
|
|
|
|
rect.y + rect.height - maskHeight,
|
|
|
|
|
(std::max)(rect.width - 2.0f, 0.0f),
|
|
|
|
|
maskHeight + 1.0f),
|
|
|
|
|
palette.contentBackgroundColor,
|
|
|
|
|
0.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UIEditorTabStripInsertionPreview BuildInsertionPreview(
|
|
|
|
|
const UIEditorTabStripLayout& layout,
|
|
|
|
|
const UIEditorTabStripState& state,
|
|
|
|
|
const UIEditorTabStripMetrics& metrics) {
|
|
|
|
|
UIEditorTabStripInsertionPreview preview = {};
|
|
|
|
|
if (!state.reorder.dragging ||
|
|
|
|
|
state.reorder.previewInsertionIndex == UIEditorTabStripInvalidIndex ||
|
|
|
|
|
layout.tabHeaderRects.empty() ||
|
|
|
|
|
state.reorder.previewInsertionIndex > layout.tabHeaderRects.size() ||
|
|
|
|
|
layout.headerRect.height <= 0.0f) {
|
|
|
|
|
return preview;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float indicatorX = layout.tabHeaderRects.front().x;
|
|
|
|
|
if (state.reorder.previewInsertionIndex >= layout.tabHeaderRects.size()) {
|
|
|
|
|
const UIRect& lastRect = layout.tabHeaderRects.back();
|
|
|
|
|
indicatorX = lastRect.x + lastRect.width;
|
|
|
|
|
} else {
|
|
|
|
|
indicatorX = layout.tabHeaderRects[state.reorder.previewInsertionIndex].x;
|
2026-04-07 01:42:02 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-11 17:07:37 +08:00
|
|
|
const float thickness = (std::max)(ClampNonNegative(metrics.reorderPreviewThickness), 1.0f);
|
|
|
|
|
const float insetY = ClampNonNegative(metrics.reorderPreviewInsetY);
|
|
|
|
|
const float indicatorHeight = (std::max)(layout.headerRect.height - insetY * 2.0f, thickness);
|
|
|
|
|
|
|
|
|
|
preview.visible = true;
|
|
|
|
|
preview.insertionIndex = state.reorder.previewInsertionIndex;
|
|
|
|
|
preview.indicatorRect = UIRect(
|
|
|
|
|
indicatorX - thickness * 0.5f,
|
|
|
|
|
layout.headerRect.y + insetY,
|
|
|
|
|
thickness,
|
|
|
|
|
indicatorHeight);
|
|
|
|
|
return preview;
|
2026-04-07 01:42:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
float ResolveUIEditorTabStripDesiredHeaderLabelWidth(
|
|
|
|
|
const UIEditorTabStripItem& item,
|
|
|
|
|
const UIEditorTabStripMetrics& metrics) {
|
|
|
|
|
const float labelWidth = ResolveEstimatedLabelWidth(item, metrics);
|
|
|
|
|
const float horizontalPadding = ClampNonNegative(metrics.layoutMetrics.tabHorizontalPadding);
|
|
|
|
|
const float extraLeftInset = (std::max)(ClampNonNegative(metrics.labelInsetX) - horizontalPadding, 0.0f);
|
2026-04-11 17:07:37 +08:00
|
|
|
(void)item;
|
|
|
|
|
return labelWidth + extraLeftInset;
|
2026-04-07 01:42:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::size_t ResolveUIEditorTabStripSelectedIndex(
|
|
|
|
|
const std::vector<UIEditorTabStripItem>& items,
|
|
|
|
|
std::string_view selectedTabId,
|
|
|
|
|
std::size_t fallbackIndex) {
|
|
|
|
|
for (std::size_t index = 0; index < items.size(); ++index) {
|
|
|
|
|
if (items[index].tabId == selectedTabId) {
|
|
|
|
|
return index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (items.empty()) {
|
|
|
|
|
return UIEditorTabStripInvalidIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ResolveSelectedIndex(items.size(), fallbackIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UIEditorTabStripLayout BuildUIEditorTabStripLayout(
|
|
|
|
|
const UIRect& bounds,
|
|
|
|
|
const std::vector<UIEditorTabStripItem>& items,
|
|
|
|
|
const UIEditorTabStripState& state,
|
|
|
|
|
const UIEditorTabStripMetrics& metrics) {
|
|
|
|
|
UIEditorTabStripLayout layout = {};
|
|
|
|
|
layout.bounds = UIRect(
|
|
|
|
|
bounds.x,
|
|
|
|
|
bounds.y,
|
|
|
|
|
ClampNonNegative(bounds.width),
|
|
|
|
|
ClampNonNegative(bounds.height));
|
|
|
|
|
layout.selectedIndex = ResolveSelectedIndex(items.size(), state.selectedIndex);
|
|
|
|
|
|
|
|
|
|
std::vector<float> desiredHeaderWidths = {};
|
|
|
|
|
desiredHeaderWidths.reserve(items.size());
|
|
|
|
|
for (const UIEditorTabStripItem& item : items) {
|
|
|
|
|
desiredHeaderWidths.push_back(
|
|
|
|
|
MeasureUITabStripHeaderWidth(
|
|
|
|
|
ResolveUIEditorTabStripDesiredHeaderLabelWidth(item, metrics),
|
|
|
|
|
metrics.layoutMetrics));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ::XCEngine::UI::Layout::UITabStripLayoutResult arranged =
|
|
|
|
|
ArrangeUITabStrip(layout.bounds, desiredHeaderWidths, metrics.layoutMetrics);
|
|
|
|
|
layout.headerRect = arranged.headerRect;
|
|
|
|
|
layout.contentRect = arranged.contentRect;
|
|
|
|
|
layout.tabHeaderRects = arranged.tabHeaderRects;
|
|
|
|
|
layout.closeButtonRects.resize(items.size());
|
|
|
|
|
layout.showCloseButtons.resize(items.size(), false);
|
2026-04-11 17:07:37 +08:00
|
|
|
layout.insertionPreview = BuildInsertionPreview(layout, state, metrics);
|
2026-04-07 01:42:02 +08:00
|
|
|
return layout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UIEditorTabStripHitTarget HitTestUIEditorTabStrip(
|
|
|
|
|
const UIEditorTabStripLayout& layout,
|
|
|
|
|
const UIEditorTabStripState&,
|
|
|
|
|
const UIPoint& point) {
|
|
|
|
|
UIEditorTabStripHitTarget target = {};
|
|
|
|
|
|
|
|
|
|
if (!IsPointInsideRect(layout.bounds, point)) {
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (std::size_t index = 0; index < layout.tabHeaderRects.size(); ++index) {
|
|
|
|
|
if (IsPointInsideRect(layout.tabHeaderRects[index], point)) {
|
|
|
|
|
target.kind = UIEditorTabStripHitTargetKind::Tab;
|
|
|
|
|
target.index = index;
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsPointInsideRect(layout.headerRect, point)) {
|
|
|
|
|
target.kind = UIEditorTabStripHitTargetKind::HeaderBackground;
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IsPointInsideRect(layout.contentRect, point)) {
|
|
|
|
|
target.kind = UIEditorTabStripHitTargetKind::Content;
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return target;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AppendUIEditorTabStripBackground(
|
|
|
|
|
UIDrawList& drawList,
|
|
|
|
|
const UIEditorTabStripLayout& layout,
|
|
|
|
|
const UIEditorTabStripState& state,
|
|
|
|
|
const UIEditorTabStripPalette& palette,
|
|
|
|
|
const UIEditorTabStripMetrics& metrics) {
|
2026-04-10 00:41:28 +08:00
|
|
|
const float stripRounding = ResolveStripRounding(metrics);
|
|
|
|
|
const float tabRounding = ResolveTabRounding(metrics);
|
|
|
|
|
drawList.AddFilledRect(layout.bounds, palette.stripBackgroundColor, stripRounding);
|
2026-04-07 01:42:02 +08:00
|
|
|
if (layout.contentRect.height > 0.0f) {
|
2026-04-10 00:41:28 +08:00
|
|
|
drawList.AddFilledRect(layout.contentRect, palette.contentBackgroundColor, stripRounding);
|
2026-04-07 01:42:02 +08:00
|
|
|
}
|
|
|
|
|
if (layout.headerRect.height > 0.0f) {
|
2026-04-10 00:41:28 +08:00
|
|
|
drawList.AddFilledRect(layout.headerRect, palette.headerBackgroundColor, stripRounding);
|
2026-04-07 01:42:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (std::size_t index = 0; index < layout.tabHeaderRects.size(); ++index) {
|
|
|
|
|
const bool selected = layout.selectedIndex == index;
|
2026-04-11 17:07:37 +08:00
|
|
|
const bool hovered = state.hoveredIndex == index;
|
2026-04-07 01:42:02 +08:00
|
|
|
drawList.AddFilledRect(
|
|
|
|
|
layout.tabHeaderRects[index],
|
|
|
|
|
ResolveTabFillColor(selected, hovered, palette),
|
2026-04-10 00:41:28 +08:00
|
|
|
tabRounding);
|
2026-04-07 01:42:02 +08:00
|
|
|
drawList.AddRectOutline(
|
|
|
|
|
layout.tabHeaderRects[index],
|
|
|
|
|
ResolveTabBorderColor(selected, hovered, state.focused, palette),
|
|
|
|
|
ResolveTabBorderThickness(selected, state.focused, metrics),
|
2026-04-10 00:41:28 +08:00
|
|
|
tabRounding);
|
2026-04-11 17:07:37 +08:00
|
|
|
if (selected) {
|
|
|
|
|
AppendSelectedTabBottomBorderMask(
|
|
|
|
|
drawList,
|
|
|
|
|
layout.tabHeaderRects[index],
|
|
|
|
|
ResolveTabBorderThickness(selected, state.focused, metrics),
|
|
|
|
|
palette);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (layout.insertionPreview.visible) {
|
|
|
|
|
drawList.AddFilledRect(
|
|
|
|
|
layout.insertionPreview.indicatorRect,
|
|
|
|
|
palette.reorderPreviewColor,
|
|
|
|
|
0.0f);
|
2026-04-07 01:42:02 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AppendUIEditorTabStripForeground(
|
|
|
|
|
UIDrawList& drawList,
|
|
|
|
|
const UIEditorTabStripLayout& layout,
|
|
|
|
|
const std::vector<UIEditorTabStripItem>& items,
|
|
|
|
|
const UIEditorTabStripState& state,
|
|
|
|
|
const UIEditorTabStripPalette& palette,
|
|
|
|
|
const UIEditorTabStripMetrics& metrics) {
|
2026-04-11 17:07:37 +08:00
|
|
|
AppendHeaderContentSeparator(drawList, layout, palette, metrics);
|
2026-04-11 17:37:13 +08:00
|
|
|
const float horizontalPadding = (std::max)(
|
2026-04-07 01:42:02 +08:00
|
|
|
ClampNonNegative(metrics.layoutMetrics.tabHorizontalPadding),
|
|
|
|
|
ClampNonNegative(metrics.labelInsetX));
|
|
|
|
|
|
|
|
|
|
for (std::size_t index = 0; index < items.size() && index < layout.tabHeaderRects.size(); ++index) {
|
|
|
|
|
const UIRect& tabRect = layout.tabHeaderRects[index];
|
|
|
|
|
const bool selected = layout.selectedIndex == index;
|
2026-04-11 17:07:37 +08:00
|
|
|
const bool hovered = state.hoveredIndex == index;
|
2026-04-11 17:37:13 +08:00
|
|
|
const float clipLeft = tabRect.x + horizontalPadding;
|
|
|
|
|
const float textLeft = ResolveTabTextLeft(tabRect, items[index], metrics);
|
|
|
|
|
const float textRight = tabRect.x + tabRect.width - horizontalPadding;
|
2026-04-07 01:42:02 +08:00
|
|
|
|
2026-04-11 17:37:13 +08:00
|
|
|
if (textRight > clipLeft) {
|
2026-04-07 01:42:02 +08:00
|
|
|
const UIRect clipRect(
|
2026-04-11 17:37:13 +08:00
|
|
|
clipLeft,
|
2026-04-07 01:42:02 +08:00
|
|
|
tabRect.y,
|
2026-04-11 17:37:13 +08:00
|
|
|
textRight - clipLeft,
|
2026-04-07 01:42:02 +08:00
|
|
|
tabRect.height);
|
|
|
|
|
drawList.PushClipRect(clipRect, true);
|
|
|
|
|
drawList.AddText(
|
|
|
|
|
UIPoint(textLeft, ResolveTabTextTop(tabRect, metrics)),
|
|
|
|
|
items[index].title,
|
|
|
|
|
selected || hovered ? palette.textPrimary : palette.textSecondary,
|
|
|
|
|
kHeaderFontSize);
|
|
|
|
|
drawList.PopClipRect();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AppendUIEditorTabStrip(
|
|
|
|
|
UIDrawList& drawList,
|
|
|
|
|
const UIRect& bounds,
|
|
|
|
|
const std::vector<UIEditorTabStripItem>& items,
|
|
|
|
|
const UIEditorTabStripState& state,
|
|
|
|
|
const UIEditorTabStripPalette& palette,
|
|
|
|
|
const UIEditorTabStripMetrics& metrics) {
|
|
|
|
|
const UIEditorTabStripLayout layout =
|
|
|
|
|
BuildUIEditorTabStripLayout(bounds, items, state, metrics);
|
|
|
|
|
AppendUIEditorTabStripBackground(drawList, layout, state, palette, metrics);
|
|
|
|
|
AppendUIEditorTabStripForeground(drawList, layout, items, state, palette, metrics);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace XCEngine::UI::Editor::Widgets
|