Add XCUI style theme token system

This commit is contained in:
2026-04-04 19:34:29 +08:00
parent 75ded6f630
commit 95c4a50186
12 changed files with 1100 additions and 1 deletions

View File

@@ -413,7 +413,14 @@ add_library(XCEngine STATIC
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Core/UIElementTree.h
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/UI/Core/UIContext.h
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Core/UIBuildContext.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/UI/Core/UIElementTree.cpp
/src/UI/Core/UIElementTree.cpp
/include/XCEngine/UI/Style/StyleTypes.h
/include/XCEngine/UI/Style/Theme.h
/include/XCEngine/UI/Style/StyleSet.h
/include/XCEngine/UI/Style/StyleResolver.h
/src/UI/Style/StyleTypes.cpp
/src/UI/Style/Theme.cpp
/src/UI/Style/StyleResolver.cpp
# Input
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Input/InputTypes.h

View File

@@ -0,0 +1,61 @@
#pragma once
#include "StyleSet.h"
#include "Theme.h"
#include <map>
namespace XCEngine {
namespace UI {
namespace Style {
enum class UIStyleLayer : std::uint8_t {
None = 0,
Default,
Type,
Named,
Local
};
struct UIStylePropertyResolution {
bool resolved = false;
UIStyleLayer layer = UIStyleLayer::None;
UIStyleValue value = {};
};
class UIResolvedStyle {
public:
void SetProperty(UIStylePropertyId propertyId, const UIStylePropertyResolution& resolution) {
m_properties[propertyId] = resolution;
}
const UIStylePropertyResolution* FindProperty(UIStylePropertyId propertyId) const {
const auto it = m_properties.find(propertyId);
return it != m_properties.end() ? &it->second : nullptr;
}
const std::map<UIStylePropertyId, UIStylePropertyResolution>& GetProperties() const {
return m_properties;
}
private:
std::map<UIStylePropertyId, UIStylePropertyResolution> m_properties = {};
};
struct UIStyleResolveContext {
const UITheme* theme = nullptr;
const UIStyleSheet* styleSheet = nullptr;
UIStyleSelector selector = {};
const UIStyleSet* localStyle = nullptr;
};
UIStylePropertyResolution ResolveStyleProperty(
UIStylePropertyId propertyId,
const UIStyleResolveContext& context);
UIResolvedStyle ResolveStyle(const UIStyleResolveContext& context);
const char* ToString(UIStyleLayer layer);
} // namespace Style
} // namespace UI
} // namespace XCEngine

View File

@@ -0,0 +1,80 @@
#pragma once
#include "StyleTypes.h"
#include <map>
#include <string>
namespace XCEngine {
namespace UI {
namespace Style {
class UIStyleSet {
public:
void SetProperty(UIStylePropertyId propertyId, const UIStyleValue& value) {
m_properties[propertyId] = value;
}
bool RemoveProperty(UIStylePropertyId propertyId) {
return m_properties.erase(propertyId) > 0u;
}
bool HasProperty(UIStylePropertyId propertyId) const {
return m_properties.find(propertyId) != m_properties.end();
}
const UIStyleValue* FindProperty(UIStylePropertyId propertyId) const {
const auto it = m_properties.find(propertyId);
return it != m_properties.end() ? &it->second : nullptr;
}
const std::map<UIStylePropertyId, UIStyleValue>& GetProperties() const {
return m_properties;
}
private:
std::map<UIStylePropertyId, UIStyleValue> m_properties = {};
};
struct UIStyleSelector {
std::string typeName;
std::string styleName;
};
class UIStyleSheet {
public:
UIStyleSet& DefaultStyle() {
return m_defaultStyle;
}
const UIStyleSet& DefaultStyle() const {
return m_defaultStyle;
}
UIStyleSet& GetOrCreateTypeStyle(const std::string& typeName) {
return m_typeStyles[typeName];
}
UIStyleSet& GetOrCreateNamedStyle(const std::string& styleName) {
return m_namedStyles[styleName];
}
const UIStyleSet* FindTypeStyle(const std::string& typeName) const {
const auto it = m_typeStyles.find(typeName);
return it != m_typeStyles.end() ? &it->second : nullptr;
}
const UIStyleSet* FindNamedStyle(const std::string& styleName) const {
const auto it = m_namedStyles.find(styleName);
return it != m_namedStyles.end() ? &it->second : nullptr;
}
private:
UIStyleSet m_defaultStyle = {};
std::map<std::string, UIStyleSet> m_typeStyles = {};
std::map<std::string, UIStyleSet> m_namedStyles = {};
};
} // namespace Style
} // namespace UI
} // namespace XCEngine

View File

@@ -0,0 +1,104 @@
#pragma once
#include <XCEngine/Core/Math/Color.h>
#include <XCEngine/UI/Types.h>
#include <cstdint>
#include <string>
#include <variant>
namespace XCEngine {
namespace UI {
namespace Style {
struct UIThickness {
float left = 0.0f;
float top = 0.0f;
float right = 0.0f;
float bottom = 0.0f;
static UIThickness Uniform(float value);
bool IsUniform() const;
};
struct UICornerRadius {
float topLeft = 0.0f;
float topRight = 0.0f;
float bottomRight = 0.0f;
float bottomLeft = 0.0f;
static UICornerRadius Uniform(float value);
bool IsUniform() const;
};
struct UITokenReference {
std::string name;
bool IsValid() const;
};
enum class UIStyleValueType : std::uint8_t {
None = 0,
Float,
Color,
Thickness,
CornerRadius,
Size,
Point,
TokenReference
};
class UIStyleValue {
public:
using Storage =
std::variant<std::monostate, float, Math::Color, UIThickness, UICornerRadius, UISize, UIPoint, UITokenReference>;
UIStyleValue() = default;
explicit UIStyleValue(float value);
explicit UIStyleValue(const Math::Color& value);
explicit UIStyleValue(const UIThickness& value);
explicit UIStyleValue(const UICornerRadius& value);
explicit UIStyleValue(const UISize& value);
explicit UIStyleValue(const UIPoint& value);
static UIStyleValue Token(const std::string& tokenName);
bool IsSet() const;
bool IsTokenReference() const;
UIStyleValueType GetType() const;
const float* TryGetFloat() const;
const Math::Color* TryGetColor() const;
const UIThickness* TryGetThickness() const;
const UICornerRadius* TryGetCornerRadius() const;
const UISize* TryGetSize() const;
const UIPoint* TryGetPoint() const;
const UITokenReference* TryGetTokenReference() const;
bool operator==(const UIStyleValue& other) const;
bool operator!=(const UIStyleValue& other) const;
private:
explicit UIStyleValue(const UITokenReference& tokenReference);
Storage m_storage = {};
};
enum class UIStylePropertyId : std::uint8_t {
BackgroundColor = 0,
ForegroundColor,
BorderColor,
BorderWidth,
CornerRadius,
Padding,
Gap,
FontSize,
LineWidth
};
UIStyleValueType GetExpectedValueType(UIStylePropertyId propertyId);
const char* ToString(UIStylePropertyId propertyId);
} // namespace Style
} // namespace UI
} // namespace XCEngine

View File

@@ -0,0 +1,75 @@
#pragma once
#include "StyleTypes.h"
#include <map>
#include <string>
#include <vector>
namespace XCEngine {
namespace UI {
namespace Style {
enum class UITokenResolveStatus : std::uint8_t {
Unresolved = 0,
Resolved,
MissingToken,
CycleDetected,
TypeMismatch
};
struct UITokenResolveResult {
UITokenResolveStatus status = UITokenResolveStatus::Unresolved;
UIStyleValue value = {};
};
struct UIThemeDefinition {
std::string name;
std::map<std::string, UIStyleValue> tokens = {};
void SetToken(const std::string& tokenName, const UIStyleValue& value);
const UIStyleValue* FindToken(const std::string& tokenName) const;
};
enum class UIBuiltinThemeKind : std::uint8_t {
NeutralDark = 0,
NeutralLight
};
class UITheme {
public:
UITheme() = default;
explicit UITheme(const UIThemeDefinition& definition);
void SetName(const std::string& name);
const std::string& GetName() const;
void SetParent(const UITheme* parent);
const UITheme* GetParent() const;
void SetToken(const std::string& tokenName, const UIStyleValue& value);
bool RemoveToken(const std::string& tokenName);
const UIStyleValue* FindToken(const std::string& tokenName) const;
const std::map<std::string, UIStyleValue>& GetTokens() const;
UITokenResolveResult ResolveToken(
const std::string& tokenName,
UIStyleValueType expectedType = UIStyleValueType::None) const;
private:
UITokenResolveResult ResolveTokenRecursive(
const std::string& tokenName,
UIStyleValueType expectedType,
std::vector<std::string>& visiting) const;
std::string m_name;
const UITheme* m_parent = nullptr;
std::map<std::string, UIStyleValue> m_tokens = {};
};
UITheme BuildTheme(const UIThemeDefinition& definition, const UITheme* parent = nullptr);
UITheme BuildBuiltinTheme(UIBuiltinThemeKind themeKind);
} // namespace Style
} // namespace UI
} // namespace XCEngine

View File

@@ -0,0 +1,139 @@
#include <XCEngine/UI/Style/StyleResolver.h>
namespace XCEngine {
namespace UI {
namespace Style {
namespace {
bool TryResolveAssignedValue(
const UIStyleValue& assignedValue,
UIStyleValueType expectedType,
const UITheme* theme,
UIStyleValue& outValue) {
if (!assignedValue.IsSet()) {
return false;
}
if (const UITokenReference* tokenReference = assignedValue.TryGetTokenReference()) {
if (theme == nullptr || !tokenReference->IsValid()) {
return false;
}
const UITokenResolveResult tokenResult = theme->ResolveToken(tokenReference->name, expectedType);
if (tokenResult.status != UITokenResolveStatus::Resolved) {
return false;
}
outValue = tokenResult.value;
return true;
}
if (expectedType != UIStyleValueType::None && assignedValue.GetType() != expectedType) {
return false;
}
outValue = assignedValue;
return true;
}
const UIStyleSet* GetStyleSetForLayer(UIStyleLayer layer, const UIStyleResolveContext& context) {
if (context.styleSheet == nullptr) {
return layer == UIStyleLayer::Local ? context.localStyle : nullptr;
}
switch (layer) {
case UIStyleLayer::Default:
return &context.styleSheet->DefaultStyle();
case UIStyleLayer::Type:
return context.styleSheet->FindTypeStyle(context.selector.typeName);
case UIStyleLayer::Named:
return context.styleSheet->FindNamedStyle(context.selector.styleName);
case UIStyleLayer::Local:
return context.localStyle;
default:
return nullptr;
}
}
} // namespace
UIStylePropertyResolution ResolveStyleProperty(
UIStylePropertyId propertyId,
const UIStyleResolveContext& context) {
UIStylePropertyResolution resolution = {};
const UIStyleValueType expectedType = GetExpectedValueType(propertyId);
const UIStyleLayer lookupOrder[] = {
UIStyleLayer::Local,
UIStyleLayer::Named,
UIStyleLayer::Type,
UIStyleLayer::Default
};
for (const UIStyleLayer layer : lookupOrder) {
const UIStyleSet* styleSet = GetStyleSetForLayer(layer, context);
if (styleSet == nullptr) {
continue;
}
const UIStyleValue* assignedValue = styleSet->FindProperty(propertyId);
if (assignedValue == nullptr) {
continue;
}
UIStyleValue resolvedValue = {};
if (!TryResolveAssignedValue(*assignedValue, expectedType, context.theme, resolvedValue)) {
continue;
}
resolution.resolved = true;
resolution.layer = layer;
resolution.value = resolvedValue;
return resolution;
}
return resolution;
}
UIResolvedStyle ResolveStyle(const UIStyleResolveContext& context) {
UIResolvedStyle resolvedStyle = {};
const UIStylePropertyId knownProperties[] = {
UIStylePropertyId::BackgroundColor,
UIStylePropertyId::ForegroundColor,
UIStylePropertyId::BorderColor,
UIStylePropertyId::BorderWidth,
UIStylePropertyId::CornerRadius,
UIStylePropertyId::Padding,
UIStylePropertyId::Gap,
UIStylePropertyId::FontSize,
UIStylePropertyId::LineWidth
};
for (const UIStylePropertyId propertyId : knownProperties) {
const UIStylePropertyResolution propertyResolution = ResolveStyleProperty(propertyId, context);
if (propertyResolution.resolved) {
resolvedStyle.SetProperty(propertyId, propertyResolution);
}
}
return resolvedStyle;
}
const char* ToString(UIStyleLayer layer) {
switch (layer) {
case UIStyleLayer::Default:
return "Default";
case UIStyleLayer::Type:
return "Type";
case UIStyleLayer::Named:
return "Named";
case UIStyleLayer::Local:
return "Local";
default:
return "None";
}
}
} // namespace Style
} // namespace UI
} // namespace XCEngine

View File

@@ -0,0 +1,245 @@
#include <XCEngine/UI/Style/StyleTypes.h>
#include <cmath>
namespace XCEngine {
namespace UI {
namespace Style {
namespace {
bool NearlyEqual(float left, float right) {
return std::abs(left - right) <= Math::EPSILON;
}
bool ColorsEqual(const Math::Color& left, const Math::Color& right) {
return NearlyEqual(left.r, right.r) &&
NearlyEqual(left.g, right.g) &&
NearlyEqual(left.b, right.b) &&
NearlyEqual(left.a, right.a);
}
bool ThicknessEqual(const UIThickness& left, const UIThickness& right) {
return NearlyEqual(left.left, right.left) &&
NearlyEqual(left.top, right.top) &&
NearlyEqual(left.right, right.right) &&
NearlyEqual(left.bottom, right.bottom);
}
bool CornerRadiusEqual(const UICornerRadius& left, const UICornerRadius& right) {
return NearlyEqual(left.topLeft, right.topLeft) &&
NearlyEqual(left.topRight, right.topRight) &&
NearlyEqual(left.bottomRight, right.bottomRight) &&
NearlyEqual(left.bottomLeft, right.bottomLeft);
}
bool SizeEqual(const UISize& left, const UISize& right) {
return NearlyEqual(left.width, right.width) &&
NearlyEqual(left.height, right.height);
}
bool PointEqual(const UIPoint& left, const UIPoint& right) {
return NearlyEqual(left.x, right.x) &&
NearlyEqual(left.y, right.y);
}
} // namespace
UIThickness UIThickness::Uniform(float value) {
return { value, value, value, value };
}
bool UIThickness::IsUniform() const {
return NearlyEqual(left, top) &&
NearlyEqual(left, right) &&
NearlyEqual(left, bottom);
}
UICornerRadius UICornerRadius::Uniform(float value) {
return { value, value, value, value };
}
bool UICornerRadius::IsUniform() const {
return NearlyEqual(topLeft, topRight) &&
NearlyEqual(topLeft, bottomRight) &&
NearlyEqual(topLeft, bottomLeft);
}
bool UITokenReference::IsValid() const {
return !name.empty();
}
UIStyleValue::UIStyleValue(float value)
: m_storage(value) {
}
UIStyleValue::UIStyleValue(const Math::Color& value)
: m_storage(value) {
}
UIStyleValue::UIStyleValue(const UIThickness& value)
: m_storage(value) {
}
UIStyleValue::UIStyleValue(const UICornerRadius& value)
: m_storage(value) {
}
UIStyleValue::UIStyleValue(const UISize& value)
: m_storage(value) {
}
UIStyleValue::UIStyleValue(const UIPoint& value)
: m_storage(value) {
}
UIStyleValue::UIStyleValue(const UITokenReference& tokenReference)
: m_storage(tokenReference) {
}
UIStyleValue UIStyleValue::Token(const std::string& tokenName) {
return UIStyleValue(UITokenReference{ tokenName });
}
bool UIStyleValue::IsSet() const {
return !std::holds_alternative<std::monostate>(m_storage);
}
bool UIStyleValue::IsTokenReference() const {
return std::holds_alternative<UITokenReference>(m_storage);
}
UIStyleValueType UIStyleValue::GetType() const {
if (std::holds_alternative<float>(m_storage)) {
return UIStyleValueType::Float;
}
if (std::holds_alternative<Math::Color>(m_storage)) {
return UIStyleValueType::Color;
}
if (std::holds_alternative<UIThickness>(m_storage)) {
return UIStyleValueType::Thickness;
}
if (std::holds_alternative<UICornerRadius>(m_storage)) {
return UIStyleValueType::CornerRadius;
}
if (std::holds_alternative<UISize>(m_storage)) {
return UIStyleValueType::Size;
}
if (std::holds_alternative<UIPoint>(m_storage)) {
return UIStyleValueType::Point;
}
if (std::holds_alternative<UITokenReference>(m_storage)) {
return UIStyleValueType::TokenReference;
}
return UIStyleValueType::None;
}
const float* UIStyleValue::TryGetFloat() const {
return std::get_if<float>(&m_storage);
}
const Math::Color* UIStyleValue::TryGetColor() const {
return std::get_if<Math::Color>(&m_storage);
}
const UIThickness* UIStyleValue::TryGetThickness() const {
return std::get_if<UIThickness>(&m_storage);
}
const UICornerRadius* UIStyleValue::TryGetCornerRadius() const {
return std::get_if<UICornerRadius>(&m_storage);
}
const UISize* UIStyleValue::TryGetSize() const {
return std::get_if<UISize>(&m_storage);
}
const UIPoint* UIStyleValue::TryGetPoint() const {
return std::get_if<UIPoint>(&m_storage);
}
const UITokenReference* UIStyleValue::TryGetTokenReference() const {
return std::get_if<UITokenReference>(&m_storage);
}
bool UIStyleValue::operator==(const UIStyleValue& other) const {
if (GetType() != other.GetType()) {
return false;
}
if (const float* left = TryGetFloat()) {
return NearlyEqual(*left, *other.TryGetFloat());
}
if (const Math::Color* left = TryGetColor()) {
return ColorsEqual(*left, *other.TryGetColor());
}
if (const UIThickness* left = TryGetThickness()) {
return ThicknessEqual(*left, *other.TryGetThickness());
}
if (const UICornerRadius* left = TryGetCornerRadius()) {
return CornerRadiusEqual(*left, *other.TryGetCornerRadius());
}
if (const UISize* left = TryGetSize()) {
return SizeEqual(*left, *other.TryGetSize());
}
if (const UIPoint* left = TryGetPoint()) {
return PointEqual(*left, *other.TryGetPoint());
}
if (const UITokenReference* left = TryGetTokenReference()) {
return left->name == other.TryGetTokenReference()->name;
}
return true;
}
bool UIStyleValue::operator!=(const UIStyleValue& other) const {
return !(*this == other);
}
UIStyleValueType GetExpectedValueType(UIStylePropertyId propertyId) {
switch (propertyId) {
case UIStylePropertyId::BackgroundColor:
case UIStylePropertyId::ForegroundColor:
case UIStylePropertyId::BorderColor:
return UIStyleValueType::Color;
case UIStylePropertyId::BorderWidth:
case UIStylePropertyId::Gap:
case UIStylePropertyId::FontSize:
case UIStylePropertyId::LineWidth:
return UIStyleValueType::Float;
case UIStylePropertyId::CornerRadius:
return UIStyleValueType::CornerRadius;
case UIStylePropertyId::Padding:
return UIStyleValueType::Thickness;
default:
return UIStyleValueType::None;
}
}
const char* ToString(UIStylePropertyId propertyId) {
switch (propertyId) {
case UIStylePropertyId::BackgroundColor:
return "BackgroundColor";
case UIStylePropertyId::ForegroundColor:
return "ForegroundColor";
case UIStylePropertyId::BorderColor:
return "BorderColor";
case UIStylePropertyId::BorderWidth:
return "BorderWidth";
case UIStylePropertyId::CornerRadius:
return "CornerRadius";
case UIStylePropertyId::Padding:
return "Padding";
case UIStylePropertyId::Gap:
return "Gap";
case UIStylePropertyId::FontSize:
return "FontSize";
case UIStylePropertyId::LineWidth:
return "LineWidth";
default:
return "Unknown";
}
}
} // namespace Style
} // namespace UI
} // namespace XCEngine

View File

@@ -0,0 +1,145 @@
#include <XCEngine/UI/Style/Theme.h>
#include <algorithm>
namespace XCEngine {
namespace UI {
namespace Style {
void UIThemeDefinition::SetToken(const std::string& tokenName, const UIStyleValue& value) {
tokens[tokenName] = value;
}
const UIStyleValue* UIThemeDefinition::FindToken(const std::string& tokenName) const {
const auto it = tokens.find(tokenName);
return it != tokens.end() ? &it->second : nullptr;
}
UITheme::UITheme(const UIThemeDefinition& definition)
: m_name(definition.name)
, m_tokens(definition.tokens) {
}
void UITheme::SetName(const std::string& name) {
m_name = name;
}
const std::string& UITheme::GetName() const {
return m_name;
}
void UITheme::SetParent(const UITheme* parent) {
m_parent = parent;
}
const UITheme* UITheme::GetParent() const {
return m_parent;
}
void UITheme::SetToken(const std::string& tokenName, const UIStyleValue& value) {
m_tokens[tokenName] = value;
}
bool UITheme::RemoveToken(const std::string& tokenName) {
return m_tokens.erase(tokenName) > 0u;
}
const UIStyleValue* UITheme::FindToken(const std::string& tokenName) const {
const auto it = m_tokens.find(tokenName);
if (it != m_tokens.end()) {
return &it->second;
}
return m_parent != nullptr ? m_parent->FindToken(tokenName) : nullptr;
}
const std::map<std::string, UIStyleValue>& UITheme::GetTokens() const {
return m_tokens;
}
UITokenResolveResult UITheme::ResolveToken(
const std::string& tokenName,
UIStyleValueType expectedType) const {
std::vector<std::string> visiting = {};
return ResolveTokenRecursive(tokenName, expectedType, visiting);
}
UITokenResolveResult UITheme::ResolveTokenRecursive(
const std::string& tokenName,
UIStyleValueType expectedType,
std::vector<std::string>& visiting) const {
UITokenResolveResult result = {};
if (tokenName.empty()) {
result.status = UITokenResolveStatus::MissingToken;
return result;
}
if (std::find(visiting.begin(), visiting.end(), tokenName) != visiting.end()) {
result.status = UITokenResolveStatus::CycleDetected;
return result;
}
const UIStyleValue* value = FindToken(tokenName);
if (value == nullptr || !value->IsSet()) {
result.status = UITokenResolveStatus::MissingToken;
return result;
}
if (const UITokenReference* tokenReference = value->TryGetTokenReference()) {
visiting.push_back(tokenName);
result = ResolveTokenRecursive(tokenReference->name, expectedType, visiting);
visiting.pop_back();
return result;
}
if (expectedType != UIStyleValueType::None && value->GetType() != expectedType) {
result.status = UITokenResolveStatus::TypeMismatch;
return result;
}
result.status = UITokenResolveStatus::Resolved;
result.value = *value;
return result;
}
UITheme BuildTheme(const UIThemeDefinition& definition, const UITheme* parent) {
UITheme theme(definition);
theme.SetParent(parent);
return theme;
}
UITheme BuildBuiltinTheme(UIBuiltinThemeKind themeKind) {
UIThemeDefinition definition = {};
if (themeKind == UIBuiltinThemeKind::NeutralLight) {
definition.name = "NeutralLight";
definition.SetToken("color.surface", UIStyleValue(Math::Color(0.93f, 0.94f, 0.96f, 1.0f)));
definition.SetToken("color.surface.elevated", UIStyleValue(Math::Color(1.0f, 1.0f, 1.0f, 1.0f)));
definition.SetToken("color.text.primary", UIStyleValue(Math::Color(0.10f, 0.11f, 0.14f, 1.0f)));
definition.SetToken("color.accent", UIStyleValue(Math::Color(0.14f, 0.43f, 0.88f, 1.0f)));
definition.SetToken("space.compact", UIStyleValue(6.0f));
definition.SetToken("space.regular", UIStyleValue(10.0f));
definition.SetToken("padding.control", UIStyleValue(UIThickness::Uniform(10.0f)));
definition.SetToken("radius.control", UIStyleValue(UICornerRadius::Uniform(6.0f)));
definition.SetToken("font.body", UIStyleValue(14.0f));
definition.SetToken("line.default", UIStyleValue(1.0f));
return BuildTheme(definition);
}
definition.name = "NeutralDark";
definition.SetToken("color.surface", UIStyleValue(Math::Color(0.13f, 0.14f, 0.16f, 1.0f)));
definition.SetToken("color.surface.elevated", UIStyleValue(Math::Color(0.18f, 0.19f, 0.22f, 1.0f)));
definition.SetToken("color.text.primary", UIStyleValue(Math::Color(0.92f, 0.93f, 0.95f, 1.0f)));
definition.SetToken("color.accent", UIStyleValue(Math::Color(0.98f, 0.53f, 0.17f, 1.0f)));
definition.SetToken("space.compact", UIStyleValue(6.0f));
definition.SetToken("space.regular", UIStyleValue(10.0f));
definition.SetToken("padding.control", UIStyleValue(UIThickness::Uniform(10.0f)));
definition.SetToken("radius.control", UIStyleValue(UICornerRadius::Uniform(6.0f)));
definition.SetToken("font.body", UIStyleValue(14.0f));
definition.SetToken("line.default", UIStyleValue(1.0f));
return BuildTheme(definition);
}
} // namespace Style
} // namespace UI
} // namespace XCEngine