#pragma once #include "StyleTokens.h" #include #include #include namespace XCEngine { namespace Editor { namespace UI { struct PropertyLayoutSpec { float labelInset = InspectorPropertyLabelInset(); float controlColumnStart = InspectorPropertyControlColumnStart(); float labelControlGap = InspectorPropertyLabelControlGap(); float controlTrailingInset = InspectorPropertyControlTrailingInset(); float minimumRowHeight = 0.0f; }; struct PropertyLayoutMetrics { ImVec2 cursorPos = ImVec2(0.0f, 0.0f); ImVec2 screenPos = ImVec2(0.0f, 0.0f); float rowWidth = 0.0f; float rowHeight = 0.0f; float labelX = 0.0f; float labelWidth = 0.0f; float controlX = 0.0f; float controlWidth = 0.0f; }; inline PropertyLayoutSpec MakePropertyLayout() { return PropertyLayoutSpec{}; } inline float CalcPropertyRowHeightForFramePadding(const ImVec2& framePadding) { return ImGui::GetFontSize() + framePadding.y * 2.0f + ControlRowHeightOffset(); } inline PropertyLayoutSpec WithMinimumRowHeight(PropertyLayoutSpec spec, float minimumRowHeight) { spec.minimumRowHeight = std::max(spec.minimumRowHeight, minimumRowHeight); return spec; } inline void PushPropertyLayoutStyles() { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ControlFramePadding()); } inline float GetPropertyControlWidth( const PropertyLayoutMetrics& layout, float trailingInset = 0.0f) { return std::max(layout.controlWidth - trailingInset, 1.0f); } inline void SetNextPropertyControlWidth( const PropertyLayoutMetrics& layout, float trailingInset = 0.0f) { ImGui::SetNextItemWidth(GetPropertyControlWidth(layout, trailingInset)); } inline void AlignPropertyControlToRight( const PropertyLayoutMetrics& layout, float width, float trailingInset = 0.0f) { const float offset = layout.controlWidth - width - trailingInset; if (offset > 0.0f) { ImGui::SetCursorPosX(layout.controlX + offset); } } inline void AlignPropertyControlVertically( const PropertyLayoutMetrics& layout, float height) { const float offset = std::max((layout.rowHeight - height) * 0.5f, 0.0f); if (offset > 0.0f) { ImGui::SetCursorPosY(layout.cursorPos.y + offset); } } template inline auto DrawPropertyRow( const char* label, const PropertyLayoutSpec& spec, DrawControlFn&& drawControl) -> decltype(drawControl(std::declval())) { using Result = decltype(drawControl(std::declval())); Result result{}; ImGui::PushID(label); PushPropertyLayoutStyles(); const ImVec2 rowCursorPos = ImGui::GetCursorPos(); const ImVec2 rowScreenPos = ImGui::GetCursorScreenPos(); const float rowWidth = std::max(ImGui::GetContentRegionAvail().x, 1.0f); const float rowHeight = std::max( ImGui::GetFrameHeight() + ControlRowHeightOffset(), spec.minimumRowHeight); const float labelInset = std::max(spec.labelInset, 0.0f); const float controlColumnStart = std::clamp( std::max(spec.controlColumnStart, 0.0f), labelInset, rowWidth); const float labelWidth = std::max( controlColumnStart - labelInset - std::max(spec.labelControlGap, 0.0f), 0.0f); const float controlX = rowCursorPos.x + controlColumnStart; const float controlRight = rowCursorPos.x + rowWidth - std::max(spec.controlTrailingInset, 0.0f); const float controlWidth = std::max(controlRight - controlX, 1.0f); const float labelX = rowCursorPos.x + labelInset; const PropertyLayoutMetrics metrics{ rowCursorPos, rowScreenPos, rowWidth, rowHeight, labelX, labelWidth, controlX, controlWidth }; if (label && label[0] != '\0') { ImDrawList* drawList = ImGui::GetWindowDrawList(); const float textY = rowScreenPos.y + std::max(0.0f, (rowHeight - ImGui::GetTextLineHeight()) * 0.5f); drawList->PushClipRect( ImVec2(rowScreenPos.x + labelInset, rowScreenPos.y), ImVec2(rowScreenPos.x + labelInset + labelWidth, rowScreenPos.y + rowHeight), true); drawList->AddText( ImVec2(rowScreenPos.x + labelInset, textY), ImGui::GetColorU32(ImGuiCol_Text), label); drawList->PopClipRect(); } ImGui::SetCursorPos(ImVec2(controlX, rowCursorPos.y)); result = drawControl(metrics); const float consumedHeight = std::max(rowHeight, ImGui::GetCursorPosY() - rowCursorPos.y); ImGui::SetCursorPos(rowCursorPos); ImGui::Dummy(ImVec2(rowWidth, consumedHeight)); ImGui::PopStyleVar(); ImGui::PopID(); return result; } } // namespace UI } // namespace Editor } // namespace XCEngine