#include #include #include #include namespace XCEngine::UI::Editor::Widgets { namespace { using ::XCEngine::UI::UIDrawList; using ::XCEngine::UI::UIPoint; using ::XCEngine::UI::UIRect; float ClampNonNegative(float value) { return (std::max)(0.0f, value); } bool ContainsPoint(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; } bool HasClearButton(const UIEditorObjectFieldSpec& spec) { return spec.showClearButton && spec.hasValue; } bool HasPickerButton(const UIEditorObjectFieldSpec& spec) { return spec.showPickerButton; } bool HasTypeText(const UIEditorObjectFieldSpec& spec) { return spec.hasValue && !spec.objectTypeName.empty(); } ::XCEngine::UI::UIColor ResolveRowFillColor( const UIEditorObjectFieldState& state, const UIEditorObjectFieldPalette& palette) { if (state.activeTarget != UIEditorObjectFieldHitTargetKind::None) { return palette.rowActiveColor; } if (state.hoveredTarget != UIEditorObjectFieldHitTargetKind::None) { return palette.rowHoverColor; } return palette.surfaceColor; } ::XCEngine::UI::UIColor ResolveControlFillColor( const UIEditorObjectFieldSpec& spec, const UIEditorObjectFieldState& state, const UIEditorObjectFieldPalette& palette) { if (spec.readOnly) { return palette.readOnlyColor; } if (state.hoveredTarget == UIEditorObjectFieldHitTargetKind::ValueBox || state.activeTarget == UIEditorObjectFieldHitTargetKind::ValueBox) { return palette.valueBoxHoverColor; } return palette.valueBoxColor; } ::XCEngine::UI::UIColor ResolveButtonFillColor( const UIEditorObjectFieldSpec& spec, const UIEditorObjectFieldState& state, UIEditorObjectFieldHitTargetKind target, const UIEditorObjectFieldPalette& palette) { if (spec.readOnly) { return palette.readOnlyColor; } if (state.activeTarget == target) { return palette.buttonActiveColor; } if (state.hoveredTarget == target) { return palette.buttonHoverColor; } return palette.buttonColor; } void AppendButtonGlyph( UIDrawList& drawList, const UIRect& rect, const char* glyph, const UIEditorObjectFieldPalette& palette, const UIEditorObjectFieldMetrics& metrics) { if (rect.width <= 0.0f || rect.height <= 0.0f) { return; } drawList.AddText( UIPoint( rect.x + ClampNonNegative((rect.width - metrics.buttonGlyphFontSize) * 0.5f), ResolveUIEditorTextTop(rect, metrics.buttonGlyphFontSize, metrics.buttonGlyphInsetY)), glyph, palette.buttonGlyphColor, metrics.buttonGlyphFontSize); } } // namespace std::string ResolveUIEditorObjectFieldDisplayText(const UIEditorObjectFieldSpec& spec) { if (!spec.hasValue) { return spec.emptyText.empty() ? "(none)" : spec.emptyText; } return spec.objectName.empty() ? "(unnamed)" : spec.objectName; } UIEditorObjectFieldLayout BuildUIEditorObjectFieldLayout( const UIRect& bounds, const UIEditorObjectFieldSpec& spec, const UIEditorObjectFieldMetrics& metrics) { const UIEditorFieldRowLayout hostLayout = BuildUIEditorFieldRowLayout( bounds, metrics.valueBoxMinWidth, UIEditorFieldRowLayoutMetrics { metrics.rowHeight, metrics.horizontalPadding, metrics.labelControlGap, metrics.controlColumnStart, metrics.controlMinWidth, metrics.controlTrailingInset, metrics.controlInsetY, }); UIEditorObjectFieldLayout layout = {}; layout.bounds = hostLayout.bounds; layout.labelRect = hostLayout.labelRect; layout.controlRect = hostLayout.controlRect; const float pickerWidth = HasPickerButton(spec) ? (std::min)(metrics.buttonWidth, layout.controlRect.width) : 0.0f; const float clearWidth = HasClearButton(spec) ? (std::min)(metrics.buttonWidth, ClampNonNegative(layout.controlRect.width - pickerWidth)) : 0.0f; const float buttonWidth = pickerWidth + clearWidth; layout.valueRect = UIRect( layout.controlRect.x, layout.controlRect.y, ClampNonNegative(layout.controlRect.width - buttonWidth), layout.controlRect.height); float nextButtonX = layout.valueRect.x + layout.valueRect.width; if (clearWidth > 0.0f) { layout.clearButtonRect = UIRect( nextButtonX, layout.controlRect.y, clearWidth, layout.controlRect.height); nextButtonX += clearWidth; } if (pickerWidth > 0.0f) { layout.pickerButtonRect = UIRect( nextButtonX, layout.controlRect.y, pickerWidth, layout.controlRect.height); } if (HasTypeText(spec) && layout.valueRect.width > metrics.typeMinWidth + metrics.valueTextInsetX) { const float reservedWidth = ClampNonNegative( layout.valueRect.width * 0.35f + metrics.typeTextInsetX); const float typeWidth = (std::min)( metrics.typeMaxWidth, (std::max)(metrics.typeMinWidth, reservedWidth)); if (layout.valueRect.width > typeWidth + metrics.valueTextInsetX + metrics.valueTypeGap) { layout.typeRect = UIRect( layout.valueRect.x + layout.valueRect.width - typeWidth, layout.valueRect.y, typeWidth, layout.valueRect.height); } } return layout; } UIEditorObjectFieldHitTarget HitTestUIEditorObjectField( const UIEditorObjectFieldLayout& layout, const UIPoint& point) { if (!ContainsPoint(layout.bounds, point)) { return {}; } if (layout.pickerButtonRect.width > 0.0f && ContainsPoint(layout.pickerButtonRect, point)) { return { UIEditorObjectFieldHitTargetKind::PickerButton }; } if (layout.clearButtonRect.width > 0.0f && ContainsPoint(layout.clearButtonRect, point)) { return { UIEditorObjectFieldHitTargetKind::ClearButton }; } if (ContainsPoint(layout.valueRect, point)) { return { UIEditorObjectFieldHitTargetKind::ValueBox }; } return { UIEditorObjectFieldHitTargetKind::Row }; } void AppendUIEditorObjectFieldBackground( UIDrawList& drawList, const UIEditorObjectFieldLayout& layout, const UIEditorObjectFieldSpec& spec, const UIEditorObjectFieldState& state, const UIEditorObjectFieldPalette& palette, const UIEditorObjectFieldMetrics& metrics) { const auto rowFillColor = ResolveRowFillColor(state, palette); if (rowFillColor.a > 0.0f) { drawList.AddFilledRect(layout.bounds, rowFillColor, metrics.cornerRounding); } const auto rowBorderColor = state.focused ? palette.focusedBorderColor : palette.borderColor; if (rowBorderColor.a > 0.0f) { drawList.AddRectOutline( layout.bounds, rowBorderColor, state.focused ? metrics.focusedBorderThickness : metrics.borderThickness, metrics.cornerRounding); } drawList.AddFilledRect( layout.controlRect, ResolveControlFillColor(spec, state, palette), metrics.valueBoxRounding); drawList.AddRectOutline( layout.controlRect, state.focused ? palette.controlFocusedBorderColor : palette.controlBorderColor, state.focused ? metrics.focusedBorderThickness : metrics.borderThickness, metrics.valueBoxRounding); if (layout.clearButtonRect.width > 0.0f) { drawList.AddFilledRect( layout.clearButtonRect, ResolveButtonFillColor(spec, state, UIEditorObjectFieldHitTargetKind::ClearButton, palette)); drawList.AddLine( UIPoint(layout.clearButtonRect.x, layout.clearButtonRect.y + 1.0f), UIPoint(layout.clearButtonRect.x, layout.clearButtonRect.y + layout.clearButtonRect.height - 1.0f), palette.separatorColor, 1.0f); } if (layout.pickerButtonRect.width > 0.0f) { drawList.AddFilledRect( layout.pickerButtonRect, ResolveButtonFillColor(spec, state, UIEditorObjectFieldHitTargetKind::PickerButton, palette)); drawList.AddLine( UIPoint(layout.pickerButtonRect.x, layout.pickerButtonRect.y + 1.0f), UIPoint(layout.pickerButtonRect.x, layout.pickerButtonRect.y + layout.pickerButtonRect.height - 1.0f), palette.separatorColor, 1.0f); } } void AppendUIEditorObjectFieldForeground( UIDrawList& drawList, const UIEditorObjectFieldLayout& layout, const UIEditorObjectFieldSpec& spec, const UIEditorObjectFieldPalette& palette, const UIEditorObjectFieldMetrics& metrics) { drawList.PushClipRect(ResolveUIEditorTextClipRect(layout.labelRect, metrics.labelFontSize)); drawList.AddText( UIPoint( layout.labelRect.x, ResolveUIEditorTextTop(layout.labelRect, metrics.labelFontSize, metrics.labelTextInsetY)), spec.label, palette.labelColor, metrics.labelFontSize); drawList.PopClipRect(); float primaryClipWidth = layout.valueRect.width; if (layout.typeRect.width > 0.0f) { primaryClipWidth = ClampNonNegative(layout.typeRect.x - layout.valueRect.x - metrics.valueTypeGap); } const UIRect primaryClipRect( layout.valueRect.x + metrics.valueTextInsetX, layout.valueRect.y, ClampNonNegative(primaryClipWidth - metrics.valueTextInsetX), layout.valueRect.height); drawList.PushClipRect(ResolveUIEditorTextClipRect(primaryClipRect, metrics.valueFontSize)); drawList.AddText( UIPoint( layout.valueRect.x + metrics.valueTextInsetX, ResolveUIEditorTextTop(layout.valueRect, metrics.valueFontSize, metrics.valueTextInsetY)), ResolveUIEditorObjectFieldDisplayText(spec), spec.hasValue ? palette.valueColor : palette.emptyValueColor, metrics.valueFontSize); drawList.PopClipRect(); if (layout.typeRect.width > 0.0f) { const UIRect typeClipRect( layout.typeRect.x + metrics.typeTextInsetX, layout.typeRect.y, ClampNonNegative(layout.typeRect.width - metrics.typeTextInsetX), layout.typeRect.height); drawList.PushClipRect(ResolveUIEditorTextClipRect(typeClipRect, metrics.typeFontSize)); drawList.AddText( UIPoint( layout.typeRect.x + metrics.typeTextInsetX, ResolveUIEditorTextTop(layout.typeRect, metrics.typeFontSize, metrics.typeTextInsetY)), spec.objectTypeName, palette.typeColor, metrics.typeFontSize); drawList.PopClipRect(); } AppendButtonGlyph(drawList, layout.clearButtonRect, "X", palette, metrics); AppendButtonGlyph(drawList, layout.pickerButtonRect, "o", palette, metrics); } void AppendUIEditorObjectField( UIDrawList& drawList, const UIRect& bounds, const UIEditorObjectFieldSpec& spec, const UIEditorObjectFieldState& state, const UIEditorObjectFieldPalette& palette, const UIEditorObjectFieldMetrics& metrics) { const UIEditorObjectFieldLayout layout = BuildUIEditorObjectFieldLayout(bounds, spec, metrics); AppendUIEditorObjectFieldBackground(drawList, layout, spec, state, palette, metrics); AppendUIEditorObjectFieldForeground(drawList, layout, spec, palette, metrics); } } // namespace XCEngine::UI::Editor::Widgets