Code/CharacterKit/EarShapePicker.razor

A Blazor/Razor UI component for choosing ear shapes in a character editor. It displays buttons for presets (Elf, Goblin, Animal, Human) and a draggable surface (EarShapeSurface) that updates a Vector2 Value and notifies a callback when changed.

File Access
@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();
}