A Blazor/Razor UI panel for picking ear shapes in a character editor. It renders preset buttons (Elf, Goblin, Animal, Human) and an EarShapeSurface component, stores a Vector2 Value representing the selection, and notifies a callback OnValueChanged when a preset or surface changes.
@using Sandbox
@using Sandbox.UI
@using System
@namespace CharacterEditor
@inherits Panel
<root>
<div class="ear-picker-top"><button class=@($"creatorbutton ear-corner ear-elf {(At(Elf)?"chosen":"")}") onclick=@(()=>Pick(Elf)) tooltip="Elf ears">Elf</button></div>
<div class="ear-picker-stage">
<EarShapeSurface Value=@Value OnValueChanged=@Pick />
<button class=@($"creatorbutton ear-human {(At(Human)?"chosen":"")}") onclick=@(()=>Pick(Human)) tooltip="Reset ear shape to Human">Human</button>
</div>
<div class="ear-picker-bottom">
<button class=@($"creatorbutton ear-corner ear-goblin {(At(Goblin)?"chosen":"")}") onclick=@(()=>Pick(Goblin)) tooltip="Goblin ears">Goblin</button>
<button class=@($"creatorbutton ear-corner ear-animal {(At(Animal)?"chosen":"")}") onclick=@(()=>Pick(Animal)) tooltip="Animal ears">Animal</button>
</div>
<div class="fitnesshint ear-picker-hint">Drag between shapes. Human is the center.</div>
</root>
@code {
public Vector2 Value { get; set; } = new(.5f, 2f / 3f);
public Action<Vector2> OnValueChanged { get; set; }
static readonly Vector2 Elf = new(.5f, 0);
static readonly Vector2 Goblin = new(0, 1);
static readonly Vector2 Animal = new(1, 1);
static readonly Vector2 Human = new(.5f, 2f / 3f);
bool At(Vector2 point) => (Value - point).Length < .005f;
void Pick(Vector2 position)
{
Value = position;
OnValueChanged?.Invoke(position);
StateHasChanged();
}
protected override int BuildHash() => Value.GetHashCode();
}