12 DiagnosticSeverity::Error, std::move(
message));
30 float ui::ThemeLayout::*
member;
35const ColorTok kColors[] = {
83const FloatTok kFloats[] = {
111const LayoutTok kLayout[] = {
131 if (!
object)
return nullptr;
132 const auto found =
object->find(
key);
133 return found ==
object->end() ? nullptr : &
found->second;
138 if (!array || array->size() < 4)
return false;
139 for (
int i = 0; i < 4; ++i) {
140 const auto*
number = (*array)[
static_cast<std::size_t
>(i)].getIf<double>();
141 if (!
number || !std::isfinite(*
number) || *number < 0.0 || *number > 1.0)
return false;
142 dest[i] =
static_cast<float>(*number);
148 return EditorValue::Array{
static_cast<double>(src[0]),
static_cast<double>(src[1]),
static_cast<double>(src[2]),
149 static_cast<double>(src[3])};
200 for (
const FloatTok&
token : kFloats)
202 for (
const LayoutTok&
token : kLayout)
203 object[
token.path] =
static_cast<double>(
theme.layout.*(
token.member));
204 object[
"nav.keyboard"] =
theme.navEnableKeyboard;
205 object[
"nav.gamepad"] =
theme.navEnableGamepad;
211 return eve::editing::failed<ui::Theme>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.tokens"),
212 "Theme tokens must be an object");
214 for (
const ColorTok&
token : kColors) {
216 if (!entry || !readColor(*entry,
theme.*(
token.member)))
217 return eve::editing::failed<ui::Theme>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.color"),
218 std::string(
"Theme color is missing or invalid: ") +
token.path);
220 for (
const FloatTok&
token : kFloats) {
222 const auto*
number = entry ? entry->
getIf<
double>() :
nullptr;
224 return eve::editing::failed<ui::Theme>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.float"),
225 std::string(
"Theme number is missing or invalid: ") +
token.path);
228 for (
const LayoutTok&
token : kLayout) {
230 const auto*
number = entry ? entry->
getIf<
double>() :
nullptr;
232 return eve::editing::failed<ui::Theme>(
233 EditorStatus::Rejected, RuleId(
"editor.ui-theme.layout"),
234 std::string(
"Theme layout number is missing or invalid: ") +
token.path);
239 const auto* keyboardFlag = keyboard ? keyboard->
getIf<
bool>() :
nullptr;
240 const auto* gamepadFlag = gamepad ? gamepad->
getIf<
bool>() :
nullptr;
241 if (!keyboardFlag || !gamepadFlag)
242 return eve::editing::failed<ui::Theme>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.nav"),
243 "Theme navigation flags are required");
244 theme.navEnableKeyboard = *keyboardFlag;
245 theme.navEnableGamepad = *gamepadFlag;
247 if (!diagnostics.empty())
248 return eve::editing::failed<ui::Theme>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.range"),
249 diagnostics.front().message());
250 return eve::editing::applied<ui::Theme>(
theme);
254 for (
const ColorTok&
token : kColors) {
257 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.token-color"),
258 std::string(
"Theme color token is invalid: ") +
token.path);
259 return eve::editing::applied<void>();
261 for (
const FloatTok&
token : kFloats) {
265 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.token-float"),
266 std::string(
"Theme number token is invalid: ") +
token.path);
268 return eve::editing::applied<void>();
270 for (
const LayoutTok&
token : kLayout) {
274 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.token-layout"),
275 std::string(
"Theme layout token is invalid: ") +
token.path);
277 return eve::editing::applied<void>();
280 const auto* flag =
value.getIf<
bool>();
282 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.token-nav"),
283 "Theme navigation token is invalid");
284 theme.navEnableKeyboard = *flag;
285 return eve::editing::applied<void>();
288 const auto* flag =
value.getIf<
bool>();
290 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.token-nav"),
291 "Theme navigation token is invalid");
292 theme.navEnableGamepad = *flag;
293 return eve::editing::applied<void>();
295 return eve::editing::failed<void>(EditorStatus::NotFound, RuleId(
"editor.ui-theme.token"),
300 for (
const ColorTok&
token : kColors) {
303 for (
const FloatTok&
token : kFloats) {
306 for (
const LayoutTok&
token : kLayout) {
316 schema.
typeId =
"ui.theme";
317 schema.
properties.push_back(colorProperty(
"color.text"));
318 schema.
properties.push_back(colorProperty(
"color.windowBg"));
319 schema.
properties.push_back(colorProperty(
"color.childBg"));
320 schema.
properties.push_back(colorProperty(
"color.frameBg"));
321 schema.
properties.push_back(colorProperty(
"color.button"));
322 schema.
properties.push_back(colorProperty(
"color.buttonHovered"));
323 schema.
properties.push_back(colorProperty(
"color.buttonActive"));
324 schema.
properties.push_back(colorProperty(
"color.header"));
325 schema.
properties.push_back(colorProperty(
"color.checkMark"));
326 schema.
properties.push_back(colorProperty(
"color.sliderGrab"));
333 keyboard.
type = PropertyType::Bool;
334 keyboard.
flags = PropertyFlag::Runtime;
341 schema.
properties.push_back(std::move(gamepad));
346 std::vector<EditorDiagnostic> diagnostics;
347 for (
const ColorTok&
token : kColors) {
349 for (
int i = 0; i < 4; ++i) {
350 if (!inRange(
color[i], 0.f, 1.f)) {
351 diagnostics.push_back(tokenDiagnostic(
"editor.ui-theme.color-range",
352 std::string(
"Theme color channel is out of range: ") +
token.path));
357 for (
const FloatTok&
token : kFloats) {
359 diagnostics.push_back(tokenDiagnostic(
"editor.ui-theme.float-range",
360 std::string(
"Theme number is out of range: ") +
token.path));
362 for (
const LayoutTok&
token : kLayout) {
364 diagnostics.push_back(tokenDiagnostic(
"editor.ui-theme.layout-range",
365 std::string(
"Theme layout number is out of range: ") +
token.path));
387 return eve::editing::failed<UiThemeBasePreset>(EditorStatus::Rejected, RuleId(
"editor.ui-theme.preset"),
388 "Theme base preset must be dark, light, or custom");
float(ui::Theme::* member)[4]
Move-only operation result carrying either a value or Status.
const std::string & value() const noexcept
Returns the exact legacy or canonical compatibility spelling.
Dotted property path (layout.size) compared and persisted as text.
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
editing::PropertyDescriptor PropertyDescriptor
editing::PropertyPath PropertyPath
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
const EditorValue * field(const EditorValue &value, const char *name)
EditorValue readThemeToken(const ui::Theme &theme, const PropertyPath &path)
Reads theme token.
ui::Theme themeFromPreset(UiThemeBasePreset preset)
Theme from preset.
Result< void > assignThemeToken(ui::Theme &theme, const PropertyPath &path, const EditorValue &value)
Assign theme token.
editing::Diagnostic EditorDiagnostic
std::vector< EditorDiagnostic > validateThemeTokens(const ui::Theme &theme)
Validate theme tokens.
std::string presetName(UiThemeBasePreset preset)
Preset name.
PropertySchema themeTokenSchema()
Theme token schema.
Result< UiThemeBasePreset > parsePreset(const std::string &name)
Parse preset.
UiThemeBasePreset
Base preset used when resetting a named theme asset.
editing::Value EditorValue
Result< ui::Theme > parseThemeTokens(const EditorValue &value)
Parse theme tokens.
EditorValue themeTokensValue(const ui::Theme &theme)
Theme tokens value.
PropertyDescriptor public API.
std::string displayNameKey
EVENGINE_API_PLATFORM public API.
std::vector< PropertyDescriptor > properties
Unified UI design tokens (colors, geometry, typography). Applied to ImGui each frame via applyThemeTo...
float tableBorderLight[4]
float titleBgCollapsed[4]
float scrollbarGrabHovered[4]
float scrollbarGrabActive[4]
float tabUnfocusedActive[4]
static Theme light()
Light.
float sliderGrabActive[4]
float plotLinesHovered[4]
float plotHistogramHovered[4]
float tableBorderStrong[4]
static Theme dark()
Built-in presets with matching geometry; only colors differ.