#include #include #include namespace XCEngine::UI::Editor::Widgets { namespace { using ::XCEngine::UI::UIColor; using ::XCEngine::UI::UIDrawList; using ::XCEngine::UI::UIPoint; using ::XCEngine::UI::UIRect; float ClampNonNegative(float value) { return (std::max)(value, 0.0f); } float ResolveActionButtonExtent( const UIEditorPanelFrameMetrics& metrics, float headerHeight) { const float inset = (std::max)(metrics.actionInsetX, 0.0f); return (std::min)( (std::max)(metrics.actionButtonExtent, 0.0f), (std::max)(headerHeight - inset * 2.0f, 0.0f)); } UIColor ResolveActionFillColor( bool selected, bool hovered, const UIEditorPanelFramePalette& palette) { if (selected) { return palette.actionButtonSelectedColor; } if (hovered) { return palette.actionButtonHoveredColor; } return palette.actionButtonColor; } void AppendActionButton( UIDrawList& drawList, const UIRect& rect, std::string_view glyph, bool selected, bool hovered, const UIEditorPanelFramePalette& palette) { if (rect.width <= 0.0f || rect.height <= 0.0f) { return; } drawList.AddFilledRect( rect, ResolveActionFillColor(selected, hovered, palette), 5.0f); drawList.AddRectOutline( rect, palette.actionButtonBorderColor, 1.0f, 5.0f); drawList.AddText( UIPoint(rect.x + 5.0f, rect.y + 2.0f), std::string(glyph), palette.actionGlyphColor, 12.0f); } } // namespace bool IsUIEditorPanelFramePointInside( 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 IsUIEditorPanelFramePinButtonVisible(const UIEditorPanelFrameState& state) { return state.pinnable; } bool IsUIEditorPanelFrameCloseButtonVisible(const UIEditorPanelFrameState& state) { return state.closable; } UIEditorPanelFrameLayout BuildUIEditorPanelFrameLayout( const UIRect& frameRect, const UIEditorPanelFrameState& state, const UIEditorPanelFrameMetrics& metrics) { UIEditorPanelFrameLayout layout = {}; layout.frameRect = UIRect( frameRect.x, frameRect.y, ClampNonNegative(frameRect.width), ClampNonNegative(frameRect.height)); const float headerHeight = (std::min)(ClampNonNegative(metrics.headerHeight), layout.frameRect.height); const float footerHeight = state.showFooter ? (std::min)( ClampNonNegative(metrics.footerHeight), (std::max)(layout.frameRect.height - headerHeight, 0.0f)) : 0.0f; layout.hasFooter = footerHeight > 0.0f; layout.showPinButton = IsUIEditorPanelFramePinButtonVisible(state); layout.showCloseButton = IsUIEditorPanelFrameCloseButtonVisible(state); layout.headerRect = UIRect( layout.frameRect.x, layout.frameRect.y, layout.frameRect.width, headerHeight); if (layout.hasFooter) { layout.footerRect = UIRect( layout.frameRect.x, layout.frameRect.y + layout.frameRect.height - footerHeight, layout.frameRect.width, footerHeight); } const float bodyBandTop = layout.headerRect.y + layout.headerRect.height; const float bodyBandBottom = layout.hasFooter ? layout.footerRect.y : layout.frameRect.y + layout.frameRect.height; const float contentPadding = ClampNonNegative(metrics.contentPadding); layout.bodyRect = UIRect( layout.frameRect.x + contentPadding, bodyBandTop + contentPadding, (std::max)(layout.frameRect.width - contentPadding * 2.0f, 0.0f), (std::max)(bodyBandBottom - bodyBandTop - contentPadding * 2.0f, 0.0f)); if (layout.headerRect.height <= 0.0f) { return layout; } const float buttonExtent = ResolveActionButtonExtent(metrics, layout.headerRect.height); if (buttonExtent <= 0.0f) { return layout; } const float buttonY = layout.headerRect.y + (layout.headerRect.height - buttonExtent) * 0.5f; float buttonRight = layout.headerRect.x + layout.headerRect.width - ClampNonNegative(metrics.actionInsetX); if (layout.showCloseButton) { layout.closeButtonRect = UIRect( buttonRight - buttonExtent, buttonY, buttonExtent, buttonExtent); buttonRight = layout.closeButtonRect.x - ClampNonNegative(metrics.actionGap); } if (layout.showPinButton) { layout.pinButtonRect = UIRect( buttonRight - buttonExtent, buttonY, buttonExtent, buttonExtent); } return layout; } UIColor ResolveUIEditorPanelFrameBorderColor( const UIEditorPanelFrameState& state, const UIEditorPanelFramePalette& palette) { if (state.focused) { return palette.focusedBorderColor; } if (state.active) { return palette.activeBorderColor; } if (state.hovered) { return palette.hoveredBorderColor; } return palette.borderColor; } float ResolveUIEditorPanelFrameBorderThickness( const UIEditorPanelFrameState& state, const UIEditorPanelFrameMetrics& metrics) { if (state.focused) { return metrics.focusedBorderThickness; } if (state.active) { return metrics.activeBorderThickness; } if (state.hovered) { return metrics.hoveredBorderThickness; } return metrics.baseBorderThickness; } UIEditorPanelFrameAction HitTestUIEditorPanelFrameAction( const UIEditorPanelFrameLayout& layout, const UIEditorPanelFrameState& state, const UIPoint& point) { if (layout.showPinButton && IsUIEditorPanelFramePinButtonVisible(state) && IsUIEditorPanelFramePointInside(layout.pinButtonRect, point)) { return UIEditorPanelFrameAction::Pin; } if (layout.showCloseButton && IsUIEditorPanelFrameCloseButtonVisible(state) && IsUIEditorPanelFramePointInside(layout.closeButtonRect, point)) { return UIEditorPanelFrameAction::Close; } return UIEditorPanelFrameAction::None; } UIEditorPanelFrameHitTarget HitTestUIEditorPanelFrame( const UIEditorPanelFrameLayout& layout, const UIEditorPanelFrameState& state, const UIPoint& point) { if (!IsUIEditorPanelFramePointInside(layout.frameRect, point)) { return UIEditorPanelFrameHitTarget::None; } switch (HitTestUIEditorPanelFrameAction(layout, state, point)) { case UIEditorPanelFrameAction::Pin: return UIEditorPanelFrameHitTarget::PinButton; case UIEditorPanelFrameAction::Close: return UIEditorPanelFrameHitTarget::CloseButton; default: break; } if (IsUIEditorPanelFramePointInside(layout.headerRect, point)) { return UIEditorPanelFrameHitTarget::Header; } if (layout.hasFooter && IsUIEditorPanelFramePointInside(layout.footerRect, point)) { return UIEditorPanelFrameHitTarget::Footer; } if (IsUIEditorPanelFramePointInside(layout.bodyRect, point)) { return UIEditorPanelFrameHitTarget::Body; } return UIEditorPanelFrameHitTarget::Body; } void AppendUIEditorPanelFrameBackground( UIDrawList& drawList, const UIEditorPanelFrameLayout& layout, const UIEditorPanelFrameState& state, const UIEditorPanelFramePalette& palette, const UIEditorPanelFrameMetrics& metrics) { drawList.AddFilledRect(layout.frameRect, palette.surfaceColor, metrics.cornerRounding); drawList.AddRectOutline( layout.frameRect, ResolveUIEditorPanelFrameBorderColor(state, palette), ResolveUIEditorPanelFrameBorderThickness(state, metrics), metrics.cornerRounding); drawList.AddFilledRect(layout.headerRect, palette.headerColor, metrics.cornerRounding); if (layout.hasFooter) { drawList.AddFilledRect(layout.footerRect, palette.footerColor, metrics.cornerRounding); } } void AppendUIEditorPanelFrameForeground( UIDrawList& drawList, const UIEditorPanelFrameLayout& layout, const UIEditorPanelFrameState& state, const UIEditorPanelFrameText& text, const UIEditorPanelFramePalette& palette, const UIEditorPanelFrameMetrics& metrics) { if (!text.title.empty()) { drawList.AddText( UIPoint(layout.frameRect.x + metrics.titleInsetX, layout.headerRect.y + metrics.titleInsetY), std::string(text.title), palette.textPrimary, 16.0f); } if (!text.subtitle.empty()) { drawList.AddText( UIPoint(layout.frameRect.x + metrics.titleInsetX, layout.headerRect.y + metrics.subtitleInsetY), std::string(text.subtitle), palette.textSecondary, 12.0f); } if (layout.hasFooter && !text.footer.empty()) { drawList.AddText( UIPoint(layout.footerRect.x + metrics.footerInsetX, layout.footerRect.y + metrics.footerInsetY), std::string(text.footer), palette.textMuted, 11.0f); } if (layout.showPinButton) { AppendActionButton( drawList, layout.pinButtonRect, "P", state.pinned, state.pinHovered, palette); } if (layout.showCloseButton) { AppendActionButton( drawList, layout.closeButtonRect, "X", false, state.closeHovered, palette); } } void AppendUIEditorPanelFrame( UIDrawList& drawList, const UIRect& frameRect, const UIEditorPanelFrameState& state, const UIEditorPanelFrameText& text, const UIEditorPanelFramePalette& palette, const UIEditorPanelFrameMetrics& metrics) { const UIEditorPanelFrameLayout layout = BuildUIEditorPanelFrameLayout(frameRect, state, metrics); AppendUIEditorPanelFrameBackground(drawList, layout, state, palette, metrics); AppendUIEditorPanelFrameForeground(drawList, layout, state, text, palette, metrics); } } // namespace XCEngine::UI::Editor::Widgets