Build XCEditor viewport slot shell foundation
This commit is contained in:
@@ -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
|
||||
|
||||
203
new_editor/include/XCEditor/Widgets/UIEditorViewportSlot.h
Normal file
203
new_editor/include/XCEditor/Widgets/UIEditorViewportSlot.h
Normal 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
|
||||
532
new_editor/src/Widgets/UIEditorViewportSlot.cpp
Normal file
532
new_editor/src/Widgets/UIEditorViewportSlot.cpp
Normal 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
|
||||
Reference in New Issue
Block a user