feat: update editor ui framework and assets
This commit is contained in:
@@ -1,35 +1,165 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core.h"
|
||||
#include "PropertyLayout.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <imgui.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
namespace UI {
|
||||
|
||||
inline float CalcComboPopupMaxHeightFromItemCount(int itemCount) {
|
||||
if (itemCount <= 0) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
return (ImGui::GetFontSize() + style.ItemSpacing.y) * itemCount -
|
||||
style.ItemSpacing.y +
|
||||
(ComboPopupWindowPadding().y * 2.0f);
|
||||
}
|
||||
|
||||
inline void DrawComboPreviewFrame(
|
||||
const char* id,
|
||||
const char* previewValue,
|
||||
float width,
|
||||
bool popupOpen) {
|
||||
const float frameHeight = ImGui::GetFrameHeight();
|
||||
ImGui::InvisibleButton(id, ImVec2(width, frameHeight));
|
||||
|
||||
const bool hovered = ImGui::IsItemHovered();
|
||||
const ImVec2 min = ImGui::GetItemRectMin();
|
||||
const ImVec2 max = ImGui::GetItemRectMax();
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
const float arrowWidth = frameHeight;
|
||||
const float valueMaxX = ImMax(min.x, max.x - arrowWidth);
|
||||
const ImU32 frameColor = ImGui::GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg);
|
||||
const ImU32 arrowColor = ImGui::GetColorU32((popupOpen || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button);
|
||||
const ImU32 borderColor = ImGui::GetColorU32(ImGuiCol_Border);
|
||||
const ImU32 textColor = ImGui::GetColorU32(ImGuiCol_Text);
|
||||
|
||||
drawList->AddRectFilled(
|
||||
min,
|
||||
ImVec2(valueMaxX, max.y),
|
||||
frameColor,
|
||||
style.FrameRounding,
|
||||
ImDrawFlags_RoundCornersLeft);
|
||||
drawList->AddRectFilled(
|
||||
ImVec2(valueMaxX, min.y),
|
||||
max,
|
||||
arrowColor,
|
||||
style.FrameRounding,
|
||||
ImDrawFlags_RoundCornersRight);
|
||||
drawList->AddRect(
|
||||
ImVec2(min.x + 0.5f, min.y + 0.5f),
|
||||
ImVec2(max.x - 0.5f, max.y - 0.5f),
|
||||
borderColor,
|
||||
style.FrameRounding,
|
||||
0,
|
||||
style.FrameBorderSize);
|
||||
|
||||
if (previewValue && previewValue[0] != '\0') {
|
||||
const ImVec2 textSize = ImGui::CalcTextSize(previewValue);
|
||||
const ImVec2 textPos(
|
||||
min.x + style.FramePadding.x,
|
||||
min.y + ImMax(0.0f, (frameHeight - textSize.y) * 0.5f));
|
||||
ImGui::PushClipRect(
|
||||
ImVec2(min.x + style.FramePadding.x, min.y),
|
||||
ImVec2(valueMaxX - style.FramePadding.x, max.y),
|
||||
true);
|
||||
drawList->AddText(textPos, textColor, previewValue);
|
||||
ImGui::PopClipRect();
|
||||
}
|
||||
|
||||
const float arrowHalfWidth = 4.0f;
|
||||
const float arrowHalfHeight = 2.5f;
|
||||
const ImVec2 arrowCenter((valueMaxX + max.x) * 0.5f, (min.y + max.y) * 0.5f + 0.5f);
|
||||
drawList->AddTriangleFilled(
|
||||
ImVec2(arrowCenter.x - arrowHalfWidth, arrowCenter.y - arrowHalfHeight),
|
||||
ImVec2(arrowCenter.x + arrowHalfWidth, arrowCenter.y - arrowHalfHeight),
|
||||
ImVec2(arrowCenter.x, arrowCenter.y + arrowHalfHeight),
|
||||
textColor);
|
||||
}
|
||||
|
||||
inline bool DrawFloat(
|
||||
const char* label,
|
||||
float& value,
|
||||
float columnWidth = DefaultControlLabelWidth(),
|
||||
const PropertyLayoutSpec& layoutSpec = MakePropertyLayout(),
|
||||
float dragSpeed = 0.1f,
|
||||
float min = 0.0f,
|
||||
float max = 0.0f,
|
||||
const char* format = "%.2f"
|
||||
) {
|
||||
return DrawControlRow(label, columnWidth, [&]() {
|
||||
return DrawPropertyRow(label, layoutSpec, [&](const PropertyLayoutMetrics& layout) {
|
||||
SetNextPropertyControlWidth(layout);
|
||||
return ImGui::DragFloat("##value", &value, dragSpeed, min, max, format);
|
||||
});
|
||||
}
|
||||
|
||||
inline bool DrawLinearSlider(
|
||||
const char* id,
|
||||
float width,
|
||||
float normalizedValue) {
|
||||
const float clampedValue = std::clamp(normalizedValue, 0.0f, 1.0f);
|
||||
const float frameHeight = ImGui::GetFrameHeight();
|
||||
ImGui::InvisibleButton(id, ImVec2(width, frameHeight));
|
||||
|
||||
const bool hovered = ImGui::IsItemHovered();
|
||||
const bool active = ImGui::IsItemActive();
|
||||
const ImVec2 min = ImGui::GetItemRectMin();
|
||||
const ImVec2 max = ImGui::GetItemRectMax();
|
||||
const float trackPadding = LinearSliderHorizontalPadding();
|
||||
const float trackMinX = min.x + trackPadding;
|
||||
const float trackMaxX = max.x - trackPadding;
|
||||
const float centerY = (min.y + max.y) * 0.5f;
|
||||
const float knobX = trackMinX + (trackMaxX - trackMinX) * clampedValue;
|
||||
const float trackHalfThickness = LinearSliderTrackThickness() * 0.5f;
|
||||
|
||||
ImDrawList* drawList = ImGui::GetWindowDrawList();
|
||||
drawList->AddRectFilled(
|
||||
ImVec2(trackMinX, centerY - trackHalfThickness),
|
||||
ImVec2(trackMaxX, centerY + trackHalfThickness),
|
||||
ImGui::GetColorU32(LinearSliderTrackColor()),
|
||||
trackHalfThickness);
|
||||
drawList->AddRectFilled(
|
||||
ImVec2(trackMinX, centerY - trackHalfThickness),
|
||||
ImVec2(knobX, centerY + trackHalfThickness),
|
||||
ImGui::GetColorU32(LinearSliderFillColor()),
|
||||
trackHalfThickness);
|
||||
|
||||
const ImVec4 grabColor = active
|
||||
? LinearSliderGrabActiveColor()
|
||||
: (hovered ? LinearSliderGrabHoveredColor() : LinearSliderGrabColor());
|
||||
drawList->AddCircleFilled(
|
||||
ImVec2(knobX, centerY),
|
||||
LinearSliderGrabRadius(),
|
||||
ImGui::GetColorU32(grabColor),
|
||||
20);
|
||||
|
||||
if (!active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const float trackWidth = std::max(trackMaxX - trackMinX, 1.0f);
|
||||
const float mouseRatio = std::clamp((ImGui::GetIO().MousePos.x - trackMinX) / trackWidth, 0.0f, 1.0f);
|
||||
return std::fabs(mouseRatio - clampedValue) > 0.0001f;
|
||||
}
|
||||
|
||||
inline bool DrawInt(
|
||||
const char* label,
|
||||
int& value,
|
||||
float columnWidth = DefaultControlLabelWidth(),
|
||||
const PropertyLayoutSpec& layoutSpec = MakePropertyLayout(),
|
||||
int step = 1,
|
||||
int min = 0,
|
||||
int max = 0
|
||||
) {
|
||||
return DrawControlRow(label, columnWidth, [&]() {
|
||||
return DrawPropertyRow(label, layoutSpec, [&](const PropertyLayoutMetrics& layout) {
|
||||
SetNextPropertyControlWidth(layout);
|
||||
return ImGui::DragInt("##value", &value, static_cast<float>(step), min, max);
|
||||
});
|
||||
}
|
||||
@@ -37,9 +167,9 @@ inline bool DrawInt(
|
||||
inline bool DrawBool(
|
||||
const char* label,
|
||||
bool& value,
|
||||
float columnWidth = DefaultControlLabelWidth()
|
||||
const PropertyLayoutSpec& layoutSpec = MakePropertyLayout()
|
||||
) {
|
||||
return DrawControlRow(label, columnWidth, [&]() {
|
||||
return DrawPropertyRow(label, layoutSpec, [&](const PropertyLayoutMetrics&) {
|
||||
return ImGui::Checkbox("##value", &value);
|
||||
});
|
||||
}
|
||||
@@ -47,9 +177,9 @@ inline bool DrawBool(
|
||||
inline bool DrawColor3(
|
||||
const char* label,
|
||||
float color[3],
|
||||
float columnWidth = DefaultControlLabelWidth()
|
||||
const PropertyLayoutSpec& layoutSpec = MakePropertyLayout()
|
||||
) {
|
||||
return DrawControlRow(label, columnWidth, [&]() {
|
||||
return DrawPropertyRow(label, layoutSpec, [&](const PropertyLayoutMetrics&) {
|
||||
return ImGui::ColorEdit3("##value", color, ImGuiColorEditFlags_NoInputs);
|
||||
});
|
||||
}
|
||||
@@ -57,9 +187,9 @@ inline bool DrawColor3(
|
||||
inline bool DrawColor4(
|
||||
const char* label,
|
||||
float color[4],
|
||||
float columnWidth = DefaultControlLabelWidth()
|
||||
const PropertyLayoutSpec& layoutSpec = MakePropertyLayout()
|
||||
) {
|
||||
return DrawControlRow(label, columnWidth, [&]() {
|
||||
return DrawPropertyRow(label, layoutSpec, [&](const PropertyLayoutMetrics&) {
|
||||
return ImGui::ColorEdit4("##value", color, ImGuiColorEditFlags_NoInputs);
|
||||
});
|
||||
}
|
||||
@@ -69,11 +199,33 @@ inline bool DrawSliderFloat(
|
||||
float& value,
|
||||
float min,
|
||||
float max,
|
||||
float columnWidth = DefaultControlLabelWidth(),
|
||||
const PropertyLayoutSpec& layoutSpec = MakePropertyLayout(),
|
||||
const char* format = "%.2f"
|
||||
) {
|
||||
return DrawControlRow(label, columnWidth, [&]() {
|
||||
return ImGui::SliderFloat("##value", &value, min, max, format);
|
||||
return DrawPropertyRow(label, layoutSpec, [&](const PropertyLayoutMetrics& layout) {
|
||||
const float totalWidth = layout.controlWidth;
|
||||
const float inputWidth = SliderValueFieldWidth();
|
||||
const float spacing = CompoundControlSpacing();
|
||||
const float sliderWidth = ImMax(totalWidth - inputWidth - spacing, 1.0f);
|
||||
const float range = max - min;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, 0.0f));
|
||||
float normalizedValue = range > 0.0f ? (value - min) / range : 0.0f;
|
||||
bool changed = DrawLinearSlider("##slider", sliderWidth, normalizedValue);
|
||||
if (ImGui::IsItemActive()) {
|
||||
const float trackWidth = std::max(sliderWidth - LinearSliderHorizontalPadding() * 2.0f, 1.0f);
|
||||
const float mouseRatio = std::clamp(
|
||||
(ImGui::GetIO().MousePos.x - (ImGui::GetItemRectMin().x + LinearSliderHorizontalPadding())) / trackWidth,
|
||||
0.0f,
|
||||
1.0f);
|
||||
value = min + range * mouseRatio;
|
||||
}
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
ImGui::SetNextItemWidth(inputWidth);
|
||||
changed = ImGui::InputFloat("##value", &value, 0.0f, 0.0f, format) || changed;
|
||||
value = std::clamp(value, min, max);
|
||||
ImGui::PopStyleVar();
|
||||
return changed;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -82,10 +234,32 @@ inline bool DrawSliderInt(
|
||||
int& value,
|
||||
int min,
|
||||
int max,
|
||||
float columnWidth = DefaultControlLabelWidth()
|
||||
const PropertyLayoutSpec& layoutSpec = MakePropertyLayout()
|
||||
) {
|
||||
return DrawControlRow(label, columnWidth, [&]() {
|
||||
return ImGui::SliderInt("##value", &value, min, max);
|
||||
return DrawPropertyRow(label, layoutSpec, [&](const PropertyLayoutMetrics& layout) {
|
||||
const float totalWidth = layout.controlWidth;
|
||||
const float inputWidth = SliderValueFieldWidth();
|
||||
const float spacing = CompoundControlSpacing();
|
||||
const float sliderWidth = ImMax(totalWidth - inputWidth - spacing, 1.0f);
|
||||
const int range = max - min;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, 0.0f));
|
||||
const float normalizedValue = range > 0 ? static_cast<float>(value - min) / static_cast<float>(range) : 0.0f;
|
||||
bool changed = DrawLinearSlider("##slider", sliderWidth, normalizedValue);
|
||||
if (ImGui::IsItemActive()) {
|
||||
const float trackWidth = std::max(sliderWidth - LinearSliderHorizontalPadding() * 2.0f, 1.0f);
|
||||
const float mouseRatio = std::clamp(
|
||||
(ImGui::GetIO().MousePos.x - (ImGui::GetItemRectMin().x + LinearSliderHorizontalPadding())) / trackWidth,
|
||||
0.0f,
|
||||
1.0f);
|
||||
value = min + static_cast<int>(std::round(mouseRatio * static_cast<float>(range)));
|
||||
}
|
||||
ImGui::SameLine(0.0f, spacing);
|
||||
ImGui::SetNextItemWidth(inputWidth);
|
||||
changed = ImGui::InputInt("##value", &value, 0, 0) || changed;
|
||||
value = std::clamp(value, min, max);
|
||||
ImGui::PopStyleVar();
|
||||
return changed;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -94,14 +268,42 @@ inline int DrawCombo(
|
||||
int currentItem,
|
||||
const char* const items[],
|
||||
int itemCount,
|
||||
float columnWidth = DefaultControlLabelWidth(),
|
||||
const PropertyLayoutSpec& layoutSpec = MakePropertyLayout(),
|
||||
int heightInItems = -1
|
||||
) {
|
||||
int changedItem = currentItem;
|
||||
DrawControlRow(label, columnWidth, [&]() {
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
if (ImGui::Combo("##value", ¤tItem, items, itemCount, heightInItems)) {
|
||||
changedItem = currentItem;
|
||||
DrawPropertyRow(label, layoutSpec, [&](const PropertyLayoutMetrics& layout) {
|
||||
const char* popupId = "##value_popup";
|
||||
const char* previewValue =
|
||||
(currentItem >= 0 && currentItem < itemCount && items[currentItem] != nullptr)
|
||||
? items[currentItem]
|
||||
: "";
|
||||
const float comboWidth = layout.controlWidth;
|
||||
const float popupWidth = comboWidth;
|
||||
DrawComboPreviewFrame("##value", previewValue, comboWidth, ImGui::IsPopupOpen(popupId));
|
||||
|
||||
const ImVec2 comboMin = ImGui::GetItemRectMin();
|
||||
const ImVec2 comboMax = ImGui::GetItemRectMax();
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
|
||||
ImGui::OpenPopup(popupId);
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(comboMin.x, comboMax.y), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSizeConstraints(
|
||||
ImVec2(popupWidth, 0.0f),
|
||||
ImVec2(popupWidth, CalcComboPopupMaxHeightFromItemCount(heightInItems)));
|
||||
if (BeginStyledComboPopup(popupId, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings)) {
|
||||
for (int itemIndex = 0; itemIndex < itemCount; ++itemIndex) {
|
||||
const bool selected = itemIndex == currentItem;
|
||||
if (ImGui::Selectable(items[itemIndex], selected)) {
|
||||
changedItem = itemIndex;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
EndStyledComboPopup();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user