#include "Fields/UIEditorPropertyGridInternal.h" #include 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& 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& 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 {}; } 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)); case UIEditorPropertyGridFieldKind::Enum: return ResolveUIEditorEnumFieldValueText(Detail::BuildEnumFieldSpec(field)); case UIEditorPropertyGridFieldKind::Color: return Detail::FormatColorValueText(field); case UIEditorPropertyGridFieldKind::Vector2: return Detail::FormatVector2ValueText(field); case UIEditorPropertyGridFieldKind::Vector3: return Detail::FormatVector3ValueText(field); case UIEditorPropertyGridFieldKind::Vector4: return Detail::FormatVector4ValueText(field); case UIEditorPropertyGridFieldKind::Text: default: return field.valueText; } } std::size_t FindUIEditorPropertyGridVisibleFieldIndex( const UIEditorPropertyGridLayout& layout, std::string_view fieldId, const std::vector& 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& 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); 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