This is a UI component for a game punchboard. It displays an icon and a count, handling visual states like empty or focused, and implements a scale pop animation when its count changes.
@namespace CitySim.Punchboard
@using Sandbox.UI
@inherits Panel
<root class="pb-slot @CssClass @(Empty ? "empty" : "")">
@if ( !Empty )
{
<div class="pb-slot-icon"><PbIcon Icon=@Icon /></div>
@if ( Count > 1 )
{
<div class="pb-slot-count">@($"×{Count}")</div>
}
}
</root>
@code
{
[Parameter] public string Icon { get; set; }
[Parameter] public string CssClass { get; set; }
/// <summary>Shows a count badge when above 1. Changing it plays a pop.</summary>
[Parameter] public int Count { get; set; } = 1;
/// <summary>An empty slot is drawn dashed and dim.</summary>
[Parameter] public bool Empty { get; set; }
/// <summary>Text shown by the engine tooltip.</summary>
[Parameter] public string TooltipText { get; set; }
/// <summary>Called when the pointer or keyboard focus lands on the slot, for custom tooltips.</summary>
[Parameter] public Action Inspected { get; set; }
int lastCount;
float pop = 1f;
protected override void OnParametersSet()
{
base.OnParametersSet();
AcceptsFocus = !Empty;
Tooltip = Empty ? null : TooltipText;
TooltipClass = "city-tooltip";
if ( lastCount != Count && lastCount != 0 ) pop = 0f;
lastCount = Count;
}
protected override void OnMouseOver( MousePanelEvent e ) { base.OnMouseOver( e ); if ( !Empty ) Inspected?.Invoke(); }
protected override void OnFocus( PanelEvent e ) { base.OnFocus( e ); if ( !Empty ) Inspected?.Invoke(); }
public override void Tick()
{
base.Tick();
if ( pop >= 1f ) return;
pop = Math.Min( 1f, pop + Time.Delta / 0.25f );
Style.Set( "transform", $"scale({Pb.F( 0.8f + 0.2f * Pb.BackOut( pop ) )})" );
}
protected override int BuildHash() => HashCode.Combine(GameText.LanguageCode, HashCode.Combine( Icon, Count, Empty, TooltipText, CssClass ));
}