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();
|
||||
GamecraftModdingAPI.App.Client.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");
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
|
||||
using GamecraftModdingAPI.App;
|
||||
using HarmonyLib;
|
||||
using IllusionInjector;
|
||||
// test
|
||||
|
@ -22,8 +22,16 @@ using GamecraftModdingAPI.Commands;
|
|||
using GamecraftModdingAPI.Events;
|
||||
using GamecraftModdingAPI.Utility;
|
||||
using GamecraftModdingAPI.Blocks;
|
||||
using GamecraftModdingAPI.Interface.IMGUI;
|
||||
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 Label = GamecraftModdingAPI.Interface.IMGUI.Label;
|
||||
|
||||
namespace GamecraftModdingAPI.Tests
|
||||
{
|
||||
|
@ -347,7 +355,32 @@ namespace GamecraftModdingAPI.Tests
|
|||
{
|
||||
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
|
||||
TestRoot.RunTests();
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue