diff --git a/README.md b/README.md index 455a144..4a87be3 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 f948fd5..98393ef 100644 --- a/src/Core/Reflection/Extensions.cs +++ b/src/Core/Reflection/Extensions.cs @@ -36,6 +36,27 @@ namespace UnityExplorer // ------- 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 c65948e..f60aea6 100644 --- a/src/Core/Tests/TestClass.cs +++ b/src/Core/Tests/TestClass.cs @@ -14,8 +14,40 @@ using UnhollowerBaseLib; 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 e82a073..b8092f4 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 f17c447..e49161c 100644 --- a/src/UI/IValues/InteractiveDictionary.cs +++ b/src/UI/IValues/InteractiveDictionary.cs @@ -75,10 +75,11 @@ namespace UnityExplorer.UI.IValues 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 c861653..6a1f6be 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 @@ namespace UnityExplorer.UI.IValues object ICacheObjectController.Target => 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 @@ namespace UnityExplorer.UI.IValues private void ClearAndRelease() { RefIList = null; - values.Clear(); foreach (var entry in cachedEntries) { @@ -66,14 +68,14 @@ namespace UnityExplorer.UI.IValues 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 @@ namespace UnityExplorer.UI.IValues { 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 @@ namespace UnityExplorer.UI.IValues { 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 @@ namespace UnityExplorer.UI.IValues } // 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 @@ namespace UnityExplorer.UI.IValues } } + 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 b9f836b..7848298 100644 --- a/src/UI/Inspectors/GameObjectInspector.cs +++ b/src/UI/Inspectors/GameObjectInspector.cs @@ -226,7 +226,7 @@ namespace UnityExplorer.UI.Inspectors 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 @@ namespace UnityExplorer.UI.Inspectors { 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 19bf893..12b5cba 100644 --- a/src/UI/Inspectors/InspectUnderMouse.cs +++ b/src/UI/Inspectors/InspectUnderMouse.cs @@ -155,8 +155,8 @@ namespace UnityExplorer.UI.Inspectors 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 @@ namespace UnityExplorer.UI.Inspectors 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 73f4ddf..0ed4612 100644 --- a/src/UI/Panels/CSConsolePanel.cs +++ b/src/UI/Panels/CSConsolePanel.cs @@ -62,10 +62,10 @@ namespace UnityExplorer.UI.Panels 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 1c08e14..f2117d3 100644 --- a/src/UI/Panels/InspectorPanel.cs +++ b/src/UI/Panels/InspectorPanel.cs @@ -26,8 +26,8 @@ namespace UnityExplorer.UI.Panels 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 @@ namespace UnityExplorer.UI.Panels { base.OnFinishResize(panel); - InspectorManager.PanelWidth = this.mainPanelRect.rect.width; + InspectorManager.PanelWidth = this.Rect.rect.width; InspectorManager.OnPanelResized(panel.rect.width); } @@ -51,10 +51,10 @@ namespace UnityExplorer.UI.Panels 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 43a5db6..a49502d 100644 --- a/src/UI/Panels/LogPanel.cs +++ b/src/UI/Panels/LogPanel.cs @@ -51,7 +51,7 @@ namespace UnityExplorer.UI.Panels if (active && !DoneScrollPoolInit) { - LayoutRebuilder.ForceRebuildLayoutImmediate(this.mainPanelRect); + LayoutRebuilder.ForceRebuildLayoutImmediate(this.Rect); logScrollPool.Initialize(this); DoneScrollPoolInit = true; } @@ -158,10 +158,10 @@ namespace UnityExplorer.UI.Panels 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 9d32e89..1158122 100644 --- a/src/UI/Panels/ObjectExplorerPanel.cs +++ b/src/UI/Panels/ObjectExplorerPanel.cs @@ -99,10 +99,10 @@ namespace UnityExplorer.UI.Panels 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 4da55bc..a0bf3b0 100644 --- a/src/UI/Panels/OptionsPanel.cs +++ b/src/UI/Panels/OptionsPanel.cs @@ -69,11 +69,11 @@ namespace UnityExplorer.UI.Panels 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 abc856e..78524c5 100644 --- a/src/UI/Panels/PanelDragger.cs +++ b/src/UI/Panels/PanelDragger.cs @@ -250,15 +250,9 @@ namespace UnityExplorer.UI.Panels 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 @@ namespace UnityExplorer.UI.Panels 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 68aa2cf..d8c40f8 100644 --- a/src/UI/Panels/UIPanel.cs +++ b/src/UI/Panels/UIPanel.cs @@ -44,8 +44,8 @@ namespace UnityExplorer.UI.Panels 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 @@ namespace UnityExplorer.UI.Panels 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 @@ namespace UnityExplorer.UI.Panels 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 GameObject titleBar; + 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; + } + + + + #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 @@ namespace UnityExplorer.UI.Panels // 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 @@ namespace UnityExplorer.UI.Panels 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 @@ namespace UnityExplorer.UI.Panels // 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 @@ namespace UnityExplorer.UI.Panels UIManager.SetPanelActive(this.PanelType, ShowByDefault); ApplyingSaveData = true; + SetTransformDefaults(); // apply panel save data or revert to default try { @@ -234,6 +305,13 @@ namespace UnityExplorer.UI.Panels 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 @@ namespace UnityExplorer.UI.Panels { 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 3d3a85a..cfcc566 100644 --- a/src/UI/UIManager.cs +++ b/src/UI/UIManager.cs @@ -123,6 +123,9 @@ namespace UnityExplorer.UI // Main UI Update loop + private static int lastScreenWidth; + private static int lastScreenHeight; + public static void Update() { if (!CanvasRoot || Initializing) @@ -150,6 +153,19 @@ namespace UnityExplorer.UI 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 @@ namespace UnityExplorer.UI 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 122dcb6..4380ea1 100644 --- a/src/UI/Widgets/AutoComplete/AutoCompleteModal.cs +++ b/src/UI/Widgets/AutoComplete/AutoCompleteModal.cs @@ -292,9 +292,9 @@ namespace UnityExplorer.UI.Widgets.AutoComplete 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()