mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-01 11:12:49 +08:00
Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
e270f205a1 | |||
084aee617c | |||
d0e508727a | |||
a9a53ba924 | |||
3cd9819790 | |||
5abfa3da67 | |||
e5d2d29a47 | |||
6a47e542e5 | |||
f1b83e7c9e | |||
44c6503ae2 | |||
6970dcbbc7 | |||
ac9c2d5286 | |||
496a5de55e | |||
b062924af7 | |||
5aef8ddc99 | |||
c134c1752e |
@ -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 <b>Active config</b> 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.
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -26,18 +26,16 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
public static bool ShouldActuallyUnlock => UIManager.ShowMenu && Unlock;
|
||||
|
||||
private static CursorLockMode m_lastLockMode;
|
||||
private static bool m_lastVisibleState;
|
||||
private static CursorLockMode lastLockMode;
|
||||
private static bool lastVisibleState;
|
||||
|
||||
private static bool m_currentlySettingCursor = false;
|
||||
|
||||
private static Type CursorType
|
||||
=> m_cursorType
|
||||
?? (m_cursorType = ReflectionUtility.GetTypeByName("UnityEngine.Cursor"));
|
||||
private static Type m_cursorType;
|
||||
private static bool currentlySettingCursor = false;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
lastLockMode = Cursor.lockState;
|
||||
lastVisibleState = Cursor.visible;
|
||||
|
||||
SetupPatches();
|
||||
|
||||
UpdateCursorControl();
|
||||
@ -78,7 +76,7 @@ namespace UnityExplorer.Core.Input
|
||||
{
|
||||
try
|
||||
{
|
||||
m_currentlySettingCursor = true;
|
||||
currentlySettingCursor = true;
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
{
|
||||
@ -90,14 +88,14 @@ namespace UnityExplorer.Core.Input
|
||||
}
|
||||
else
|
||||
{
|
||||
Cursor.lockState = m_lastLockMode;
|
||||
Cursor.visible = m_lastVisibleState;
|
||||
Cursor.lockState = lastLockMode;
|
||||
Cursor.visible = lastVisibleState;
|
||||
|
||||
if (UIManager.EventSys)
|
||||
ReleaseEventSystem();
|
||||
}
|
||||
|
||||
m_currentlySettingCursor = false;
|
||||
currentlySettingCursor = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -107,9 +105,9 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
// Event system overrides
|
||||
|
||||
private static bool m_settingEventSystem;
|
||||
private static EventSystem m_lastEventSystem;
|
||||
private static BaseInputModule m_lastInputModule;
|
||||
private static bool settingEventSystem;
|
||||
private static EventSystem lastEventSystem;
|
||||
private static BaseInputModule lastInputModule;
|
||||
|
||||
public static void SetEventSystem()
|
||||
{
|
||||
@ -118,16 +116,16 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
if (EventSystem.current && EventSystem.current != UIManager.EventSys)
|
||||
{
|
||||
m_lastEventSystem = EventSystem.current;
|
||||
m_lastEventSystem.enabled = false;
|
||||
lastEventSystem = EventSystem.current;
|
||||
lastEventSystem.enabled = false;
|
||||
}
|
||||
|
||||
// Set to our current system
|
||||
m_settingEventSystem = true;
|
||||
settingEventSystem = true;
|
||||
UIManager.EventSys.enabled = true;
|
||||
EventSystem.current = UIManager.EventSys;
|
||||
InputManager.ActivateUIModule();
|
||||
m_settingEventSystem = false;
|
||||
settingEventSystem = false;
|
||||
}
|
||||
|
||||
public static void ReleaseEventSystem()
|
||||
@ -135,14 +133,14 @@ namespace UnityExplorer.Core.Input
|
||||
if (InputManager.CurrentType == InputType.InputSystem)
|
||||
return;
|
||||
|
||||
if (m_lastEventSystem && m_lastEventSystem.gameObject.activeSelf)
|
||||
if (lastEventSystem && lastEventSystem.gameObject.activeSelf)
|
||||
{
|
||||
m_lastEventSystem.enabled = true;
|
||||
lastEventSystem.enabled = true;
|
||||
|
||||
m_settingEventSystem = true;
|
||||
EventSystem.current = m_lastEventSystem;
|
||||
m_lastInputModule?.ActivateModule();
|
||||
m_settingEventSystem = false;
|
||||
settingEventSystem = true;
|
||||
EventSystem.current = lastEventSystem;
|
||||
lastInputModule?.ActivateModule();
|
||||
settingEventSystem = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,30 +150,30 @@ namespace UnityExplorer.Core.Input
|
||||
{
|
||||
try
|
||||
{
|
||||
if (CursorType == null)
|
||||
throw new Exception("Could not load Type 'UnityEngine.Cursor'!");
|
||||
|
||||
// Get current cursor state and enable cursor
|
||||
m_lastLockMode = (CursorLockMode?)CursorType.GetProperty("lockState", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? CursorLockMode.None;
|
||||
|
||||
m_lastVisibleState = (bool?)CursorType.GetProperty("visible", BF.Public | BF.Static)?.GetValue(null, null)
|
||||
?? false;
|
||||
|
||||
ExplorerCore.Loader.SetupCursorPatches();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExplorerCore.Log($"Error on CursorUnlocker.Init! {e.GetType()}, {e.Message}");
|
||||
ExplorerCore.Log($"Exception setting up Cursor patches: {e.GetType()}, {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
||||
{
|
||||
if (!m_settingEventSystem && value != UIManager.EventSys)
|
||||
if (!UIManager.EventSys)
|
||||
{
|
||||
m_lastEventSystem = value;
|
||||
m_lastInputModule = value?.currentInputModule;
|
||||
if (value)
|
||||
{
|
||||
lastEventSystem = value;
|
||||
lastInputModule = value.currentInputModule;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!settingEventSystem && value != UIManager.EventSys)
|
||||
{
|
||||
lastEventSystem = value;
|
||||
lastInputModule = value?.currentInputModule;
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
{
|
||||
@ -191,9 +189,9 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
public static void Prefix_set_lockState(ref CursorLockMode value)
|
||||
{
|
||||
if (!m_currentlySettingCursor)
|
||||
if (!currentlySettingCursor)
|
||||
{
|
||||
m_lastLockMode = value;
|
||||
lastLockMode = value;
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
value = CursorLockMode.None;
|
||||
@ -202,9 +200,9 @@ namespace UnityExplorer.Core.Input
|
||||
|
||||
public static void Prefix_set_visible(ref bool value)
|
||||
{
|
||||
if (!m_currentlySettingCursor)
|
||||
if (!currentlySettingCursor)
|
||||
{
|
||||
m_lastVisibleState = value;
|
||||
lastVisibleState = value;
|
||||
|
||||
if (ShouldActuallyUnlock)
|
||||
value = true;
|
||||
|
@ -61,7 +61,7 @@ namespace UnityExplorer.Core.Input
|
||||
// First, just try to use the legacy input, see if its working.
|
||||
// The InputSystem package may be present but not actually activated, so we can find out this way.
|
||||
|
||||
if (LegacyInput.TInput != null || (ReflectionUtility.LoadModule("UnityEngine.InputLegacyModule") && LegacyInput.TInput != null))
|
||||
if (LegacyInput.TInput != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -80,7 +80,7 @@ namespace UnityExplorer.Core.Input
|
||||
}
|
||||
}
|
||||
|
||||
if (InputSystem.TKeyboard != null || (ReflectionUtility.LoadModule("Unity.InputSystem") && InputSystem.TKeyboard != null))
|
||||
if (InputSystem.TKeyboard != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -15,6 +15,7 @@ using UnityExplorer.Core;
|
||||
using CppType = Il2CppSystem.Type;
|
||||
using BF = System.Reflection.BindingFlags;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnhollowerBaseLib.Attributes;
|
||||
|
||||
namespace UnityExplorer
|
||||
{
|
||||
@ -80,15 +81,19 @@ namespace UnityExplorer
|
||||
{
|
||||
try
|
||||
{
|
||||
// Thanks to Slaynash for this
|
||||
if (type.CustomAttributes.Any(it => it.AttributeType.Name == "ObfuscatedNameAttribute"))
|
||||
{
|
||||
var cppType = Il2CppType.From(type);
|
||||
if (!type.CustomAttributes.Any())
|
||||
return;
|
||||
|
||||
if (!DeobfuscatedTypes.ContainsKey(cppType.FullName))
|
||||
foreach (var att in type.CustomAttributes)
|
||||
{
|
||||
// Thanks to Slaynash for this
|
||||
|
||||
if (att.AttributeType == typeof(ObfuscatedNameAttribute))
|
||||
{
|
||||
DeobfuscatedTypes.Add(cppType.FullName, type);
|
||||
reverseDeobCache.Add(type.FullName, cppType.FullName);
|
||||
string obfuscatedName = att.ConstructorArguments[0].Value.ToString();
|
||||
|
||||
DeobfuscatedTypes.Add(obfuscatedName, type);
|
||||
reverseDeobCache.Add(type.FullName, obfuscatedName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,6 +263,18 @@ namespace UnityExplorer
|
||||
}
|
||||
}
|
||||
|
||||
//private static bool IsAssignableFrom(Type thisType, Type fromType)
|
||||
//{
|
||||
// if (!Il2CppTypeNotNull(fromType, out IntPtr fromTypePtr)
|
||||
// || !Il2CppTypeNotNull(thisType, out IntPtr thisTypePtr))
|
||||
// {
|
||||
// // one or both of the types are not Il2Cpp types, use normal check
|
||||
// return thisType.IsAssignableFrom(fromType);
|
||||
// }
|
||||
//
|
||||
// return il2cpp_class_is_assignable_from(thisTypePtr, fromTypePtr);
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@ -438,42 +455,27 @@ namespace UnityExplorer
|
||||
|
||||
#region Force-loading game modules
|
||||
|
||||
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
|
||||
|
||||
internal override bool Internal_LoadModule(string moduleName)
|
||||
{
|
||||
if (!moduleName.EndsWith(".dll", StringComparison.InvariantCultureIgnoreCase))
|
||||
moduleName += ".dll";
|
||||
internal static string UnhollowedFolderPath => Path.GetFullPath(
|
||||
#if ML
|
||||
var path = Path.Combine("MelonLoader", "Managed", $"{moduleName}");
|
||||
Path.Combine("MelonLoader", "Managed")
|
||||
#elif BIE
|
||||
Path.Combine("BepInEx", "unhollowed")
|
||||
#else
|
||||
var path = Path.Combine("BepInEx", "unhollowed", $"{moduleName}");
|
||||
Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Modules")
|
||||
#endif
|
||||
return DoLoadModule(path);
|
||||
}
|
||||
);
|
||||
|
||||
// Helper for IL2CPP to try to make sure the Unhollowed game assemblies are actually loaded.
|
||||
|
||||
// Force loading all il2cpp modules
|
||||
|
||||
internal void TryLoadGameModules()
|
||||
{
|
||||
string dirpath =
|
||||
#if ML
|
||||
Path.Combine("MelonLoader", "Managed");
|
||||
#elif BIE
|
||||
Path.Combine("BepInEx", "unhollowed");
|
||||
#else
|
||||
Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Modules");
|
||||
#endif
|
||||
;
|
||||
|
||||
if (Directory.Exists(dirpath))
|
||||
if (Directory.Exists(UnhollowedFolderPath))
|
||||
{
|
||||
var files = Directory.GetFiles(dirpath);
|
||||
var files = Directory.GetFiles(UnhollowedFolderPath);
|
||||
foreach (var filePath in files)
|
||||
{
|
||||
var name = Path.GetFileName(filePath);
|
||||
if (!name.StartsWith("Unity") && !name.StartsWith("Assembly-CSharp"))
|
||||
continue;
|
||||
try
|
||||
{
|
||||
DoLoadModule(filePath, true);
|
||||
@ -484,6 +486,8 @@ namespace UnityExplorer
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ExplorerCore.LogWarning($"Expected Unhollowed folder path does not exist: '{UnhollowedFolderPath}'");
|
||||
}
|
||||
|
||||
internal bool DoLoadModule(string fullPath, bool suppressWarning = false)
|
||||
@ -493,7 +497,8 @@ namespace UnityExplorer
|
||||
|
||||
try
|
||||
{
|
||||
Assembly.Load(File.ReadAllBytes(fullPath));
|
||||
Assembly.LoadFile(fullPath);
|
||||
//Assembly.Load(File.ReadAllBytes(fullPath));
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -508,7 +513,7 @@ namespace UnityExplorer
|
||||
#endregion
|
||||
|
||||
|
||||
#region Il2cpp reflection blacklist
|
||||
#region Il2cpp reflection blacklist
|
||||
|
||||
public override string DefaultReflectionBlacklist => string.Join(";", defaultIl2CppBlacklist);
|
||||
|
||||
@ -636,6 +641,9 @@ namespace UnityExplorer
|
||||
"UnityEngine.Scripting.GarbageCollector+CollectIncrementalDelegate.Invoke",
|
||||
"UnityEngine.Scripting.GarbageCollector.CollectIncremental",
|
||||
"UnityEngine.SpherecastCommand.ScheduleBatch",
|
||||
"UnityEngine.Texture.GetPixelDataSize",
|
||||
"UnityEngine.Texture.GetPixelDataOffset",
|
||||
"UnityEngine.Texture.GetPixelDataOffset",
|
||||
"UnityEngine.Texture2D+SetPixelDataImplArrayDelegate.Invoke",
|
||||
"UnityEngine.Texture2D+SetPixelDataImplDelegate.Invoke",
|
||||
"UnityEngine.Texture2D.SetPixelDataImpl",
|
||||
@ -654,10 +662,54 @@ namespace UnityExplorer
|
||||
"UnityEngine.XR.InputDevice.SendHapticImpulse",
|
||||
};
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
|
||||
#region Temp il2cpp list/dictionary fixes
|
||||
#region IL2CPP IEnumerable and IDictionary
|
||||
|
||||
protected override bool Internal_TryGetEntryType(Type enumerableType, out Type type)
|
||||
{
|
||||
// Check for system types (not unhollowed)
|
||||
if (base.Internal_TryGetEntryType(enumerableType, out type))
|
||||
return true;
|
||||
|
||||
// Type is either an IL2CPP enumerable, or its not generic.
|
||||
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
// Temporary naive solution until IL2CPP interface support improves.
|
||||
// This will work fine for most cases, but there are edge cases which would not work.
|
||||
type = type.GetGenericArguments()[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unable to determine entry type
|
||||
type = typeof(object);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool Internal_TryGetEntryTypes(Type type, out Type keys, out Type values)
|
||||
{
|
||||
if (base.Internal_TryGetEntryTypes(type, out keys, out values))
|
||||
return true;
|
||||
|
||||
// Type is either an IL2CPP dictionary, or its not generic.
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
// Naive solution until IL2CPP interfaces improve.
|
||||
var args = type.GetGenericArguments();
|
||||
if (args.Length == 2)
|
||||
{
|
||||
keys = args[0];
|
||||
values = args[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
keys = typeof(object);
|
||||
values = typeof(object);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Temp fix until Unhollower interface support improves
|
||||
|
||||
|
@ -151,12 +151,14 @@ namespace UnityExplorer
|
||||
internal virtual string Internal_ProcessTypeInString(string theString, Type type)
|
||||
=> theString;
|
||||
|
||||
// Force loading modules
|
||||
public static bool LoadModule(string moduleName)
|
||||
=> Instance.Internal_LoadModule(moduleName);
|
||||
//// Force loading modules
|
||||
//public static bool LoadModule(string moduleName)
|
||||
// => Instance.Internal_LoadModule(moduleName);
|
||||
//
|
||||
//internal virtual bool Internal_LoadModule(string moduleName)
|
||||
// => false;
|
||||
|
||||
internal virtual bool Internal_LoadModule(string moduleName)
|
||||
=> false;
|
||||
// Singleton finder
|
||||
|
||||
public static void FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List<object> instances)
|
||||
=> Instance.Internal_FindSingleton(possibleNames, type, flags, instances);
|
||||
@ -441,7 +443,6 @@ namespace UnityExplorer
|
||||
blacklist = Instance.DefaultReflectionBlacklist;
|
||||
ConfigManager.Reflection_Signature_Blacklist.Value = blacklist;
|
||||
ConfigManager.Handler.SaveConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(blacklist))
|
||||
@ -466,6 +467,7 @@ namespace UnityExplorer
|
||||
return false;
|
||||
|
||||
var sig = $"{member.DeclaringType.FullName}.{member.Name}";
|
||||
|
||||
return currentBlacklist.Contains(sig);
|
||||
}
|
||||
|
||||
@ -495,6 +497,39 @@ namespace UnityExplorer
|
||||
enumerator = (list as IEnumerable).GetEnumerator();
|
||||
return true;
|
||||
}
|
||||
|
||||
// TryGetEntryType
|
||||
|
||||
public static bool TryGetEntryType(Type enumerableType, out Type type)
|
||||
=> Instance.Internal_TryGetEntryType(enumerableType, out type);
|
||||
|
||||
protected virtual bool Internal_TryGetEntryType(Type enumerableType, out Type type)
|
||||
{
|
||||
// Check for arrays
|
||||
if (enumerableType.IsArray)
|
||||
{
|
||||
type = enumerableType.GetElementType();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for implementation of IEnumerable<T>, IList<T> or ICollection<T>
|
||||
foreach (var t in enumerableType.GetInterfaces())
|
||||
{
|
||||
if (t.IsGenericType)
|
||||
{
|
||||
var typeDef = t.GetGenericTypeDefinition();
|
||||
if (typeDef == typeof(IEnumerable<>) || typeDef == typeof(IList<>) || typeDef == typeof(ICollection<>))
|
||||
{
|
||||
type = t.GetGenericArguments()[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unable to determine any generic element type, just use object.
|
||||
type = typeof(object);
|
||||
return false;
|
||||
}
|
||||
|
||||
// IsDictionary
|
||||
|
||||
@ -524,5 +559,28 @@ namespace UnityExplorer
|
||||
yield return new DictionaryEntry(enumerator.Key, enumerator.Value);
|
||||
}
|
||||
}
|
||||
|
||||
// TryGetEntryTypes
|
||||
|
||||
public static bool TryGetEntryTypes(Type dictionaryType, out Type keys, out Type values)
|
||||
=> Instance.Internal_TryGetEntryTypes(dictionaryType, out keys, out values);
|
||||
|
||||
protected virtual bool Internal_TryGetEntryTypes(Type dictionaryType, out Type keys, out Type values)
|
||||
{
|
||||
foreach (var t in dictionaryType.GetInterfaces())
|
||||
{
|
||||
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDictionary<,>))
|
||||
{
|
||||
var args = t.GetGenericArguments();
|
||||
keys = args[0];
|
||||
values = args[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
keys = typeof(object);
|
||||
values = typeof(object);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
public override void Initialize()
|
||||
{
|
||||
ExplorerCore.Context = RuntimeContext.IL2CPP;
|
||||
//Reflection = new Il2CppReflection();
|
||||
TextureUtil = new Il2CppTextureUtil();
|
||||
}
|
||||
|
||||
@ -30,19 +29,12 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
||||
{
|
||||
try
|
||||
{
|
||||
//Application.add_logMessageReceived(new Action<string, string, LogType>(ExplorerCore.Instance.OnUnityLog));
|
||||
|
||||
var logType = ReflectionUtility.GetTypeByName("UnityEngine.Application+LogCallback");
|
||||
var castMethod = logType.GetMethod("op_Implicit", new[] { typeof(Action<string, string, LogType>) });
|
||||
var addMethod = typeof(Application).GetMethod("add_logMessageReceived", BF.Static | BF.Public, null, new[] { logType }, null);
|
||||
addMethod.Invoke(null, new[]
|
||||
{
|
||||
castMethod.Invoke(null, new[] { new Action<string, string, LogType>(Application_logMessageReceived) })
|
||||
});
|
||||
Application.add_logMessageReceived(new Action<string, string, LogType>(Application_logMessageReceived));
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning("Exception setting up Unity log listener, make sure Unity libraries have been unstripped!");
|
||||
ExplorerCore.Log(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,40 @@ using UnhollowerBaseLib;
|
||||
|
||||
namespace UnityExplorer.Tests
|
||||
{
|
||||
public class TestIndexer : IList<int>
|
||||
{
|
||||
private readonly List<int> list = new List<int>() { 1,2,3,4,5 };
|
||||
|
||||
public int Count => list.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
int IList<int>.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<int> 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}");
|
||||
|
@ -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.3";
|
||||
public const string AUTHOR = "Sinai";
|
||||
public const string GUID = "com.sinai.unityexplorer";
|
||||
|
||||
|
@ -246,7 +246,6 @@ namespace UnityExplorer.UI.CacheObject
|
||||
var sig = GetSig(member);
|
||||
|
||||
//ExplorerCore.Log($"Trying to cache member {sig}...");
|
||||
//ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name);
|
||||
|
||||
CacheMember cached;
|
||||
Type returnType;
|
||||
@ -324,7 +323,8 @@ namespace UnityExplorer.UI.CacheObject
|
||||
}
|
||||
}
|
||||
|
||||
internal static string GetSig(MemberInfo member) => $"{member.DeclaringType.Name}.{member.Name}";
|
||||
internal static string GetSig(MemberInfo member)
|
||||
=> $"{member.DeclaringType.Name}.{member.Name}";
|
||||
|
||||
internal static string GetArgumentString(ParameterInfo[] args)
|
||||
{
|
||||
|
@ -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;
|
||||
@ -21,8 +22,8 @@ namespace UnityExplorer.UI.IValues
|
||||
|
||||
public override bool CanWrite => base.CanWrite && RefIDictionary != null && !RefIDictionary.IsReadOnly;
|
||||
|
||||
public Type KeyType;
|
||||
public Type ValueType;
|
||||
public Type KeysType;
|
||||
public Type ValuesType;
|
||||
public IDictionary RefIDictionary;
|
||||
|
||||
public int ItemCount => cachedEntries.Count;
|
||||
@ -75,23 +76,13 @@ namespace UnityExplorer.UI.IValues
|
||||
else
|
||||
{
|
||||
var type = value.GetActualType();
|
||||
if (type.IsGenericType && type.GetGenericArguments().Length == 2)
|
||||
{
|
||||
KeyType = type.GetGenericArguments()[0];
|
||||
ValueType = type.GetGenericArguments()[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyType = typeof(object);
|
||||
ValueType = typeof(object);
|
||||
}
|
||||
ReflectionUtility.TryGetEntryTypes(type, out KeysType, out ValuesType);
|
||||
|
||||
CacheEntries(value);
|
||||
|
||||
TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.Parse(type, false)}";
|
||||
}
|
||||
|
||||
|
||||
this.DictScrollPool.Refresh(true, false);
|
||||
}
|
||||
|
||||
@ -116,7 +107,7 @@ namespace UnityExplorer.UI.IValues
|
||||
else
|
||||
cache = cachedEntries[idx];
|
||||
|
||||
cache.SetFallbackType(ValueType);
|
||||
cache.SetFallbackType(ValuesType);
|
||||
cache.SetKey(dictEnumerator.Current.Key);
|
||||
cache.SetValueFromSource(dictEnumerator.Current.Value);
|
||||
|
||||
|
@ -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<object> values = new List<object>();
|
||||
private bool IsWritableGenericIList;
|
||||
private PropertyInfo genericIndexer;
|
||||
|
||||
public int ItemCount => cachedEntries.Count;
|
||||
private readonly List<CacheListEntry> cachedEntries = new List<CacheListEntry>();
|
||||
|
||||
public ScrollPool<CacheListEntryCell> 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,18 +68,13 @@ 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];
|
||||
else if (type.HasElementType)
|
||||
EntryType = type.GetElementType();
|
||||
else
|
||||
EntryType = typeof(object);
|
||||
ReflectionUtility.TryGetEntryType(type, out EntryType);
|
||||
|
||||
CacheEntries(value);
|
||||
|
||||
@ -92,7 +89,12 @@ namespace UnityExplorer.UI.IValues
|
||||
{
|
||||
RefIList = value as IList;
|
||||
|
||||
values.Clear();
|
||||
// Check if the type implements IList<T> 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 +105,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 +122,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 +141,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<T> type '{type.FullName}'!");
|
||||
IsWritableGenericIList = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning($"Exception processing IEnumerable for IList<T> 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);
|
||||
|
@ -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<VerticalLayoutGroup>(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<HorizontalLayoutGroup>(listHolder, false, true, true, true, 8, 2, 2, 2, 2);
|
||||
UIFactory.SetLayoutElement(listHolder, minHeight: 350, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||
//var listRect = listHolder.GetComponent<RectTransform>();
|
||||
//listRect.anchorMin = new Vector2(0, 1);
|
||||
//listRect.anchorMax = new Vector2(1, 1);
|
||||
UIFactory.SetLayoutElement(listHolder, minHeight: 150, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||
|
||||
// Left group (Children)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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<RectTransform>();
|
||||
Rect = this.uiRoot.GetComponent<RectTransform>();
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(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<VerticalLayoutGroup>(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<RectTransform>(), mainPanelRect, this);
|
||||
Dragger = new PanelDragger(titleBar.GetComponent<RectTransform>(), 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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -298,7 +298,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
if (CellPool.Count > 1)
|
||||
{
|
||||
int index = CellPool.Count - 1 - (topPoolIndex % (CellPool.Count - 1));
|
||||
cell.Rect.SetSiblingIndex(index);
|
||||
cell.Rect.SetSiblingIndex(index + 1);
|
||||
|
||||
if (bottomPoolIndex == index - 1)
|
||||
bottomPoolIndex++;
|
||||
|
Reference in New Issue
Block a user