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

739 lines
28 KiB
C++
Raw Normal View History

#include <XCEditor/Widgets/UIEditorPropertyGrid.h>
2026-04-08 02:52:28 +08:00
#include <XCEditor/Core/UIEditorTheme.h>
#include <XCEditor/Widgets/UIEditorBoolField.h>
#include <XCEditor/Widgets/UIEditorEnumField.h>
#include <XCEditor/Widgets/UIEditorMenuPopup.h>
#include <XCEditor/Widgets/UIEditorNumberField.h>
#include <XCEditor/Widgets/UIEditorTextField.h>
#include <XCEditor/Widgets/UIEditorTextLayout.h>
#include <XCEngine/UI/Widgets/UIPopupOverlayModel.h>
#include <algorithm>
2026-04-08 02:52:28 +08:00
#include <utility>
namespace XCEngine::UI::Editor::Widgets {
namespace {
2026-04-08 02:52:28 +08:00
using ::XCEngine::UI::UIDrawList;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIRect;
using ::XCEngine::UI::UISize;
using ::XCEngine::UI::Widgets::ResolvePopupPlacementRect;
using ::XCEngine::UI::Widgets::UIPopupPlacement;
using ::XCEngine::UI::Editor::UIEditorMenuItemKind;
float ClampNonNegative(float value) {
return (std::max)(value, 0.0f);
}
float ResolveSectionHeaderHeight(
const UIEditorPropertyGridSection& section,
const UIEditorPropertyGridMetrics& metrics) {
return section.desiredHeaderHeight > 0.0f
? section.desiredHeaderHeight
: metrics.sectionHeaderHeight;
}
float ResolveFieldRowHeight(
const UIEditorPropertyGridField& field,
const UIEditorPropertyGridMetrics& metrics) {
return field.desiredHeight > 0.0f
? field.desiredHeight
: metrics.fieldRowHeight;
}
2026-04-08 02:52:28 +08:00
UIPoint ResolveDisclosureGlyphPosition(
const UIRect& rect,
const UIEditorPropertyGridMetrics& metrics) {
return UIPoint(
rect.x + metrics.disclosureGlyphInsetX,
rect.y + metrics.sectionTextInsetY + metrics.disclosureGlyphInsetY);
}
UIEditorBoolFieldSpec BuildBoolFieldSpec(const UIEditorPropertyGridField& field) {
UIEditorBoolFieldSpec spec = {};
spec.fieldId = field.fieldId;
spec.label = field.label;
spec.value = field.boolValue;
spec.readOnly = field.readOnly;
return spec;
}
UIEditorNumberFieldSpec BuildNumberFieldSpec(const UIEditorPropertyGridField& field) {
UIEditorNumberFieldSpec spec = {};
spec.fieldId = field.fieldId;
spec.label = field.label;
spec.value = field.numberValue.value;
spec.step = field.numberValue.step;
spec.minValue = field.numberValue.minValue;
spec.maxValue = field.numberValue.maxValue;
spec.integerMode = field.numberValue.integerMode;
spec.readOnly = field.readOnly;
return spec;
}
UIEditorEnumFieldSpec BuildEnumFieldSpec(const UIEditorPropertyGridField& field) {
UIEditorEnumFieldSpec spec = {};
spec.fieldId = field.fieldId;
spec.label = field.label;
spec.options = field.enumValue.options;
spec.selectedIndex = field.enumValue.selectedIndex;
spec.readOnly = field.readOnly;
return spec;
}
UIEditorTextFieldSpec BuildTextFieldSpec(const UIEditorPropertyGridField& field) {
UIEditorTextFieldSpec spec = {};
spec.fieldId = field.fieldId;
spec.label = field.label;
spec.value = field.valueText;
spec.readOnly = field.readOnly;
return spec;
}
struct UIEditorPropertyGridFieldRects {
UIRect labelRect = {};
UIRect valueRect = {};
};
UIEditorPropertyGridFieldRects ResolveFieldRects(
const UIRect& rowRect,
const UIEditorPropertyGridField& field,
const UIEditorPropertyGridMetrics& metrics) {
switch (field.kind) {
case UIEditorPropertyGridFieldKind::Bool: {
const UIEditorBoolFieldLayout fieldLayout = BuildUIEditorBoolFieldLayout(
rowRect,
BuildBoolFieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedBoolFieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.controlRect };
}
case UIEditorPropertyGridFieldKind::Number: {
const UIEditorNumberFieldLayout fieldLayout = BuildUIEditorNumberFieldLayout(
rowRect,
BuildNumberFieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedNumberFieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.valueRect };
}
case UIEditorPropertyGridFieldKind::Enum: {
const UIEditorEnumFieldLayout fieldLayout = BuildUIEditorEnumFieldLayout(
rowRect,
BuildEnumFieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedEnumFieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.valueRect };
}
case UIEditorPropertyGridFieldKind::Text:
default: {
const UIEditorTextFieldLayout fieldLayout = BuildUIEditorTextFieldLayout(
rowRect,
BuildTextFieldSpec(field),
::XCEngine::UI::Editor::BuildUIEditorHostedTextFieldMetrics(metrics));
return { fieldLayout.labelRect, fieldLayout.valueRect };
}
}
}
2026-04-08 02:52:28 +08:00
const std::string& ResolveDisplayedTextFieldValue(
const UIEditorPropertyGridField& field,
const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel) {
2026-04-08 02:52:28 +08:00
if (field.kind == UIEditorPropertyGridFieldKind::Text &&
propertyEditModel.HasActiveEdit() &&
propertyEditModel.GetActiveFieldId() == field.fieldId) {
return propertyEditModel.GetStagedValue();
}
return field.valueText;
}
2026-04-08 02:52:28 +08:00
UIEditorBoolFieldHitTargetKind ResolveBoolHoveredTarget(
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridField& field) {
if (state.hoveredFieldId != field.fieldId) {
return UIEditorBoolFieldHitTargetKind::None;
}
return state.hoveredHitTarget == UIEditorPropertyGridHitTargetKind::ValueBox
? UIEditorBoolFieldHitTargetKind::Checkbox
: UIEditorBoolFieldHitTargetKind::Row;
}
UIEditorNumberFieldHitTargetKind ResolveNumberHoveredTarget(
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridField& field) {
if (state.hoveredFieldId != field.fieldId) {
return UIEditorNumberFieldHitTargetKind::None;
}
return state.hoveredHitTarget == UIEditorPropertyGridHitTargetKind::ValueBox
? UIEditorNumberFieldHitTargetKind::ValueBox
: UIEditorNumberFieldHitTargetKind::Row;
}
UIEditorEnumFieldHitTargetKind ResolveEnumHoveredTarget(
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridField& field) {
if (state.hoveredFieldId != field.fieldId) {
return UIEditorEnumFieldHitTargetKind::None;
}
return state.hoveredHitTarget == UIEditorPropertyGridHitTargetKind::ValueBox
? UIEditorEnumFieldHitTargetKind::ValueBox
: UIEditorEnumFieldHitTargetKind::Row;
}
UIEditorTextFieldHitTargetKind ResolveTextHoveredTarget(
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridField& field) {
if (state.hoveredFieldId != field.fieldId) {
return UIEditorTextFieldHitTargetKind::None;
}
return state.hoveredHitTarget == UIEditorPropertyGridHitTargetKind::ValueBox
? UIEditorTextFieldHitTargetKind::ValueBox
: UIEditorTextFieldHitTargetKind::Row;
}
std::vector<UIEditorMenuPopupItem> BuildEnumPopupItems(
const UIEditorPropertyGridField& field) {
std::vector<UIEditorMenuPopupItem> items = {};
if (field.kind != UIEditorPropertyGridFieldKind::Enum) {
return items;
}
items.reserve(field.enumValue.options.size());
const std::size_t selectedIndex =
field.enumValue.options.empty()
? 0u
: (std::min)(field.enumValue.selectedIndex, field.enumValue.options.size() - 1u);
for (std::size_t index = 0u; index < field.enumValue.options.size(); ++index) {
UIEditorMenuPopupItem item = {};
item.itemId = field.fieldId + "." + std::to_string(index);
item.kind = UIEditorMenuItemKind::Command;
item.label = field.enumValue.options[index];
item.enabled = !field.readOnly;
item.checked = index == selectedIndex;
items.push_back(std::move(item));
}
return items;
}
UIRect ResolvePopupViewportRect(const UIRect& bounds) {
return UIRect(bounds.x - 4096.0f, bounds.y - 4096.0f, 8192.0f, 8192.0f);
}
bool BuildEnumPopupRuntime(
const UIEditorPropertyGridLayout& layout,
const std::vector<UIEditorPropertyGridSection>& sections,
const UIEditorPropertyGridState& state,
const UIEditorMenuPopupMetrics& popupMetrics,
UIEditorMenuPopupLayout& popupLayout,
UIEditorMenuPopupState& popupState,
std::vector<UIEditorMenuPopupItem>& popupItems) {
if (state.popupFieldId.empty()) {
return false;
}
const std::size_t visibleFieldIndex =
FindUIEditorPropertyGridVisibleFieldIndex(layout, state.popupFieldId, sections);
if (visibleFieldIndex == UIEditorPropertyGridInvalidIndex ||
visibleFieldIndex >= layout.visibleFieldSectionIndices.size() ||
visibleFieldIndex >= layout.visibleFieldIndices.size()) {
return false;
}
const std::size_t sectionIndex = layout.visibleFieldSectionIndices[visibleFieldIndex];
const std::size_t fieldIndex = layout.visibleFieldIndices[visibleFieldIndex];
if (sectionIndex >= sections.size() ||
fieldIndex >= sections[sectionIndex].fields.size()) {
return false;
}
const UIEditorPropertyGridField& field = sections[sectionIndex].fields[fieldIndex];
popupItems = BuildEnumPopupItems(field);
if (popupItems.empty()) {
return false;
}
const float popupWidth = (std::max)(
layout.fieldValueRects[visibleFieldIndex].width,
ResolveUIEditorMenuPopupDesiredWidth(popupItems, popupMetrics));
const float popupHeight = MeasureUIEditorMenuPopupHeight(popupItems, popupMetrics);
const auto placement = ResolvePopupPlacementRect(
layout.fieldValueRects[visibleFieldIndex],
UISize(popupWidth, popupHeight),
ResolvePopupViewportRect(layout.bounds),
UIPopupPlacement::BottomStart);
popupLayout = BuildUIEditorMenuPopupLayout(placement.rect, popupItems, popupMetrics);
popupState.focused = state.focused || !state.popupFieldId.empty();
popupState.hoveredIndex =
state.popupHighlightedIndex < popupItems.size()
? state.popupHighlightedIndex
: UIEditorMenuPopupInvalidIndex;
return true;
}
} // namespace
bool IsUIEditorPropertyGridPointInside(
2026-04-08 02:52:28 +08:00
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;
}
std::size_t FindUIEditorPropertyGridSectionIndex(
const std::vector<UIEditorPropertyGridSection>& sections,
std::string_view sectionId) {
for (std::size_t sectionIndex = 0u; sectionIndex < sections.size(); ++sectionIndex) {
if (sections[sectionIndex].sectionId == sectionId) {
return sectionIndex;
}
}
return UIEditorPropertyGridInvalidIndex;
}
UIEditorPropertyGridFieldLocation FindUIEditorPropertyGridFieldLocation(
const std::vector<UIEditorPropertyGridSection>& sections,
std::string_view fieldId) {
for (std::size_t sectionIndex = 0u; sectionIndex < sections.size(); ++sectionIndex) {
const auto& fields = sections[sectionIndex].fields;
for (std::size_t fieldIndex = 0u; fieldIndex < fields.size(); ++fieldIndex) {
if (fields[fieldIndex].fieldId == fieldId) {
return { sectionIndex, fieldIndex };
}
}
}
return {};
}
2026-04-08 02:52:28 +08:00
std::string ResolveUIEditorPropertyGridFieldValueText(
const UIEditorPropertyGridField& field) {
switch (field.kind) {
case UIEditorPropertyGridFieldKind::Bool:
return field.boolValue ? "true" : "false";
case UIEditorPropertyGridFieldKind::Number:
return FormatUIEditorNumberFieldValue(BuildNumberFieldSpec(field));
case UIEditorPropertyGridFieldKind::Enum:
return ResolveUIEditorEnumFieldValueText(BuildEnumFieldSpec(field));
case UIEditorPropertyGridFieldKind::Text:
default:
return field.valueText;
}
}
std::size_t FindUIEditorPropertyGridVisibleFieldIndex(
const UIEditorPropertyGridLayout& layout,
std::string_view fieldId,
const std::vector<UIEditorPropertyGridSection>& sections) {
for (std::size_t visibleIndex = 0u;
visibleIndex < layout.visibleFieldIndices.size();
++visibleIndex) {
const std::size_t sectionIndex = layout.visibleFieldSectionIndices[visibleIndex];
const std::size_t fieldIndex = layout.visibleFieldIndices[visibleIndex];
if (sectionIndex >= sections.size() ||
fieldIndex >= sections[sectionIndex].fields.size()) {
continue;
}
if (sections[sectionIndex].fields[fieldIndex].fieldId == fieldId) {
return visibleIndex;
}
}
return UIEditorPropertyGridInvalidIndex;
}
UIEditorPropertyGridLayout BuildUIEditorPropertyGridLayout(
2026-04-08 02:52:28 +08:00
const UIRect& bounds,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const UIEditorPropertyGridMetrics& metrics) {
UIEditorPropertyGridLayout layout = {};
2026-04-08 02:52:28 +08:00
layout.bounds = UIRect(
bounds.x,
bounds.y,
ClampNonNegative(bounds.width),
ClampNonNegative(bounds.height));
const float inset = ClampNonNegative(metrics.contentInset);
const float contentX = layout.bounds.x + inset;
const float contentY = layout.bounds.y + inset;
const float contentWidth = (std::max)(0.0f, layout.bounds.width - inset * 2.0f);
float cursorY = contentY;
for (std::size_t sectionIndex = 0u; sectionIndex < sections.size(); ++sectionIndex) {
const UIEditorPropertyGridSection& section = sections[sectionIndex];
const float headerHeight = ResolveSectionHeaderHeight(section, metrics);
const bool expanded = expansionModel.IsExpanded(section.sectionId);
2026-04-08 02:52:28 +08:00
const UIRect headerRect(
contentX,
cursorY,
contentWidth,
headerHeight);
2026-04-08 02:52:28 +08:00
const UIRect disclosureRect(
headerRect.x + metrics.horizontalPadding,
headerRect.y + (headerRect.height - metrics.disclosureExtent) * 0.5f,
metrics.disclosureExtent,
metrics.disclosureExtent);
const float titleX =
disclosureRect.x + disclosureRect.width + metrics.disclosureLabelGap;
2026-04-08 02:52:28 +08:00
const UIRect titleRect(
titleX,
headerRect.y,
(std::max)(0.0f, headerRect.x + headerRect.width - titleX - metrics.horizontalPadding),
headerRect.height);
layout.sectionIndices.push_back(sectionIndex);
layout.sectionHeaderRects.push_back(headerRect);
layout.sectionDisclosureRects.push_back(disclosureRect);
layout.sectionTitleRects.push_back(titleRect);
layout.sectionExpanded.push_back(expanded);
cursorY += headerHeight;
if (expanded) {
for (std::size_t fieldIndex = 0u; fieldIndex < section.fields.size(); ++fieldIndex) {
const UIEditorPropertyGridField& field = section.fields[fieldIndex];
const float rowHeight = ResolveFieldRowHeight(field, metrics);
2026-04-08 02:52:28 +08:00
const UIRect rowRect(
contentX,
cursorY,
contentWidth,
rowHeight);
2026-04-08 02:52:28 +08:00
const UIEditorPropertyGridFieldRects fieldRects =
ResolveFieldRects(rowRect, field, metrics);
layout.visibleFieldSectionIndices.push_back(sectionIndex);
layout.visibleFieldIndices.push_back(fieldIndex);
layout.fieldRowRects.push_back(rowRect);
2026-04-08 02:52:28 +08:00
layout.fieldLabelRects.push_back(fieldRects.labelRect);
layout.fieldValueRects.push_back(fieldRects.valueRect);
layout.fieldReadOnly.push_back(field.readOnly);
cursorY += rowHeight + metrics.rowGap;
}
if (!section.fields.empty()) {
cursorY -= metrics.rowGap;
}
}
cursorY += metrics.sectionGap;
}
return layout;
}
UIEditorPropertyGridHitTarget HitTestUIEditorPropertyGrid(
const UIEditorPropertyGridLayout& layout,
2026-04-08 02:52:28 +08:00
const UIPoint& point) {
for (std::size_t sectionVisibleIndex = 0u;
sectionVisibleIndex < layout.sectionHeaderRects.size();
++sectionVisibleIndex) {
if (!IsUIEditorPropertyGridPointInside(layout.sectionHeaderRects[sectionVisibleIndex], point)) {
continue;
}
UIEditorPropertyGridHitTarget target = {};
target.kind = UIEditorPropertyGridHitTargetKind::SectionHeader;
target.sectionIndex = layout.sectionIndices[sectionVisibleIndex];
return target;
}
for (std::size_t visibleFieldIndex = 0u;
visibleFieldIndex < layout.fieldRowRects.size();
++visibleFieldIndex) {
if (!IsUIEditorPropertyGridPointInside(layout.fieldRowRects[visibleFieldIndex], point)) {
continue;
}
UIEditorPropertyGridHitTarget target = {};
target.visibleFieldIndex = visibleFieldIndex;
target.sectionIndex = layout.visibleFieldSectionIndices[visibleFieldIndex];
target.fieldIndex = layout.visibleFieldIndices[visibleFieldIndex];
target.kind = IsUIEditorPropertyGridPointInside(layout.fieldValueRects[visibleFieldIndex], point)
? UIEditorPropertyGridHitTargetKind::ValueBox
: UIEditorPropertyGridHitTargetKind::FieldRow;
return target;
}
return {};
}
void AppendUIEditorPropertyGridBackground(
2026-04-08 02:52:28 +08:00
UIDrawList& drawList,
const UIEditorPropertyGridLayout& layout,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
2026-04-08 02:52:28 +08:00
const ::XCEngine::UI::Widgets::UIPropertyEditModel&,
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridPalette& palette,
const UIEditorPropertyGridMetrics& metrics) {
drawList.AddFilledRect(layout.bounds, palette.surfaceColor, metrics.cornerRounding);
drawList.AddRectOutline(
layout.bounds,
state.focused ? palette.focusedBorderColor : palette.borderColor,
state.focused ? metrics.focusedBorderThickness : metrics.borderThickness,
metrics.cornerRounding);
for (std::size_t sectionVisibleIndex = 0u;
sectionVisibleIndex < layout.sectionHeaderRects.size();
++sectionVisibleIndex) {
const UIEditorPropertyGridSection& section =
sections[layout.sectionIndices[sectionVisibleIndex]];
const bool hovered =
state.hoveredFieldId.empty() &&
state.hoveredSectionId == section.sectionId;
drawList.AddFilledRect(
layout.sectionHeaderRects[sectionVisibleIndex],
hovered ? palette.sectionHeaderHoverColor : palette.sectionHeaderColor,
metrics.cornerRounding);
}
for (std::size_t visibleFieldIndex = 0u;
visibleFieldIndex < layout.fieldRowRects.size();
++visibleFieldIndex) {
const UIEditorPropertyGridSection& section =
sections[layout.visibleFieldSectionIndices[visibleFieldIndex]];
const UIEditorPropertyGridField& field =
section.fields[layout.visibleFieldIndices[visibleFieldIndex]];
const bool selected = selectionModel.IsSelected(field.fieldId);
const bool hovered = state.hoveredFieldId == field.fieldId;
if (selected || hovered) {
drawList.AddFilledRect(
layout.fieldRowRects[visibleFieldIndex],
selected
? (state.focused ? palette.fieldSelectedFocusedColor : palette.fieldSelectedColor)
: palette.fieldHoverColor,
metrics.cornerRounding);
}
}
}
void AppendUIEditorPropertyGridForeground(
2026-04-08 02:52:28 +08:00
UIDrawList& drawList,
const UIEditorPropertyGridLayout& layout,
const std::vector<UIEditorPropertyGridSection>& sections,
2026-04-08 02:52:28 +08:00
const UIEditorPropertyGridState& state,
const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const UIEditorPropertyGridPalette& palette,
2026-04-08 02:52:28 +08:00
const UIEditorPropertyGridMetrics& metrics,
const UIEditorMenuPopupPalette& popupPalette,
const UIEditorMenuPopupMetrics& popupMetrics) {
drawList.PushClipRect(layout.bounds);
for (std::size_t sectionVisibleIndex = 0u;
sectionVisibleIndex < layout.sectionHeaderRects.size();
++sectionVisibleIndex) {
const UIEditorPropertyGridSection& section =
sections[layout.sectionIndices[sectionVisibleIndex]];
drawList.AddText(
ResolveDisclosureGlyphPosition(
layout.sectionDisclosureRects[sectionVisibleIndex],
2026-04-08 02:52:28 +08:00
metrics),
layout.sectionExpanded[sectionVisibleIndex] ? "v" : ">",
palette.disclosureColor,
2026-04-08 02:52:28 +08:00
metrics.disclosureGlyphFontSize);
drawList.AddText(
2026-04-08 02:52:28 +08:00
UIPoint(
layout.sectionTitleRects[sectionVisibleIndex].x,
layout.sectionTitleRects[sectionVisibleIndex].y + metrics.sectionTextInsetY),
section.title,
palette.sectionTextColor,
2026-04-08 02:52:28 +08:00
metrics.sectionFontSize);
}
2026-04-08 02:52:28 +08:00
const UIEditorBoolFieldMetrics boolMetrics =
::XCEngine::UI::Editor::BuildUIEditorHostedBoolFieldMetrics(metrics);
const UIEditorBoolFieldPalette boolPalette =
::XCEngine::UI::Editor::BuildUIEditorHostedBoolFieldPalette(palette);
const UIEditorNumberFieldMetrics numberMetrics =
::XCEngine::UI::Editor::BuildUIEditorHostedNumberFieldMetrics(metrics);
const UIEditorNumberFieldPalette numberPalette =
::XCEngine::UI::Editor::BuildUIEditorHostedNumberFieldPalette(palette);
const UIEditorTextFieldMetrics textMetrics =
::XCEngine::UI::Editor::BuildUIEditorHostedTextFieldMetrics(metrics);
const UIEditorTextFieldPalette textPalette =
::XCEngine::UI::Editor::BuildUIEditorHostedTextFieldPalette(palette);
const UIEditorEnumFieldMetrics enumMetrics =
::XCEngine::UI::Editor::BuildUIEditorHostedEnumFieldMetrics(metrics);
const UIEditorEnumFieldPalette enumPalette =
::XCEngine::UI::Editor::BuildUIEditorHostedEnumFieldPalette(palette);
for (std::size_t visibleFieldIndex = 0u;
visibleFieldIndex < layout.fieldRowRects.size();
++visibleFieldIndex) {
const UIEditorPropertyGridSection& section =
sections[layout.visibleFieldSectionIndices[visibleFieldIndex]];
const UIEditorPropertyGridField& field =
section.fields[layout.visibleFieldIndices[visibleFieldIndex]];
const bool editing = propertyEditModel.HasActiveEdit() &&
propertyEditModel.GetActiveFieldId() == field.fieldId;
2026-04-08 02:52:28 +08:00
switch (field.kind) {
case UIEditorPropertyGridFieldKind::Bool: {
UIEditorBoolFieldState fieldState = {};
fieldState.hoveredTarget = ResolveBoolHoveredTarget(state, field);
fieldState.focused = state.focused;
fieldState.active = state.pressedFieldId == field.fieldId;
AppendUIEditorBoolField(
drawList,
layout.fieldRowRects[visibleFieldIndex],
BuildBoolFieldSpec(field),
fieldState,
boolPalette,
boolMetrics);
break;
}
case UIEditorPropertyGridFieldKind::Number: {
UIEditorNumberFieldState fieldState = {};
fieldState.hoveredTarget = ResolveNumberHoveredTarget(state, field);
fieldState.activeTarget =
state.pressedFieldId == field.fieldId
? UIEditorNumberFieldHitTargetKind::ValueBox
: UIEditorNumberFieldHitTargetKind::None;
fieldState.focused = state.focused;
fieldState.editing = editing;
fieldState.displayText = editing
? propertyEditModel.GetStagedValue()
: ResolveUIEditorPropertyGridFieldValueText(field);
AppendUIEditorNumberField(
drawList,
layout.fieldRowRects[visibleFieldIndex],
BuildNumberFieldSpec(field),
fieldState,
numberPalette,
numberMetrics);
break;
}
case UIEditorPropertyGridFieldKind::Enum: {
UIEditorEnumFieldState fieldState = {};
fieldState.hoveredTarget = ResolveEnumHoveredTarget(state, field);
fieldState.focused = state.focused;
fieldState.active = state.pressedFieldId == field.fieldId;
fieldState.popupOpen = state.popupFieldId == field.fieldId;
AppendUIEditorEnumField(
drawList,
layout.fieldRowRects[visibleFieldIndex],
BuildEnumFieldSpec(field),
fieldState,
enumPalette,
enumMetrics);
break;
}
case UIEditorPropertyGridFieldKind::Text:
default: {
const std::string& displayedValue =
ResolveDisplayedTextFieldValue(field, propertyEditModel);
UIEditorTextFieldState fieldState = {};
fieldState.hoveredTarget = ResolveTextHoveredTarget(state, field);
fieldState.activeTarget =
state.pressedFieldId == field.fieldId
? (state.hoveredHitTarget == UIEditorPropertyGridHitTargetKind::ValueBox
? UIEditorTextFieldHitTargetKind::ValueBox
: UIEditorTextFieldHitTargetKind::Row)
: UIEditorTextFieldHitTargetKind::None;
fieldState.focused = state.focused;
fieldState.editing = editing;
fieldState.displayText = displayedValue;
UIEditorTextFieldSpec fieldSpec = BuildTextFieldSpec(field);
fieldSpec.value = editing ? std::string() : displayedValue;
AppendUIEditorTextField(
drawList,
layout.fieldRowRects[visibleFieldIndex],
fieldSpec,
fieldState,
textPalette,
textMetrics);
if (editing) {
drawList.PushClipRect(
ResolveUIEditorTextClipRect(
layout.fieldValueRects[visibleFieldIndex],
metrics.tagFontSize));
drawList.AddText(
UIPoint(
layout.fieldValueRects[visibleFieldIndex].x +
(std::max)(0.0f, layout.fieldValueRects[visibleFieldIndex].width - 34.0f),
ResolveUIEditorTextTop(
layout.fieldValueRects[visibleFieldIndex],
metrics.tagFontSize,
metrics.valueTextInsetY)),
"EDIT",
palette.editTagColor,
metrics.tagFontSize);
drawList.PopClipRect();
}
break;
}
}
}
drawList.PopClipRect();
2026-04-08 02:52:28 +08:00
UIEditorMenuPopupLayout popupLayout = {};
UIEditorMenuPopupState popupState = {};
std::vector<UIEditorMenuPopupItem> popupItems = {};
if (BuildEnumPopupRuntime(layout, sections, state, popupMetrics, popupLayout, popupState, popupItems)) {
AppendUIEditorMenuPopupBackground(drawList, popupLayout, popupItems, popupState, popupPalette, popupMetrics);
AppendUIEditorMenuPopupForeground(drawList, popupLayout, popupItems, popupState, popupPalette, popupMetrics);
}
}
void AppendUIEditorPropertyGrid(
2026-04-08 02:52:28 +08:00
UIDrawList& drawList,
const UIRect& bounds,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel,
const UIEditorPropertyGridState& state,
const UIEditorPropertyGridPalette& palette,
2026-04-08 02:52:28 +08:00
const UIEditorPropertyGridMetrics& metrics,
const UIEditorMenuPopupPalette& popupPalette,
const UIEditorMenuPopupMetrics& popupMetrics) {
const UIEditorPropertyGridLayout layout =
BuildUIEditorPropertyGridLayout(bounds, sections, expansionModel, metrics);
AppendUIEditorPropertyGridBackground(
drawList,
layout,
sections,
selectionModel,
propertyEditModel,
state,
palette,
metrics);
AppendUIEditorPropertyGridForeground(
drawList,
layout,
sections,
2026-04-08 02:52:28 +08:00
state,
propertyEditModel,
palette,
2026-04-08 02:52:28 +08:00
metrics,
popupPalette,
popupMetrics);
}
} // namespace XCEngine::UI::Editor::Widgets