Build XCEditor menu and status shell widgets
This commit is contained in:
279
new_editor/src/Widgets/UIEditorStatusBar.cpp
Normal file
279
new_editor/src/Widgets/UIEditorStatusBar.cpp
Normal file
@@ -0,0 +1,279 @@
|
||||
#include <XCEditor/Widgets/UIEditorStatusBar.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace XCEngine::UI::Editor::Widgets {
|
||||
|
||||
namespace {
|
||||
|
||||
using ::XCEngine::UI::UIColor;
|
||||
using ::XCEngine::UI::UIDrawList;
|
||||
using ::XCEngine::UI::UIPoint;
|
||||
using ::XCEngine::UI::UIRect;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
UIColor ResolveSegmentFillColor(
|
||||
bool hovered,
|
||||
bool active,
|
||||
const UIEditorStatusBarPalette& palette) {
|
||||
if (active) {
|
||||
return palette.segmentActiveColor;
|
||||
}
|
||||
|
||||
if (hovered) {
|
||||
return palette.segmentHoveredColor;
|
||||
}
|
||||
|
||||
return palette.segmentColor;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
float ResolveUIEditorStatusBarDesiredSegmentWidth(
|
||||
const UIEditorStatusBarSegment& segment,
|
||||
const UIEditorStatusBarMetrics& metrics) {
|
||||
if (segment.desiredWidth > 0.0f) {
|
||||
return segment.desiredWidth;
|
||||
}
|
||||
|
||||
return segment.label.empty()
|
||||
? metrics.segmentPaddingX * 2.0f
|
||||
: metrics.segmentPaddingX * 2.0f +
|
||||
static_cast<float>(segment.label.size()) * metrics.estimatedGlyphWidth;
|
||||
}
|
||||
|
||||
UIEditorStatusBarLayout BuildUIEditorStatusBarLayout(
|
||||
const UIRect& bounds,
|
||||
const std::vector<UIEditorStatusBarSegment>& segments,
|
||||
const UIEditorStatusBarMetrics& metrics) {
|
||||
UIEditorStatusBarLayout layout = {};
|
||||
layout.bounds = UIRect(
|
||||
bounds.x,
|
||||
bounds.y,
|
||||
ClampNonNegative(bounds.width),
|
||||
ClampNonNegative(bounds.height));
|
||||
layout.segmentRects.resize(segments.size(), UIRect{});
|
||||
layout.separatorRects.resize(segments.size(), UIRect{});
|
||||
|
||||
const float contentTop = layout.bounds.y;
|
||||
const float contentHeight = layout.bounds.height;
|
||||
const float leftStart = layout.bounds.x + metrics.outerPaddingX;
|
||||
const float rightLimit = layout.bounds.x + layout.bounds.width - metrics.outerPaddingX;
|
||||
|
||||
float leadingCursor = leftStart;
|
||||
float leadingRight = leftStart;
|
||||
for (std::size_t index = 0u; index < segments.size(); ++index) {
|
||||
const auto& segment = segments[index];
|
||||
if (segment.slot != UIEditorStatusBarSlot::Leading) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float segmentWidth = ResolveUIEditorStatusBarDesiredSegmentWidth(segment, metrics);
|
||||
layout.segmentRects[index] = UIRect(
|
||||
leadingCursor,
|
||||
contentTop,
|
||||
segmentWidth,
|
||||
contentHeight);
|
||||
leadingCursor += segmentWidth;
|
||||
leadingRight = leadingCursor;
|
||||
|
||||
if (segment.showSeparator) {
|
||||
layout.separatorRects[index] = UIRect(
|
||||
leadingCursor,
|
||||
contentTop + metrics.separatorInsetY,
|
||||
metrics.separatorWidth,
|
||||
(std::max)(contentHeight - metrics.separatorInsetY * 2.0f, 0.0f));
|
||||
leadingCursor += metrics.separatorWidth;
|
||||
}
|
||||
|
||||
leadingCursor += metrics.segmentGap;
|
||||
leadingRight = (std::max)(leadingRight, leadingCursor - metrics.segmentGap);
|
||||
}
|
||||
|
||||
float trailingCursor = rightLimit;
|
||||
float trailingLeft = rightLimit;
|
||||
for (std::size_t reverseIndex = segments.size(); reverseIndex > 0u; --reverseIndex) {
|
||||
const std::size_t index = reverseIndex - 1u;
|
||||
const auto& segment = segments[index];
|
||||
if (segment.slot != UIEditorStatusBarSlot::Trailing) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float segmentWidth = ResolveUIEditorStatusBarDesiredSegmentWidth(segment, metrics);
|
||||
const bool hasSeparator = segment.showSeparator;
|
||||
|
||||
if (hasSeparator) {
|
||||
trailingCursor -= metrics.separatorWidth;
|
||||
layout.separatorRects[index] = UIRect(
|
||||
trailingCursor,
|
||||
contentTop + metrics.separatorInsetY,
|
||||
metrics.separatorWidth,
|
||||
(std::max)(contentHeight - metrics.separatorInsetY * 2.0f, 0.0f));
|
||||
trailingCursor -= metrics.segmentGap;
|
||||
}
|
||||
|
||||
trailingCursor -= segmentWidth;
|
||||
layout.segmentRects[index] = UIRect(
|
||||
trailingCursor,
|
||||
contentTop,
|
||||
segmentWidth,
|
||||
contentHeight);
|
||||
trailingCursor -= metrics.segmentGap;
|
||||
trailingLeft = (std::min)(trailingLeft, layout.segmentRects[index].x);
|
||||
}
|
||||
|
||||
const float leadingWidth =
|
||||
leadingRight > leftStart ? leadingRight - leftStart : 0.0f;
|
||||
layout.leadingSlotRect = UIRect(leftStart, contentTop, leadingWidth, contentHeight);
|
||||
|
||||
const float trailingWidth =
|
||||
trailingLeft < rightLimit ? rightLimit - trailingLeft : 0.0f;
|
||||
layout.trailingSlotRect = UIRect(trailingLeft, contentTop, trailingWidth, contentHeight);
|
||||
|
||||
if (HasArea(layout.leadingSlotRect) &&
|
||||
HasArea(layout.trailingSlotRect) &&
|
||||
layout.leadingSlotRect.x + layout.leadingSlotRect.width + metrics.slotGapMin >
|
||||
layout.trailingSlotRect.x) {
|
||||
const float overlap =
|
||||
layout.leadingSlotRect.x + layout.leadingSlotRect.width + metrics.slotGapMin -
|
||||
layout.trailingSlotRect.x;
|
||||
layout.trailingSlotRect.x += overlap;
|
||||
layout.trailingSlotRect.width = (std::max)(layout.trailingSlotRect.width - overlap, 0.0f);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
UIEditorStatusBarHitTarget HitTestUIEditorStatusBar(
|
||||
const UIEditorStatusBarLayout& layout,
|
||||
const UIPoint& point) {
|
||||
if (!ContainsPoint(layout.bounds, point)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (std::size_t index = 0u; index < layout.separatorRects.size(); ++index) {
|
||||
if (HasArea(layout.separatorRects[index]) &&
|
||||
ContainsPoint(layout.separatorRects[index], point)) {
|
||||
return { UIEditorStatusBarHitTargetKind::Separator, index };
|
||||
}
|
||||
}
|
||||
|
||||
for (std::size_t index = 0u; index < layout.segmentRects.size(); ++index) {
|
||||
if (HasArea(layout.segmentRects[index]) &&
|
||||
ContainsPoint(layout.segmentRects[index], point)) {
|
||||
return { UIEditorStatusBarHitTargetKind::Segment, index };
|
||||
}
|
||||
}
|
||||
|
||||
return { UIEditorStatusBarHitTargetKind::Background, UIEditorStatusBarInvalidIndex };
|
||||
}
|
||||
|
||||
UIColor ResolveUIEditorStatusBarTextColor(
|
||||
UIEditorStatusBarTextTone tone,
|
||||
const UIEditorStatusBarPalette& palette) {
|
||||
switch (tone) {
|
||||
case UIEditorStatusBarTextTone::Muted:
|
||||
return palette.textMuted;
|
||||
case UIEditorStatusBarTextTone::Accent:
|
||||
return palette.textAccent;
|
||||
case UIEditorStatusBarTextTone::Primary:
|
||||
default:
|
||||
return palette.textPrimary;
|
||||
}
|
||||
}
|
||||
|
||||
void AppendUIEditorStatusBarBackground(
|
||||
UIDrawList& drawList,
|
||||
const UIEditorStatusBarLayout& layout,
|
||||
const std::vector<UIEditorStatusBarSegment>& segments,
|
||||
const UIEditorStatusBarState& state,
|
||||
const UIEditorStatusBarPalette& palette,
|
||||
const UIEditorStatusBarMetrics& metrics) {
|
||||
drawList.AddFilledRect(layout.bounds, palette.surfaceColor, metrics.cornerRounding);
|
||||
drawList.AddRectOutline(
|
||||
layout.bounds,
|
||||
state.focused ? palette.focusedBorderColor : palette.borderColor,
|
||||
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
|
||||
metrics.cornerRounding);
|
||||
|
||||
for (std::size_t index = 0u; index < segments.size(); ++index) {
|
||||
if (!HasArea(layout.segmentRects[index])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool hovered = state.hoveredIndex == index;
|
||||
const bool active = state.activeIndex == index;
|
||||
if (!hovered && !active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drawList.AddFilledRect(
|
||||
layout.segmentRects[index],
|
||||
ResolveSegmentFillColor(hovered, active, palette),
|
||||
6.0f);
|
||||
drawList.AddRectOutline(
|
||||
layout.segmentRects[index],
|
||||
palette.segmentBorderColor,
|
||||
1.0f,
|
||||
6.0f);
|
||||
}
|
||||
|
||||
for (const UIRect& separatorRect : layout.separatorRects) {
|
||||
if (!HasArea(separatorRect)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drawList.AddFilledRect(separatorRect, palette.separatorColor);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendUIEditorStatusBarForeground(
|
||||
UIDrawList& drawList,
|
||||
const UIEditorStatusBarLayout& layout,
|
||||
const std::vector<UIEditorStatusBarSegment>& segments,
|
||||
const UIEditorStatusBarState&,
|
||||
const UIEditorStatusBarPalette& palette,
|
||||
const UIEditorStatusBarMetrics& metrics) {
|
||||
for (std::size_t index = 0u; index < segments.size(); ++index) {
|
||||
if (!HasArea(layout.segmentRects[index]) || segments[index].label.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
layout.segmentRects[index].x + metrics.segmentPaddingX,
|
||||
layout.segmentRects[index].y + metrics.segmentPaddingY),
|
||||
segments[index].label,
|
||||
ResolveUIEditorStatusBarTextColor(segments[index].tone, palette),
|
||||
12.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendUIEditorStatusBar(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& bounds,
|
||||
const std::vector<UIEditorStatusBarSegment>& segments,
|
||||
const UIEditorStatusBarState& state,
|
||||
const UIEditorStatusBarPalette& palette,
|
||||
const UIEditorStatusBarMetrics& metrics) {
|
||||
const UIEditorStatusBarLayout layout =
|
||||
BuildUIEditorStatusBarLayout(bounds, segments, metrics);
|
||||
AppendUIEditorStatusBarBackground(drawList, layout, segments, state, palette, metrics);
|
||||
AppendUIEditorStatusBarForeground(drawList, layout, segments, state, palette, metrics);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::Widgets
|
||||
Reference in New Issue
Block a user