Build XCEditor viewport slot shell foundation

This commit is contained in:
2026-04-07 04:23:33 +08:00
parent 8eeb7af56e
commit 7f0d1f0b08
11 changed files with 1807 additions and 0 deletions

View File

@@ -30,6 +30,7 @@ add_library(XCUIEditorLib STATIC
src/Widgets/UIEditorPanelFrame.cpp
src/Widgets/UIEditorStatusBar.cpp
src/Widgets/UIEditorTabStrip.cpp
src/Widgets/UIEditorViewportSlot.cpp
)
target_include_directories(XCUIEditorLib

View File

@@ -0,0 +1,203 @@
#pragma once
#include <XCEngine/UI/DrawData.h>
#include <XCEditor/Widgets/UIEditorStatusBar.h>
#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace XCEngine::UI::Editor::Widgets {
inline constexpr std::size_t UIEditorViewportSlotInvalidIndex =
static_cast<std::size_t>(-1);
enum class UIEditorViewportSlotToolSlot : std::uint8_t {
Leading = 0,
Trailing
};
enum class UIEditorViewportSlotHitTargetKind : std::uint8_t {
None = 0,
TopBar,
Title,
ToolItem,
Surface,
BottomBar,
StatusSegment,
StatusSeparator
};
struct UIEditorViewportSlotFrame {
::XCEngine::UI::UITextureHandle texture = {};
::XCEngine::UI::UISize requestedSize = {};
::XCEngine::UI::UISize presentedSize = {};
bool hasTexture = false;
std::string statusText = {};
};
struct UIEditorViewportSlotChrome {
std::string_view title = {};
std::string_view subtitle = {};
bool showTopBar = true;
bool showBottomBar = true;
float topBarHeight = 40.0f;
float bottomBarHeight = 28.0f;
};
struct UIEditorViewportSlotToolItem {
std::string itemId = {};
std::string label = {};
UIEditorViewportSlotToolSlot slot = UIEditorViewportSlotToolSlot::Trailing;
bool enabled = true;
bool selected = false;
float desiredWidth = 0.0f;
};
struct UIEditorViewportSlotState {
bool focused = false;
bool surfaceHovered = false;
bool surfaceActive = false;
bool inputCaptured = false;
std::size_t hoveredToolIndex = UIEditorViewportSlotInvalidIndex;
std::size_t activeToolIndex = UIEditorViewportSlotInvalidIndex;
UIEditorStatusBarState statusBarState = {};
};
struct UIEditorViewportSlotMetrics {
float cornerRounding = 10.0f;
float outerBorderThickness = 1.0f;
float focusedBorderThickness = 1.5f;
float topBarPaddingX = 12.0f;
float topBarPaddingY = 7.0f;
float toolPaddingX = 10.0f;
float toolGap = 6.0f;
float toolCornerRounding = 6.0f;
float titleGap = 10.0f;
float titleInsetY = 10.0f;
float subtitleInsetY = 25.0f;
float estimatedGlyphWidth = 7.0f;
float surfaceInset = 0.0f;
float surfaceBorderThickness = 1.0f;
float focusedSurfaceBorderThickness = 1.5f;
};
struct UIEditorViewportSlotPalette {
::XCEngine::UI::UIColor frameColor =
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
::XCEngine::UI::UIColor topBarColor =
::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
::XCEngine::UI::UIColor surfaceColor =
::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
::XCEngine::UI::UIColor surfaceHoverOverlayColor =
::XCEngine::UI::UIColor(0.22f, 0.22f, 0.22f, 0.24f);
::XCEngine::UI::UIColor surfaceActiveOverlayColor =
::XCEngine::UI::UIColor(0.34f, 0.34f, 0.34f, 0.18f);
::XCEngine::UI::UIColor captureOverlayColor =
::XCEngine::UI::UIColor(0.70f, 0.70f, 0.70f, 0.10f);
::XCEngine::UI::UIColor borderColor =
::XCEngine::UI::UIColor(0.28f, 0.28f, 0.28f, 1.0f);
::XCEngine::UI::UIColor focusedBorderColor =
::XCEngine::UI::UIColor(0.84f, 0.84f, 0.84f, 1.0f);
::XCEngine::UI::UIColor surfaceBorderColor =
::XCEngine::UI::UIColor(0.22f, 0.22f, 0.22f, 1.0f);
::XCEngine::UI::UIColor surfaceHoveredBorderColor =
::XCEngine::UI::UIColor(0.44f, 0.44f, 0.44f, 1.0f);
::XCEngine::UI::UIColor surfaceActiveBorderColor =
::XCEngine::UI::UIColor(0.64f, 0.64f, 0.64f, 1.0f);
::XCEngine::UI::UIColor surfaceCapturedBorderColor =
::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
::XCEngine::UI::UIColor toolColor =
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
::XCEngine::UI::UIColor toolHoveredColor =
::XCEngine::UI::UIColor(0.31f, 0.31f, 0.31f, 1.0f);
::XCEngine::UI::UIColor toolSelectedColor =
::XCEngine::UI::UIColor(0.39f, 0.39f, 0.39f, 1.0f);
::XCEngine::UI::UIColor toolDisabledColor =
::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
::XCEngine::UI::UIColor toolBorderColor =
::XCEngine::UI::UIColor(0.40f, 0.40f, 0.40f, 1.0f);
::XCEngine::UI::UIColor textPrimary =
::XCEngine::UI::UIColor(0.94f, 0.94f, 0.94f, 1.0f);
::XCEngine::UI::UIColor textSecondary =
::XCEngine::UI::UIColor(0.76f, 0.76f, 0.76f, 1.0f);
::XCEngine::UI::UIColor textMuted =
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f);
::XCEngine::UI::UIColor imageTint =
::XCEngine::UI::UIColor(1.0f, 1.0f, 1.0f, 1.0f);
UIEditorStatusBarPalette statusBarPalette = {};
};
struct UIEditorViewportSlotLayout {
::XCEngine::UI::UIRect bounds = {};
::XCEngine::UI::UIRect topBarRect = {};
::XCEngine::UI::UIRect titleRect = {};
::XCEngine::UI::UIRect subtitleRect = {};
::XCEngine::UI::UIRect toolbarLeadingRect = {};
::XCEngine::UI::UIRect toolbarTrailingRect = {};
::XCEngine::UI::UIRect surfaceRect = {};
::XCEngine::UI::UIRect textureRect = {};
::XCEngine::UI::UIRect inputRect = {};
::XCEngine::UI::UIRect bottomBarRect = {};
std::vector<::XCEngine::UI::UIRect> toolItemRects = {};
UIEditorStatusBarLayout statusBarLayout = {};
::XCEngine::UI::UISize requestedSurfaceSize = {};
bool hasTopBar = false;
bool hasBottomBar = false;
};
struct UIEditorViewportSlotHitTarget {
UIEditorViewportSlotHitTargetKind kind = UIEditorViewportSlotHitTargetKind::None;
std::size_t index = UIEditorViewportSlotInvalidIndex;
};
float ResolveUIEditorViewportSlotDesiredToolWidth(
const UIEditorViewportSlotToolItem& item,
const UIEditorViewportSlotMetrics& metrics = {});
UIEditorViewportSlotLayout BuildUIEditorViewportSlotLayout(
const ::XCEngine::UI::UIRect& bounds,
const UIEditorViewportSlotChrome& chrome,
const UIEditorViewportSlotFrame& frame,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotMetrics& metrics = {});
UIEditorViewportSlotHitTarget HitTestUIEditorViewportSlot(
const UIEditorViewportSlotLayout& layout,
const ::XCEngine::UI::UIPoint& point);
void AppendUIEditorViewportSlotBackground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorViewportSlotLayout& layout,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotPalette& palette = {},
const UIEditorViewportSlotMetrics& metrics = {});
void AppendUIEditorViewportSlotForeground(
::XCEngine::UI::UIDrawList& drawList,
const UIEditorViewportSlotLayout& layout,
const UIEditorViewportSlotChrome& chrome,
const UIEditorViewportSlotFrame& frame,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotPalette& palette = {},
const UIEditorViewportSlotMetrics& metrics = {});
void AppendUIEditorViewportSlot(
::XCEngine::UI::UIDrawList& drawList,
const ::XCEngine::UI::UIRect& bounds,
const UIEditorViewportSlotChrome& chrome,
const UIEditorViewportSlotFrame& frame,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotPalette& palette = {},
const UIEditorViewportSlotMetrics& metrics = {});
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,532 @@
#include <XCEditor/Widgets/UIEditorViewportSlot.h>
#include <algorithm>
#include <string>
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::UISize;
bool ContainsPoint(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;
}
bool HasArea(const UIRect& rect) {
return rect.width > 0.0f && rect.height > 0.0f;
}
float ClampNonNegative(float value) {
return (std::max)(value, 0.0f);
}
UIRect InsetRect(const UIRect& rect, float inset) {
const float clampedInset = ClampNonNegative(inset);
return UIRect(
rect.x + clampedInset,
rect.y + clampedInset,
(std::max)(rect.width - clampedInset * 2.0f, 0.0f),
(std::max)(rect.height - clampedInset * 2.0f, 0.0f));
}
UISize ResolveFrameAspectSize(const UIEditorViewportSlotFrame& frame, const UISize& fallback) {
if (frame.presentedSize.width > 0.0f && frame.presentedSize.height > 0.0f) {
return frame.presentedSize;
}
if (frame.texture.IsValid()) {
return UISize(
static_cast<float>(frame.texture.width),
static_cast<float>(frame.texture.height));
}
if (frame.requestedSize.width > 0.0f && frame.requestedSize.height > 0.0f) {
return frame.requestedSize;
}
return fallback;
}
UIRect FitRectToAspect(const UIRect& container, const UISize& size) {
if (!HasArea(container) || size.width <= 0.0f || size.height <= 0.0f) {
return container;
}
const float containerAspect = container.width / container.height;
const float frameAspect = size.width / size.height;
if (frameAspect <= 0.0f) {
return container;
}
if (frameAspect >= containerAspect) {
const float fittedHeight = container.width / frameAspect;
return UIRect(
container.x,
container.y + (container.height - fittedHeight) * 0.5f,
container.width,
fittedHeight);
}
const float fittedWidth = container.height * frameAspect;
return UIRect(
container.x + (container.width - fittedWidth) * 0.5f,
container.y,
fittedWidth,
container.height);
}
UIColor ResolveToolFillColor(
const UIEditorViewportSlotToolItem& item,
bool hovered,
bool active,
const UIEditorViewportSlotPalette& palette) {
if (!item.enabled) {
return palette.toolDisabledColor;
}
if (active || item.selected) {
return palette.toolSelectedColor;
}
if (hovered) {
return palette.toolHoveredColor;
}
return palette.toolColor;
}
UIColor ResolveSurfaceBorderColor(
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotPalette& palette) {
if (state.inputCaptured) {
return palette.surfaceCapturedBorderColor;
}
if (state.surfaceActive) {
return palette.surfaceActiveBorderColor;
}
if (state.surfaceHovered) {
return palette.surfaceHoveredBorderColor;
}
return palette.surfaceBorderColor;
}
float ResolveSurfaceBorderThickness(
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotMetrics& metrics) {
if (state.inputCaptured || state.focused) {
return metrics.focusedSurfaceBorderThickness;
}
return metrics.surfaceBorderThickness;
}
} // namespace
float ResolveUIEditorViewportSlotDesiredToolWidth(
const UIEditorViewportSlotToolItem& item,
const UIEditorViewportSlotMetrics& metrics) {
if (item.desiredWidth > 0.0f) {
return item.desiredWidth;
}
return item.label.empty()
? metrics.toolPaddingX * 2.0f
: metrics.toolPaddingX * 2.0f +
static_cast<float>(item.label.size()) * metrics.estimatedGlyphWidth;
}
UIEditorViewportSlotLayout BuildUIEditorViewportSlotLayout(
const UIRect& bounds,
const UIEditorViewportSlotChrome& chrome,
const UIEditorViewportSlotFrame& frame,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotMetrics& metrics) {
UIEditorViewportSlotLayout layout = {};
layout.bounds = UIRect(
bounds.x,
bounds.y,
ClampNonNegative(bounds.width),
ClampNonNegative(bounds.height));
layout.toolItemRects.resize(toolItems.size(), UIRect{});
float remainingHeight = layout.bounds.height;
const float topBarHeight =
chrome.showTopBar
? (std::min)(ClampNonNegative(chrome.topBarHeight), remainingHeight)
: 0.0f;
remainingHeight = (std::max)(remainingHeight - topBarHeight, 0.0f);
const float bottomBarHeight =
chrome.showBottomBar
? (std::min)(ClampNonNegative(chrome.bottomBarHeight), remainingHeight)
: 0.0f;
layout.hasTopBar = topBarHeight > 0.0f;
layout.hasBottomBar = bottomBarHeight > 0.0f;
if (layout.hasTopBar) {
layout.topBarRect = UIRect(
layout.bounds.x,
layout.bounds.y,
layout.bounds.width,
topBarHeight);
}
if (layout.hasBottomBar) {
layout.bottomBarRect = UIRect(
layout.bounds.x,
layout.bounds.y + layout.bounds.height - bottomBarHeight,
layout.bounds.width,
bottomBarHeight);
layout.statusBarLayout =
BuildUIEditorStatusBarLayout(layout.bottomBarRect, statusSegments);
}
const float surfaceTop = layout.hasTopBar
? layout.topBarRect.y + layout.topBarRect.height
: layout.bounds.y;
const float surfaceBottom = layout.hasBottomBar
? layout.bottomBarRect.y
: layout.bounds.y + layout.bounds.height;
layout.surfaceRect = UIRect(
layout.bounds.x,
surfaceTop,
layout.bounds.width,
(std::max)(surfaceBottom - surfaceTop, 0.0f));
layout.inputRect = InsetRect(layout.surfaceRect, metrics.surfaceInset);
layout.requestedSurfaceSize =
UISize(layout.inputRect.width, layout.inputRect.height);
layout.textureRect = frame.hasTexture
? FitRectToAspect(
layout.inputRect,
ResolveFrameAspectSize(frame, layout.requestedSurfaceSize))
: layout.inputRect;
if (!layout.hasTopBar) {
return layout;
}
const float toolbarTop = layout.topBarRect.y + metrics.topBarPaddingY;
const float toolbarHeight =
(std::max)(layout.topBarRect.height - metrics.topBarPaddingY * 2.0f, 0.0f);
const float innerLeft = layout.topBarRect.x + metrics.topBarPaddingX;
const float innerRight =
layout.topBarRect.x + layout.topBarRect.width - metrics.topBarPaddingX;
float leadingCursor = innerLeft;
float trailingCursor = innerRight;
float leadingRight = innerLeft;
float trailingLeft = innerRight;
for (std::size_t index = 0u; index < toolItems.size(); ++index) {
const auto& item = toolItems[index];
const float itemWidth = ResolveUIEditorViewportSlotDesiredToolWidth(item, metrics);
if (item.slot != UIEditorViewportSlotToolSlot::Leading) {
continue;
}
layout.toolItemRects[index] = UIRect(
leadingCursor,
toolbarTop,
itemWidth,
toolbarHeight);
leadingCursor += itemWidth + metrics.toolGap;
leadingRight = layout.toolItemRects[index].x + layout.toolItemRects[index].width;
}
for (std::size_t reverseIndex = toolItems.size(); reverseIndex > 0u; --reverseIndex) {
const std::size_t index = reverseIndex - 1u;
const auto& item = toolItems[index];
const float itemWidth = ResolveUIEditorViewportSlotDesiredToolWidth(item, metrics);
if (item.slot != UIEditorViewportSlotToolSlot::Trailing) {
continue;
}
trailingCursor -= itemWidth;
layout.toolItemRects[index] = UIRect(
trailingCursor,
toolbarTop,
itemWidth,
toolbarHeight);
trailingLeft = trailingCursor;
trailingCursor -= metrics.toolGap;
}
if (leadingRight > innerLeft) {
layout.toolbarLeadingRect = UIRect(
innerLeft,
layout.topBarRect.y,
leadingRight - innerLeft,
layout.topBarRect.height);
}
if (trailingLeft < innerRight) {
layout.toolbarTrailingRect = UIRect(
trailingLeft,
layout.topBarRect.y,
innerRight - trailingLeft,
layout.topBarRect.height);
}
float titleLeft = innerLeft;
if (HasArea(layout.toolbarLeadingRect)) {
titleLeft = layout.toolbarLeadingRect.x + layout.toolbarLeadingRect.width + metrics.titleGap;
}
float titleRight = innerRight;
if (HasArea(layout.toolbarTrailingRect)) {
titleRight = layout.toolbarTrailingRect.x - metrics.titleGap;
}
layout.titleRect = UIRect(
titleLeft,
layout.topBarRect.y,
(std::max)(titleRight - titleLeft, 0.0f),
layout.topBarRect.height);
layout.subtitleRect = layout.titleRect;
return layout;
}
UIEditorViewportSlotHitTarget HitTestUIEditorViewportSlot(
const UIEditorViewportSlotLayout& layout,
const UIPoint& point) {
if (!ContainsPoint(layout.bounds, point)) {
return {};
}
if (layout.hasTopBar) {
for (std::size_t index = 0u; index < layout.toolItemRects.size(); ++index) {
if (HasArea(layout.toolItemRects[index]) &&
ContainsPoint(layout.toolItemRects[index], point)) {
return { UIEditorViewportSlotHitTargetKind::ToolItem, index };
}
}
if (HasArea(layout.titleRect) && ContainsPoint(layout.titleRect, point)) {
return { UIEditorViewportSlotHitTargetKind::Title, UIEditorViewportSlotInvalidIndex };
}
if (ContainsPoint(layout.topBarRect, point)) {
return { UIEditorViewportSlotHitTargetKind::TopBar, UIEditorViewportSlotInvalidIndex };
}
}
if (layout.hasBottomBar && ContainsPoint(layout.bottomBarRect, point)) {
const UIEditorStatusBarHitTarget hit =
HitTestUIEditorStatusBar(layout.statusBarLayout, point);
switch (hit.kind) {
case UIEditorStatusBarHitTargetKind::Segment:
return { UIEditorViewportSlotHitTargetKind::StatusSegment, hit.index };
case UIEditorStatusBarHitTargetKind::Separator:
return { UIEditorViewportSlotHitTargetKind::StatusSeparator, hit.index };
case UIEditorStatusBarHitTargetKind::Background:
return { UIEditorViewportSlotHitTargetKind::BottomBar, UIEditorViewportSlotInvalidIndex };
case UIEditorStatusBarHitTargetKind::None:
default:
break;
}
}
if (ContainsPoint(layout.inputRect, point) || ContainsPoint(layout.surfaceRect, point)) {
return { UIEditorViewportSlotHitTargetKind::Surface, UIEditorViewportSlotInvalidIndex };
}
return {};
}
void AppendUIEditorViewportSlotBackground(
UIDrawList& drawList,
const UIEditorViewportSlotLayout& layout,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotPalette& palette,
const UIEditorViewportSlotMetrics& metrics) {
drawList.AddFilledRect(layout.bounds, palette.frameColor, metrics.cornerRounding);
drawList.AddRectOutline(
layout.bounds,
state.focused ? palette.focusedBorderColor : palette.borderColor,
state.focused ? metrics.focusedBorderThickness : metrics.outerBorderThickness,
metrics.cornerRounding);
if (layout.hasTopBar) {
drawList.AddFilledRect(layout.topBarRect, palette.topBarColor, metrics.cornerRounding);
}
drawList.AddFilledRect(layout.surfaceRect, palette.surfaceColor);
if (state.surfaceHovered) {
drawList.AddFilledRect(layout.inputRect, palette.surfaceHoverOverlayColor);
}
if (state.surfaceActive) {
drawList.AddFilledRect(layout.inputRect, palette.surfaceActiveOverlayColor);
}
if (state.inputCaptured) {
drawList.AddFilledRect(layout.inputRect, palette.captureOverlayColor);
}
drawList.AddRectOutline(
layout.inputRect,
ResolveSurfaceBorderColor(state, palette),
ResolveSurfaceBorderThickness(state, metrics));
for (std::size_t index = 0u; index < toolItems.size(); ++index) {
if (!HasArea(layout.toolItemRects[index])) {
continue;
}
const bool hovered = state.hoveredToolIndex == index;
const bool active = state.activeToolIndex == index;
drawList.AddFilledRect(
layout.toolItemRects[index],
ResolveToolFillColor(toolItems[index], hovered, active, palette),
metrics.toolCornerRounding);
drawList.AddRectOutline(
layout.toolItemRects[index],
palette.toolBorderColor,
1.0f,
metrics.toolCornerRounding);
}
if (layout.hasBottomBar) {
AppendUIEditorStatusBarBackground(
drawList,
layout.statusBarLayout,
statusSegments,
state.statusBarState,
palette.statusBarPalette);
}
}
void AppendUIEditorViewportSlotForeground(
UIDrawList& drawList,
const UIEditorViewportSlotLayout& layout,
const UIEditorViewportSlotChrome& chrome,
const UIEditorViewportSlotFrame& frame,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotPalette& palette,
const UIEditorViewportSlotMetrics& metrics) {
if (layout.hasTopBar) {
if (!chrome.title.empty()) {
drawList.AddText(
UIPoint(layout.titleRect.x, layout.topBarRect.y + metrics.titleInsetY),
std::string(chrome.title),
palette.textPrimary,
15.0f);
}
if (!chrome.subtitle.empty()) {
drawList.AddText(
UIPoint(layout.subtitleRect.x, layout.topBarRect.y + metrics.subtitleInsetY),
std::string(chrome.subtitle),
palette.textSecondary,
11.0f);
}
for (std::size_t index = 0u; index < toolItems.size(); ++index) {
if (!HasArea(layout.toolItemRects[index]) || toolItems[index].label.empty()) {
continue;
}
drawList.AddText(
UIPoint(
layout.toolItemRects[index].x + metrics.toolPaddingX,
layout.toolItemRects[index].y + 5.0f),
toolItems[index].label,
toolItems[index].enabled ? palette.textPrimary : palette.textMuted,
12.0f);
}
}
const UISize frameSize = ResolveFrameAspectSize(frame, layout.requestedSurfaceSize);
if (frame.hasTexture && frame.texture.IsValid()) {
drawList.AddImage(layout.textureRect, frame.texture, palette.imageTint);
drawList.AddText(
UIPoint(layout.inputRect.x + 14.0f, layout.inputRect.y + 14.0f),
"Texture " +
std::to_string(static_cast<int>(frameSize.width)) +
"x" +
std::to_string(static_cast<int>(frameSize.height)),
palette.textMuted,
12.0f);
} else {
const std::string statusText = frame.statusText.empty()
? std::string("Viewport is waiting for frame")
: frame.statusText;
drawList.AddText(
UIPoint(layout.inputRect.x + 16.0f, layout.inputRect.y + 18.0f),
statusText,
palette.textPrimary,
14.0f);
drawList.AddText(
UIPoint(layout.inputRect.x + 16.0f, layout.inputRect.y + 42.0f),
"Requested surface: " +
std::to_string(static_cast<int>(layout.requestedSurfaceSize.width)) +
"x" +
std::to_string(static_cast<int>(layout.requestedSurfaceSize.height)),
palette.textMuted,
12.0f);
}
if (layout.hasBottomBar) {
AppendUIEditorStatusBarForeground(
drawList,
layout.statusBarLayout,
statusSegments,
state.statusBarState,
palette.statusBarPalette);
}
}
void AppendUIEditorViewportSlot(
UIDrawList& drawList,
const UIRect& bounds,
const UIEditorViewportSlotChrome& chrome,
const UIEditorViewportSlotFrame& frame,
const std::vector<UIEditorViewportSlotToolItem>& toolItems,
const std::vector<UIEditorStatusBarSegment>& statusSegments,
const UIEditorViewportSlotState& state,
const UIEditorViewportSlotPalette& palette,
const UIEditorViewportSlotMetrics& metrics) {
const UIEditorViewportSlotLayout layout =
BuildUIEditorViewportSlotLayout(
bounds,
chrome,
frame,
toolItems,
statusSegments,
metrics);
AppendUIEditorViewportSlotBackground(
drawList,
layout,
toolItems,
statusSegments,
state,
palette,
metrics);
AppendUIEditorViewportSlotForeground(
drawList,
layout,
chrome,
frame,
toolItems,
statusSegments,
state,
palette,
metrics);
}
} // namespace XCEngine::UI::Editor::Widgets