mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-15 22:07:48 +08:00
More progress
This commit is contained in:
parent
0bc14b2f76
commit
2378925a8b
@ -6,6 +6,10 @@
|
||||
An in-game explorer and a suite of debugging tools for <a href="https://docs.unity3d.com/Manual/IL2CPP.html">IL2CPP</a> and <b>Mono</b> Unity games, to aid with modding development.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
Supports most Unity games from versions 5.2 to 2020+.
|
||||
</p>
|
||||
|
||||
## Releases [](../../releases/latest) [](../../releases) [](../../releases/latest)
|
||||
|
||||
| Mod Loader | IL2CPP | Mono |
|
||||
|
@ -31,6 +31,7 @@ namespace UnityExplorer.Core.Config
|
||||
|
||||
public static ConfigElement<string> ObjectExplorerData;
|
||||
public static ConfigElement<string> InspectorData;
|
||||
public static ConfigElement<string> CSConsoleData;
|
||||
|
||||
internal static readonly Dictionary<string, IConfigElement> ConfigElements = new Dictionary<string, IConfigElement>();
|
||||
internal static readonly Dictionary<string, IConfigElement> InternalConfigs = new Dictionary<string, IConfigElement>();
|
||||
@ -107,6 +108,7 @@ namespace UnityExplorer.Core.Config
|
||||
|
||||
ObjectExplorerData = new ConfigElement<string>("ObjectExplorer", "", "", true);
|
||||
InspectorData = new ConfigElement<string>("Inspector", "", "", true);
|
||||
CSConsoleData = new ConfigElement<string>("CSConsole", "", "", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ namespace UnityExplorer.Core.Input
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning($"Exception setting up Camera.onPostRender callback: {ex}");
|
||||
ExplorerCore.LogWarning($"Exception setting up Aggressive Mouse Unlock: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,22 +35,6 @@ namespace UnityExplorer
|
||||
return;
|
||||
}
|
||||
|
||||
// TEMP DEBUG TEST FOR TRANSFORM TREE
|
||||
|
||||
var stressTest = new GameObject("StressTest");
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var obj = new GameObject($"Parent_{i}");
|
||||
obj.transform.parent = stressTest.transform;
|
||||
for (int j = 0; j < 100; j++)
|
||||
{
|
||||
var obj2 = new GameObject($"Child_{j}");
|
||||
obj2.transform.parent = obj.transform;
|
||||
}
|
||||
}
|
||||
|
||||
// END
|
||||
|
||||
Loader = loader;
|
||||
|
||||
if (!Directory.Exists(Loader.ExplorerFolder))
|
||||
@ -81,8 +65,24 @@ namespace UnityExplorer
|
||||
|
||||
UIManager.InitUI();
|
||||
|
||||
InspectorManager.Inspect(typeof(TestClass));
|
||||
//InspectorManager.Inspect(UIManager.CanvasRoot.gameObject.GetComponent<GraphicRaycaster>());
|
||||
// TEMP DEBUG TEST FOR TRANSFORM TREE
|
||||
|
||||
var stressTest = new GameObject("StressTest");
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
var obj = new GameObject($"Parent_{i}");
|
||||
obj.transform.parent = stressTest.transform;
|
||||
for (int j = 0; j < 100; j++)
|
||||
{
|
||||
var obj2 = new GameObject($"Child_{j}");
|
||||
obj2.transform.parent = obj.transform;
|
||||
}
|
||||
}
|
||||
|
||||
// END
|
||||
|
||||
//InspectorManager.Inspect(typeof(TestClass));
|
||||
InspectorManager.Inspect(UIManager.CanvasRoot.gameObject.GetComponent<GraphicRaycaster>());
|
||||
//InspectorManager.InspectType(typeof(ReflectionUtility));
|
||||
}
|
||||
|
||||
|
34
src/UI/CSConsole/CSConsoleManager.cs
Normal file
34
src/UI/CSConsole/CSConsoleManager.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityExplorer.UI.CSConsole
|
||||
{
|
||||
public static class CSConsoleManager
|
||||
{
|
||||
internal const string STARTUP_TEXT = @"Welcome to the UnityExplorer C# Console.
|
||||
|
||||
The following helper methods are available:
|
||||
|
||||
* <color=#add490>Log(""message"")</color> logs a message to the debug console
|
||||
|
||||
* <color=#add490>StartCoroutine(IEnumerator routine)</color> start the IEnumerator as a UnityEngine.Coroutine
|
||||
|
||||
* <color=#add490>CurrentTarget()</color> returns the currently inspected target on the Home page
|
||||
|
||||
* <color=#add490>AllTargets()</color> returns an object[] array containing all inspected instances
|
||||
|
||||
* <color=#add490>Inspect(someObject)</color> to inspect an instance, eg. Inspect(Camera.main);
|
||||
|
||||
* <color=#add490>Inspect(typeof(SomeClass))</color> to inspect a Class with static reflection
|
||||
|
||||
* <color=#add490>AddUsing(""SomeNamespace"")</color> adds a using directive to the C# console
|
||||
|
||||
* <color=#add490>GetUsing()</color> logs the current using directives to the debug console
|
||||
|
||||
* <color=#add490>Reset()</color> resets all using directives and variables
|
||||
";
|
||||
|
||||
}
|
||||
}
|
@ -12,9 +12,9 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
|
||||
|
||||
public override bool ShouldAutoEvaluate => true;
|
||||
|
||||
public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
|
||||
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
|
||||
{
|
||||
base.Initialize(inspector, declaringType, member, returnType);
|
||||
base.SetInspectorOwner(inspector, member);
|
||||
|
||||
// not constant
|
||||
CanWrite = !(FieldInfo.IsLiteral && !FieldInfo.IsInitOnly);
|
||||
|
@ -15,98 +15,26 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
|
||||
public abstract class CacheMember : CacheObjectBase
|
||||
{
|
||||
public ReflectionInspector ParentInspector { get; internal set; }
|
||||
public CacheMemberCell CurrentView { get; internal set; }
|
||||
public bool AutoUpdateWanted { get; internal set; }
|
||||
|
||||
public Type DeclaringType { get; private set; }
|
||||
public string NameForFiltering { get; private set; }
|
||||
public Type DeclaringType { get; protected set; }
|
||||
public string NameForFiltering { get; protected set; }
|
||||
|
||||
public object Value { get; protected set; }
|
||||
public Type FallbackType { get; private set; }
|
||||
|
||||
public abstract bool ShouldAutoEvaluate { get; }
|
||||
public bool HasArguments => Arguments?.Length > 0;
|
||||
public override bool HasArguments => Arguments?.Length > 0;
|
||||
public ParameterInfo[] Arguments { get; protected set; }
|
||||
public bool Evaluating { get; protected set; }
|
||||
public bool CanWrite { get; protected set; }
|
||||
public bool HadException { get; protected set; }
|
||||
public Exception LastException { get; protected set; }
|
||||
|
||||
public string MemberLabelText { get; private set; }
|
||||
public string TypeLabelText { get; protected set; }
|
||||
public string ValueLabelText { get; protected set; }
|
||||
|
||||
private static readonly Dictionary<string, MethodInfo> numberParseMethods = new Dictionary<string, MethodInfo>();
|
||||
|
||||
public enum ValueState
|
||||
public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
|
||||
{
|
||||
NotEvaluated, Exception, NullValue,
|
||||
Boolean, Number, String, Enum,
|
||||
Collection, ValueStruct, Unsupported
|
||||
}
|
||||
|
||||
protected ValueState State = ValueState.NotEvaluated;
|
||||
|
||||
private const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>";
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the CacheMember when an Inspector is opened and caches the member
|
||||
/// </summary>
|
||||
public virtual void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
|
||||
{
|
||||
this.DeclaringType = declaringType;
|
||||
this.ParentInspector = inspector;
|
||||
this.FallbackType = returnType;
|
||||
this.MemberLabelText = SignatureHighlighter.ParseFullSyntax(declaringType, false, member);
|
||||
this.NameForFiltering = $"{declaringType.Name}.{member.Name}";
|
||||
this.TypeLabelText = SignatureHighlighter.ParseFullType(FallbackType, false);
|
||||
this.ValueLabelText = GetValueLabel();
|
||||
}
|
||||
|
||||
public virtual void OnDestroyed()
|
||||
{
|
||||
// TODO release IValue / Evaluate back to pool, etc
|
||||
this.NameLabelText = SignatureHighlighter.ParseFullSyntax(member.DeclaringType, false, member);
|
||||
this.NameForFiltering = $"{member.DeclaringType.Name}.{member.Name}";
|
||||
}
|
||||
|
||||
protected abstract void TryEvaluate();
|
||||
|
||||
public void SetValue(object value)
|
||||
{
|
||||
// TODO unbox string, cast, etc
|
||||
|
||||
TrySetValue(value);
|
||||
|
||||
Evaluate();
|
||||
}
|
||||
|
||||
protected abstract void TrySetValue(object value);
|
||||
|
||||
public void OnCellApplyClicked()
|
||||
{
|
||||
if (CurrentView == null)
|
||||
{
|
||||
ExplorerCore.LogWarning("Trying to apply CacheMember but current cell reference is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (State == ValueState.Boolean)
|
||||
SetValue(this.CurrentView.Toggle.isOn);
|
||||
else
|
||||
{
|
||||
if (!numberParseMethods.ContainsKey(FallbackType.AssemblyQualifiedName))
|
||||
{
|
||||
var method = FallbackType.GetMethod("Parse", new Type[] { typeof(string) });
|
||||
numberParseMethods.Add(FallbackType.AssemblyQualifiedName, method);
|
||||
}
|
||||
|
||||
var val = numberParseMethods[FallbackType.AssemblyQualifiedName]
|
||||
.Invoke(null, new object[] { CurrentView.InputField.text });
|
||||
SetValue(val);
|
||||
}
|
||||
|
||||
SetCell(this.CurrentView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate when first shown (if ShouldAutoEvaluate), or else when Evaluate button is clicked.
|
||||
/// </summary>
|
||||
@ -120,79 +48,27 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
|
||||
ProcessOnEvaluate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the CacheMember state when the value has been evaluated (or re-evaluated)
|
||||
/// </summary>
|
||||
protected virtual void ProcessOnEvaluate()
|
||||
public override void SetUserValue(object value)
|
||||
{
|
||||
var prevState = State;
|
||||
// TODO unbox string, cast, etc
|
||||
|
||||
if (HadException)
|
||||
State = ValueState.Exception;
|
||||
else if (Value.IsNullOrDestroyed())
|
||||
State = ValueState.NullValue;
|
||||
else
|
||||
{
|
||||
var type = Value.GetActualType();
|
||||
TrySetValue(value);
|
||||
|
||||
if (type == typeof(bool))
|
||||
State = ValueState.Boolean;
|
||||
else if (type.IsPrimitive || type == typeof(decimal))
|
||||
State = ValueState.Number;
|
||||
else if (type == typeof(string))
|
||||
State = ValueState.String;
|
||||
else if (type.IsEnum)
|
||||
State = ValueState.Enum;
|
||||
else if (type.IsEnumerable() || type.IsDictionary())
|
||||
State = ValueState.Collection;
|
||||
// todo Color and ValueStruct
|
||||
else
|
||||
State = ValueState.Unsupported;
|
||||
}
|
||||
|
||||
// Set label text
|
||||
ValueLabelText = GetValueLabel();
|
||||
|
||||
if (State != prevState)
|
||||
{
|
||||
// TODO handle if subcontent / evaluate shown, check type change, etc
|
||||
}
|
||||
Evaluate();
|
||||
}
|
||||
|
||||
private string GetValueLabel()
|
||||
protected override void SetValueState(CacheObjectCell cell, bool valueActive, bool valueRichText, Color valueColor,
|
||||
bool typeLabelActive, bool toggleActive, bool inputActive, bool applyActive, bool inspectActive, bool subContentActive)
|
||||
{
|
||||
switch (State)
|
||||
{
|
||||
case ValueState.NotEvaluated:
|
||||
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.ParseFullType(FallbackType, true)})</i>";
|
||||
case ValueState.Exception:
|
||||
return $"<i><color=red>{ReflectionUtility.ReflectionExToString(LastException)}</color></i>";
|
||||
case ValueState.Boolean:
|
||||
case ValueState.Number:
|
||||
return null;
|
||||
case ValueState.String:
|
||||
string s = Value as string;
|
||||
if (s.Length > 200)
|
||||
s = $"{s.Substring(0, 200)}...";
|
||||
return $"\"{s}\"";
|
||||
case ValueState.NullValue:
|
||||
return $"<i>{ToStringUtility.ToStringWithType(Value, FallbackType, true)}</i>";
|
||||
case ValueState.Enum:
|
||||
case ValueState.Collection:
|
||||
case ValueState.ValueStruct:
|
||||
case ValueState.Unsupported:
|
||||
default:
|
||||
return ToStringUtility.ToStringWithType(Value, FallbackType, true);
|
||||
}
|
||||
base.SetValueState(cell, valueActive, valueRichText, valueColor, typeLabelActive, toggleActive, inputActive, applyActive,
|
||||
inspectActive, subContentActive);
|
||||
|
||||
(cell as CacheMemberCell).UpdateToggle.gameObject.SetActive(ShouldAutoEvaluate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the cell view for an enabled cell based on this CacheMember model.
|
||||
/// </summary>
|
||||
public void SetCell(CacheMemberCell cell)
|
||||
protected override bool SetCellEvaluateState(CacheObjectCell objectcell)
|
||||
{
|
||||
cell.MemberLabel.text = MemberLabelText;
|
||||
cell.ValueLabel.gameObject.SetActive(true);
|
||||
var cell = objectcell as CacheMemberCell;
|
||||
|
||||
cell.EvaluateHolder.SetActive(!ShouldAutoEvaluate);
|
||||
if (!ShouldAutoEvaluate)
|
||||
@ -215,79 +91,15 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
|
||||
// todo evaluate buttons etc
|
||||
SetValueState(cell, true, true, Color.white, false, false, false, false, false, false);
|
||||
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (State == ValueState.NotEvaluated)
|
||||
Evaluate();
|
||||
|
||||
switch (State)
|
||||
{
|
||||
case ValueState.Exception:
|
||||
case ValueState.NullValue:
|
||||
SetValueState(cell, true, true, Color.white, false, false, false, false, false, false);
|
||||
break;
|
||||
case ValueState.Boolean:
|
||||
SetValueState(cell, false, false, default, true, toggleActive: true, false, CanWrite, false, false);
|
||||
break;
|
||||
case ValueState.Number:
|
||||
SetValueState(cell, false, true, Color.white, true, false, inputActive: true, CanWrite, false, false);
|
||||
break;
|
||||
case ValueState.String:
|
||||
SetValueState(cell, true, false, SignatureHighlighter.StringOrange, false, false, false, false, false, true);
|
||||
break;
|
||||
case ValueState.Enum:
|
||||
SetValueState(cell, true, true, Color.white, false, false, false, false, false, true);
|
||||
break;
|
||||
case ValueState.Collection:
|
||||
case ValueState.ValueStruct:
|
||||
SetValueState(cell, true, true, Color.white, false, false, false, false, true, true);
|
||||
break;
|
||||
case ValueState.Unsupported:
|
||||
SetValueState(cell, true, true, Color.white, false, false, false, false, true, false);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetValueState(CacheMemberCell cell, bool valueActive, bool valueRichText, Color valueColor,
|
||||
bool typeLabelActive, bool toggleActive, bool inputActive, bool applyActive, bool inspectActive, bool subContentActive)
|
||||
{
|
||||
//cell.ValueLabel.gameObject.SetActive(valueActive);
|
||||
if (valueActive)
|
||||
{
|
||||
cell.ValueLabel.text = ValueLabelText;
|
||||
cell.ValueLabel.supportRichText = valueRichText;
|
||||
cell.ValueLabel.color = valueColor;
|
||||
}
|
||||
else
|
||||
cell.ValueLabel.text = "";
|
||||
|
||||
cell.TypeLabel.gameObject.SetActive(typeLabelActive);
|
||||
if (typeLabelActive)
|
||||
cell.TypeLabel.text = TypeLabelText;
|
||||
|
||||
cell.Toggle.gameObject.SetActive(toggleActive);
|
||||
if (toggleActive)
|
||||
{
|
||||
cell.Toggle.isOn = (bool)Value;
|
||||
cell.ToggleText.text = Value.ToString();
|
||||
}
|
||||
|
||||
cell.InputField.gameObject.SetActive(inputActive);
|
||||
if (inputActive)
|
||||
{
|
||||
cell.InputField.text = Value.ToString();
|
||||
cell.InputField.readOnly = !CanWrite;
|
||||
}
|
||||
|
||||
cell.ApplyButton.Button.gameObject.SetActive(applyActive);
|
||||
cell.InspectButton.Button.gameObject.SetActive(inspectActive);
|
||||
cell.SubContentButton.Button.gameObject.SetActive(subContentActive);
|
||||
|
||||
cell.UpdateToggle.gameObject.SetActive(ShouldAutoEvaluate);
|
||||
}
|
||||
|
||||
|
||||
#region Cache Member Util
|
||||
|
||||
public static bool CanProcessArgs(ParameterInfo[] parameters)
|
||||
@ -430,7 +242,9 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
|
||||
|
||||
cachedSigs.Add(sig);
|
||||
|
||||
cached.Initialize(_inspector, declaringType, member, returnType);
|
||||
//cached.Initialize(_inspector, declaringType, member, returnType);
|
||||
cached.Initialize(returnType);
|
||||
cached.SetInspectorOwner(_inspector, member);
|
||||
|
||||
list.Add(cached);
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
|
||||
|
||||
public override bool ShouldAutoEvaluate => false;
|
||||
|
||||
public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
|
||||
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
|
||||
{
|
||||
base.Initialize(inspector, declaringType, member, returnType);
|
||||
base.SetInspectorOwner(inspector, member);
|
||||
|
||||
Arguments = MethodInfo.GetParameters();
|
||||
}
|
||||
|
@ -1,12 +1,307 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.UI.Inspectors.CacheObject.Views;
|
||||
using UnityExplorer.UI.Inspectors.IValues;
|
||||
using UnityExplorer.UI.ObjectPool;
|
||||
using UnityExplorer.UI.Utility;
|
||||
|
||||
namespace UnityExplorer.UI.Inspectors.CacheObject
|
||||
{
|
||||
public abstract class CacheObjectBase
|
||||
{
|
||||
public CacheObjectCell CellView { get; internal set; }
|
||||
|
||||
public InteractiveValue IValue { get; private set; }
|
||||
public Type CurrentIValueType { get; private set; }
|
||||
public bool SubContentState { get; private set; }
|
||||
|
||||
public object Value { get; protected set; }
|
||||
public Type FallbackType { get; protected set; }
|
||||
|
||||
public string NameLabelText { get; protected set; }
|
||||
public string TypeLabelText { get; protected set; }
|
||||
public string ValueLabelText { get; protected set; }
|
||||
|
||||
public abstract bool ShouldAutoEvaluate { get; }
|
||||
public abstract bool HasArguments { get; }
|
||||
public bool CanWrite { get; protected set; }
|
||||
public bool HadException { get; protected set; }
|
||||
public Exception LastException { get; protected set; }
|
||||
|
||||
public virtual void Initialize(Type fallbackType)
|
||||
{
|
||||
this.FallbackType = fallbackType;
|
||||
this.TypeLabelText = SignatureHighlighter.ParseFullType(FallbackType, false);
|
||||
this.ValueLabelText = GetValueLabel();
|
||||
}
|
||||
|
||||
// internals
|
||||
|
||||
private static readonly Dictionary<string, MethodInfo> numberParseMethods = new Dictionary<string, MethodInfo>();
|
||||
|
||||
public enum ValueState
|
||||
{
|
||||
NotEvaluated, Exception, NullValue,
|
||||
Boolean, Number, String, Enum,
|
||||
Collection, ValueStruct, Unsupported
|
||||
}
|
||||
|
||||
public ValueState State = ValueState.NotEvaluated;
|
||||
|
||||
protected const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>";
|
||||
|
||||
internal static GameObject InactiveIValueHolder
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!inactiveIValueHolder)
|
||||
{
|
||||
inactiveIValueHolder = new GameObject("InactiveIValueHolder");
|
||||
GameObject.DontDestroyOnLoad(inactiveIValueHolder);
|
||||
inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform;
|
||||
inactiveIValueHolder.SetActive(false);
|
||||
}
|
||||
return inactiveIValueHolder;
|
||||
}
|
||||
}
|
||||
private static GameObject inactiveIValueHolder;
|
||||
|
||||
// On parent destroying this
|
||||
|
||||
public virtual void OnDestroyed()
|
||||
{
|
||||
// TODO release IValue / Evaluate back to pool, etc
|
||||
ReleaseIValue();
|
||||
}
|
||||
|
||||
// Updating and applying values
|
||||
|
||||
public abstract void SetUserValue(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Process the CacheMember state when the value has been evaluated (or re-evaluated)
|
||||
/// </summary>
|
||||
protected virtual void ProcessOnEvaluate()
|
||||
{
|
||||
var prevState = State;
|
||||
|
||||
if (HadException)
|
||||
State = ValueState.Exception;
|
||||
else if (Value.IsNullOrDestroyed())
|
||||
State = ValueState.NullValue;
|
||||
else
|
||||
{
|
||||
var type = Value.GetActualType();
|
||||
|
||||
if (type == typeof(bool))
|
||||
State = ValueState.Boolean;
|
||||
else if (type.IsPrimitive || type == typeof(decimal))
|
||||
State = ValueState.Number;
|
||||
else if (type == typeof(string))
|
||||
State = ValueState.String;
|
||||
else if (type.IsEnum)
|
||||
State = ValueState.Enum;
|
||||
else if (type.IsEnumerable() || type.IsDictionary())
|
||||
State = ValueState.Collection;
|
||||
// todo Color and ValueStruct
|
||||
else
|
||||
State = ValueState.Unsupported;
|
||||
}
|
||||
|
||||
// Set label text
|
||||
ValueLabelText = GetValueLabel();
|
||||
|
||||
if (State != prevState)
|
||||
{
|
||||
// TODO handle if subcontent / evaluate shown, check type change, etc
|
||||
}
|
||||
}
|
||||
|
||||
protected string GetValueLabel()
|
||||
{
|
||||
switch (State)
|
||||
{
|
||||
case ValueState.NotEvaluated:
|
||||
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.ParseFullType(FallbackType, true)})</i>";
|
||||
case ValueState.Exception:
|
||||
return $"<i><color=red>{ReflectionUtility.ReflectionExToString(LastException)}</color></i>";
|
||||
case ValueState.Boolean:
|
||||
case ValueState.Number:
|
||||
return null;
|
||||
case ValueState.String:
|
||||
string s = Value as string;
|
||||
if (s.Length > 200)
|
||||
s = $"{s.Substring(0, 200)}...";
|
||||
return $"\"{s}\"";
|
||||
case ValueState.NullValue:
|
||||
return $"<i>{ToStringUtility.ToStringWithType(Value, FallbackType, true)}</i>";
|
||||
case ValueState.Enum:
|
||||
case ValueState.Collection:
|
||||
case ValueState.ValueStruct:
|
||||
case ValueState.Unsupported:
|
||||
default:
|
||||
return ToStringUtility.ToStringWithType(Value, FallbackType, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract bool SetCellEvaluateState(CacheObjectCell cell);
|
||||
|
||||
public virtual void SetCell(CacheObjectCell cell)
|
||||
{
|
||||
cell.NameLabel.text = NameLabelText;
|
||||
cell.ValueLabel.gameObject.SetActive(true);
|
||||
|
||||
cell.SubContentHolder.gameObject.SetActive(SubContentState);
|
||||
if (IValue != null)
|
||||
IValue.UIRoot.transform.SetParent(cell.SubContentHolder.transform, false);
|
||||
|
||||
if (SetCellEvaluateState(cell))
|
||||
return;
|
||||
|
||||
switch (State)
|
||||
{
|
||||
case ValueState.Exception:
|
||||
case ValueState.NullValue:
|
||||
ReleaseIValue();
|
||||
SetValueState(cell, true, true, Color.white, false, false, false, false, false, false);
|
||||
break;
|
||||
case ValueState.Boolean:
|
||||
SetValueState(cell, false, false, default, false, toggleActive: true, false, CanWrite, false, false);
|
||||
break;
|
||||
case ValueState.Number:
|
||||
SetValueState(cell, false, true, Color.white, true, false, inputActive: true, CanWrite, false, false);
|
||||
break;
|
||||
case ValueState.String:
|
||||
UpdateIValueOnValueUpdate();
|
||||
SetValueState(cell, true, false, SignatureHighlighter.StringOrange, false, false, false, false, false, true);
|
||||
break;
|
||||
case ValueState.Enum:
|
||||
UpdateIValueOnValueUpdate();
|
||||
SetValueState(cell, true, true, Color.white, false, false, false, false, false, true);
|
||||
break;
|
||||
case ValueState.Collection:
|
||||
case ValueState.ValueStruct:
|
||||
UpdateIValueOnValueUpdate();
|
||||
SetValueState(cell, true, true, Color.white, false, false, false, false, true, true);
|
||||
break;
|
||||
case ValueState.Unsupported:
|
||||
SetValueState(cell, true, true, Color.white, false, false, false, false, true, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SetValueState(CacheObjectCell cell, bool valueActive, bool valueRichText, Color valueColor,
|
||||
bool typeLabelActive, bool toggleActive, bool inputActive, bool applyActive, bool inspectActive, bool subContentActive)
|
||||
{
|
||||
//cell.ValueLabel.gameObject.SetActive(valueActive);
|
||||
if (valueActive)
|
||||
{
|
||||
cell.ValueLabel.text = ValueLabelText;
|
||||
cell.ValueLabel.supportRichText = valueRichText;
|
||||
cell.ValueLabel.color = valueColor;
|
||||
}
|
||||
else
|
||||
cell.ValueLabel.text = "";
|
||||
|
||||
cell.TypeLabel.gameObject.SetActive(typeLabelActive);
|
||||
if (typeLabelActive)
|
||||
cell.TypeLabel.text = TypeLabelText;
|
||||
|
||||
cell.Toggle.gameObject.SetActive(toggleActive);
|
||||
if (toggleActive)
|
||||
{
|
||||
cell.Toggle.isOn = (bool)Value;
|
||||
cell.ToggleText.text = Value.ToString();
|
||||
}
|
||||
|
||||
cell.InputField.gameObject.SetActive(inputActive);
|
||||
if (inputActive)
|
||||
{
|
||||
cell.InputField.text = Value.ToString();
|
||||
cell.InputField.readOnly = !CanWrite;
|
||||
}
|
||||
|
||||
cell.ApplyButton.Button.gameObject.SetActive(applyActive);
|
||||
cell.InspectButton.Button.gameObject.SetActive(inspectActive);
|
||||
cell.SubContentButton.Button.gameObject.SetActive(subContentActive);
|
||||
}
|
||||
|
||||
// IValues
|
||||
|
||||
public virtual void OnCellSubContentToggle()
|
||||
{
|
||||
if (this.IValue == null)
|
||||
{
|
||||
IValue = (InteractiveValue)Pool.Borrow(typeof(InteractiveValue));
|
||||
IValue.SetOwner(this);
|
||||
IValue.UIRoot.transform.SetParent(CellView.SubContentHolder.transform, false);
|
||||
CellView.SubContentHolder.SetActive(true);
|
||||
SubContentState = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SubContentState = !SubContentState;
|
||||
CellView.SubContentHolder.SetActive(SubContentState);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ReleaseIValue()
|
||||
{
|
||||
if (IValue == null)
|
||||
return;
|
||||
|
||||
IValue.OnOwnerReleased();
|
||||
Pool.Return(CurrentIValueType, IValue);
|
||||
|
||||
IValue = null;
|
||||
}
|
||||
|
||||
internal void HideIValue()
|
||||
{
|
||||
if (this.IValue == null)
|
||||
return;
|
||||
|
||||
this.IValue.UIRoot.transform.SetParent(InactiveIValueHolder.transform, false);
|
||||
}
|
||||
|
||||
public void UpdateIValueOnValueUpdate()
|
||||
{
|
||||
if (this.IValue == null)
|
||||
return;
|
||||
|
||||
IValue.SetValue(Value);
|
||||
}
|
||||
|
||||
// CacheObjectCell Apply
|
||||
|
||||
public virtual void OnCellApplyClicked()
|
||||
{
|
||||
if (CellView == null)
|
||||
{
|
||||
ExplorerCore.LogWarning("Trying to apply CacheMember but current cell reference is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (State == ValueState.Boolean)
|
||||
SetUserValue(this.CellView.Toggle.isOn);
|
||||
else
|
||||
{
|
||||
if (!numberParseMethods.ContainsKey(FallbackType.AssemblyQualifiedName))
|
||||
{
|
||||
var method = FallbackType.GetMethod("Parse", new Type[] { typeof(string) });
|
||||
numberParseMethods.Add(FallbackType.AssemblyQualifiedName, method);
|
||||
}
|
||||
|
||||
var val = numberParseMethods[FallbackType.AssemblyQualifiedName]
|
||||
.Invoke(null, new object[] { CellView.InputField.text });
|
||||
SetUserValue(val);
|
||||
}
|
||||
|
||||
SetCell(this.CellView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,9 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
|
||||
|
||||
public override bool ShouldAutoEvaluate => !HasArguments;
|
||||
|
||||
public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
|
||||
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
|
||||
{
|
||||
base.Initialize(inspector, declaringType, member, returnType);
|
||||
base.SetInspectorOwner(inspector, member);
|
||||
|
||||
this.CanWrite = PropertyInfo.CanWrite;
|
||||
Arguments = PropertyInfo.GetIndexParameters();
|
||||
|
@ -4,207 +4,57 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.Utility;
|
||||
using UnityExplorer.UI.Widgets;
|
||||
|
||||
namespace UnityExplorer.UI.Inspectors.CacheObject.Views
|
||||
{
|
||||
public class CacheMemberCell : ICell
|
||||
public class CacheMemberCell : CacheObjectCell
|
||||
{
|
||||
#region ICell
|
||||
|
||||
public float DefaultHeight => 30f;
|
||||
|
||||
public GameObject UIRoot => uiRoot;
|
||||
public GameObject uiRoot;
|
||||
|
||||
public bool Enabled => m_enabled;
|
||||
private bool m_enabled;
|
||||
|
||||
public RectTransform Rect => m_rect;
|
||||
private RectTransform m_rect;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
m_enabled = false;
|
||||
uiRoot.SetActive(false);
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
m_enabled = true;
|
||||
uiRoot.SetActive(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public ReflectionInspector CurrentOwner { get; set; }
|
||||
public CacheMember CurrentOccupant { get; set; }
|
||||
|
||||
public LayoutElement MemberLayout;
|
||||
public LayoutElement RightGroupLayout;
|
||||
|
||||
public Text MemberLabel;
|
||||
public Text TypeLabel;
|
||||
public Text ValueLabel;
|
||||
public Toggle Toggle;
|
||||
public Text ToggleText;
|
||||
public InputField InputField;
|
||||
public CacheMember MemberOccupant => Occupant as CacheMember;
|
||||
|
||||
public GameObject EvaluateHolder;
|
||||
public ButtonRef EvaluateButton;
|
||||
|
||||
public ButtonRef InspectButton;
|
||||
public ButtonRef SubContentButton;
|
||||
public ButtonRef ApplyButton;
|
||||
|
||||
public Toggle UpdateToggle;
|
||||
|
||||
public GameObject SubContentHolder;
|
||||
|
||||
public void OnReturnToPool()
|
||||
protected virtual void EvaluateClicked()
|
||||
{
|
||||
if (CurrentOccupant != null)
|
||||
{
|
||||
// TODO
|
||||
// TODO
|
||||
}
|
||||
|
||||
CurrentOccupant = null;
|
||||
}
|
||||
public override void OnReturnToPool()
|
||||
{
|
||||
base.OnReturnToPool();
|
||||
|
||||
// TODO ?
|
||||
|
||||
CurrentOwner = null;
|
||||
}
|
||||
|
||||
private void ApplyClicked()
|
||||
protected override void ConstructEvaluateHolder(GameObject parent)
|
||||
{
|
||||
CurrentOccupant.OnCellApplyClicked();
|
||||
}
|
||||
|
||||
private void InspectClicked()
|
||||
{
|
||||
InspectorManager.Inspect(CurrentOccupant.Value);
|
||||
}
|
||||
|
||||
private void EvaluateClicked()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void SubContentClicked()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void ToggleClicked(bool value)
|
||||
{
|
||||
ToggleText.text = value.ToString();
|
||||
}
|
||||
|
||||
// Todo could create these as needed maybe, just need to make sure the transform order is correct.
|
||||
|
||||
public GameObject CreateContent(GameObject parent)
|
||||
{
|
||||
// Main layout
|
||||
|
||||
uiRoot = UIFactory.CreateUIObject("CacheMemberCell", parent, new Vector2(100, 30));
|
||||
m_rect = uiRoot.GetComponent<RectTransform>();
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(uiRoot, true, false, true, true, 2, 0);
|
||||
UIFactory.SetLayoutElement(uiRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
|
||||
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
var separator = UIFactory.CreateUIObject("TopSeperator", uiRoot);
|
||||
UIFactory.SetLayoutElement(separator, minHeight: 1, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
separator.AddComponent<Image>().color = Color.black;
|
||||
|
||||
var horiRow = UIFactory.CreateUIObject("HoriGroup", uiRoot);
|
||||
UIFactory.SetLayoutElement(horiRow, minHeight: 29, flexibleHeight: 150, flexibleWidth: 9999);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horiRow, false, false, true, true, 5, 2, childAlignment: TextAnchor.UpperLeft);
|
||||
horiRow.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
// Left member label
|
||||
|
||||
MemberLabel = UIFactory.CreateLabel(horiRow, "MemberLabel", "<notset>", TextAnchor.MiddleLeft);
|
||||
MemberLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
UIFactory.SetLayoutElement(MemberLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0);
|
||||
MemberLayout = MemberLabel.GetComponent<LayoutElement>();
|
||||
|
||||
// Right vertical group
|
||||
|
||||
var rightGroupHolder = UIFactory.CreateUIObject("RightGroup", horiRow);
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(rightGroupHolder, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
|
||||
UIFactory.SetLayoutElement(rightGroupHolder, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 800);
|
||||
RightGroupLayout = rightGroupHolder.GetComponent<LayoutElement>();
|
||||
|
||||
// Evaluate vert group
|
||||
|
||||
EvaluateHolder = UIFactory.CreateUIObject("EvalGroup", rightGroupHolder);
|
||||
EvaluateHolder = UIFactory.CreateUIObject("EvalGroup", parent);
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(EvaluateHolder, false, false, true, true, 3);
|
||||
UIFactory.SetLayoutElement(EvaluateHolder, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 775);
|
||||
|
||||
EvaluateButton = UIFactory.CreateButton(EvaluateHolder, "EvaluateButton", "Evaluate", new Color(0.15f, 0.15f, 0.15f));
|
||||
UIFactory.SetLayoutElement(EvaluateButton.Button.gameObject, minWidth: 100, minHeight: 25);
|
||||
EvaluateButton.OnClick += EvaluateClicked;
|
||||
}
|
||||
|
||||
// Right horizontal group
|
||||
|
||||
var rightHoriGroup = UIFactory.CreateUIObject("RightHoriGroup", rightGroupHolder);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(rightHoriGroup, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
|
||||
UIFactory.SetLayoutElement(rightHoriGroup, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 800);
|
||||
|
||||
SubContentButton = UIFactory.CreateButton(rightHoriGroup, "SubContentButton", "▲");
|
||||
UIFactory.SetLayoutElement(SubContentButton.Button.gameObject, minWidth: 25, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||
SubContentButton.OnClick += SubContentClicked;
|
||||
|
||||
TypeLabel = UIFactory.CreateLabel(rightHoriGroup, "ReturnLabel", "<notset>", TextAnchor.MiddleLeft);
|
||||
TypeLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
UIFactory.SetLayoutElement(TypeLabel.gameObject, minHeight: 25, flexibleHeight: 150, minWidth: 70, flexibleWidth: 0);
|
||||
|
||||
// Bool and number value interaction
|
||||
|
||||
var toggleObj = UIFactory.CreateToggle(rightHoriGroup, "Toggle", out Toggle, out ToggleText);
|
||||
UIFactory.SetLayoutElement(toggleObj, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||
ToggleText.color = SignatureHighlighter.KeywordBlue;
|
||||
Toggle.onValueChanged.AddListener(ToggleClicked);
|
||||
|
||||
var inputObj = UIFactory.CreateInputField(rightHoriGroup, "InputField", "...", out InputField);
|
||||
UIFactory.SetLayoutElement(inputObj, minWidth: 150, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
// Inspect and apply buttons
|
||||
|
||||
InspectButton = UIFactory.CreateButton(rightHoriGroup, "InspectButton", "Inspect", new Color(0.15f, 0.15f, 0.15f));
|
||||
UIFactory.SetLayoutElement(InspectButton.Button.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25);
|
||||
InspectButton.OnClick += InspectClicked;
|
||||
|
||||
ApplyButton = UIFactory.CreateButton(rightHoriGroup, "ApplyButton", "Apply", new Color(0.15f, 0.15f, 0.15f));
|
||||
UIFactory.SetLayoutElement(ApplyButton.Button.gameObject, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||
ApplyButton.OnClick += ApplyClicked;
|
||||
|
||||
// Main value label
|
||||
|
||||
ValueLabel = UIFactory.CreateLabel(rightHoriGroup, "ValueLabel", "Value goes here", TextAnchor.MiddleLeft);
|
||||
ValueLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
UIFactory.SetLayoutElement(ValueLabel.gameObject, minHeight: 25, flexibleHeight: 150, flexibleWidth: 9999);
|
||||
|
||||
protected override void ConstructUpdateToggle(GameObject parent)
|
||||
{
|
||||
// Auto-update toggle
|
||||
|
||||
var updateToggle = UIFactory.CreateToggle(rightHoriGroup, "AutoUpdate", out UpdateToggle, out Text autoText);
|
||||
var updateToggle = UIFactory.CreateToggle(parent, "AutoUpdate", out UpdateToggle, out Text autoText);
|
||||
UIFactory.SetLayoutElement(updateToggle, minHeight: 25, minWidth: 30, flexibleWidth: 0, flexibleHeight: 0);
|
||||
GameObject.Destroy(autoText);
|
||||
UpdateToggle.isOn = false;
|
||||
UpdateToggle.onValueChanged.AddListener((bool val) => { CurrentOccupant.AutoUpdateWanted = val; });
|
||||
|
||||
//UpdateButton = UIFactory.CreateButton(rightHoriGroup, "UpdateButton", "Update", new Color(0.15f, 0.2f, 0.15f));
|
||||
//UIFactory.SetLayoutElement(UpdateButton.Button.gameObject, minWidth: 65, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
|
||||
//UpdateButton.OnClick += UpdateClicked;
|
||||
|
||||
// Subcontent (todo?)
|
||||
|
||||
SubContentHolder = UIFactory.CreateUIObject("SubContent", uiRoot);
|
||||
UIFactory.SetLayoutElement(SubContentHolder.gameObject, minHeight: 30, flexibleHeight: 500, minWidth: 100, flexibleWidth: 9999);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(SubContentHolder, true, false, true, true, 2, childAlignment: TextAnchor.UpperLeft);
|
||||
|
||||
SubContentHolder.SetActive(false);
|
||||
|
||||
return uiRoot;
|
||||
UpdateToggle.onValueChanged.AddListener((bool val) => { MemberOccupant.AutoUpdateWanted = val; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,189 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.Inspectors.IValues;
|
||||
using UnityExplorer.UI.ObjectPool;
|
||||
using UnityExplorer.UI.Utility;
|
||||
using UnityExplorer.UI.Widgets;
|
||||
|
||||
namespace UnityExplorer.UI.Inspectors.CacheObject.Views
|
||||
{
|
||||
class CacheObjectCell
|
||||
public abstract class CacheObjectCell : ICell
|
||||
{
|
||||
#region ICell
|
||||
|
||||
public float DefaultHeight => 30f;
|
||||
|
||||
public GameObject UIRoot => uiRoot;
|
||||
public GameObject uiRoot;
|
||||
|
||||
public bool Enabled => m_enabled;
|
||||
private bool m_enabled;
|
||||
|
||||
public RectTransform Rect => m_rect;
|
||||
private RectTransform m_rect;
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
m_enabled = false;
|
||||
uiRoot.SetActive(false);
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
m_enabled = true;
|
||||
uiRoot.SetActive(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public CacheObjectBase Occupant { get; set; }
|
||||
public bool SubContentActive => SubContentHolder.activeSelf;
|
||||
|
||||
public LayoutElement MemberLayout;
|
||||
public LayoutElement RightGroupLayout;
|
||||
|
||||
public Text NameLabel;
|
||||
public Text TypeLabel;
|
||||
public Text ValueLabel;
|
||||
public Toggle Toggle;
|
||||
public Text ToggleText;
|
||||
public InputField InputField;
|
||||
|
||||
public ButtonRef InspectButton;
|
||||
public ButtonRef SubContentButton;
|
||||
public ButtonRef ApplyButton;
|
||||
|
||||
public GameObject SubContentHolder;
|
||||
|
||||
public virtual void OnReturnToPool()
|
||||
{
|
||||
if (Occupant != null)
|
||||
{
|
||||
// TODO ?
|
||||
|
||||
SubContentHolder.SetActive(false);
|
||||
|
||||
Occupant = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ApplyClicked()
|
||||
{
|
||||
Occupant.OnCellApplyClicked();
|
||||
}
|
||||
|
||||
protected virtual void InspectClicked()
|
||||
{
|
||||
InspectorManager.Inspect(Occupant.Value);
|
||||
}
|
||||
|
||||
protected virtual void ToggleClicked(bool value)
|
||||
{
|
||||
ToggleText.text = value.ToString();
|
||||
}
|
||||
|
||||
protected virtual void SubContentClicked()
|
||||
{
|
||||
this.Occupant.OnCellSubContentToggle();
|
||||
}
|
||||
|
||||
protected abstract void ConstructEvaluateHolder(GameObject parent);
|
||||
|
||||
protected abstract void ConstructUpdateToggle(GameObject parent);
|
||||
|
||||
// Todo could create these as needed maybe, just need to make sure the transform order is correct.
|
||||
|
||||
public GameObject CreateContent(GameObject parent)
|
||||
{
|
||||
// Main layout
|
||||
|
||||
uiRoot = UIFactory.CreateUIObject("CacheMemberCell", parent, new Vector2(100, 30));
|
||||
m_rect = uiRoot.GetComponent<RectTransform>();
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(uiRoot, true, false, true, true, 2, 0);
|
||||
UIFactory.SetLayoutElement(uiRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
|
||||
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
var separator = UIFactory.CreateUIObject("TopSeperator", uiRoot);
|
||||
UIFactory.SetLayoutElement(separator, minHeight: 1, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
separator.AddComponent<Image>().color = Color.black;
|
||||
|
||||
var horiRow = UIFactory.CreateUIObject("HoriGroup", uiRoot);
|
||||
UIFactory.SetLayoutElement(horiRow, minHeight: 29, flexibleHeight: 150, flexibleWidth: 9999);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horiRow, false, false, true, true, 5, 2, childAlignment: TextAnchor.UpperLeft);
|
||||
horiRow.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
// Left member label
|
||||
|
||||
NameLabel = UIFactory.CreateLabel(horiRow, "MemberLabel", "<notset>", TextAnchor.MiddleLeft);
|
||||
NameLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
UIFactory.SetLayoutElement(NameLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0);
|
||||
MemberLayout = NameLabel.GetComponent<LayoutElement>();
|
||||
|
||||
// Right vertical group
|
||||
|
||||
var rightGroupHolder = UIFactory.CreateUIObject("RightGroup", horiRow);
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(rightGroupHolder, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
|
||||
UIFactory.SetLayoutElement(rightGroupHolder, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 800);
|
||||
RightGroupLayout = rightGroupHolder.GetComponent<LayoutElement>();
|
||||
|
||||
ConstructEvaluateHolder(rightGroupHolder);
|
||||
|
||||
// Right horizontal group
|
||||
|
||||
var rightHoriGroup = UIFactory.CreateUIObject("RightHoriGroup", rightGroupHolder);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(rightHoriGroup, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
|
||||
UIFactory.SetLayoutElement(rightHoriGroup, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 800);
|
||||
|
||||
SubContentButton = UIFactory.CreateButton(rightHoriGroup, "SubContentButton", "▲");
|
||||
UIFactory.SetLayoutElement(SubContentButton.Button.gameObject, minWidth: 25, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||
SubContentButton.OnClick += SubContentClicked;
|
||||
|
||||
// Type label
|
||||
|
||||
TypeLabel = UIFactory.CreateLabel(rightHoriGroup, "ReturnLabel", "<notset>", TextAnchor.MiddleLeft);
|
||||
TypeLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
UIFactory.SetLayoutElement(TypeLabel.gameObject, minHeight: 25, flexibleHeight: 150, minWidth: 60, flexibleWidth: 0);
|
||||
|
||||
// Bool and number value interaction
|
||||
|
||||
var toggleObj = UIFactory.CreateToggle(rightHoriGroup, "Toggle", out Toggle, out ToggleText);
|
||||
UIFactory.SetLayoutElement(toggleObj, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||
ToggleText.color = SignatureHighlighter.KeywordBlue;
|
||||
Toggle.onValueChanged.AddListener(ToggleClicked);
|
||||
|
||||
var inputObj = UIFactory.CreateInputField(rightHoriGroup, "InputField", "...", out InputField);
|
||||
UIFactory.SetLayoutElement(inputObj, minWidth: 150, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
// Inspect and apply buttons
|
||||
|
||||
InspectButton = UIFactory.CreateButton(rightHoriGroup, "InspectButton", "Inspect", new Color(0.15f, 0.15f, 0.15f));
|
||||
UIFactory.SetLayoutElement(InspectButton.Button.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25);
|
||||
InspectButton.OnClick += InspectClicked;
|
||||
|
||||
ApplyButton = UIFactory.CreateButton(rightHoriGroup, "ApplyButton", "Apply", new Color(0.15f, 0.15f, 0.15f));
|
||||
UIFactory.SetLayoutElement(ApplyButton.Button.gameObject, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
||||
ApplyButton.OnClick += ApplyClicked;
|
||||
|
||||
// Main value label
|
||||
|
||||
ValueLabel = UIFactory.CreateLabel(rightHoriGroup, "ValueLabel", "Value goes here", TextAnchor.MiddleLeft);
|
||||
ValueLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
UIFactory.SetLayoutElement(ValueLabel.gameObject, minHeight: 25, flexibleHeight: 150, flexibleWidth: 9999);
|
||||
|
||||
ConstructUpdateToggle(rightHoriGroup);
|
||||
|
||||
// Subcontent (todo?)
|
||||
|
||||
SubContentHolder = UIFactory.CreateUIObject("SubContent", uiRoot);
|
||||
UIFactory.SetLayoutElement(SubContentHolder.gameObject, minHeight: 30, flexibleHeight: 500, minWidth: 100, flexibleWidth: 9999);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(SubContentHolder, true, false, true, true, 2, childAlignment: TextAnchor.UpperLeft);
|
||||
|
||||
SubContentHolder.SetActive(false);
|
||||
|
||||
return uiRoot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.ObjectPool;
|
||||
|
||||
namespace UnityExplorer.UI.Inspectors.IValues
|
||||
{
|
||||
public class IValueTest : IPooledObject
|
||||
{
|
||||
public GameObject UIRoot => uiRoot;
|
||||
private GameObject uiRoot;
|
||||
|
||||
public float DefaultHeight => -1f;
|
||||
|
||||
public GameObject CreateContent(GameObject parent)
|
||||
{
|
||||
uiRoot = UIFactory.CreateUIObject(this.GetType().Name, parent);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(uiRoot, true, true, true, true, 3, childAlignment: TextAnchor.MiddleLeft);
|
||||
|
||||
|
||||
|
||||
return uiRoot;
|
||||
}
|
||||
}
|
||||
}
|
60
src/UI/Inspectors/IValues/InteractiveValue.cs
Normal file
60
src/UI/Inspectors/IValues/InteractiveValue.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.Inspectors.CacheObject;
|
||||
using UnityExplorer.UI.ObjectPool;
|
||||
|
||||
namespace UnityExplorer.UI.Inspectors.IValues
|
||||
{
|
||||
public class InteractiveValue : IPooledObject
|
||||
{
|
||||
public GameObject UIRoot => uiRoot;
|
||||
private GameObject uiRoot;
|
||||
|
||||
public float DefaultHeight => -1f;
|
||||
|
||||
public CacheObjectBase CurrentOwner { get; }
|
||||
private CacheObjectBase m_owner;
|
||||
|
||||
public object EditedValue { get; private set; }
|
||||
|
||||
public virtual void SetOwner(CacheObjectBase owner)
|
||||
{
|
||||
if (this.m_owner != null)
|
||||
{
|
||||
ExplorerCore.LogWarning("Setting an IValue's owner but there is already one set. Maybe it wasn't cleaned up?");
|
||||
OnOwnerReleased();
|
||||
}
|
||||
|
||||
this.m_owner = owner;
|
||||
// ...
|
||||
}
|
||||
|
||||
public virtual void SetValue(object value)
|
||||
{
|
||||
this.EditedValue = value;
|
||||
}
|
||||
|
||||
public virtual void OnOwnerReleased()
|
||||
{
|
||||
if (this.m_owner == null)
|
||||
return;
|
||||
|
||||
// ...
|
||||
this.m_owner = null;
|
||||
}
|
||||
|
||||
public GameObject CreateContent(GameObject parent)
|
||||
{
|
||||
uiRoot = UIFactory.CreateUIObject(this.GetType().Name, parent);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(uiRoot, true, true, true, true, 3, childAlignment: TextAnchor.MiddleLeft);
|
||||
|
||||
UIFactory.CreateLabel(uiRoot, "Label", "this is an ivalue", TextAnchor.MiddleLeft);
|
||||
|
||||
return uiRoot;
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,6 @@ namespace UnityExplorer.UI.Inspectors
|
||||
public class ReflectionInspector : InspectorBase, IPoolDataSource<CacheMemberCell>
|
||||
{
|
||||
public bool StaticOnly { get; internal set; }
|
||||
//public bool AutoUpdate { get; internal set; }
|
||||
|
||||
public object Target { get; private set; }
|
||||
public Type TargetType { get; private set; }
|
||||
@ -38,6 +37,8 @@ namespace UnityExplorer.UI.Inspectors
|
||||
|
||||
private LayoutElement memberTitleLayout;
|
||||
|
||||
private Toggle autoUpdateToggle;
|
||||
|
||||
public override void OnBorrowedFromPool(object target)
|
||||
{
|
||||
base.OnBorrowedFromPool(target);
|
||||
@ -45,6 +46,7 @@ namespace UnityExplorer.UI.Inspectors
|
||||
SetTitleLayouts();
|
||||
SetTarget(target);
|
||||
|
||||
MemberScrollPool.Initialize(this);
|
||||
RuntimeProvider.Instance.StartCoroutine(InitCoroutine());
|
||||
}
|
||||
|
||||
@ -53,9 +55,6 @@ namespace UnityExplorer.UI.Inspectors
|
||||
yield return null;
|
||||
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect);
|
||||
|
||||
MemberScrollPool.RecreateHeightCache();
|
||||
MemberScrollPool.Rebuild();
|
||||
}
|
||||
|
||||
public override void OnReturnToPool()
|
||||
@ -71,6 +70,8 @@ namespace UnityExplorer.UI.Inspectors
|
||||
filteredMembers.Clear();
|
||||
displayedMembers.Clear();
|
||||
|
||||
autoUpdateToggle.isOn = false;
|
||||
|
||||
base.OnReturnToPool();
|
||||
}
|
||||
|
||||
@ -113,6 +114,8 @@ namespace UnityExplorer.UI.Inspectors
|
||||
var member = members[i];
|
||||
filteredMembers.Add(member);
|
||||
}
|
||||
|
||||
//MemberScrollPool.RecreateHeightCache();
|
||||
}
|
||||
|
||||
public override void OnSetActive()
|
||||
@ -161,11 +164,11 @@ namespace UnityExplorer.UI.Inspectors
|
||||
bool shouldRefresh = false;
|
||||
foreach (var member in displayedMembers)
|
||||
{
|
||||
if (!onlyAutoUpdate || member.AutoUpdateWanted)
|
||||
if (member.ShouldAutoEvaluate && (!onlyAutoUpdate || member.AutoUpdateWanted))
|
||||
{
|
||||
shouldRefresh = true;
|
||||
member.Evaluate();
|
||||
member.SetCell(member.CurrentView);
|
||||
member.SetCell(member.CellView);
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,12 +194,12 @@ namespace UnityExplorer.UI.Inspectors
|
||||
{
|
||||
if (index < 0 || index >= filteredMembers.Count)
|
||||
{
|
||||
if (cell.CurrentOccupant != null)
|
||||
if (cell.Occupant != null)
|
||||
{
|
||||
if (displayedMembers.Contains(cell.CurrentOccupant))
|
||||
displayedMembers.Remove(cell.CurrentOccupant);
|
||||
if (displayedMembers.Contains(cell.MemberOccupant))
|
||||
displayedMembers.Remove(cell.MemberOccupant);
|
||||
|
||||
cell.CurrentOccupant.CurrentView = null;
|
||||
cell.Occupant.CellView = null;
|
||||
}
|
||||
|
||||
cell.Disable();
|
||||
@ -205,19 +208,17 @@ namespace UnityExplorer.UI.Inspectors
|
||||
|
||||
var member = filteredMembers[index];
|
||||
|
||||
if (member != cell.CurrentOccupant)
|
||||
if (member != cell.Occupant)
|
||||
{
|
||||
if (cell.CurrentOccupant != null)
|
||||
if (cell.Occupant != null)
|
||||
{
|
||||
// TODO
|
||||
// changing occupant, put subcontent/evaluator on temp holder, etc
|
||||
|
||||
displayedMembers.Remove(cell.CurrentOccupant);
|
||||
cell.CurrentOccupant.CurrentView = null;
|
||||
cell.Occupant.HideIValue();
|
||||
displayedMembers.Remove(cell.MemberOccupant);
|
||||
cell.Occupant.CellView = null;
|
||||
}
|
||||
|
||||
cell.CurrentOccupant = member;
|
||||
member.CurrentView = cell;
|
||||
cell.Occupant = member;
|
||||
member.CellView = cell;
|
||||
displayedMembers.Add(member);
|
||||
}
|
||||
|
||||
@ -226,6 +227,18 @@ namespace UnityExplorer.UI.Inspectors
|
||||
SetCellLayout(cell);
|
||||
}
|
||||
|
||||
private void ToggleAllAutoUpdateStates(bool state)
|
||||
{
|
||||
if (members == null || !members.Any())
|
||||
return;
|
||||
|
||||
foreach (var member in members)
|
||||
member.AutoUpdateWanted = state;
|
||||
|
||||
foreach (var cell in MemberScrollPool.CellPool)
|
||||
cell.UpdateToggle.isOn = state;
|
||||
}
|
||||
|
||||
// Cell layout (fake table alignment)
|
||||
|
||||
private static float MemLabelWidth { get; set; }
|
||||
@ -240,7 +253,7 @@ namespace UnityExplorer.UI.Inspectors
|
||||
memberTitleLayout.minWidth = MemLabelWidth;
|
||||
}
|
||||
|
||||
private void SetCellLayout(CacheMemberCell cell)
|
||||
private void SetCellLayout(CacheObjectCell cell)
|
||||
{
|
||||
cell.MemberLayout.minWidth = MemLabelWidth;
|
||||
cell.RightGroupLayout.minWidth = RightGroupWidth;
|
||||
@ -279,7 +292,7 @@ namespace UnityExplorer.UI.Inspectors
|
||||
memberTitleLayout = memberTitle.gameObject.AddComponent<LayoutElement>();
|
||||
|
||||
var valueTitle = UIFactory.CreateLabel(listTitles, "ValueTitle", "Value", TextAnchor.LowerLeft, Color.grey, fontSize: 15);
|
||||
UIFactory.SetLayoutElement(valueTitle.gameObject, minWidth: 150, flexibleWidth: 9999);
|
||||
UIFactory.SetLayoutElement(valueTitle.gameObject, minWidth: 50, flexibleWidth: 9999);
|
||||
|
||||
var updateButton = UIFactory.CreateButton(listTitles, "UpdateButton", "Update values", new Color(0.22f, 0.28f, 0.22f));
|
||||
UIFactory.SetLayoutElement(updateButton.Button.gameObject, minHeight: 25, minWidth: 130, flexibleWidth: 0);
|
||||
@ -288,17 +301,24 @@ namespace UnityExplorer.UI.Inspectors
|
||||
var updateText = UIFactory.CreateLabel(listTitles, "AutoUpdateLabel", "Auto-update", TextAnchor.MiddleRight, Color.grey);
|
||||
UIFactory.SetLayoutElement(updateText.gameObject, minHeight: 25, minWidth: 80, flexibleWidth: 0);
|
||||
|
||||
var toggleObj = UIFactory.CreateToggle(listTitles, "AutoUpdateToggle", out autoUpdateToggle, out Text toggleText);
|
||||
GameObject.DestroyImmediate(toggleText);
|
||||
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, minWidth: 25);
|
||||
autoUpdateToggle.isOn = false;
|
||||
autoUpdateToggle.onValueChanged.AddListener((bool val) => { ToggleAllAutoUpdateStates(val); });
|
||||
|
||||
var spacer = UIFactory.CreateUIObject("spacer", listTitles);
|
||||
UIFactory.SetLayoutElement(spacer, minWidth: 25, flexibleWidth: 0);
|
||||
|
||||
// Member scroll pool
|
||||
|
||||
MemberScrollPool = UIFactory.CreateScrollPool<CacheMemberCell>(uiRoot, "MemberList", out GameObject scrollObj,
|
||||
out GameObject _, new Color(0.09f, 0.09f, 0.09f));
|
||||
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
|
||||
|
||||
MemberScrollPool.Initialize(this);
|
||||
|
||||
InspectorPanel.Instance.UIRoot.GetComponent<Mask>().enabled = false;
|
||||
MemberScrollPool.Viewport.GetComponent<Mask>().enabled = false;
|
||||
MemberScrollPool.Viewport.GetComponent<Image>().color = new Color(0.12f, 0.12f, 0.12f);
|
||||
//InspectorPanel.Instance.UIRoot.GetComponent<Mask>().enabled = false;
|
||||
//MemberScrollPool.Viewport.GetComponent<Mask>().enabled = false;
|
||||
//MemberScrollPool.Viewport.GetComponent<Image>().color = new Color(0.12f, 0.12f, 0.12f);
|
||||
|
||||
return uiRoot;
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ namespace UnityExplorer.UI.Models
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => UIRoot && UIRoot.activeSelf;
|
||||
get => UIRoot && UIRoot.activeInHierarchy;
|
||||
set
|
||||
{
|
||||
if (!UIRoot || Enabled == value)
|
||||
|
220
src/UI/Panels/CSConsolePanel.cs
Normal file
220
src/UI/Panels/CSConsolePanel.cs
Normal file
@ -0,0 +1,220 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.UI.CSConsole;
|
||||
using UnityExplorer.UI.Utility;
|
||||
|
||||
namespace UnityExplorer.UI.Panels
|
||||
{
|
||||
public class CSConsolePanel : UIPanel
|
||||
{
|
||||
public override string Name => "C# Console";
|
||||
public override UIManager.Panels PanelType => UIManager.Panels.CSConsole;
|
||||
|
||||
public static CSConsolePanel Instance { get; private set; }
|
||||
|
||||
public InputField InputField { get; private set; }
|
||||
public Text InputText { get; private set; }
|
||||
public Text HighlightText { get; private set; }
|
||||
|
||||
public Action<string> OnInputChanged;
|
||||
private float m_timeOfLastInputInvoke;
|
||||
|
||||
public Action OnResetClicked;
|
||||
public Action OnCompileClicked;
|
||||
public Action<bool> OnCtrlRToggled;
|
||||
public Action<bool> OnSuggestionsToggled;
|
||||
public Action<bool> OnAutoIndentToggled;
|
||||
|
||||
private int m_lastCaretPosition;
|
||||
private int m_desiredCaretFix;
|
||||
private bool m_fixWaiting;
|
||||
private float m_defaultInputFieldAlpha;
|
||||
|
||||
public void UseSuggestion(string suggestion)
|
||||
{
|
||||
string input = InputField.text;
|
||||
input = input.Insert(m_lastCaretPosition, suggestion);
|
||||
InputField.text = input;
|
||||
|
||||
m_desiredCaretFix = m_lastCaretPosition += suggestion.Length;
|
||||
|
||||
var color = InputField.selectionColor;
|
||||
color.a = 0f;
|
||||
InputField.selectionColor = color;
|
||||
}
|
||||
|
||||
private void InvokeOnValueChanged(string value)
|
||||
{
|
||||
if (Time.time - m_timeOfLastInputInvoke <= 0f)
|
||||
return;
|
||||
|
||||
m_timeOfLastInputInvoke = Time.time;
|
||||
OnInputChanged?.Invoke(value);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (m_desiredCaretFix >= 0)
|
||||
{
|
||||
if (!m_fixWaiting)
|
||||
{
|
||||
EventSystem.current.SetSelectedGameObject(InputField.gameObject, null);
|
||||
m_fixWaiting = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
InputField.caretPosition = m_desiredCaretFix;
|
||||
InputField.selectionFocusPosition = m_desiredCaretFix;
|
||||
var color = InputField.selectionColor;
|
||||
color.a = m_defaultInputFieldAlpha;
|
||||
InputField.selectionColor = color;
|
||||
|
||||
m_fixWaiting = false;
|
||||
m_desiredCaretFix = -1;
|
||||
}
|
||||
}
|
||||
else if (InputField.caretPosition > 0)
|
||||
m_lastCaretPosition = InputField.caretPosition;
|
||||
}
|
||||
|
||||
// Saving
|
||||
|
||||
public override void DoSaveToConfigElement()
|
||||
{
|
||||
ConfigManager.CSConsoleData.Value = this.ToSaveData();
|
||||
}
|
||||
|
||||
public override void LoadSaveData()
|
||||
{
|
||||
this.ApplySaveData(ConfigManager.CSConsoleData.Value);
|
||||
}
|
||||
|
||||
public override void SetDefaultPosAndAnchors()
|
||||
{
|
||||
mainPanelRect.localPosition = Vector2.zero;
|
||||
mainPanelRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
mainPanelRect.anchorMin = new Vector2(0.5f, 0);
|
||||
mainPanelRect.anchorMax = new Vector2(0.5f, 1);
|
||||
mainPanelRect.offsetMin = new Vector2(mainPanelRect.offsetMin.x, 100); // bottom
|
||||
mainPanelRect.offsetMax = new Vector2(mainPanelRect.offsetMax.x, -50); // top
|
||||
mainPanelRect.sizeDelta = new Vector2(700f, mainPanelRect.sizeDelta.y);
|
||||
mainPanelRect.anchoredPosition = new Vector2(-150, 0);
|
||||
}
|
||||
|
||||
// UI Construction
|
||||
|
||||
public override void ConstructPanelContent()
|
||||
{
|
||||
//Content = UIFactory.CreateVerticalGroup(MainMenu.Instance.PageViewport, "CSharpConsole", true, true, true, true);
|
||||
//UIFactory.SetLayoutElement(Content, preferredHeight: 500, flexibleHeight: 9000);
|
||||
|
||||
#region TOP BAR
|
||||
|
||||
// Main group object
|
||||
|
||||
var topBarObj = UIFactory.CreateHorizontalGroup(this.content, "TopBar", true, true, true, true, 10, new Vector4(8, 8, 30, 30),
|
||||
default, TextAnchor.LowerCenter);
|
||||
UIFactory.SetLayoutElement(topBarObj, minHeight: 50, flexibleHeight: 0);
|
||||
|
||||
// Top label
|
||||
|
||||
var topBarLabel = UIFactory.CreateLabel(topBarObj, "TopLabel", "C# Console", TextAnchor.MiddleLeft, default, true, 25);
|
||||
UIFactory.SetLayoutElement(topBarLabel.gameObject, preferredWidth: 150, flexibleWidth: 5000);
|
||||
|
||||
// Enable Ctrl+R toggle
|
||||
|
||||
var ctrlRToggleObj = UIFactory.CreateToggle(topBarObj, "CtrlRToggle", out var CtrlRToggle, out Text ctrlRToggleText);
|
||||
UIFactory.SetLayoutElement(ctrlRToggleObj, minWidth: 140, flexibleWidth: 0, minHeight: 25);
|
||||
ctrlRToggleText.alignment = TextAnchor.UpperLeft;
|
||||
ctrlRToggleText.text = "Run on Ctrl+R";
|
||||
CtrlRToggle.onValueChanged.AddListener((bool val) => { OnCtrlRToggled?.Invoke(val); });
|
||||
|
||||
// Enable Suggestions toggle
|
||||
|
||||
var suggestToggleObj = UIFactory.CreateToggle(topBarObj, "SuggestionToggle", out var SuggestionsToggle, out Text suggestToggleText);
|
||||
UIFactory.SetLayoutElement(suggestToggleObj, minWidth: 120, flexibleWidth: 0, minHeight: 25);
|
||||
suggestToggleText.alignment = TextAnchor.UpperLeft;
|
||||
suggestToggleText.text = "Suggestions";
|
||||
SuggestionsToggle.onValueChanged.AddListener((bool val) => { OnSuggestionsToggled?.Invoke(val); });
|
||||
|
||||
// Enable Auto-indent toggle
|
||||
|
||||
var autoIndentToggleObj = UIFactory.CreateToggle(topBarObj, "IndentToggle", out var AutoIndentToggle, out Text autoIndentToggleText);
|
||||
UIFactory.SetLayoutElement(autoIndentToggleObj, minWidth: 180, flexibleWidth: 0, minHeight: 25);
|
||||
autoIndentToggleText.alignment = TextAnchor.UpperLeft;
|
||||
autoIndentToggleText.text = "Auto-indent on Enter";
|
||||
AutoIndentToggle.onValueChanged.AddListener((bool val) => { OnAutoIndentToggled?.Invoke(val); });
|
||||
|
||||
#endregion
|
||||
|
||||
#region CONSOLE INPUT
|
||||
|
||||
int fontSize = 16;
|
||||
|
||||
//var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", CSConsoleManager.STARTUP_TEXT,
|
||||
// out InputFieldScroller consoleScroll, fontSize);
|
||||
|
||||
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", CSConsoleManager.STARTUP_TEXT, out var inputField, fontSize);
|
||||
InputField = inputField.InputField;
|
||||
m_defaultInputFieldAlpha = InputField.selectionColor.a;
|
||||
InputField.onValueChanged.AddListener(InvokeOnValueChanged);
|
||||
|
||||
var placeHolderText = InputField.placeholder.GetComponent<Text>();
|
||||
placeHolderText.fontSize = fontSize;
|
||||
|
||||
InputText = InputField.textComponent;
|
||||
InputText.supportRichText = false;
|
||||
InputText.color = new Color(1, 1, 1, 0.5f);
|
||||
|
||||
var mainTextObj = InputText.gameObject;
|
||||
var highlightTextObj = UIFactory.CreateUIObject("HighlightText", mainTextObj.gameObject);
|
||||
var highlightTextRect = highlightTextObj.GetComponent<RectTransform>();
|
||||
highlightTextRect.pivot = new Vector2(0, 1);
|
||||
highlightTextRect.anchorMin = Vector2.zero;
|
||||
highlightTextRect.anchorMax = Vector2.one;
|
||||
highlightTextRect.offsetMin = new Vector2(20, 0);
|
||||
highlightTextRect.offsetMax = new Vector2(14, 0);
|
||||
|
||||
HighlightText = highlightTextObj.AddComponent<Text>();
|
||||
HighlightText.supportRichText = true;
|
||||
HighlightText.fontSize = fontSize;
|
||||
|
||||
#endregion
|
||||
|
||||
#region COMPILE BUTTON BAR
|
||||
|
||||
var horozGroupObj = UIFactory.CreateHorizontalGroup(this.content, "BigButtons", true, true, true, true, 0, new Vector4(2, 2, 2, 2),
|
||||
new Color(1, 1, 1, 0));
|
||||
|
||||
var resetButton = UIFactory.CreateButton(horozGroupObj, "ResetButton", "Reset", new Color(0.33f, 0.33f, 0.33f));
|
||||
UIFactory.SetLayoutElement(resetButton.Button.gameObject, minHeight: 45, minWidth: 80, flexibleHeight: 0);
|
||||
resetButton.ButtonText.fontSize = 18;
|
||||
resetButton.OnClick += OnResetClicked;
|
||||
|
||||
var compileButton = UIFactory.CreateButton(horozGroupObj, "CompileButton", "Compile", new Color(0.33f, 0.5f, 0.33f));
|
||||
UIFactory.SetLayoutElement(compileButton.Button.gameObject, minHeight: 45, minWidth: 80, flexibleHeight: 0);
|
||||
compileButton.ButtonText.fontSize = 18;
|
||||
compileButton.OnClick += OnCompileClicked;
|
||||
|
||||
#endregion
|
||||
|
||||
InputText.font = UIManager.ConsoleFont;
|
||||
placeHolderText.font = UIManager.ConsoleFont;
|
||||
HighlightText.font = UIManager.ConsoleFont;
|
||||
|
||||
// reset this after formatting finalized
|
||||
highlightTextRect.anchorMin = Vector2.zero;
|
||||
highlightTextRect.anchorMax = Vector2.one;
|
||||
highlightTextRect.offsetMin = Vector2.zero;
|
||||
highlightTextRect.offsetMax = Vector2.zero;
|
||||
}
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ namespace UnityExplorer.UI.Panels
|
||||
public SceneExplorer SceneExplorer;
|
||||
public ObjectSearch ObjectSearch;
|
||||
|
||||
public override bool ShowByDefault => true;
|
||||
public override bool ShouldSaveActiveState => true;
|
||||
|
||||
public int SelectedTab = 0;
|
||||
|
@ -76,6 +76,7 @@ namespace UnityExplorer.UI.Panels
|
||||
public abstract UIManager.Panels PanelType { get; }
|
||||
public abstract string Name { get; }
|
||||
|
||||
public virtual bool ShowByDefault => false;
|
||||
public virtual bool ShouldSaveActiveState => true;
|
||||
public virtual bool CanDragAndResize => true;
|
||||
public virtual bool NavButtonWanted => true;
|
||||
@ -127,6 +128,8 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
public void ConstructUI()
|
||||
{
|
||||
//this.Enabled = true;
|
||||
|
||||
if (NavButtonWanted)
|
||||
{
|
||||
// create navbar button
|
||||
@ -143,12 +146,12 @@ namespace UnityExplorer.UI.Panels
|
||||
// create core canvas
|
||||
uiRoot = UIFactory.CreatePanel(Name, out GameObject panelContent);
|
||||
mainPanelRect = this.uiRoot.GetComponent<RectTransform>();
|
||||
content = panelContent;
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.uiRoot, true, true, true, true, 0, 2, 2, 2, 2, TextAnchor.UpperLeft);
|
||||
|
||||
int id = this.uiRoot.transform.GetInstanceID();
|
||||
transformToPanelDict.Add(id, this);
|
||||
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.uiRoot, true, true, true, true, 0, 0, 0, 0, 0, TextAnchor.UpperLeft);
|
||||
content = panelContent;
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.content, true, true, true, true, 2, 2, 2, 2, 2, TextAnchor.UpperLeft);
|
||||
|
||||
// always apply default pos and anchors (save data may only be partial)
|
||||
@ -166,10 +169,9 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
// close button
|
||||
|
||||
var closeBtn = UIFactory.CreateButton(titleGroup, "CloseButton", "X");
|
||||
var closeBtn = UIFactory.CreateButton(titleGroup, "CloseButton", "—");
|
||||
UIFactory.SetLayoutElement(closeBtn.Button.gameObject, minHeight: 25, minWidth: 25, flexibleWidth: 0);
|
||||
RuntimeProvider.Instance.SetColorBlock(closeBtn.Button, new Color(0.63f, 0.32f, 0.31f),
|
||||
new Color(0.81f, 0.25f, 0.2f), new Color(0.6f, 0.18f, 0.16f));
|
||||
RuntimeProvider.Instance.SetColorBlock(closeBtn.Button, new Color(0.33f, 0.32f, 0.31f));
|
||||
|
||||
closeBtn.OnClick += () =>
|
||||
{
|
||||
@ -186,12 +188,14 @@ namespace UnityExplorer.UI.Panels
|
||||
Dragger.OnFinishResize += OnFinishResize;
|
||||
Dragger.OnFinishDrag += OnFinishDrag;
|
||||
Dragger.AllowDragAndResize = this.CanDragAndResize;
|
||||
//Dragger.CanResize = this.CanResize;
|
||||
|
||||
// content (abstract)
|
||||
|
||||
ConstructPanelContent();
|
||||
|
||||
UIManager.SetPanelActive(this.PanelType, false);
|
||||
UIManager.SetPanelActive(this.PanelType, ShowByDefault);
|
||||
|
||||
ApplyingSaveData = true;
|
||||
// apply panel save data or revert to default
|
||||
try
|
||||
|
@ -148,6 +148,8 @@ namespace UnityExplorer.UI
|
||||
public static GameObject CreatePanel(string name, out GameObject contentHolder, Color? bgColor = null)
|
||||
{
|
||||
var panelObj = CreateUIObject(name, UIManager.PanelHolder);
|
||||
SetLayoutGroup<VerticalLayoutGroup>(panelObj, true, true, true, true);
|
||||
|
||||
var rect = panelObj.GetComponent<RectTransform>();
|
||||
rect.anchorMin = Vector2.zero;
|
||||
rect.anchorMax = Vector2.one;
|
||||
@ -155,17 +157,15 @@ namespace UnityExplorer.UI
|
||||
rect.sizeDelta = Vector2.zero;
|
||||
|
||||
var maskImg = panelObj.AddComponent<Image>();
|
||||
maskImg.color = Color.white;
|
||||
panelObj.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
SetLayoutGroup<VerticalLayoutGroup>(panelObj, true, true, true, true);
|
||||
maskImg.color = Color.black;
|
||||
panelObj.AddComponent<Mask>().showMaskGraphic = true;
|
||||
|
||||
contentHolder = CreateUIObject("Content", panelObj);
|
||||
|
||||
Image bgImage = contentHolder.AddComponent<Image>();
|
||||
bgImage.type = Image.Type.Filled;
|
||||
if (bgColor == null)
|
||||
bgImage.color = new Color(0.06f, 0.06f, 0.06f);
|
||||
bgImage.color = new Color(0.07f, 0.07f, 0.07f);
|
||||
else
|
||||
bgImage.color = (Color)bgColor;
|
||||
|
||||
@ -454,27 +454,6 @@ namespace UnityExplorer.UI
|
||||
return toggleObj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Scrollable Input Field control (custom InputFieldScroller).
|
||||
/// </summary>
|
||||
public static GameObject CreateSrollInputField(GameObject parent, string name, string placeHolderText,
|
||||
out InputFieldScroller inputScroll, int fontSize = 14, Color color = default)
|
||||
{
|
||||
if (color == default)
|
||||
color = new Color(0.15f, 0.15f, 0.15f);
|
||||
|
||||
var mainObj = CreateScrollView(parent, "InputFieldScrollView", out GameObject scrollContent, out SliderScrollbar scroller, color);
|
||||
|
||||
CreateInputField(scrollContent, name, placeHolderText ?? "...", out InputField inputField, fontSize, 0);
|
||||
|
||||
inputField.lineType = InputField.LineType.MultiLineNewline;
|
||||
inputField.targetGraphic.color = color;
|
||||
|
||||
inputScroll = new InputFieldScroller(scroller, inputField);
|
||||
|
||||
return mainObj;
|
||||
}
|
||||
|
||||
// Little helper class to force rebuild of an input field's layout on value change.
|
||||
// This is limited to once per frame per input field, so its not too expensive.
|
||||
private class InputFieldRefresher
|
||||
@ -507,6 +486,7 @@ namespace UnityExplorer.UI
|
||||
int fontSize = 14, int alignment = 3, int wrap = 0)
|
||||
{
|
||||
GameObject mainObj = CreateUIObject(name, parent);
|
||||
//SetLayoutGroup<VerticalLayoutGroup>(mainObj, true, true, true, true);
|
||||
|
||||
Image mainImage = mainObj.AddComponent<Image>();
|
||||
mainImage.type = Image.Type.Sliced;
|
||||
@ -524,10 +504,9 @@ namespace UnityExplorer.UI
|
||||
RuntimeProvider.Instance.SetColorBlock(inputField, new Color(1, 1, 1, 1),
|
||||
new Color(0.95f, 0.95f, 0.95f, 1.0f), new Color(0.78f, 0.78f, 0.78f, 1.0f));
|
||||
|
||||
SetLayoutGroup<VerticalLayoutGroup>(mainObj, true, true, true, true);
|
||||
|
||||
GameObject textArea = CreateUIObject("TextArea", mainObj);
|
||||
textArea.AddComponent<RectMask2D>();
|
||||
//textArea.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
RectTransform textAreaRect = textArea.GetComponent<RectTransform>();
|
||||
textAreaRect.anchorMin = Vector2.zero;
|
||||
@ -552,7 +531,7 @@ namespace UnityExplorer.UI
|
||||
placeHolderRect.offsetMin = Vector2.zero;
|
||||
placeHolderRect.offsetMax = Vector2.zero;
|
||||
|
||||
SetLayoutElement(placeHolderObj, minWidth: 500, flexibleWidth: 5000);
|
||||
SetLayoutElement(placeHolderObj, minWidth: 200, flexibleWidth: 5000);
|
||||
|
||||
inputField.placeholder = placeholderText;
|
||||
|
||||
@ -571,7 +550,7 @@ namespace UnityExplorer.UI
|
||||
inputTextRect.offsetMin = Vector2.zero;
|
||||
inputTextRect.offsetMax = Vector2.zero;
|
||||
|
||||
SetLayoutElement(inputTextObj, minWidth: 500, flexibleWidth: 5000);
|
||||
SetLayoutElement(inputTextObj, minWidth: 200, flexibleWidth: 5000);
|
||||
|
||||
inputField.textComponent = inputText;
|
||||
|
||||
@ -773,73 +752,130 @@ namespace UnityExplorer.UI
|
||||
SetLayoutElement(sliderContainer, minWidth: 25, flexibleWidth:0, flexibleHeight: 9999);
|
||||
sliderContainer.AddComponent<Mask>();
|
||||
|
||||
var sliderObj = SliderScrollbar.CreateSliderScrollbar(sliderContainer, out Slider slider);
|
||||
slider.direction = Slider.Direction.TopToBottom;
|
||||
SetLayoutElement(sliderObj, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
|
||||
CreateSliderScrollbar(sliderContainer, out Slider slider);
|
||||
|
||||
RuntimeProvider.Instance.SetColorBlock(slider, disabled: new Color(0.1f, 0.1f, 0.1f));
|
||||
|
||||
slider.handleRect.offsetMin = new Vector2(slider.handleRect.offsetMin.x, 0);
|
||||
slider.handleRect.offsetMax = new Vector2(slider.handleRect.offsetMax.x, 0);
|
||||
slider.handleRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
|
||||
var container = slider.m_HandleContainerRect;
|
||||
container.anchorMin = Vector3.zero;
|
||||
container.anchorMax = Vector3.one;
|
||||
container.pivot = new Vector3(0.5f, 0.5f);
|
||||
|
||||
// finalize and create ScrollPool
|
||||
|
||||
uiRoot = mainObj;
|
||||
var scrollPool = new ScrollPool<T>(scrollRect);
|
||||
|
||||
//viewportObj.GetComponent<Mask>().enabled = false;
|
||||
|
||||
return scrollPool;
|
||||
}
|
||||
|
||||
public static GameObject CreateSliderScrollbar(GameObject parent, out Slider slider)
|
||||
{
|
||||
GameObject mainObj = CreateUIObject("SliderScrollbar", parent, UIFactory._smallElementSize);
|
||||
|
||||
GameObject bgImageObj = CreateUIObject("Background", mainObj);
|
||||
GameObject handleSlideAreaObj = CreateUIObject("Handle Slide Area", mainObj);
|
||||
GameObject handleObj = CreateUIObject("Handle", handleSlideAreaObj);
|
||||
|
||||
Image bgImage = bgImageObj.AddComponent<Image>();
|
||||
bgImage.type = Image.Type.Sliced;
|
||||
bgImage.color = new Color(0.05f, 0.05f, 0.05f, 1.0f);
|
||||
|
||||
RectTransform bgRect = bgImageObj.GetComponent<RectTransform>();
|
||||
bgRect.pivot = new Vector2(0, 1);
|
||||
bgRect.anchorMin = Vector2.zero;
|
||||
bgRect.anchorMax = Vector2.one;
|
||||
bgRect.sizeDelta = Vector2.zero;
|
||||
bgRect.offsetMax = new Vector2(0f, 0f);
|
||||
|
||||
RectTransform handleSlideRect = handleSlideAreaObj.GetComponent<RectTransform>();
|
||||
handleSlideRect.anchorMin = Vector3.zero;
|
||||
handleSlideRect.anchorMax = Vector3.one;
|
||||
handleSlideRect.pivot = new Vector3(0.5f, 0.5f);
|
||||
|
||||
Image handleImage = handleObj.AddComponent<Image>();
|
||||
handleImage.color = new Color(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
|
||||
var handleRect = handleObj.GetComponent<RectTransform>();
|
||||
handleRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
UIFactory.SetLayoutElement(handleObj, minWidth: 21, flexibleWidth: 0);
|
||||
|
||||
var sliderBarLayout = mainObj.AddComponent<LayoutElement>();
|
||||
sliderBarLayout.minWidth = 25;
|
||||
sliderBarLayout.flexibleWidth = 0;
|
||||
sliderBarLayout.minHeight = 30;
|
||||
sliderBarLayout.flexibleHeight = 5000;
|
||||
|
||||
slider = mainObj.AddComponent<Slider>();
|
||||
slider.handleRect = handleObj.GetComponent<RectTransform>();
|
||||
slider.targetGraphic = handleImage;
|
||||
slider.direction = Slider.Direction.TopToBottom;
|
||||
|
||||
SetLayoutElement(mainObj, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
|
||||
|
||||
RuntimeProvider.Instance.SetColorBlock(slider,
|
||||
new Color(0.4f, 0.4f, 0.4f),
|
||||
new Color(0.5f, 0.5f, 0.5f),
|
||||
new Color(0.3f, 0.3f, 0.3f),
|
||||
new Color(0.5f, 0.5f, 0.5f));
|
||||
|
||||
return mainObj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a ScrollView element.
|
||||
/// </summary>
|
||||
public static GameObject CreateScrollView(GameObject parent, string name, out GameObject content, out SliderScrollbar scroller,
|
||||
public static GameObject CreateAutoScrollView(GameObject parent, string name, out GameObject content, out AutoSliderScrollbar autoScrollbar,
|
||||
Color color = default)
|
||||
{
|
||||
GameObject mainObj = CreateUIObject("DynamicScrollView", parent);
|
||||
|
||||
GameObject mainObj = CreateUIObject(name, parent);
|
||||
SetLayoutElement(mainObj, minWidth: 100, minHeight: 30, flexibleWidth: 5000, flexibleHeight: 5000);
|
||||
|
||||
SetLayoutGroup<HorizontalLayoutGroup>(mainObj, false, true, true, true, 2);
|
||||
Image mainImage = mainObj.AddComponent<Image>();
|
||||
mainImage.type = Image.Type.Filled;
|
||||
mainImage.color = (color == default) ? new Color(0.3f, 0.3f, 0.3f, 1f) : color;
|
||||
|
||||
GameObject viewportObj = CreateUIObject("Viewport", mainObj);
|
||||
|
||||
UIFactory.SetLayoutElement(viewportObj, minWidth: 1, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||
var viewportRect = viewportObj.GetComponent<RectTransform>();
|
||||
viewportRect.anchorMin = Vector2.zero;
|
||||
viewportRect.anchorMax = Vector2.one;
|
||||
viewportRect.pivot = new Vector2(0.0f, 1.0f);
|
||||
viewportRect.sizeDelta = new Vector2(-15.0f, 0.0f);
|
||||
viewportRect.offsetMax = new Vector2(-20.0f, 0.0f);
|
||||
|
||||
//viewportRect.sizeDelta = new Vector2(-15.0f, 0.0f);
|
||||
//viewportRect.offsetMax = new Vector2(-25.0f, 0.0f);
|
||||
viewportObj.AddComponent<Image>().color = Color.white;
|
||||
viewportObj.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
content = CreateUIObject("Content", viewportObj);
|
||||
var contentRect = content.GetComponent<RectTransform>();
|
||||
SetLayoutGroup<VerticalLayoutGroup>(content, true, true, true, true);//, 5, 5, 5, 5, 5);
|
||||
contentRect.anchorMin = new Vector2(0.0f, 1.0f);
|
||||
contentRect.anchorMax = new Vector2(1.0f, 1.0f);
|
||||
contentRect.pivot = new Vector2(0.0f, 1.0f);
|
||||
contentRect.sizeDelta = new Vector2(5f, 0f);
|
||||
contentRect.offsetMax = new Vector2(0f, 0f);
|
||||
var contentFitter = content.AddComponent<ContentSizeFitter>();
|
||||
contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
//contentRect.sizeDelta = new Vector2(5f, 0f);
|
||||
//contentRect.offsetMax = new Vector2(0f, 0f);
|
||||
|
||||
SetLayoutGroup<VerticalLayoutGroup>(content, true, true, true, true, 5, 5, 5, 5, 5);
|
||||
// Slider
|
||||
|
||||
CreateSliderScrollbar(mainObj, out scroller, out Scrollbar hiddenScrollbar);
|
||||
GameObject scrollBarObj = CreateUIObject("AutoSliderScrollbar", mainObj);
|
||||
SetLayoutGroup<VerticalLayoutGroup>(scrollBarObj, true, true, true, true);
|
||||
SetLayoutElement(scrollBarObj, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
|
||||
scrollBarObj.AddComponent<Image>().color = Color.white;
|
||||
scrollBarObj.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
// Back to the main scrollview ScrollRect, setting it up now that we have all references.
|
||||
GameObject hiddenBar = CreateScrollbar(scrollBarObj, "HiddenScrollviewScroller", out var hiddenScrollbar);
|
||||
hiddenScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);
|
||||
|
||||
for (int i = 0; i < hiddenBar.transform.childCount; i++)
|
||||
{
|
||||
var child = hiddenBar.transform.GetChild(i);
|
||||
child.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
CreateSliderScrollbar(scrollBarObj, out Slider scrollSlider);
|
||||
|
||||
autoScrollbar = new AutoSliderScrollbar(hiddenScrollbar, scrollSlider, contentRect, viewportRect);
|
||||
|
||||
//var sliderContainer = autoScrollbar.Slider.m_HandleContainerRect.gameObject;
|
||||
//SetLayoutElement(sliderContainer, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
|
||||
//sliderContainer.AddComponent<Mask>();
|
||||
|
||||
// Set up the ScrollRect component
|
||||
|
||||
var scrollRect = mainObj.AddComponent<ScrollRect>();
|
||||
scrollRect.horizontal = false;
|
||||
@ -856,21 +892,54 @@ namespace UnityExplorer.UI
|
||||
return mainObj;
|
||||
}
|
||||
|
||||
public static GameObject CreateSliderScrollbar(GameObject mainObj, out SliderScrollbar scroller, out Scrollbar hiddenScrollbar)
|
||||
|
||||
/// <summary>
|
||||
/// Create a Scrollable Input Field control (custom InputFieldScroller).
|
||||
/// </summary>
|
||||
public static GameObject CreateSrollInputField(GameObject parent, string name, string placeHolderText, out InputFieldScroller inputScroll,
|
||||
int fontSize = 14, Color color = default)
|
||||
{
|
||||
GameObject scrollBarObj = CreateUIObject("DynamicScrollbar", mainObj);
|
||||
if (color == default)
|
||||
color = new Color(0.12f, 0.12f, 0.12f);
|
||||
|
||||
var scrollbarLayout = scrollBarObj.AddComponent<VerticalLayoutGroup>();
|
||||
scrollbarLayout.childForceExpandHeight = true;
|
||||
scrollbarLayout.SetChildControlHeight(true);
|
||||
GameObject mainObj = CreateUIObject(name, parent);
|
||||
SetLayoutElement(mainObj, minWidth: 100, minHeight: 30, flexibleWidth: 5000, flexibleHeight: 5000);
|
||||
SetLayoutGroup<HorizontalLayoutGroup>(mainObj, false, true, true, true, 2);
|
||||
Image mainImage = mainObj.AddComponent<Image>();
|
||||
mainImage.type = Image.Type.Filled;
|
||||
mainImage.color = (color == default) ? new Color(0.3f, 0.3f, 0.3f, 1f) : color;
|
||||
|
||||
RectTransform scrollBarRect = scrollBarObj.GetComponent<RectTransform>();
|
||||
scrollBarRect.anchorMin = new Vector2(1.0f, 0.0f);
|
||||
scrollBarRect.anchorMax = new Vector2(1.0f, 1.0f);
|
||||
scrollBarRect.sizeDelta = new Vector2(15.0f, 0.0f);
|
||||
scrollBarRect.offsetMin = new Vector2(-15.0f, 0.0f);
|
||||
GameObject viewportObj = CreateUIObject("Viewport", mainObj);
|
||||
SetLayoutElement(viewportObj, minWidth: 1, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||
var viewportRect = viewportObj.GetComponent<RectTransform>();
|
||||
viewportRect.anchorMin = Vector2.zero;
|
||||
viewportRect.anchorMax = Vector2.one;
|
||||
viewportRect.pivot = new Vector2(0.0f, 1.0f);
|
||||
viewportObj.AddComponent<Image>().color = Color.white;
|
||||
viewportObj.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
GameObject hiddenBar = CreateScrollbar(scrollBarObj, "HiddenScrollviewScroller", out hiddenScrollbar);
|
||||
// Input Field
|
||||
|
||||
var content = CreateInputField(viewportObj, name, placeHolderText ?? "...", out InputField inputField, fontSize, 0);
|
||||
SetLayoutElement(content, flexibleHeight: 9999, flexibleWidth: 9999);
|
||||
var contentRect = content.GetComponent<RectTransform>();
|
||||
contentRect.pivot = new Vector2(0, 1);
|
||||
contentRect.anchorMin = new Vector2(0, 1);
|
||||
contentRect.anchorMax = new Vector2(1, 1);
|
||||
contentRect.offsetMin = new Vector2(2, 0);
|
||||
contentRect.offsetMax = new Vector2(2, 0);
|
||||
inputField.lineType = InputField.LineType.MultiLineNewline;
|
||||
inputField.targetGraphic.color = color;
|
||||
|
||||
// Slider
|
||||
|
||||
GameObject scrollBarObj = CreateUIObject("AutoSliderScrollbar", mainObj);
|
||||
SetLayoutGroup<VerticalLayoutGroup>(scrollBarObj, true, true, true, true);
|
||||
SetLayoutElement(scrollBarObj, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
|
||||
scrollBarObj.AddComponent<Image>().color = Color.white;
|
||||
scrollBarObj.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
GameObject hiddenBar = CreateScrollbar(scrollBarObj, "HiddenScrollviewScroller", out var hiddenScrollbar);
|
||||
hiddenScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);
|
||||
|
||||
for (int i = 0; i < hiddenBar.transform.childCount; i++)
|
||||
@ -879,11 +948,37 @@ namespace UnityExplorer.UI
|
||||
child.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
SliderScrollbar.CreateSliderScrollbar(scrollBarObj, out Slider scrollSlider);
|
||||
CreateSliderScrollbar(scrollBarObj, out Slider scrollSlider);
|
||||
|
||||
scroller = new SliderScrollbar(hiddenScrollbar, scrollSlider);
|
||||
// Set up the AutoSliderScrollbar module
|
||||
|
||||
return scrollBarObj;
|
||||
var autoScroller = new AutoSliderScrollbar(hiddenScrollbar, scrollSlider, contentRect, viewportRect);
|
||||
|
||||
var sliderContainer = autoScroller.Slider.m_HandleContainerRect.gameObject;
|
||||
SetLayoutElement(sliderContainer, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
|
||||
sliderContainer.AddComponent<Mask>();
|
||||
|
||||
// Set up the InputFieldScroller module
|
||||
|
||||
inputScroll = new InputFieldScroller(autoScroller, inputField);
|
||||
inputScroll.ProcessInputText();
|
||||
|
||||
// Set up the ScrollRect component
|
||||
|
||||
var scrollRect = mainObj.AddComponent<ScrollRect>();
|
||||
scrollRect.horizontal = false;
|
||||
scrollRect.vertical = true;
|
||||
scrollRect.verticalScrollbar = hiddenScrollbar;
|
||||
scrollRect.movementType = ScrollRect.MovementType.Clamped;
|
||||
scrollRect.scrollSensitivity = 35;
|
||||
scrollRect.horizontalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
|
||||
scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.Permanent;
|
||||
|
||||
scrollRect.viewport = viewportRect;
|
||||
scrollRect.content = contentRect;
|
||||
|
||||
|
||||
return mainObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ namespace UnityExplorer.UI
|
||||
|
||||
public static ObjectExplorer Explorer { get; private set; }
|
||||
public static InspectorPanel Inspector { get; private set; }
|
||||
public static CSConsolePanel CSConsole { get; private set; }
|
||||
|
||||
public static AutoCompleter AutoCompleter { get; private set; }
|
||||
|
||||
// other
|
||||
@ -50,6 +52,23 @@ namespace UnityExplorer.UI
|
||||
internal static readonly Color navButtonEnabledColor = new Color(0.2f, 0.4f, 0.28f);
|
||||
internal static readonly Color navButtonDisabledColor = new Color(0.25f, 0.25f, 0.25f);
|
||||
|
||||
public static UIPanel GetPanel(Panels panel)
|
||||
{
|
||||
switch (panel)
|
||||
{
|
||||
case Panels.ObjectExplorer:
|
||||
return Explorer;
|
||||
case Panels.Inspector:
|
||||
return Inspector;
|
||||
case Panels.AutoCompleter:
|
||||
return AutoCompleter;
|
||||
case Panels.CSConsole:
|
||||
return CSConsole;
|
||||
default:
|
||||
throw new NotImplementedException($"TODO GetPanel: {panel}");
|
||||
}
|
||||
}
|
||||
|
||||
// main menu toggle
|
||||
public static bool ShowMenu
|
||||
{
|
||||
@ -96,21 +115,6 @@ namespace UnityExplorer.UI
|
||||
AutoCompleter.Update();
|
||||
}
|
||||
|
||||
public static UIPanel GetPanel(Panels panel)
|
||||
{
|
||||
switch (panel)
|
||||
{
|
||||
case Panels.ObjectExplorer:
|
||||
return Explorer;
|
||||
case Panels.Inspector:
|
||||
return Inspector;
|
||||
case Panels.AutoCompleter:
|
||||
return AutoCompleter;
|
||||
default:
|
||||
throw new NotImplementedException($"TODO GetPanel: {panel}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void TogglePanel(Panels panel)
|
||||
{
|
||||
var uiPanel = GetPanel(panel);
|
||||
@ -153,17 +157,20 @@ namespace UnityExplorer.UI
|
||||
|
||||
CreateTopNavBar();
|
||||
|
||||
//InspectUnderMouse.ConstructUI();
|
||||
|
||||
AutoCompleter = new AutoCompleter();
|
||||
AutoCompleter.ConstructUI();
|
||||
|
||||
//InspectUnderMouse.ConstructUI();
|
||||
|
||||
Explorer = new ObjectExplorer();
|
||||
Explorer.ConstructUI();
|
||||
|
||||
Inspector = new InspectorPanel();
|
||||
Inspector.ConstructUI();
|
||||
|
||||
CSConsole = new CSConsolePanel();
|
||||
CSConsole.ConstructUI();
|
||||
|
||||
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
|
||||
|
||||
ExplorerCore.Log("UI initialized.");
|
||||
|
138
src/UI/Widgets/AutoSliderScrollbar.cs
Normal file
138
src/UI/Widgets/AutoSliderScrollbar.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Models;
|
||||
|
||||
namespace UnityExplorer.UI.Utility
|
||||
{
|
||||
// A Slider Scrollbar which automatically resizes for the content size (no pooling).
|
||||
// Currently just used for the C# Console input field.
|
||||
|
||||
public class AutoSliderScrollbar : UIBehaviourModel
|
||||
{
|
||||
public override GameObject UIRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Slider)
|
||||
return Slider.gameObject;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//public event Action<float> OnValueChanged;
|
||||
|
||||
internal readonly Scrollbar Scrollbar;
|
||||
internal readonly Slider Slider;
|
||||
internal RectTransform ContentRect;
|
||||
internal RectTransform ViewportRect;
|
||||
|
||||
//internal InputFieldScroller m_parentInputScroller;
|
||||
|
||||
public AutoSliderScrollbar(Scrollbar scrollbar, Slider slider, RectTransform contentRect, RectTransform viewportRect)
|
||||
{
|
||||
this.Scrollbar = scrollbar;
|
||||
this.Slider = slider;
|
||||
this.ContentRect = contentRect;
|
||||
this.ViewportRect = viewportRect;
|
||||
|
||||
this.Scrollbar.onValueChanged.AddListener(this.OnScrollbarValueChanged);
|
||||
this.Slider.onValueChanged.AddListener(this.OnSliderValueChanged);
|
||||
|
||||
//this.RefreshVisibility();
|
||||
this.Slider.Set(0f, false);
|
||||
}
|
||||
|
||||
private float lastAnchorPosition;
|
||||
private float lastContentHeight;
|
||||
private float lastViewportHeight;
|
||||
private bool _refreshWanted;
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
_refreshWanted = false;
|
||||
if (ContentRect.localPosition.y != lastAnchorPosition)
|
||||
{
|
||||
lastAnchorPosition = ContentRect.localPosition.y;
|
||||
_refreshWanted = true;
|
||||
}
|
||||
if (ContentRect.rect.height != lastContentHeight)
|
||||
{
|
||||
lastContentHeight = ContentRect.rect.height;
|
||||
_refreshWanted = true;
|
||||
}
|
||||
if (ViewportRect.rect.height != lastViewportHeight)
|
||||
{
|
||||
lastViewportHeight = ViewportRect.rect.height;
|
||||
_refreshWanted = true;
|
||||
}
|
||||
|
||||
if (_refreshWanted)
|
||||
UpdateSliderHandle();
|
||||
}
|
||||
|
||||
public void UpdateSliderHandle()
|
||||
{
|
||||
// calculate handle size based on viewport / total data height
|
||||
var totalHeight = ContentRect.rect.height;
|
||||
var viewportHeight = ViewportRect.rect.height;
|
||||
|
||||
if (totalHeight <= viewportHeight)
|
||||
{
|
||||
Slider.value = 0f;
|
||||
Slider.interactable = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var handleHeight = viewportHeight * Math.Min(1, viewportHeight / totalHeight);
|
||||
handleHeight = Math.Max(15f, handleHeight);
|
||||
|
||||
// resize the handle container area for the size of the handle (bigger handle = smaller container)
|
||||
var container = Slider.m_HandleContainerRect;
|
||||
container.offsetMax = new Vector2(container.offsetMax.x, -(handleHeight * 0.5f));
|
||||
container.offsetMin = new Vector2(container.offsetMin.x, handleHeight * 0.5f);
|
||||
|
||||
// set handle size
|
||||
Slider.handleRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, handleHeight);
|
||||
|
||||
// if slider is 100% height then make it not interactable
|
||||
Slider.interactable = !Mathf.Approximately(handleHeight, viewportHeight);
|
||||
|
||||
float val = 0f;
|
||||
if (totalHeight > 0f)
|
||||
val = (float)((decimal)ContentRect.localPosition.y / (decimal)(totalHeight - ViewportRect.rect.height));
|
||||
|
||||
Slider.value = val;
|
||||
}
|
||||
|
||||
public void OnScrollbarValueChanged(float value)
|
||||
{
|
||||
value = 1f - value;
|
||||
if (this.Slider.value != value)
|
||||
this.Slider.Set(value, false);
|
||||
//OnValueChanged?.Invoke(value);
|
||||
}
|
||||
|
||||
public void OnSliderValueChanged(float value)
|
||||
{
|
||||
value = 1f - value;
|
||||
this.Scrollbar.value = value;
|
||||
//OnValueChanged?.Invoke(value);
|
||||
}
|
||||
|
||||
public override void ConstructUI(GameObject parent)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -19,106 +19,93 @@ namespace UnityExplorer.UI.Utility
|
||||
{
|
||||
get
|
||||
{
|
||||
if (inputField)
|
||||
return inputField.gameObject;
|
||||
if (InputField)
|
||||
return InputField.gameObject;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal SliderScrollbar sliderScroller;
|
||||
internal InputField inputField;
|
||||
internal AutoSliderScrollbar Slider;
|
||||
internal InputField InputField;
|
||||
|
||||
internal RectTransform inputRect;
|
||||
internal LayoutElement layoutElement;
|
||||
internal VerticalLayoutGroup parentLayoutGroup;
|
||||
internal RectTransform ContentRect;
|
||||
internal RectTransform ViewportRect;
|
||||
|
||||
internal static CanvasScaler canvasScaler;
|
||||
internal static CanvasScaler RootScaler;
|
||||
|
||||
public InputFieldScroller(SliderScrollbar sliderScroller, InputField inputField)
|
||||
public InputFieldScroller(AutoSliderScrollbar sliderScroller, InputField inputField)
|
||||
{
|
||||
//Instances.Add(this);
|
||||
|
||||
this.sliderScroller = sliderScroller;
|
||||
this.inputField = inputField;
|
||||
|
||||
sliderScroller.m_parentInputScroller = this;
|
||||
this.Slider = sliderScroller;
|
||||
this.InputField = inputField;
|
||||
|
||||
inputField.onValueChanged.AddListener(OnTextChanged);
|
||||
|
||||
inputRect = inputField.GetComponent<RectTransform>();
|
||||
layoutElement = inputField.gameObject.AddComponent<LayoutElement>();
|
||||
parentLayoutGroup = inputField.transform.parent.GetComponent<VerticalLayoutGroup>();
|
||||
ContentRect = inputField.GetComponent<RectTransform>();
|
||||
ViewportRect = ContentRect.transform.parent.GetComponent<RectTransform>();
|
||||
|
||||
layoutElement.minHeight = 25;
|
||||
layoutElement.minWidth = 100;
|
||||
|
||||
if (!canvasScaler)
|
||||
canvasScaler = UIManager.CanvasRoot.GetComponent<CanvasScaler>();
|
||||
if (!RootScaler)
|
||||
RootScaler = UIManager.CanvasRoot.GetComponent<CanvasScaler>();
|
||||
}
|
||||
|
||||
internal string m_lastText;
|
||||
internal bool m_updateWanted;
|
||||
|
||||
// only done once, to fix height on creation.
|
||||
internal bool heightInitAfterLayout;
|
||||
internal bool m_wantJumpToBottom;
|
||||
private float m_desiredContentHeight;
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (!heightInitAfterLayout)
|
||||
{
|
||||
heightInitAfterLayout = true;
|
||||
var height = sliderScroller.m_scrollRect.parent.parent.GetComponent<RectTransform>().rect.height;
|
||||
layoutElement.preferredHeight = height;
|
||||
}
|
||||
|
||||
if (m_updateWanted && inputField.gameObject.activeInHierarchy)
|
||||
if (m_updateWanted)
|
||||
{
|
||||
m_updateWanted = false;
|
||||
RefreshUI();
|
||||
ProcessInputText();
|
||||
}
|
||||
|
||||
float desiredHeight = Math.Max(m_desiredContentHeight, ViewportRect.rect.height);
|
||||
|
||||
if (ContentRect.rect.height < desiredHeight)
|
||||
{
|
||||
ContentRect.sizeDelta = new Vector2(0, desiredHeight);
|
||||
this.Slider.UpdateSliderHandle();
|
||||
}
|
||||
else if (ContentRect.rect.height > desiredHeight)
|
||||
{
|
||||
ContentRect.sizeDelta = new Vector2(0, desiredHeight);
|
||||
this.Slider.UpdateSliderHandle();
|
||||
}
|
||||
|
||||
if (m_wantJumpToBottom)
|
||||
{
|
||||
Slider.Slider.value = 1f;
|
||||
m_wantJumpToBottom = false;
|
||||
}
|
||||
}
|
||||
|
||||
//internal bool CheckDestroyed()
|
||||
//{
|
||||
// if (sliderScroller == null || sliderScroller.CheckDestroyed())
|
||||
// {
|
||||
// Instances.Remove(this);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// return false;
|
||||
//}
|
||||
|
||||
internal void OnTextChanged(string text)
|
||||
{
|
||||
m_lastText = text;
|
||||
m_updateWanted = true;
|
||||
}
|
||||
|
||||
internal void RefreshUI()
|
||||
internal void ProcessInputText()
|
||||
{
|
||||
var curInputRect = inputField.textComponent.rectTransform.rect;
|
||||
var scaleFactor = canvasScaler.scaleFactor;
|
||||
var curInputRect = InputField.textComponent.rectTransform.rect;
|
||||
var scaleFactor = RootScaler.scaleFactor;
|
||||
|
||||
// Current text settings
|
||||
var texGenSettings = inputField.textComponent.GetGenerationSettings(curInputRect.size);
|
||||
var texGenSettings = InputField.textComponent.GetGenerationSettings(curInputRect.size);
|
||||
texGenSettings.generateOutOfBounds = false;
|
||||
texGenSettings.scaleFactor = scaleFactor;
|
||||
|
||||
// Preferred text rect height
|
||||
var textGen = inputField.textComponent.cachedTextGeneratorForLayout;
|
||||
float preferredHeight = textGen.GetPreferredHeight(m_lastText, texGenSettings) + 10;
|
||||
var textGen = InputField.textComponent.cachedTextGeneratorForLayout;
|
||||
m_desiredContentHeight = textGen.GetPreferredHeight(m_lastText, texGenSettings) + 10;
|
||||
|
||||
// Default text rect height (fit to scroll parent or expand to fit text)
|
||||
float minHeight = Mathf.Max(preferredHeight, sliderScroller.m_scrollRect.rect.height - 25);
|
||||
|
||||
layoutElement.preferredHeight = minHeight;
|
||||
|
||||
if (inputField.caretPosition == inputField.text.Length
|
||||
&& inputField.text.Length > 0
|
||||
&& inputField.text[inputField.text.Length - 1] == '\n')
|
||||
// jump to bottom
|
||||
if (InputField.caretPosition == InputField.text.Length
|
||||
&& InputField.text.Length > 0
|
||||
&& InputField.text[InputField.text.Length - 1] == '\n')
|
||||
{
|
||||
sliderScroller.m_slider.value = 0f;
|
||||
m_wantJumpToBottom = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
// our first cell and they take priority, so reduce our height by
|
||||
// (minHeight - remainder) to account for that. We need to fill that
|
||||
// gap and reach the next cell before we take priority.
|
||||
if (!Mathf.Approximately(rem, 0f))
|
||||
if (rem != 0.0f)
|
||||
height -= (DefaultHeight - rem);
|
||||
|
||||
return (int)Math.Ceiling((decimal)height / (decimal)DefaultHeight);
|
||||
@ -139,8 +139,11 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
if (dataIndex >= ScrollPool.DataSource.ItemCount)
|
||||
{
|
||||
while (heightCache.Count > dataIndex)
|
||||
RemoveLast();
|
||||
if (heightCache.Count > dataIndex)
|
||||
{
|
||||
while (heightCache.Count > dataIndex)
|
||||
RemoveLast();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -249,14 +252,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//// if sister cache is set, then update it too.
|
||||
//if (SisterCache != null)
|
||||
//{
|
||||
// var realIdx = ScrollPool.DataSource.GetRealIndexOfTempIndex(dataIndex);
|
||||
// if (realIdx >= 0)
|
||||
// SisterCache.SetIndex(realIdx, height, true);
|
||||
//}
|
||||
}
|
||||
|
||||
private void RebuildCache()
|
||||
|
@ -115,7 +115,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
if (writingLocked && timeofLastWriteLock < Time.time)
|
||||
writingLocked = false;
|
||||
|
||||
if (prevContentHeight <= 1f && Content?.rect.height > 1f)
|
||||
if (prevContentHeight <= 1f && Content.rect.height > 1f)
|
||||
{
|
||||
prevContentHeight = Content.rect.height;
|
||||
}
|
||||
@ -131,10 +131,12 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
public void Rebuild()
|
||||
{
|
||||
HeightCache = new DataHeightCache<T>(this);
|
||||
|
||||
SetRecycleViewBounds(false);
|
||||
SetScrollBounds();
|
||||
|
||||
ExtendCellPool();
|
||||
CheckExtendCellPool();
|
||||
writingLocked = false;
|
||||
Content.anchoredPosition = Vector2.zero;
|
||||
UpdateSliderHandle(true);
|
||||
@ -163,6 +165,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
// Initialize
|
||||
|
||||
private bool m_doneFirstInit;
|
||||
private bool m_initialized;
|
||||
|
||||
public void Initialize(IPoolDataSource<T> dataSource)
|
||||
@ -173,12 +176,16 @@ namespace UnityExplorer.UI.Widgets
|
||||
HeightCache = new DataHeightCache<T>(this);
|
||||
DataSource = dataSource;
|
||||
|
||||
this.contentLayout = ScrollRect.content.GetComponent<VerticalLayoutGroup>();
|
||||
this.slider = ScrollRect.GetComponentInChildren<Slider>();
|
||||
slider.onValueChanged.AddListener(OnSliderValueChanged);
|
||||
if (!m_doneFirstInit)
|
||||
{
|
||||
m_doneFirstInit = true;
|
||||
this.contentLayout = ScrollRect.content.GetComponent<VerticalLayoutGroup>();
|
||||
this.slider = ScrollRect.GetComponentInChildren<Slider>();
|
||||
slider.onValueChanged.AddListener(OnSliderValueChanged);
|
||||
|
||||
ScrollRect.vertical = true;
|
||||
ScrollRect.horizontal = false;
|
||||
ScrollRect.vertical = true;
|
||||
ScrollRect.horizontal = false;
|
||||
}
|
||||
|
||||
ScrollRect.onValueChanged.RemoveListener(OnValueChangedListener);
|
||||
RuntimeProvider.Instance.StartCoroutine(InitCoroutine());
|
||||
@ -224,12 +231,18 @@ namespace UnityExplorer.UI.Widgets
|
||||
}
|
||||
CellPool.Clear();
|
||||
}
|
||||
|
||||
bottomDataIndex = -1;
|
||||
topPoolIndex = 0;
|
||||
bottomPoolIndex = 0;
|
||||
}
|
||||
|
||||
private void CreateCellPool(bool andResetDataIndex = true)
|
||||
private void CreateCellPool()
|
||||
{
|
||||
ReturnCells();
|
||||
|
||||
CheckDataSourceCountChange(out _);
|
||||
|
||||
float currentPoolCoverage = 0f;
|
||||
float requiredCoverage = ScrollRect.viewport.rect.height + RecycleThreshold;
|
||||
|
||||
@ -250,8 +263,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
currentPoolCoverage += PrototypeHeight;
|
||||
}
|
||||
|
||||
if (andResetDataIndex)
|
||||
bottomDataIndex = CellPool.Count - 1;
|
||||
bottomDataIndex = CellPool.Count - 1;
|
||||
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
|
||||
|
||||
@ -266,13 +278,12 @@ namespace UnityExplorer.UI.Widgets
|
||||
RecycleViewBounds = new Vector2(Viewport.MinY() + HalfThreshold, Viewport.MaxY() - HalfThreshold);
|
||||
|
||||
if (extendPoolIfGrown && prevViewportHeight < Viewport.rect.height && prevViewportHeight != 0.0f)
|
||||
ExtendCellPool();
|
||||
CheckExtendCellPool();
|
||||
|
||||
prevViewportHeight = Viewport.rect.height;
|
||||
|
||||
}
|
||||
|
||||
private bool ExtendCellPool()
|
||||
private bool CheckExtendCellPool()
|
||||
{
|
||||
CheckDataSourceCountChange(out _);
|
||||
|
||||
@ -285,6 +296,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
bottomDataIndex += cellsRequired;
|
||||
|
||||
// TODO sometimes still jumps a litte bit, need to figure out why.
|
||||
float prevAnchor = Content.localPosition.y;
|
||||
float prevHeight = Content.rect.height;
|
||||
|
||||
@ -299,21 +311,29 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
int index = CellPool.Count - 1 - (topPoolIndex % (CellPool.Count - 1));
|
||||
cell.Rect.SetSiblingIndex(index);
|
||||
|
||||
if (bottomPoolIndex == index - 1)
|
||||
bottomPoolIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
RefreshCells(true);
|
||||
|
||||
//ExplorerCore.Log("Anchor: " + Content.localPosition.y + ", prev: " + prevAnchor);
|
||||
//ExplorerCore.Log("Height: " + Content.rect.height + ", prev:" + prevHeight);
|
||||
|
||||
if (Content.localPosition.y != prevAnchor)
|
||||
{
|
||||
var diff = Content.localPosition.y - prevAnchor;
|
||||
Content.localPosition = new Vector3(Content.localPosition.x, Content.localPosition.y - diff);
|
||||
}
|
||||
|
||||
ScrollRect.UpdatePrevData();
|
||||
|
||||
SetScrollBounds();
|
||||
UpdateSliderHandle(true);
|
||||
if (Content.rect.height != prevHeight)
|
||||
{
|
||||
var diff = Content.rect.height - prevHeight;
|
||||
//ExplorerCore.Log("Height diff: " + diff);
|
||||
//Content.localPosition = new Vector3(Content.localPosition.x, Content.localPosition.y - diff);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -413,6 +433,16 @@ namespace UnityExplorer.UI.Widgets
|
||||
ScrollRect.UpdatePrevData();
|
||||
}
|
||||
|
||||
private void RefreshCellHeightsFast()
|
||||
{
|
||||
var enumerator = GetPoolEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var curr = enumerator.Current;
|
||||
HeightCache.SetIndex(curr.dataIndex, CellPool[curr.cellIndex].Rect.rect.height);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetCell(T cachedCell, int dataIndex)
|
||||
{
|
||||
cachedCell.Enable();
|
||||
@ -432,6 +462,8 @@ namespace UnityExplorer.UI.Widgets
|
||||
if (InputManager.MouseScrollDelta != Vector2.zero)
|
||||
ScrollRect.StopMovement();
|
||||
|
||||
RefreshCellHeightsFast();
|
||||
|
||||
SetRecycleViewBounds(true);
|
||||
|
||||
float yChange = ((Vector2)ScrollRect.content.localPosition - prevAnchoredPos).y;
|
||||
@ -458,7 +490,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
SetScrollBounds();
|
||||
|
||||
//WritingLocked = true;
|
||||
UpdateSliderHandle();
|
||||
UpdateSliderHandle(true);
|
||||
}
|
||||
|
||||
private bool ShouldRecycleTop => GetCellExtent(CellPool[topPoolIndex].Rect) > RecycleViewBounds.x
|
||||
@ -598,6 +630,8 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
ScrollRect.StopMovement();
|
||||
|
||||
RefreshCellHeightsFast();
|
||||
|
||||
// normalize the scroll position for the scroll bounds.
|
||||
// this translates the value into saying "point at the center of the height of the viewport"
|
||||
var scrollHeight = NormalizedScrollBounds.y - NormalizedScrollBounds.x;
|
||||
@ -630,52 +664,49 @@ namespace UnityExplorer.UI.Widgets
|
||||
// check if our pool indices contain the desired index. If so, rotate and set
|
||||
if (bottomDataIndex == desiredBottomIndex)
|
||||
{
|
||||
// cells will be the same, do nothing?
|
||||
// cells will be the same, do nothing
|
||||
}
|
||||
else if (TopDataIndex > poolStartIndex && TopDataIndex < desiredBottomIndex)
|
||||
{
|
||||
// top cell falls within the new range, rotate around that
|
||||
int rotate = TopDataIndex - poolStartIndex;
|
||||
for (int i = 0; i < rotate; i++)
|
||||
{
|
||||
CellPool[bottomPoolIndex].Rect.SetAsFirstSibling();
|
||||
|
||||
//set new indices
|
||||
topPoolIndex = bottomPoolIndex;
|
||||
bottomPoolIndex = (bottomPoolIndex - 1 + CellPool.Count) % CellPool.Count;
|
||||
bottomDataIndex--;
|
||||
|
||||
SetCell(CellPool[topPoolIndex], TopDataIndex);
|
||||
}
|
||||
}
|
||||
else if (bottomDataIndex > poolStartIndex && bottomDataIndex < desiredBottomIndex)
|
||||
{
|
||||
// bottom cells falls within the new range, rotate around that
|
||||
int rotate = desiredBottomIndex - bottomDataIndex;
|
||||
for (int i = 0; i < rotate; i++)
|
||||
{
|
||||
CellPool[topPoolIndex].Rect.SetAsLastSibling();
|
||||
|
||||
//set new indices
|
||||
bottomPoolIndex = topPoolIndex;
|
||||
topPoolIndex = (topPoolIndex + 1) % CellPool.Count;
|
||||
bottomDataIndex++;
|
||||
|
||||
SetCell(CellPool[bottomPoolIndex], bottomDataIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TopDataIndex > poolStartIndex && TopDataIndex < desiredBottomIndex)
|
||||
bottomDataIndex = desiredBottomIndex;
|
||||
var enumerator = GetPoolEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
// top cell falls within the new range, rotate around that
|
||||
int rotate = TopDataIndex - poolStartIndex;
|
||||
for (int i = 0; i < rotate; i++)
|
||||
{
|
||||
CellPool[bottomPoolIndex].Rect.SetAsFirstSibling();
|
||||
|
||||
//set new indices
|
||||
topPoolIndex = bottomPoolIndex;
|
||||
bottomPoolIndex = (bottomPoolIndex - 1 + CellPool.Count) % CellPool.Count;
|
||||
bottomDataIndex--;
|
||||
|
||||
SetCell(CellPool[topPoolIndex], TopDataIndex);
|
||||
}
|
||||
}
|
||||
else if (bottomDataIndex > poolStartIndex && bottomDataIndex < desiredBottomIndex)
|
||||
{
|
||||
// bottom cells falls within the new range, rotate around that
|
||||
int rotate = desiredBottomIndex - bottomDataIndex;
|
||||
for (int i = 0; i < rotate; i++)
|
||||
{
|
||||
CellPool[topPoolIndex].Rect.SetAsLastSibling();
|
||||
|
||||
//set new indices
|
||||
bottomPoolIndex = topPoolIndex;
|
||||
topPoolIndex = (topPoolIndex + 1) % CellPool.Count;
|
||||
bottomDataIndex++;
|
||||
|
||||
SetCell(CellPool[bottomPoolIndex], bottomDataIndex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bottomDataIndex = desiredBottomIndex;
|
||||
var enumerator = GetPoolEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var curr = enumerator.Current;
|
||||
var cell = CellPool[curr.cellIndex];
|
||||
SetCell(cell, curr.dataIndex);
|
||||
}
|
||||
var curr = enumerator.Current;
|
||||
var cell = CellPool[curr.cellIndex];
|
||||
SetCell(cell, curr.dataIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,155 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI;
|
||||
using UnityExplorer.UI.Models;
|
||||
|
||||
namespace UnityExplorer.UI.Utility
|
||||
{
|
||||
// Basically just to fix an issue with Scrollbars, instead we use a Slider as the scrollbar.
|
||||
public class SliderScrollbar : UIBehaviourModel
|
||||
{
|
||||
public bool IsActive { get; private set; }
|
||||
|
||||
public override GameObject UIRoot
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_slider)
|
||||
return m_slider.gameObject;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public event Action<float> OnValueChanged;
|
||||
|
||||
internal readonly Scrollbar m_scrollbar;
|
||||
internal readonly Slider m_slider;
|
||||
internal readonly RectTransform m_scrollRect;
|
||||
|
||||
internal InputFieldScroller m_parentInputScroller;
|
||||
|
||||
public SliderScrollbar(Scrollbar scrollbar, Slider slider)
|
||||
{
|
||||
this.m_scrollbar = scrollbar;
|
||||
this.m_slider = slider;
|
||||
this.m_scrollRect = scrollbar.transform.parent.GetComponent<RectTransform>();
|
||||
|
||||
this.m_scrollbar.onValueChanged.AddListener(this.OnScrollbarValueChanged);
|
||||
this.m_slider.onValueChanged.AddListener(this.OnSliderValueChanged);
|
||||
|
||||
this.RefreshVisibility();
|
||||
this.m_slider.Set(1f, false);
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
this.RefreshVisibility();
|
||||
}
|
||||
|
||||
internal void RefreshVisibility()
|
||||
{
|
||||
if (!m_slider.gameObject.activeInHierarchy)
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool shouldShow = !Mathf.Approximately(this.m_scrollbar.size, 1);
|
||||
//var obj = this.m_slider.handleRect.gameObject;
|
||||
|
||||
if (IsActive != shouldShow)
|
||||
{
|
||||
IsActive = shouldShow;
|
||||
m_slider.interactable = shouldShow;
|
||||
|
||||
if (IsActive)
|
||||
this.m_slider.Set(this.m_scrollbar.value, false);
|
||||
else
|
||||
m_slider.Set(1f, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnScrollbarValueChanged(float _value)
|
||||
{
|
||||
if (this.m_slider.value != _value)
|
||||
this.m_slider.Set(_value, false);
|
||||
OnValueChanged?.Invoke(_value);
|
||||
}
|
||||
|
||||
public void OnSliderValueChanged(float _value)
|
||||
{
|
||||
this.m_scrollbar.value = _value;
|
||||
OnValueChanged?.Invoke(_value);
|
||||
}
|
||||
|
||||
#region UI CONSTRUCTION
|
||||
|
||||
public static GameObject CreateSliderScrollbar(GameObject parent, out Slider slider)
|
||||
{
|
||||
GameObject sliderObj = UIFactory.CreateUIObject("SliderScrollbar", parent, UIFactory._smallElementSize);
|
||||
|
||||
GameObject bgObj = UIFactory.CreateUIObject("Background", sliderObj);
|
||||
GameObject handleSlideAreaObj = UIFactory.CreateUIObject("Handle Slide Area", sliderObj);
|
||||
GameObject handleObj = UIFactory.CreateUIObject("Handle", handleSlideAreaObj);
|
||||
|
||||
Image bgImage = bgObj.AddComponent<Image>();
|
||||
bgImage.type = Image.Type.Sliced;
|
||||
bgImage.color = new Color(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
|
||||
RectTransform bgRect = bgObj.GetComponent<RectTransform>();
|
||||
bgRect.anchorMin = Vector2.zero;
|
||||
bgRect.anchorMax = Vector2.one;
|
||||
bgRect.sizeDelta = Vector2.zero;
|
||||
bgRect.offsetMax = new Vector2(0f, 0f);
|
||||
|
||||
RectTransform handleSlideRect = handleSlideAreaObj.GetComponent<RectTransform>();
|
||||
handleSlideRect.anchorMin = new Vector2(0f, 0f);
|
||||
handleSlideRect.anchorMax = new Vector2(1f, 1f);
|
||||
handleSlideRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
handleSlideRect.offsetMin = new Vector2(25f, 30f);
|
||||
handleSlideRect.offsetMax = new Vector2(-15f, 0f);
|
||||
handleSlideRect.sizeDelta = new Vector2(-20f, -30f);
|
||||
|
||||
Image handleImage = handleObj.AddComponent<Image>();
|
||||
handleImage.color = new Color(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
|
||||
var handleRect = handleObj.GetComponent<RectTransform>();
|
||||
handleRect.sizeDelta = new Vector2(15f, 30f);
|
||||
handleRect.offsetMin = new Vector2(-13f, -28f);
|
||||
handleRect.offsetMax = new Vector2(2f, -2f);
|
||||
|
||||
var sliderBarLayout = sliderObj.AddComponent<LayoutElement>();
|
||||
sliderBarLayout.minWidth = 25;
|
||||
sliderBarLayout.flexibleWidth = 0;
|
||||
sliderBarLayout.minHeight = 30;
|
||||
sliderBarLayout.flexibleHeight = 5000;
|
||||
|
||||
slider = sliderObj.AddComponent<Slider>();
|
||||
slider.handleRect = handleObj.GetComponent<RectTransform>();
|
||||
slider.targetGraphic = handleImage;
|
||||
slider.direction = Slider.Direction.BottomToTop;
|
||||
|
||||
RuntimeProvider.Instance.SetColorBlock(slider,
|
||||
new Color(0.4f, 0.4f, 0.4f),
|
||||
new Color(0.5f, 0.5f, 0.5f),
|
||||
new Color(0.3f, 0.3f, 0.3f),
|
||||
new Color(0.2f, 0.2f, 0.2f));
|
||||
|
||||
return sliderObj;
|
||||
}
|
||||
|
||||
public override void ConstructUI(GameObject parent)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -230,6 +230,7 @@
|
||||
<Compile Include="Inspectors_OLD\Reflection\InstanceInspector.cs" />
|
||||
<Compile Include="Inspectors_OLD\Reflection\ReflectionInspector.cs" />
|
||||
<Compile Include="Inspectors_OLD\Reflection\StaticInspector.cs" />
|
||||
<Compile Include="UI\CSConsole\CSConsoleManager.cs" />
|
||||
<Compile Include="UI\Inspectors\CacheObject\CacheField.cs" />
|
||||
<Compile Include="UI\Inspectors\CacheObject\CacheMember.cs" />
|
||||
<Compile Include="UI\Inspectors\CacheObject\CacheMethod.cs" />
|
||||
@ -241,10 +242,11 @@
|
||||
<Compile Include="UI\Inspectors\InspectorManager.cs" />
|
||||
<Compile Include="UI\Inspectors\InspectorTab.cs" />
|
||||
<Compile Include="UI\Inspectors\InspectorBase.cs" />
|
||||
<Compile Include="UI\Inspectors\IValues\IValueTest.cs" />
|
||||
<Compile Include="UI\Inspectors\IValues\InteractiveValue.cs" />
|
||||
<Compile Include="UI\Inspectors\ReflectionInspector.cs" />
|
||||
<Compile Include="UI\ObjectPool\IPooledObject.cs" />
|
||||
<Compile Include="UI\ObjectPool\Pool.cs" />
|
||||
<Compile Include="UI\Panels\CSConsolePanel.cs" />
|
||||
<Compile Include="UI\Widgets\AutoComplete\ISuggestionProvider.cs" />
|
||||
<Compile Include="UI\Widgets\AutoComplete\Suggestion.cs" />
|
||||
<Compile Include="Core\Config\ConfigElement.cs" />
|
||||
@ -330,7 +332,7 @@
|
||||
<Compile Include="UI\Widgets\InputFieldScroller.cs" />
|
||||
<Compile Include="UI\Widgets\ButtonList\ButtonCell.cs" />
|
||||
<Compile Include="UI\Widgets\ButtonList\ButtonListSource.cs" />
|
||||
<Compile Include="UI\Widgets\SliderScrollbar.cs" />
|
||||
<Compile Include="UI\Widgets\AutoSliderScrollbar.cs" />
|
||||
<Compile Include="UI\Widgets\TransformTree\CachedTransform.cs" />
|
||||
<Compile Include="UI\Widgets\TransformTree\TransformCell.cs" />
|
||||
<Compile Include="UI\Widgets\TransformTree\TransformTree.cs" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user