Stabilize XCEditor shell foundation widgets
This commit is contained in:
341
new_editor/src/Widgets/UIEditorPanelFrame.cpp
Normal file
341
new_editor/src/Widgets/UIEditorPanelFrame.cpp
Normal file
@@ -0,0 +1,341 @@
|
||||
#include <XCEditor/Widgets/UIEditorPanelFrame.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;
|
||||
|
||||
float ClampNonNegative(float value) {
|
||||
return (std::max)(value, 0.0f);
|
||||
}
|
||||
|
||||
float ResolveActionButtonExtent(
|
||||
const UIEditorPanelFrameMetrics& metrics,
|
||||
float headerHeight) {
|
||||
const float inset = (std::max)(metrics.actionInsetX, 0.0f);
|
||||
return (std::min)(
|
||||
(std::max)(metrics.actionButtonExtent, 0.0f),
|
||||
(std::max)(headerHeight - inset * 2.0f, 0.0f));
|
||||
}
|
||||
|
||||
UIColor ResolveActionFillColor(
|
||||
bool selected,
|
||||
bool hovered,
|
||||
const UIEditorPanelFramePalette& palette) {
|
||||
if (selected) {
|
||||
return palette.actionButtonSelectedColor;
|
||||
}
|
||||
|
||||
if (hovered) {
|
||||
return palette.actionButtonHoveredColor;
|
||||
}
|
||||
|
||||
return palette.actionButtonColor;
|
||||
}
|
||||
|
||||
void AppendActionButton(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& rect,
|
||||
std::string_view glyph,
|
||||
bool selected,
|
||||
bool hovered,
|
||||
const UIEditorPanelFramePalette& palette) {
|
||||
if (rect.width <= 0.0f || rect.height <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawList.AddFilledRect(
|
||||
rect,
|
||||
ResolveActionFillColor(selected, hovered, palette),
|
||||
5.0f);
|
||||
drawList.AddRectOutline(
|
||||
rect,
|
||||
palette.actionButtonBorderColor,
|
||||
1.0f,
|
||||
5.0f);
|
||||
drawList.AddText(
|
||||
UIPoint(rect.x + 5.0f, rect.y + 2.0f),
|
||||
std::string(glyph),
|
||||
palette.actionGlyphColor,
|
||||
12.0f);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsUIEditorPanelFramePointInside(
|
||||
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 IsUIEditorPanelFramePinButtonVisible(const UIEditorPanelFrameState& state) {
|
||||
return state.pinnable;
|
||||
}
|
||||
|
||||
bool IsUIEditorPanelFrameCloseButtonVisible(const UIEditorPanelFrameState& state) {
|
||||
return state.closable;
|
||||
}
|
||||
|
||||
UIEditorPanelFrameLayout BuildUIEditorPanelFrameLayout(
|
||||
const UIRect& frameRect,
|
||||
const UIEditorPanelFrameState& state,
|
||||
const UIEditorPanelFrameMetrics& metrics) {
|
||||
UIEditorPanelFrameLayout layout = {};
|
||||
layout.frameRect = UIRect(
|
||||
frameRect.x,
|
||||
frameRect.y,
|
||||
ClampNonNegative(frameRect.width),
|
||||
ClampNonNegative(frameRect.height));
|
||||
|
||||
const float headerHeight =
|
||||
(std::min)(ClampNonNegative(metrics.headerHeight), layout.frameRect.height);
|
||||
const float footerHeight =
|
||||
state.showFooter
|
||||
? (std::min)(
|
||||
ClampNonNegative(metrics.footerHeight),
|
||||
(std::max)(layout.frameRect.height - headerHeight, 0.0f))
|
||||
: 0.0f;
|
||||
|
||||
layout.hasFooter = footerHeight > 0.0f;
|
||||
layout.showPinButton = IsUIEditorPanelFramePinButtonVisible(state);
|
||||
layout.showCloseButton = IsUIEditorPanelFrameCloseButtonVisible(state);
|
||||
|
||||
layout.headerRect = UIRect(
|
||||
layout.frameRect.x,
|
||||
layout.frameRect.y,
|
||||
layout.frameRect.width,
|
||||
headerHeight);
|
||||
|
||||
if (layout.hasFooter) {
|
||||
layout.footerRect = UIRect(
|
||||
layout.frameRect.x,
|
||||
layout.frameRect.y + layout.frameRect.height - footerHeight,
|
||||
layout.frameRect.width,
|
||||
footerHeight);
|
||||
}
|
||||
|
||||
const float bodyBandTop = layout.headerRect.y + layout.headerRect.height;
|
||||
const float bodyBandBottom = layout.hasFooter
|
||||
? layout.footerRect.y
|
||||
: layout.frameRect.y + layout.frameRect.height;
|
||||
const float contentPadding = ClampNonNegative(metrics.contentPadding);
|
||||
|
||||
layout.bodyRect = UIRect(
|
||||
layout.frameRect.x + contentPadding,
|
||||
bodyBandTop + contentPadding,
|
||||
(std::max)(layout.frameRect.width - contentPadding * 2.0f, 0.0f),
|
||||
(std::max)(bodyBandBottom - bodyBandTop - contentPadding * 2.0f, 0.0f));
|
||||
|
||||
if (layout.headerRect.height <= 0.0f) {
|
||||
return layout;
|
||||
}
|
||||
|
||||
const float buttonExtent = ResolveActionButtonExtent(metrics, layout.headerRect.height);
|
||||
if (buttonExtent <= 0.0f) {
|
||||
return layout;
|
||||
}
|
||||
|
||||
const float buttonY = layout.headerRect.y + (layout.headerRect.height - buttonExtent) * 0.5f;
|
||||
float buttonRight = layout.headerRect.x + layout.headerRect.width - ClampNonNegative(metrics.actionInsetX);
|
||||
|
||||
if (layout.showCloseButton) {
|
||||
layout.closeButtonRect = UIRect(
|
||||
buttonRight - buttonExtent,
|
||||
buttonY,
|
||||
buttonExtent,
|
||||
buttonExtent);
|
||||
buttonRight = layout.closeButtonRect.x - ClampNonNegative(metrics.actionGap);
|
||||
}
|
||||
|
||||
if (layout.showPinButton) {
|
||||
layout.pinButtonRect = UIRect(
|
||||
buttonRight - buttonExtent,
|
||||
buttonY,
|
||||
buttonExtent,
|
||||
buttonExtent);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
UIColor ResolveUIEditorPanelFrameBorderColor(
|
||||
const UIEditorPanelFrameState& state,
|
||||
const UIEditorPanelFramePalette& palette) {
|
||||
if (state.focused) {
|
||||
return palette.focusedBorderColor;
|
||||
}
|
||||
|
||||
if (state.active) {
|
||||
return palette.activeBorderColor;
|
||||
}
|
||||
|
||||
if (state.hovered) {
|
||||
return palette.hoveredBorderColor;
|
||||
}
|
||||
|
||||
return palette.borderColor;
|
||||
}
|
||||
|
||||
float ResolveUIEditorPanelFrameBorderThickness(
|
||||
const UIEditorPanelFrameState& state,
|
||||
const UIEditorPanelFrameMetrics& metrics) {
|
||||
if (state.focused) {
|
||||
return metrics.focusedBorderThickness;
|
||||
}
|
||||
|
||||
if (state.active) {
|
||||
return metrics.activeBorderThickness;
|
||||
}
|
||||
|
||||
if (state.hovered) {
|
||||
return metrics.hoveredBorderThickness;
|
||||
}
|
||||
|
||||
return metrics.baseBorderThickness;
|
||||
}
|
||||
|
||||
UIEditorPanelFrameAction HitTestUIEditorPanelFrameAction(
|
||||
const UIEditorPanelFrameLayout& layout,
|
||||
const UIEditorPanelFrameState& state,
|
||||
const UIPoint& point) {
|
||||
if (layout.showPinButton &&
|
||||
IsUIEditorPanelFramePinButtonVisible(state) &&
|
||||
IsUIEditorPanelFramePointInside(layout.pinButtonRect, point)) {
|
||||
return UIEditorPanelFrameAction::Pin;
|
||||
}
|
||||
|
||||
if (layout.showCloseButton &&
|
||||
IsUIEditorPanelFrameCloseButtonVisible(state) &&
|
||||
IsUIEditorPanelFramePointInside(layout.closeButtonRect, point)) {
|
||||
return UIEditorPanelFrameAction::Close;
|
||||
}
|
||||
|
||||
return UIEditorPanelFrameAction::None;
|
||||
}
|
||||
|
||||
UIEditorPanelFrameHitTarget HitTestUIEditorPanelFrame(
|
||||
const UIEditorPanelFrameLayout& layout,
|
||||
const UIEditorPanelFrameState& state,
|
||||
const UIPoint& point) {
|
||||
if (!IsUIEditorPanelFramePointInside(layout.frameRect, point)) {
|
||||
return UIEditorPanelFrameHitTarget::None;
|
||||
}
|
||||
|
||||
switch (HitTestUIEditorPanelFrameAction(layout, state, point)) {
|
||||
case UIEditorPanelFrameAction::Pin:
|
||||
return UIEditorPanelFrameHitTarget::PinButton;
|
||||
case UIEditorPanelFrameAction::Close:
|
||||
return UIEditorPanelFrameHitTarget::CloseButton;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsUIEditorPanelFramePointInside(layout.headerRect, point)) {
|
||||
return UIEditorPanelFrameHitTarget::Header;
|
||||
}
|
||||
|
||||
if (layout.hasFooter && IsUIEditorPanelFramePointInside(layout.footerRect, point)) {
|
||||
return UIEditorPanelFrameHitTarget::Footer;
|
||||
}
|
||||
|
||||
if (IsUIEditorPanelFramePointInside(layout.bodyRect, point)) {
|
||||
return UIEditorPanelFrameHitTarget::Body;
|
||||
}
|
||||
|
||||
return UIEditorPanelFrameHitTarget::Body;
|
||||
}
|
||||
|
||||
void AppendUIEditorPanelFrameBackground(
|
||||
UIDrawList& drawList,
|
||||
const UIEditorPanelFrameLayout& layout,
|
||||
const UIEditorPanelFrameState& state,
|
||||
const UIEditorPanelFramePalette& palette,
|
||||
const UIEditorPanelFrameMetrics& metrics) {
|
||||
drawList.AddFilledRect(layout.frameRect, palette.surfaceColor, metrics.cornerRounding);
|
||||
drawList.AddRectOutline(
|
||||
layout.frameRect,
|
||||
ResolveUIEditorPanelFrameBorderColor(state, palette),
|
||||
ResolveUIEditorPanelFrameBorderThickness(state, metrics),
|
||||
metrics.cornerRounding);
|
||||
drawList.AddFilledRect(layout.headerRect, palette.headerColor, metrics.cornerRounding);
|
||||
if (layout.hasFooter) {
|
||||
drawList.AddFilledRect(layout.footerRect, palette.footerColor, metrics.cornerRounding);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendUIEditorPanelFrameForeground(
|
||||
UIDrawList& drawList,
|
||||
const UIEditorPanelFrameLayout& layout,
|
||||
const UIEditorPanelFrameState& state,
|
||||
const UIEditorPanelFrameText& text,
|
||||
const UIEditorPanelFramePalette& palette,
|
||||
const UIEditorPanelFrameMetrics& metrics) {
|
||||
if (!text.title.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(layout.frameRect.x + metrics.titleInsetX, layout.headerRect.y + metrics.titleInsetY),
|
||||
std::string(text.title),
|
||||
palette.textPrimary,
|
||||
16.0f);
|
||||
}
|
||||
|
||||
if (!text.subtitle.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(layout.frameRect.x + metrics.titleInsetX, layout.headerRect.y + metrics.subtitleInsetY),
|
||||
std::string(text.subtitle),
|
||||
palette.textSecondary,
|
||||
12.0f);
|
||||
}
|
||||
|
||||
if (layout.hasFooter && !text.footer.empty()) {
|
||||
drawList.AddText(
|
||||
UIPoint(layout.footerRect.x + metrics.footerInsetX, layout.footerRect.y + metrics.footerInsetY),
|
||||
std::string(text.footer),
|
||||
palette.textMuted,
|
||||
11.0f);
|
||||
}
|
||||
|
||||
if (layout.showPinButton) {
|
||||
AppendActionButton(
|
||||
drawList,
|
||||
layout.pinButtonRect,
|
||||
"P",
|
||||
state.pinned,
|
||||
state.pinHovered,
|
||||
palette);
|
||||
}
|
||||
|
||||
if (layout.showCloseButton) {
|
||||
AppendActionButton(
|
||||
drawList,
|
||||
layout.closeButtonRect,
|
||||
"X",
|
||||
false,
|
||||
state.closeHovered,
|
||||
palette);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendUIEditorPanelFrame(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& frameRect,
|
||||
const UIEditorPanelFrameState& state,
|
||||
const UIEditorPanelFrameText& text,
|
||||
const UIEditorPanelFramePalette& palette,
|
||||
const UIEditorPanelFrameMetrics& metrics) {
|
||||
const UIEditorPanelFrameLayout layout =
|
||||
BuildUIEditorPanelFrameLayout(frameRect, state, metrics);
|
||||
AppendUIEditorPanelFrameBackground(drawList, layout, state, palette, metrics);
|
||||
AppendUIEditorPanelFrameForeground(drawList, layout, state, text, palette, metrics);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::Widgets
|
||||
Reference in New Issue
Block a user