关键节点

This commit is contained in:
2026-04-25 16:46:01 +08:00
parent 6002d86a7e
commit ef41c44464
516 changed files with 6175 additions and 12401 deletions

View File

@@ -0,0 +1,90 @@
#include <XCEditor/Widgets/UIEditorCollectionPrimitives.h>
namespace XCEngine::UI::Editor::Widgets {
UIEditorCollectionPrimitiveKind ClassifyUIEditorCollectionPrimitive(std::string_view tagName) {
if (tagName == "ScrollView") {
return UIEditorCollectionPrimitiveKind::ScrollView;
}
if (tagName == "TreeView") {
return UIEditorCollectionPrimitiveKind::TreeView;
}
if (tagName == "TreeItem") {
return UIEditorCollectionPrimitiveKind::TreeItem;
}
if (tagName == "ListView") {
return UIEditorCollectionPrimitiveKind::ListView;
}
if (tagName == "ListItem") {
return UIEditorCollectionPrimitiveKind::ListItem;
}
if (tagName == "PropertySection") {
return UIEditorCollectionPrimitiveKind::PropertySection;
}
if (tagName == "FieldRow") {
return UIEditorCollectionPrimitiveKind::FieldRow;
}
return UIEditorCollectionPrimitiveKind::None;
}
bool IsUIEditorCollectionPrimitiveContainer(UIEditorCollectionPrimitiveKind kind) {
return kind == UIEditorCollectionPrimitiveKind::ScrollView ||
kind == UIEditorCollectionPrimitiveKind::TreeView ||
kind == UIEditorCollectionPrimitiveKind::ListView ||
kind == UIEditorCollectionPrimitiveKind::PropertySection;
}
bool UsesUIEditorCollectionPrimitiveColumnLayout(UIEditorCollectionPrimitiveKind kind) {
return kind == UIEditorCollectionPrimitiveKind::TreeView ||
kind == UIEditorCollectionPrimitiveKind::ListView ||
kind == UIEditorCollectionPrimitiveKind::PropertySection;
}
bool IsUIEditorCollectionPrimitiveHoverable(UIEditorCollectionPrimitiveKind kind) {
return kind == UIEditorCollectionPrimitiveKind::TreeItem ||
kind == UIEditorCollectionPrimitiveKind::ListItem ||
kind == UIEditorCollectionPrimitiveKind::PropertySection ||
kind == UIEditorCollectionPrimitiveKind::FieldRow;
}
bool DoesUIEditorCollectionPrimitiveClipChildren(UIEditorCollectionPrimitiveKind kind) {
return kind == UIEditorCollectionPrimitiveKind::ScrollView ||
kind == UIEditorCollectionPrimitiveKind::TreeView ||
kind == UIEditorCollectionPrimitiveKind::ListView;
}
float ResolveUIEditorCollectionPrimitivePadding(
UIEditorCollectionPrimitiveKind kind) {
return kind == UIEditorCollectionPrimitiveKind::TreeView ||
kind == UIEditorCollectionPrimitiveKind::ListView ||
kind == UIEditorCollectionPrimitiveKind::PropertySection
? 12.0f
: 0.0f;
}
float ResolveUIEditorCollectionPrimitiveDefaultHeight(
UIEditorCollectionPrimitiveKind kind) {
switch (kind) {
case UIEditorCollectionPrimitiveKind::TreeItem:
return 28.0f;
case UIEditorCollectionPrimitiveKind::ListItem:
return 60.0f;
case UIEditorCollectionPrimitiveKind::FieldRow:
return 32.0f;
case UIEditorCollectionPrimitiveKind::PropertySection:
return 148.0f;
default:
return 0.0f;
}
}
float ResolveUIEditorCollectionPrimitiveIndent(
UIEditorCollectionPrimitiveKind kind,
float indentLevel) {
return kind == UIEditorCollectionPrimitiveKind::TreeItem
? indentLevel * 18.0f
: 0.0f;
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,168 @@
#include <XCEditor/Widgets/UIEditorColorUtils.h>
#include <algorithm>
#include <cmath>
#include <cstdio>
namespace XCEngine::UI::Editor::Widgets {
float ClampUIEditorColorUnit(float value) {
return (std::clamp)(value, 0.0f, 1.0f);
}
int ToUIEditorColorByte(float value) {
return static_cast<int>(std::lround(ClampUIEditorColorUnit(value) * 255.0f));
}
UIEditorHsvColor ConvertUIEditorColorToHsv(
const ::XCEngine::UI::UIColor& color,
float fallbackHue) {
const float red = ClampUIEditorColorUnit(color.r);
const float green = ClampUIEditorColorUnit(color.g);
const float blue = ClampUIEditorColorUnit(color.b);
const float maxChannel = (std::max)({ red, green, blue });
const float minChannel = (std::min)({ red, green, blue });
const float delta = maxChannel - minChannel;
UIEditorHsvColor hsv = {};
hsv.hue = ClampUIEditorColorUnit(fallbackHue);
hsv.saturation = maxChannel <= 0.0f ? 0.0f : delta / maxChannel;
hsv.value = maxChannel;
hsv.alpha = ClampUIEditorColorUnit(color.a);
if (delta <= 0.00001f) {
return hsv;
}
if (maxChannel == red) {
hsv.hue = std::fmod(((green - blue) / delta), 6.0f) / 6.0f;
} else if (maxChannel == green) {
hsv.hue = (((blue - red) / delta) + 2.0f) / 6.0f;
} else {
hsv.hue = (((red - green) / delta) + 4.0f) / 6.0f;
}
if (hsv.hue < 0.0f) {
hsv.hue += 1.0f;
}
return hsv;
}
::XCEngine::UI::UIColor ConvertUIEditorHsvToColor(const UIEditorHsvColor& hsv) {
const float hue = ClampUIEditorColorUnit(hsv.hue);
const float saturation = ClampUIEditorColorUnit(hsv.saturation);
const float value = ClampUIEditorColorUnit(hsv.value);
if (saturation <= 0.00001f) {
return ::XCEngine::UI::UIColor(value, value, value, ClampUIEditorColorUnit(hsv.alpha));
}
const float sector = hue * 6.0f;
const int sectorIndex = static_cast<int>(std::floor(sector)) % 6;
const float fraction = sector - std::floor(sector);
const float p = value * (1.0f - saturation);
const float q = value * (1.0f - saturation * fraction);
const float t = value * (1.0f - saturation * (1.0f - fraction));
float red = value;
float green = t;
float blue = p;
switch (sectorIndex) {
case 0:
red = value;
green = t;
blue = p;
break;
case 1:
red = q;
green = value;
blue = p;
break;
case 2:
red = p;
green = value;
blue = t;
break;
case 3:
red = p;
green = q;
blue = value;
break;
case 4:
red = t;
green = p;
blue = value;
break;
case 5:
default:
red = value;
green = p;
blue = q;
break;
}
return ::XCEngine::UI::UIColor(red, green, blue, ClampUIEditorColorUnit(hsv.alpha));
}
UIEditorHsvColor ResolveUIEditorDisplayHsv(
const ::XCEngine::UI::UIColor& color,
float rememberedHue,
bool hueValid) {
UIEditorHsvColor hsv = ConvertUIEditorColorToHsv(color, hueValid ? rememberedHue : 0.0f);
if (hsv.saturation <= 0.00001f && hueValid) {
hsv.hue = ClampUIEditorColorUnit(rememberedHue);
}
return hsv;
}
std::string FormatUIEditorColorHex(
const ::XCEngine::UI::UIColor& color,
bool includeAlpha) {
char buffer[16] = {};
if (includeAlpha) {
std::snprintf(
buffer,
sizeof(buffer),
"#%02X%02X%02X%02X",
ToUIEditorColorByte(color.r),
ToUIEditorColorByte(color.g),
ToUIEditorColorByte(color.b),
ToUIEditorColorByte(color.a));
} else {
std::snprintf(
buffer,
sizeof(buffer),
"#%02X%02X%02X",
ToUIEditorColorByte(color.r),
ToUIEditorColorByte(color.g),
ToUIEditorColorByte(color.b));
}
return std::string(buffer);
}
std::string FormatUIEditorColorChannelsText(
const ::XCEngine::UI::UIColor& color,
bool includeAlpha) {
char buffer[64] = {};
if (includeAlpha) {
std::snprintf(
buffer,
sizeof(buffer),
"RGBA %d, %d, %d, %d",
ToUIEditorColorByte(color.r),
ToUIEditorColorByte(color.g),
ToUIEditorColorByte(color.b),
ToUIEditorColorByte(color.a));
} else {
std::snprintf(
buffer,
sizeof(buffer),
"RGB %d, %d, %d",
ToUIEditorColorByte(color.r),
ToUIEditorColorByte(color.g),
ToUIEditorColorByte(color.b));
}
return std::string(buffer);
}
} // namespace XCEngine::UI::Editor::Widgets

View File

@@ -0,0 +1,116 @@
#include <XCEditor/Widgets/UIEditorFieldRowLayout.h>
#include <algorithm>
#include <cmath>
namespace XCEngine::UI::Editor::Widgets {
namespace {
float ClampNonNegative(float value) {
return (std::max)(0.0f, value);
}
bool AreEqual(float lhs, float rhs) {
return std::abs(lhs - rhs) <= 0.001f;
}
} // namespace
const UIEditorInspectorFieldStyleTokens& GetUIEditorInspectorFieldStyleTokens() {
static const UIEditorInspectorFieldStyleTokens kTokens = [] {
UIEditorInspectorFieldStyleTokens tokens = {};
tokens.rowHoverColor = ::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
tokens.rowActiveColor = ::XCEngine::UI::UIColor(0.17f, 0.17f, 0.17f, 1.0f);
tokens.labelColor = ::XCEngine::UI::UIColor(0.72f, 0.72f, 0.72f, 1.0f);
tokens.valueColor = ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
tokens.readOnlyValueColor = ::XCEngine::UI::UIColor(0.60f, 0.60f, 0.60f, 1.0f);
tokens.controlColor = ::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
tokens.controlHoverColor = ::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
tokens.controlEditingColor = ::XCEngine::UI::UIColor(0.18f, 0.18f, 0.18f, 1.0f);
tokens.controlReadOnlyColor = ::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
tokens.controlBorderColor = ::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
tokens.controlFocusedBorderColor = ::XCEngine::UI::UIColor(0.19f, 0.19f, 0.19f, 1.0f);
tokens.prefixColor = ::XCEngine::UI::UIColor(0.13f, 0.13f, 0.13f, 1.0f);
tokens.prefixBorderColor = ::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
tokens.popupColor = ::XCEngine::UI::UIColor(0.10f, 0.10f, 0.10f, 1.0f);
tokens.popupBorderColor = ::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
tokens.popupHeaderColor = ::XCEngine::UI::UIColor(0.11f, 0.11f, 0.11f, 1.0f);
tokens.popupTitleColor = ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
tokens.popupTextColor = ::XCEngine::UI::UIColor(0.88f, 0.88f, 0.88f, 1.0f);
tokens.popupTextMutedColor = ::XCEngine::UI::UIColor(0.66f, 0.66f, 0.66f, 1.0f);
tokens.previewBorderColor = ::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
tokens.previewBaseColor = ::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
tokens.checkerLightColor = ::XCEngine::UI::UIColor(0.24f, 0.24f, 0.24f, 1.0f);
tokens.checkerDarkColor = ::XCEngine::UI::UIColor(0.14f, 0.14f, 0.14f, 1.0f);
tokens.sliderBorderColor = ::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
tokens.numericBoxColor = ::XCEngine::UI::UIColor(0.12f, 0.12f, 0.12f, 1.0f);
tokens.numericBoxBorderColor = ::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
tokens.numericBoxTextColor = ::XCEngine::UI::UIColor(0.92f, 0.92f, 0.92f, 1.0f);
tokens.closeButtonHoverColor = ::XCEngine::UI::UIColor(0.15f, 0.15f, 0.15f, 1.0f);
tokens.closeGlyphColor = ::XCEngine::UI::UIColor(0.86f, 0.86f, 0.86f, 1.0f);
return tokens;
}();
return kTokens;
}
bool AreUIEditorFieldMetricsEqual(float lhs, float rhs) {
return AreEqual(lhs, rhs);
}
bool AreUIEditorFieldColorsEqual(
const ::XCEngine::UI::UIColor& lhs,
const ::XCEngine::UI::UIColor& rhs) {
return AreEqual(lhs.r, rhs.r) &&
AreEqual(lhs.g, rhs.g) &&
AreEqual(lhs.b, rhs.b) &&
AreEqual(lhs.a, rhs.a);
}
UIEditorFieldRowLayout BuildUIEditorFieldRowLayout(
const ::XCEngine::UI::UIRect& bounds,
float minimumControlWidth,
const UIEditorFieldRowLayoutMetrics& metrics) {
const float rowHeight = bounds.height > 0.0f ? bounds.height : metrics.rowHeight;
const ::XCEngine::UI::UIRect rowBounds(bounds.x, bounds.y, bounds.width, rowHeight);
const float horizontalPadding = ClampNonNegative(metrics.horizontalPadding);
const float trailingInset = ClampNonNegative(metrics.controlTrailingInset);
const float defaultGap = ClampNonNegative(metrics.labelControlGap);
const float contentLeft = rowBounds.x + horizontalPadding;
const float contentRight =
rowBounds.x + ClampNonNegative(rowBounds.width) - trailingInset;
const float contentWidth = ClampNonNegative(contentRight - contentLeft);
const float requestedReservedControlWidth = ClampNonNegative(
metrics.controlMinWidth > 0.0f
? metrics.controlMinWidth
: minimumControlWidth);
const float reservedControlWidth =
ClampNonNegative((std::min)(requestedReservedControlWidth, contentWidth));
const float effectiveGap =
(std::min)(defaultGap, ClampNonNegative(contentWidth - reservedControlWidth));
const float preferredControlX =
(std::clamp)(rowBounds.x + metrics.controlColumnStart, contentLeft, contentRight);
const float maximumControlX = contentRight - reservedControlWidth;
const float controlX =
(std::clamp)((std::min)(preferredControlX, maximumControlX), contentLeft, contentRight);
const float controlInsetY = (std::min)(metrics.controlInsetY, rowBounds.height * 0.25f);
const float controlWidth =
ClampNonNegative(contentRight - controlX);
UIEditorFieldRowLayout layout = {};
layout.bounds = rowBounds;
layout.labelRect = ::XCEngine::UI::UIRect(
contentLeft,
rowBounds.y,
ClampNonNegative(controlX - effectiveGap - contentLeft),
rowBounds.height);
layout.controlRect = ::XCEngine::UI::UIRect(
controlX,
rowBounds.y + controlInsetY,
controlWidth,
ClampNonNegative(rowBounds.height - controlInsetY * 2.0f));
return layout;
}
} // namespace XCEngine::UI::Editor::Widgets