feat: update editor ui framework and assets

This commit is contained in:
2026-03-28 15:07:19 +08:00
parent 4a12e26860
commit 4717b595c4
45 changed files with 2434 additions and 461 deletions

View File

@@ -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", &currentItem, 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;
});