diff --git a/README.md b/README.md
index 455a1447..4a87be37 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,6 @@
| Standalone | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Mono.zip) |
### Known issues
-* UI layouts broken/unusable after changing resolutions: delete the file `data.ini` in the UnityExplorer folder (same place as where you put the DLL). Better fix being worked on.
* Any `MissingMethodException` or `NotSupportedException`: please report the issue and provide a copy of your mod loader log and/or Unity log.
* The C# console may unexpectedly produce a GC Mark Overflow crash when calling certain outside methods. Not clear yet what is causing this, but it's being looked into.
* In IL2CPP, some IEnumerable and IDictionary types may fail enumeration. Waiting for the Unhollower rewrite to address this any further.
@@ -95,8 +94,7 @@ Depending on the release you are using, the config file will be found at:
## Building
-Building the project should be straight-forward, the references are all inside the `lib\` folder.
-
+0. Clone the repository and run `git submodule update --init --recursive` to get the submodules.
1. Open the `src\UnityExplorer.sln` project in Visual Studio.
2. Select `Solution 'UnityExplorer' (1 of 1 project)` in the Solution Explorer panel, and set the Active config property to the version you want to build, then build it. Alternatively, use "Batch Build" and select all releases.
3. The DLLs are built to the `Release\` folder in the root of the repository.
diff --git a/src/Core/Reflection/Extensions.cs b/src/Core/Reflection/Extensions.cs
index f948fd5a..98393ef4 100644
--- a/src/Core/Reflection/Extensions.cs
+++ b/src/Core/Reflection/Extensions.cs
@@ -36,6 +36,27 @@ public static HashSet GetImplementationsOf(this Type baseType, bool allowA
// ------- Misc extensions --------
+ ///
+ /// Recursively check the type and its base types to find any generic arguments.
+ ///
+ public static bool TryGetGenericArguments(this Type type, out Type[] args)
+ {
+ if (type.IsGenericType)
+ {
+ args = type.GetGenericArguments();
+ return true;
+ }
+ else if (type.BaseType != null)
+ {
+ return TryGetGenericArguments(type.BaseType, out args);
+ }
+ else
+ {
+ args = null;
+ return false;
+ }
+ }
+
///
/// Safely try to get all Types inside an Assembly.
///
diff --git a/src/Core/Tests/TestClass.cs b/src/Core/Tests/TestClass.cs
index c65948e6..f60aea69 100644
--- a/src/Core/Tests/TestClass.cs
+++ b/src/Core/Tests/TestClass.cs
@@ -14,8 +14,40 @@
namespace UnityExplorer.Tests
{
+ public class TestIndexer : IList
+ {
+ private readonly List list = new List() { 1,2,3,4,5 };
+
+ public int Count => list.Count;
+ public bool IsReadOnly => false;
+
+ int IList.this[int index]
+ {
+ get => list[index];
+ set => list[index] = value;
+ }
+
+ public int IndexOf(int item) => list.IndexOf(item);
+ public bool Contains(int item) => list.Contains(item);
+
+ public void Add(int item) => list.Add(item);
+ public void Insert(int index, int item) => list.Insert(index, item);
+
+ public bool Remove(int item) => list.Remove(item);
+ public void RemoveAt(int index) => list.RemoveAt(index);
+
+ public void Clear() => list.Clear();
+
+ public void CopyTo(int[] array, int arrayIndex) => list.CopyTo(array, arrayIndex);
+
+ public IEnumerator GetEnumerator() => list.GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => list.GetEnumerator();
+ }
+
public static class TestClass
{
+ public static readonly TestIndexer AAAAATest = new TestIndexer();
+
public static void ATestMethod(string s, float f, Vector3 vector, DateTime date, Quaternion quater, bool b, CameraClearFlags enumvalue)
{
ExplorerCore.Log($"{s}, {f}, {vector.ToString()}, {date}, {quater.eulerAngles.ToString()}, {b}, {enumvalue}");
diff --git a/src/ExplorerCore.cs b/src/ExplorerCore.cs
index e82a073a..b8092f4b 100644
--- a/src/ExplorerCore.cs
+++ b/src/ExplorerCore.cs
@@ -19,7 +19,7 @@ namespace UnityExplorer
public static class ExplorerCore
{
public const string NAME = "UnityExplorer";
- public const string VERSION = "4.0.0";
+ public const string VERSION = "4.0.1";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer";
diff --git a/src/UI/IValues/InteractiveDictionary.cs b/src/UI/IValues/InteractiveDictionary.cs
index f17c4477..e49161c4 100644
--- a/src/UI/IValues/InteractiveDictionary.cs
+++ b/src/UI/IValues/InteractiveDictionary.cs
@@ -75,10 +75,11 @@ public override void SetValue(object value)
else
{
var type = value.GetActualType();
- if (type.IsGenericType && type.GetGenericArguments().Length == 2)
- {
- KeyType = type.GetGenericArguments()[0];
- ValueType = type.GetGenericArguments()[1];
+
+ if (type.TryGetGenericArguments(out var args) && args.Length == 2)
+ {
+ KeyType = args[0];
+ ValueType = args[1];
}
else
{
diff --git a/src/UI/IValues/InteractiveList.cs b/src/UI/IValues/InteractiveList.cs
index c8616538..6a1f6beb 100644
--- a/src/UI/IValues/InteractiveList.cs
+++ b/src/UI/IValues/InteractiveList.cs
@@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
@@ -19,13 +20,15 @@ public class InteractiveList : InteractiveValue, ICellPoolDataSource this.CurrentOwner.Value;
public Type TargetType { get; private set; }
- public override bool CanWrite => base.CanWrite && RefIList != null && !RefIList.IsReadOnly;
+ public override bool CanWrite => base.CanWrite && ((RefIList != null && !RefIList.IsReadOnly) || IsWritableGenericIList);
public Type EntryType;
public IList RefIList;
- public int ItemCount => values.Count;
- private readonly List values = new List();
+ private bool IsWritableGenericIList;
+ private PropertyInfo genericIndexer;
+
+ public int ItemCount => cachedEntries.Count;
private readonly List cachedEntries = new List();
public ScrollPool ListScrollPool { get; private set; }
@@ -49,7 +52,6 @@ public override void ReleaseFromOwner()
private void ClearAndRelease()
{
RefIList = null;
- values.Clear();
foreach (var entry in cachedEntries)
{
@@ -66,14 +68,14 @@ public override void SetValue(object value)
if (value == null)
{
// should never be null
- if (values.Any())
+ if (cachedEntries.Any())
ClearAndRelease();
}
else
{
var type = value.GetActualType();
- if (type.IsGenericType)
- EntryType = type.GetGenericArguments()[0];
+ if (type.TryGetGenericArguments(out var args))
+ EntryType = args[0];
else if (type.HasElementType)
EntryType = type.GetElementType();
else
@@ -92,7 +94,12 @@ private void CacheEntries(object value)
{
RefIList = value as IList;
- values.Clear();
+ // Check if the type implements IList but not IList (ie. Il2CppArrayBase)
+ if (RefIList == null)
+ CheckGenericIList(value);
+ else
+ IsWritableGenericIList = false;
+
int idx = 0;
if (ReflectionUtility.TryGetEnumerator(value, out IEnumerator enumerator))
@@ -103,8 +110,6 @@ private void CacheEntries(object value)
{
var entry = enumerator.Current;
- values.Add(entry);
-
// If list count increased, create new cache entries
CacheListEntry cache;
if (idx >= cachedEntries.Count)
@@ -122,9 +127,9 @@ private void CacheEntries(object value)
}
// Remove excess cached entries if list count decreased
- if (cachedEntries.Count > values.Count)
+ if (cachedEntries.Count > idx)
{
- for (int i = cachedEntries.Count - 1; i >= values.Count; i--)
+ for (int i = cachedEntries.Count - 1; i >= idx; i--)
{
var cache = cachedEntries[i];
if (cache.CellView != null)
@@ -141,14 +146,61 @@ private void CacheEntries(object value)
}
}
+ private void CheckGenericIList(object value)
+ {
+ try
+ {
+ var type = value.GetType();
+ if (type.GetInterfaces().Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IList<>)))
+ IsWritableGenericIList = !(bool)type.GetProperty("IsReadOnly").GetValue(value, null);
+ else
+ IsWritableGenericIList = false;
+
+ if (IsWritableGenericIList)
+ {
+ // Find the "this[int index]" property.
+ // It might be a private implementation.
+ foreach (var prop in type.GetProperties(ReflectionUtility.FLAGS))
+ {
+ if ((prop.Name == "Item"
+ || (prop.Name.StartsWith("System.Collections.Generic.IList<") && prop.Name.EndsWith(">.Item")))
+ && prop.GetIndexParameters() is ParameterInfo[] parameters
+ && parameters.Length == 1
+ && parameters[0].ParameterType == typeof(int))
+ {
+ genericIndexer = prop;
+ break;
+ }
+ }
+
+ if (genericIndexer == null)
+ {
+ ExplorerCore.LogWarning($"Failed to find indexer property for IList type '{type.FullName}'!");
+ IsWritableGenericIList = false;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ ExplorerCore.LogWarning($"Exception processing IEnumerable for IList check: {ex.ReflectionExToString()}");
+ IsWritableGenericIList = false;
+ }
+ }
+
// Setting the value of an index to the list
public void TrySetValueToIndex(object value, int index)
{
try
{
- //value = value.TryCast(this.EntryType);
- RefIList[index] = value;
+ if (!IsWritableGenericIList)
+ {
+ RefIList[index] = value;
+ }
+ else
+ {
+ genericIndexer.SetValue(CurrentOwner.Value, value, new object[] { index });
+ }
var entry = cachedEntries[index];
entry.SetValueFromSource(value);
diff --git a/src/UI/Inspectors/GameObjectInspector.cs b/src/UI/Inspectors/GameObjectInspector.cs
index b9f836b0..78482986 100644
--- a/src/UI/Inspectors/GameObjectInspector.cs
+++ b/src/UI/Inspectors/GameObjectInspector.cs
@@ -226,7 +226,7 @@ public override GameObject CreateContent(GameObject parent)
var scrollObj = UIFactory.CreateScrollView(UIRoot, "GameObjectInspector", out Content, out var scrollbar,
new Color(0.065f, 0.065f, 0.065f));
- UIFactory.SetLayoutElement(scrollObj, minHeight: 300, flexibleWidth: 9999, flexibleHeight: 1);
+ UIFactory.SetLayoutElement(scrollObj, minHeight: 250, preferredHeight: 300, flexibleHeight: 0, flexibleWidth: 9999);
UIFactory.SetLayoutGroup(Content, spacing: 3, padTop: 2, padBottom: 2, padLeft: 2, padRight: 2);
@@ -244,10 +244,7 @@ private void ConstructLists()
{
var listHolder = UIFactory.CreateUIObject("ListHolders", UIRoot);
UIFactory.SetLayoutGroup(listHolder, false, true, true, true, 8, 2, 2, 2, 2);
- UIFactory.SetLayoutElement(listHolder, minHeight: 350, flexibleWidth: 9999, flexibleHeight: 9999);
- //var listRect = listHolder.GetComponent();
- //listRect.anchorMin = new Vector2(0, 1);
- //listRect.anchorMax = new Vector2(1, 1);
+ UIFactory.SetLayoutElement(listHolder, minHeight: 150, flexibleWidth: 9999, flexibleHeight: 9999);
// Left group (Children)
diff --git a/src/UI/Inspectors/InspectUnderMouse.cs b/src/UI/Inspectors/InspectUnderMouse.cs
index 19bf893c..12b5cba6 100644
--- a/src/UI/Inspectors/InspectUnderMouse.cs
+++ b/src/UI/Inspectors/InspectUnderMouse.cs
@@ -155,8 +155,8 @@ internal void UpdatePosition(Vector2 mousePos)
mousePos.x = 350;
if (mousePos.x > Screen.width - 350)
mousePos.x = Screen.width - 350;
- if (mousePos.y < mainPanelRect.rect.height)
- mousePos.y += mainPanelRect.rect.height + 10;
+ if (mousePos.y < Rect.rect.height)
+ mousePos.y += Rect.rect.height + 10;
else
mousePos.y -= 10;
@@ -341,10 +341,10 @@ private static void StopUIInspect()
protected internal override void DoSetDefaultPosAndAnchors()
{
- mainPanelRect.anchorMin = Vector2.zero;
- mainPanelRect.anchorMax = Vector2.zero;
- mainPanelRect.pivot = new Vector2(0.5f, 1);
- mainPanelRect.sizeDelta = new Vector2(700, 150);
+ Rect.anchorMin = Vector2.zero;
+ Rect.anchorMax = Vector2.zero;
+ Rect.pivot = new Vector2(0.5f, 1);
+ Rect.sizeDelta = new Vector2(700, 150);
}
public override void ConstructPanelContent()
diff --git a/src/UI/Panels/CSConsolePanel.cs b/src/UI/Panels/CSConsolePanel.cs
index 73f4ddf8..0ed46128 100644
--- a/src/UI/Panels/CSConsolePanel.cs
+++ b/src/UI/Panels/CSConsolePanel.cs
@@ -62,10 +62,10 @@ public override void DoSaveToConfigElement()
protected internal override void DoSetDefaultPosAndAnchors()
{
- mainPanelRect.localPosition = Vector2.zero;
- mainPanelRect.pivot = new Vector2(0f, 1f);
- mainPanelRect.anchorMin = new Vector2(0.4f, 0.175f);
- mainPanelRect.anchorMax = new Vector2(0.85f, 0.925f);
+ Rect.localPosition = Vector2.zero;
+ Rect.pivot = new Vector2(0f, 1f);
+ Rect.anchorMin = new Vector2(0.4f, 0.175f);
+ Rect.anchorMax = new Vector2(0.85f, 0.925f);
}
public override void ConstructPanelContent()
diff --git a/src/UI/Panels/InspectorPanel.cs b/src/UI/Panels/InspectorPanel.cs
index 1c08e144..f2117d3d 100644
--- a/src/UI/Panels/InspectorPanel.cs
+++ b/src/UI/Panels/InspectorPanel.cs
@@ -26,8 +26,8 @@ public class InspectorPanel : UIPanel
public GameObject ContentHolder;
public RectTransform ContentRect;
- public static float CurrentPanelWidth => Instance.mainPanelRect.rect.width;
- public static float CurrentPanelHeight => Instance.mainPanelRect.rect.height;
+ public static float CurrentPanelWidth => Instance.Rect.rect.width;
+ public static float CurrentPanelHeight => Instance.Rect.rect.height;
public override void Update()
{
@@ -38,7 +38,7 @@ public override void OnFinishResize(RectTransform panel)
{
base.OnFinishResize(panel);
- InspectorManager.PanelWidth = this.mainPanelRect.rect.width;
+ InspectorManager.PanelWidth = this.Rect.rect.width;
InspectorManager.OnPanelResized(panel.rect.width);
}
@@ -51,10 +51,10 @@ public override void DoSaveToConfigElement()
protected internal override void DoSetDefaultPosAndAnchors()
{
- mainPanelRect.localPosition = Vector2.zero;
- mainPanelRect.pivot = new Vector2(0f, 1f);
- mainPanelRect.anchorMin = new Vector2(0.35f, 0.175f);
- mainPanelRect.anchorMax = new Vector2(0.8f, 0.925f);
+ Rect.localPosition = Vector2.zero;
+ Rect.pivot = new Vector2(0f, 1f);
+ Rect.anchorMin = new Vector2(0.35f, 0.175f);
+ Rect.anchorMax = new Vector2(0.8f, 0.925f);
}
public override void ConstructPanelContent()
diff --git a/src/UI/Panels/LogPanel.cs b/src/UI/Panels/LogPanel.cs
index 43a5db68..a49502d9 100644
--- a/src/UI/Panels/LogPanel.cs
+++ b/src/UI/Panels/LogPanel.cs
@@ -51,7 +51,7 @@ public override void SetActive(bool active)
if (active && !DoneScrollPoolInit)
{
- LayoutRebuilder.ForceRebuildLayoutImmediate(this.mainPanelRect);
+ LayoutRebuilder.ForceRebuildLayoutImmediate(this.Rect);
logScrollPool.Initialize(this);
DoneScrollPoolInit = true;
}
@@ -158,10 +158,10 @@ public override void DoSaveToConfigElement()
protected internal override void DoSetDefaultPosAndAnchors()
{
- mainPanelRect.localPosition = Vector2.zero;
- mainPanelRect.pivot = new Vector2(0f, 1f);
- mainPanelRect.anchorMin = new Vector2(0.5f, 0.03f);
- mainPanelRect.anchorMax = new Vector2(0.9f, 0.2f);
+ Rect.localPosition = Vector2.zero;
+ Rect.pivot = new Vector2(0f, 1f);
+ Rect.anchorMin = new Vector2(0.5f, 0.03f);
+ Rect.anchorMax = new Vector2(0.9f, 0.2f);
}
// UI Construction
diff --git a/src/UI/Panels/ObjectExplorerPanel.cs b/src/UI/Panels/ObjectExplorerPanel.cs
index 9d32e897..11581223 100644
--- a/src/UI/Panels/ObjectExplorerPanel.cs
+++ b/src/UI/Panels/ObjectExplorerPanel.cs
@@ -99,10 +99,10 @@ public override void ApplySaveData(string data)
protected internal override void DoSetDefaultPosAndAnchors()
{
- mainPanelRect.localPosition = Vector2.zero;
- mainPanelRect.pivot = new Vector2(0f, 1f);
- mainPanelRect.anchorMin = new Vector2(0.125f, 0.175f);
- mainPanelRect.anchorMax = new Vector2(0.325f, 0.925f);
+ Rect.localPosition = Vector2.zero;
+ Rect.pivot = new Vector2(0f, 1f);
+ Rect.anchorMin = new Vector2(0.125f, 0.175f);
+ Rect.anchorMax = new Vector2(0.325f, 0.925f);
//mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 350);
}
diff --git a/src/UI/Panels/OptionsPanel.cs b/src/UI/Panels/OptionsPanel.cs
index 4da55bca..a0bf3b08 100644
--- a/src/UI/Panels/OptionsPanel.cs
+++ b/src/UI/Panels/OptionsPanel.cs
@@ -69,11 +69,11 @@ public override void DoSaveToConfigElement()
protected internal override void DoSetDefaultPosAndAnchors()
{
- mainPanelRect.localPosition = Vector2.zero;
- mainPanelRect.pivot = new Vector2(0f, 1f);
- mainPanelRect.anchorMin = new Vector2(0.5f, 0.1f);
- mainPanelRect.anchorMax = new Vector2(0.5f, 0.85f);
- mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 600f);
+ Rect.localPosition = Vector2.zero;
+ Rect.pivot = new Vector2(0f, 1f);
+ Rect.anchorMin = new Vector2(0.5f, 0.1f);
+ Rect.anchorMax = new Vector2(0.5f, 0.85f);
+ Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 600f);
}
// UI Construction
diff --git a/src/UI/Panels/PanelDragger.cs b/src/UI/Panels/PanelDragger.cs
index abc856e7..78524c50 100644
--- a/src/UI/Panels/PanelDragger.cs
+++ b/src/UI/Panels/PanelDragger.cs
@@ -250,15 +250,9 @@ public void OnDrag()
Vector2 diff = (Vector2)mousePos - m_lastDragPosition;
m_lastDragPosition = mousePos;
- var pos = Panel.localPosition + (Vector3)diff;
+ Panel.localPosition = Panel.localPosition + (Vector3)diff;
- // Prevent panel going oustide screen bounds
- var halfW = Screen.width * 0.5f;
- var halfH = Screen.height * 0.5f;
- pos.x = Math.Max(-halfW, Math.Min(pos.x, halfW - Panel.rect.width));
- pos.y = Math.Max(-halfH + Panel.rect.height, Math.Min(pos.y, halfH));
-
- Panel.localPosition = pos;
+ UIPanel.EnsureValidPosition(Panel);
}
public void OnEndDrag()
@@ -425,6 +419,9 @@ public void OnResize()
if ((Vector2)mousePos == m_lastResizePos)
return;
+ if (mousePos.x < 0 || mousePos.y < 0 || mousePos.x > Screen.width || mousePos.y > Screen.height)
+ return;
+
m_lastResizePos = mousePos;
float diffX = (float)((decimal)diff.x / Screen.width);
diff --git a/src/UI/Panels/UIPanel.cs b/src/UI/Panels/UIPanel.cs
index 68aa2cfe..d8c40f8d 100644
--- a/src/UI/Panels/UIPanel.cs
+++ b/src/UI/Panels/UIPanel.cs
@@ -44,8 +44,8 @@ public static void UpdateFocus()
continue;
// check if our mouse is clicking inside the panel
- var pos = panel.mainPanelRect.InverseTransformPoint(mousePos);
- if (!panel.Enabled || !panel.mainPanelRect.rect.Contains(pos))
+ var pos = panel.Rect.InverseTransformPoint(mousePos);
+ if (!panel.Enabled || !panel.Rect.rect.Contains(pos))
continue;
// if this is not the top panel, reorder and invoke the onchanged event
@@ -88,9 +88,11 @@ public UIPanel()
public override GameObject UIRoot => uiRoot;
protected GameObject uiRoot;
- protected RectTransform mainPanelRect;
+ public RectTransform Rect;
public GameObject content;
+ public GameObject titleBar;
+
public abstract void ConstructPanelContent();
public virtual void OnFinishResize(RectTransform panel)
@@ -136,14 +138,85 @@ public override void Destroy()
public void SetTransformDefaults()
{
DoSetDefaultPosAndAnchors();
+ }
- if (mainPanelRect.rect.width < MinWidth)
- mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, MinWidth);
- if (mainPanelRect.rect.height < MinHeight)
- mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, MinHeight);
+ public void EnsureValidSize()
+ {
+ if (Rect.rect.width < MinWidth)
+ Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, MinWidth);
+ if (Rect.rect.height < MinHeight)
+ Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, MinHeight);
+ }
+
+ public static void EnsureValidPosition(RectTransform panel)
+ {
+ var pos = panel.localPosition;
+
+ // Prevent panel going oustide screen bounds
+ var halfW = Screen.width * 0.5f;
+ var halfH = Screen.height * 0.5f;
+ pos.x = Math.Max(-halfW, Math.Min(pos.x, halfW - panel.rect.width));
+ pos.y = Math.Max(-halfH + panel.rect.height, Math.Min(pos.y, halfH));
+
+ panel.localPosition = pos;
}
- public GameObject titleBar;
+
+
+ #region Save Data
+
+ public abstract void DoSaveToConfigElement();
+
+ public void SaveToConfigManager()
+ {
+ if (UIManager.Initializing)
+ return;
+
+ DoSaveToConfigElement();
+ }
+
+ public abstract string GetSaveDataFromConfigManager();
+
+ public bool ApplyingSaveData { get; set; }
+
+ public virtual string ToSaveData()
+ {
+ try
+ {
+ return $"{ShouldSaveActiveState && Enabled}" +
+ $"|{Rect.RectAnchorsToString()}" +
+ $"|{Rect.RectPositionToString()}";
+ }
+ catch (Exception ex)
+ {
+ ExplorerCore.LogWarning($"Exception generating Panel save data: {ex}");
+ return "";
+ }
+ }
+
+ public virtual void ApplySaveData(string data)
+ {
+ if (string.IsNullOrEmpty(data))
+ return;
+
+ var split = data.Split('|');
+
+ try
+ {
+ Rect.SetAnchorsFromString(split[1]);
+ Rect.SetPositionFromString(split[2]);
+ UIManager.SetPanelActive(this.PanelType, bool.Parse(split[0]));
+ }
+ catch
+ {
+ ExplorerCore.LogWarning("Invalid or corrupt panel save data! Restoring to default.");
+ SetTransformDefaults();
+ }
+ }
+
+ #endregion
+
+ // UI Construction
public void ConstructUI()
{
@@ -168,7 +241,7 @@ public void ConstructUI()
// create core canvas
uiRoot = UIFactory.CreatePanel(Name, out GameObject panelContent);
- mainPanelRect = this.uiRoot.GetComponent();
+ Rect = this.uiRoot.GetComponent();
UIFactory.SetLayoutGroup(this.uiRoot, false, false, true, true, 0, 2, 2, 2, 2, TextAnchor.UpperLeft);
int id = this.uiRoot.transform.GetInstanceID();
@@ -177,9 +250,6 @@ public void ConstructUI()
content = panelContent;
UIFactory.SetLayoutGroup(this.content, false, false, true, true, 2, 2, 2, 2, 2, TextAnchor.UpperLeft);
- // always apply default pos and anchors (save data may only be partial)
- SetTransformDefaults();
-
// Title bar
titleBar = UIFactory.CreateHorizontalGroup(content, "TitleBar", false, true, true, true, 2,
new Vector4(2, 2, 2, 2), new Color(0.06f, 0.06f, 0.06f));
@@ -210,7 +280,7 @@ public void ConstructUI()
// Panel dragger
- Dragger = new PanelDragger(titleBar.GetComponent(), mainPanelRect, this);
+ Dragger = new PanelDragger(titleBar.GetComponent(), Rect, this);
Dragger.OnFinishResize += OnFinishResize;
Dragger.OnFinishDrag += OnFinishDrag;
@@ -223,6 +293,7 @@ public void ConstructUI()
UIManager.SetPanelActive(this.PanelType, ShowByDefault);
ApplyingSaveData = true;
+ SetTransformDefaults();
// apply panel save data or revert to default
try
{
@@ -234,6 +305,13 @@ public void ConstructUI()
SetTransformDefaults();
}
+ LayoutRebuilder.ForceRebuildLayoutImmediate(this.Rect);
+
+ // ensure initialized position is valid
+ EnsureValidSize();
+ EnsureValidPosition(this.Rect);
+
+ // update dragger and save data
Dragger.OnEndResize();
// simple listener for saving enabled state
@@ -241,61 +319,11 @@ public void ConstructUI()
{
SaveToConfigManager();
};
+
ApplyingSaveData = false;
}
public override void ConstructUI(GameObject parent) => ConstructUI();
-
- // SAVE DATA
-
- public abstract void DoSaveToConfigElement();
-
- public void SaveToConfigManager()
- {
- if (UIManager.Initializing)
- return;
-
- DoSaveToConfigElement();
- }
-
- public abstract string GetSaveDataFromConfigManager();
-
- public bool ApplyingSaveData { get; set; }
-
- public virtual string ToSaveData()
- {
- try
- {
- return $"{ShouldSaveActiveState && Enabled}" +
- $"|{mainPanelRect.RectAnchorsToString()}" +
- $"|{mainPanelRect.RectPositionToString()}";
- }
- catch (Exception ex)
- {
- ExplorerCore.LogWarning($"Exception generating Panel save data: {ex}");
- return "";
- }
- }
-
- public virtual void ApplySaveData(string data)
- {
- if (string.IsNullOrEmpty(data))
- return;
-
- var split = data.Split('|');
-
- try
- {
- mainPanelRect.SetAnchorsFromString(split[1]);
- mainPanelRect.SetPositionFromString(split[2]);
- UIManager.SetPanelActive(this.PanelType, bool.Parse(split[0]));
- }
- catch
- {
- ExplorerCore.LogWarning("Invalid or corrupt panel save data! Restoring to default.");
- SetTransformDefaults();
- }
- }
}
#region WINDOW ANCHORS / POSITION HELPERS
diff --git a/src/UI/UIManager.cs b/src/UI/UIManager.cs
index 3d3a85a1..cfcc5669 100644
--- a/src/UI/UIManager.cs
+++ b/src/UI/UIManager.cs
@@ -123,6 +123,9 @@ internal static void SetPanelActive(Transform transform, bool value)
// Main UI Update loop
+ private static int lastScreenWidth;
+ private static int lastScreenHeight;
+
public static void Update()
{
if (!CanvasRoot || Initializing)
@@ -150,6 +153,19 @@ public static void Update()
PanelDragger.UpdateInstances();
InputFieldRef.UpdateInstances();
UIBehaviourModel.UpdateInstances();
+
+ if (Screen.width != lastScreenWidth || Screen.height != lastScreenHeight)
+ {
+ lastScreenWidth = Screen.width;
+ lastScreenHeight = Screen.height;
+
+ foreach (var panel in UIPanels)
+ {
+ panel.Value.EnsureValidSize();
+ UIPanel.EnsureValidPosition(panel.Value.Rect);
+ panel.Value.Dragger.OnEndResize();
+ }
+ }
}
// Initialization and UI Construction
@@ -184,6 +200,9 @@ internal static void InitUI()
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
+ lastScreenWidth = Screen.width;
+ lastScreenHeight = Screen.height;
+
Initializing = false;
}
diff --git a/src/UI/Widgets/AutoComplete/AutoCompleteModal.cs b/src/UI/Widgets/AutoComplete/AutoCompleteModal.cs
index 122dcb64..4380ea10 100644
--- a/src/UI/Widgets/AutoComplete/AutoCompleteModal.cs
+++ b/src/UI/Widgets/AutoComplete/AutoCompleteModal.cs
@@ -292,9 +292,9 @@ private void UIPanel_OnPanelsReordered()
protected internal override void DoSetDefaultPosAndAnchors()
{
- mainPanelRect.pivot = new Vector2(0f, 1f);
- mainPanelRect.anchorMin = new Vector2(0.42f, 0.4f);
- mainPanelRect.anchorMax = new Vector2(0.68f, 0.6f);
+ Rect.pivot = new Vector2(0f, 1f);
+ Rect.anchorMin = new Vector2(0.42f, 0.4f);
+ Rect.anchorMax = new Vector2(0.68f, 0.6f);
}
public override void ConstructPanelContent()