#include #include #include #include #include namespace XCEngine::UI::Editor::Widgets { namespace { float ClampNonNegative(float value) { return (std::max)(0.0f, value); } UIEditorEnumFieldMetrics ResolveMetrics(const UIEditorEnumFieldMetrics& metrics) { const auto& tokens = GetUIEditorInspectorFieldStyleTokens(); UIEditorEnumFieldMetrics resolved = metrics; if (AreUIEditorFieldMetricsEqual(metrics.controlTrailingInset, 8.0f)) { resolved.controlTrailingInset = tokens.controlTrailingInset; } if (AreUIEditorFieldMetricsEqual(metrics.valueBoxMinWidth, 96.0f)) { resolved.valueBoxMinWidth = tokens.controlMinWidth; } if (AreUIEditorFieldMetricsEqual(metrics.dropdownArrowWidth, 20.0f)) { resolved.dropdownArrowWidth = tokens.dropdownArrowWidth; } return resolved; } UIEditorEnumFieldPalette ResolvePalette(const UIEditorEnumFieldPalette& palette) { const auto& tokens = GetUIEditorInspectorFieldStyleTokens(); UIEditorEnumFieldPalette resolved = palette; if (AreUIEditorFieldColorsEqual(palette.focusedBorderColor, ::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f))) { resolved.focusedBorderColor = tokens.controlFocusedBorderColor; } if (AreUIEditorFieldColorsEqual(palette.valueBoxColor, ::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f))) { resolved.valueBoxColor = tokens.controlColor; } if (AreUIEditorFieldColorsEqual(palette.valueBoxHoverColor, ::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f))) { resolved.valueBoxHoverColor = tokens.controlHoverColor; } if (AreUIEditorFieldColorsEqual(palette.readOnlyColor, ::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f))) { resolved.readOnlyColor = tokens.controlReadOnlyColor; } if (AreUIEditorFieldColorsEqual(palette.controlBorderColor, ::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f))) { resolved.controlBorderColor = tokens.controlBorderColor; } if (AreUIEditorFieldColorsEqual(palette.labelColor, ::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f))) { resolved.labelColor = tokens.labelColor; } if (AreUIEditorFieldColorsEqual(palette.valueColor, ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f))) { resolved.valueColor = tokens.valueColor; } if (AreUIEditorFieldColorsEqual(palette.arrowColor, ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f))) { resolved.arrowColor = tokens.arrowColor; } return resolved; } bool ContainsPoint(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 ClampSelectedIndex(const UIEditorEnumFieldSpec& spec) { if (spec.options.empty()) { return 0u; } return (std::min)(spec.selectedIndex, spec.options.size() - 1u); } void AppendDropdownChevron( ::XCEngine::UI::UIDrawList& drawList, const ::XCEngine::UI::UIRect& rect, const ::XCEngine::UI::UIColor& color) { const float centerX = std::floor(rect.x + rect.width * 0.5f) + 0.5f; const float centerY = std::floor(rect.y + rect.height * 0.5f) + 0.5f; const float halfWidth = ClampNonNegative(rect.width * 0.18f); const float halfHeight = ClampNonNegative(rect.height * 0.12f); drawList.AddFilledTriangle( ::XCEngine::UI::UIPoint(centerX - halfWidth, centerY - halfHeight), ::XCEngine::UI::UIPoint(centerX + halfWidth, centerY - halfHeight), ::XCEngine::UI::UIPoint(centerX, centerY + halfHeight), color); } } // namespace std::string ResolveUIEditorEnumFieldValueText(const UIEditorEnumFieldSpec& spec) { if (spec.options.empty()) { return "(none)"; } return spec.options[ClampSelectedIndex(spec)]; } UIEditorEnumFieldLayout BuildUIEditorEnumFieldLayout( const ::XCEngine::UI::UIRect& bounds, const UIEditorEnumFieldSpec&, const UIEditorEnumFieldMetrics& metrics) { const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics); const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout( bounds, resolvedMetrics.valueBoxMinWidth, UIEditorFieldRowLayoutMetrics { resolvedMetrics.rowHeight, resolvedMetrics.horizontalPadding, resolvedMetrics.labelControlGap, resolvedMetrics.controlColumnStart, resolvedMetrics.controlMinWidth, resolvedMetrics.controlTrailingInset, resolvedMetrics.controlInsetY, }); UIEditorEnumFieldLayout layout = {}; layout.bounds = hostLayout.bounds; layout.labelRect = hostLayout.labelRect; layout.controlRect = hostLayout.controlRect; layout.valueRect = layout.controlRect; const float arrowWidth = (std::min)( (std::max)(resolvedMetrics.dropdownArrowWidth, 0.0f), layout.valueRect.width); layout.arrowRect = ::XCEngine::UI::UIRect( layout.valueRect.x + (std::max)(0.0f, layout.valueRect.width - arrowWidth), layout.valueRect.y, arrowWidth, layout.valueRect.height); return layout; } UIEditorEnumFieldHitTarget HitTestUIEditorEnumField( const UIEditorEnumFieldLayout& layout, const ::XCEngine::UI::UIPoint& point) { if (!ContainsPoint(layout.bounds, point)) { return {}; } if (ContainsPoint(layout.arrowRect, point)) { return { UIEditorEnumFieldHitTargetKind::DropdownArrow }; } if (ContainsPoint(layout.valueRect, point)) { return { UIEditorEnumFieldHitTargetKind::ValueBox }; } return { UIEditorEnumFieldHitTargetKind::Row }; } void AppendUIEditorEnumFieldBackground( ::XCEngine::UI::UIDrawList& drawList, const UIEditorEnumFieldLayout& layout, const UIEditorEnumFieldSpec& spec, const UIEditorEnumFieldState& state, const UIEditorEnumFieldPalette& palette, const UIEditorEnumFieldMetrics& metrics) { const UIEditorEnumFieldPalette resolvedPalette = ResolvePalette(palette); const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics); const auto rowFillColor = state.active ? resolvedPalette.rowActiveColor : (state.hoveredTarget != UIEditorEnumFieldHitTargetKind::None ? resolvedPalette.rowHoverColor : resolvedPalette.surfaceColor); if (rowFillColor.a > 0.0f) { drawList.AddFilledRect(layout.bounds, rowFillColor, resolvedMetrics.cornerRounding); } const bool controlHovered = state.hoveredTarget == UIEditorEnumFieldHitTargetKind::ValueBox || state.hoveredTarget == UIEditorEnumFieldHitTargetKind::DropdownArrow; const ::XCEngine::UI::UIColor previewColor = spec.readOnly ? resolvedPalette.readOnlyColor : (controlHovered || state.popupOpen ? resolvedPalette.valueBoxHoverColor : resolvedPalette.valueBoxColor); const ::XCEngine::UI::UIColor arrowColor = spec.readOnly ? resolvedPalette.readOnlyColor : (state.popupOpen || state.hoveredTarget == UIEditorEnumFieldHitTargetKind::DropdownArrow ? resolvedPalette.valueBoxHoverColor : resolvedPalette.valueBoxColor); const ::XCEngine::UI::UIRect previewRect( layout.valueRect.x, layout.valueRect.y, ClampNonNegative(layout.arrowRect.x - layout.valueRect.x), layout.valueRect.height); drawList.AddFilledRect( previewRect, previewColor, resolvedMetrics.valueBoxRounding); drawList.AddFilledRect( layout.arrowRect, arrowColor, resolvedMetrics.valueBoxRounding); drawList.AddRectOutline( layout.valueRect, state.popupOpen ? resolvedPalette.focusedBorderColor : resolvedPalette.controlBorderColor, state.popupOpen ? resolvedMetrics.focusedBorderThickness : resolvedMetrics.borderThickness, resolvedMetrics.valueBoxRounding); drawList.AddLine( ::XCEngine::UI::UIPoint(layout.arrowRect.x, layout.arrowRect.y + 1.0f), ::XCEngine::UI::UIPoint(layout.arrowRect.x, layout.arrowRect.y + layout.arrowRect.height - 1.0f), resolvedPalette.controlBorderColor, 1.0f); } void AppendUIEditorEnumFieldForeground( ::XCEngine::UI::UIDrawList& drawList, const UIEditorEnumFieldLayout& layout, const UIEditorEnumFieldSpec& spec, const UIEditorEnumFieldPalette& palette, const UIEditorEnumFieldMetrics& metrics) { const UIEditorEnumFieldPalette resolvedPalette = ResolvePalette(palette); const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics); drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, resolvedMetrics.labelFontSize)); drawList.AddText( ::XCEngine::UI::UIPoint( layout.labelRect.x, ResolveUIEditorTextTop(layout.labelRect, resolvedMetrics.labelFontSize, resolvedMetrics.labelTextInsetY)), spec.label, resolvedPalette.labelColor, resolvedMetrics.labelFontSize); drawList.PopClipRect(); const ::XCEngine::UI::UIRect valueTextClipRect( layout.valueRect.x + resolvedMetrics.valueTextInsetX, layout.valueRect.y, ClampNonNegative(layout.arrowRect.x - layout.valueRect.x - resolvedMetrics.valueTextInsetX), layout.valueRect.height); drawList.PushClipRect(ResolveUIEditorTextClipRect(valueTextClipRect, resolvedMetrics.valueFontSize)); drawList.AddText( ::XCEngine::UI::UIPoint( layout.valueRect.x + resolvedMetrics.valueTextInsetX, ResolveUIEditorTextTop(layout.valueRect, resolvedMetrics.valueFontSize, resolvedMetrics.valueTextInsetY)), ResolveUIEditorEnumFieldValueText(spec), resolvedPalette.valueColor, resolvedMetrics.valueFontSize); drawList.PopClipRect(); AppendDropdownChevron(drawList, layout.arrowRect, resolvedPalette.arrowColor); } void AppendUIEditorEnumField( ::XCEngine::UI::UIDrawList& drawList, const ::XCEngine::UI::UIRect& bounds, const UIEditorEnumFieldSpec& spec, const UIEditorEnumFieldState& state, const UIEditorEnumFieldPalette& palette, const UIEditorEnumFieldMetrics& metrics) { const UIEditorEnumFieldPalette resolvedPalette = ResolvePalette(palette); const UIEditorEnumFieldMetrics resolvedMetrics = ResolveMetrics(metrics); const UIEditorEnumFieldLayout layout = BuildUIEditorEnumFieldLayout(bounds, spec, resolvedMetrics); AppendUIEditorEnumFieldBackground(drawList, layout, spec, state, resolvedPalette, resolvedMetrics); AppendUIEditorEnumFieldForeground(drawList, layout, spec, resolvedPalette, resolvedMetrics); } } // namespace XCEngine::UI::Editor::Widgets