Add XCUI style theme token system
This commit is contained in:
61
engine/include/XCEngine/UI/Style/StyleResolver.h
Normal file
61
engine/include/XCEngine/UI/Style/StyleResolver.h
Normal 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
|
||||
80
engine/include/XCEngine/UI/Style/StyleSet.h
Normal file
80
engine/include/XCEngine/UI/Style/StyleSet.h
Normal 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
|
||||
104
engine/include/XCEngine/UI/Style/StyleTypes.h
Normal file
104
engine/include/XCEngine/UI/Style/StyleTypes.h
Normal 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
|
||||
75
engine/include/XCEngine/UI/Style/Theme.h
Normal file
75
engine/include/XCEngine/UI/Style/Theme.h
Normal 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
|
||||
Reference in New Issue
Block a user