#include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace XCEngine::UI::Editor::Widgets { namespace { 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; } 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; } UIEditorVector2FieldSpec BuildVector2FieldSpec(const UIEditorPropertyGridField& field) { UIEditorVector2FieldSpec spec = {}; spec.fieldId = field.fieldId; spec.label = field.label; spec.values = field.vector2Value.values; spec.componentLabels = field.vector2Value.componentLabels; spec.step = field.vector2Value.step; spec.minValue = field.vector2Value.minValue; spec.maxValue = field.vector2Value.maxValue; spec.integerMode = field.vector2Value.integerMode; spec.readOnly = field.readOnly; return spec; } UIEditorVector3FieldSpec BuildVector3FieldSpec(const UIEditorPropertyGridField& field) { UIEditorVector3FieldSpec spec = {}; spec.fieldId = field.fieldId; spec.label = field.label; spec.values = field.vector3Value.values; spec.componentLabels = field.vector3Value.componentLabels; spec.step = field.vector3Value.step; spec.minValue = field.vector3Value.minValue; spec.maxValue = field.vector3Value.maxValue; spec.integerMode = field.vector3Value.integerMode; spec.readOnly = field.readOnly; return spec; } UIEditorVector4FieldSpec BuildVector4FieldSpec(const UIEditorPropertyGridField& field) { UIEditorVector4FieldSpec spec = {}; spec.fieldId = field.fieldId; spec.label = field.label; spec.values = field.vector4Value.values; spec.componentLabels = field.vector4Value.componentLabels; spec.step = field.vector4Value.step; spec.minValue = field.vector4Value.minValue; spec.maxValue = field.vector4Value.maxValue; spec.integerMode = field.vector4Value.integerMode; 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::Vector2: { const UIEditorVector2FieldLayout fieldLayout = BuildUIEditorVector2FieldLayout( rowRect, BuildVector2FieldSpec(field), ::XCEngine::UI::Editor::BuildUIEditorHostedVector2FieldMetrics(metrics)); return { fieldLayout.labelRect, fieldLayout.controlRect }; } case UIEditorPropertyGridFieldKind::Vector3: { const UIEditorVector3FieldLayout fieldLayout = BuildUIEditorVector3FieldLayout( rowRect, BuildVector3FieldSpec(field), ::XCEngine::UI::Editor::BuildUIEditorHostedVector3FieldMetrics(metrics)); return { fieldLayout.labelRect, fieldLayout.controlRect }; } case UIEditorPropertyGridFieldKind::Vector4: { const UIEditorVector4FieldLayout fieldLayout = BuildUIEditorVector4FieldLayout( rowRect, BuildVector4FieldSpec(field), ::XCEngine::UI::Editor::BuildUIEditorHostedVector4FieldMetrics(metrics)); return { fieldLayout.labelRect, fieldLayout.controlRect }; } case UIEditorPropertyGridFieldKind::Text: default: { const UIEditorTextFieldLayout fieldLayout = BuildUIEditorTextFieldLayout( rowRect, BuildTextFieldSpec(field), ::XCEngine::UI::Editor::BuildUIEditorHostedTextFieldMetrics(metrics)); return { fieldLayout.labelRect, fieldLayout.valueRect }; } } } const std::string& ResolveDisplayedTextFieldValue( const UIEditorPropertyGridField& field, const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel) { if (field.kind == UIEditorPropertyGridFieldKind::Text && propertyEditModel.HasActiveEdit() && propertyEditModel.GetActiveFieldId() == field.fieldId) { return propertyEditModel.GetStagedValue(); } return field.valueText; } 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; } UIEditorVector2FieldHitTargetKind ResolveVector2HoveredTarget( const UIEditorPropertyGridState& state, const UIEditorPropertyGridField& field) { if (state.hoveredFieldId != field.fieldId) { return UIEditorVector2FieldHitTargetKind::None; } return UIEditorVector2FieldHitTargetKind::Row; } UIEditorVector3FieldHitTargetKind ResolveVector3HoveredTarget( const UIEditorPropertyGridState& state, const UIEditorPropertyGridField& field) { if (state.hoveredFieldId != field.fieldId) { return UIEditorVector3FieldHitTargetKind::None; } return UIEditorVector3FieldHitTargetKind::Row; } UIEditorVector4FieldHitTargetKind ResolveVector4HoveredTarget( const UIEditorPropertyGridState& state, const UIEditorPropertyGridField& field) { if (state.hoveredFieldId != field.fieldId) { return UIEditorVector4FieldHitTargetKind::None; } return UIEditorVector4FieldHitTargetKind::Row; } std::vector BuildEnumPopupItems( const UIEditorPropertyGridField& field) { std::vector 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& sections, const UIEditorPropertyGridState& state, const UIEditorMenuPopupMetrics& popupMetrics, UIEditorMenuPopupLayout& popupLayout, UIEditorMenuPopupState& popupState, std::vector& 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; } std::string FormatVector2ValueText(const UIEditorPropertyGridField& field) { const UIEditorVector2FieldSpec spec = BuildVector2FieldSpec(field); return FormatUIEditorVector2FieldComponentValue(spec, 0u) + ", " + FormatUIEditorVector2FieldComponentValue(spec, 1u); } std::string FormatVector3ValueText(const UIEditorPropertyGridField& field) { const UIEditorVector3FieldSpec spec = BuildVector3FieldSpec(field); return FormatUIEditorVector3FieldComponentValue(spec, 0u) + ", " + FormatUIEditorVector3FieldComponentValue(spec, 1u) + ", " + FormatUIEditorVector3FieldComponentValue(spec, 2u); } std::string FormatVector4ValueText(const UIEditorPropertyGridField& field) { const UIEditorVector4FieldSpec spec = BuildVector4FieldSpec(field); return FormatUIEditorVector4FieldComponentValue(spec, 0u) + ", " + FormatUIEditorVector4FieldComponentValue(spec, 1u) + ", " + FormatUIEditorVector4FieldComponentValue(spec, 2u) + ", " + FormatUIEditorVector4FieldComponentValue(spec, 3u); } } // namespace bool IsUIEditorPropertyGridPointInside( 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& 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(BuildNumberFieldSpec(field)); case UIEditorPropertyGridFieldKind::Enum: return ResolveUIEditorEnumFieldValueText(BuildEnumFieldSpec(field)); case UIEditorPropertyGridFieldKind::Vector2: return FormatVector2ValueText(field); case UIEditorPropertyGridFieldKind::Vector3: return FormatVector3ValueText(field); case UIEditorPropertyGridFieldKind::Vector4: return 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 UIRect& bounds, const std::vector& sections, const ::XCEngine::UI::Widgets::UIExpansionModel& expansionModel, const UIEditorPropertyGridMetrics& metrics) { UIEditorPropertyGridLayout layout = {}; 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); const UIRect headerRect( contentX, cursorY, contentWidth, headerHeight); 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; 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); const UIRect rowRect( contentX, cursorY, contentWidth, rowHeight); const UIEditorPropertyGridFieldRects fieldRects = 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 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( UIDrawList& drawList, const UIEditorPropertyGridLayout& layout, const std::vector& sections, const ::XCEngine::UI::Widgets::UISelectionModel& selectionModel, 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( UIDrawList& drawList, const UIEditorPropertyGridLayout& layout, const std::vector& sections, const UIEditorPropertyGridState& state, const ::XCEngine::UI::Widgets::UIPropertyEditModel& propertyEditModel, const UIEditorPropertyGridPalette& palette, 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], metrics), layout.sectionExpanded[sectionVisibleIndex] ? "v" : ">", palette.disclosureColor, metrics.disclosureGlyphFontSize); drawList.AddText( UIPoint( layout.sectionTitleRects[sectionVisibleIndex].x, layout.sectionTitleRects[sectionVisibleIndex].y + metrics.sectionTextInsetY), section.title, palette.sectionTextColor, metrics.sectionFontSize); } 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 UIEditorVector2FieldMetrics vector2Metrics = ::XCEngine::UI::Editor::BuildUIEditorHostedVector2FieldMetrics(metrics); const UIEditorVector2FieldPalette vector2Palette = ::XCEngine::UI::Editor::BuildUIEditorHostedVector2FieldPalette(palette); const UIEditorVector3FieldMetrics vector3Metrics = ::XCEngine::UI::Editor::BuildUIEditorHostedVector3FieldMetrics(metrics); const UIEditorVector3FieldPalette vector3Palette = ::XCEngine::UI::Editor::BuildUIEditorHostedVector3FieldPalette(palette); const UIEditorVector4FieldMetrics vector4Metrics = ::XCEngine::UI::Editor::BuildUIEditorHostedVector4FieldMetrics(metrics); const UIEditorVector4FieldPalette vector4Palette = ::XCEngine::UI::Editor::BuildUIEditorHostedVector4FieldPalette(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; 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::Vector2: { UIEditorVector2FieldState fieldState = {}; fieldState.hoveredTarget = ResolveVector2HoveredTarget(state, field); fieldState.activeTarget = state.pressedFieldId == field.fieldId ? UIEditorVector2FieldHitTargetKind::Row : UIEditorVector2FieldHitTargetKind::None; fieldState.focused = state.focused; AppendUIEditorVector2Field( drawList, layout.fieldRowRects[visibleFieldIndex], BuildVector2FieldSpec(field), fieldState, vector2Palette, vector2Metrics); break; } case UIEditorPropertyGridFieldKind::Vector3: { UIEditorVector3FieldState fieldState = {}; fieldState.hoveredTarget = ResolveVector3HoveredTarget(state, field); fieldState.activeTarget = state.pressedFieldId == field.fieldId ? UIEditorVector3FieldHitTargetKind::Row : UIEditorVector3FieldHitTargetKind::None; fieldState.focused = state.focused; AppendUIEditorVector3Field( drawList, layout.fieldRowRects[visibleFieldIndex], BuildVector3FieldSpec(field), fieldState, vector3Palette, vector3Metrics); break; } case UIEditorPropertyGridFieldKind::Vector4: { UIEditorVector4FieldState fieldState = {}; fieldState.hoveredTarget = ResolveVector4HoveredTarget(state, field); fieldState.activeTarget = state.pressedFieldId == field.fieldId ? UIEditorVector4FieldHitTargetKind::Row : UIEditorVector4FieldHitTargetKind::None; fieldState.focused = state.focused; AppendUIEditorVector4Field( drawList, layout.fieldRowRects[visibleFieldIndex], BuildVector4FieldSpec(field), fieldState, vector4Palette, vector4Metrics); 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(); UIEditorMenuPopupLayout popupLayout = {}; UIEditorMenuPopupState popupState = {}; std::vector 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( UIDrawList& drawList, const UIRect& bounds, const std::vector& 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, 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, state, propertyEditModel, palette, metrics, popupPalette, popupMetrics); } } // namespace XCEngine::UI::Editor::Widgets