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

224 lines
8.1 KiB
C++
Raw Normal View History

#include "Fields/UIEditorPropertyGridInternal.h"
2026-04-08 02:52:28 +08:00
#include <algorithm>
namespace XCEngine::UI::Editor::Widgets {
bool IsUIEditorPropertyGridPointInside(
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 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(Detail::BuildNumberFieldSpec(field));
2026-04-08 02:52:28 +08:00
case UIEditorPropertyGridFieldKind::Enum:
return ResolveUIEditorEnumFieldValueText(Detail::BuildEnumFieldSpec(field));
2026-04-08 02:52:28 +08:00
2026-04-10 00:41:28 +08:00
case UIEditorPropertyGridFieldKind::Color:
return Detail::FormatColorValueText(field);
2026-04-10 00:41:28 +08:00
case UIEditorPropertyGridFieldKind::Vector2:
return Detail::FormatVector2ValueText(field);
case UIEditorPropertyGridFieldKind::Vector3:
return Detail::FormatVector3ValueText(field);
case UIEditorPropertyGridFieldKind::Vector4:
return Detail::FormatVector4ValueText(field);
2026-04-08 02:52:28 +08:00
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(
const ::XCEngine::UI::UIRect& bounds,
const std::vector<UIEditorPropertyGridSection>& sections,
const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel,
const UIEditorPropertyGridMetrics& metrics) {
UIEditorPropertyGridLayout layout = {};
layout.bounds = ::XCEngine::UI::UIRect(
bounds.x,
bounds.y,
Detail::ClampNonNegative(bounds.width),
Detail::ClampNonNegative(bounds.height));
const float inset = Detail::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 =
Detail::ResolveSectionHeaderHeight(section, metrics);
const bool expanded = expansionModel.IsExpanded(section.sectionId);
const ::XCEngine::UI::UIRect headerRect(
contentX,
cursorY,
contentWidth,
headerHeight);
const ::XCEngine::UI::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;
const ::XCEngine::UI::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 =
Detail::ResolveFieldRowHeight(field, metrics);
const ::XCEngine::UI::UIRect rowRect(
contentX,
cursorY,
contentWidth,
rowHeight);
const Detail::UIEditorPropertyGridFieldRects fieldRects =
Detail::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,
const ::XCEngine::UI::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 {};
}
} // namespace XCEngine::UI::Editor::Widgets