260 lines
11 KiB
C++
260 lines
11 KiB
C++
#include <XCEditor/Fields/UIEditorEnumField.h>
|
|
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
|
|
#include <XCEditor/Widgets/UIEditorTextLayout.h>
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
|
|
namespace XCEngine::UI::Editor::Widgets {
|
|
|
|
namespace {
|
|
|
|
float ClampNonNegative(float value) {
|
|
return (std::max)(0.0f, value);
|
|
}
|
|
|
|
UIEditorEnumFieldMetrics ResolveMetrics(const UIEditorEnumFieldMetrics& metrics) {
|
|
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
|
|
|
|
UIEditorEnumFieldMetrics resolved = metrics;
|
|
if (AreUIEditorFieldMetricsEqual(metrics.controlTrailingInset, 8.0f)) {
|
|
resolved.controlTrailingInset = tokens.controlTrailingInset;
|
|
}
|
|
if (AreUIEditorFieldMetricsEqual(metrics.valueBoxMinWidth, 96.0f)) {
|
|
resolved.valueBoxMinWidth = tokens.controlMinWidth;
|
|
}
|
|
if (AreUIEditorFieldMetricsEqual(metrics.dropdownArrowWidth, 20.0f)) {
|
|
resolved.dropdownArrowWidth = tokens.dropdownArrowWidth;
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
UIEditorEnumFieldPalette ResolvePalette(const UIEditorEnumFieldPalette& palette) {
|
|
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
|
|
|
|
UIEditorEnumFieldPalette resolved = palette;
|
|
if (AreUIEditorFieldColorsEqual(palette.focusedBorderColor, ::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f))) {
|
|
resolved.focusedBorderColor = tokens.controlFocusedBorderColor;
|
|
}
|
|
if (AreUIEditorFieldColorsEqual(palette.valueBoxColor, ::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f))) {
|
|
resolved.valueBoxColor = tokens.controlColor;
|
|
}
|
|
if (AreUIEditorFieldColorsEqual(palette.valueBoxHoverColor, ::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f))) {
|
|
resolved.valueBoxHoverColor = tokens.controlHoverColor;
|
|
}
|
|
if (AreUIEditorFieldColorsEqual(palette.readOnlyColor, ::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f))) {
|
|
resolved.readOnlyColor = tokens.controlReadOnlyColor;
|
|
}
|
|
if (AreUIEditorFieldColorsEqual(palette.controlBorderColor, ::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f))) {
|
|
resolved.controlBorderColor = tokens.controlBorderColor;
|
|
}
|
|
if (AreUIEditorFieldColorsEqual(palette.labelColor, ::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f))) {
|
|
resolved.labelColor = tokens.labelColor;
|
|
}
|
|
if (AreUIEditorFieldColorsEqual(palette.valueColor, ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f))) {
|
|
resolved.valueColor = tokens.valueColor;
|
|
}
|
|
if (AreUIEditorFieldColorsEqual(palette.arrowColor, ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f))) {
|
|
resolved.arrowColor = tokens.arrowColor;
|
|
}
|
|
|
|
return resolved;
|
|
}
|
|
|
|
bool ContainsPoint(const ::XCEngine::UI::UIRect& rect, const ::XCEngine::UI::UIPoint& point) {
|
|
return point.x >= rect.x &&
|
|
point.x <= rect.x + rect.width &&
|
|
point.y >= rect.y &&
|
|
point.y <= rect.y + rect.height;
|
|
}
|
|
|
|
std::size_t ClampSelectedIndex(const UIEditorEnumFieldSpec& spec) {
|
|
if (spec.options.empty()) {
|
|
return 0u;
|
|
}
|
|
|
|
return (std::min)(spec.selectedIndex, spec.options.size() - 1u);
|
|
}
|
|
|
|
void AppendDropdownChevron(
|
|
::XCEngine::UI::UIDrawList& drawList,
|
|
const ::XCEngine::UI::UIRect& rect,
|
|
const ::XCEngine::UI::UIColor& color) {
|
|
const float centerX = std::floor(rect.x + rect.width * 0.5f) + 0.5f;
|
|
const float centerY = std::floor(rect.y + rect.height * 0.5f) + 0.5f;
|
|
const float halfWidth = ClampNonNegative(rect.width * 0.18f);
|
|
const float halfHeight = ClampNonNegative(rect.height * 0.12f);
|
|
drawList.AddFilledTriangle(
|
|
::XCEngine::UI::UIPoint(centerX - halfWidth, centerY - halfHeight),
|
|
::XCEngine::UI::UIPoint(centerX + halfWidth, centerY - halfHeight),
|
|
::XCEngine::UI::UIPoint(centerX, centerY + halfHeight),
|
|
color);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::string ResolveUIEditorEnumFieldValueText(const UIEditorEnumFieldSpec& spec) {
|
|
if (spec.options.empty()) {
|
|
return "(none)";
|
|
}
|
|
|
|
return spec.options[ClampSelectedIndex(spec)];
|
|
}
|
|
|
|
UIEditorEnumFieldLayout BuildUIEditorEnumFieldLayout(
|
|
const ::XCEngine::UI::UIRect& bounds,
|
|
const UIEditorEnumFieldSpec&,
|
|
const UIEditorEnumFieldMetrics& metrics) {
|
|
const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
|
|
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
|
|
bounds,
|
|
resolvedMetrics.valueBoxMinWidth,
|
|
UIEditorFieldRowLayoutMetrics {
|
|
resolvedMetrics.rowHeight,
|
|
resolvedMetrics.horizontalPadding,
|
|
resolvedMetrics.labelControlGap,
|
|
resolvedMetrics.controlColumnStart,
|
|
resolvedMetrics.controlMinWidth,
|
|
resolvedMetrics.controlTrailingInset,
|
|
resolvedMetrics.controlInsetY,
|
|
});
|
|
|
|
UIEditorEnumFieldLayout layout = {};
|
|
layout.bounds = hostLayout.bounds;
|
|
layout.labelRect = hostLayout.labelRect;
|
|
layout.controlRect = hostLayout.controlRect;
|
|
layout.valueRect = layout.controlRect;
|
|
const float arrowWidth = (std::min)(
|
|
(std::max)(resolvedMetrics.dropdownArrowWidth, 0.0f),
|
|
layout.valueRect.width);
|
|
layout.arrowRect = ::XCEngine::UI::UIRect(
|
|
layout.valueRect.x + (std::max)(0.0f, layout.valueRect.width - arrowWidth),
|
|
layout.valueRect.y,
|
|
arrowWidth,
|
|
layout.valueRect.height);
|
|
return layout;
|
|
}
|
|
|
|
UIEditorEnumFieldHitTarget HitTestUIEditorEnumField(
|
|
const UIEditorEnumFieldLayout& layout,
|
|
const ::XCEngine::UI::UIPoint& point) {
|
|
if (!ContainsPoint(layout.bounds, point)) {
|
|
return {};
|
|
}
|
|
if (ContainsPoint(layout.arrowRect, point)) {
|
|
return { UIEditorEnumFieldHitTargetKind::DropdownArrow };
|
|
}
|
|
if (ContainsPoint(layout.valueRect, point)) {
|
|
return { UIEditorEnumFieldHitTargetKind::ValueBox };
|
|
}
|
|
return { UIEditorEnumFieldHitTargetKind::Row };
|
|
}
|
|
|
|
void AppendUIEditorEnumFieldBackground(
|
|
::XCEngine::UI::UIDrawList& drawList,
|
|
const UIEditorEnumFieldLayout& layout,
|
|
const UIEditorEnumFieldSpec& spec,
|
|
const UIEditorEnumFieldState& state,
|
|
const UIEditorEnumFieldPalette& palette,
|
|
const UIEditorEnumFieldMetrics& metrics) {
|
|
const UIEditorEnumFieldPalette resolvedPalette = ResolvePalette(palette);
|
|
const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
|
|
const auto rowFillColor =
|
|
state.active ? resolvedPalette.rowActiveColor
|
|
: (state.hoveredTarget != UIEditorEnumFieldHitTargetKind::None
|
|
? resolvedPalette.rowHoverColor
|
|
: resolvedPalette.surfaceColor);
|
|
if (rowFillColor.a > 0.0f) {
|
|
drawList.AddFilledRect(layout.bounds, rowFillColor, resolvedMetrics.cornerRounding);
|
|
}
|
|
|
|
const bool controlHovered =
|
|
state.hoveredTarget == UIEditorEnumFieldHitTargetKind::ValueBox ||
|
|
state.hoveredTarget == UIEditorEnumFieldHitTargetKind::DropdownArrow;
|
|
const ::XCEngine::UI::UIColor previewColor =
|
|
spec.readOnly
|
|
? resolvedPalette.readOnlyColor
|
|
: (controlHovered || state.popupOpen ? resolvedPalette.valueBoxHoverColor : resolvedPalette.valueBoxColor);
|
|
const ::XCEngine::UI::UIColor arrowColor =
|
|
spec.readOnly
|
|
? resolvedPalette.readOnlyColor
|
|
: (state.popupOpen || state.hoveredTarget == UIEditorEnumFieldHitTargetKind::DropdownArrow
|
|
? resolvedPalette.valueBoxHoverColor
|
|
: resolvedPalette.valueBoxColor);
|
|
const ::XCEngine::UI::UIRect previewRect(
|
|
layout.valueRect.x,
|
|
layout.valueRect.y,
|
|
ClampNonNegative(layout.arrowRect.x - layout.valueRect.x),
|
|
layout.valueRect.height);
|
|
drawList.AddFilledRect(
|
|
previewRect,
|
|
previewColor,
|
|
resolvedMetrics.valueBoxRounding);
|
|
drawList.AddFilledRect(
|
|
layout.arrowRect,
|
|
arrowColor,
|
|
resolvedMetrics.valueBoxRounding);
|
|
drawList.AddRectOutline(
|
|
layout.valueRect,
|
|
state.popupOpen ? resolvedPalette.focusedBorderColor : resolvedPalette.controlBorderColor,
|
|
state.popupOpen ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness,
|
|
resolvedMetrics.valueBoxRounding);
|
|
drawList.AddLine(
|
|
::XCEngine::UI::UIPoint(layout.arrowRect.x, layout.arrowRect.y + 1.0f),
|
|
::XCEngine::UI::UIPoint(layout.arrowRect.x, layout.arrowRect.y + layout.arrowRect.height - 1.0f),
|
|
resolvedPalette.controlBorderColor,
|
|
1.0f);
|
|
}
|
|
|
|
void AppendUIEditorEnumFieldForeground(
|
|
::XCEngine::UI::UIDrawList& drawList,
|
|
const UIEditorEnumFieldLayout& layout,
|
|
const UIEditorEnumFieldSpec& spec,
|
|
const UIEditorEnumFieldPalette& palette,
|
|
const UIEditorEnumFieldMetrics& metrics) {
|
|
const UIEditorEnumFieldPalette resolvedPalette = ResolvePalette(palette);
|
|
const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
|
|
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, resolvedMetrics.labelFontSize));
|
|
drawList.AddText(
|
|
::XCEngine::UI::UIPoint(
|
|
layout.labelRect.x,
|
|
ResolveUIEditorTextTop(layout.labelRect, resolvedMetrics.labelFontSize, resolvedMetrics.labelTextInsetY)),
|
|
spec.label,
|
|
resolvedPalette.labelColor,
|
|
resolvedMetrics.labelFontSize);
|
|
drawList.PopClipRect();
|
|
|
|
const ::XCEngine::UI::UIRect valueTextClipRect(
|
|
layout.valueRect.x + resolvedMetrics.valueTextInsetX,
|
|
layout.valueRect.y,
|
|
ClampNonNegative(layout.arrowRect.x - layout.valueRect.x - resolvedMetrics.valueTextInsetX),
|
|
layout.valueRect.height);
|
|
drawList.PushClipRect(ResolveUIEditorTextClipRect(valueTextClipRect, resolvedMetrics.valueFontSize));
|
|
drawList.AddText(
|
|
::XCEngine::UI::UIPoint(
|
|
layout.valueRect.x + resolvedMetrics.valueTextInsetX,
|
|
ResolveUIEditorTextTop(layout.valueRect, resolvedMetrics.valueFontSize, resolvedMetrics.valueTextInsetY)),
|
|
ResolveUIEditorEnumFieldValueText(spec),
|
|
resolvedPalette.valueColor,
|
|
resolvedMetrics.valueFontSize);
|
|
drawList.PopClipRect();
|
|
AppendDropdownChevron(drawList, layout.arrowRect, resolvedPalette.arrowColor);
|
|
}
|
|
|
|
void AppendUIEditorEnumField(
|
|
::XCEngine::UI::UIDrawList& drawList,
|
|
const ::XCEngine::UI::UIRect& bounds,
|
|
const UIEditorEnumFieldSpec& spec,
|
|
const UIEditorEnumFieldState& state,
|
|
const UIEditorEnumFieldPalette& palette,
|
|
const UIEditorEnumFieldMetrics& metrics) {
|
|
const UIEditorEnumFieldPalette resolvedPalette = ResolvePalette(palette);
|
|
const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics);
|
|
const UIEditorEnumFieldLayout layout = BuildUIEditorEnumFieldLayout(bounds, spec, resolvedMetrics);
|
|
AppendUIEditorEnumFieldBackground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
|
|
AppendUIEditorEnumFieldForeground(drawList, layout, spec, resolvedPalette, resolvedMetrics);
|
|
}
|
|
|
|
} // namespace XCEngine::UI::Editor::Widgets
|