UI/SpawnMenuHost.razor
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent
@namespace Sandbox
<root @onmousedown=@OnMenuClicked>
<div class="menu-wrapper">
<div class="menu-backdrop"></div>
<SpawnMenuModeBar></SpawnMenuModeBar>
<div class="menu-slide">
<PanelSwitcher @ref="PanelSwitcher"></PanelSwitcher>
</div>
</div>
</root>
@code
{
bool stickOpen = false;
bool lastState;
string _lastMode = "SpawnMenu";
PanelSwitcher PanelSwitcher = default;
protected override void OnUpdate()
{
UpdateSpawnMenuState();
ScanForNewModes();
var activeType = GetActiveMode()?.GetType().Name;
SetClass( "no-backdrop", activeType is "ContextMenuHost" or "SaveMenu" or "EffectsHost" );
base.OnUpdate();
}
void UpdateSpawnMenuState()
{
var openSpawnMenu = Input.Down("spawnmenu");
var openInspectMenu = Input.Down("inspectmenu");
var openAnyMenu = openSpawnMenu || openInspectMenu;
stickOpen = stickOpen || (Sandbox.UI.InputFocus.Current?.Ancestors.Contains(Panel) ?? false);
if (stickOpen && Input.Pressed("spawnmenu")) stickOpen = false;
bool state = stickOpen || openAnyMenu;
SetClass("open", state);
if (lastState != state )
{
// closed
if (!state)
{
Popup.CloseAll();
}
// opened
if (state)
{
if (Input.Pressed("inspectmenu"))
{
Hints.Current.Cancel("openinspectmenu");
SwitchMode("ContextMenuHost", false);
PanelSwitcher.SkipTransitions();
}
if (Input.Pressed("spawnmenu"))
{
Hints.Current.Cancel("openspawnmenu");
SwitchMode(_lastMode, false);
PanelSwitcher.SkipTransitions();
Sandbox.Services.Stats.Increment( "menu.spawnmenu.open", 1 );
}
}
}
else if ( state )
{
// menu stays open — switch modes if the other key is pressed
if (Input.Pressed("inspectmenu"))
{
SwitchMode("ContextMenuHost", false);
}
else if (Input.Pressed("spawnmenu"))
{
SwitchMode(_lastMode, false);
}
}
lastState = state;
}
// When clicking anywhere in the spawn menu, blur any focused text inputs
// so we can close the menu by pressing the spawnmenu key.
void OnMenuClicked()
{
Sandbox.UI.InputFocus.Clear();
}
void ScanForNewModes()
{
if ( PanelSwitcher == null )
return;
var modes = Game.TypeLibrary.GetTypesWithAttribute<SpawnMenuMode>().OrderBy( x => x.Type.Name != "SpawnMenu" ).ToList();
// Remove panels whose condition is no longer met
foreach ( var child in PanelSwitcher.Children.ToList() )
{
var mode = modes.FirstOrDefault( m => m.Type.TargetType == child.GetType() );
if ( mode.Type != null && !mode.Attribute.CheckCondition() )
{
if ( PanelSwitcher.ActivePanel == child )
SwitchMode( "SpawnMenu" );
child.Delete();
}
}
// Create panels for modes whose condition is met
foreach ( var type in modes )
{
if ( !type.Attribute.CheckCondition() ) continue;
if ( PanelSwitcher.Children.Any( x => x.GetType() == type.Type.TargetType ) ) continue;
var panel = type.Type.Create<Panel>();
if ( panel == null ) continue;
PanelSwitcher.AddChild( panel );
}
}
public static void SwitchMode( string modeName, bool remember = true )
{
var host = Game.ActiveScene.Get<SpawnMenuHost>();
if (host?.PanelSwitcher == null) return;
if (remember) host._lastMode = modeName;
foreach ( var child in host.PanelSwitcher.Children )
{
if ( child.GetType().Name == modeName )
{
host.PanelSwitcher.SwitchToPanel(child);
return;
}
}
}
public static Panel GetActiveMode()
{
var host = Game.ActiveScene.Get<SpawnMenuHost>();
if (host?.PanelSwitcher == null) return null;
return host.PanelSwitcher.ActivePanel;
}
public interface ISpawnMenuCondition
{
static abstract bool IsVisible();
}
public class SpawnMenuMode : System.Attribute
{
public virtual bool CheckCondition() => true;
}
public class SpawnMenuMode<T> : SpawnMenuMode where T : ISpawnMenuCondition
{
public override bool CheckCondition() => T.IsVisible();
}
public class HostOnly : ISpawnMenuCondition
{
public static bool IsVisible() => Networking.IsHost;
}
}