Add IMGUI styling and initial OOP implementation
This commit is contained in:
parent
be7d8ba33a
commit
1c014e36ac
10 changed files with 803 additions and 2 deletions
92
GamecraftModdingAPI/Interface/IMGUI/Button.cs
Normal file
92
GamecraftModdingAPI/Interface/IMGUI/Button.cs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
using System;
|
||||||
|
using Unity.Mathematics;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace GamecraftModdingAPI.Interface.IMGUI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A clickable button.
|
||||||
|
/// This wraps Unity's IMGUI Button.
|
||||||
|
/// </summary>
|
||||||
|
public class Button : UIElement
|
||||||
|
{
|
||||||
|
private bool automaticLayout = false;
|
||||||
|
|
||||||
|
private string text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rectangular area that the button can use.
|
||||||
|
/// </summary>
|
||||||
|
public Rect Box { get; set; } = Rect.zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An event that fires when the button is clicked.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<bool> OnClick;
|
||||||
|
|
||||||
|
public void OnGUI()
|
||||||
|
{
|
||||||
|
if (automaticLayout)
|
||||||
|
{
|
||||||
|
if (GUILayout.Button(text, Constants.Default.button))
|
||||||
|
{
|
||||||
|
OnClick?.Invoke(this, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (GUI.Button(Box, text, Constants.Default.button))
|
||||||
|
{
|
||||||
|
OnClick?.Invoke(this, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The button's unique name.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to display the button.
|
||||||
|
/// </summary>
|
||||||
|
public bool Enabled { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize a new button with automatic layout.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The text to display on the button.</param>
|
||||||
|
/// <param name="name">The button's name.</param>
|
||||||
|
public Button(string text, string name = null)
|
||||||
|
{
|
||||||
|
automaticLayout = true;
|
||||||
|
this.text = text;
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
this.Name = typeof(Button).FullName + "::" + text;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.Name = name;
|
||||||
|
}
|
||||||
|
IMGUIManager.AddElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize a new button.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The text to display on the button.</param>
|
||||||
|
/// <param name="box">Rectangular area for the button to use.</param>
|
||||||
|
/// <param name="name">The button's name.</param>
|
||||||
|
public Button(string text, Rect box, string name = null) : this(text, name)
|
||||||
|
{
|
||||||
|
automaticLayout = false;
|
||||||
|
this.Box = box;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Button()
|
||||||
|
{
|
||||||
|
IMGUIManager.RemoveElement(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
168
GamecraftModdingAPI/Interface/IMGUI/Constants.cs
Normal file
168
GamecraftModdingAPI/Interface/IMGUI/Constants.cs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Reflection;
|
||||||
|
using GamecraftModdingAPI.Utility;
|
||||||
|
using HarmonyLib;
|
||||||
|
using Svelto.Tasks;
|
||||||
|
using Svelto.Tasks.Lean;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.AddressableAssets;
|
||||||
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||||
|
|
||||||
|
namespace GamecraftModdingAPI.Interface.IMGUI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Convenient IMGUI values.
|
||||||
|
/// </summary>
|
||||||
|
public static class Constants
|
||||||
|
{
|
||||||
|
private static byte _defaultCompletion = 0;
|
||||||
|
private static GUISkin _default = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Best-effort imitation of Gamecraft's UI style.
|
||||||
|
/// </summary>
|
||||||
|
public static GUISkin Default
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_defaultCompletion != 0) _default = BuildDefaultGUISkin();
|
||||||
|
return _default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Font _riffic = null;
|
||||||
|
|
||||||
|
private static Texture2D _blueBackground = null;
|
||||||
|
private static Texture2D _grayBackground = null;
|
||||||
|
private static Texture2D _whiteBackground = null;
|
||||||
|
private static Texture2D _textInputBackground = null;
|
||||||
|
private static Texture2D _areaBackground = null;
|
||||||
|
|
||||||
|
internal static void Init()
|
||||||
|
{
|
||||||
|
LoadGUIAssets();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GUISkin BuildDefaultGUISkin()
|
||||||
|
{
|
||||||
|
_defaultCompletion = 0;
|
||||||
|
if (_riffic == null) return GUI.skin;
|
||||||
|
// build GUISkin
|
||||||
|
GUISkin gui = ScriptableObject.CreateInstance<GUISkin>();
|
||||||
|
gui.font = _riffic;
|
||||||
|
gui.settings.selectionColor = Color.white;
|
||||||
|
gui.settings.tripleClickSelectsLine = true;
|
||||||
|
// set properties off all UI elements
|
||||||
|
foreach (PropertyInfo p in typeof(GUISkin).GetProperties())
|
||||||
|
{
|
||||||
|
// for a "scriptable" GUI system, it's ironic there's no better way to do this
|
||||||
|
if (p.GetValue(gui) is GUIStyle style)
|
||||||
|
{
|
||||||
|
style.richText = true;
|
||||||
|
style.alignment = TextAnchor.MiddleCenter;
|
||||||
|
style.fontSize = 30;
|
||||||
|
style.wordWrap = true;
|
||||||
|
style.border = new RectOffset(4, 4, 4, 4);
|
||||||
|
style.margin = new RectOffset(4, 4, 4, 4);
|
||||||
|
style.padding = new RectOffset(4, 4, 4, 4);
|
||||||
|
// normal state
|
||||||
|
style.normal.background = _blueBackground;
|
||||||
|
style.normal.textColor = Color.white;
|
||||||
|
// hover state
|
||||||
|
style.hover.background = _grayBackground;
|
||||||
|
style.hover.textColor = Color.white;
|
||||||
|
// focused
|
||||||
|
style.focused.background = _grayBackground;
|
||||||
|
style.focused.textColor = Color.white;
|
||||||
|
// clicking state
|
||||||
|
style.active.background = _whiteBackground;
|
||||||
|
style.active.textColor = Color.white;
|
||||||
|
|
||||||
|
p.SetValue(gui, style); // probably unnecessary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// set element-specific styles
|
||||||
|
// label
|
||||||
|
gui.label.normal.background = null;
|
||||||
|
gui.label.hover.background = null;
|
||||||
|
gui.label.focused.background = null;
|
||||||
|
gui.label.active.background = null;
|
||||||
|
// text input
|
||||||
|
gui.textField.normal.background = _textInputBackground;
|
||||||
|
gui.textField.hover.background = _textInputBackground;
|
||||||
|
gui.textField.focused.background = _textInputBackground;
|
||||||
|
gui.textField.active.background = _textInputBackground;
|
||||||
|
// text area
|
||||||
|
gui.textArea.normal.background = _textInputBackground;
|
||||||
|
gui.textArea.hover.background = _textInputBackground;
|
||||||
|
gui.textArea.focused.background = _textInputBackground;
|
||||||
|
gui.textArea.active.background = _textInputBackground;
|
||||||
|
// window
|
||||||
|
gui.window.normal.background = _areaBackground;
|
||||||
|
gui.window.hover.background = _areaBackground;
|
||||||
|
gui.window.focused.background = _areaBackground;
|
||||||
|
gui.window.active.background = _areaBackground;
|
||||||
|
// box (also used by layout groups & areas)
|
||||||
|
gui.box.normal.background = _areaBackground;
|
||||||
|
gui.box.hover.background = _areaBackground;
|
||||||
|
gui.box.focused.background = _areaBackground;
|
||||||
|
gui.box.active.background = _areaBackground;
|
||||||
|
return gui;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadGUIAssets()
|
||||||
|
{
|
||||||
|
AsyncOperationHandle<Font> rifficHandle = Addressables.LoadAssetAsync<Font>("Assets/Art/Fonts/riffic-bold.ttf");
|
||||||
|
rifficHandle.Completed += handle =>
|
||||||
|
{
|
||||||
|
_riffic = handle.Result;
|
||||||
|
_defaultCompletion++;
|
||||||
|
};
|
||||||
|
_blueBackground = new Texture2D(1, 1);
|
||||||
|
_blueBackground.SetPixel(0, 0, new Color(0.004f, 0.522f, 0.847f) /* Gamecraft Blue */);
|
||||||
|
_blueBackground.Apply();
|
||||||
|
_grayBackground = new Texture2D(1, 1);
|
||||||
|
_grayBackground.SetPixel(0, 0, new Color(0.745f, 0.745f, 0.745f) /* Gray */);
|
||||||
|
_grayBackground.Apply();
|
||||||
|
_whiteBackground = new Texture2D(1, 1);
|
||||||
|
_whiteBackground.SetPixel(0, 0, new Color(0.898f, 0.898f, 0.898f) /* Very light gray */);
|
||||||
|
_whiteBackground.Apply();
|
||||||
|
_textInputBackground = new Texture2D(1, 1);
|
||||||
|
_textInputBackground.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.25f) /* Translucent gray */);
|
||||||
|
_textInputBackground.Apply();
|
||||||
|
_areaBackground = new Texture2D(1, 1);
|
||||||
|
_areaBackground.SetPixel(0, 0, new Color(0f, 0f, 0f, 0.25f) /* Translucent gray */);
|
||||||
|
_areaBackground.Apply();
|
||||||
|
/* // this is actually gray (used for the loading screen)
|
||||||
|
AsyncOperationHandle<Texture2D> backgroundHandle =
|
||||||
|
Addressables.LoadAssetAsync<Texture2D>("Assets/Art/Textures/UI/FrontEndMap/RCX_Blue_Background_5k.jpg");
|
||||||
|
backgroundHandle.Completed += handle =>
|
||||||
|
{
|
||||||
|
_blueBackground = handle.Result;
|
||||||
|
_defaultCompletion++;
|
||||||
|
};*/
|
||||||
|
_defaultCompletion++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(FMODUnity.RuntimeManager), "PlayOneShot", new []{ typeof(Guid), typeof(Vector3)})]
|
||||||
|
public class FMODRuntimeManagerPlayOneShotPatch
|
||||||
|
{
|
||||||
|
public static void Prefix(Guid guid)
|
||||||
|
{
|
||||||
|
Logging.MetaLog($"Playing sound with guid '{guid.ToString()}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(FMODUnity.RuntimeManager), "PlayOneShot", new []{ typeof(string), typeof(Vector3)})]
|
||||||
|
public class FMODRuntimeManagerPlayOneShotPatch2
|
||||||
|
{
|
||||||
|
public static void Prefix(string path)
|
||||||
|
{
|
||||||
|
Logging.MetaLog($"Playing sound with str '{path}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
157
GamecraftModdingAPI/Interface/IMGUI/Group.cs
Normal file
157
GamecraftModdingAPI/Interface/IMGUI/Group.cs
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
using System;
|
||||||
|
using GamecraftModdingAPI.Utility;
|
||||||
|
using Svelto.DataStructures;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace GamecraftModdingAPI.Interface.IMGUI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A group of elements.
|
||||||
|
/// This wraps Unity's GUILayout Area and GUI Group system.
|
||||||
|
/// </summary>
|
||||||
|
public class Group : UIElement
|
||||||
|
{
|
||||||
|
private bool automaticLayout;
|
||||||
|
|
||||||
|
private FasterList<UIElement> elements = new FasterList<UIElement>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rectangular area in the window that the UI group can use
|
||||||
|
/// </summary>
|
||||||
|
public Rect Box { get; set; }
|
||||||
|
|
||||||
|
public void OnGUI()
|
||||||
|
{
|
||||||
|
/*if (Constants.Default == null) return;
|
||||||
|
if (Constants.Default.box == null) return;*/
|
||||||
|
GUIStyle guiStyle = Constants.Default.box;
|
||||||
|
UIElement[] elems = elements.ToArrayFast(out uint count);
|
||||||
|
if (automaticLayout)
|
||||||
|
{
|
||||||
|
GUILayout.BeginArea(Box, guiStyle);
|
||||||
|
for (uint i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
/*try
|
||||||
|
{
|
||||||
|
if (elems[i].Enabled)
|
||||||
|
elems[i].OnGUI();
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
// ignore these, since this is (hopefully) just Unity being dumb
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.MetaDebugLog($"Element '{elems[i].Name}' threw exception:\n{e.ToString()}");
|
||||||
|
}*/
|
||||||
|
if (elems[i].Enabled)
|
||||||
|
elems[i].OnGUI();
|
||||||
|
}
|
||||||
|
GUILayout.EndArea();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUI.BeginGroup(Box, guiStyle);
|
||||||
|
for (uint i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (elems[i].Enabled)
|
||||||
|
elems[i].OnGUI();
|
||||||
|
}
|
||||||
|
GUI.EndGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The group's unique name.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to display the group and everything in it.
|
||||||
|
/// </summary>
|
||||||
|
public bool Enabled { set; get; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of elements in the group.
|
||||||
|
/// </summary>
|
||||||
|
public int Length
|
||||||
|
{
|
||||||
|
get => elements.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="T:GamecraftModdingAPI.Interface.IMGUI.Group"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="box">The rectangular area to use in the window.</param>
|
||||||
|
/// <param name="name">Name of the group.</param>
|
||||||
|
/// <param name="automaticLayout">Whether to use automatic UI layout.</param>
|
||||||
|
public Group(Rect box, string name = null, bool automaticLayout = false)
|
||||||
|
{
|
||||||
|
Box = box;
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
this.Name = typeof(Group).FullName + "::" + box.ToString().Replace(" ", "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.Name = name;
|
||||||
|
}
|
||||||
|
this.automaticLayout = automaticLayout;
|
||||||
|
IMGUIManager.AddElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an element to the group.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="element">The element to add.</param>
|
||||||
|
/// <returns>Index of the new element.</returns>
|
||||||
|
public int AddElement(UIElement element)
|
||||||
|
{
|
||||||
|
IMGUIManager.RemoveElement(element); // groups manage internal elements themselves
|
||||||
|
elements.Add(element);
|
||||||
|
return elements.count - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove an element from the group.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="element">The element to remove.</param>
|
||||||
|
/// <returns>Whether removal was successful.</returns>
|
||||||
|
public bool RemoveElement(UIElement element)
|
||||||
|
{
|
||||||
|
int index = IndexOf(element);
|
||||||
|
return RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove the element in a specific location.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index of the element.</param>
|
||||||
|
/// <returns>Whether removal was successful.</returns>
|
||||||
|
public bool RemoveAt(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= elements.count) return false;
|
||||||
|
IMGUIManager.AddElement(elements[index]); // re-add to global manager
|
||||||
|
elements.RemoveAt(index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the index of an element.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="element">The element to search for.</param>
|
||||||
|
/// <returns>The element's index, or -1 if not found.</returns>
|
||||||
|
public int IndexOf(UIElement element)
|
||||||
|
{
|
||||||
|
UIElement[] elems = elements.ToArrayFast(out uint count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (elems[i].Name == element.Name)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
94
GamecraftModdingAPI/Interface/IMGUI/IMGUIManager.cs
Normal file
94
GamecraftModdingAPI/Interface/IMGUI/IMGUIManager.cs
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using GamecraftModdingAPI.App;
|
||||||
|
using GamecraftModdingAPI.Utility;
|
||||||
|
using Rewired.Internal;
|
||||||
|
using Svelto.DataStructures;
|
||||||
|
using Svelto.Tasks;
|
||||||
|
using Svelto.Tasks.ExtraLean;
|
||||||
|
using Svelto.Tasks.ExtraLean.Unity;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace GamecraftModdingAPI.Interface.IMGUI
|
||||||
|
{
|
||||||
|
public static class IMGUIManager
|
||||||
|
{
|
||||||
|
internal static OnGuiRunner ImguiScheduler = new OnGuiRunner("GamecraftModdingAPI_IMGUIScheduler");
|
||||||
|
|
||||||
|
private static FasterDictionary<string, UIElement> _activeElements = new FasterDictionary<string,UIElement>();
|
||||||
|
|
||||||
|
public static void AddElement(UIElement e)
|
||||||
|
{
|
||||||
|
if (!ExistsElement(e))
|
||||||
|
{
|
||||||
|
_activeElements[e.Name] = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ExistsElement(string name)
|
||||||
|
{
|
||||||
|
return _activeElements.ContainsKey(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ExistsElement(UIElement element)
|
||||||
|
{
|
||||||
|
return ExistsElement(element.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool RemoveElement(string name)
|
||||||
|
{
|
||||||
|
if (ExistsElement(name))
|
||||||
|
{
|
||||||
|
return _activeElements.Remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool RemoveElement(UIElement element)
|
||||||
|
{
|
||||||
|
return RemoveElement(element.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnGUI()
|
||||||
|
{
|
||||||
|
UIElement[] elements = _activeElements.GetValuesArray(out uint count);
|
||||||
|
for(uint i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (elements[i].Enabled)
|
||||||
|
elements[i].OnGUI();
|
||||||
|
/*try
|
||||||
|
{
|
||||||
|
if (elements[i].Enabled)
|
||||||
|
elements[i].OnGUI();
|
||||||
|
}
|
||||||
|
catch (ArgumentException)
|
||||||
|
{
|
||||||
|
// ignore these, since this is (hopefully) just Unity being dumb
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logging.MetaDebugLog($"Element '{elements[i].Name}' threw exception:\n{e.ToString()}");
|
||||||
|
}*/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerator<TaskContract> OnGUIAsync()
|
||||||
|
{
|
||||||
|
yield return (new Svelto.Tasks.Enumerators.WaitForSecondsEnumerator(5)).Continue(); // wait for some startup
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
yield return Yield.It;
|
||||||
|
GUI.skin = Constants.Default;
|
||||||
|
OnGUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Init()
|
||||||
|
{
|
||||||
|
OnGUIAsync().RunOn(ImguiScheduler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
GamecraftModdingAPI/Interface/IMGUI/Image.cs
Normal file
58
GamecraftModdingAPI/Interface/IMGUI/Image.cs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace GamecraftModdingAPI.Interface.IMGUI
|
||||||
|
{
|
||||||
|
public class Image : UIElement
|
||||||
|
{
|
||||||
|
private bool automaticLayout = false;
|
||||||
|
|
||||||
|
public Texture Texture { get; set; }
|
||||||
|
|
||||||
|
public Rect Box { get; set; } = Rect.zero;
|
||||||
|
|
||||||
|
public void OnGUI()
|
||||||
|
{
|
||||||
|
//if (Texture == null) return;
|
||||||
|
if (automaticLayout)
|
||||||
|
{
|
||||||
|
GUILayout.Label(Texture, Constants.Default.label);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUI.Label(Box, Texture, Constants.Default.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public bool Enabled { set; get; } = true;
|
||||||
|
|
||||||
|
public Image(Texture texture = null, string name = null)
|
||||||
|
{
|
||||||
|
automaticLayout = true;
|
||||||
|
Texture = texture;
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
this.Name = typeof(Image).FullName + "::" + texture;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.Name = typeof(Image).FullName + "::" + texture.name + "(" + texture.width + "x" + texture.height + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.Name = name;
|
||||||
|
}
|
||||||
|
IMGUIManager.AddElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Image(Rect box, Texture texture = null, string name = null) : this(texture, name)
|
||||||
|
{
|
||||||
|
this.Box = box;
|
||||||
|
automaticLayout = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
GamecraftModdingAPI/Interface/IMGUI/Label.cs
Normal file
59
GamecraftModdingAPI/Interface/IMGUI/Label.cs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace GamecraftModdingAPI.Interface.IMGUI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A simple text label.
|
||||||
|
/// This wraps Unity IMGUI's Label.
|
||||||
|
/// </summary>
|
||||||
|
public class Label : UIElement
|
||||||
|
{
|
||||||
|
private bool automaticLayout = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String to display on the label.
|
||||||
|
/// </summary>
|
||||||
|
public string Text { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rectangular area that the label can use.
|
||||||
|
/// </summary>
|
||||||
|
public Rect Box { get; set; } = Rect.zero;
|
||||||
|
|
||||||
|
public void OnGUI()
|
||||||
|
{
|
||||||
|
if (automaticLayout)
|
||||||
|
{
|
||||||
|
GUILayout.Label(Text, Constants.Default.label);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUI.Label(Box, Text, Constants.Default.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public bool Enabled { set; get; } = true;
|
||||||
|
|
||||||
|
public Label(string initialText = null, string name = null)
|
||||||
|
{
|
||||||
|
automaticLayout = true;
|
||||||
|
Text = initialText;
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
this.Name = typeof(Label).FullName + "::" + initialText;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.Name = name;
|
||||||
|
}
|
||||||
|
IMGUIManager.AddElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Label(Rect box, string initialText = null, string name = null) : this(initialText, name)
|
||||||
|
{
|
||||||
|
this.Box = box;
|
||||||
|
automaticLayout = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
109
GamecraftModdingAPI/Interface/IMGUI/Text.cs
Normal file
109
GamecraftModdingAPI/Interface/IMGUI/Text.cs
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace GamecraftModdingAPI.Interface.IMGUI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A text input field.
|
||||||
|
/// This wraps Unity's IMGUI TextField and TextArea.
|
||||||
|
/// </summary>
|
||||||
|
public class Text : UIElement
|
||||||
|
{
|
||||||
|
private bool automaticLayout;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the text input field is multiline (true -> TextArea) or not (false -> TextField).
|
||||||
|
/// </summary>
|
||||||
|
public bool Multiline { get; set; }
|
||||||
|
|
||||||
|
private string text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rectangular area that the text field can use.
|
||||||
|
/// </summary>
|
||||||
|
public Rect Box { get; set; } = Rect.zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An event that fires whenever the text input is edited.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<string> OnEdit;
|
||||||
|
|
||||||
|
public void OnGUI()
|
||||||
|
{
|
||||||
|
string editedText = null;
|
||||||
|
if (automaticLayout)
|
||||||
|
{
|
||||||
|
if (Multiline)
|
||||||
|
{
|
||||||
|
editedText = GUILayout.TextArea(text, Constants.Default.textArea);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
editedText = GUILayout.TextField(text, Constants.Default.textField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Multiline)
|
||||||
|
{
|
||||||
|
editedText = GUI.TextArea(Box, text, Constants.Default.textArea);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
editedText = GUI.TextField(Box, text, Constants.Default.textField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (editedText != null && editedText != text)
|
||||||
|
{
|
||||||
|
OnEdit?.Invoke(this, editedText);
|
||||||
|
text = editedText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The text field's unique name.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to display the text field.
|
||||||
|
/// </summary>
|
||||||
|
public bool Enabled { set; get; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the text input field with automatic layout.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="initialText">Initial text in the input field.</param>
|
||||||
|
/// <param name="name">The text field's name.</param>
|
||||||
|
/// <param name="multiline">Allow multiple lines?</param>
|
||||||
|
public Text(string initialText = null, string name = null, bool multiline = false)
|
||||||
|
{
|
||||||
|
this.Multiline = multiline;
|
||||||
|
automaticLayout = true;
|
||||||
|
text = initialText ?? "";
|
||||||
|
if (name == null)
|
||||||
|
{
|
||||||
|
this.Name = typeof(Text).FullName + "::" + text;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.Name = name;
|
||||||
|
}
|
||||||
|
IMGUIManager.AddElement(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the text input field.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="box">Rectangular area for the text field.</param>
|
||||||
|
/// <param name="initialText">Initial text in the input field.</param>
|
||||||
|
/// <param name="name">The text field's name.</param>
|
||||||
|
/// <param name="multiline">Allow multiple lines?</param>
|
||||||
|
public Text(Rect box, string initialText = null, string name = null, bool multiline = false) : this(initialText, name, multiline)
|
||||||
|
{
|
||||||
|
this.Box = box;
|
||||||
|
automaticLayout = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
GamecraftModdingAPI/Interface/IMGUI/UIElement.cs
Normal file
28
GamecraftModdingAPI/Interface/IMGUI/UIElement.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace GamecraftModdingAPI.Interface.IMGUI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// GUI Element like a text field, button or picture.
|
||||||
|
/// This interface is used to wrap many elements from Unity's IMGUI system.
|
||||||
|
/// </summary>
|
||||||
|
public interface UIElement
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// GUI operations to perform in the OnGUI scope.
|
||||||
|
/// This is basically equivalent to a MonoBehaviour's OnGUI method.
|
||||||
|
/// </summary>
|
||||||
|
void OnGUI();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The element's name.
|
||||||
|
/// This should be unique for every instance of the class.
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to display the UI element or not.
|
||||||
|
/// </summary>
|
||||||
|
bool Enabled { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -89,6 +89,9 @@ namespace GamecraftModdingAPI
|
||||||
AsyncUtils.Init();
|
AsyncUtils.Init();
|
||||||
GamecraftModdingAPI.App.Client.Init();
|
GamecraftModdingAPI.App.Client.Init();
|
||||||
GamecraftModdingAPI.App.Game.Init();
|
GamecraftModdingAPI.App.Game.Init();
|
||||||
|
// init UI
|
||||||
|
Interface.IMGUI.Constants.Init();
|
||||||
|
Interface.IMGUI.IMGUIManager.Init();
|
||||||
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
|
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using GamecraftModdingAPI.App;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using IllusionInjector;
|
using IllusionInjector;
|
||||||
// test
|
// test
|
||||||
|
@ -22,8 +22,16 @@ using GamecraftModdingAPI.Commands;
|
||||||
using GamecraftModdingAPI.Events;
|
using GamecraftModdingAPI.Events;
|
||||||
using GamecraftModdingAPI.Utility;
|
using GamecraftModdingAPI.Utility;
|
||||||
using GamecraftModdingAPI.Blocks;
|
using GamecraftModdingAPI.Blocks;
|
||||||
|
using GamecraftModdingAPI.Interface.IMGUI;
|
||||||
using GamecraftModdingAPI.Players;
|
using GamecraftModdingAPI.Players;
|
||||||
|
using UnityEngine.AddressableAssets;
|
||||||
|
using UnityEngine.AddressableAssets.ResourceLocators;
|
||||||
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||||
|
using UnityEngine.ResourceManagement.ResourceLocations;
|
||||||
|
using UnityEngine.ResourceManagement.ResourceProviders;
|
||||||
|
using Debug = FMOD.Debug;
|
||||||
using EventType = GamecraftModdingAPI.Events.EventType;
|
using EventType = GamecraftModdingAPI.Events.EventType;
|
||||||
|
using Label = GamecraftModdingAPI.Interface.IMGUI.Label;
|
||||||
|
|
||||||
namespace GamecraftModdingAPI.Tests
|
namespace GamecraftModdingAPI.Tests
|
||||||
{
|
{
|
||||||
|
@ -347,13 +355,38 @@ namespace GamecraftModdingAPI.Tests
|
||||||
{
|
{
|
||||||
Logging.Log("Compatible GamecraftScripting detected");
|
Logging.Log("Compatible GamecraftScripting detected");
|
||||||
}
|
}
|
||||||
|
// Interface test
|
||||||
|
/*Interface.IMGUI.Group uiGroup = new Group(new Rect(20, 20, 200, 500), "GamecraftModdingAPI_UITestGroup", true);
|
||||||
|
Interface.IMGUI.Button button = new Button("TEST");
|
||||||
|
button.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
|
||||||
|
Interface.IMGUI.Button button2 = new Button("TEST2");
|
||||||
|
button2.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
|
||||||
|
Text uiText = new Text("This is text!", multiline: true);
|
||||||
|
uiText.OnEdit += (t, txt) => { Logging.MetaDebugLog($"Text in {((Text)t).Name} is now '{txt}'"); };
|
||||||
|
Label uiLabel = new Label("Label!");
|
||||||
|
Image uiImg = new Image(name:"Behold this texture!");
|
||||||
|
uiImg.Enabled = false;
|
||||||
|
uiGroup.AddElement(button);
|
||||||
|
uiGroup.AddElement(button2);
|
||||||
|
uiGroup.AddElement(uiText);
|
||||||
|
uiGroup.AddElement(uiLabel);
|
||||||
|
uiGroup.AddElement(uiImg);
|
||||||
|
|
||||||
|
Addressables.LoadAssetAsync<Texture2D>("Assets/Art/Textures/UI/FrontEndMap/RCX_Blue_Background_5k.jpg")
|
||||||
|
.Completed +=
|
||||||
|
handle =>
|
||||||
|
{
|
||||||
|
uiImg.Texture = handle.Result;
|
||||||
|
uiImg.Enabled = true;
|
||||||
|
Logging.MetaDebugLog($"Got blue bg asset {handle.Result}");
|
||||||
|
};
|
||||||
|
*/
|
||||||
#if TEST
|
#if TEST
|
||||||
TestRoot.RunTests();
|
TestRoot.RunTests();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private string modsString;
|
private string modsString;
|
||||||
private string InstalledMods()
|
private string InstalledMods()
|
||||||
{
|
{
|
||||||
if (modsString != null) return modsString;
|
if (modsString != null) return modsString;
|
||||||
|
|
Loading…
Reference in a new issue