Files
XCEngine/new_editor/src/Widgets/UIEditorPanelFrame.cpp

342 lines
10 KiB
C++

#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