feat(ui): port retained widget foundations
This commit is contained in:
parent
44f9ec13d9
commit
d825572e31
44 changed files with 84813 additions and 292 deletions
152
src/AcDream.App/UI/Layout/UiPropertyBag.cs
Normal file
152
src/AcDream.App/UI/Layout/UiPropertyBag.cs
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
namespace AcDream.App.UI.Layout;
|
||||
|
||||
/// <summary>
|
||||
/// Dat property value kinds carried by a retail <c>StateDesc</c>. The enum mirrors
|
||||
/// <c>BasePropertyType</c> without exposing DatReaderWriter types to the retained UI.
|
||||
/// </summary>
|
||||
public enum UiPropertyKind : byte
|
||||
{
|
||||
Enum,
|
||||
Bool,
|
||||
DataId,
|
||||
Float,
|
||||
Integer,
|
||||
StringInfo,
|
||||
Color,
|
||||
Array,
|
||||
Struct,
|
||||
Vector,
|
||||
Bitfield32,
|
||||
Bitfield64,
|
||||
InstanceId,
|
||||
}
|
||||
|
||||
/// <summary>Exact byte representation of a retail ARGB property value.</summary>
|
||||
public readonly record struct UiColorValue(byte Blue, byte Green, byte Red, byte Alpha);
|
||||
|
||||
/// <summary>Dat-independent snapshot of a retail <c>StringInfo</c>.</summary>
|
||||
public readonly record struct UiStringInfoValue(
|
||||
byte Token,
|
||||
uint StringId,
|
||||
uint TableId,
|
||||
byte Override,
|
||||
byte English,
|
||||
byte Comment);
|
||||
|
||||
/// <summary>
|
||||
/// Lossless, serializable representation of one supported retail base property.
|
||||
/// Only the field selected by <see cref="Kind"/> is meaningful. Arrays and structs
|
||||
/// recursively preserve their members and each member's master-property id.
|
||||
/// </summary>
|
||||
public sealed class UiPropertyValue
|
||||
{
|
||||
public UiPropertyKind Kind;
|
||||
public uint MasterPropertyId;
|
||||
public ulong UnsignedValue;
|
||||
public int IntegerValue;
|
||||
public float FloatValue;
|
||||
public bool BoolValue;
|
||||
public UiStringInfoValue StringInfoValue;
|
||||
public UiColorValue ColorValue;
|
||||
public Vector3 VectorValue;
|
||||
public List<UiPropertyValue> ArrayValue = new();
|
||||
public Dictionary<uint, UiPropertyValue> StructValue = new();
|
||||
|
||||
public UiPropertyValue Clone()
|
||||
{
|
||||
var clone = new UiPropertyValue
|
||||
{
|
||||
Kind = Kind,
|
||||
MasterPropertyId = MasterPropertyId,
|
||||
UnsignedValue = UnsignedValue,
|
||||
IntegerValue = IntegerValue,
|
||||
FloatValue = FloatValue,
|
||||
BoolValue = BoolValue,
|
||||
StringInfoValue = StringInfoValue,
|
||||
ColorValue = ColorValue,
|
||||
VectorValue = VectorValue,
|
||||
};
|
||||
|
||||
foreach (var item in ArrayValue)
|
||||
clone.ArrayValue.Add(item.Clone());
|
||||
foreach (var (key, value) in StructValue)
|
||||
clone.StructValue[key] = value.Clone();
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State-scoped retail properties. Presence of a key is authoritative: an explicit
|
||||
/// false, zero, centered justification, or empty collection still overrides a base
|
||||
/// value during inheritance.
|
||||
/// </summary>
|
||||
public sealed class UiPropertyBag
|
||||
{
|
||||
public Dictionary<uint, UiPropertyValue> Values = new();
|
||||
|
||||
public bool TryGetValue(uint id, out UiPropertyValue value)
|
||||
=> Values.TryGetValue(id, out value!);
|
||||
|
||||
public UiPropertyBag Clone()
|
||||
{
|
||||
var clone = new UiPropertyBag();
|
||||
foreach (var (key, value) in Values)
|
||||
clone.Values[key] = value.Clone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
public static UiPropertyBag Merge(UiPropertyBag baseProperties, UiPropertyBag derivedProperties)
|
||||
{
|
||||
var merged = baseProperties.Clone();
|
||||
foreach (var (key, value) in derivedProperties.Values)
|
||||
merged.Values[key] = value.Clone();
|
||||
return merged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Primary render-surface media for a retail UI state.</summary>
|
||||
public readonly record struct UiImageMedia(uint File, int DrawMode);
|
||||
|
||||
/// <summary>
|
||||
/// Dat-independent state descriptor. DirectState uses
|
||||
/// <see cref="DirectStateId"/> so it cannot collide with <c>UIStateId.Undef == 0</c>.
|
||||
/// </summary>
|
||||
public sealed class UiStateInfo
|
||||
{
|
||||
public const uint DirectStateId = uint.MaxValue;
|
||||
|
||||
public uint Id;
|
||||
public string Name = "";
|
||||
public bool PassToChildren;
|
||||
public uint IncorporationFlags;
|
||||
public UiImageMedia? Image;
|
||||
public UiCursorMedia? Cursor;
|
||||
public UiPropertyBag Properties = new();
|
||||
|
||||
public UiStateInfo Clone()
|
||||
=> new()
|
||||
{
|
||||
Id = Id,
|
||||
Name = Name,
|
||||
PassToChildren = PassToChildren,
|
||||
IncorporationFlags = IncorporationFlags,
|
||||
Image = Image,
|
||||
Cursor = Cursor,
|
||||
Properties = Properties.Clone(),
|
||||
};
|
||||
|
||||
public static UiStateInfo Merge(UiStateInfo baseState, UiStateInfo derivedState)
|
||||
=> new()
|
||||
{
|
||||
Id = derivedState.Id,
|
||||
Name = derivedState.Name,
|
||||
PassToChildren = derivedState.PassToChildren,
|
||||
IncorporationFlags = derivedState.IncorporationFlags,
|
||||
Image = derivedState.Image ?? baseState.Image,
|
||||
Cursor = derivedState.Cursor ?? baseState.Cursor,
|
||||
Properties = UiPropertyBag.Merge(baseState.Properties, derivedState.Properties),
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue