关键节点
This commit is contained in:
320
editor/src/Fields/UIEditorObjectField.cpp
Normal file
320
editor/src/Fields/UIEditorObjectField.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
#include <XCEditor/Fields/UIEditorObjectField.h>
|
||||
#include <XCEditor/Widgets/UIEditorFieldRowLayout.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);
|
||||
}
|
||||
|
||||
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 HasClearButton(const UIEditorObjectFieldSpec& spec) {
|
||||
return spec.showClearButton && spec.hasValue;
|
||||
}
|
||||
|
||||
bool HasPickerButton(const UIEditorObjectFieldSpec& spec) {
|
||||
return spec.showPickerButton;
|
||||
}
|
||||
|
||||
bool HasTypeText(const UIEditorObjectFieldSpec& spec) {
|
||||
return spec.hasValue && !spec.objectTypeName.empty();
|
||||
}
|
||||
|
||||
::XCEngine::UI::UIColor ResolveRowFillColor(
|
||||
const UIEditorObjectFieldState& state,
|
||||
const UIEditorObjectFieldPalette& palette) {
|
||||
if (state.activeTarget != UIEditorObjectFieldHitTargetKind::None) {
|
||||
return palette.rowActiveColor;
|
||||
}
|
||||
if (state.hoveredTarget != UIEditorObjectFieldHitTargetKind::None) {
|
||||
return palette.rowHoverColor;
|
||||
}
|
||||
return palette.surfaceColor;
|
||||
}
|
||||
|
||||
::XCEngine::UI::UIColor ResolveControlFillColor(
|
||||
const UIEditorObjectFieldSpec& spec,
|
||||
const UIEditorObjectFieldState& state,
|
||||
const UIEditorObjectFieldPalette& palette) {
|
||||
if (spec.readOnly) {
|
||||
return palette.readOnlyColor;
|
||||
}
|
||||
if (state.hoveredTarget == UIEditorObjectFieldHitTargetKind::ValueBox ||
|
||||
state.activeTarget == UIEditorObjectFieldHitTargetKind::ValueBox) {
|
||||
return palette.valueBoxHoverColor;
|
||||
}
|
||||
return palette.valueBoxColor;
|
||||
}
|
||||
|
||||
::XCEngine::UI::UIColor ResolveButtonFillColor(
|
||||
const UIEditorObjectFieldSpec& spec,
|
||||
const UIEditorObjectFieldState& state,
|
||||
UIEditorObjectFieldHitTargetKind target,
|
||||
const UIEditorObjectFieldPalette& palette) {
|
||||
if (spec.readOnly) {
|
||||
return palette.readOnlyColor;
|
||||
}
|
||||
if (state.activeTarget == target) {
|
||||
return palette.buttonActiveColor;
|
||||
}
|
||||
if (state.hoveredTarget == target) {
|
||||
return palette.buttonHoverColor;
|
||||
}
|
||||
return palette.buttonColor;
|
||||
}
|
||||
|
||||
void AppendButtonGlyph(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& rect,
|
||||
const char* glyph,
|
||||
const UIEditorObjectFieldPalette& palette,
|
||||
const UIEditorObjectFieldMetrics& metrics) {
|
||||
if (rect.width <= 0.0f || rect.height <= 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
rect.x + ClampNonNegative((rect.width - metrics.buttonGlyphFontSize) * 0.5f),
|
||||
ResolveUIEditorTextTop(rect, metrics.buttonGlyphFontSize, metrics.buttonGlyphInsetY)),
|
||||
glyph,
|
||||
palette.buttonGlyphColor,
|
||||
metrics.buttonGlyphFontSize);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string ResolveUIEditorObjectFieldDisplayText(const UIEditorObjectFieldSpec& spec) {
|
||||
if (!spec.hasValue) {
|
||||
return spec.emptyText.empty() ? "(none)" : spec.emptyText;
|
||||
}
|
||||
|
||||
return spec.objectName.empty() ? "(unnamed)" : spec.objectName;
|
||||
}
|
||||
|
||||
UIEditorObjectFieldLayout BuildUIEditorObjectFieldLayout(
|
||||
const UIRect& bounds,
|
||||
const UIEditorObjectFieldSpec& spec,
|
||||
const UIEditorObjectFieldMetrics& metrics) {
|
||||
const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout(
|
||||
bounds,
|
||||
metrics.valueBoxMinWidth,
|
||||
UIEditorFieldRowLayoutMetrics {
|
||||
metrics.rowHeight,
|
||||
metrics.horizontalPadding,
|
||||
metrics.labelControlGap,
|
||||
metrics.controlColumnStart,
|
||||
metrics.controlMinWidth,
|
||||
metrics.controlTrailingInset,
|
||||
metrics.controlInsetY,
|
||||
});
|
||||
|
||||
UIEditorObjectFieldLayout layout = {};
|
||||
layout.bounds = hostLayout.bounds;
|
||||
layout.labelRect = hostLayout.labelRect;
|
||||
layout.controlRect = hostLayout.controlRect;
|
||||
|
||||
const float pickerWidth =
|
||||
HasPickerButton(spec) ? (std::min)(metrics.buttonWidth, layout.controlRect.width) : 0.0f;
|
||||
const float clearWidth =
|
||||
HasClearButton(spec)
|
||||
? (std::min)(metrics.buttonWidth, ClampNonNegative(layout.controlRect.width - pickerWidth))
|
||||
: 0.0f;
|
||||
const float buttonWidth = pickerWidth + clearWidth;
|
||||
layout.valueRect = UIRect(
|
||||
layout.controlRect.x,
|
||||
layout.controlRect.y,
|
||||
ClampNonNegative(layout.controlRect.width - buttonWidth),
|
||||
layout.controlRect.height);
|
||||
|
||||
float nextButtonX = layout.valueRect.x + layout.valueRect.width;
|
||||
if (clearWidth > 0.0f) {
|
||||
layout.clearButtonRect = UIRect(
|
||||
nextButtonX,
|
||||
layout.controlRect.y,
|
||||
clearWidth,
|
||||
layout.controlRect.height);
|
||||
nextButtonX += clearWidth;
|
||||
}
|
||||
if (pickerWidth > 0.0f) {
|
||||
layout.pickerButtonRect = UIRect(
|
||||
nextButtonX,
|
||||
layout.controlRect.y,
|
||||
pickerWidth,
|
||||
layout.controlRect.height);
|
||||
}
|
||||
|
||||
if (HasTypeText(spec) && layout.valueRect.width > metrics.typeMinWidth + metrics.valueTextInsetX) {
|
||||
const float reservedWidth = ClampNonNegative(
|
||||
layout.valueRect.width * 0.35f + metrics.typeTextInsetX);
|
||||
const float typeWidth = (std::min)(
|
||||
metrics.typeMaxWidth,
|
||||
(std::max)(metrics.typeMinWidth, reservedWidth));
|
||||
if (layout.valueRect.width > typeWidth + metrics.valueTextInsetX + metrics.valueTypeGap) {
|
||||
layout.typeRect = UIRect(
|
||||
layout.valueRect.x + layout.valueRect.width - typeWidth,
|
||||
layout.valueRect.y,
|
||||
typeWidth,
|
||||
layout.valueRect.height);
|
||||
}
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
UIEditorObjectFieldHitTarget HitTestUIEditorObjectField(
|
||||
const UIEditorObjectFieldLayout& layout,
|
||||
const UIPoint& point) {
|
||||
if (!ContainsPoint(layout.bounds, point)) {
|
||||
return {};
|
||||
}
|
||||
if (layout.pickerButtonRect.width > 0.0f && ContainsPoint(layout.pickerButtonRect, point)) {
|
||||
return { UIEditorObjectFieldHitTargetKind::PickerButton };
|
||||
}
|
||||
if (layout.clearButtonRect.width > 0.0f && ContainsPoint(layout.clearButtonRect, point)) {
|
||||
return { UIEditorObjectFieldHitTargetKind::ClearButton };
|
||||
}
|
||||
if (ContainsPoint(layout.valueRect, point)) {
|
||||
return { UIEditorObjectFieldHitTargetKind::ValueBox };
|
||||
}
|
||||
return { UIEditorObjectFieldHitTargetKind::Row };
|
||||
}
|
||||
|
||||
void AppendUIEditorObjectFieldBackground(
|
||||
UIDrawList& drawList,
|
||||
const UIEditorObjectFieldLayout& layout,
|
||||
const UIEditorObjectFieldSpec& spec,
|
||||
const UIEditorObjectFieldState& state,
|
||||
const UIEditorObjectFieldPalette& palette,
|
||||
const UIEditorObjectFieldMetrics& metrics) {
|
||||
const auto rowFillColor = ResolveRowFillColor(state, palette);
|
||||
if (rowFillColor.a > 0.0f) {
|
||||
drawList.AddFilledRect(layout.bounds, rowFillColor, metrics.cornerRounding);
|
||||
}
|
||||
const auto rowBorderColor = state.focused ? palette.focusedBorderColor : palette.borderColor;
|
||||
if (rowBorderColor.a > 0.0f) {
|
||||
drawList.AddRectOutline(
|
||||
layout.bounds,
|
||||
rowBorderColor,
|
||||
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
|
||||
metrics.cornerRounding);
|
||||
}
|
||||
|
||||
drawList.AddFilledRect(
|
||||
layout.controlRect,
|
||||
ResolveControlFillColor(spec, state, palette),
|
||||
metrics.valueBoxRounding);
|
||||
drawList.AddRectOutline(
|
||||
layout.controlRect,
|
||||
state.focused ? palette.controlFocusedBorderColor : palette.controlBorderColor,
|
||||
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
|
||||
metrics.valueBoxRounding);
|
||||
|
||||
if (layout.clearButtonRect.width > 0.0f) {
|
||||
drawList.AddFilledRect(
|
||||
layout.clearButtonRect,
|
||||
ResolveButtonFillColor(spec, state, UIEditorObjectFieldHitTargetKind::ClearButton, palette));
|
||||
drawList.AddLine(
|
||||
UIPoint(layout.clearButtonRect.x, layout.clearButtonRect.y + 1.0f),
|
||||
UIPoint(layout.clearButtonRect.x, layout.clearButtonRect.y + layout.clearButtonRect.height - 1.0f),
|
||||
palette.separatorColor,
|
||||
1.0f);
|
||||
}
|
||||
|
||||
if (layout.pickerButtonRect.width > 0.0f) {
|
||||
drawList.AddFilledRect(
|
||||
layout.pickerButtonRect,
|
||||
ResolveButtonFillColor(spec, state, UIEditorObjectFieldHitTargetKind::PickerButton, palette));
|
||||
drawList.AddLine(
|
||||
UIPoint(layout.pickerButtonRect.x, layout.pickerButtonRect.y + 1.0f),
|
||||
UIPoint(layout.pickerButtonRect.x, layout.pickerButtonRect.y + layout.pickerButtonRect.height - 1.0f),
|
||||
palette.separatorColor,
|
||||
1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void AppendUIEditorObjectFieldForeground(
|
||||
UIDrawList& drawList,
|
||||
const UIEditorObjectFieldLayout& layout,
|
||||
const UIEditorObjectFieldSpec& spec,
|
||||
const UIEditorObjectFieldPalette& palette,
|
||||
const UIEditorObjectFieldMetrics& metrics) {
|
||||
drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, metrics.labelFontSize));
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
layout.labelRect.x,
|
||||
ResolveUIEditorTextTop(layout.labelRect, metrics.labelFontSize, metrics.labelTextInsetY)),
|
||||
spec.label,
|
||||
palette.labelColor,
|
||||
metrics.labelFontSize);
|
||||
drawList.PopClipRect();
|
||||
|
||||
float primaryClipWidth = layout.valueRect.width;
|
||||
if (layout.typeRect.width > 0.0f) {
|
||||
primaryClipWidth =
|
||||
ClampNonNegative(layout.typeRect.x - layout.valueRect.x - metrics.valueTypeGap);
|
||||
}
|
||||
const UIRect primaryClipRect(
|
||||
layout.valueRect.x + metrics.valueTextInsetX,
|
||||
layout.valueRect.y,
|
||||
ClampNonNegative(primaryClipWidth - metrics.valueTextInsetX),
|
||||
layout.valueRect.height);
|
||||
drawList.PushClipRect(ResolveUIEditorTextClipRect(primaryClipRect, metrics.valueFontSize));
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
layout.valueRect.x + metrics.valueTextInsetX,
|
||||
ResolveUIEditorTextTop(layout.valueRect, metrics.valueFontSize, metrics.valueTextInsetY)),
|
||||
ResolveUIEditorObjectFieldDisplayText(spec),
|
||||
spec.hasValue ? palette.valueColor : palette.emptyValueColor,
|
||||
metrics.valueFontSize);
|
||||
drawList.PopClipRect();
|
||||
|
||||
if (layout.typeRect.width > 0.0f) {
|
||||
const UIRect typeClipRect(
|
||||
layout.typeRect.x + metrics.typeTextInsetX,
|
||||
layout.typeRect.y,
|
||||
ClampNonNegative(layout.typeRect.width - metrics.typeTextInsetX),
|
||||
layout.typeRect.height);
|
||||
drawList.PushClipRect(ResolveUIEditorTextClipRect(typeClipRect, metrics.typeFontSize));
|
||||
drawList.AddText(
|
||||
UIPoint(
|
||||
layout.typeRect.x + metrics.typeTextInsetX,
|
||||
ResolveUIEditorTextTop(layout.typeRect, metrics.typeFontSize, metrics.typeTextInsetY)),
|
||||
spec.objectTypeName,
|
||||
palette.typeColor,
|
||||
metrics.typeFontSize);
|
||||
drawList.PopClipRect();
|
||||
}
|
||||
|
||||
AppendButtonGlyph(drawList, layout.clearButtonRect, "X", palette, metrics);
|
||||
AppendButtonGlyph(drawList, layout.pickerButtonRect, "o", palette, metrics);
|
||||
}
|
||||
|
||||
void AppendUIEditorObjectField(
|
||||
UIDrawList& drawList,
|
||||
const UIRect& bounds,
|
||||
const UIEditorObjectFieldSpec& spec,
|
||||
const UIEditorObjectFieldState& state,
|
||||
const UIEditorObjectFieldPalette& palette,
|
||||
const UIEditorObjectFieldMetrics& metrics) {
|
||||
const UIEditorObjectFieldLayout layout = BuildUIEditorObjectFieldLayout(bounds, spec, metrics);
|
||||
AppendUIEditorObjectFieldBackground(drawList, layout, spec, state, palette, metrics);
|
||||
AppendUIEditorObjectFieldForeground(drawList, layout, spec, palette, metrics);
|
||||
}
|
||||
|
||||
} // namespace XCEngine::UI::Editor::Widgets
|
||||
Reference in New Issue
Block a user