150 lines
4.7 KiB
C++
150 lines
4.7 KiB
C++
#pragma once
|
|
|
|
#include "StyleTokens.h"
|
|
|
|
#include <algorithm>
|
|
#include <imgui.h>
|
|
#include <utility>
|
|
|
|
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 <typename DrawControlFn>
|
|
inline auto DrawPropertyRow(
|
|
const char* label,
|
|
const PropertyLayoutSpec& spec,
|
|
DrawControlFn&& drawControl) -> decltype(drawControl(std::declval<const PropertyLayoutMetrics&>())) {
|
|
using Result = decltype(drawControl(std::declval<const PropertyLayoutMetrics&>()));
|
|
|
|
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
|