146 lines
5.0 KiB
C++
146 lines
5.0 KiB
C++
|
|
#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
|