Code/SboxSuperuser/UI/AdminMenuRenderer.Run.cs

Part of the admin menu ImGui renderer. Shows a modal confirmation popup for admin commands, with optional required reason text appended as the final argument, then dispatches the command through the host's Server.Run.

using Sandbox;
using SboxSuperuser.Engine.Vendor.ImGui;

namespace SboxSuperuser.UI;

internal sealed partial class AdminMenuRenderer
{
	private const string RunPopup = "Confirm command";

	/// <summary>A command waiting for the user to confirm it, and optionally to give a reason that becomes its last argument.</summary>
	private sealed class PendingRun
	{
		internal string Title = "";
		internal string Command = "";
		internal string[] Arguments = Array.Empty<string>();
		internal (string Key, string Value)[] Details = Array.Empty<(string, string)>();
		internal bool NeedsReason;
		internal bool Danger;
		internal string Reason = "";
	}

	private PendingRun _pendingRun;

	/// <summary>Runs a command through the host; changes and anything needing a reason are confirmed first.</summary>
	private void RunCommand( string title, string command, string[] arguments, bool confirm,
		bool needsReason = false, bool danger = false, params (string Key, string Value)[] details )
	{
		if ( !confirm && !needsReason )
		{
			Server.Run( command, arguments );
			return;
		}
		_pendingRun = new PendingRun
		{
			Title = title, Command = command, Arguments = arguments, Details = details,
			NeedsReason = needsReason, Danger = danger
		};
	}

	private void DrawRunConfirmation()
	{
		if ( _pendingRun is not null && !ImGui.IsPopupOpen( RunPopup ) ) ImGui.OpenPopup( RunPopup );

		ImGui.SetNextWindowPos( new Vector2( Screen.Width, Screen.Height ) / 2, ImGuiCond.Appearing, new Vector2( 0.5f, 0.5f ) );
		if ( !ImGui.BeginPopupModal( RunPopup, ImGuiWindowFlags.AlwaysAutoResize | ImGuiWindowFlags.NoSavedSettings ) ) return;
		var run = _pendingRun;
		if ( run is null )
		{
			ImGui.CloseCurrentPopup();
			ImGui.EndPopup();
			return;
		}

		ImGui.TextColored( run.Danger ? AdminMenuTheme.Danger : AdminMenuTheme.Warning, "{0}", run.Title );
		ImGui.Separator();
		ImGui.PushTextWrapPos( 420 * Scale );
		foreach ( var (key, value) in run.Details ) ConfirmRow( key, value );
		ImGui.PopTextWrapPos();
		if ( run.NeedsReason )
		{
			if ( ImGui.IsWindowAppearing() ) ImGui.SetKeyboardFocusHere();
			Field( "##run-reason", "Reason (required, recorded in history)", ref run.Reason, 256, 420 * Scale );
		}
		ImGui.Separator();

		var width = 120 * Scale;
		var ready = !run.NeedsReason || run.Reason.Trim().Length > 0;
		ImGui.BeginDisabled( !ready || Server.Running );
		if ( ImGui.Button( "Confirm", new Vector2( width, 0 ) ) )
		{
			var arguments = run.NeedsReason ? run.Arguments.Append( run.Reason.Trim() ).ToArray() : run.Arguments;
			if ( run.NeedsReason ) _preferences.Remember( run.Reason, "" );
			Server.Run( run.Command, arguments );
			_pendingRun = null;
			ImGui.CloseCurrentPopup();
		}
		ImGui.EndDisabled();
		ImGui.SameLine();
		if ( ImGui.Button( "Cancel", new Vector2( width, 0 ) ) )
		{
			_pendingRun = null;
			ImGui.CloseCurrentPopup();
		}
		ImGui.EndPopup();
	}

	private void ConfirmRow( string key, string value )
	{
		ImGui.TextDisabled( "{0}", key );
		ImGui.SameLine( 80 * Scale );
		ImGui.TextWrapped( "{0}", value );
	}
}