Studio/StudioEscapeMenu.razor

A Razor UI panel for the Character Editor escape menu. It shows menu options (resume, main menu, quit), an optional Experimental tab, and a confirmation dialog when navigating away with unsaved edits.

NetworkingFile Access
@using System
@using Sandbox
@using Sandbox.UI
@namespace CharacterEditor
@inherits Panel

<root>
@if(IsOpen)
{
 <div class="menu-shade"><div class="menu-card">
 @if(IsConfirming)
 {
  <h2>Discard unsaved edits?</h2><p>Your saved characters will stay as they are.</p>
  <button onclick=@CancelNavigation>Keep editing</button>
  <button class="primary" onclick=@ConfirmNavigation>Discard edits and continue</button>
 }
 else
 {
  <div class="menu-title">Character Editor</div>
  <div class="menu-tabs"><button class=@(!Experimental?"chosen":"") onclick=@(()=>Experimental=false)>Menu</button>@if(EnableExperiments){<button class=@(Experimental?"chosen":"") onclick=@(()=>Experimental=true)>Experimental</button>}</div>
  @if(Experimental && EnableExperiments)
  {
   <h2>Experiments</h2><p>Development tools and research. These do not create saved characters.</p>
   <button onclick=@(()=>RequestNavigation(OpenLab))>Face lab</button>
  }
  else
  {
   <button class="primary" onclick=@Close>Resume</button>
   <button onclick=@(()=>RequestNavigation(MainMenu))>Main menu</button>
   <button onclick=@(()=>RequestNavigation(()=>Game.Close()))>Quit to s&amp;box</button>
  }
  <div class="menu-hint">Escape to return</div>
 }
 </div></div>
}
</root>

@code {
	public Action MainMenu { get; set; }
	public Action OpenLab { get; set; }
	public Func<bool> HasUnsavedEdits { get; set; }
	public bool EnableExperiments { get; set; }
	public bool IsOpen { get; private set; }
	public bool IsConfirming => pending is not null;

	bool Experimental;
	Action pending;
	public void Toggle()
	{
		if (IsConfirming)
			CancelNavigation();
		else if (IsOpen)
			Close();
		else
		{
			IsOpen = true;
			Experimental = false;
		}
	}

	public void Close()
	{
		IsOpen = false;
		Experimental = false;
		pending = null;
	}

	public void RequestNavigation(Action action)
	{
		if (action is null)
			return;
		if (HasUnsavedEdits?.Invoke() == true)
			pending = action;
		else
		{
			Close();
			action();
		}
	}

	public void CancelNavigation()
	{
		pending = null;
		Close();
	}

	public void ConfirmNavigation()
	{
		var action = pending;
		Close();
		action?.Invoke();
	}

	protected override int BuildHash() => HashCode.Combine(IsOpen, Experimental, IsConfirming, EnableExperiments);
}