riversong code showcase

This commit is contained in:
Daniele Marotta
2026-05-21 15:52:18 +02:00
commit 4c9eea1c02
462 changed files with 23406 additions and 0 deletions

View File

@@ -0,0 +1,228 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
namespace DanieleMarotta.RiversongCodeShowcase
{
[GameSystemGroup(typeof(UISystemGroup))]
[InitializeAfter(typeof(UIInitializationSystem))]
public class DayNightUIThemeSystem : GameSystem, IInitializable, IDisposable, IUpdatable
{
[InjectService]
private GameConfig _config;
[InjectService]
private UIService _uiService;
[InjectService]
private World _world;
private VisualElement _root;
private RuntimeThemeSheet _runtimeThemeSheet;
private float _lastAppliedNightBlend = -1;
private bool _refreshClassEnabled;
public DayNightUIThemeSystem(IServiceLocator serviceLocator) : base(serviceLocator)
{
}
public UniTask InitializeAsync()
{
_root = _uiService.UIRoot.RootVisualElement;
_runtimeThemeSheet = RuntimeThemeSheet.TryCreate(_root, _config.UI.Theme);
ApplyTheme(true);
return UniTask.CompletedTask;
}
public void Update()
{
ApplyTheme();
}
public void Dispose()
{
if (_root == null || _runtimeThemeSheet == null) return;
_runtimeThemeSheet.Apply(0);
ToggleRefreshClass();
_lastAppliedNightBlend = 0;
}
private void ApplyTheme(bool force = false)
{
if (_root == null || _config.UI.Theme == null || _runtimeThemeSheet == null) return;
var nightBlend = _world.TimeState.NightBlend;
if (!force && !Application.isEditor && Mathf.Approximately(nightBlend, _lastAppliedNightBlend)) return;
_runtimeThemeSheet.Apply(nightBlend);
ToggleRefreshClass();
_lastAppliedNightBlend = nightBlend;
}
private void ToggleRefreshClass()
{
_refreshClassEnabled = !_refreshClassEnabled;
_root.EnableInClassList(RuntimeThemeSheet.RefreshClassName, _refreshClassEnabled);
}
private sealed class RuntimeThemeSheet
{
public const string RefreshClassName = "__day-night-theme-refresh";
private readonly StyleSheet _styleSheet;
private readonly RuntimePropertyBinding[] _bindings;
private RuntimeThemeSheet(StyleSheet styleSheet, RuntimePropertyBinding[] bindings)
{
_styleSheet = styleSheet;
_bindings = bindings;
}
public static RuntimeThemeSheet TryCreate(VisualElement root, DayNightUITheme theme)
{
if (root == null || theme == null) return null;
var styleSheets = StyleSheetReflection.GetStyleSheets(root);
if (styleSheets == null) return null;
foreach (var styleSheet in styleSheets)
{
if (!StyleSheetReflection.TryCreateBindings(styleSheet, theme.OrderedProperties, out var bindings)) continue;
return new RuntimeThemeSheet(styleSheet, bindings);
}
return null;
}
public void Apply(float nightBlend)
{
foreach (var binding in _bindings) binding.Apply(nightBlend);
_styleSheet.contentHash = unchecked(_styleSheet.contentHash + 1);
}
}
private sealed class RuntimePropertyBinding
{
private readonly DayNightUITheme.ThemeColorProperty _themeProperty;
private readonly object _manipulator;
private readonly object[] _setColorArguments = new object[2];
public RuntimePropertyBinding(DayNightUITheme.ThemeColorProperty themeProperty, object manipulator)
{
_themeProperty = themeProperty;
_manipulator = manipulator;
}
public void Apply(float nightBlend)
{
_setColorArguments[0] = 0;
_setColorArguments[1] = Color.Lerp(_themeProperty.DayColor, _themeProperty.NightColor, nightBlend);
StyleSheetReflection.SetColor(_manipulator, _setColorArguments);
}
}
private static class StyleSheetReflection
{
private static readonly FieldInfo VisualElementStyleSheetListField = typeof(VisualElement).GetField("styleSheetList", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly FieldInfo StyleSheetRulesField = typeof(StyleSheet).GetField("m_Rules", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly Type StyleRuleType = StyleSheetRulesField?.FieldType.GetElementType();
private static readonly MethodInfo StyleRuleGetPropertiesMethod = StyleRuleType?.GetMethod("get_properties", BindingFlags.Instance | BindingFlags.Public);
private static readonly Type StylePropertyType = StyleRuleGetPropertiesMethod?.ReturnType.GetElementType();
private static readonly MethodInfo StylePropertyGetNameMethod = StylePropertyType?.GetMethod("get_name", BindingFlags.Instance | BindingFlags.Public);
private static readonly MethodInfo StylePropertyGetManipulatorMethod = StylePropertyType?.GetMethod("GetManipulator", BindingFlags.Instance | BindingFlags.NonPublic);
private static readonly Type ManipulatorType = StylePropertyGetManipulatorMethod?.ReturnType;
private static readonly MethodInfo ManipulatorSetColorMethod = ManipulatorType?.GetMethod(
"SetColor",
BindingFlags.Instance | BindingFlags.Public,
null,
new[]
{
typeof(int),
typeof(Color)
},
null);
public static List<StyleSheet> GetStyleSheets(VisualElement root)
{
return VisualElementStyleSheetListField?.GetValue(root) as List<StyleSheet>;
}
public static void SetColor(object manipulator, object[] arguments)
{
ManipulatorSetColorMethod?.Invoke(manipulator, arguments);
}
public static bool TryCreateBindings(StyleSheet styleSheet, IReadOnlyList<DayNightUITheme.ThemeColorProperty> themeProperties, out RuntimePropertyBinding[] bindings)
{
bindings = null;
if (styleSheet == null || themeProperties == null) return false;
if (StyleSheetRulesField == null ||
StyleRuleGetPropertiesMethod == null ||
StylePropertyGetNameMethod == null ||
StylePropertyGetManipulatorMethod == null ||
ManipulatorSetColorMethod == null)
return false;
var propertiesByName = new Dictionary<string, object>(themeProperties.Count, StringComparer.Ordinal);
var rules = StyleSheetRulesField.GetValue(styleSheet) as Array;
if (rules == null) return false;
foreach (var rule in rules)
{
if (rule == null) continue;
var styleProperties = StyleRuleGetPropertiesMethod.Invoke(rule, null) as Array;
if (styleProperties == null) continue;
foreach (var styleProperty in styleProperties)
{
if (styleProperty == null) continue;
var propertyName = StylePropertyGetNameMethod.Invoke(styleProperty, null) as string;
if (string.IsNullOrEmpty(propertyName)) continue;
if (propertiesByName.ContainsKey(propertyName)) continue;
propertiesByName[propertyName] = styleProperty;
}
}
var resolvedBindings = new RuntimePropertyBinding[themeProperties.Count];
for (var i = 0; i < themeProperties.Count; i++)
{
var themeProperty = themeProperties[i];
if (!propertiesByName.TryGetValue(themeProperty.Name, out var styleProperty)) return false;
var manipulator = StylePropertyGetManipulatorMethod.Invoke(styleProperty, new object[] { styleSheet });
resolvedBindings[i] = new RuntimePropertyBinding(themeProperty, manipulator);
}
bindings = resolvedBindings;
return true;
}
}
}
}