More progress

This commit is contained in:
Sinai 2021-04-30 21:34:50 +10:00
parent 0bc14b2f76
commit 2378925a8b
27 changed files with 1401 additions and 846 deletions

View File

@ -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 [![](https://img.shields.io/github/release/sinai-dev/UnityExplorer.svg?label=release%20notes)](../../releases/latest) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/total.svg)](../../releases) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/latest/total.svg)](../../releases/latest)
| Mod Loader | IL2CPP | Mono |

View File

@ -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);
}
}
}

View File

@ -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}");
}
}

View File

@ -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));
}

View 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
";
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}
}
}

View File

@ -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();

View File

@ -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; });
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View 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;
}
}
}

View File

@ -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;
}

View File

@ -12,7 +12,7 @@ namespace UnityExplorer.UI.Models
public bool Enabled
{
get => UIRoot && UIRoot.activeSelf;
get => UIRoot && UIRoot.activeInHierarchy;
set
{
if (!UIRoot || Enabled == value)

View 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;
}
}
}

View File

@ -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;

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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.");

View 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();
}
}
}

View File

@ -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;
}
}

View File

@ -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()

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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" />