271 lines
9.2 KiB
C#
271 lines
9.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using NitroxClient.Unity.Helper;
|
|
using NitroxModel.DataStructures.Util;
|
|
using NitroxModel.Helper;
|
|
using UnityEngine;
|
|
|
|
namespace NitroxClient.Debuggers
|
|
{
|
|
[ExcludeFromCodeCoverage]
|
|
public abstract class BaseDebugger
|
|
{
|
|
public readonly string DebuggerName;
|
|
public readonly KeyCode Hotkey;
|
|
public readonly bool HotkeyAltRequired;
|
|
public readonly bool HotkeyControlRequired;
|
|
public readonly bool HotkeyShiftRequired;
|
|
public readonly string HotkeyString;
|
|
public readonly GUISkinCreationOptions SkinCreationOptions;
|
|
|
|
/// <summary>
|
|
/// Currently active tab. This is the index used with <see cref="tabs"/>.
|
|
/// </summary>
|
|
public DebuggerTab ActiveTab;
|
|
|
|
public bool CanDragWindow = true;
|
|
|
|
public virtual bool Enabled { get; set; }
|
|
|
|
public Rect WindowRect;
|
|
|
|
/// <summary>
|
|
/// Optional rendered tabs of the current debugger.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <see cref="ActiveTab"/> gives the index of the currently selected tab.
|
|
/// </remarks>
|
|
private readonly Dictionary<string, DebuggerTab> tabs = new Dictionary<string, DebuggerTab>();
|
|
|
|
private float maxHeight;
|
|
|
|
protected BaseDebugger(float desiredWidth, string debuggerName = null, KeyCode hotkey = KeyCode.None, bool control = false, bool shift = false, bool alt = false, GUISkinCreationOptions skinOptions = GUISkinCreationOptions.DEFAULT, float maxHeight = 1000f)
|
|
{
|
|
this.maxHeight = maxHeight;
|
|
|
|
if (desiredWidth < 200)
|
|
{
|
|
desiredWidth = 200;
|
|
}
|
|
|
|
WindowRect = new Rect(Screen.width / 2 - (desiredWidth / 2), Screen.height * 0.1f, desiredWidth, Math.Min(Screen.height * 0.8f, maxHeight)); // Default position in center of screen.
|
|
|
|
Hotkey = hotkey;
|
|
HotkeyAltRequired = alt;
|
|
HotkeyShiftRequired = shift;
|
|
HotkeyControlRequired = control;
|
|
HotkeyString = Hotkey == KeyCode.None ? "None" : $"{(HotkeyControlRequired ? "CTRL+" : "")}{(HotkeyAltRequired ? "ALT+" : "")}{(HotkeyShiftRequired ? "SHIFT+" : "")}{Hotkey}";
|
|
SkinCreationOptions = skinOptions;
|
|
|
|
if (string.IsNullOrEmpty(debuggerName))
|
|
{
|
|
string name = GetType().Name;
|
|
DebuggerName = name.Substring(0, name.IndexOf("Debugger", StringComparison.Ordinal));
|
|
}
|
|
else
|
|
{
|
|
DebuggerName = debuggerName;
|
|
}
|
|
}
|
|
|
|
public enum GUISkinCreationOptions
|
|
{
|
|
/// <summary>
|
|
/// Uses the NitroxDebug skin.
|
|
/// </summary>
|
|
DEFAULT,
|
|
|
|
/// <summary>
|
|
/// Creates a copy of the default Unity IMGUI skin and sets the copied skin as render skin.
|
|
/// </summary>
|
|
UNITYCOPY,
|
|
|
|
/// <summary>
|
|
/// Creates a copy based on the NitroxDebug skin and sets the copied skin as render skin.
|
|
/// </summary>
|
|
DERIVEDCOPY
|
|
}
|
|
|
|
protected DebuggerTab AddTab(string name, Action render)
|
|
{
|
|
DebuggerTab tab = new(name, render);
|
|
tabs.Add(tab.Name, tab);
|
|
return tab;
|
|
}
|
|
|
|
protected Optional<DebuggerTab> GetTab(string name)
|
|
{
|
|
Validate.NotNull(name);
|
|
|
|
tabs.TryGetValue(name, out DebuggerTab tab);
|
|
return Optional.OfNullable(tab);
|
|
}
|
|
|
|
public virtual void Update()
|
|
{
|
|
// Defaults to a no-op but can be overriden
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this inside a <see cref="MonoBehaviour.OnGUI"/> method.
|
|
/// </summary>
|
|
public virtual void OnGUI()
|
|
{
|
|
if (!Enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GUISkin skin = GetSkin();
|
|
GUISkinUtils.RenderWithSkin(skin, () =>
|
|
{
|
|
WindowRect = GUILayout.Window(GUIUtility.GetControlID(FocusType.Keyboard), WindowRect, RenderInternal, $"[DEBUGGER] {DebuggerName}", GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true));
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Optionally adjust the skin that is used during render.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Set <see cref="SkinCreationOptions"/> on <see cref="GUISkinCreationOptions.UNITYCOPY"/> or <see cref="GUISkinCreationOptions.DERIVEDCOPY"/> in constructor before using this method.
|
|
/// </remarks>
|
|
/// <param name="skin">Skin that is being used during <see cref="Render(int)"/>.</param>
|
|
protected virtual void OnSetSkin(GUISkin skin)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Optionally use a custom render solution for the debugger by overriding this method.
|
|
/// </summary>
|
|
protected virtual void Render()
|
|
{
|
|
ActiveTab?.Render();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets (a copy of) a skin specified by <see cref="GUISkinCreationOptions"/>.
|
|
/// </summary>
|
|
/// <returns>A reference to an existing or copied skin.</returns>
|
|
private GUISkin GetSkin()
|
|
{
|
|
GUISkin skin = GUI.skin;
|
|
string skinName = GetSkinName();
|
|
switch (SkinCreationOptions)
|
|
{
|
|
case GUISkinCreationOptions.DEFAULT:
|
|
skin = GUISkinUtils.RegisterDerivedOnce("debuggers.default", s =>
|
|
{
|
|
SetBaseStyle(s);
|
|
OnSetSkinImpl(s);
|
|
});
|
|
break;
|
|
|
|
case GUISkinCreationOptions.UNITYCOPY:
|
|
skin = GUISkinUtils.RegisterDerivedOnce(skinName, OnSetSkinImpl);
|
|
break;
|
|
|
|
case GUISkinCreationOptions.DERIVEDCOPY:
|
|
GUISkin baseSkin = GUISkinUtils.RegisterDerivedOnce("debuggers.default", SetBaseStyle);
|
|
skin = GUISkinUtils.RegisterDerivedOnce(skinName, OnSetSkinImpl, baseSkin);
|
|
break;
|
|
}
|
|
|
|
return skin;
|
|
}
|
|
|
|
private string GetSkinName()
|
|
{
|
|
string name = GetType().Name;
|
|
return $"debuggers.{name.Substring(0, name.IndexOf("Debugger")).ToLowerInvariant()}";
|
|
}
|
|
|
|
private void OnSetSkinImpl(GUISkin skin)
|
|
{
|
|
if (SkinCreationOptions == GUISkinCreationOptions.DEFAULT)
|
|
{
|
|
Enabled = false;
|
|
throw new NotSupportedException($"Cannot change {nameof(GUISkin)} for {GetType().FullName} when accessing the default skin. Change {nameof(SkinCreationOptions)} to something else than {nameof(GUISkinCreationOptions.DEFAULT)}.");
|
|
}
|
|
|
|
OnSetSkin(skin);
|
|
}
|
|
|
|
private void RenderInternal(int windowId)
|
|
{
|
|
using (new GUILayout.HorizontalScope("Box"))
|
|
{
|
|
if (tabs.Count == 1)
|
|
{
|
|
GUILayout.Label(tabs.First().Key, "tabActive");
|
|
}
|
|
else
|
|
{
|
|
foreach (DebuggerTab tab in tabs.Values)
|
|
{
|
|
if (GUILayout.Button(tab.Name, ActiveTab == tab ? "tabActive" : "tab"))
|
|
{
|
|
ActiveTab = tab;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Render();
|
|
if (CanDragWindow)
|
|
{
|
|
GUI.DragWindow();
|
|
}
|
|
}
|
|
|
|
private void SetBaseStyle(GUISkin skin)
|
|
{
|
|
skin.label.alignment = TextAnchor.MiddleLeft;
|
|
skin.label.margin = new RectOffset();
|
|
skin.label.padding = new RectOffset();
|
|
|
|
skin.SetCustomStyle("header", skin.label, s =>
|
|
{
|
|
s.margin.top = 10;
|
|
s.margin.bottom = 10;
|
|
s.alignment = TextAnchor.MiddleCenter;
|
|
s.fontSize = 16;
|
|
s.fontStyle = FontStyle.Bold;
|
|
});
|
|
|
|
skin.SetCustomStyle("tab", skin.button, s =>
|
|
{
|
|
s.fontSize = 16;
|
|
s.margin = new RectOffset(5, 5, 5, 5);
|
|
});
|
|
|
|
skin.SetCustomStyle("tabActive", skin.button, s =>
|
|
{
|
|
s.fontStyle = FontStyle.Bold;
|
|
s.fontSize = 16;
|
|
});
|
|
}
|
|
|
|
public virtual void ResetWindowPosition()
|
|
{
|
|
WindowRect = new Rect(Screen.width / 2f - (WindowRect.width / 2), Screen.height / 2f - (WindowRect.height / 2), WindowRect.width, Math.Min(Screen.height * 0.8f, maxHeight)); //Reset position of debuggers because SN sometimes throws the windows from planet 4546B
|
|
}
|
|
|
|
public class DebuggerTab
|
|
{
|
|
public DebuggerTab(string name, Action render)
|
|
{
|
|
Validate.NotNull(name, $"Expected a name for the {nameof(DebuggerTab)}");
|
|
Validate.NotNull(render, $"Expected an action for the {nameof(DebuggerTab)}");
|
|
|
|
Name = name;
|
|
Render = render;
|
|
}
|
|
|
|
public string Name { get; }
|
|
public Action Render { get; }
|
|
}
|
|
}
|
|
}
|