Files
XCEngine/new_editor/src/Fields/UIEditorVector4Field.cpp

402 lines
17 KiB
C++

#include <XCEditor/Fields/UIEditorVector4Field.h>
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <XCEditor/Fields/UIEditorNumberField.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
#include <algorithm>
namespace XCEngine::UI::Editor::Widgets {
namespace {
using ::XCEngine::UI::UIDrawList;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIRect;
float ClampNonNegative(float value) {
return (std::max)(0.0f, value);
}
UIEditorVector4FieldMetrics ResolveMetrics(const UIEditorVector4FieldMetrics& metrics) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorVector4FieldMetrics resolved = metrics;
if (AreUIEditorFieldMetricsEqual(metrics.controlTrailingInset, 8.0f)) {
resolved.controlTrailingInset = tokens.controlTrailingInset;
}
if (AreUIEditorFieldMetricsEqual(metrics.componentMinWidth, 72.0f)) {
resolved.componentMinWidth = tokens.vectorComponentMinWidth;
}
if (AreUIEditorFieldMetricsEqual(metrics.componentPrefixWidth, 9.0f)) {
resolved.componentPrefixWidth = tokens.vectorPrefixWidth;
}
if (AreUIEditorFieldMetricsEqual(metrics.componentLabelGap, 4.0f)) {
resolved.componentLabelGap = tokens.vectorPrefixGap;
}
return resolved;
}
UIEditorVector4FieldPalette ResolvePalette(const UIEditorVector4FieldPalette& palette) {
const auto& tokens = GetUIEditorInspectorFieldStyleTokens();
UIEditorVector4FieldPalette resolved = palette;
if (AreUIEditorFieldColorsEqual(palette.rowHoverColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowHoverColor = tokens.rowHoverColor;
}
if (AreUIEditorFieldColorsEqual(palette.rowActiveColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.rowActiveColor = tokens.rowActiveColor;
}
if (AreUIEditorFieldColorsEqual(palette.componentColor, ::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f))) {
resolved.componentColor = tokens.controlColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentHoverColor,
::XCEngine::UI::UIColor(0.21f, 0.21f, 0.21f, 1.0f))) {
resolved.componentHoverColor = tokens.controlHoverColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentEditingColor,
::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f))) {
resolved.componentEditingColor = tokens.controlEditingColor;
}
if (AreUIEditorFieldColorsEqual(palette.readOnlyColor, ::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f))) {
resolved.readOnlyColor = tokens.controlReadOnlyColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentBorderColor,
::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f))) {
resolved.componentBorderColor = tokens.controlBorderColor;
}
if (AreUIEditorFieldColorsEqual(
palette.componentFocusedBorderColor,
::XCEngine::UI::UIColor(0.20f, 0.20f, 0.20f, 1.0f))) {
resolved.componentFocusedBorderColor = tokens.controlFocusedBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.prefixColor, ::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.prefixColor = tokens.prefixColor;
}
if (AreUIEditorFieldColorsEqual(
palette.prefixBorderColor,
::XCEngine::UI::UIColor(0.0f, 0.0f, 0.0f, 0.0f))) {
resolved.prefixBorderColor = tokens.prefixBorderColor;
}
if (AreUIEditorFieldColorsEqual(palette.labelColor, ::XCEngine::UI::UIColor(0.80f, 0.80f, 0.80f, 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.readOnlyValueColor,
::XCEngine::UI::UIColor(0.62f, 0.62f, 0.62f, 1.0f))) {
resolved.readOnlyValueColor = tokens.readOnlyValueColor;
}
if (AreUIEditorFieldColorsEqual(palette.axisXColor, ::XCEngine::UI::UIColor(0.67f, 0.67f, 0.67f, 1.0f))) {
resolved.axisXColor = tokens.axisXColor;
}
if (AreUIEditorFieldColorsEqual(palette.axisYColor, ::XCEngine::UI::UIColor(0.67f, 0.67f, 0.67f, 1.0f))) {
resolved.axisYColor = tokens.axisYColor;
}
if (AreUIEditorFieldColorsEqual(palette.axisZColor, ::XCEngine::UI::UIColor(0.67f, 0.67f, 0.67f, 1.0f))) {
resolved.axisZColor = tokens.axisZColor;
}
if (AreUIEditorFieldColorsEqual(palette.axisWColor, ::XCEngine::UI::UIColor(0.67f, 0.67f, 0.67f, 1.0f))) {
resolved.axisWColor = tokens.axisWColor;
}
return resolved;
}
float ApproximateTextWidth(float fontSize, std::size_t characterCount) {
return fontSize * 0.55f * static_cast<float>(characterCount);
}
UIEditorNumberFieldSpec BuildComponentNumberSpec(
const UIEditorVector4FieldSpec& spec,
std::size_t componentIndex) {
UIEditorNumberFieldSpec numberSpec = {};
numberSpec.fieldId = spec.fieldId + "." + std::to_string(componentIndex);
numberSpec.label.clear();
numberSpec.value = componentIndex < spec.values.size() ? spec.values[componentIndex] : 0.0;
numberSpec.step = spec.step;
numberSpec.minValue = spec.minValue;
numberSpec.maxValue = spec.maxValue;
numberSpec.integerMode = spec.integerMode;
numberSpec.readOnly = spec.readOnly;
return numberSpec;
}
::XCEngine::UI::UIColor ResolveRowFillColor(
const UIEditorVector4FieldState& state,
const UIEditorVector4FieldPalette& palette) {
if (state.activeTarget != UIEditorVector4FieldHitTargetKind::None) {
return palette.rowActiveColor;
}
if (state.hoveredTarget != UIEditorVector4FieldHitTargetKind::None) {
return palette.rowHoverColor;
}
return palette.surfaceColor;
}
::XCEngine::UI::UIColor ResolveComponentFillColor(
const UIEditorVector4FieldSpec& spec,
const UIEditorVector4FieldState& state,
const UIEditorVector4FieldPalette& palette,
std::size_t componentIndex) {
if (spec.readOnly) {
return palette.readOnlyColor;
}
if (state.editing && state.selectedComponentIndex == componentIndex) {
return palette.componentEditingColor;
}
if (state.hoveredComponentIndex == componentIndex) {
return palette.componentHoverColor;
}
return palette.componentColor;
}
::XCEngine::UI::UIColor ResolveComponentBorderColor(
const UIEditorVector4FieldState& state,
const UIEditorVector4FieldPalette& palette,
std::size_t componentIndex) {
if (state.editing && state.selectedComponentIndex == componentIndex) {
return palette.componentFocusedBorderColor;
}
return palette.componentBorderColor;
}
::XCEngine::UI::UIColor ResolveAxisColor(
const UIEditorVector4FieldPalette& palette,
std::size_t componentIndex) {
switch (componentIndex) {
case 0u:
return palette.axisXColor;
case 1u:
return palette.axisYColor;
case 2u:
return palette.axisZColor;
case 3u:
default:
return palette.axisWColor;
}
}
} // namespace
bool IsUIEditorVector4FieldPointInside(
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;
}
double NormalizeUIEditorVector4FieldComponentValue(
const UIEditorVector4FieldSpec& spec,
double value) {
return NormalizeUIEditorNumberFieldValue(BuildComponentNumberSpec(spec, 0u), value);
}
bool TryParseUIEditorVector4FieldComponentValue(
const UIEditorVector4FieldSpec& spec,
std::string_view text,
double& outValue) {
return TryParseUIEditorNumberFieldValue(BuildComponentNumberSpec(spec, 0u), text, outValue);
}
std::string FormatUIEditorVector4FieldComponentValue(
const UIEditorVector4FieldSpec& spec,
std::size_t componentIndex) {
return FormatUIEditorNumberFieldValue(BuildComponentNumberSpec(spec, componentIndex));
}
UIEditorVector4FieldLayout BuildUIEditorVector4FieldLayout(
const UIRect& bounds,
const UIEditorVector4FieldSpec&,
const UIEditorVector4FieldMetrics& metrics) {
const UIEditorVector4FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const float requiredControlWidth =
resolvedMetrics.componentMinWidth * 4.0f + resolvedMetrics.componentGap * 3.0f;
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
bounds,
requiredControlWidth,
UIEditorFieldRowLayoutMetrics {
resolvedMetrics.rowHeight,
resolvedMetrics.horizontalPadding,
resolvedMetrics.labelControlGap,
resolvedMetrics.controlColumnStart,
resolvedMetrics.controlTrailingInset,
resolvedMetrics.controlInsetY,
});
const float componentWidth =
ClampNonNegative((hostLayout.controlRect.width - resolvedMetrics.componentGap * 3.0f) / 4.0f);
UIEditorVector4FieldLayout layout = {};
layout.bounds = hostLayout.bounds;
layout.labelRect = hostLayout.labelRect;
layout.controlRect = hostLayout.controlRect;
for (std::size_t componentIndex = 0u; componentIndex < layout.componentRects.size(); ++componentIndex) {
const float componentX =
layout.controlRect.x +
(componentWidth + resolvedMetrics.componentGap) * static_cast<float>(componentIndex);
const UIRect componentRect(componentX, layout.controlRect.y, componentWidth, layout.controlRect.height);
const float prefixWidth = (std::min)(resolvedMetrics.componentPrefixWidth, componentRect.width);
const float labelGap = ClampNonNegative(resolvedMetrics.componentLabelGap);
const float valueX = componentRect.x + prefixWidth + labelGap;
layout.componentRects[componentIndex] = componentRect;
layout.componentPrefixRects[componentIndex] =
UIRect(componentRect.x, componentRect.y, prefixWidth, componentRect.height);
layout.componentValueRects[componentIndex] =
UIRect(
valueX,
componentRect.y,
ClampNonNegative(componentRect.width - prefixWidth - labelGap),
componentRect.height);
}
return layout;
}
UIEditorVector4FieldHitTarget HitTestUIEditorVector4Field(
const UIEditorVector4FieldLayout& layout,
const UIPoint& point) {
for (std::size_t componentIndex = 0u; componentIndex < layout.componentRects.size(); ++componentIndex) {
if (IsUIEditorVector4FieldPointInside(layout.componentRects[componentIndex], point)) {
return { UIEditorVector4FieldHitTargetKind::Component, componentIndex };
}
}
if (IsUIEditorVector4FieldPointInside(layout.bounds, point)) {
return { UIEditorVector4FieldHitTargetKind::Row, UIEditorVector4FieldInvalidComponentIndex };
}
return {};
}
void AppendUIEditorVector4FieldBackground(
UIDrawList& drawList,
const UIEditorVector4FieldLayout& layout,
const UIEditorVector4FieldSpec& spec,
const UIEditorVector4FieldState& state,
const UIEditorVector4FieldPalette& palette,
const UIEditorVector4FieldMetrics& metrics) {
const UIEditorVector4FieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorVector4FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
if (ResolveRowFillColor(state, resolvedPalette).a > 0.0f) {
drawList.AddFilledRect(
layout.bounds,
ResolveRowFillColor(state, resolvedPalette),
resolvedMetrics.cornerRounding);
}
if ((state.focused ? resolvedPalette.focusedBorderColor.a : resolvedPalette.borderColor.a) > 0.0f) {
drawList.AddRectOutline(
layout.bounds,
state.focused ? resolvedPalette.focusedBorderColor : resolvedPalette.borderColor,
state.focused ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness,
resolvedMetrics.cornerRounding);
}
for (std::size_t componentIndex = 0u; componentIndex < layout.componentRects.size(); ++componentIndex) {
if (resolvedPalette.prefixColor.a > 0.0f) {
drawList.AddFilledRect(
layout.componentPrefixRects[componentIndex],
resolvedPalette.prefixColor,
resolvedMetrics.componentRounding);
}
if (resolvedPalette.prefixBorderColor.a > 0.0f) {
drawList.AddRectOutline(
layout.componentPrefixRects[componentIndex],
resolvedPalette.prefixBorderColor,
resolvedMetrics.borderThickness,
resolvedMetrics.componentRounding);
}
drawList.AddFilledRect(
layout.componentValueRects[componentIndex],
ResolveComponentFillColor(spec, state, resolvedPalette, componentIndex),
resolvedMetrics.componentRounding);
drawList.AddRectOutline(
layout.componentValueRects[componentIndex],
ResolveComponentBorderColor(state, resolvedPalette, componentIndex),
resolvedMetrics.borderThickness,
resolvedMetrics.componentRounding);
}
}
void AppendUIEditorVector4FieldForeground(
UIDrawList& drawList,
const UIEditorVector4FieldLayout& layout,
const UIEditorVector4FieldSpec& spec,
const UIEditorVector4FieldState& state,
const UIEditorVector4FieldPalette& palette,
const UIEditorVector4FieldMetrics& metrics) {
const UIEditorVector4FieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorVector4FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, resolvedMetrics.labelFontSize));
drawList.AddText(
UIPoint(
layout.labelRect.x,
ResolveUIEditorTextTop(layout.labelRect, resolvedMetrics.labelFontSize, resolvedMetrics.labelTextInsetY)),
spec.label,
resolvedPalette.labelColor,
resolvedMetrics.labelFontSize);
drawList.PopClipRect();
for (std::size_t componentIndex = 0u; componentIndex < layout.componentRects.size(); ++componentIndex) {
drawList.PushClipRect(
ResolveUIEditorTextClipRect(layout.componentPrefixRects[componentIndex], resolvedMetrics.prefixFontSize));
const std::string& componentLabel = spec.componentLabels[componentIndex];
const float prefixTextX =
layout.componentPrefixRects[componentIndex].x +
ClampNonNegative(
(layout.componentPrefixRects[componentIndex].width -
ApproximateTextWidth(resolvedMetrics.prefixFontSize, componentLabel.size())) *
0.5f) +
resolvedMetrics.prefixTextInsetX;
drawList.AddText(
UIPoint(
prefixTextX,
ResolveUIEditorTextTop(
layout.componentPrefixRects[componentIndex],
resolvedMetrics.prefixFontSize,
resolvedMetrics.prefixTextInsetY)),
componentLabel,
ResolveAxisColor(resolvedPalette, componentIndex),
resolvedMetrics.prefixFontSize);
drawList.PopClipRect();
drawList.PushClipRect(
ResolveUIEditorTextClipRect(layout.componentValueRects[componentIndex], resolvedMetrics.valueFontSize));
drawList.AddText(
UIPoint(
layout.componentValueRects[componentIndex].x + resolvedMetrics.valueTextInsetX,
ResolveUIEditorTextTop(
layout.componentValueRects[componentIndex],
resolvedMetrics.valueFontSize,
resolvedMetrics.valueTextInsetY)),
state.editing && state.selectedComponentIndex == componentIndex
? state.displayTexts[componentIndex]
: FormatUIEditorVector4FieldComponentValue(spec, componentIndex),
spec.readOnly ? resolvedPalette.readOnlyValueColor : resolvedPalette.valueColor,
resolvedMetrics.valueFontSize);
drawList.PopClipRect();
}
}
void AppendUIEditorVector4Field(
UIDrawList& drawList,
const UIRect& bounds,
const UIEditorVector4FieldSpec& spec,
const UIEditorVector4FieldState& state,
const UIEditorVector4FieldPalette& palette,
const UIEditorVector4FieldMetrics& metrics) {
const UIEditorVector4FieldPalette resolvedPalette = ResolvePalette(palette);
const UIEditorVector4FieldMetrics resolvedMetrics = ResolveMetrics(metrics);
const UIEditorVector4FieldLayout layout = BuildUIEditorVector4FieldLayout(bounds, spec, resolvedMetrics);
AppendUIEditorVector4FieldBackground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
AppendUIEditorVector4FieldForeground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics);
}
} // namespace XCEngine::UI::Editor::Widgets