diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..68425d93f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/@Solutions/ZiosThemes.dll diff --git a/@Solutions/3.5.7/UnityEditor.dll b/@Solutions/3.5.7/UnityEditor.dll new file mode 100644 index 000000000..e67d8a5ad Binary files /dev/null and b/@Solutions/3.5.7/UnityEditor.dll differ diff --git a/@Solutions/3.5.7/UnityEngine.dll b/@Solutions/3.5.7/UnityEngine.dll new file mode 100644 index 000000000..ab6e95bad Binary files /dev/null and b/@Solutions/3.5.7/UnityEngine.dll differ diff --git a/@Solutions/4.7.2/UnityEditor.dll b/@Solutions/4.7.2/UnityEditor.dll new file mode 100644 index 000000000..bb805ba1c Binary files /dev/null and b/@Solutions/4.7.2/UnityEditor.dll differ diff --git a/@Solutions/4.7.2/UnityEngine.dll b/@Solutions/4.7.2/UnityEngine.dll new file mode 100644 index 000000000..a3c7fe225 Binary files /dev/null and b/@Solutions/4.7.2/UnityEngine.dll differ diff --git a/@Solutions/5.4/UnityEditor.dll b/@Solutions/5.4/UnityEditor.dll new file mode 100644 index 000000000..9d27f76eb Binary files /dev/null and b/@Solutions/5.4/UnityEditor.dll differ diff --git a/@Solutions/5.4/UnityEngine.dll b/@Solutions/5.4/UnityEngine.dll new file mode 100644 index 000000000..7b6ed0e3b Binary files /dev/null and b/@Solutions/5.4/UnityEngine.dll differ diff --git a/@Solutions/Compile Themes 3.5.7.bat b/@Solutions/Compile Themes 3.5.7.bat new file mode 100644 index 000000000..617f1e804 --- /dev/null +++ b/@Solutions/Compile Themes 3.5.7.bat @@ -0,0 +1,27 @@ +@ECHO OFF +copy /Y 3.5.7\* . >NUL +del ZiosThemes.dll +IF EXIST "%programfiles%\MSBuild\14.0\Bin" GOTO USE_VS14_X32 +IF EXIST "%programfiles(x86)%\MSBuild\14.0\Bin" GOTO USE_VS14_X64 +IF EXIST "%programfiles%\MSBuild\15.0\Bin" GOTO USE_VS15_X32 +IF EXIST "%programfiles(x86)%\MSBuild\15.0\Bin" GOTO USE_VS15_X64 +GOTO CLOSE +:USE_VS14_X32 + echo Compiling ZiosThemes.dll - Visual Studio 2015 / MSBuild 2015 (on 32-bit host)... + "%programfiles%\MSBuild\14.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:USE_VS14_X64 + echo Compiling ZiosThemes.dll - Visual Studio 2015 / MSBuild 2015 (on 64-bit host)... + "%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:USE_VS15_X32 + echo Compiling ZiosThemes.dll - Visual Studio 15 / MSBuild 15 (on 32-bit host)... + "%programfiles%\MSBuild\15.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:USE_VS15_X64 + echo Compiling ZiosThemes.dll - Visual Studio 15 / MSBuild 15 (on 64-bit host)... + "%programfiles(x86)%\MSBuild\15.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:CLOSE +copy /Y 5.4\* . >NUL +rmdir /Q /S obj \ No newline at end of file diff --git a/@Solutions/Compile Themes 4.7.2.bat b/@Solutions/Compile Themes 4.7.2.bat new file mode 100644 index 000000000..ea91ecba5 --- /dev/null +++ b/@Solutions/Compile Themes 4.7.2.bat @@ -0,0 +1,27 @@ +@ECHO OFF +copy /Y 4.7.2\* . >NUL +del ZiosThemes.dll +IF EXIST "%programfiles%\MSBuild\14.0\Bin" GOTO USE_VS14_X32 +IF EXIST "%programfiles(x86)%\MSBuild\14.0\Bin" GOTO USE_VS14_X64 +IF EXIST "%programfiles%\MSBuild\15.0\Bin" GOTO USE_VS15_X32 +IF EXIST "%programfiles(x86)%\MSBuild\15.0\Bin" GOTO USE_VS15_X64 +GOTO CLOSE +:USE_VS14_X32 + echo Compiling ZiosThemes.dll - Visual Studio 2015 / MSBuild 2015 (on 32-bit host)... + "%programfiles%\MSBuild\14.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:USE_VS14_X64 + echo Compiling ZiosThemes.dll - Visual Studio 2015 / MSBuild 2015 (on 64-bit host)... + "%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:USE_VS15_X32 + echo Compiling ZiosThemes.dll - Visual Studio 15 / MSBuild 15 (on 32-bit host)... + "%programfiles%\MSBuild\15.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:USE_VS15_X64 + echo Compiling ZiosThemes.dll - Visual Studio 15 / MSBuild 15 (on 64-bit host)... + "%programfiles(x86)%\MSBuild\15.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:CLOSE +copy /Y 5.4\* . >NUL +rmdir /Q /S obj \ No newline at end of file diff --git a/@Solutions/Compile Themes 5.4 (Mono Windows).bat b/@Solutions/Compile Themes 5.4 (Mono Windows).bat new file mode 100644 index 000000000..5bcdb6fff --- /dev/null +++ b/@Solutions/Compile Themes 5.4 (Mono Windows).bat @@ -0,0 +1,7 @@ +@ECHO OFF +copy /Y 5.4\* . >NUL +echo Compiling ZiosThemes.dll - Mono Framework 4.5 / Unity 5.x... +del ZiosThemes.dll +call "%programfiles%\Unity\Editor\Data\MonoBleedingEdge\bin\xbuild.bat" ZiosThemes.sln +rmdir /Q /S obj +copy /Y 5.4\* . >NUL \ No newline at end of file diff --git a/@Solutions/Compile Themes 5.4.bat b/@Solutions/Compile Themes 5.4.bat new file mode 100644 index 000000000..d5d782d0a --- /dev/null +++ b/@Solutions/Compile Themes 5.4.bat @@ -0,0 +1,27 @@ +@ECHO OFF +copy /Y 5.4\* . >NUL +del ZiosThemes.dll +IF EXIST "%programfiles%\MSBuild\14.0\Bin" GOTO USE_VS14_X32 +IF EXIST "%programfiles(x86)%\MSBuild\14.0\Bin" GOTO USE_VS14_X64 +IF EXIST "%programfiles%\MSBuild\15.0\Bin" GOTO USE_VS15_X32 +IF EXIST "%programfiles(x86)%\MSBuild\15.0\Bin" GOTO USE_VS15_X64 +GOTO CLOSE +:USE_VS14_X32 + echo Compiling ZiosThemes.dll - Visual Studio 2015 / MSBuild 2015 (on 32-bit host)... + "%programfiles%\MSBuild\14.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:USE_VS14_X64 + echo Compiling ZiosThemes.dll - Visual Studio 2015 / MSBuild 2015 (on 64-bit host)... + "%programfiles(x86)%\MSBuild\14.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:USE_VS15_X32 + echo Compiling ZiosThemes.dll - Visual Studio 15 / MSBuild 15 (on 32-bit host)... + "%programfiles%\MSBuild\15.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:USE_VS15_X64 + echo Compiling ZiosThemes.dll - Visual Studio 15 / MSBuild 15 (on 64-bit host)... + "%programfiles(x86)%\MSBuild\15.0\Bin\MSBuild.exe" ZiosThemes.sln + GOTO CLOSE +:CLOSE +copy /Y 5.4\* . >NUL +rmdir /Q /S obj \ No newline at end of file diff --git a/@Solutions/Compile Themes Mac OS X.sh b/@Solutions/Compile Themes Mac OS X.sh new file mode 100644 index 000000000..27beb2f52 --- /dev/null +++ b/@Solutions/Compile Themes Mac OS X.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +rm ZiosThemes.dll +/Applications/Unity/Unity.app/Contents/MonoBleedingEdge/bin/xbuild ZiosThemesMono.sln +rm -R obj diff --git a/@Solutions/Download MSBuild Tools 2015.url b/@Solutions/Download MSBuild Tools 2015.url new file mode 100644 index 000000000..e15a96826 --- /dev/null +++ b/@Solutions/Download MSBuild Tools 2015.url @@ -0,0 +1,2 @@ +[InternetShortcut] +URL=https://go.microsoft.com/fwlink/?LinkId=615458 diff --git a/@Solutions/Properties/AssemblyInfo.cs b/@Solutions/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..6d4ed75aa --- /dev/null +++ b/@Solutions/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Zios Themes")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Zios")] +[assembly: AssemblyProduct("ZiosThemes")] +[assembly: AssemblyCopyright("Copyright © Zios 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("90c40447-c2b4-4725-9cea-9e0e8199eefc")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/@Solutions/UnityEditor.dll b/@Solutions/UnityEditor.dll new file mode 100644 index 000000000..9d27f76eb Binary files /dev/null and b/@Solutions/UnityEditor.dll differ diff --git a/@Solutions/UnityEngine.dll b/@Solutions/UnityEngine.dll new file mode 100644 index 000000000..7b6ed0e3b Binary files /dev/null and b/@Solutions/UnityEngine.dll differ diff --git a/@Solutions/ZiosThemes.csproj b/@Solutions/ZiosThemes.csproj new file mode 100644 index 000000000..1fccbec3f --- /dev/null +++ b/@Solutions/ZiosThemes.csproj @@ -0,0 +1,97 @@ + + + + + Release + AnyCPU + {90C40447-C2B4-4725-9CEA-9E0E8199EEFC} + Library + Properties + ZiosThemes + ZiosThemes + v3.5 + 512 + + + true + . + prompt + 4 + UNITY_EDITOR;UNITY_EDITOR_64;UNITY_EDITOR_WIN;UNITY_STANDALONE;UNITY_THEMES + + + + + + UnityEditor.dll + + + UnityEngine.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/@Solutions/ZiosThemes.sln b/@Solutions/ZiosThemes.sln new file mode 100644 index 000000000..075597457 --- /dev/null +++ b/@Solutions/ZiosThemes.sln @@ -0,0 +1,17 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZiosThemes", "ZiosThemes.csproj", "{90C40447-C2B4-4725-9CEA-9E0E8199EEFC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {90C40447-C2B4-4725-9CEA-9E0E8199EEFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90C40447-C2B4-4725-9CEA-9E0E8199EEFC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Containers/Container.cs b/Containers/Container.cs new file mode 100644 index 000000000..13f0e4cdb --- /dev/null +++ b/Containers/Container.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +namespace Zios.Containers{ + [Serializable] + public class Container{ + public List keys = new List(); + public List values = new List(); + public Dictionary collection = new Dictionary(); + public int Count{ + get{return this.collection.Count;} + set{} + } + public TValue this[TKey key]{ + get{ + int index = this.keys.IndexOf(key); + return this.values.ElementAt(index); + } + set{ + int index = this.keys.IndexOf(key); + if(index == -1){ + this.keys.Add(key); + this.values.Add(value); + this.collection = this.keys.ToDictionary(x => x,x => this.values[this.keys.IndexOf(x)]); + } + else{ + this.values[index] = value; + } + } + } + public void Clear(){ + this.keys.Clear(); + this.values.Clear(); + this.collection.Clear(); + } + public IEnumerator> GetEnumerator(){ + return this.collection.GetEnumerator(); + } + } + [Serializable] public class IntContainer : Container{} + [Serializable] public class StringContainer : Container{} + [Serializable] public class FloatContainer : Container{} + [Serializable] public class BoolContainer : Container{} + [Serializable] public class GameObjectContainer : Container{} + [Serializable] + public class FixedContainer{ + public TKey[] keys; + public TValue[] values; + private int nextIndex = 0; + public FixedContainer(int size){ + this.keys = new TKey[size]; + this.values = new TValue[size]; + } + public TValue this[TKey key]{ + get{ + int index = Array.IndexOf(this.keys,key); + return this.values[index]; + } + set{ + int index = Array.IndexOf(this.keys,key); + if(index == -1){ + this.keys[this.nextIndex] = key; + this.values[this.nextIndex] = value; + ++this.nextIndex; + } + else{ + this.values[index] = value; + } + } + } + } + public class FixedList : List{ + public int maxSize = 0; + public FixedList(int size) : base(size){ + this.maxSize = size; + } + public new void Add(T item){ + if(this.Count >= this.maxSize){ + this.RemoveAt(0); + } + base.Add(item); + } + } + public class Hierarchy : Dictionary{public Hierarchy():base(){}} + public class Hierarchy : Dictionary>{public Hierarchy():base(){}} + public class Hierarchy : Dictionary>>{public Hierarchy():base(){}} + public class Hierarchy : Dictionary>>>{public Hierarchy():base(){}} + public class Hierarchy : Dictionary>>>{public Hierarchy():base(){}} + public class Hierarchy : Dictionary>>>>{public Hierarchy():base(){}} + public class Hierarchy : Dictionary>>>>>{public Hierarchy():base(){}} + public class Hierarchy : Dictionary>>>>>>{public Hierarchy():base(){}} +} \ No newline at end of file diff --git a/Extensions/@Unity/AnimationCurve.cs b/Extensions/@Unity/AnimationCurve.cs new file mode 100644 index 000000000..0a4bb96d0 --- /dev/null +++ b/Extensions/@Unity/AnimationCurve.cs @@ -0,0 +1,31 @@ +using UnityEngine; +using System; +using System.Text; +using System.Collections.Generic; +namespace Zios{ + public static class AnimationCurveExtension{ + public static string Serialize(this AnimationCurve current){ + var output = new StringBuilder(); + foreach(var key in current.keys){ + output.Append(key.time); + output.Append("-"); + output.Append(key.value); + output.Append("-"); + output.Append(key.inTangent); + output.Append("-"); + output.Append(key.outTangent); + output.Append("|"); + } + return output.ToString().TrimRight("|"); + } + public static AnimationCurve Deserialize(this AnimationCurve current,string value){ + var keys = new List(); + foreach(var keyData in value.Split("|")){ + var data = keyData.Split("-").Convert(); + keys.Add(new Keyframe(data[0],data[1],data[2],data[3])); + } + current.keys = keys.ToArray(); + return current; + } + } +} \ No newline at end of file diff --git a/Extensions/@Unity/AnimationExtensions.cs b/Extensions/@Unity/AnimationExtensions.cs new file mode 100644 index 000000000..da788846d --- /dev/null +++ b/Extensions/@Unity/AnimationExtensions.cs @@ -0,0 +1,9 @@ +using UnityEngine; +namespace Zios{ + public static class AnimationExtension{ + public static void Set(this Animation current,string name,bool state){ + if(state){current.Play(name);} + else{current.Stop(name);} + } + } +} \ No newline at end of file diff --git a/Extensions/@Unity/Color.cs b/Extensions/@Unity/Color.cs new file mode 100644 index 000000000..20bdc4149 --- /dev/null +++ b/Extensions/@Unity/Color.cs @@ -0,0 +1,53 @@ +using System.Linq; +using UnityEngine; +namespace Zios{ + using System.Collections.Generic; + public static class ColorExtension{ + public static Color Add(this Color current,Color amount){ + current.r = Mathf.Clamp(current.r+amount.r,0,1); + current.g = Mathf.Clamp(current.g+amount.g,0,1); + current.b = Mathf.Clamp(current.b+amount.b,0,1); + return current; + } + public static Color Add(this Color current,float amount){ + current.r = Mathf.Clamp(current.r+amount,0,1); + current.g = Mathf.Clamp(current.g+amount,0,1); + current.b = Mathf.Clamp(current.b+amount,0,1); + return current; + } + public static Color Multiply(this Color current,Color amount){ + current.r = Mathf.Clamp(current.r*amount.r,0,1); + current.g = Mathf.Clamp(current.g*amount.g,0,1); + current.b = Mathf.Clamp(current.b*amount.b,0,1); + return current; + } + public static Color Multiply(this Color current,float amount){ + current.r = Mathf.Clamp(current.r*amount,0,1); + current.g = Mathf.Clamp(current.g*amount,0,1); + current.b = Mathf.Clamp(current.b*amount,0,1); + return current; + } + public static Color Random(this Color current,float intensity=1.0f){ + int[] order = (new List(){0,1,2}).Shuffle().ToArray(); + float[] color = new float[3]; + color[order[0]] = UnityEngine.Random.Range(intensity,1.0f); + color[order[1]] = UnityEngine.Random.Range(0,1.0f - intensity); + color[order[2]] = UnityEngine.Random.Range(0,1.0f); + return new Color(color[0],color[1],color[2]); + } + public static string ToHex(this Color current,bool alwaysAlpha=true){ + var red = (current.r*255).ToInt().ToString("X2"); + var green = (current.g*255).ToInt().ToString("X2"); + var blue = (current.b*255).ToInt().ToString("X2"); + var alpha = (current.a*255).ToInt().ToString("X2"); + if(alpha == "FF" && !alwaysAlpha){alpha = "";} + return "#"+red+green+blue+alpha; + } + public static string Serialize(this Color current){ + return current.ToHex(false); + } + public static Color Deserialize(this Color current,string value){ + return value.ToColor("-"); + } + } +} \ No newline at end of file diff --git a/Extensions/@Unity/ComponentExtensions.cs b/Extensions/@Unity/ComponentExtensions.cs new file mode 100644 index 000000000..6e3d8f6cd --- /dev/null +++ b/Extensions/@Unity/ComponentExtensions.cs @@ -0,0 +1,97 @@ +using UnityEngine; +namespace Zios{ + public static class ComponentExtension{ + public static void UpdateSerialized(this Component current){ + #if UNITY_EDITOR + Utility.UpdateSerialized(current); + #endif + } + public static GameObject GetPrefabRoot(this Component current){ + return current.gameObject.GetPrefabRoot(); + } + public static GameObject GetParent(this Component current){ + return current.gameObject.GetParent(); + } + public static string GetPath(this Component current,bool includeSelf=true){ + if(current.IsNull() || current.gameObject.IsNull()){return "Null";} + string path = current.gameObject.GetPath(); + if(includeSelf){path += current.GetAlias();} + return path; + } + public static bool IsPrefab(this Component current){ + return current.gameObject.IsPrefab(); + } + public static bool IsEnabled(this Component current){ + bool enabled = !current.IsNull() && current.gameObject.activeInHierarchy; + if(current is MonoBehaviour){enabled = enabled && current.As().enabled;} + return enabled; + } + public static void Move(this Component current,int amount){ + Utility.DisconnectPrefabInstance(current); + while(amount != 0){ + if(amount > 0){ + Utility.MoveComponentDown(current); + amount -= 1; + } + if(amount < 0){ + Utility.MoveComponentUp(current); + amount += 1; + } + } + } + public static void MoveUp(this Component current){ + Component[] components = current.GetComponents(); + int position = components.IndexOf(current); + int amount = 1; + if(position != 0){ + while(components[position-1].hideFlags.Contains(HideFlags.HideInInspector)){ + position -= 1; + amount += 1; + } + } + current.Move(-amount); + } + public static void MoveDown(this Component current){ + Component[] components = current.GetComponents(); + int position = components.IndexOf(current); + int amount = 1; + if(position < components.Length-1){ + while(components[position+1].hideFlags.Contains(HideFlags.HideInInspector)){ + position += 1; + amount += 1; + } + } + current.Move(amount); + } + public static void MoveToTop(this Component current){ + Utility.DisconnectPrefabInstance(current); + Component[] components = current.GetComponents(); + int position = components.IndexOf(current); + current.Move(-position); + } + public static void MoveToBottom(this Component current){ + Utility.DisconnectPrefabInstance(current); + Component[] components = current.GetComponents(); + int position = components.IndexOf(current); + current.Move(components.Length-position); + } + //==================== + // Interface + //==================== + public static Component[] GetComponentsByInterface(this Component current) where T : Component{ + return current.gameObject.GetComponentsByInterface(); + } + public static T GetComponent(this Component current,bool includeInactive) where T : Component{ + return current.gameObject.GetComponent(includeInactive); + } + public static T[] GetComponents(this Component current,bool includeInactive) where T : Component{ + return current.gameObject.GetComponents(includeInactive); + } + public static T GetComponentInParent(this Component current,bool includeInactive) where T : Component{ + return current.gameObject.GetComponentInParent(includeInactive); + } + public static T GetComponentInChildren(this Component current,bool includeInactive) where T : Component{ + return current.gameObject.GetComponentInChildren(includeInactive); + } + } +} \ No newline at end of file diff --git a/Extensions/@Unity/Editor/GenericMenu.cs b/Extensions/@Unity/Editor/GenericMenu.cs new file mode 100644 index 000000000..7a929fbad --- /dev/null +++ b/Extensions/@Unity/Editor/GenericMenu.cs @@ -0,0 +1,12 @@ +using UnityEditor; +using UnityEngine; +namespace Zios{ + public static class GenericMenuExtension{ + public static void AddItem(this GenericMenu current,string label,bool state,GenericMenu.MenuFunction method){ + current.AddItem(new GUIContent(label),state,method); + } + public static void AddItem(this GenericMenu current,string label,bool state,GenericMenu.MenuFunction2 method,object data){ + current.AddItem(new GUIContent(label),state,method,data); + } + } +} \ No newline at end of file diff --git a/Extensions/@Unity/EditorGUI.cs b/Extensions/@Unity/EditorGUI.cs new file mode 100644 index 000000000..67e71ef80 --- /dev/null +++ b/Extensions/@Unity/EditorGUI.cs @@ -0,0 +1,222 @@ +#if UNITY_EDITOR +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityObject = UnityEngine.Object; +namespace Zios.Interface{ + public static class LabelExtensions{ + public static UnityLabel ToLabel(this GUIContent current){ + return new UnityLabel(current); + } + public static UnityLabel ToLabel(this string current){ + return new UnityLabel(current); + } + } + public class UnityLabel{ + public GUIContent value = new GUIContent(""); + public UnityLabel(string value){this.value = new GUIContent(value);} + public UnityLabel(GUIContent value){this.value = value;} + public override string ToString(){return this.value.text;} + public GUIContent ToContent(){return this.value;} + //public static implicit operator string(UnityLabel current){return current.value.text;} + public static implicit operator GUIContent(UnityLabel current){ + if(current == null){return GUIContent.none;} + return current.value; + } + public static implicit operator UnityLabel(GUIContent current){return new UnityLabel(current);} + public static implicit operator UnityLabel(string current){return new UnityLabel(current);} + } + public static class EditorGUIExtension{ + public static bool render = true; + public static bool lastChanged; + public static Type Draw(Func method,bool indention=true){ + int indentValue = EditorGUI.indentLevel; + if(!indention){EditorGUI.indentLevel = 0;} + bool wasChanged = GUI.changed; + GUI.changed = false; + Type value = (Type)method(); + EditorGUIExtension.lastChanged = GUI.changed; + GUI.changed = GUI.changed || wasChanged; + EditorGUI.indentLevel = indentValue; + return value; + } + public static void Draw(Action method,bool indention=true){ + int indentValue = EditorGUI.indentLevel; + if(!indention){EditorGUI.indentLevel = 0;} + if(EditorGUIExtension.render){method();} + if(!indention){EditorGUI.indentLevel = indentValue;} + } + public static string Draw(this string current,Rect area,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.textField; + return EditorGUIExtension.Draw(()=>EditorGUI.TextField(area,label,current,style),indention); + } + public static float Draw(this float current,Rect area,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.numberField; + return EditorGUIExtension.Draw(()=>EditorGUI.FloatField(area,label,current,style),indention); + } + public static bool Draw(this bool current,Rect area,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.toggle; + return EditorGUIExtension.Draw(()=>EditorGUI.Toggle(area,label,current,style),indention); + } + public static Enum Draw(this Enum current,Rect area,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.popup; + return EditorGUIExtension.Draw(()=>EditorGUI.EnumPopup(area,label,current,style),indention); + } + public static int Draw(this IEnumerable current,Rect area,int index,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.popup; + string name = label.IsNull() ? "" : label.ToString(); + return EditorGUIExtension.Draw(()=>EditorGUI.Popup(area,name,index,current.ToArray(),style),indention); + } + public static void DrawMenu(this IEnumerable current,Rect area,GenericMenu.MenuFunction2 callback,IEnumerable selected=null,IEnumerable disabled=null){ + if(selected.IsNull()){selected = new List();} + if(disabled.IsNull()){disabled = new List();} + var menu = new GenericMenu(); + var index = 0; + foreach(var item in current){ + ++index; + if(!disabled.Contains(item)){ + menu.AddItem(item.ToContent(),selected.Contains(item),callback,index-1); + continue; + } + menu.AddDisabledItem(item.ToContent()); + } + menu.DropDown(area); + } + public static void Draw(this SerializedProperty current,Rect area,UnityLabel label=null,bool allowScene=true,bool indention=true){ + if(label != null && label.value.text.IsEmpty()){label = new GUIContent(current.displayName);} + EditorGUIExtension.Draw(()=>EditorGUI.PropertyField(area,current,label,allowScene),indention); + } + public static Rect Draw(this Rect current,Rect area,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUI.RectField(area,label,current),indention); + } + public static AnimationCurve Draw(this AnimationCurve current,Rect area,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUI.CurveField(area,label,current),indention); + } + public static Color Draw(this Color current,Rect area,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUI.ColorField(area,label,current),indention); + } + } + public static class EditorGUIExtensionSpecial{ + public static Rect menuArea; + public static object menuValue; + public static void DrawAuto(this object current,Rect area,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + if(current is string){current.As().Draw(area,label,style,indention);} + if(current is int){current.As().DrawInt(area,label,style,indention);} + if(current is float){current.As().Draw(area,label,style,indention);} + if(current is Enum){current.As().Draw(area,label,style,indention);} + if(current is SerializedProperty){current.As().Draw(area,label,indention);} + if(current is AnimationCurve){current.As().Draw(area,label,indention);} + if(current is Color){current.As().Draw(area,label,indention);} + if(current is Rect){current.As().Draw(area,label,indention);} + if(current is GameObject){current.As().Draw(area,label,indention);} + if(current is Component){current.As().Draw(area,label,indention);} + if(current is Material){current.As().Draw(area,label,indention);} + if(current is Shader){current.As().Draw(area,label,indention);} + if(current is Vector2){current.As().DrawVector2(area,label,indention);} + if(current is Vector3){current.As().DrawVector3(area,label,indention);} + if(current is Vector4){current.As().DrawVector4(area,label,indention);} + } + public static bool DrawFoldout(this UnityLabel current,Rect area,object key,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.foldout; + string name = key.IsNull() ? current + "Foldout" : key.GetHashCode().ToString(); + if(key is string){name = (string)key;} + bool previous = EditorPrefs.GetBool(name); + bool state = EditorGUIExtension.Draw(()=>EditorGUI.Foldout(area,previous,current,style),indention); + if(previous != state){EditorPrefs.SetBool(name,state);} + return state; + } + public static bool DrawHeader(this UnityLabel current,Rect area,object key,GUIStyle style=null,bool indention=true){ + string stateName = key.IsNull() ? current + "Foldout" : key.GetHashCode().ToString(); + if(key is string){stateName = (string)key;} + bool state = EditorPrefs.GetBool(stateName); + current = state ? "▼ " + current : "▶ " + current; + var currentStyle = style.IsNull() ? null : new GUIStyle(style); + if(state){currentStyle.normal = currentStyle.active;} + if(current.DrawButton(area,currentStyle,indention)){ + state = !state; + EditorPrefs.SetBool(stateName,state); + } + return state; + } + //public static void DrawLabel(this string current,Rect area,GUIStyle style=null,bool indention=true){new UnityLabel(current).DrawLabel(area,style,indention);} + //public static void DrawLabel(this GUIContent current,Rect area,GUIStyle style=null,bool indention=true){new UnityLabel(current).DrawLabel(area,style,indention);} + public static void DrawLabel(this UnityLabel current,Rect area,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.label; + EditorGUIExtension.Draw(()=>EditorGUI.LabelField(area,current,style),indention); + } + public static void DrawHelp(this string current,Rect area,string textType,bool indention=true){ + MessageType type = MessageType.None; + if(textType.Contains("Info",true)){type = MessageType.Info;} + if(textType.Contains("Error",true)){type = MessageType.Error;} + if(textType.Contains("Warning",true)){type = MessageType.Warning;} + EditorGUIExtension.Draw(()=>EditorGUI.HelpBox(area,current,type),indention); + } + public static string DrawTextArea(this string current,Rect area,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.textField; + return EditorGUIExtension.Draw(()=>EditorGUI.TextField(area,label,current,style),indention); + } + //public static bool DrawButton(this string current,Rect area,GUIStyle style=null,bool indention=true){return new UnityLabel(current).DrawButton(area,style,indention);} + //public static bool DrawButton(this GUIContent current,Rect area,GUIStyle style=null,bool indention=true){return new UnityLabel(current).DrawButton(area,style,indention);} + public static bool DrawButton(this UnityLabel current,Rect area,GUIStyle style=null,bool indention=true){ + style = style ?? GUI.skin.button; + return EditorGUIExtension.Draw(()=>GUI.Button(area,current,style),indention); + } + public static int DrawInt(this int current,Rect area,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.numberField; + return EditorGUIExtension.Draw(()=>EditorGUI.IntField(area,label,current,style),indention); + } + public static float DrawSlider(this float current,Rect area,float min,float max,bool indention=true){ + var value = EditorGUIExtension.Draw(()=>GUI.HorizontalSlider(area,current,min,max),indention); + if(value != current){GUI.FocusControl("");} + return value; + } + public static Type Draw(this UnityObject current,Rect area,UnityLabel label=null,bool allowScene=true,bool indention=true) where Type : UnityObject{ + return (Type)EditorGUIExtension.Draw(()=>EditorGUI.ObjectField(area,label,current,typeof(Type),allowScene),indention); + } + public static Enum DrawMask(this Enum current,Rect area,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.popup; + string value = current.ToName().Replace(" "," | ").ToTitleCase(); + Rect valueArea = area; + if(!label.IsNull()){ + Rect labelArea = area.AddWidth(-EditorGUIUtility.labelWidth); + valueArea = labelArea.AddX(EditorGUIUtility.labelWidth); + if(value.IsEmpty()){value = "None";} + label.DrawLabel(labelArea,null,true); + } + if(GUI.Button(valueArea,value.Trim("| "),style)){ + var items = current.ToName().Split(" ").ToTitleCase(); + GenericMenu.MenuFunction2 callback = index=>{ + EditorGUIExtensionSpecial.menuArea = area; + EditorGUIExtensionSpecial.menuValue = current.GetValues().GetValue((int)index); + }; + current.GetNames().ToTitleCase().DrawMenu(valueArea,callback,items); + } + if(EditorGUIExtensionSpecial.menuArea == area && !EditorGUIExtensionSpecial.menuValue.IsNull()){ + var menuValue = (Enum)EditorGUIExtensionSpecial.menuValue; + var newValue = current.ToInt() ^ menuValue.ToInt(); + current = (Enum)Enum.ToObject(current.GetType(),newValue); + EditorGUIExtensionSpecial.menuValue = null; + EditorGUIExtensionSpecial.menuArea = new Rect(); + GUI.changed = true; + } + return current; + } + public static Enum DrawMaskField(this Enum current,Rect area,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.popup; + return EditorGUIExtension.Draw(()=>EditorGUI.EnumMaskField(area,label,current,style),indention); + } + public static Vector2 DrawVector2(this Vector2 current,Rect area,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUI.Vector2Field(area,label,current),indention); + } + public static Vector3 DrawVector3(this Vector3 current,Rect area,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUI.Vector3Field(area,label,current),indention); + } + public static Vector4 DrawVector4(this Vector4 current,Rect area,UnityLabel label=null,bool indention=true){ + string name = label.IsNull() ? null : label.ToString(); + return EditorGUIExtension.Draw(()=>EditorGUI.Vector4Field(area,name,current),indention); + } + } +} +#endif \ No newline at end of file diff --git a/Extensions/@Unity/EditorGUILayout.cs b/Extensions/@Unity/EditorGUILayout.cs new file mode 100644 index 000000000..76c750cfd --- /dev/null +++ b/Extensions/@Unity/EditorGUILayout.cs @@ -0,0 +1,203 @@ +#if UNITY_EDITOR +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; +using UnityObject = UnityEngine.Object; +namespace Zios.Interface{ + public static class EditorGUILayoutExtension{ + public static string Draw(this string current,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.textField; + return EditorGUIExtension.Draw(()=>EditorGUILayout.TextField(label,current,style,style.CreateLayout()),indention); + } + public static float Draw(this float current,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.numberField; + return EditorGUIExtension.Draw(()=>EditorGUILayout.FloatField(label,current,style,style.CreateLayout()),indention); + } + public static bool Draw(this bool current,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.toggle; + return EditorGUIExtension.Draw(()=>EditorGUILayout.Toggle(label,current,style,style.CreateLayout()),indention); + } + public static Enum Draw(this Enum current,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.popup; + return EditorGUIExtension.Draw(()=>EditorGUILayout.EnumPopup(label,current,style,style.CreateLayout()),indention); + } + public static int Draw(this IEnumerable current,int index,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.popup; + return EditorGUIExtension.Draw(()=>EditorGUILayout.Popup(label.ToString(),index,current.ToArray(),style),indention); + } + public static void Draw(this SerializedProperty current,UnityLabel label=null,bool allowScene=true,bool indention=true){ + if(label != null && label.value.text.IsEmpty()){label = new GUIContent(current.displayName);} + Action action = ()=>EditorGUILayout.PropertyField(current,label,allowScene); + EditorGUIExtension.Draw(action,indention); + } + public static Rect Draw(this Rect current,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUILayout.RectField(label,current),indention); + } + public static AnimationCurve Draw(this AnimationCurve current,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUILayout.CurveField(label,current),indention); + } + public static Color Draw(this Color current,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUILayout.ColorField(label,current),indention); + } + public static void Draw(this IList current,string header="List"){ + if(header.IsEmpty() || EditorGUILayoutExtensionSpecial.DrawFoldout(header)){ + if(!header.IsEmpty()){EditorGUI.indentLevel += 1;} + for(int index=0;index().ToList().Draw(label); + continue; + } + if(!item.IsNull() && item.GetType() != typeof(UnityObject) && item.GetType().IsClass && item.GetType().IsSerializable && !(item is string)){ + item.DrawFields(label); + continue; + } + item.DrawAuto(label); + } + if(!header.IsEmpty()){EditorGUI.indentLevel -= 1;} + } + } + public static void Draw(this IDictionary current,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + var open = EditorGUILayoutExtensionSpecial.DrawFoldout(label.ToString().ToTitleCase()); + if(!open){return;} + EditorGUI.indentLevel += 1; + foreach(DictionaryEntry item in current){ + item.Value.DrawAuto(item.Key.ToString(),style,true); + } + EditorGUI.indentLevel -= 1; + } + } + public static class EditorGUILayoutExtensionSpecial{ + public static void DrawAuto(this object current,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + bool isDictionary = current.GetType().IsGenericType && current.GetType().GetGenericTypeDefinition() == typeof(Dictionary<,>); + if(current is string){current.As().Draw(label,style,indention);} + if(current is int){current.As().DrawInt(label,style,indention);} + if(current is float){current.As().Draw(label,style,indention);} + if(current is Enum){current.As().Draw(label,style,indention);} + if(current is SerializedProperty){current.As().Draw(label,indention);} + if(current is AnimationCurve){current.As().Draw(label,indention);} + if(current is Color){current.As().Draw(label,indention);} + if(current is Rect){current.As().Draw(label,indention);} + if(current is GameObject){current.As().Draw(label,indention);} + if(current is Component){current.As().Draw(label,indention);} + if(current is Material){current.As().Draw(label,indention);} + if(current is Shader){current.As().Draw(label,indention);} + if(current is Vector2){current.As().DrawVector2(label,indention);} + if(current is Vector3){current.As().DrawVector3(label,indention);} + if(current is Vector4){current.As().DrawVector4(label,indention);} + if(isDictionary){current.As().Draw(label,style,indention);} + } + public static void DrawFields(this object current,string header="Fields"){ + if(header.IsEmpty() || EditorGUILayoutExtensionSpecial.DrawFoldout(header)){ + if(!header.IsEmpty()){EditorGUI.indentLevel += 1;} + foreach(var item in current.GetVariables()){ + string label = item.Key.ToTitleCase(); + object field = item.Value; + if(field is ICollection){ + var enumerable = field as IEnumerable; + enumerable.OfType().ToList().Draw(label); + continue; + } + if(field.GetType().IsClass && field.GetType().IsSerializable && !(field is string)){ + field.DrawFields(label); + continue; + } + string value = Convert.ToString(field); + if(value != null){ + value.Draw(label); + } + } + if(!header.IsEmpty()){EditorGUI.indentLevel -= 1;} + } + } + //public static bool DrawFoldout(this string current,bool indention=true){return new UnityLabel(current).DrawFoldout(indention);} + //public static bool DrawFoldout(this GUIContent current,bool indention=true){return new UnityLabel(current).DrawFoldout(indention);} + public static bool DrawFoldout(this UnityLabel current,object key=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.foldout; + string name = key.IsNull() ? current + "Header" : key.GetHashCode().ToString(); + if(key is string){name = (string)key;} + bool previous = EditorPrefs.GetBool(name); + bool state = EditorGUIExtension.Draw(()=>EditorGUILayout.Foldout(previous,current,style),indention); + if(previous != state){EditorPrefs.SetBool(name,state);} + return state; + } + public static bool DrawHeader(this UnityLabel current,object key=null,GUIStyle style=null,bool editable=false,Action callback=null,bool indention=true){ + string stateName = key.IsNull() ? current + "Header" : key.GetHashCode().ToString(); + if(key is string){stateName = (string)key;} + bool state = EditorPrefs.GetBool(stateName); + //current = state ? "▼ " + current : "▶ " + current; + var fallback = editable ? EditorStyles.textField : EditorStyles.label; + var currentStyle = style.IsNull() ? fallback: new GUIStyle(style); + if(state){currentStyle.normal = currentStyle.onNormal;} + if(!editable){ + if(current.DrawButton(currentStyle,indention)){ + state = !state; + EditorPrefs.SetBool(stateName,state); + if(!callback.IsNull()){callback();} + } + } + else{ + current.value.text = current.value.text.Draw(null,currentStyle,indention); + } + return state; + } + //public static void DrawLabel(this string current,GUIStyle style=null,bool indention=true){new UnityLabel(current).DrawLabel(style,indention);} + //public static void DrawLabel(this GUIContent current,GUIStyle style=null,bool indention=true){new UnityLabel(current).DrawLabel(style,indention);} + public static void DrawLabel(this UnityLabel current,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.label; + if(indention){ + var options = new List(); + if(style.fixedWidth != 0){options.Add(GUILayout.Width(style.fixedWidth));} + EditorGUIExtension.Draw(()=>EditorGUILayout.LabelField(current,style,options.ToArray()),indention); + return; + } + EditorGUIExtension.Draw(()=>GUILayout.Label(current,style),indention); + } + public static void DrawHelp(this string current,string textType="Info",bool indention=true){ + MessageType type = MessageType.None; + if(textType.Contains("Info",true)){type = MessageType.Info;} + if(textType.Contains("Error",true)){type = MessageType.Error;} + if(textType.Contains("Warning",true)){type = MessageType.Warning;} + EditorGUIExtension.Draw(()=>EditorGUILayout.HelpBox(current,type),indention); + } + public static string DrawTextArea(this string current,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.textField; + return EditorGUIExtension.Draw(()=>EditorGUILayout.TextField(label,current,style),indention); + } + //public static bool DrawButton(this string current,GUIStyle style=null,bool indention=true){return new UnityLabel(current).DrawButton(style,indention);} + //public static bool DrawButton(this GUIContent current,GUIStyle style=null,bool indention=true){return new UnityLabel(current).DrawButton(style,indention);} + public static bool DrawButton(this UnityLabel current,GUIStyle style=null,bool indention=true){ + style = style ?? GUI.skin.button; + return EditorGUIExtension.Draw(()=>GUILayout.Button(current,style),indention); + } + public static int DrawInt(this int current,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.numberField; + return EditorGUIExtension.Draw(()=>EditorGUILayout.IntField(label,current,style,style.CreateLayout()),indention); + } + public static int DrawSlider(this int current,int min,int max,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUILayout.IntSlider(label,current,min,max),indention); + } + public static Type Draw(this UnityObject current,UnityLabel label=null,bool allowScene=true,bool indention=true) where Type : UnityObject{ + return (Type)EditorGUIExtension.Draw(()=>EditorGUILayout.ObjectField(label,current,typeof(Type),allowScene),indention); + } + public static Enum DrawMask(this Enum current,UnityLabel label=null,GUIStyle style=null,bool indention=true){ + style = style ?? EditorStyles.popup; + return EditorGUIExtension.Draw(()=>EditorGUILayout.EnumMaskField(label,current,style,style.CreateLayout()),indention); + } + public static Vector2 DrawVector2(this Vector2 current,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUILayout.Vector2Field(label,current),indention); + } + public static Vector3 DrawVector3(this Vector3 current,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUILayout.Vector3Field(label,current),indention); + } + public static Vector4 DrawVector4(this Vector4 current,UnityLabel label=null,bool indention=true){ + return EditorGUIExtension.Draw(()=>EditorGUILayout.Vector4Field(label.ToString(),current),indention); + } + } +} +#endif \ No newline at end of file diff --git a/Extensions/@Unity/GUIContent.cs b/Extensions/@Unity/GUIContent.cs new file mode 100644 index 000000000..ca212d900 --- /dev/null +++ b/Extensions/@Unity/GUIContent.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +namespace Zios{ + public static class GUIContentExtension{ + public static string ToString(this GUIContent current){return current.text;} + } +} \ No newline at end of file diff --git a/Extensions/@Unity/GUISkin.cs b/Extensions/@Unity/GUISkin.cs new file mode 100644 index 000000000..0b4cdd51f --- /dev/null +++ b/Extensions/@Unity/GUISkin.cs @@ -0,0 +1,166 @@ +using System.Collections.Generic; +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif +namespace Zios{ + public static class GUISkinExtension{ + public static GUISkin Copy(this GUISkin current){ + var copy = ScriptableObject.CreateInstance(); + copy.font = current.font; + copy.name = current.name; + copy.customStyles = current.customStyles.Copy(); + copy.box = new GUIStyle(current.box); + copy.button = new GUIStyle(current.button); + copy.toggle = new GUIStyle(current.toggle); + copy.label = new GUIStyle(current.label); + copy.textField = new GUIStyle(current.textField); + copy.textArea = new GUIStyle(current.textArea); + copy.window = new GUIStyle(current.window); + copy.horizontalSlider = new GUIStyle(current.horizontalSlider); + copy.horizontalSliderThumb = new GUIStyle(current.horizontalSliderThumb); + copy.verticalSlider = new GUIStyle(current.verticalSlider); + copy.verticalSliderThumb = new GUIStyle(current.verticalScrollbarThumb); + copy.horizontalScrollbar = new GUIStyle(current.horizontalScrollbar); + copy.horizontalScrollbarThumb = new GUIStyle(current.horizontalScrollbarThumb); + copy.horizontalScrollbarLeftButton = new GUIStyle(current.horizontalScrollbarLeftButton); + copy.horizontalScrollbarRightButton = new GUIStyle(current.horizontalScrollbarRightButton); + copy.verticalScrollbar = new GUIStyle(current.verticalScrollbar); + copy.verticalScrollbarThumb = new GUIStyle(current.verticalScrollbarThumb); + copy.verticalScrollbarUpButton = new GUIStyle(current.verticalScrollbarUpButton); + copy.verticalScrollbarDownButton = new GUIStyle(current.verticalScrollbarDownButton); + copy.scrollView = new GUIStyle(current.scrollView); + copy.settings.doubleClickSelectsWord = current.settings.doubleClickSelectsWord; + copy.settings.tripleClickSelectsLine = current.settings.tripleClickSelectsLine; + copy.settings.cursorColor = current.settings.cursorColor; + copy.settings.cursorFlashSpeed = current.settings.cursorFlashSpeed; + copy.settings.selectionColor = current.settings.selectionColor; + return copy; + } + public static GUISkin Use(this GUISkin current,GUISkin other,bool inline=false){ + if(other.IsNull()){return current;} + current.font = other.font; + current.settings.doubleClickSelectsWord = other.settings.doubleClickSelectsWord; + current.settings.tripleClickSelectsLine = other.settings.tripleClickSelectsLine; + current.settings.cursorColor = other.settings.cursorColor; + current.settings.cursorFlashSpeed = other.settings.cursorFlashSpeed; + current.settings.selectionColor = other.settings.selectionColor; + if(inline){ + var currentStyles = current.GetNamedStyles(); + var otherStyles = other.GetNamedStyles(); + foreach(var style in currentStyles){ + if(otherStyles.ContainsKey(style.Key)){ + style.Value.Use(otherStyles[style.Key]); + } + } + } + else{ + current.box = other.box; + current.button = other.button; + current.toggle = other.toggle; + current.label = other.label; + current.textField = other.textField; + current.textArea = other.textArea; + current.window = other.window; + current.horizontalSlider = other.horizontalSlider; + current.horizontalSliderThumb = other.horizontalSliderThumb; + current.verticalSlider = other.verticalSlider; + current.verticalSliderThumb = other.verticalScrollbarThumb; + current.horizontalScrollbar = other.horizontalScrollbar; + current.horizontalScrollbarThumb = other.horizontalScrollbarThumb; + current.horizontalScrollbarLeftButton = other.horizontalScrollbarLeftButton; + current.horizontalScrollbarRightButton = other.horizontalScrollbarRightButton; + current.verticalScrollbar = other.verticalScrollbar; + current.verticalScrollbarThumb = other.verticalScrollbarThumb; + current.verticalScrollbarUpButton = other.verticalScrollbarUpButton; + current.verticalScrollbarDownButton = other.verticalScrollbarDownButton; + current.scrollView = other.scrollView; + current.customStyles = other.customStyles; + } + return current; + } + public static Dictionary GetNamedStyles(this GUISkin current,bool includeStandard=true,bool includeCustom=true,bool trimBaseName=false){ + var data = new Dictionary(); + var styles = current.GetStyles(includeStandard,includeCustom); + for(int index=0;index(); + if(includeStandard){ + styles.Add(current.box); + styles.Add(current.button); + styles.Add(current.toggle); + styles.Add(current.label); + styles.Add(current.textField); + styles.Add(current.textArea); + styles.Add(current.window); + styles.Add(current.horizontalSlider); + styles.Add(current.horizontalSliderThumb); + styles.Add(current.verticalSlider); + styles.Add(current.verticalSliderThumb); + styles.Add(current.horizontalScrollbar); + styles.Add(current.horizontalScrollbarThumb); + styles.Add(current.horizontalScrollbarLeftButton); + styles.Add(current.horizontalScrollbarRightButton); + styles.Add(current.verticalScrollbar); + styles.Add(current.verticalScrollbarThumb); + styles.Add(current.verticalScrollbarUpButton); + styles.Add(current.verticalScrollbarDownButton); + styles.Add(current.scrollView); + } + if(includeCustom){styles.AddRange(current.customStyles);} + return styles.ToArray(); + } + public static void SaveBackgrounds(this GUISkin current,string path,bool includeBuiltin=true){ + foreach(var style in current.GetStyles()){ + foreach(var state in style.GetStates()){ + if(!state.background.IsNull()){ + string assetPath = FileManager.GetPath(state.background); + string savePath = path+"/"+state.background.name+".png"; + if(!includeBuiltin && assetPath.Contains("unity editor resources")){continue;} + if(!FileManager.Exists(savePath)){ + state.background.SaveAs(savePath,true); + } + } + } + } + } + public static GUIStyle AddStyle(this GUISkin current,GUIStyle style){ + if(style.IsNull()){return null;} + return current.AddStyle(style.name,style); + } + public static GUIStyle AddStyle(this GUISkin current,string name,GUIStyle style){ + if(!style.IsNull() && !current.customStyles.Exists(x=>x.name==name)){ + current.customStyles = current.customStyles.Add(style); + } + return style; + } + #if UNITY_EDITOR + public static void SaveFonts(this GUISkin current,string path,bool includeBuiltin=true){ + foreach(var style in current.GetStyles()){ + if(!style.font.IsNull()){ + string assetPath = FileManager.GetPath(style.font); + string savePath = path+"/"+assetPath.GetPathTerm(); + if(!includeBuiltin && assetPath.Contains("unity editor resources")){continue;} + if(!FileManager.Exists(savePath)){ + AssetDatabase.CopyAsset(assetPath,savePath); + } + } + } + } + #endif + } +} \ No newline at end of file diff --git a/Extensions/@Unity/GUIStyle.cs b/Extensions/@Unity/GUIStyle.cs new file mode 100644 index 000000000..cb5b7180b --- /dev/null +++ b/Extensions/@Unity/GUIStyle.cs @@ -0,0 +1,243 @@ +using System.Collections.Generic; +using System.Text; +using System; +using UnityEngine; +namespace Zios{ + public static class GUIStyleExtension{ + public static GUILayoutOption[] CreateLayout(this GUIStyle current){ + var options = new List(); + if(current.fixedWidth != 0){options.Add(GUILayout.Width(current.fixedWidth));} + return options.ToArray(); + } + public static GUIStyle Rotate90(this GUIStyle current){ + float width = current.fixedWidth; + float height = current.fixedHeight; + current.fixedWidth = height; + current.fixedHeight = width; + current.margin = RectOffsetExtension.Rotate90(current.margin); + current.padding = RectOffsetExtension.Rotate90(current.padding); + return current; + } + public static GUIStyle Border(this GUIStyle current,int value,bool asCopy=true){ + return current.Border(value,value,value,value,asCopy); + } + public static GUIStyle Border(this GUIStyle current,int left,int right,int top,int bottom,bool asCopy=true){ + return current.Border(new RectOffset(left,right,top,bottom),asCopy); + } + public static GUIStyle Border(this GUIStyle current,RectOffset offset,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.border = offset; + return current; + } + public static GUIStyle ContentOffset(this GUIStyle current,float x,float y,bool asCopy=true){ + return current.ContentOffset(new Vector2(x,y),asCopy); + } + public static GUIStyle ContentOffset(this GUIStyle current,Vector2 offset,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.contentOffset = offset; + return current; + } + public static GUIStyle Clipping(this GUIStyle current,TextClipping clipping,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.clipping = clipping; + return current; + } + public static GUIStyle Clipping(this GUIStyle current,string value,bool asCopy=true){ + var clipValue = value.ToLower() == "overflow" ? TextClipping.Overflow : TextClipping.Clip; + return current.Clipping(clipValue,asCopy); + } + public static GUIStyle FixedWidth(this GUIStyle current,float value,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.fixedWidth = value; + return current; + } + public static GUIStyle FixedHeight(this GUIStyle current,float value,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.fixedHeight = value; + return current; + } + public static GUIStyle Margin(this GUIStyle current,int value,bool asCopy=true){ + return current.Margin(value,value,value,value,asCopy); + } + public static GUIStyle Margin(this GUIStyle current,int left,int right,int top,int bottom,bool asCopy=true){ + return current.Margin(new RectOffset(left,right,top,bottom),asCopy); + } + public static GUIStyle Margin(this GUIStyle current,RectOffset offset,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.margin = offset; + return current; + } + public static GUIStyle Padding(this GUIStyle current,int value,bool asCopy=true){ + return current.Padding(value,value,value,value,asCopy); + } + public static GUIStyle Padding(this GUIStyle current,int left,int right,int top,int bottom,bool asCopy=true){ + return current.Padding(new RectOffset(left,right,top,bottom),asCopy); + } + public static GUIStyle Padding(this GUIStyle current,RectOffset offset,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.padding = offset; + return current; + } + public static GUIStyle Overflow(this GUIStyle current,int value,bool asCopy=true){ + return current.Overflow(value,value,value,value,asCopy); + } + public static GUIStyle Overflow(this GUIStyle current,int left,int right,int top,int bottom,bool asCopy=true){ + return current.Overflow(new RectOffset(left,right,top,bottom),asCopy); + } + public static GUIStyle Overflow(this GUIStyle current,RectOffset offset,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.overflow = offset; + return current; + } + public static GUIStyle RichText(this GUIStyle current,bool value,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.richText = value; + return current; + } + public static GUIStyle StretchHeight(this GUIStyle current,bool value,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.stretchHeight = value; + return current; + } + public static GUIStyle StretchWidth(this GUIStyle current,bool value,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.stretchWidth = value; + return current; + } + public static GUIStyle Alignment(this GUIStyle current,TextAnchor anchor,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.alignment = anchor; + return current; + } + public static GUIStyle Alignment(this GUIStyle current,string value,bool asCopy=true){ + value = value.ToLower(); + TextAnchor anchor = current.alignment; + if(value == "upperleft"){anchor = TextAnchor.UpperLeft;} + if(value == "uppercenter"){anchor = TextAnchor.UpperCenter;} + if(value == "upperright"){anchor = TextAnchor.UpperRight;} + if(value == "middleleft"){anchor = TextAnchor.MiddleLeft;} + if(value == "middlecenter"){anchor = TextAnchor.MiddleCenter;} + if(value == "middleright"){anchor = TextAnchor.MiddleRight;} + if(value == "lowerleft"){anchor = TextAnchor.LowerLeft;} + if(value == "lowercenter"){anchor = TextAnchor.LowerCenter;} + if(value == "lowerright"){anchor = TextAnchor.LowerRight;} + return current.Alignment(anchor,asCopy); + } + public static GUIStyle Font(this GUIStyle current,Font font,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.font = font; + return current; + } + public static GUIStyle Font(this GUIStyle current,string value,bool asCopy=true){ + Font font = FileManager.GetAsset(value); + if(font != null){return current.Font(font,asCopy);} + return current; + } + public static GUIStyle FontSize(this GUIStyle current,int value,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.fontSize = value; + return current; + } + public static GUIStyle FontStyle(this GUIStyle current,FontStyle fontStyle,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.fontStyle = fontStyle; + return current; + } + public static GUIStyle FontStyle(this GUIStyle current,string value,bool asCopy=true){ + value = value.ToLower(); + var fontStyle = current.fontStyle; + if(value == "normal"){fontStyle = UnityEngine.FontStyle.Normal;} + if(value == "bold"){fontStyle = UnityEngine.FontStyle.Bold;} + if(value == "italic"){fontStyle = UnityEngine.FontStyle.Italic;} + if(value == "boldanditalic"){fontStyle = UnityEngine.FontStyle.BoldAndItalic;} + return current.FontStyle(fontStyle,asCopy); + } + public static GUIStyle ImagePosition(this GUIStyle current,ImagePosition imagePosition,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.imagePosition = imagePosition; + return current; + } + public static GUIStyle Background(this GUIStyle current,Texture2D background,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.normal.background = background; + return current; + } + public static GUIStyle Background(this GUIStyle current,string value,bool asCopy=true){ + if(value.IsEmpty()){return current.Background(default(Texture2D),asCopy);} + Texture2D texture = FileManager.GetAsset(value); + if(texture != null){return current.Background(texture,asCopy);} + return current; + } + public static GUIStyle TextColor(this GUIStyle current,Color textColor,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.normal.textColor = textColor; + return current; + } + public static GUIStyle TextColor(this GUIStyle current,string value,bool asCopy=true){ + return current.TextColor(Colors.FromHex(value),asCopy); + } + public static GUIStyle ImagePosition(this GUIStyle current,string value,bool asCopy=true){ + value = value.ToLower(); + ImagePosition imagePosition = current.imagePosition; + if(value.ContainsAny("imageleft","left")){imagePosition = UnityEngine.ImagePosition.ImageLeft;} + if(value.ContainsAny("imageabove","above")){imagePosition = UnityEngine.ImagePosition.ImageAbove;} + if(value.ContainsAny("imageonly")){imagePosition = UnityEngine.ImagePosition.ImageOnly;} + if(value.ContainsAny("textOnly")){imagePosition = UnityEngine.ImagePosition.TextOnly;} + return current.ImagePosition(imagePosition,asCopy); + } + public static GUIStyle WordWrap(this GUIStyle current,bool value,bool asCopy=true){ + if(asCopy){current = new GUIStyle(current);} + current.wordWrap = value; + return current; + } + public static GUIStyleState[] GetStates(this GUIStyle current,bool offStates=true,bool onStates=true){ + var states = new List(); + if(offStates){ + states.Add(current.normal); + states.Add(current.hover); + states.Add(current.active); + states.Add(current.focused); + } + if(onStates){ + states.Add(current.onNormal); + states.Add(current.onHover); + states.Add(current.onActive); + states.Add(current.onFocused); + } + return states.ToArray(); + } + public static GUIStyle Rename(this GUIStyle current,string name){ + current.name = name; + return current; + } + public static GUIStyle Use(this GUIStyle current,GUIStyle other){ + if(other.IsNull()){return current;} + current.normal = other.normal; + current.hover = other.hover; + current.focused = other.focused; + current.active = other.active; + current.onNormal = other.onNormal; + current.onHover = other.onHover; + current.onFocused = other.onFocused; + current.onActive = other.onActive; + current.border = other.border; + current.margin = other.margin; + current.padding = other.padding; + current.overflow = other.overflow; + current.font = other.font; + current.fontSize = other.fontSize; + current.fontStyle = other.fontStyle; + current.alignment = other.alignment; + current.wordWrap = other.wordWrap; + current.richText = other.richText; + current.clipping = other.clipping; + current.imagePosition = other.imagePosition; + current.contentOffset = other.contentOffset; + current.fixedWidth = other.fixedWidth; + current.fixedHeight = other.fixedHeight; + current.stretchWidth = other.stretchWidth; + current.stretchHeight = other.stretchHeight; + return current; + } + } +} \ No newline at end of file diff --git a/Extensions/@Unity/GameObject.cs b/Extensions/@Unity/GameObject.cs new file mode 100644 index 000000000..bc26b7a78 --- /dev/null +++ b/Extensions/@Unity/GameObject.cs @@ -0,0 +1,268 @@ +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif +using System; +using System.Collections.Generic; +namespace Zios{ + using Events; + public static class GameObjectExtension{ + //==================== + // Retrieval + //==================== + public static GameObject GetPrefabRoot(this GameObject current){ + return Utility.GetPrefabRoot(current); + } + public static int GetSiblingCount(this GameObject current,bool includeInactive=false){ + return Locate.GetSiblings(current,true,includeInactive).Length; + } + public static GameObject GetPreviousSibling(this GameObject current,bool includeInactive=false){ + GameObject[] siblings = Locate.GetSiblings(current,true,includeInactive); + if(siblings.Length == 0){return current;} + int previousIndex = siblings.IndexOf(current) - 1; + if(previousIndex < 0){previousIndex = siblings.Length-1;} + return siblings[previousIndex].gameObject; + } + public static GameObject GetNextSibling(this GameObject current,bool includeInactive=false){ + GameObject[] siblings = Locate.GetSiblings(current,true,includeInactive); + if(siblings.Length == 0){return current;} + int nextIndex = siblings.IndexOf(current) + 1; + if(nextIndex >= siblings.Length){nextIndex = 0;} + return siblings[nextIndex].gameObject; + } + public static Component[] GetComponentsByInterface(this GameObject current){ + List results = new List(); + Component[] items = current.GetComponentsInChildren(true); + foreach(Component item in items){ + if(item.GetType().IsAssignableFrom(typeof(T))){ + results.Add(item); + } + } + return results.ToArray(); + } + public static bool HasComponent(this GameObject current,bool includeInactive=false) where T : Component{ + if(current.IsNull()){return false;} + return !current.GetComponent(includeInactive).IsNull(); + } + public static T GetComponent(this GameObject current,bool includeInactive=false) where T : Component{ + if(current.IsNull()){return null;} + T[] results = current.GetComponentsInChildren(includeInactive); + foreach(T item in results){ + if(item.transform == current.transform){ + return item; + } + } + return null; + } + public static T[] GetComponents(this GameObject current,bool includeInactive=false) where T : Component{ + if(current.IsNull()){return null;} + List results = new List(); + T[] search = current.GetComponentsInChildren(includeInactive); + foreach(T item in search){ + if(item.transform == current.transform){ + results.Add(item); + } + } + return results.ToArray(); + } + public static T GetComponentInParent(this GameObject current,bool includeInactive=false) where T : Component{ + if(current.IsNull()){return null;} + T[] results = current.GetComponentsInParent(includeInactive); + if(results.Length > 0){ + return results[0]; + } + return null; + } + public static T GetComponentInChildren(this GameObject current,bool includeInactive=false) where T : Component{ + T[] results = current.GetComponentsInChildren(includeInactive); + if(results.Length > 0){ + return results[0]; + } + return null; + } + public static GameObject[] GetByName(this GameObject current,string name,bool includeInactive=true){ + if(current.IsNull()){return null;} + Transform[] all = current.GetComponentsInChildren(includeInactive); + List matches = new List(); + foreach(Transform transform in all){ + if(transform.name == name){ + matches.Add(transform.gameObject); + } + } + return matches.ToArray(); + } + //==================== + // Layers / Tags + //==================== + public static void ReplaceLayer(this GameObject current,string search,string replace){ + int layer = LayerMask.NameToLayer(replace); + foreach(GameObject item in current.GetByLayer(search)){ + item.layer = layer; + } + } + public static void ReplaceTag(this GameObject current,string search,string replace){ + foreach(GameObject item in current.GetByTag(search)){ + item.tag = replace; + } + } + public static GameObject[] GetByLayer(this GameObject current,string search){ + int layer = LayerMask.NameToLayer(search); + List results = new List(); + Transform[] children = current.GetComponentsInChildren(true); + foreach(Transform child in children){ + if(child.gameObject.layer == layer){ + results.Add(child.gameObject); + } + } + return results.ToArray(); + } + public static GameObject[] GetByTag(this GameObject current,string search){ + List results = new List(); + Transform[] children = current.GetComponentsInChildren(true); + foreach(Transform child in children){ + if(child.gameObject.tag == search){ + results.Add(child.gameObject); + } + } + return results.ToArray(); + } + public static void SetAllTags(this GameObject current,string name){ + Transform[] children = current.GetComponentsInChildren(true); + foreach(Transform child in children){ + child.gameObject.tag = name; + } + } + public static void SetAllLayers(this GameObject current,string name){ + int layer = LayerMask.NameToLayer(name); + Transform[] children = current.GetComponentsInChildren(true); + foreach(Transform child in children){ + child.gameObject.layer = layer; + } + } + public static void SetLayer(this GameObject current,string name){ + int layer = LayerMask.NameToLayer(name); + current.layer = layer; + } + //==================== + // Collisions + //==================== + public static void ToggleAllCollisions(this GameObject current,bool state){ + current.ToggleComponents(state,true,typeof(Collider)); + } + public static void ToggleAllTriggers(this GameObject current,bool state){ + Collider[] colliders = current.GetComponentsInChildren(); + foreach(Collider collider in colliders){ + collider.isTrigger = state; + } + } + public static void ToggleIgnoreCollisions(this GameObject current,GameObject target,bool state){ + var colliders = current.GetComponentsInChildren(); + var targetColliders = target.GetComponentsInChildren(); + foreach(Collider collider in colliders){ + if(!collider.enabled){continue;} + foreach(Collider targetCollider in targetColliders){ + if(collider == targetCollider){continue;} + if(!targetCollider.enabled){continue;} + Physics.IgnoreCollision(collider,targetCollider,state); + } + } + } + public static void IgnoreAllCollisions(this GameObject current,GameObject target){ + current.ToggleIgnoreCollisions(target,true); + } + public static void UnignoreAllCollisions(this GameObject current,GameObject target){ + current.ToggleIgnoreCollisions(target,false); + } + //==================== + // Components + //==================== + public static void EnableComponents(this GameObject current,params Type[] types){ + current.ToggleComponents(true,false,types); + } + public static void DisableComponents(this GameObject current,params Type[] types){ + current.ToggleComponents(false,false,types); + } + public static void EnableAllComponents(this GameObject current,params Type[] types){ + current.ToggleComponents(true,true,types); + } + public static void DisableAllComponents(this GameObject current,params Type[] types){ + current.ToggleComponents(false,true,types); + } + public static void ToggleComponents(this GameObject current,bool state,bool all=true,params Type[] types){ + Type renderer = typeof(Renderer); + Type collider = typeof(Collider); + Type behaviour = typeof(MonoBehaviour); + Type animation = typeof(Animation); + foreach(Type type in types){ + var components = all ? current.GetComponentsInChildren(type,true) : current.GetComponents(type); + foreach(var item in components){ + Type itemType = item.GetType(); + Func subClass = x => itemType.IsSubclassOf(x); + Func matches = x => itemType.IsAssignableFrom(x); + if(subClass(renderer) || matches(renderer)){((Renderer)item).enabled = state;} + else if(subClass(behaviour) || matches(behaviour)){((MonoBehaviour)item).enabled = state;} + else if(subClass(collider) || matches(collider)){((Collider)item).enabled = state;} + else if(subClass(animation) || matches(animation)){((Animation)item).enabled = state;} + } + } + } + public static void ToggleAllVisible(this GameObject current,bool state){ + current.ToggleComponents(state,true,typeof(Renderer)); + } + //==================== + // Utility + //==================== + public static void PauseValidate(this GameObject current){ + var components = current.GetComponentsInChildren(); + foreach(var component in components){Event.Pause("On Validate",component);} + Event.Pause("On Validate",current); + } + public static void ResumeValidate(this GameObject current){ + var components = current.GetComponentsInChildren(); + foreach(var component in components){Event.Resume("On Validate",component);} + Event.Resume("On Validate",current); + } + public static void MoveTo(this GameObject current,Vector3 location,bool useX=true,bool useY=true,bool useZ=true){ + Vector3 position = current.transform.position; + if(useX){position.x = location.x;} + if(useY){position.y = location.y;} + if(useZ){position.z = location.z;} + current.transform.position = position; + } + public static string GetPath(this GameObject current){ + if(current.IsNull() || current.transform.IsNull()){return "";} + string path = current.transform.name; + #if UNITY_EDITOR + PrefabType type = PrefabUtility.GetPrefabType(current); + if(current.hideFlags == HideFlags.HideInHierarchy || type == PrefabType.Prefab || type == PrefabType.ModelPrefab){ + path = "Prefab/"+path; + } + #endif + Transform parent = current.transform.parent; + while(!parent.IsNull()){ + path = parent.name + "/" + path; + parent = parent.parent; + } + return "/" + path + "/"; + } + public static GameObject GetParent(this GameObject current){ + if(current.transform.parent != null){ + return current.transform.parent.gameObject; + } + return null; + } + public static bool IsPrefab(this GameObject current){ + if(current.IsNull()){return false;} + if(current.hideFlags == HideFlags.NotEditable || current.hideFlags == HideFlags.HideAndDontSave){ + return true; + } + #if UNITY_EDITOR + string assetPath = FileManager.GetPath(current.transform.root.gameObject); + if(!assetPath.IsEmpty()){ + return true; + } + #endif + return false; + } + } +} \ No newline at end of file diff --git a/Extensions/@Unity/Rect.cs b/Extensions/@Unity/Rect.cs new file mode 100644 index 000000000..973498451 --- /dev/null +++ b/Extensions/@Unity/Rect.cs @@ -0,0 +1,122 @@ +using System; +using System.Linq; +using UnityEngine; +using UnityEvent = UnityEngine.Event; +#if UNITY_EDITOR +using UnityEditor; +#endif +namespace Zios{ + public static class RectExtension{ + /*public static bool ContainsPoint(this Rect area,Vector3 position){ + return (position.x > area.xMin) && (position.x < area.xMax) && (position.y > area.yMin) && (position.y < area.yMax); + }*/ + public static Rect OverrideX(this Rect current,float value){ + current.x = value; + return current; + } + public static Rect OverrideY(this Rect current,float value){ + current.y = value; + return current; + } + public static Rect OverrideWidth(this Rect current,float value){ + current.width = value; + return current; + } + public static Rect OverrideHeight(this Rect current,float value){ + current.height = value; + return current; + } + public static Rect Scale(this Rect current,float x,float y){ + return new Rect(current).OverrideWidth(current.width*x).OverrideHeight(current.height*y); + } + public static Rect SetXY(this Rect current,float x,float y){ + return new Rect(current).OverrideX(x).OverrideY(y); + } + public static Rect SetSize(this Rect current,float width,float height){ + return new Rect(current).OverrideWidth(width).OverrideHeight(height); + } + public static Rect SetX(this Rect current,float value){ + return new Rect(current).OverrideX(value); + } + public static Rect SetY(this Rect current,float value){ + return new Rect(current).OverrideY(value); + } + public static Rect SetWidth(this Rect current,float value){ + return new Rect(current).OverrideWidth(value); + } + public static Rect SetHeight(this Rect current,float value){ + return new Rect(current).OverrideHeight(value); + } + public static Rect AddXY(this Rect current,Vector2 value){return current.AddX(value.x).AddY(value.y);} + public static Rect AddXY(this Rect current,float x,float y){return current.AddX(x).AddY(y);} + public static Rect AddX(this Rect current,float value){return current.Add(value);} + public static Rect AddY(this Rect current,float value){return current.Add(0,value);} + public static Rect AddSize(this Rect current,float width,float height){return current.AddWidth(width).AddHeight(height);} + public static Rect AddWidth(this Rect current,float value){return current.Add(0,0,value);} + public static Rect AddHeight(this Rect current,float value){return current.Add(0,0,0,value);} + public static Rect Add(this Rect current,params float[] other){ + return current.Add(other.ToRect()); + } + public static Rect Add(this Rect current,Rect other){ + Rect result = new Rect(current); + result.x += other.x; + result.y += other.y; + result.width += other.width; + result.height += other.height; + return result; + } + public static bool Hovered(this Rect current,string cursor="Link"){ + Vector2 mouse = UnityEvent.current.mousePosition; + bool state = current.Contains(mouse); + if(state && !cursor.IsEmpty()){ + #if UNITY_EDITOR + var pointer = (MouseCursor)Enum.Parse(typeof(MouseCursor),cursor); + EditorGUIUtility.AddCursorRect(current,pointer); + #endif + } + return state; + } + public static bool Clicked(this Rect current,int button=-1){ + bool eventMatch = UnityEvent.current.type == EventType.MouseDown; + bool buttonMatch = button == -1 ? true : UnityEvent.current.button == button; + return current.Hovered("") && eventMatch && buttonMatch; + } + public static bool InFocusedWindow(this Rect current){ + Rect windowRect = new Rect(0,0,Screen.width,Screen.height); + #if UNITY_EDITOR + if(EditorWindow.focusedWindow){ + //EditorWindow.focusedWindow.maxSize + Vector2 scroll = EditorWindow.focusedWindow.GetVariable("m_ScrollPosition"); + windowRect.y = scroll.y; + } + #endif + return current.Overlaps(windowRect); + } + public static bool IsEmpty(this Rect current){ + bool oneSize = current.x == 0 && current.y == 0 && current.width == 1 && current.height == 1; + bool noSize = current.x == 0 && current.y == 0 && current.width == 0 && current.height == 0; + return oneSize || noSize; + } + #if UNITY_EDITOR + public static EditorWindow GetInspectorWindow(this Rect current){ + Type inspectorWindow = Utility.GetUnityType("InspectorWindow"); + return EditorWindow.GetWindowWithRect(inspectorWindow,current); + } + public static Rect GetInspectorArea(this Rect current,EditorWindow window=null){ + Rect windowRect = new Rect(0,0,Screen.width,Screen.height); + //var window = current.GetInspectorWindow(); + //Debug.Log(window.GetVariable("position")); + if(window == null){window = Utility.GetInspectors().First();} + Vector2 scroll = window.GetVariable("m_ScrollPosition"); + windowRect.x = scroll.x; + windowRect.y = scroll.y; + return windowRect; + } + public static bool InInspectorWindow(this Rect current,EditorWindow window=null){ + if(current.IsEmpty()){return false;} + Rect windowRect = current.GetInspectorArea(window); + return current.Overlaps(windowRect); + } + #endif + } +} \ No newline at end of file diff --git a/Extensions/@Unity/RectOffset.cs b/Extensions/@Unity/RectOffset.cs new file mode 100644 index 000000000..295c8f7f1 --- /dev/null +++ b/Extensions/@Unity/RectOffset.cs @@ -0,0 +1,29 @@ +using UnityEngine; +namespace Zios{ + public static class RectOffsetExtension{ + public static RectOffset Rotate90(RectOffset current){ + int left = current.left; + int right = current.right; + int top = current.top; + int bottom = current.bottom; + current.left = top; + current.right = bottom; + current.top = left; + current.bottom = right; + return current; + } + public static bool IsEmpty(this RectOffset current){ + return current.Matches(new RectOffset(0,0,0,0)); + } + public static bool Matches(this RectOffset current,RectOffset other){ + if(current.left != other.left){return false;} + if(current.top != other.top){return false;} + if(current.right != other.right){return false;} + if(current.bottom != other.bottom){return false;} + return true; + } + public static string Serialize(this RectOffset current,string separator="-"){ + return current.top+separator+current.right+separator+current.bottom+separator+current.left; + } + } +} \ No newline at end of file diff --git a/Extensions/@Unity/Texture.cs b/Extensions/@Unity/Texture.cs new file mode 100644 index 000000000..7508ea5ad --- /dev/null +++ b/Extensions/@Unity/Texture.cs @@ -0,0 +1,16 @@ +using UnityEngine; +using System; +namespace Zios{ + public static class TextureExtensions{ + public static void SaveAs(this Texture current,string path,bool useBlit=false){ + var texture = current is Texture2D ? (Texture2D)current : new Texture2D(1,1); + if(useBlit){ + RenderTexture.active = new RenderTexture(current.width,current.height,0); + Graphics.Blit(current,RenderTexture.active); + texture = new Texture2D(current.width,current.height); + texture.ReadPixels(new Rect(0,0,current.width,current.height),0,0); + } + FileManager.WriteFile(path,texture.EncodeToPNG()); + } + } +} \ No newline at end of file diff --git a/Extensions/@Unity/UnityObjectExtensions.cs b/Extensions/@Unity/UnityObjectExtensions.cs new file mode 100644 index 000000000..e0dbfc143 --- /dev/null +++ b/Extensions/@Unity/UnityObjectExtensions.cs @@ -0,0 +1,19 @@ +using System; +using UnityObject = UnityEngine.Object; +namespace Zios{ + public static class UnityObjectExtension{ + #if UNITY_EDITOR + public static UnityObject GetPrefab(this UnityObject current){ + return Utility.GetPrefab(current); + } + public static bool IsExpanded(this UnityObject current){ + Type editorUtility = Utility.GetUnityType("InternalEditorUtility"); + return editorUtility.CallMethod("GetIsInspectorExpanded",current); + } + public static void SetExpanded(this UnityObject current,bool state){ + Type editorUtility = Utility.GetUnityType("InternalEditorUtility"); + editorUtility.CallMethod("SetIsInspectorExpanded",current,state); + } + #endif + } +} \ No newline at end of file diff --git a/Extensions/@Unity/Vector3.cs b/Extensions/@Unity/Vector3.cs new file mode 100644 index 000000000..e9f722018 --- /dev/null +++ b/Extensions/@Unity/Vector3.cs @@ -0,0 +1,98 @@ +using UnityEngine; +using System.Linq; +namespace Zios{ + public static class Vector3Extension{ + //===================== + // Conversion + //===================== + public static string ToString(this Vector3 current){return "("+current.x+","+current.y+","+current.z+")";} + public static byte[] ToBytes(this Vector3 current){return current.ToBytes(false);} + public static byte[] ToBytes(this Vector3 current,bool pack){ + if(pack){return Store.PackFloats(current.x,current.y,current.z).ToBytes();} + return current.x.ToBytes().Append(current.y).Append(current.z); + } + public static Vector3 ToRadian(this Vector3 vector){ + Vector3 copy = vector; + copy.x = vector.x / 360.0f; + copy.y = vector.y / 360.0f; + copy.z = vector.z / 360.0f; + return copy; + } + public static Quaternion ToRotation(this Vector3 current){ + return Quaternion.Euler(current[1],current[0],current[2]); + } + public static float[] ToFloat(this Vector3 current){ + return new float[3]{current.x,current.y,current.z}; + } + public static string Serialize(this Vector3 current){return current.ToString();} + public static Vector3 Deserialize(this Vector3 current,string value){return value.ToVector3();} + //===================== + // General + //===================== + public static Vector3 MoveTowards(this Vector3 current,Vector3 end,Vector3 speed){ + current.x = current.x.MoveTowards(end.x,speed.x); + current.y = current.y.MoveTowards(end.y,speed.y); + current.z = current.z.MoveTowards(end.z,speed.z); + return current; + } + public static Vector3 MoveTowards(this Vector3 current,Vector3 end,float speed){ + return Vector3.MoveTowards(current,end,speed); + } + public static float Distance(this Vector3 current,Vector3 end){ + return Vector3.Distance(current,end); + } + public static Vector3 ScaleBy(this Vector3 current,Vector3 other){ + return Vector3.Scale(current,other); + } + public static Vector3 Sign(this Vector3 vector,bool allowZero=true){ + Vector3 signed = new Vector3(0,0,0); + if(!allowZero || vector.x != 0){ + signed.x = vector.x > 0 ? 1 : -1; + } + if(!allowZero || vector.y != 0){ + signed.y = vector.y > 0 ? 1 : -1; + } + if(!allowZero || vector.z != 0){ + signed.z = vector.z > 0 ? 1 : -1; + } + return signed; + } + public static Vector3 LerpAngle(this Vector3 vector,Vector3 start,Vector3 end,float percent){ + Vector3 copy = vector; + copy.x = Mathf.LerpAngle(start.x,end.x,percent); + copy.y = Mathf.LerpAngle(start.y,end.y,percent); + copy.z = Mathf.LerpAngle(start.z,end.z,percent); + return copy; + } + public static Vector3 Clamp(this Vector3 vector,Vector3 min,Vector3 max){ + Vector3 clamp = vector; + clamp.x = Mathf.Clamp(clamp.x,min.x,max.x); + clamp.y = Mathf.Clamp(clamp.y,min.y,max.y); + clamp.z = Mathf.Clamp(clamp.z,min.z,max.z); + return clamp; + } + public static Vector3 Clamp(this Vector3 vector,float[] min,float[] max){ + Vector3 clamp = vector; + clamp.x = Mathf.Clamp(clamp.x,min[0],max[0]); + clamp.y = Mathf.Clamp(clamp.y,min[1],max[1]); + clamp.z = Mathf.Clamp(clamp.z,min[2],max[2]); + return clamp; + } + public static Vector3 Abs(this Vector3 vector){ + Vector3 copy = vector; + copy.x = Mathf.Abs(copy.x); + copy.y = Mathf.Abs(copy.y); + copy.z = Mathf.Abs(copy.z); + return copy; + } + public static Vector3 RotateAround(this Vector3 current,Vector3 point,Vector3 euler){ + return euler.ToRotation() * (current - point) + point; + } + public static bool Approximately(this Vector3 current,Vector3 value){ + bool x = Mathf.Approximately(current.x,value.x); + bool y = Mathf.Approximately(current.y,value.y); + bool z = Mathf.Approximately(current.z,value.z); + return x && y && z; + } + } +} \ No newline at end of file diff --git a/Extensions/Array.cs b/Extensions/Array.cs new file mode 100644 index 000000000..31e842f1a --- /dev/null +++ b/Extensions/Array.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +namespace Zios{ + public static class ArrayExtension{ + //======================= + // Default + //======================= + public static T[] Convert(this Array current){ + List casted = new List(); + Type type = typeof(T); + foreach(var item in current){ + if(type == typeof(Single)){ + float value = System.Convert.ToSingle(item); + casted.Add((T)System.Convert.ChangeType(value,type)); + } + else{ + casted.Add((T)item); + } + } + return casted.ToArray(); + } + public static List ToList(this T[] current){ + return new List(current); + } + public static int IndexOf(this Array current,T value){ + return Array.IndexOf(current,value); + } + public static int IndexOf(this Array current,Enum enumerable){ + string name = enumerable.ToString(); + return current.IndexOf(name); + } + public static T[] Copy(this T[] current){ + T[] result = new T[current.Length]; + current.CopyTo(result,0); + return result; + } + public static T[] Concat(this T[] current,T[] list){ + T[] result = new T[current.Length + list.Length]; + current.CopyTo(result,0); + list.CopyTo(result,current.Length); + return result; + } + public static bool Exists(this T[] current,Predicate predicate){ + return Array.Exists(current,predicate); + } + public static T Find(this T[] current,Predicate predicate){ + return Array.Find(current,predicate); + } + public static void Clear(this T[] current){ + Array.Clear(current,0,current.Length); + } + public static T[] Add(this T[] current,T element){ + T[] extra = new T[]{element}; + return current.Concat(extra); + } + public static T[] Remove(this T[] current,T value){ + List copy = new List(current); + copy.Remove(value); + return copy.ToArray(); + } + public static T[] RemoveAt(this T[] current,int index){ + List copy = new List(current); + copy.RemoveAt(index); + return copy.ToArray(); + } + public static T[] RemoveAll(this T[] current,T value){ + List copy = new List(current); + copy.RemoveAll(x=>x.Equals(value)); + return copy.ToArray(); + } + public static T[] Resize(this T[] current,int newSize){ + while(current.Length > newSize){ + current = current.RemoveAt(current.Length-1); + } + while(current.Length < newSize){ + current = current.Add(default(T)); + } + return current; + } + public static T[] Order(this T[] current){ + var copy = current.Copy().ToList(); + copy.Sort(); + return copy.ToArray(); + } + public static bool HasAny(this T[] current,params T[] values){return current.ContainsAny(values);} + public static bool HasAll(this T[] current,params T[] values){return current.ContainsAll(values);} + public static bool ContainsAny(this T[] current,params T[] values){ + for(int index=0;index(this T[] current,params T[] values){ + for(int index=0;index(this T[] current,Action method){ + for(int index=0;index= 3){ + float r = current[0]; + float g = current[1]; + float b = current[2]; + if(current.Length > 3){ + return new Color(r,g,b,current[3]); + } + return new Color(r,g,b); + } + return Color.white; + } + public static Vector2 ToVector2(this float[] current){ + if(current.Length >= 2){ + float a = current[0]; + float b = current[1]; + return new Vector2(a,b); + } + return Vector2.zero; + } + public static Vector3 ToVector3(this float[] current){ + if(current.Length >= 3){ + float a = current[0]; + float b = current[1]; + float c = current[2]; + return new Vector3(a,b,c); + } + return Vector3.zero; + } + public static Vector4 ToVector4(this float[] current){ + if(current.Length >= 4){ + float a = current[0]; + float b = current[1]; + float c = current[2]; + float d = current[3]; + return new Vector4(a,b,c,d); + } + return Vector4.zero; + } + public static Rect ToRect(this float[] current){ + Rect result = new Rect(); + for(int index=0;index 3){break;} + if(index == 0){result.x = current[index];} + if(index == 1){result.y = current[index];} + if(index == 2){result.width = current[index];} + if(index == 3){result.height = current[index];} + } + return result; + } + } +} \ No newline at end of file diff --git a/Extensions/BitArray.cs b/Extensions/BitArray.cs new file mode 100644 index 000000000..7c07ba6b7 --- /dev/null +++ b/Extensions/BitArray.cs @@ -0,0 +1,52 @@ +using System.Collections; +using UnityEngine; +namespace Zios{ + public static class BitArrayExtension{ + public static int GetInt(this BitArray bitArray){ + int[] array = new int[1]; + bitArray.CopyTo(array,0); + return array[0]; + } + public static bool Contains(this BitArray bitArray,int value,int start=0){ + for(int index=start;index < bitArray.Length;++index){ + bool active = bitArray[index]; + if(active && ((value | (1< end){speed *= -1;} + current += speed; + current = end < current ? Math.Max(current,end) : Math.Min(current,end); + if((speed > 0 && current > end) || (speed < 0 && current < end)){current = end;} + return current; + } + public static decimal Distance(this decimal current,decimal end){ + return Math.Abs(current-end); + } + public static bool Between(this decimal current,decimal start,decimal end){ + return current >= start && current <= end; + } + public static bool InRange(this decimal current,decimal start,decimal end){ + return current.Between(start,end); + } + public static bool ToBool(this decimal current){ + return current != 0; + } + public static decimal Closest(this decimal current,params decimal[] values){ + decimal match = decimal.MaxValue; + foreach(decimal value in values){ + if(current.Distance(value) < match){ + match = value; + } + } + return match; + } + public static decimal RoundClosestDown(this decimal current,params decimal[] values){ + decimal highest = -1; + foreach(decimal value in values){ + if(current >= value){ + highest = value; + break; + } + } + foreach(decimal value in values){ + if(current >= value && value > highest){ + highest = value; + } + } + return highest; + } + public static decimal RoundClosestUp(this decimal current,params decimal[] values){ + decimal lowest = -1; + foreach(decimal value in values){ + if(current >= value){ + lowest = value; + break; + } + } + foreach(decimal value in values){ + if(current <= value && value < lowest){ + lowest = value; + } + } + return lowest; + } + public static decimal Mean(this IEnumerable current){return (decimal)current.Average();} + public static decimal Median(this IEnumerable current){ + int count = current.Count(); + var sorted = current.OrderBy(n=>n); + decimal midValue = sorted.ElementAt(count/2); + decimal median = midValue; + if(count%2==0){ + median = (midValue + sorted.ElementAt((count/2)-1))/2; + } + return median; + } + public static decimal Mode(this IEnumerable current){ + return current.GroupBy(x=>x).OrderByDescending(x=>x.Count()).Select(x=>x.Key).FirstOrDefault(); + } + public static decimal Min(this decimal current,decimal value){return Math.Min(current,value);} + public static decimal Max(this decimal current,decimal value){return Math.Max(current,value);} + } +} \ No newline at end of file diff --git a/Extensions/Delegate.cs b/Extensions/Delegate.cs new file mode 100644 index 000000000..5fd00c277 --- /dev/null +++ b/Extensions/Delegate.cs @@ -0,0 +1,21 @@ +using System; +namespace Zios{ + public static class DelegateExtension{ + public static bool ContainsMethod(this Delegate current,Delegate value){ + if(current.IsNull()){return false;} + foreach(Delegate item in current.GetInvocationList()){ + if(item == value){return true;} + } + return false; + } + public static bool Contains(this Delegate current,Delegate value){ + return current.ContainsMethod(value); + } + public static Delegate Add(this Delegate current,Delegate value){ + if(!current.ContainsMethod(value)){ + return Delegate.Combine(current,value); + } + return current; + } + } +} \ No newline at end of file diff --git a/Extensions/Dictionary.cs b/Extensions/Dictionary.cs new file mode 100644 index 000000000..d6870cc51 --- /dev/null +++ b/Extensions/Dictionary.cs @@ -0,0 +1,71 @@ +using System; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +namespace Zios{ + public static class DictionaryExtension{ + public static SortedList ToSortedList(this Dictionary current){ + return new SortedList(current); + } + public static Dictionary Copy(this Dictionary current){ + return new Dictionary(current); + } + public static TValue Get(this IDictionary current,TKey key,TValue value=default(TValue)) where TValue : new(){ + if(!current.ContainsKey(key)){ + return value; + } + return current[key]; + } + public static void SetValues(this IDictionary current,IList values) where TValue : new(){ + int index = 0; + foreach(var key in current.Keys.ToList()){ + current[key] = values[index]; + ++index; + } + } + public static TValue AddDefault(this IDictionary current,TKey key){ + if(!current.ContainsKey(key)){ + current[key] = default(TValue); + } + return current[key]; + } + public static TValue AddNew(this IDictionary current,TKey key) where TValue : new(){ + if(!current.ContainsKey(key)){ + current[key] = new TValue(); + } + return current[key]; + } + public static TValue AddNewSequence(this IDictionary,TValue> current,IList key) where TValue : new(){ + if(!current.Keys.ToArray().Exists(x=>x.SequenceEqual(key))){ + current[key] = new TValue(); + } + return current[key]; + } + public static bool ContainsKey(this IDictionary current,string value,bool ignoreCase){ + value = value.ToLower(); + foreach(string key in current.Keys){ + if(key.ToLower() == value){ + return true; + } + } + return false; + } + public static string GetKey(this Dictionary current,string value){ + foreach(var item in current){ + string itemValue = Convert.ToString(item.Value); + if(itemValue.Matches(value,true)){ + return Convert.ToString(item.Key); + } + } + return ""; + } + public static void RemoveValue(this Dictionary current,TValue value){ + foreach(var item in current.Copy()){ + if(item.Value.Equals(value)){ + current.Remove(item.Key); + } + } + } + } +} \ No newline at end of file diff --git a/Extensions/Double.cs b/Extensions/Double.cs new file mode 100644 index 000000000..4ba509e80 --- /dev/null +++ b/Extensions/Double.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +namespace Zios{ + public static class DoubleExtension{ + //===================== + // Conversion + //===================== + public static bool ToBool(this double current){return current != 0;} + public static int ToInt(this double current){return (int)current;} + public static byte ToByte(this double current){return (byte)current;} + public static float ToFloat(this double current) {return (float)current;} + public static byte[] ToBytes(this double current){return BitConverter.GetBytes(current);} + public static string Serialize(this double current){return current.ToString();} + public static double Deserialize(this double current,string value){return value.ToDouble();} + //===================== + // Numeric + //===================== + public static double MoveTowards(this double current,double end,double speed){ + if(current > end){speed *= -1;} + current += speed; + current = end < current ? Math.Max(current,end) : Math.Min(current,end); + if((speed > 0 && current > end) || (speed < 0 && current < end)){current = end;} + return current; + } + public static double Distance(this double current,double end){ + return Math.Abs(current-end); + } + public static bool Between(this double current,double start,double end){ + return current >= start && current <= end; + } + public static bool InRange(this double current,double start,double end){ + return current.Between(start,end); + } + public static double Closest(this double current,params double[] values){ + double match = double.MaxValue; + foreach(double value in values){ + if(current.Distance(value) < match){ + match = value; + } + } + return match; + } + public static double RoundClosestDown(this double current,params double[] values){ + double highest = -1; + foreach(double value in values){ + if(current >= value){ + highest = value; + break; + } + } + foreach(double value in values){ + if(current >= value && value > highest){ + highest = value; + } + } + return highest; + } + public static double RoundClosestUp(this double current,params double[] values){ + double lowest = -1; + foreach(double value in values){ + if(current >= value){ + lowest = value; + break; + } + } + foreach(double value in values){ + if(current <= value && value < lowest){ + lowest = value; + } + } + return lowest; + } + public static double Mean(this IEnumerable current){return (double)current.Average();} + public static double Median(this IEnumerable current){ + int count = current.Count(); + var sorted = current.OrderBy(n=>n); + double midValue = sorted.ElementAt(count/2); + double median = midValue; + if(count%2==0){ + median = (midValue + sorted.ElementAt((count/2)-1))/2; + } + return median; + } + public static double Mode(this IEnumerable current){ + return current.GroupBy(x=>x).OrderByDescending(x=>x.Count()).Select(x=>x.Key).FirstOrDefault(); + } + public static double Min(this double current,double value){return Math.Min(current,value);} + public static double Max(this double current,double value){return Math.Max(current,value);} + public static double Abs(this double current){return Math.Abs(current);} + } +} \ No newline at end of file diff --git a/Extensions/Enum.cs b/Extensions/Enum.cs new file mode 100644 index 000000000..0af25fac9 --- /dev/null +++ b/Extensions/Enum.cs @@ -0,0 +1,98 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Text; +namespace Zios{ + public static class EnumExtension{ + public static Dictionary> nameCache = new Dictionary>(); + public static string ToName(this Enum current){ + Type type = current.GetType(); + var cache = EnumExtension.nameCache; + if(cache.ContainsKey(type) && cache[type].ContainsKey(current)){ + return cache[type][current]; + } + if(current.ToInt() == -1){ + string[] allNames = Enum.GetNames(type); + return string.Join(" ",allNames); + } + string name = Enum.GetName(type,current); + if(name.IsEmpty() || name.IsNull()){ + string[] allNames = Enum.GetNames(type); + StringBuilder names = new StringBuilder(); + for(int index=0;index(this Enum current){ + return (T[])Enum.GetValues(typeof(T)); + } + public static int GetMaskFull(this Enum current){ + return current.GetValues().Cast().Sum(); + } + public static T ParseEnum(this T current,string value){ + return (T)Enum.Parse(current.GetType(),value); + } + public static bool Within(this Enum current,params string[] values){ + for(int index=0;index end){speed *= -1;} + current += speed; + current = end < current ? Math.Max(current,end) : Math.Min(current,end); + if((speed > 0 && current > end) || (speed < 0 && current < end)){current = end;} + return current; + } + public static float Distance(this float current,float end){ + return Math.Abs(current-end); + } + public static bool Between(this float current,float start,float end){ + return current >= start && current <= end; + } + public static bool InRange(this float current,float start,float end){ + return current.Between(start,end); + } + public static float Closest(this float current,params float[] values){ + float match = float.MaxValue; + foreach(float value in values){ + if(current.Distance(value) < match){ + match = value; + } + } + return match; + } + public static float RoundClosestDown(this float current,params float[] values){ + float highest = -1; + foreach(float value in values){ + if(current >= value){ + highest = value; + break; + } + } + foreach(float value in values){ + if(current >= value && value > highest){ + highest = value; + } + } + return highest; + } + public static float RoundClosestUp(this float current,params float[] values){ + float lowest = -1; + foreach(float value in values){ + if(current >= value){ + lowest = value; + break; + } + } + foreach(float value in values){ + if(current <= value && value < lowest){ + lowest = value; + } + } + return lowest; + } + public static float Mean(this IEnumerable current){return (float)current.Average();} + public static float Median(this IEnumerable current){ + int count = current.Count(); + var sorted = current.OrderBy(n=>n); + float midValue = sorted.ElementAt(count/2); + float median = midValue; + if(count%2==0){ + median = (midValue + sorted.ElementAt((count/2)-1))/2; + } + return median; + } + public static float Mode(this IEnumerable current){ + return current.GroupBy(x=>x).OrderByDescending(x=>x.Count()).Select(x=>x.Key).FirstOrDefault(); + } + public static float Saturate(this float current){ + return current.Clamp(0,1); + } + public static float Clamp(this float current,float min,float max){ + if(current < min){return min;} + if(current > max){return max;} + return current; + } + public static float LerpRelative(this float current,float start,float end){ + return ((current-start)/(end-start)).Saturate(); + } + public static float Min(this float current,float value){return Math.Min(current,value);} + public static float Max(this float current,float value){return Math.Max(current,value);} + public static float Abs(this float current){return Math.Abs(current);} + } +} \ No newline at end of file diff --git a/Extensions/IEnumerable.cs b/Extensions/IEnumerable.cs new file mode 100644 index 000000000..909ecbbd2 --- /dev/null +++ b/Extensions/IEnumerable.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System; +using System.Text; +using System.Linq; +namespace Zios{ + public static class IEnumerableExtension{ + //======================= + // General + //======================= + public static Dictionary ToDictionary(this IEnumerable> current){ + return current.ToDictionary(x=>x.Key,x=>x.Value); + } + public static string ToText(this IEnumerable current){ + var value = new StringBuilder(); + foreach(var item in current){ + value.Append(item.ToString()); + value.Append(" | "); + } + return value.ToString().TrimRight(" | "); + } + public static string ToString(this IEnumerable current,string separator=" ",string endTerm="or"){ + string result = ""; + foreach(var item in current){ + bool isLast = current.Last().Equals(item); + if(isLast){result += endTerm;} + result += item.ToString(); + if(!isLast){result += separator;} + } + return result; + } + public static HashSet ToHashSet(this IEnumerable current){ + return new HashSet(current); + } + public static bool ContainsAll(this IEnumerable current,IEnumerable other){ + return !other.Except(current).Any(); + } + public static string Serialize(this IEnumerable current){ + string output = ""; + foreach(var value in current){ + output += value.Serialize()+"-"; + } + return output.TrimRight("-"); + } + public static IEnumerable Deserialize(this IEnumerable current,string value){ + return value.Split("-").Select(x=>x.Deserialize()).ToArray(); + } + //======================= + // LINQ-ish + //======================= + public static List If(this IEnumerable current,Func comparer){ + var results = new List(); + foreach(var item in current){ + if(comparer(item)){ + results.Add(item); + } + } + return results; + } + //======================= + // String + //======================= + public static string Join(this IEnumerable current,string separator=" "){ + return string.Join(separator,current.ToArray()); + } + public static List Filter(this IEnumerable current,string text){ + List newList = new List(); + bool wildcard = text.Contains("*"); + text = text.Replace("*",""); + foreach(string item in current){ + if(wildcard && item.Contains(text)){ + newList.Add(item); + } + else if(item == text){ + newList.Add(item); + } + } + return newList; + } + public static List Replace(this IEnumerable current,string replace,string with,bool ignoreCase=true){ + List results = new List(); + foreach(string item in current){ + results.Add(item.Replace(replace,with)); + } + return results; + } + public static List AddSuffix(this IEnumerable current,string suffix){ + List results = new List(); + foreach(string item in current){ + results.Add(item+suffix); + } + return results; + } + public static string[] Trim(this IEnumerable current,string values){return current.Select(x=>x.Trim(values)).ToArray();} + public static string[] ToTitleCase(this IEnumerable current){return current.Select(x=>x.ToTitleCase()).ToArray();} + public static string[] ToCamelCase(this IEnumerable current){return current.Select(x=>x.ToCamelCase()).ToArray();} + public static string[] ToPascalCase(this IEnumerable current){return current.Select(x=>x.ToPascalCase()).ToArray();} + public static int[] ToInt(this IEnumerable current){return current.Select(x=>x.ToInt()).ToArray();} + public static bool[] ToBool(this IEnumerable current){return current.Select(x=>x.ToBool()).ToArray();} + public static float[] ToFloat(this IEnumerable current){return current.Select(x=>x.ToFloat()).ToArray();} + public static UnityEngine.Color[] ToColor(this IEnumerable current){return current.Select(x=>x.ToColor()).ToArray();} + } +} \ No newline at end of file diff --git a/Extensions/Int.cs b/Extensions/Int.cs new file mode 100644 index 000000000..a20f906eb --- /dev/null +++ b/Extensions/Int.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Linq; +namespace Zios{ + public static class IntExtension{ + //===================== + // General + //===================== + public static int Modulus(this int current,int max){ + return (((current % max) + max) % max); + } + //===================== + // Bitwise + //===================== + public static bool Contains(this int current,Enum mask){ + return (current & mask.ToInt()) != 0; + } + public static bool Contains(this int current,int mask){ + return (current & mask) != 0; + } + //===================== + // Conversion + //===================== + public static string ToHex(this int current){return current.ToString("X6");} + public static Enum ToEnum(this int current,Type enumType){return (Enum)Enum.ToObject(enumType,current);} + public static T ToEnum(this int current){return (T)Enum.ToObject(typeof(T),current);} + public static bool ToBool(this int current){return current != 0;} + public static byte ToByte(this int current){return (byte)current;} + public static short ToShort(this int current){return (short)current;} + public static byte[] ToBytes(this int current){return BitConverter.GetBytes(current);} + public static string Serialize(this int current){return current.ToString();} + public static int Deserialize(this int current,string value){return value.ToInt();} + //===================== + // Numeric + //===================== + public static int MoveTowards(this int current,int end,int speed){ + if(current > end){speed *= -1;} + current += speed; + current = end < current ? Math.Max(current,end) : Math.Min(current,end); + if((speed > 0 && current > end) || (speed < 0 && current < end)){current = end;} + return current; + } + public static int Distance(this int current,int end){ + return Math.Abs(current-end); + } + public static bool Between(this int current,int start,int end){ + return current >= start && current <= end; + } + public static bool InRange(this int current,int start,int end){ + return current.Between(start,end); + } + public static int Closest(this int current,params int[] values){ + int match = int.MaxValue; + foreach(int value in values){ + if(current.Distance(value) < match){ + match = value; + } + } + return match; + } + public static int RoundClosestDown(this int current,params int[] values){ + int highest = -1; + foreach(int value in values){ + if(current >= value){ + highest = value; + break; + } + } + foreach(int value in values){ + if(current >= value && value > highest){ + highest = value; + } + } + return highest; + } + public static int RoundClosestUp(this int current,params int[] values){ + int lowest = -1; + foreach(int value in values){ + if(current >= value){ + lowest = value; + break; + } + } + foreach(int value in values){ + if(current <= value && value < lowest){ + lowest = value; + } + } + return lowest; + } + public static int Mean(this IEnumerable current){return (int)current.Average();} + public static int Median(this IEnumerable current){ + int count = current.Count(); + var sorted = current.OrderBy(n=>n); + int midValue = sorted.ElementAt(count/2); + int median = midValue; + if(count%2==0){ + median = (midValue + sorted.ElementAt((count/2)-1))/2; + } + return median; + } + public static int Mode(this IEnumerable current){ + return current.GroupBy(x=>x).OrderByDescending(x=>x.Count()).Select(x=>x.Key).FirstOrDefault(); + } + public static int Min(this int current,int value){return Math.Min(current,value);} + public static int Max(this int current,int value){return Math.Max(current,value);} + public static int Abs(this int current){return Math.Abs(current);} + } +} \ No newline at end of file diff --git a/Extensions/List.cs b/Extensions/List.cs new file mode 100644 index 000000000..b5f9126c6 --- /dev/null +++ b/Extensions/List.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +namespace Zios{ + public static class ListExtension{ + public static List Copy(this List current){ + return new List(current); + } + public static void Move(this List current,int index,int newIndex) where T : class{ + T item = current[index]; + current.Remove(item); + current.Insert(newIndex,item); + } + public static T Find(this List current,T value){ + return current.Find(x=>x.Equals(value)); + } + public static bool Exists(this List current,T value){ + return current.Exists(x=>x.Equals(value)); + } + public static bool Has(this List current,T value){ + foreach(T item in current){ + if(item.Equals(value)){return true;} + } + return false; + } + public static T AddNew(this List current) where T : new(){ + T item = new T(); + current.Add(item); + return item; + } + public static T AddNew(this List current,T value){ + if(!current.Contains(value)){ + current.Add(value); + } + return value; + } + public static int IndexOf(this List current,T item){ + return current.FindIndex(x=>x.Equals(item)); + } + public static int IndexOf(this List current,Enum enumerable){ + string name = enumerable.ToString(); + return current.ToArray().IndexOf(name); + } + public static List Shuffle(this List current){ + List copy = current.Copy(); + System.Random random = new System.Random(); + int total = copy.Count; + while(total > 1){ + total--; + int index = random.Next(total + 1); + T value = copy[index]; + copy[index] = copy[total]; + copy[total] = value; + } + return copy; + } + public static List ToLower(this List current){ + List newList = new List(); + foreach(string item in current){ + newList.Add(item.ToLower()); + } + return newList; + } + public static List Order(this List current){ + //var copy = current.Copy(); + current.Sort(); + return current; + } + public static List Extend(this List current,List values){ + List copy = new List(current); + copy.AddRange(values); + return copy; + } + } +} \ No newline at end of file diff --git a/Extensions/Object/Object.cs b/Extensions/Object/Object.cs new file mode 100644 index 000000000..581e2bd1c --- /dev/null +++ b/Extensions/Object/Object.cs @@ -0,0 +1,174 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Collections; +using System.Security.Cryptography; +using System.Xml.Serialization; +using UnityEngine; +namespace Zios{ + public static partial class ObjectExtension{ + //============================ + // Checks + //============================ + public static T Real(this T current){ + if(current.IsNull()){return default(T);} + return current; + } + public static T Real(this object current){ + if(current.IsNull()){return default(T);} + return (T)current; + } + public static bool IsDefault(this T current){ + return current == null || current.Equals(default(T)); + } + public static bool IsEmpty(this object current){ + bool isEmptyString = (current is string && ((string)current).IsEmpty()); + return current.IsNull() || isEmptyString; + } + public static bool IsNumber(this object current){ + bool isByte = current is sbyte || current is byte; + bool isInteger = current is short || current is ushort || current is int || current is uint || current is long || current is ulong; + bool isDecimal = current is float || current is double || current is decimal; + return isInteger || isDecimal || isByte; + } + public static bool IsNull(this object current){ + return current == null || current.Equals(null); + } + public static bool IsStatic(this object current){ + Type type = current is Type ? (Type)current : current.GetType(); + return type.IsStatic(); + } + public static bool Is(this object current){ + if(current.IsNull()){return false;} + var type = current.GetType(); + var value = typeof(T); + return (type.Equals(value)) || (type.IsSubclassOf(value)); + } + public static bool Is(this Type current,Type value){ + return (current.Equals(value)) || (current.IsSubclassOf(value)); + } + public static bool Is(this T current,Type value){ + var type = typeof(T); + return (type.Equals(value)) || (type.IsSubclassOf(value)); + } + public static bool Is(this T current,string name){ + var type = typeof(T); + var value = Type.GetType(name); + if(value.IsNull()){ + Debug.Log("[ObjectExtension] Type -- " + name + " not found."); + return false; + } + return (type == value) || (type.IsSubclassOf(value)); + } + public static bool IsNot(this T current,Type value){return !current.Is(value);} + public static bool IsNot(this T current,string name){return !current.Is(name);} + //============================ + // Casts + //============================ + public static object Box(this T current){ + return current.AsBox(); + } + public static object AsBox(this T current){ + return (object)current; + } + public static object[] AsBoxedArray(this T current){ + return new object[]{current}; + } + public static List AsBoxedList(this T current){ + return new List{(object)current}; + } + public static T As(this object current){ + if(current.IsNull()){return default(T);} + return (T)current; + } + public static T[] AsArray(this T current){ + if(current.IsNull()){return new T[0];} + return new T[]{current}; + } + public static T[] AsArray(this T current,int amount){ + return current.AsList().ToArray(); + } + public static List AsList(this T current){ + if(current.IsNull()){return new List();} + return new List{current}; + } + public static List AsList(this T current,int amount){ + if(current.IsNull()){return new List();} + var collection = new List(); + while(amount > 0){ + collection.Add(current); + amount -= 1; + } + return collection; + } + //============================ + // Conversions + //============================ + public static float ToFloat(this object current){return Convert.ChangeType(current,typeof(float)).As();} + public static int ToInt(this object current){return Convert.ChangeType(current,typeof(int)).As();} + public static double ToDouble(this object current){return Convert.ChangeType(current,typeof(double)).As();} + public static string ToString(this object current){return Convert.ChangeType(current,typeof(string)).As();} + public static bool ToBool(this object current){return Convert.ChangeType(current,typeof(bool)).As();} + public static byte[] ToBytes(this object current){ + if(current is Vector3){return current.As().ToBytes();} + else if(current is float){return current.As().ToBytes();} + else if(current is int){return current.As().ToBytes();} + else if(current is bool){return current.As().ToBytes();} + else if(current is string){return current.As().ToBytes();} + else if(current is byte){return current.As().ToBytes();} + else if(current is short){return current.As().ToBytes();} + else if(current is double){return current.As().ToBytes();} + return new byte[0]; + } + public static string Serialize(this object current){ + if(current is Vector3){return current.As().Serialize();} + if(current is Color){return current.As().Serialize();} + else if(current is float){return current.As().Serialize();} + else if(current is int){return current.As().Serialize();} + else if(current is bool){return current.As().Serialize();} + else if(current is string){return current.As().Serialize();} + else if(current is byte){return current.As().Serialize();} + else if(current is short){return current.As().Serialize();} + else if(current is double){return current.As().Serialize();} + else if(current is ICollection){return current.As>().Serialize();} + return current.ToString(); + } + //============================ + // Other + //============================ + public static T Clone(this T target) where T : class{ + if(target.IsNull()){return null;} + MethodInfo method = target.GetType().GetMethod("MemberwiseClone",privateFlags); + if(method != null){ + return (T)method.Invoke(target,null); + } + return null; + } + public static byte[] CreateHash(this T current) where T : class{ + using(MemoryStream stream = new MemoryStream()){ + using(SHA512Managed hash = new SHA512Managed()){ + XmlSerializer serialize = new XmlSerializer(typeof(T)); + serialize.Serialize(stream,current); + return hash.ComputeHash(stream); + } + } + } + public static string GetClassName(this object current){ + string path = current.GetClassPath(); + if(path.Contains(".")){ + return path.Split(".").Last(); + } + return path; + } + public static string GetClassPath(this object current){ + return current.GetType().ToString(); + } + public static string GetAlias(this object current){ + if(current.HasVariable("alias")){return current.GetVariable("alias");} + //if(current.HasVariable("name")){return current.GetVariable("name");} + return current.GetType().Name; + } + } +} \ No newline at end of file diff --git a/Extensions/Object/ObjectReflection.cs b/Extensions/Object/ObjectReflection.cs new file mode 100644 index 000000000..827162eb7 --- /dev/null +++ b/Extensions/Object/ObjectReflection.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; +namespace Zios{ + using Containers; + using Class = ObjectExtension; + public static partial class ObjectExtension{ + [NonSerialized] public static bool debug; + public static List emptyList = new List(); + public static Hierarchy warned = new Hierarchy(); + public static Hierarchy,string,object> variables = new Hierarchy,string,object>(); + public static Hierarchy properties = new Hierarchy(); + public static Hierarchy fields = new Hierarchy(); + public static Hierarchy methods = new Hierarchy(); + public const BindingFlags allFlags = BindingFlags.Static|BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.Public; + public const BindingFlags allFlatFlags = BindingFlags.Static|BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.FlattenHierarchy; + public const BindingFlags staticFlags = BindingFlags.Static|BindingFlags.Public|BindingFlags.NonPublic; + public const BindingFlags instanceFlags = BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.Public; + public const BindingFlags privateFlags = BindingFlags.Instance|BindingFlags.NonPublic; + public const BindingFlags publicFlags = BindingFlags.Instance|BindingFlags.Public; + //========================= + // Methods + //========================= + public static object CallExactMethod(this object current,string name,params object[] parameters){ + return current.CallExactMethod(name,allFlags,parameters); + } + public static V CallExactMethod(this object current,string name,params object[] parameters){ + return current.CallExactMethod(name,allFlags,parameters); + } + public static V CallExactMethod(this object current,string name,BindingFlags flags,params object[] parameters){ + List argumentTypes = new List(); + foreach(var parameter in parameters){ + argumentTypes.Add(parameter.GetType()); + } + var methods = current.GetMethods(argumentTypes,name,flags); + if(methods.Count < 1){ + if(ObjectExtension.debug){Debug.LogWarning("[Object] No method found to call -- " + name);} + return default(V); + } + if(current.IsStatic() || current is Type){ + return (V)methods[0].Invoke(null,parameters); + } + return (V)methods[0].Invoke(current,parameters); + } + public static object CallMethod(this object current,string name,params object[] parameters){ + return current.CallMethod(name,allFlags,parameters); + } + public static V CallMethod(this object current,string name,params object[] parameters){ + return current.CallMethod(name,allFlags,parameters); + } + public static V CallMethod(this object current,string name,BindingFlags flags,params object[] parameters){ + var method = current.GetMethod(name,flags); + if(method == null){ + if(ObjectExtension.debug){Debug.LogWarning("[Object] No method found to call -- " + name);} + return default(V); + } + if(current.IsStatic() || current is Type){ + return (V)method.Invoke(null,parameters); + } + return (V)method.Invoke(current,parameters); + } + public static bool HasMethod(this object current,string name,BindingFlags flags=allFlags){ + Type type = current is Type ? (Type)current : current.GetType(); + return Class.GetMethod(type,name,flags) != null; + } + public static MethodInfo GetMethod(this object current,string name,BindingFlags flags=allFlags){ + Type type = current is Type ? (Type)current : current.GetType(); + return Class.GetMethod(type,name,flags); + } + public static List GetMethods(this object current,IList argumentTypes=null,string name="",BindingFlags flags=allFlags){ + Type type = current is Type ? (Type)current : current.GetType(); + List methods = new List(); + foreach(MethodInfo method in type.GetMethods(flags)){ + if(!name.IsEmpty() && !method.Name.Matches(name,true)){continue;} + if(argumentTypes != null){ + ParameterInfo[] parameters = method.GetParameters(); + bool match = parameters.Length == argumentTypes.Count; + if(match){ + for(int index=0;index ListMethods(this object current,IList argumentTypes=null,BindingFlags flags=allFlags){ + return current.GetMethods(argumentTypes,"",flags).Select(x=>x.Name).ToList(); + } + //========================= + // Attributes + //========================= + public static bool HasAttribute(this object current,string name,Type attribute){ + return current.ListAttributes(name).Exists(x=>x.GetType()==attribute); + } + public static Attribute[] ListAttributes(this object current,string name){ + Type type = current is Type ? (Type)current : current.GetType(); + var property = type.GetProperty(name,allFlags); + var field = type.GetField(name,allFlags); + Attribute[] attributes = new Attribute[0]; + if(field != null){attributes = Attribute.GetCustomAttributes(field);} + if(property != null){attributes = Attribute.GetCustomAttributes(property);} + return attributes; + } + //========================= + // Variables + //========================= + public static void ResetCache(){ + Class.properties.Clear(); + Class.variables.Clear(); + Class.methods.Clear(); + Class.fields.Clear(); + } + public static PropertyInfo GetProperty(Type type,string name,BindingFlags flags=allFlags){ + var target = Class.properties.AddNew(type).AddNew(flags); + if(!target.ContainsKey(name)){target[name] = type.GetProperty(name,flags);} + return target[name]; + } + public static FieldInfo GetField(Type type,string name,BindingFlags flags=allFlags){ + var target = Class.fields.AddNew(type).AddNew(flags); + if(!target.ContainsKey(name)){target[name] = type.GetField(name,flags);} + return target[name]; + } + public static MethodInfo GetMethod(Type type,string name,BindingFlags flags=allFlags){ + var target = Class.methods.AddNew(type).AddNew(flags); + if(!target.ContainsKey(name)){target[name] = type.GetMethod(name,flags);} + return target[name]; + } + public static bool HasVariable(this object current,string name,BindingFlags flags=allFlags){ + Type type = current is Type ? (Type)current : current.GetType(); + bool hasProperty = Class.GetProperty(type,name,flags) != null; + bool hasField = Class.GetField(type,name,flags) != null; + return hasProperty || hasField; + } + public static Type GetVariableType(this object current,string name,int index=-1,BindingFlags flags=allFlags){ + Type type = current is Type ? (Type)current : current.GetType(); + var property = Class.GetProperty(type,name,flags); + var field = Class.GetField(type,name,flags); + if(index != -1){ + if(current is Vector3){return typeof(float);} + IList list = (IList)field.GetValue(current); + return list[index].GetType(); + } + if(property != null){return property.PropertyType;} + if(field != null){return field.FieldType;} + return typeof(Type); + } + public static object GetVariable(this object current,string name,int index=-1,BindingFlags flags=allFlags){ + return current.GetVariable(name,index,flags); + } + public static T GetVariable(this object current,string name,int index=-1,BindingFlags flags=allFlags){ + if(current.IsNull()){return default(T);} + if(name.IsNumber()){ + index = name.ToInt(); + name = ""; + } + var value = default(T); + object instance = current is Type || current.IsStatic() ? null : current; + var type = current is Type ? (Type)current : current.GetType(); + var property = Class.GetProperty(type,name,flags); + var field = Class.GetField(type,name,flags); + if(!name.IsEmpty() && property.IsNull() && field.IsNull()){ + if(ObjectExtension.debug && !Class.warned.AddNew(current).AddNew(name)){ + Debug.LogWarning("[ObjectReflection] Could not find variable to get -- " + type.Name + "." + name); + Class.warned[current][name] = true; + } + return value; + } + if(property != null){value = (T)property.GetValue(instance,null);} + if(field != null){value = (T)field.GetValue(instance);} + if(index != -1){ + if(name.IsEmpty()){ + if(current is IList){return current.As>()[index];} + if(current is Vector3){return current.As()[index].As();} + } + if(value is Vector3){return value.As()[index].As();} + return value.As()[index].As(); + } + return value; + } + public static void SetVariables(this object current,IDictionary values,BindingFlags flags=allFlags){ + foreach(var item in values){ + current.SetVariable(item.Key,item.Value,-1,flags); + } + } + public static void SetVariable(this object current,string name,T value,int index=-1,BindingFlags flags=allFlags){ + if(current.IsNull()){return;} + if(name.IsNumber()){ + index = name.ToInt(); + name = ""; + } + var instance = current is Type || current.IsStatic() ? null : current; + var type = current is Type ? (Type)current : current.GetType(); + var property = Class.GetProperty(type,name,flags); + var field = Class.GetField(type,name,flags); + if(!name.IsNull() && property.IsNull() && field.IsNull() && !Class.warned.AddNew(current).AddNew(name)){ + if(ObjectExtension.debug){Debug.LogWarning("[ObjectReflection] Could not find variable to set -- " + name);} + Class.warned[current][name] = true; + return; + } + if(index != -1){ + if(name.IsEmpty()){ + if(current is IList){current.As>()[index] = value;} + if(current is Vector3){ + var goal = current.As(); + goal[index] = value.ToFloat(); + current.As().Set(goal.x,goal.y,goal.z); + } + return; + } + object existing = field.IsNull() ? property.GetValue(instance,null) : field.GetValue(instance); + if(existing is Vector3){ + var goal = existing.As(); + goal[index] = value.ToFloat(); + existing.As().Set(goal.x,goal.y,goal.z); + return; + } + existing.As().SetValue(value,index); + return; + } + if(property != null && property.CanWrite){ + property.SetValue(instance,value,null); + } + if(field != null && !field.FieldType.IsGenericType){ + field.SetValue(instance,value); + } + } + public static Dictionary GetVariables(this object current,IList withoutAttributes=null,BindingFlags flags=allFlags){ + var allVariables = current.GetVariables(withoutAttributes,flags); + return allVariables.Where(x=>x.Value.Is()).ToDictionary(x=>x.Key,x=>(T)x.Value); + } + public static Dictionary GetVariables(this object current,IList withoutAttributes=null,BindingFlags flags=allFlags){ + if(current.IsNull()){return new Dictionary();} + Type type = current is Type ? (Type)current : current.GetType(); + var target = Class.variables.AddNew(type).AddNew(flags); + IEnumerable,Dictionary>> match = null; + if(withoutAttributes.IsNull()){ + withoutAttributes = Class.emptyList; + if(target.ContainsKey(withoutAttributes)){ + return target[withoutAttributes]; + } + } + else{ + match = target.Where(x=>x.Key.SequenceEqual(withoutAttributes)); + } + if(match.IsNull() || match.Count() < 1){ + object instance = current.IsStatic() || current is Type ? null : current; + Dictionary variables = new Dictionary(); + foreach(FieldInfo field in type.GetFields(flags)){ + if(withoutAttributes.Count > 0){ + var attributes = Attribute.GetCustomAttributes(field); + if(attributes.Any(x=>withoutAttributes.Any(y=>y==x.GetType()))){continue;} + } + variables[field.Name] = field.GetValue(instance); + } + foreach(PropertyInfo property in type.GetProperties(flags).Where(x=>x.CanRead)){ + if(!property.CanWrite){continue;} + if(withoutAttributes.Count > 0){ + var attributes = Attribute.GetCustomAttributes(property); + if(attributes.Any(x=>withoutAttributes.Any(y=>y==x.GetType()))){continue;} + } + try{variables[property.Name] = property.GetValue(instance,null);} + catch{} + } + target[withoutAttributes] = variables; + return variables; + } + return match.ToDictionary().Values.FirstOrDefault(); + } + public static List ListVariables(this object current,IList withoutAttributes=null,BindingFlags flags=allFlags){ + return current.GetVariables(withoutAttributes,flags).Keys.ToList(); + } + public static void UseVariables(this T current,T other,IList withoutAttributes=null,BindingFlags flags = publicFlags) where T : class{ + foreach(var name in current.ListVariables(withoutAttributes,flags)){ + current.SetVariable(name,other.GetVariable(name)); + } + } + public static void ClearVariable(this object current,string name,BindingFlags flags=allFlags){ + Type type = current is Type ? (Type)current : current.GetType(); + current = current.IsStatic() ? null : current; + FieldInfo field = Class.GetField(type,name,flags); + if(!field.IsNull() && !field.FieldType.IsGenericType){ + field.SetValue(current,null); + return; + } + PropertyInfo property = Class.GetProperty(type,name,flags); + if(!property.IsNull() && property.CanWrite){ + property.SetValue(current,null,null); + } + } + //========================= + // Values + //========================= + public static void SetValuesByType(this object current,IList values,IList withoutAttributes=null,BindingFlags flags=allFlags){ + var existing = current.GetVariables(withoutAttributes,flags); + int index = 0; + foreach(var item in existing){ + if(index >= values.Count){break;} + current.SetVariable(item.Key,values[index]); + ++index; + } + } + public static void SetValuesByName(this object current,Dictionary values,IList withoutAttributes=null,BindingFlags flags=allFlags){ + var existing = current.GetVariables(withoutAttributes,flags); + foreach(var item in existing){ + if(!values.ContainsKey(item.Key)){ + if(ObjectExtension.debug){Debug.Log("[ObjectReflection] : No key found when attempting to assign values by name -- " + item.Key);} + continue; + } + current.SetVariable(item.Key,values[item.Key]); + } + } + public static T[] GetValues(this object current,IList withoutAttributes=null,BindingFlags flags=allFlags){ + var allVariables = current.GetVariables(withoutAttributes,flags); + return allVariables.Values.ToArray(); + } + } +} \ No newline at end of file diff --git a/Extensions/Serializable.cs b/Extensions/Serializable.cs new file mode 100644 index 000000000..1530d3ef6 --- /dev/null +++ b/Extensions/Serializable.cs @@ -0,0 +1,14 @@ +namespace Zios{ + using System.IO; + using System.Runtime.Serialization.Formatters.Binary; + public static class SerializableExtension{ + public static T DeepCopy(this T target){ + using(var stream = new MemoryStream()){ + var formatter = new BinaryFormatter(); + formatter.Serialize(stream,target); + stream.Position = 0; + return (T)formatter.Deserialize(stream); + } + } + } +} \ No newline at end of file diff --git a/Extensions/Short.cs b/Extensions/Short.cs new file mode 100644 index 000000000..50f38aa78 --- /dev/null +++ b/Extensions/Short.cs @@ -0,0 +1,14 @@ +using System; +namespace Zios{ + public static class ShortExtension{ + //===================== + // Conversion + //===================== + public static bool ToBool(this short current){return current != 0;} + public static byte ToByte(this short current){return (byte)current;} + public static int ToInt(this short current){return (int)current;} + public static byte[] ToBytes(this short current){return BitConverter.GetBytes(current);} + public static string Serialize(this short current){return current.ToString();} + public static short Deserialize(this short current,string value){return value.ToShort();} + } +} \ No newline at end of file diff --git a/Extensions/String/String.cs b/Extensions/String/String.cs new file mode 100644 index 000000000..3755d084b --- /dev/null +++ b/Extensions/String/String.cs @@ -0,0 +1,402 @@ +using System; +using System.Linq; +using System.Collections; +using System.Globalization; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using UnityEngine; +namespace Zios{ + public static class StringExtension{ + //============================ + // Conversion + //============================ + public static string ToLetterSequence(this string current){ + char lastDigit = current[current.Length-1]; + if(current.Length > 1 && current[current.Length-2] == ' ' && char.IsLetter(lastDigit)){ + char nextLetter = (char)(char.ToUpper(lastDigit)+1); + return current.TrimEnd(lastDigit) + nextLetter; + } + return current + " B"; + } + public static string ToMD5(this string current){ + byte[] bytes = Encoding.UTF8.GetBytes(current); + byte[] hash = MD5.Create().ComputeHash(bytes); + return BitConverter.ToString(hash).Replace("-",""); + } + public static string ToCapitalCase(this string current){ + return current[0].ToString().ToUpper() + current.Substring(1); + } + public static string ToTitleCase(this string current){ + string text = Regex.Replace(current,"(\\B[A-Z])"," $1"); + text = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(text); + text = text.Replace("3 D","3D").Replace("2 D","2D"); + return text; + } + public static string ToPascalCase(this string current){ + return current.ToTitleCase().Remove(" "); + } + public static string ToCamelCase(this string current){ + return current[0].ToString().ToLower() + current.Substring(1).Remove(" "); + } + public static short ToShort(this string current){ + if(current.IsEmpty()){return 0;} + return Convert.ToInt16(current); + } + public static int ToInt(this string current){ + if(current.IsEmpty()){return 0;} + return Convert.ToInt32(current); + } + public static float ToFloat(this string current){ + if(current.IsEmpty()){return 0;} + return Convert.ToSingle(current); + } + public static double ToDouble(this string current){ + if(current.IsEmpty()){return 0;} + return Convert.ToDouble(current); + } + public static bool ToBool(this string current){ + if(current.IsEmpty()){return false;} + string lower = current.ToLower(); + return lower != "false" && lower != "f" && lower != "0"; + } + public static byte ToByte(this string current){return (byte)current[0];} + public static byte[] ToBytes(this string current){return Encoding.ASCII.GetBytes(current);} + public static string Serialize(this string current){return current;} + public static string Deserialize(this string current,string value){return value;} + public static Type Deserialize(this string current){ + if(typeof(Type) == typeof(Vector3)){return (Type)Vector3.zero.Deserialize(current).Box();} + else if(typeof(Type) == typeof(Color)){return (Type) Color.white.Deserialize(current).Box();} + else if(typeof(Type) == typeof(float)){return (Type) new Single().Deserialize(current).Box();} + else if(typeof(Type) == typeof(int)){return (Type) new Int32().Deserialize(current).Box();} + else if(typeof(Type) == typeof(bool)){return (Type) new Boolean().Deserialize(current).Box();} + else if(typeof(Type) == typeof(string)){return (Type) String.Empty.Deserialize(current).Box();} + else if(typeof(Type) == typeof(byte)){return (Type) new Byte().Deserialize(current).Box();} + else if(typeof(Type) == typeof(short)){return (Type) new Int16().Deserialize(current).Box();} + else if(typeof(Type) == typeof(double)){return (Type) new Double().Deserialize(current).Box();} + else if(typeof(Type).IsCollection()){return (Type) new Type[0].Deserialize(current).Box();} + return default(Type); + } + //============================ + // Conversion - Unity + //============================ + public static Rect ToRect(this string current,string separator=","){ + var values = current.Split(separator).Convert(); + return new Rect(values[3],values[1],values[0],values[2]); + } + public static RectOffset ToRectOffset(this string current,string separator=","){ + var values = current.Split(separator).Convert(); + return new RectOffset(values[3],values[1],values[0],values[2]); + } + public static GUIContent ToContent(this string current){return new GUIContent(current);} + public static Color ToColor(this string current,bool flipOrder){return current.ToColor(",",flipOrder);} + public static Color ToColor(this string current,string separator=",",bool flipOrder=false,bool? normalized=null){ + current = current.Remove("#").Remove("0x"); + if(current.Contains(separator)){ + var parts = current.Split(separator).Convert(); + normalized = normalized.IsNull() ? current.Contains(".") : normalized; + if(!normalized.As()){ + parts = parts.Select(x=>x/255.0f).ToArray(); + } + float r = parts[0]; + float g = parts[1]; + float b = parts[2]; + float a = parts.Length > 3 ? parts[3] : 1; + return new Color(r,g,b,a); + } + else if(current.Length == 8 || current.Length == 6 || current.Length == 3){ + if(current.Length == 3){ + current += current; + } + float r = (float)Convert.ToInt32(current.Substring(0,2),16) / 255.0f; + float g = (float)Convert.ToInt32(current.Substring(2,2),16) / 255.0f; + float b = (float)Convert.ToInt32(current.Substring(4,2),16) / 255.0f; + float a = current.Length == 8 ? (float)Convert.ToInt32(current.Substring(6,2),16) / 255.0f : 1; + if(flipOrder){return new Color(b,g,r,a);} + return new Color(r,g,b,a); + } + else{ + Debug.LogError("[StringExtension] Color strings can only be converted from Hexidecimal or comma/space separated Decimal -- " + current); + return new Color(255,0,255); + } + } + public static Vector3 ToVector2(this string current,string separator=","){ + if(!current.Contains(separator)){return Vector2.zero;} + var values = current.Trim("(",")").Split(separator).Convert().ToArray(); + return new Vector3(values[0],values[1]); + } + public static Vector3 ToVector3(this string current,string separator=","){ + if(!current.Contains(separator)){return Vector3.zero;} + var values = current.Trim("(",")").Split(separator).Convert().ToArray(); + return new Vector3(values[0],values[1],values[2]); + } + public static Vector3 ToVector4(this string current,string separator=","){ + if(!current.Contains(separator)){return Vector4.zero;} + var values = current.Trim("(",")").Split(separator).Convert().ToArray(); + return new Vector4(values[0],values[1],values[2],values[3]); + } + //============================ + // Standard + //============================ + public static string Trim(this string current,params string[] values){ + foreach(string value in values){ + current = current.TrimLeft(value); + current = current.TrimRight(value); + } + return current; + } + public static string TrimRight(this string current,params string[] values){ + foreach(string value in values){current = current.TrimRight(value,true);} + return current; + } + public static string TrimLeft(this string current,params string[] values){ + foreach(string value in values){current = current.TrimLeft(value,true);} + return current; + } + public static string TrimRight(this string current,string value,bool ignoreCase){ + if(value.IsEmpty()){return current;} + while(current.EndsWith(value,ignoreCase)){ + current = current.Substring(0,current.Length - value.Length); + } + return current; + } + public static string TrimLeft(this string current,string value,bool ignoreCase){ + if(value.IsEmpty()){return current;} + while(current.StartsWith(value,ignoreCase)){ + current = current.Substring(value.Length); + } + return current; + } + public static bool Matches(this string current,string value,bool ignoreCase=false){ + if(ignoreCase){return current.ToLower() == value.ToLower();} + return current == value; + } + public static bool MatchesAny(this string current,params string[] values){ + foreach(string value in values){ + if(current.Matches(value,true)){return true;} + } + return false; + } + public static string Replace(this string current,string search,string replace,bool ignoreCase){ + if(ignoreCase){ + search = Regex.Escape(search); + replace = Regex.Escape(replace); + string output = Regex.Replace(current,search,replace,RegexOptions.IgnoreCase | RegexOptions.Multiline); + return Regex.Unescape(output); + } + return current.Replace(search,replace); + } + public static string ReplaceFirst(this string current,string search,string replace,bool ignoreCase=false){ + int position = current.IndexOf(search,ignoreCase); + if(position == -1){ + return current; + } + return current.Substring(0,position) + replace + current.Substring(position + search.Length); + } + public static string ReplaceLast(this string current,string search,string replace,bool ignoreCase=false){ + int position = current.LastIndexOf(search,ignoreCase); + if(position == -1){ + return current; + } + return current.Substring(0,position) + replace + current.Substring(position + search.Length); + } + public static int IndexOf(this string current,string value,int start,bool ignoreCase){ + if(ignoreCase){ + return current.IndexOf(value,start,StringComparison.OrdinalIgnoreCase); + } + return current.IndexOf(value,start); + } + public static int IndexOf(this string current,string value,bool ignoreCase){ + return current.IndexOf(value,0,ignoreCase); + } + public static int IndexOf(this string current,string value,int start,int occurrence,bool ignoreCase){ + while(occurrence > 0){ + start = current.IndexOf(value,start+1,ignoreCase)+1; + occurrence -= 1; + } + return Mathf.Max(start-1,-1); + } + public static int LastIndexOf(this string current,string value,int start,bool ignoreCase){ + if(ignoreCase){ + return current.LastIndexOf(value,start,StringComparison.OrdinalIgnoreCase); + } + return current.LastIndexOf(value,start); + } + public static int LastIndexOf(this string current,string value,bool ignoreCase){ + return current.LastIndexOf(value,current.Length-1,ignoreCase); + } + public static int LastIndexOf(this string current,string value,int start,int occurrence,bool ignoreCase){ + while(occurrence > 0){ + start = current.LastIndexOf(value,start+1,ignoreCase)+1; + occurrence -= 1; + } + return Mathf.Max(start-1,-1); + } + public static bool StartsWith(this string current,string value,bool ignoreCase){ + if(ignoreCase){ + return current.StartsWith(value,StringComparison.OrdinalIgnoreCase); + } + return current.StartsWith(value); + } + public static bool EndsWith(this string current,string value,bool ignoreCase){ + if(ignoreCase){ + return current.EndsWith(value,StringComparison.OrdinalIgnoreCase); + } + return current.EndsWith(value); + } + public static bool Has(this string current,string value,bool ignoreCase){return current.Contains(value,ignoreCase);} + public static bool HasAny(this string current,params string[] values){return current.ContainsAny(values);} + public static bool HasAll(this string current,params string[] values){return current.ContainsAll(values);} + public static bool Contains(this string current,string value,bool ignoreCase){ + return current.IndexOf(value,ignoreCase) >= 0; + } + public static bool ContainsAny(this string current,params string[] values){ + foreach(string name in values){ + if(current.Contains(name,true)){return true;} + } + return false; + } + public static bool ContainsAll(this string current,params string[] values){ + foreach(string name in values){ + if(!current.Contains(name,true)){return false;} + } + return true; + } + public static string Remove(this string current,params string[] values){ + string result = current; + foreach(string value in values){ + result = result.Replace(value,"",true); + } + return result; + } + public static string[] Split(this string current,string value){ + if(value.Length == 0 || !current.Contains(value)){return new string[1]{current};} + return current.Split(new string[]{value},StringSplitOptions.None); + } + //============================ + // Checks + //============================ + public static bool IsEmpty(this string text){ + return string.IsNullOrEmpty(text); + } + public static bool IsInt(this string text){ + short number; + return short.TryParse(text,out number); + } + public static bool IsFloat(this string text){ + float number; + return float.TryParse(text,out number); + } + public static bool IsNumber(this string current){ + double result; + return double.TryParse(current,out result); + } + //============================ + // Path + //============================ + public static string GetDirectory(this string current){ + int last = current.LastIndexOf('/'); + if(last < 0){ + if(current.Contains(".")){return "";} + return current; + } + return current.Substring(0,last); + } + public static string GetAssetPath(this string current){ + return "Assets" + current.Split("/Assets")[1]; + } + public static string GetPathTerm(this string current){ + return current.Split("/").Last(); + } + public static string GetFileName(this string current){ + var term = current.Split("/").Last(); + if(term.Contains(".")){return term.Replace("."+current.GetFileExtension(),"");} + return ""; + } + public static string GetFileExtension(this string current){ + var term = current.Split("/").Last(); + if(term.Contains(".")){return term.Split(".").Last();} + return term; + } + //============================ + // Extension + //============================ + public static string AddLine(this string current,string value){ + return current + value + "\r\n"; + } + public static string[] GetLines(this string current){ + return current.Remove("\r").Split("\n"); + } + public static string Implode(this string current,string separator=" "){ + StringBuilder builder = new StringBuilder(current.Length * 2); + foreach(char letter in current){ + builder.Append(letter); + builder.Append(separator); + } + return builder.ToString(); + } + public static string Cut(this string current,int startIndex=0,int endIndex=-1){ + return current.Substring(startIndex,endIndex - startIndex + 1); + } + public static string Cut(this string current,string start="",string end="",int offset=0,bool ignoreCase=true,int endCount=1){ + int startIndex = start == "" ? 0 : current.IndexOf(start,offset,ignoreCase); + if(startIndex != -1){ + if(end == ""){return current.Substring(startIndex);} + int endIndex = current.IndexOf(end,startIndex + 1,ignoreCase); + if(endIndex == -1){return current.Substring(startIndex);} + while(endCount > 1){ + endIndex = current.IndexOf(end,endIndex + 1,ignoreCase); + --endCount; + } + int distance = endIndex - startIndex + end.Length; + return current.Substring(startIndex,distance); + } + return ""; + } + public static string Parse(this string current,string start="",string end="",int offset=0,bool ignoreCase=true,int endCount=1){ + string value = current.Cut(start,end,offset,ignoreCase,endCount); + if(value.IsEmpty()){return "";} + return value.Substring(start.Length).TrimRight(end).Trim(); + } + public static string FindFirst(this string current,params string[] values){ + int index = -1; + string first = ""; + foreach(string value in values){ + int currentIndex = current.IndexOf(value,true); + if(currentIndex != -1 && (index == -1 || currentIndex < index)){ + index = currentIndex; + first = value; + } + } + return first; + } + public static string StripMarkup(this string current){ + return Regex.Replace(current,"<.*?>",string.Empty); + } + public static string Pack(this string current){ + return current.Remove("\r","\n","'","\"","{","}","[","]","(",")","\t"," "); + } + public static string Condense(this string current){ + while(current.ContainsAny("\t\t"," ")){ + current = current.Replace("\t\t","\t").Replace(" "," "); + } + return current; + } + public static string Truncate(this string current,int maxLength){ + return current.Length <= maxLength ? current : current.Substring(0,maxLength); + } + public static string TrySplit(this string current,string value,int index=0){ + return current.TrySplit(value[0],index); + } + public static string TrySplit(this string current,char value,int index=0){ + if(current.Contains(value.ToString())){ + return current.Split(value)[index]; + } + return current; + } + public static string SetDefault(this string current,string value){ + if(current.IsEmpty()){return value;} + return current; + } + } +} \ No newline at end of file diff --git a/Extensions/StringBuilder.cs b/Extensions/StringBuilder.cs new file mode 100644 index 000000000..53537fe77 --- /dev/null +++ b/Extensions/StringBuilder.cs @@ -0,0 +1,9 @@ +using System.Text; +namespace Zios{ + public static class StringBuilderExtension{ + public static void Clear(this StringBuilder current){ + current.Length = 0; + current.Capacity = 0; + } + } +} \ No newline at end of file diff --git a/Extensions/Type.cs b/Extensions/Type.cs new file mode 100644 index 000000000..06ba4f863 --- /dev/null +++ b/Extensions/Type.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections; +namespace Zios{ + public static class TypeExtension{ + public static bool Has(this Type current,object value){return current.Has(value.GetType());} + public static bool Has(this Type current,Type value){ + if(value.IsInterface){return current.GetInterface(value.Name) != null;} + return current.IsSubclassOf(typeof(Type)); + } + public static bool HasEmptyConstructor(this Type current){ + return typeof(Type).GetConstructor(Type.EmptyTypes) != null; + } + public static bool IsCollection(this Type current){ + return current.Has(typeof(ICollection)); + } + public static bool IsStatic(this Type current){ + return current.IsAbstract && current.IsSealed; + } + public static bool IsType(this Type current,Type value){ + return (current == value) || (current.IsSubclassOf(value)); + } + public static bool IsSubclass(this Type current,Type value){ + while(value != null && value != typeof(object)){ + var core = value.IsGenericType ? value.GetGenericTypeDefinition() : value; + if(current == core){return true;} + value = value.BaseType; + } + return false; + } + } +} \ No newline at end of file diff --git a/License.txt b/License.txt new file mode 100644 index 000000000..073e7fb63 --- /dev/null +++ b/License.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Zios (https://github.com/zios) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Supports/Button.cs b/Supports/Button.cs new file mode 100644 index 000000000..1642d61a7 --- /dev/null +++ b/Supports/Button.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using UnityEvent = UnityEngine.Event; +using UnityEngine; +namespace Zios{ + public static class Button{ + public static List keyCodes = new List(Enum.GetNames(typeof(KeyCode))); + public static Dictionary keyNames = new Dictionary(){ + {KeyCode.Keypad0,"0"}, + {KeyCode.Keypad1,"1"}, + {KeyCode.Keypad2,"2"}, + {KeyCode.Keypad3,"3"}, + {KeyCode.Keypad4,"4"}, + {KeyCode.Keypad5,"5"}, + {KeyCode.Keypad6,"6"}, + {KeyCode.Keypad7,"7"}, + {KeyCode.Keypad8,"8"}, + {KeyCode.Keypad9,"9"}, + {KeyCode.KeypadPeriod,"."}, + {KeyCode.KeypadDivide,"/"}, + {KeyCode.KeypadMultiply,"*"}, + {KeyCode.KeypadMinus,"-"}, + {KeyCode.KeypadPlus,"+"}, + {KeyCode.KeypadEquals,"="}, + {KeyCode.Alpha0,"0"}, + {KeyCode.Alpha1,"1"}, + {KeyCode.Alpha2,"2"}, + {KeyCode.Alpha3,"3"}, + {KeyCode.Alpha4,"4"}, + {KeyCode.Alpha5,"5"}, + {KeyCode.Alpha6,"6"}, + {KeyCode.Alpha7,"7"}, + {KeyCode.Alpha8,"8"}, + {KeyCode.Alpha9,"9"}, + {KeyCode.Exclaim,"!"}, + {KeyCode.DoubleQuote,"\""}, + {KeyCode.Hash,"#"}, + {KeyCode.Dollar,"$"}, + {KeyCode.Ampersand,"&"}, + {KeyCode.Quote,"'"}, + {KeyCode.LeftParen,"("}, + {KeyCode.RightParen,")"}, + {KeyCode.Asterisk,"*"}, + {KeyCode.Plus,"+"}, + {KeyCode.Comma,","}, + {KeyCode.Minus,"-"}, + {KeyCode.Period,"."}, + {KeyCode.Slash,"/"}, + {KeyCode.Colon,":"}, + {KeyCode.Semicolon,";"}, + {KeyCode.Less,"<"}, + {KeyCode.Equals,"="}, + {KeyCode.Greater,">"}, + {KeyCode.Question,"?"}, + {KeyCode.At,"@"}, + {KeyCode.LeftBracket,"["}, + {KeyCode.Backslash,"\\"}, + {KeyCode.RightBracket,"]"}, + {KeyCode.Caret,"^"}, + {KeyCode.Underscore,"_"}, + {KeyCode.BackQuote,"`"} + }; + public static string GetName(string name){ + return Button.keyNames.ContainsValue(name) ? Button.keyNames.GetKey(name) : name; + } + public static bool EventKeyDown(string name){ + if(UnityEvent.current.type == EventType.KeyDown){ + KeyCode code = (KeyCode)Enum.Parse(typeof(KeyCode),name); + return UnityEvent.current.keyCode == code; + } + return false; + } + public static bool EventKeyUp(string name){ + if(UnityEvent.current.type == EventType.KeyUp){ + KeyCode code = (KeyCode)Enum.Parse(typeof(KeyCode),name); + return UnityEvent.current.keyCode == code; + } + return false; + } + public static bool EventKeyDown(KeyCode code){ + return UnityEvent.current.type == EventType.KeyDown && UnityEvent.current.keyCode == code; + } + public static bool EventKeyUp(KeyCode code){ + return UnityEvent.current.type == EventType.KeyUp && UnityEvent.current.keyCode == code; + } + } +} \ No newline at end of file diff --git a/Supports/Colors.cs b/Supports/Colors.cs new file mode 100644 index 000000000..8ffac22f6 --- /dev/null +++ b/Supports/Colors.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using UnityEngine; +namespace Zios{ + public static class Colors{ + public static Color[] numbers; + public static Dictionary names; + public static Color Get(int index){return Colors.numbers[index];} + public static Color Get(string name){return Colors.names[name.ToLower()];} + public static Color FromHex(string name){ + name = name.Remove("#"); + int value = int.Parse(name,System.Globalization.NumberStyles.HexNumber); + float red = (value >> 16) & 0xFF; + float green = (value >> 8) & 0xFF; + float blue = value & 0xFF; + return new Color(red/255,green/255,blue/255); + } + static Colors(){ + numbers = new Color[50]; + names = new Dictionary(); + //======================= + // Base + //======================= + names["black"] = numbers[0] = new Color(0.00f,0.00f,0.00f,1.00f); + names["red"] = numbers[1] = new Color(1.00f,0.00f,0.00f,1.00f); + names["green"] = numbers[2] = new Color(0.00f,1.00f,0.00f,1.00f); + names["yellow"] = numbers[3] = new Color(1.00f,1.00f,0.00f,1.00f); + names["blue"] = numbers[4] = new Color(0.00f,0.00f,1.00f,1.00f); + names["cyan"] = numbers[5] = new Color(0.00f,1.00f,1.00f,1.00f); + names["purple"] = numbers[6] = new Color(1.00f,0.00f,1.00f,1.00f); + names["white"] = numbers[7] = new Color(1.00f,1.00f,1.00f,1.00f); + names["orange"] = numbers[8] = new Color(1.00f,0.50f,0.00f,1.00f); + names["gray"] = numbers[9] = new Color(0.50f,0.50f,0.50f,1.00f); + names["silver"] = numbers[10] = new Color(0.80f,0.80f,0.80f,1.00f); + names["darkgray"] = numbers[11] = new Color(0.35f,0.35f,0.35f,1.00f); + names["violet"] = numbers[12] = new Color(0.50f,0.00f,1.00f,1.00f); + //======================= + // Pastel + //======================= + names["pastelred"] = names["chestnut"] = numbers[13] = new Color(0.72f,0.32f,0.32f,1.00f); + names["pastelgreen"] = names["meadow"] = numbers[14] = new Color(0.32f,0.72f,0.42f,1.00f); + names["pastelblue"] = names["steel"] = numbers[15] = new Color(0.32f,0.42f,0.72f,1.00f); + names["pastelyellow"] = names["celery"] = numbers[16] = new Color(0.72f,0.72f,0.32f,1.00f); + names["pastelorange"] = names["twine"] = numbers[17] = new Color(0.72f,0.56f,0.32f,1.00f); + names["pastelpurple"] = names["amethyst"] = numbers[18] = new Color(0.72f,0.32f,0.72f,1.00f); + names["pastelcyan"] = names["fountain"] = numbers[19] = new Color(0.32f,0.72f,0.56f,1.00f); + //======================= + // Dark + //======================= + names["darkred"] = names["maroon"] = numbers[20] = new Color(0.50f,0.00f,0.00f,1.00f); + names["darkgreen"] = names["laurel"] = numbers[21] = new Color(0.00f,0.50f,0.00f,1.00f); + names["darkblue"] = names["navy"] = numbers[22] = new Color(0.00f,0.00f,0.50f,1.00f); + names["darkyellow"] = names["olive"] = numbers[23] = new Color(0.50f,0.50f,0.00f,1.00f); + names["darkorange"] = names["cinnamon"] = numbers[24] = new Color(0.50f,0.25f,0.00f,1.00f); + names["darkpurple"] = names["indigo"] = numbers[25] = new Color(0.25f,0.00f,0.50f,1.00f); + names["darkcyan"] = names["deapsea"] = numbers[26] = new Color(0.00f,0.50f,0.50f,1.00f); + //======================= + // Light + //======================= + names["lightred"] = names["tangerine"] = numbers[27] = new Color(1.00f,0.50f,0.50f,1.00f); + names["lightgreen"] = names["mint"] = numbers[28] = new Color(0.50f,1.00f,0.50f,1.00f); + names["lightblue"] = names["malibu"] = numbers[29] = new Color(0.50f,0.50f,1.00f,1.00f); + names["lightyellow"] = names["dolly"] = numbers[30] = new Color(1.00f,1.00f,0.50f,1.00f); + names["lightorange"] = names["macncheese"] = numbers[31] = new Color(1.00f,0.75f,0.50f,1.00f); + names["lightpurple"] = names["blush"] = numbers[32] = new Color(1.00f,0.50f,1.00f,1.00f); + names["lightcyan"] = names["anakiwa"] = numbers[33] = new Color(0.50f,1.00f,1.00f,1.00f); + names["pink"] = names["lightpurple"]; + //======================= + // Bold + //======================= + names["boldred"] = names["coral"] = numbers[34] = new Color(1.00f,0.25f,0.25f,1.00f); + names["boldgreen"] = names["earthbound"] = numbers[35] = new Color(0.25f,1.00f,0.50f,1.00f); + names["boldblue"] = names["dodger"] = numbers[36] = new Color(0.25f,0.50f,1.00f,1.00f); + names["boldyellow"] = names["golden"] = numbers[37] = new Color(1.00f,1.00f,0.25f,1.00f); + names["boldorange"] = names["crusta"] = numbers[38] = new Color(1.00f,0.50f,0.25f,1.00f); + names["boldpurple"] = names["velectric"] = numbers[39] = new Color(0.50f,0.25f,1.00f,1.00f); + names["boldcyan"] = names["aqua"] = numbers[40] = new Color(0.25f,1.00f,1.00f,1.00f); + names["boldpink"] = names["strawberry"] = numbers[41] = new Color(1.00f,0.25f,0.50f,1.00f); + //======================= + // Zesty + //======================= + names["zestyred"] = names["bittersweet"] = numbers[42] = Colors.FromHex("#ff5b5b"); + names["zestygreen"] = names["screamin"] = numbers[43] = Colors.FromHex("#91ff5b"); + names["zestyblue"] = names["maya"] = numbers[44] = Colors.FromHex("#5bc3ff"); + names["zestyyellow"] = names["lemon"] = numbers[45] = Colors.FromHex("#fffd5b"); + names["zestyorange"] = names["tainoi"] = numbers[46] = Colors.FromHex("#ffbf5b"); + names["zestypurple"] = names["heliotrope"] = numbers[47] = Colors.FromHex("#da5bff"); + names["zestycyan"] = names["aquamarine"] = numbers[48] = Colors.FromHex("#5bffda"); + names["zestypink"] = names["neon"] = numbers[49] = Colors.FromHex("#ff5bc3"); + } + } +} \ No newline at end of file diff --git a/Supports/Hook.cs b/Supports/Hook.cs new file mode 100644 index 000000000..00ced542e --- /dev/null +++ b/Supports/Hook.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using UnityEngine; +namespace Zios{ + using Events; + #if UNITY_EDITOR + using UnityEditor; + using CallbackFunction = UnityEditor.EditorApplication.CallbackFunction; + #endif + public static class Hook{ + public static bool disabled; + public static bool hidden; + static Hook(){ + Hook.hidden = EditorPrefs.GetBool("EditorSettings-HideHooks",false); + } + public static void SetHidden(bool state){ + Locate.SetDirty(); + foreach(var current in Locate.GetSceneObjects()){ + if(current.name != "@Main"){continue;} + current.hideFlags = state ? HideFlags.HideInHierarchy : HideFlags.None; + foreach(var component in Locate.GetObjectComponents(current)){ + component.hideFlags = current.hideFlags; + } + } + Hook.hidden = state; + Utility.Destroy(new GameObject("@*#&")); + } + } + public class Hook where Singleton : Component{ + private bool setup; + public bool disabled; + public CallbackFunction resetMethod; + public CallbackFunction createMethod; + public Hook(CallbackFunction reset=null,CallbackFunction create=null){ + if(this.disabled || Hook.disabled || Application.isPlaying){return;} + this.resetMethod = reset ?? this.Reset; + this.createMethod = create ?? this.Create; + #if UNITY_EDITOR + EditorApplication.delayCall += this.resetMethod; + #endif + } + public void Reset(){ + if(this.disabled || Hook.disabled){return;} + Event.Add("On Level Was Loaded",new Method(this.resetMethod)).SetPermanent(); + Event.Add("On Components Changed",new Method(this.resetMethod)).SetPermanent(); + this.setup = false; + this.createMethod(); + } + public void Create(){ + if(this.disabled || Hook.disabled || this.setup || Application.isPlaying){return;} + this.setup = true; + Func getInstance = ()=>typeof(Singleton).GetVariable("instance"); + Action setInstance = (Singleton value)=>typeof(Singleton).SetVariable("instance",value); + var rootObjects = Locate.GetSceneObjects().Where(x=>!x.IsNull()&&x.transform.parent.IsNull()); + rootObjects.Where(x=>x.name!="@Main"&&x.name.Contains("@Main")).ToList().ForEach(x=>Utility.Destroy(x)); + if(getInstance().IsNull()){ + var path = Locate.GetScenePath("@Main"); + setInstance(path.GetComponent()); + if(getInstance().IsNull() && !Hook.hidden){ + Debug.Log("[Hook] : Auto-creating " + typeof(Singleton).Name + " Singleton."); + setInstance(path.AddComponent()); + } + } + Hook.SetHidden(Hook.hidden); + } + } +} \ No newline at end of file diff --git a/Supports/Locate.cs b/Supports/Locate.cs new file mode 100644 index 000000000..b025f8468 --- /dev/null +++ b/Supports/Locate.cs @@ -0,0 +1,207 @@ +#pragma warning disable 0618 +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityObject = UnityEngine.Object; +namespace Zios{ + using Events; + [InitializeOnLoad] + public static class Locate{ + private static bool setup; + private static bool cleanGameObjects = false; + private static List cleanSceneComponents = new List(); + private static List cleanSiblings = new List(); + private static Dictionary searchCache = new Dictionary(); + private static Dictionary assets = new Dictionary(); + private static Dictionary siblings = new Dictionary(); + private static Dictionary enabledSiblings = new Dictionary(); + private static Dictionary disabledSiblings = new Dictionary(); + private static GameObject[] rootObjects = new GameObject[0]; + private static GameObject[] sceneObjects = new GameObject[0]; + private static GameObject[] enabledObjects = new GameObject[0]; + private static GameObject[] disabledObjects = new GameObject[0]; + private static Component[] allComponents = new Component[0]; + private static Dictionary sceneComponents = new Dictionary(); + private static Dictionary enabledComponents = new Dictionary(); + private static Dictionary disabledComponents = new Dictionary(); + private static Dictionary> objectComponents = new Dictionary>(); + static Locate(){ + if(!Application.isPlaying){ + //Event.Add("On Application Quit",Locate.SetDirty); + Event.Add("On Level Was Loaded",Locate.SetDirty).SetPermanent(); + Event.Add("On Hierarchy Changed",Locate.SetDirty).SetPermanent(); + Event.Add("On Asset Changed",()=>Locate.assets.Clear()).SetPermanent(); + } + Event.Register("On Components Changed"); + if(!Locate.setup){Locate.SetDirty();} + } + public static void CheckChanges(){ + var components = Resources.FindObjectsOfTypeAll(); + if(components.Length != Locate.allComponents.Count() && !Locate.allComponents.SequenceEqual(components)){ + if(Locate.setup){Event.Call("On Components Changed");} + Locate.allComponents = components; + } + } + public static void SetDirty(){ + Locate.CheckChanges(); + Locate.cleanGameObjects = false; + Locate.cleanSceneComponents.Clear(); + Locate.cleanSiblings.Clear(); + Locate.objectComponents.Clear(); + Locate.searchCache.Clear(); + Locate.setup = true; + } + public static void SetComponentsDirty() where Type : Component{Locate.cleanSceneComponents.Remove(typeof(Type));} + public static void SetComponentsDirty(GameObject target) where Type : Component{Locate.objectComponents[target].Remove(typeof(Type));} + public static void Build() where Type : Component{ + List rootObjects = new List(); + List enabled = new List(); + List disabled = new List(); + Type[] all = (Type[])Resources.FindObjectsOfTypeAll(typeof(Type)); + foreach(Type current in all){ + if(current.IsNull()){continue;} + if(current.IsPrefab()){continue;} + if(current.gameObject.IsNull()){continue;} + if(current.gameObject.transform.parent == null){rootObjects.Add(current.gameObject);} + if(current.gameObject.activeInHierarchy){enabled.Add(current);} + else{disabled.Add(current);} + } + Locate.sceneComponents[typeof(Type)] = enabled.Extend(disabled).ToArray(); + Locate.enabledComponents[typeof(Type)] = enabled.ToArray(); + Locate.disabledComponents[typeof(Type)] = disabled.ToArray(); + Locate.cleanSceneComponents.Add(typeof(Type)); + if(typeof(Type) == typeof(Transform)){ + List enabledObjects = enabled.Select(x=>x.gameObject).ToList(); + List disabledObjects = disabled.Select(x=>x.gameObject).ToList(); + Locate.sceneObjects = enabledObjects.Extend(disabledObjects).ToArray(); + Locate.enabledObjects = enabledObjects.ToArray(); + Locate.disabledObjects = disabledObjects.ToArray(); + Locate.rootObjects = rootObjects.ToArray(); + Locate.cleanGameObjects = true; + } + } + //===================== + // Gameobject + //===================== + public static bool HasDuplicate(GameObject target){ + if(Application.isLoadingLevel){return false;} + GameObject[] siblings = target.GetSiblings(true,true,false); + foreach(GameObject current in siblings){ + if(current.IsNull()){continue;} + if(current.name == target.name){return true;} + } + return false; + } + public static GameObject[] GetSiblings(this GameObject current,bool includeEnabled=true,bool includeDisabled=true,bool includeSelf=true){ + if(Application.isLoadingLevel){return new GameObject[0];} + if(!Locate.cleanSiblings.Contains(current)){ + GameObject parent = current.GetParent(); + List siblings; + if(parent.IsNull()){ + Locate.GetSceneObjects(includeEnabled,includeDisabled); + siblings = Locate.rootObjects.Remove(current).ToList(); + } + else{ + siblings = parent.GetComponentsInChildren(true).Select(x=>x.gameObject).ToList(); + siblings.RemoveAll(x=>x.GetParent()!=parent); + } + Locate.siblings[current] = siblings.ToArray(); + Locate.enabledSiblings[current] = Locate.siblings[current].Where(x=>!x.IsNull()&&x.gameObject.activeInHierarchy).Select(x=>x.gameObject).ToArray(); + Locate.disabledSiblings[current] = Locate.siblings[current].Where(x=>!x.IsNull()&&!x.gameObject.activeInHierarchy).Select(x=>x.gameObject).ToArray(); + Locate.cleanSiblings.Add(current); + } + GameObject[] results = Locate.enabledSiblings[current]; + if(includeEnabled && includeDisabled){results = Locate.siblings[current];} + if(!includeEnabled){results = Locate.disabledSiblings[current];} + if(!includeSelf){results = results.Remove(current);} + return results; + } + public static GameObject GetScenePath(string name,bool autocreate=true){ + string[] parts = name.Split('/'); + string path = ""; + GameObject current = null; + Transform parent = null; + foreach(string part in parts){ + path = path + "/" + part; + current = Locate.Find(path); + if(current.IsNull()){ + if(!autocreate){ + return null; + } + current = new GameObject(part); + current.transform.parent = parent; + Locate.SetDirty(); + } + parent = current.transform; + } + return current; + } + public static GameObject[] GetByName(string name){ + if(Application.isLoadingLevel){return new GameObject[0];} + if(!Locate.cleanGameObjects){Locate.Build();} + List matches = new List(); + foreach(GameObject current in Locate.enabledObjects){ + if(current.IsNull()){continue;} + if(current.name == name){ + matches.Add(current); + } + } + return matches.ToArray(); + } + public static GameObject[] GetSceneObjects(bool includeEnabled=true,bool includeDisabled=true){ + if(Application.isLoadingLevel){return new GameObject[0];} + if(!Locate.cleanGameObjects){Locate.Build();} + if(includeEnabled && includeDisabled){return Locate.sceneObjects;} + if(!includeEnabled){return Locate.disabledObjects;} + return Locate.enabledObjects; + } + public static GameObject Find(string name,bool includeHidden=true){ + if(Application.isLoadingLevel){return null;} + if(!Locate.cleanGameObjects){Locate.Build();} + name = name.Trim("/"); + if(Locate.searchCache.ContainsKey(name)){return Locate.searchCache[name];} + GameObject[] all = includeHidden ? Locate.sceneObjects : Locate.enabledObjects; + foreach(GameObject current in all){ + if(current.IsNull()){continue;} + string path = current.GetPath().Trim("/"); + if(path == name){ + Locate.searchCache[name] = current; + return current; + } + } + return null; + } + //===================== + // Components + //===================== + public static Type[] GetSceneComponents(bool includeEnabled=true,bool includeDisabled=true) where Type : Component{ + if(Application.isLoadingLevel){return new Type[0];} + if(!Locate.cleanSceneComponents.Contains(typeof(Type))){Locate.Build();} + if(includeEnabled && includeDisabled){return (Type[])Locate.sceneComponents[typeof(Type)];} + if(!includeEnabled){return (Type[])Locate.disabledComponents[typeof(Type)];} + return (Type[])Locate.enabledComponents[typeof(Type)]; + } + public static Type[] GetObjectComponents(GameObject target) where Type : Component{ + if(Application.isLoadingLevel){return new Type[0];} + if(!Locate.objectComponents.ContainsKey(target) || !Locate.objectComponents[target].ContainsKey(typeof(Type))){ + Locate.objectComponents.AddNew(target); + Locate.objectComponents[target][typeof(Type)] = target.GetComponents(true); + } + return (Type[])Locate.objectComponents[target][typeof(Type)]; + } + //===================== + // Assets + //===================== + public static object[] GetAssets(Type type){ + if(Application.isLoadingLevel){return new Type[0];} + if(!Locate.assets.ContainsKey(type)){Locate.assets[type] = Resources.FindObjectsOfTypeAll(type);} + return Locate.assets[type]; + } + public static Type[] GetAssets() where Type : UnityObject{ + if(Application.isLoadingLevel){return new Type[0];} + if(!Locate.assets.ContainsKey(typeof(Type))){Locate.assets[typeof(Type)] = Resources.FindObjectsOfTypeAll(typeof(Type));} + return (Type[])Locate.assets[typeof(Type)]; + } + } +} \ No newline at end of file diff --git a/Supports/Store.cs b/Supports/Store.cs new file mode 100644 index 000000000..1e2167b83 --- /dev/null +++ b/Supports/Store.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +namespace Zios{ + public static class Store{ + public static int PackBools(params bool[] values){ + int packed = 0; + int slot = 0; + for(int index=values.Length-1;index>=0;--index){ + packed |= (values[slot] ? 1 : 0) << index; + ++slot; + } + return packed; + } + public static bool[] UnpackBools(int amount,int value){ + List unpacked = new List(); + for(int index=amount-1;index>=0;--index){ + int mask = 1<0;--index){ + int shift = bitPrecision * (index - 1); + packed |= ((int)Math.Floor(values[slot] * (float)intPrecision))< unpacked = new List(); + for(int index=amount;index>0;--index){ + float slot = (float)((value / Math.Pow(intPrecision,index - 1)) % floatPrecision) / floatPrecision; + unpacked.Add(slot); + } + return unpacked.ToArray(); + } + public static float PackFloat4(float a,float b,float c,float d){ + int x = ((int)Math.Floor(a * 63))<<18; + int y = ((int)Math.Floor(b * 63))<<12; + int z = ((int)Math.Floor(c * 63))<<6; + int w = ((int)Math.Floor(d * 63)); + return (x | y | z | w) * 0.0000001f; + } + } +} \ No newline at end of file diff --git a/Supports/Utility/Utility.cs b/Supports/Utility/Utility.cs new file mode 100644 index 000000000..5804b9442 --- /dev/null +++ b/Supports/Utility/Utility.cs @@ -0,0 +1,198 @@ +#pragma warning disable 0162 +#pragma warning disable 0618 +using System; +using System.Linq; +using System.Collections.Generic; +using System.Reflection; +using UnityEngine; +using UnityObject = UnityEngine.Object; +namespace Zios{ + using Events; + #if UNITY_EDITOR + using UnityEditor; + using CallbackFunction = UnityEditor.EditorApplication.CallbackFunction; + #else + public delegate void CallbackFunction(); + #endif + [InitializeOnLoad] + public static partial class Utility{ + private static float sceneCheck; + private static Dictionary> delayedMethods = new Dictionary>(); + private static Dictionary internalTypes = new Dictionary(); + static Utility(){Utility.Setup();} + public static void Setup(){ + Event.Add("On Late Update",(Method)Utility.CheckLoaded); + Event.Add("On Late Update",(Method)Utility.CheckDelayed); + #if UNITY_EDITOR + Event.Register("On Global Event"); + Event.Register("On Editor Update"); + Event.Register("On Prefab Changed"); + Event.Register("On Lightmap Baked"); + Event.Register("On Windows Reordered"); + Event.Register("On Hierarchy Changed"); + Event.Register("On Asset Changed"); + Event.Register("On Asset Saving"); + Event.Register("On Asset Creating"); + Event.Register("On Asset Deleting"); + Event.Register("On Asset Moving"); + Event.Register("On Scene Loaded"); + Event.Register("On Editor Scene Loaded"); + Event.Register("On Mode Changed"); + Event.Register("On Enter Play"); + Event.Register("On Exit Play"); + Event.Register("On Undo Flushing"); + Event.Register("On Undo"); + Event.Register("On Redo"); + #if UNITY_5 + Camera.onPostRender += (Camera camera)=>Event.Call("On Camera Post Render",camera); + Camera.onPreRender += (Camera camera)=>Event.Call("On Camera Pre Render",camera); + Camera.onPreCull += (Camera camera)=>Event.Call("On Camera Pre Cull",camera); + Lightmapping.completed += ()=>Event.Call("On Lightmap Baked"); + #endif + Undo.willFlushUndoRecord += ()=>Event.Call("On Undo Flushing"); + Undo.undoRedoPerformed += ()=>Event.Call("On Undo"); + Undo.undoRedoPerformed += ()=>Event.Call("On Redo"); + PrefabUtility.prefabInstanceUpdated += (GameObject target)=>Event.Call("On Prefab Changed",target); + EditorApplication.projectWindowChanged += ()=>Event.Call("On Project Changed"); + EditorApplication.playmodeStateChanged += ()=>Event.Call("On Mode Changed"); + EditorApplication.playmodeStateChanged += ()=>{ + bool changing = EditorApplication.isPlayingOrWillChangePlaymode; + bool playing = Application.isPlaying; + if(changing && !playing){Event.Call("On Enter Play");} + if(!changing && playing){Event.Call("On Exit Play");} + }; + EditorApplication.hierarchyWindowChanged += ()=>Event.DelayCall("On Hierarchy Changed",0.25f); + EditorApplication.update += ()=>Event.Call("On Editor Update"); + EditorApplication.update += ()=>Utility.CheckLoaded(true); + EditorApplication.update += ()=>Utility.CheckDelayed(true); + CallbackFunction windowEvent = ()=>Event.Call("On Window Reordered"); + CallbackFunction globalEvent = ()=>Event.Call("On Global Event"); + var windowsReordered = typeof(EditorApplication).GetVariable("windowsReordered"); + typeof(EditorApplication).SetVariable("windowsReordered",windowsReordered+windowEvent); + var globalEventHandler = typeof(EditorApplication).GetVariable("globalEventHandler"); + typeof(EditorApplication).SetVariable("globalEventHandler",globalEventHandler+globalEvent); + #endif + } + public static void CheckLoaded(){Utility.CheckLoaded(false);} + public static void CheckLoaded(bool editor){ + if(editor && Application.isPlaying){return;} + if(!editor && !Application.isPlaying){return;} + if(Time.realtimeSinceStartup < 0.5 && Utility.sceneCheck == 0){ + var term = editor ? " Editor" : ""; + Event.Call("On" + term + " Scene Loaded"); + Utility.sceneCheck = 1; + } + if(Time.realtimeSinceStartup > Utility.sceneCheck){ + Utility.sceneCheck = 0; + } + } + public static void CheckDelayed(){Utility.CheckDelayed(false);} + public static void CheckDelayed(bool editorCheck){ + if(editorCheck && Application.isPlaying){return;} + if(!editorCheck && !Application.isPlaying){return;} + if(Utility.delayedMethods.Count < 1){return;} + foreach(var item in Utility.delayedMethods.Copy()){ + var method = item.Value.Key; + float callTime = item.Value.Value; + if(Time.realtimeSinceStartup > callTime){ + method(); + Utility.delayedMethods.Remove(item.Key); + } + } + } + //============================ + // General + //============================ + public static void TogglePlayerPref(string name,bool fallback=false){ + bool value = !(PlayerPrefs.GetInt(name) == fallback.ToInt()); + PlayerPrefs.SetInt(name,value.ToInt()); + } + public static void ToggleEditorPref(string name,bool fallback=false){ + #if UNITY_EDITOR + bool value = !EditorPrefs.GetBool(name,fallback); + EditorPrefs.SetBool(name,value); + #endif + } + public static void Destroy(UnityObject target){ + if(target.IsNull()){return;} + if(target is Component){ + var component = target.As(); + if(component.gameObject.IsNull()){return;} + } + if(!Application.isPlaying){UnityObject.DestroyImmediate(target,true);} + else{UnityObject.Destroy(target);} + } + public static Type GetType(string path){ + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach(var assembly in assemblies){ + Type[] types = assembly.GetTypes(); + foreach(Type type in types){ + if(type.FullName == path){ + return type; + } + } + } + return null; + } + public static void ResetTypeCache(){Utility.internalTypes.Clear();} + public static Type GetUnityType(string name){ + if(Utility.internalTypes.ContainsKey(name)){return Utility.internalTypes[name];} + #if UNITY_EDITOR + var fullCheck = name.ContainsAny(".","+"); + var alternative = name.ReplaceLast(".","+"); + var term = alternative.Split("+").Last(); + foreach(var type in typeof(UnityEditor.Editor).Assembly.GetTypes()){ + bool match = fullCheck && (type.FullName.Contains(name) || type.FullName.Contains(alternative)) && term.Matches(type.Name,true); + if(type.Name == name || match){ + Utility.internalTypes[name] = type; + return type; + } + } + foreach(var type in typeof(UnityEngine.Object).Assembly.GetTypes()){ + bool match = fullCheck && (type.FullName.Contains(name) || type.FullName.Contains(alternative)) && term.Matches(type.Name,true); + if(type.Name == name || match){ + Utility.internalTypes[name] = type; + return type; + } + } + #endif + return null; + } + public static void EditorLog(string text){ + if(!Application.isPlaying){ + Debug.Log(text); + } + } + //============================ + // Callbacks + //============================ + public static void EditorCall(CallbackFunction method){ + #if UNITY_EDITOR + if(!Utility.IsPlaying()){ + method(); + } + #endif + } + public static void DelayCall(CallbackFunction method){ + #if UNITY_EDITOR + if(EditorApplication.delayCall != method){ + EditorApplication.delayCall += method; + } + return; + #endif + Utility.DelayCall(method,0); + } + public static void DelayCall(CallbackFunction method,float seconds){ + Utility.DelayCall(method,method,seconds); + } + public static void DelayCall(object key,CallbackFunction method,float seconds){ + if(!key.IsNull() && !method.IsNull()){ + Utility.delayedMethods[key] = new KeyValuePair(method,Time.realtimeSinceStartup + seconds); + } + } + public static void RepeatCall(CallbackFunction method,float seconds){ + CallbackFunction repeat = ()=>Utility.DelayCall(()=>Utility.RepeatCall(method,seconds)); + Utility.DelayCall(method+repeat,seconds); + } + } +} \ No newline at end of file diff --git a/Supports/Utility/UtilityEditor.cs b/Supports/Utility/UtilityEditor.cs new file mode 100644 index 000000000..ef69ca073 --- /dev/null +++ b/Supports/Utility/UtilityEditor.cs @@ -0,0 +1,120 @@ +#pragma warning disable 0162 +#pragma warning disable 0618 +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityObject = UnityEngine.Object; +namespace Zios{ + #if UNITY_EDITOR + using Events; + using UnityEditor; + public class UtilityListener : AssetPostprocessor{ + public static void OnPostprocessAllAssets(string[] imported,string[] deleted,string[] movedTo, string[] movedFrom){ + bool playing = EditorApplication.isPlaying || EditorApplication.isPlayingOrWillChangePlaymode; + if(!playing){Event.Call("On Asset Changed");} + } + } + public class UtilityModificationListener : AssetModificationProcessor{ + public static string[] OnWillSaveAssets(string[] paths){ + foreach(string path in paths){Debug.Log("Saving Changes : " + path);} + if(paths.Exists(x=>x.Contains(".unity"))){Event.Call("On Scene Saving");} + Event.Call("On Asset Saving"); + Event.Call("On Asset Modifying"); + return paths; + } + public static string OnWillCreateAssets(string path){ + Debug.Log("Creating : " + path); + Event.Call("On Asset Creating"); + Event.Call("On Asset Modifying"); + return path; + } + public static string[] OnWillDeleteAssets(string[] paths,RemoveAssetOptions option){ + foreach(string path in paths){Debug.Log("Deleting : " + path);} + Event.Call("On Asset Deleting"); + Event.Call("On Asset Modifying"); + return paths; + } + public static string OnWillMoveAssets(string path,string destination){ + Debug.Log("Moving : " + path + " to " + destination); + Event.Call("On Asset Moving"); + Event.Call("On Asset Modifying"); + return path; + } + } + public static partial class Utility{ + private static EditorWindow[] inspectors; + private static List delayedDirty = new List(); + private static Dictionary serializedObjects = new Dictionary(); + public static SerializedObject GetSerializedObject(UnityObject target){ + if(!Utility.serializedObjects.ContainsKey(target)){ + Utility.serializedObjects[target] = new SerializedObject(target); + } + return Utility.serializedObjects[target]; + } + public static SerializedObject GetSerialized(UnityObject target){ + Type type = typeof(SerializedObject); + return type.CallMethod("LoadFromCache",target.GetInstanceID()); + } + public static void UpdateSerialized(UnityObject target){ + var serialized = Utility.GetSerializedObject(target); + serialized.Update(); + serialized.ApplyModifiedProperties(); + //Utility.UpdatePrefab(target); + } + public static EditorWindow[] GetInspectors(){ + if(Utility.inspectors == null){ + Type inspectorType = Utility.GetUnityType("InspectorWindow"); + Utility.inspectors = inspectorType.CallMethod("GetAllInspectorWindows"); + } + return Utility.inspectors; + } + public static Vector2 GetInspectorScroll(){ + Type inspectorWindow = Utility.GetUnityType("InspectorWindow"); + var window = EditorWindow.GetWindow(inspectorWindow); + return window.GetVariable("m_ScrollPosition"); + } + public static Vector2 GetInspectorScroll(this Rect current){ + Type inspectorWindow = Utility.GetUnityType("InspectorWindow"); + var window = EditorWindow.GetWindowWithRect(inspectorWindow,current); + return window.GetVariable("m_ScrollPosition"); + } + #if !UNITY_THEMES + [MenuItem("Zios/Prefs/Clear Player")] + public static void DeletePlayerPrefs(){ + if(EditorUtility.DisplayDialog("Clear Player Prefs","Delete all the player preferences?","Yes","No")){ + PlayerPrefs.DeleteAll(); + } + } + [MenuItem("Zios/Prefs/Clear Editor")] + public static void DeleteEditorPrefs(){ + if(EditorUtility.DisplayDialog("Clear Editor Prefs","Delete all the editor preferences?","Yes","No")){ + EditorPrefs.DeleteAll(); + } + } + //[MenuItem("Zios/Format Code")] + public static void FormatCode(){ + var output = new StringBuilder(); + var current = ""; + foreach(var file in FileManager.FindAll("*.cs")){ + var contents = file.GetText(); + output.Clear(); + foreach(var line in contents.GetLines()){ + var leading = line.Substring(0,line.TakeWhile(char.IsWhiteSpace).Count()).Replace(" ","\t"); + current = leading+line.Trim().Replace("//","////"); + if(line.Trim().IsEmpty()){continue;} + output.AppendLine(current); + } + file.WriteText(output.ToString().TrimEnd(null)); + } + } + [MenuItem("Zios/Recompile Code")] + public static void RecompileCode(){ + string path = "Assets/@Zios/Codebase/Supports/Utility/UtilityEditor.cs"; + AssetDatabase.ImportAsset(path); + } + #endif + } + #endif +} \ No newline at end of file diff --git a/Supports/Utility/UtilityProxy.cs b/Supports/Utility/UtilityProxy.cs new file mode 100644 index 000000000..a06d1c9a2 --- /dev/null +++ b/Supports/Utility/UtilityProxy.cs @@ -0,0 +1,276 @@ +#pragma warning disable 0162 +#pragma warning disable 0618 +using System; +using System.Collections; +using UnityEngine; +using UnityObject = UnityEngine.Object; +namespace Zios{ + using Events; + #if UNITY_EDITOR + using UnityEditor; + #endif + public static partial class Utility{ + //============================ + // EditorUtility + //============================ + public static bool DisplayCancelableProgressBar(string title,string message,float percent){ + #if UNITY_EDITOR + return EditorUtility.DisplayCancelableProgressBar(title,message,percent); + #endif + return true; + } + public static void ClearProgressBar(){ + #if UNITY_EDITOR + EditorUtility.ClearProgressBar(); + #endif + } + //============================ + // AssetDatabase + //============================ + public static void StartAssetEditing(){ + #if UNITY_EDITOR + AssetDatabase.StartAssetEditing(); + #endif + } + public static void StopAssetEditing(){ + #if UNITY_EDITOR + AssetDatabase.StopAssetEditing(); + #endif + } + public static void RefreshAssets(){ + #if UNITY_EDITOR + AssetDatabase.Refresh(); + #endif + } + public static void SaveAssets(){ + #if UNITY_EDITOR + AssetDatabase.SaveAssets(); + #endif + } + public static void ImportAsset(string path){ + #if UNITY_EDITOR + AssetDatabase.ImportAsset(path); + #endif + } + public static void DeleteAsset(string path){ + #if UNITY_EDITOR + AssetDatabase.DeleteAsset(path); + #endif + } + //============================ + // PrefabUtility + //============================ + public static UnityObject GetPrefab(UnityObject target){ + #if UNITY_EDITOR + return PrefabUtility.GetPrefabObject(target); + #endif + return null; + } + public static GameObject GetPrefabRoot(GameObject target){ + #if UNITY_EDITOR + return PrefabUtility.FindPrefabRoot(target); + #endif + return null; + } + public static void ApplyPrefab(GameObject target){ + #if UNITY_EDITOR + GameObject root = PrefabUtility.FindPrefabRoot(target); + PrefabUtility.ReplacePrefab(root,PrefabUtility.GetPrefabParent(root),ReplacePrefabOptions.ConnectToPrefab); + #endif + } + public static void UpdatePrefab(UnityObject target){ + #if UNITY_EDITOR + PrefabUtility.RecordPrefabInstancePropertyModifications(target); + #endif + } + public static bool ReconnectToLastPrefab(GameObject target){ + #if UNITY_EDITOR + return PrefabUtility.ReconnectToLastPrefab(target); + #endif + return false; + } + public static void DisconnectPrefabInstance(UnityObject target){ + #if UNITY_EDITOR + PrefabUtility.DisconnectPrefabInstance(target); + #endif + } + //============================ + // EditorApplication + //============================ + public static bool IsPaused(){ + #if UNITY_EDITOR + return EditorApplication.isPaused; + #endif + return false; + } + public static bool IsBusy(){ + #if UNITY_EDITOR + return EventDetector.loading || Application.isLoadingLevel || EditorApplication.isPlayingOrWillChangePlaymode; + #endif + return false; + } + public static bool IsPlaying(){ + #if UNITY_EDITOR + return Application.isPlaying || Utility.IsBusy(); + #endif + return Application.isPlaying; + } + //============================ + // Undo + //============================ + public static void RecordObject(UnityObject target,string name){ + #if UNITY_EDITOR + Undo.RecordObject(target,name); + #endif + } + public static void RegisterCompleteObjectUndo(UnityObject target,string name){ + #if UNITY_EDITOR + Undo.RegisterCompleteObjectUndo(target,name); + #endif + } + //============================ + // Prefs + //============================ + public static void SavePlayerPref(string name,object value){ + if(value is Vector3){PlayerPrefs.SetString(name,value.As().ToString());} + else if(value is float){PlayerPrefs.SetFloat(name,value.As());} + else if(value is int){PlayerPrefs.SetInt(name,value.As());} + else if(value is bool){PlayerPrefs.SetInt(name,value.As().ToInt());} + else if(value is string){PlayerPrefs.SetString(name,value.As().ToString());} + else if(value is byte){PlayerPrefs.SetString(name,value.As().ToString());} + else if(value is short){PlayerPrefs.SetInt(name,value.As().ToInt());} + else if(value is double){PlayerPrefs.SetFloat(name,value.As().ToFloat());} + else if(value is ICollection){PlayerPrefs.SetString(name,value.As().Serialize());} + } + public static Type LoadPlayerPref(string name,object fallback=null){ + if(typeof(Type) == typeof(Vector3)){return (Type)PlayerPrefs.GetString(name,fallback.Real().Serialize()).Deserialize().Box();} + else if(typeof(Type) == typeof(float)){return (Type)PlayerPrefs.GetFloat(name,fallback.Real()).Box();} + else if(typeof(Type) == typeof(int)){return (Type)PlayerPrefs.GetInt(name,fallback.Real()).Box();} + else if(typeof(Type) == typeof(bool)){return (Type)PlayerPrefs.GetInt(name,fallback.Real()).Box();} + else if(typeof(Type) == typeof(string)){return (Type)PlayerPrefs.GetString(name,fallback.Real()).Box();} + else if(typeof(Type) == typeof(byte)){return (Type)PlayerPrefs.GetString(name,fallback.Real().Serialize()).Box();} + else if(typeof(Type) == typeof(short)){return (Type)PlayerPrefs.GetInt(name,fallback.Real().ToInt()).Box();} + else if(typeof(Type) == typeof(double)){return (Type)PlayerPrefs.GetFloat(name,fallback.Real().ToFloat()).Box();} + else if(typeof(Type).IsCollection()){return (Type)PlayerPrefs.GetString(name,fallback.Real().Serialize()).Deserialize().Box();} + return default(Type); + } + //============================ + // Other + //============================ + public static void UpdateSelection(){ + #if UNITY_EDITOR + var targets = Selection.objects; + var focus = GUI.GetNameOfFocusedControl(); + if(targets.Length > 0){ + Selection.activeObject = null; + Utility.DelayCall(()=>{ + Selection.objects = targets; + EditorGUI.FocusTextInControl(focus); + GUI.FocusControl(focus); + },0.05f); + } + #endif + } + public static void RebuildInspectors(){ + #if UNITY_EDITOR + Type inspectorType = Utility.GetUnityType("InspectorWindow"); + var windows = inspectorType.CallMethod("GetAllInspectorWindows"); + for(int index=0;index("GetTracker"); + tracker.ForceRebuild(); + } + #endif + } + public static void ShowInspectors(){ + #if UNITY_EDITOR + Type inspectorType = Utility.GetUnityType("InspectorWindow"); + var windows = inspectorType.CallMethod("GetAllInspectorWindows"); + for(int index=0;index("GetTracker"); + for(int editorIndex=0;editorIndexUtility.SetDirty(target),1); + Event.AddLimited("On Enter Play",Utility.ClearDirty,1); + Utility.delayedDirty.AddNew(target); + } + return; + } + EditorUtility.SetDirty(target); + //Utility.UpdatePrefab(target); + #endif + } + public static void SetAssetDirty(UnityObject target){ + #if UNITY_EDITOR + string path = FileManager.GetPath(target); + UnityObject asset = AssetDatabase.LoadMainAssetAtPath(path); + Utility.SetDirty(asset,false,true); + #endif + } + public static bool IsDirty(UnityObject target){ + #if UNITY_EDITOR + return typeof(EditorUtility).CallMethod("IsDirty",target.GetInstanceID()); + #endif + return false; + } + public static int GetLocalID(int instanceID){ + #if UNITY_EDITOR + return Unsupported.GetLocalIdentifierInFile(instanceID); + #endif + return 0; + } + public static bool MoveComponentUp(Component component){ + #if UNITY_EDITOR + return (bool)Utility.GetUnityType("ComponentUtility").CallMethod("MoveComponentUp",component.AsArray()); + #endif + return false; + } + public static bool MoveComponentDown(Component component){ + #if UNITY_EDITOR + return (bool)Utility.GetUnityType("ComponentUtility").CallMethod("MoveComponentDown",component.AsArray()); + #endif + return false; + } + } +} \ No newline at end of file diff --git a/System Attributes/EnumMask.cs b/System Attributes/EnumMask.cs new file mode 100644 index 000000000..46f264048 --- /dev/null +++ b/System Attributes/EnumMask.cs @@ -0,0 +1,4 @@ +using UnityEngine; +namespace Zios{ + public class EnumMaskAttribute : PropertyAttribute{} +} \ No newline at end of file diff --git a/System Attributes/Internal.cs b/System Attributes/Internal.cs new file mode 100644 index 000000000..104ab6ed5 --- /dev/null +++ b/System Attributes/Internal.cs @@ -0,0 +1,4 @@ +using UnityEngine; +namespace Zios{ + public class InternalAttribute : PropertyAttribute{} +} \ No newline at end of file diff --git a/System Attributes/Unity/InitializeOnLoad.cs b/System Attributes/Unity/InitializeOnLoad.cs new file mode 100644 index 000000000..e62692402 --- /dev/null +++ b/System Attributes/Unity/InitializeOnLoad.cs @@ -0,0 +1,9 @@ +using UnityEngine; +namespace Zios{ + #if UNITY_EDITOR + using UnityEditor; + public class InitializeOnLoadAttribute : UnityEditor.InitializeOnLoadAttribute{} + #else + public class InitializeOnLoadAttribute : System.Attribute{} + #endif +} \ No newline at end of file diff --git a/Systems/Event/Event.cs b/Systems/Event/Event.cs new file mode 100644 index 000000000..d641a962e --- /dev/null +++ b/Systems/Event/Event.cs @@ -0,0 +1,551 @@ +#pragma warning disable 0618 +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityObject = UnityEngine.Object; +namespace Zios.Events{ + using Containers; + [InitializeOnLoad] + public static class EventsHook{ + public static Hook hook; + static EventsHook(){ + if(Application.isPlaying){return;} + #if !UNITY_THEMES + EventsHook.hook = new Hook(EventsHook.Reset,EventsHook.Create); + #endif + } + public static void Reset(){ + EventsHook.hook.Reset(); + if(Event.instance){ + Event.Add("On Level Was Loaded",Event.instance.Awake); + } + } + public static void Create(){ + EventsHook.hook.Create(); + if(Event.instance.IsNull()){ + Event.Cleanup(); + } + } + } + //======================= + // Enumerations + //======================= + [Flags] + public enum EventDisabled : int{ + Add = 0x001, + Call = 0x002, + } + [Flags] + public enum EventDebug : int{ + Add = 0x001, + Remove = 0x002, + Call = 0x004, + CallEmpty = 0x008, + CallDeep = 0x010, + CallTimer = 0x020, + CallTimerZero = 0x040, + CallUpdate = 0x080, + Pause = 0x100, + History = 0x200, + } + public enum EventDebugScope : int{ + Global = 0x001, + Scoped = 0x002, + } + //======================= + // Delegates + //======================= + public delegate void Method(); + public delegate void MethodObject(object value); + public delegate void MethodInt(int value); + public delegate void MethodFloat(float value); + public delegate void MethodString(string value); + public delegate void MethodBool(bool value); + public delegate void MethodVector2(Vector2 value); + public delegate void MethodVector3(Vector3 value); + public delegate void MethodFull(object[] values); + public delegate void MethodStep(object collection,int value); + public delegate object MethodReturn(); + public delegate object MethodObjectReturn(object value); + public delegate object MethodIntReturn(int value); + public delegate object MethodFloatReturn(float value); + public delegate object MethodStringReturn(string value); + public delegate object MethodBoolReturn(bool value); + public delegate object MethodVector2Return(Vector2 value); + public delegate object MethodVector3Return(Vector3 value); + //======================= + // Main + //======================= + [AddComponentMenu("")] + public class Event : EventDetector{ + [EnumMask] public static EventDisabled disabled; + [EnumMask] public static EventDebugScope debugScope; + [EnumMask] public static EventDebug debug; + public static Event instance; + public static object all = "All"; + public static object global = "Global"; + public static EventListener emptyListener = new EventListener(); + public static Dictionary> unique = new Dictionary>(); + public static Dictionary>> cache = new Dictionary>>(); + public static List listeners = new List(); + public static Dictionary> callers = new Dictionary>(); + public static Dictionary> active = new Dictionary>(); + [NonSerialized] public static string lastCalled; + public static FixedList eventHistory = new FixedList(15); + public static List stack = new List(); + private bool setup; + public static bool IsSetup(){return !Event.instance.IsNull() && Event.instance.setup;} + public override void Awake(){ + this.setup = true; + Event.instance = this; + Event.callers.Clear(); + Event.cache.Clear(); + Event.listeners.RemoveAll(x=>x.name!="On Events Reset"&&(!x.permanent||x.occurrences==0)); + foreach(var listener in Event.listeners){ + var scope = Event.cache.AddNew(listener.target).AddNew(listener.name); + scope[listener.method] = listener; + } + Event.Call("On Events Reset"); + base.Awake(); + } + [ContextMenu("Reset All")] + public void ResetAll(){ + Event.listeners.Clear(); + Event.cache.Clear(); + Event.callers.Clear(); + Event.unique.Clear(); + EventStepper.instances.Clear(); + } + public static void Cleanup(){ + if(Application.isPlaying){return;} + foreach(var cached in Event.cache.Copy()){ + foreach(var set in cached.Value.Copy()){ + foreach(var eventPair in set.Value.Copy()){ + var listener = eventPair.Value; + Delegate method = (Delegate)listener.method; + bool targetMissing = !listener.isStatic && listener.target.IsNull(); + bool methodMissing = !listener.isStatic && method.Target.IsNull(); + if(targetMissing || methodMissing || eventPair.Key.IsNull()){ + listener.Remove(); + } + } + if(set.Key.IsNull() || set.Value.Count < 1){ + Event.cache[cached.Key].Remove(set.Key); + } + } + if(cached.Key.IsNull() || cached.Value.Count < 1){ + Event.cache.Remove(cached.Key); + } + } + foreach(var listener in Event.listeners.Copy()){ + Delegate method = (Delegate)listener.method; + bool targetMissing = !listener.isStatic && listener.target.IsNull(); + bool methodMissing = !listener.isStatic && method.Target.IsNull(); + if(targetMissing || methodMissing){ + listener.Remove(); + } + } + foreach(var item in Event.callers.Copy()){ + if(item.Key.IsNull()){ + Event.callers.Remove(item.Key); + } + } + } + public static object Verify(object target=null){ + if(target.IsNull()){target = Event.global;} + return target; + } + public static object[] VerifyAll(params object[] targets){ + if(targets.Length < 1){targets = new object[1]{Event.global};} + return targets; + } + public static void Empty(){} + public static void Register(string name){Event.Register(name,Event.Verify());} + public static void Register(string name,params object[] targets){ + if(Event.disabled.Has("Add")){return;} + foreach(object target in targets){ + if(target.IsNull()){continue;} + Event.callers.AddNew(target); + Event.callers[target].AddNew(name); + } + } + public static void AddStepper(string eventName,MethodStep method,IList collection,int passes=1){ + var stepper = new EventStepper(method,null,collection,passes); + stepper.onComplete = ()=>Event.Remove(eventName,stepper.Step); + Event.Add(eventName,stepper.Step).SetPermanent(); + } + public static EventListener Add(string name,Method method,params object[] targets){return Event.Add(name,(object)method,-1,targets);} + public static EventListener Add(string name,MethodObject method,params object[] targets){return Event.Add(name,(object)method,-1,targets);} + public static EventListener Add(string name,MethodFull method,params object[] targets){return Event.Add(name,(object)method,-1,targets);} + public static EventListener Add(string name,MethodString method,params object[] targets){return Event.Add(name,(object)method,-1,targets);} + public static EventListener Add(string name,MethodInt method,params object[] targets){return Event.Add(name,(object)method,-1,targets);} + public static EventListener Add(string name,MethodFloat method,params object[] targets){return Event.Add(name,(object)method,-1,targets);} + public static EventListener Add(string name,MethodBool method,params object[] targets){return Event.Add(name,(object)method,-1,targets);} + public static EventListener Add(string name,MethodVector2 method,params object[] targets){return Event.Add(name,(object)method,-1,targets);} + public static EventListener Add(string name,MethodVector3 method,params object[] targets){return Event.Add(name,(object)method,-1,targets);} + public static EventListener AddLimited(string name,Method method,int amount=1,params object[] targets){return Event.Add(name,(object)method,amount,targets);} + public static EventListener AddLimited(string name,MethodObject method,int amount=1,params object[] targets){return Event.Add(name,(object)method,amount,targets);} + public static EventListener AddLimited(string name,MethodFull method,int amount=1,params object[] targets){return Event.Add(name,(object)method,amount,targets);} + public static EventListener AddLimited(string name,MethodString method,int amount=1,params object[] targets){return Event.Add(name,(object)method,amount,targets);} + public static EventListener AddLimited(string name,MethodInt method,int amount=1,params object[] targets){return Event.Add(name,(object)method,amount,targets);} + public static EventListener AddLimited(string name,MethodFloat method,int amount=1,params object[] targets){return Event.Add(name,(object)method,amount,targets);} + public static EventListener AddLimited(string name,MethodBool method,int amount=1,params object[] targets){return Event.Add(name,(object)method,amount,targets);} + public static EventListener AddLimited(string name,MethodVector2 method,int amount=1,params object[] targets){return Event.Add(name,(object)method,amount,targets);} + public static EventListener AddLimited(string name,MethodVector3 method,int amount=1,params object[] targets){return Event.Add(name,(object)method,amount,targets);} + public static EventListener Add(string name,object method,int amount,params object[] targets){ + bool delayed = false; + bool systemReady = !Event.instance.IsNull() && Event.instance.setup; + if(!systemReady){ + if(Event.debug.Has("Add")){ + Debug.Log("[Events] : System not ready. Delaying event add -- " + Event.GetMethodName(method.As()) + " -- " + name); + } + delayed = true; + } + if(Event.disabled.Has("Add")){ + Debug.LogWarning("[Events] : Add attempted while Events disabled. " + name); + return null; + } + targets = Event.VerifyAll(targets); + var listener = Event.emptyListener; + foreach(object current in targets){ + var target = current; + if(target.IsNull()){continue;} + if(Event.unique.ContainsKey(target) && Event.unique[target].ContainsKey(name)){ + listener = Event.unique[target][name]; + continue; + } + if(!Event.cache.AddNew(target).AddNew(name).ContainsKey(method)){ + listener = new EventListener(); + if(systemReady && Event.debug.Has("Add")){ + var info = (Delegate)method; + Debug.Log("[Events] : Adding event -- " + Event.GetMethodName(info) + " -- " + name,target as UnityObject); + } + Event.listeners.Add(listener); + Utility.DelayCall(Event.OnEventsChanged); + } + else{ + listener = Event.cache[target][name].AddNew(method); + } + if(delayed){ + listener.name = "On Events Reset"; + listener.method = (Method)(()=>{ + var newEvent = Event.Add(name,method,amount,target); + newEvent.SetPermanent(listener.permanent); + newEvent.SetUnique(listener.unique); + listener.SetPermanent(false); + listener.SetUnique(false); + }); + listener.target = target = Event.global; + listener.occurrences = 1; + } + else{ + listener.name = name; + listener.method = method; + listener.target = target; + listener.occurrences = amount; + listener.isStatic = ((Delegate)method).Target.IsNull(); + } + Event.cache.AddNew(target).AddNew(listener.name)[listener.method] = listener; + Event.cache.AddNew(Event.all).AddNew(listener.name)[listener.method] = listener; + } + return listener; + } + public static void OnEventsChanged(){Event.Call("On Events Changed");} + public static void Remove(string name,Method method,params object[] targets){Event.Remove(name,(object)method,targets);} + public static void Remove(string name,MethodObject method,params object[] targets){Event.Remove(name,(object)method,targets);} + public static void Remove(string name,MethodFull method,params object[] targets){Event.Remove(name,(object)method,targets);} + public static void Remove(string name,MethodString method,params object[] targets){Event.Remove(name,(object)method,targets);} + public static void Remove(string name,MethodInt method,params object[] targets){Event.Remove(name,(object)method,targets);} + public static void Remove(string name,MethodFloat method,params object[] targets){Event.Remove(name,(object)method,targets);} + public static void Remove(string name,MethodBool method,params object[] targets){Event.Remove(name,(object)method,targets);} + public static void Remove(string name,MethodVector2 method,params object[] targets){Event.Remove(name,(object)method,targets);} + public static void Remove(string name,MethodVector3 method,params object[] targets){Event.Remove(name,(object)method,targets);} + public static void Remove(string name,object method,params object[] targets){ + if(Event.disabled.Has("Add")){return;} + targets = Event.VerifyAll(targets); + foreach(var target in targets){ + var removals = Event.listeners.Where(x=>x.method==method && x.target==target && x.name==name).ToList(); + removals.ForEach(x=>x.Remove()); + } + Utility.DelayCall(Event.OnEventsChanged); + } + public static void RemoveAll(string name,params object[] targets){ + foreach(var target in targets){ + Event.listeners.Where(x=>x.target==target&&x.name== name).ToList().ForEach(x=>x.Remove()); + } + } + public static void RemoveAll(params object[] targets){ + if(Event.disabled.Has("Add")){return;} + targets = Event.VerifyAll(targets); + foreach(var target in targets){ + var removals = Event.listeners.Where(x=>x.target==target || x.method.As().Target==target).ToList(); + removals.ForEach(x=>x.Remove()); + Event.cache.AddNew(target).SelectMany(x=>x.Value).Select(x=>x.Value).ToList().ForEach(x=>x.Remove()); + Event.cache.Remove(target); + } + Utility.DelayCall(Event.OnEventsChanged); + } + public static Dictionary Get(string name){return Event.Get(Event.global,name);} + public static Dictionary Get(object target,string name){ + return Event.cache.AddNew(target).AddNew(name); + } + public static bool HasListeners(object target,string name="*"){ + target = Event.Verify(target); + if(name == "*"){return Event.cache.ContainsKey(target);} + return Event.cache.ContainsKey(target) && Event.cache[target].ContainsKey(name); + } + public static bool HasCallers(object target,string name="*"){ + target = Event.Verify(target); + if(name == "*"){return Event.callers.ContainsKey(target);} + return Event.callers.ContainsKey(target) && Event.callers[target].Contains(name); + } + public static void SetPause(string type,string name,object target){ + target = Event.Verify(target); + if(Event.debug.Has("Pause")){ + string message = "[Events] : " + type + " event -- " + Event.GetTargetName(target) + " -- " + name; + Debug.Log(message,target as UnityObject); + } + foreach(var item in Event.Get(target,name)){ + item.Value.paused = type == "Pausing"; + } + } + public static void Pause(string name,object target=null){Event.SetPause("Pausing",name,target);} + public static void Resume(string name,object target=null){Event.SetPause("Resuming",name,target);} + public static void AddHistory(string name){ + if(Event.debug.Has("History")){ + int lastIndex = Event.eventHistory.Count-1; + if(lastIndex >= 0){ + string last = Event.eventHistory[lastIndex]; + string lastReal = last.Split("(")[0].Trim(); + if(lastReal == name){ + string value = last.Parse("(",")"); + int count = value.IsEmpty() ? 2 : value.ToInt() + 1; + value = " (" + count.ToString() + ")"; + Event.eventHistory[lastIndex] = name + value; + return; + } + } + Event.eventHistory.Add(name); + } + } + public static void Rest(string name,float seconds){Event.Rest(Event.global,name,seconds);} + public static void Rest(object target,string name,float seconds){ + foreach(var item in Event.Get(target,name)){ + item.Value.Rest(seconds); + } + } + public static void SetCooldown(string name,float seconds){Event.SetCooldown(Event.global,name,seconds);} + public static void SetCooldown(object target,string name,float seconds){ + foreach(var item in Event.Get(target,name)){ + item.Value.SetCooldown(seconds); + } + } + public static void DelayCall(string name,float delay=0.5f,params object[] values){ + Event.DelayCall(Event.global,"Global",name,delay,values); + } + public static void DelayCall(object key,string name,float delay=0.5f,params object[] values){ + Event.DelayCall(Event.global,key,name,delay,values); + } + public static void DelayCall(object target,object key,string name,float delay=0.5f,params object[] values){ + if(target.IsNull()){return;} + Utility.DelayCall(key,()=>Event.Call(target,name,values),delay); + } + public static void Call(string name,params object[] values){ + if(Event.disabled.Has("Call")){return;} + Event.Call(Event.Verify(),name,values); + } + public static void Call(object target,string name,params object[] values){ + if(Event.disabled.Has("Call")){return;} + if(Event.active.AddNew(target).Contains(name)){return;} + if(Event.stack.Count > 1000){ + Debug.LogWarning("[Events] : Event stack overflow."); + Event.disabled = (EventDisabled)(-1); + return; + } + target = Event.Verify(target); + bool hasEvents = Event.HasListeners(target,name); + var events = hasEvents ? Event.cache[target][name] : null; + int count = hasEvents ? events.Count : 0; + bool canDebug = Event.CanDebug(target,name,count); + bool debugDeep = canDebug && Event.debug.Has("CallDeep"); + bool debugTime = canDebug && Event.debug.Has("CallTimer"); + float duration = Time.realtimeSinceStartup; + if(hasEvents){ + Event.lastCalled = name; + Event.active[target].Add(name); + foreach(var item in events.Copy()){ + item.Value.Call(debugDeep,debugTime,values); + } + Event.lastCalled = ""; + Event.active[target].Remove(name); + } + if(debugTime && (!debugDeep || count < 1)){ + duration = Time.realtimeSinceStartup - duration; + if(duration > 0.001f || Event.debug.Has("CallTimerZero")){ + string time = duration.ToString("F10").TrimRight("0",".").Trim() + " seconds."; + string message = "[Events] : " + Event.GetTargetName(target) + " -- " + name + " -- " + count + " events -- " + time; + Debug.Log(message,target as UnityObject); + } + } + } + public static void CallChildren(object target,string name,object[] values,bool self=false){ + if(Event.disabled.Has("Call")){return;} + if(self){Event.Call(target,name,values);} + if(target is GameObject){ + var gameObject = (GameObject)target; + Transform[] children = Locate.GetObjectComponents(gameObject); + foreach(Transform transform in children){ + if(transform.gameObject == gameObject){continue;} + Event.CallChildren(transform.gameObject,name,values,true); + } + } + } + public static void CallParents(object target,string name,object[] values,bool self=false){ + if(Event.disabled.Has("Call")){return;} + if(self){Event.Call(target,name,values);} + if(target is GameObject){ + var gameObject = (GameObject)target; + Transform parent = gameObject.transform.parent; + while(parent != null){ + Event.CallParents(parent.gameObject,name,values,true); + parent = parent.parent; + } + } + } + public static void CallFamily(object target,string name,object[] values,bool self=false){ + if(Event.disabled.Has("Call")){return;} + if(self){Event.Call(target,name,values);} + Event.CallChildren(target,name,values); + Event.CallParents(target,name,values); + } + //======================== + // Editor + //======================== + public static string GetTargetName(object target){ + if(target.IsNull()){return "Null";} + string targetName = ""; + if(target is string){targetName = target.ToString();} + if(target is GameObject){targetName = ((GameObject)target).GetPath();} + else if(target is Component){targetName = ((Component)target).GetPath();} + else if(target.HasVariable("path")){targetName = target.GetVariable("path");} + if(targetName.IsEmpty()){targetName = target.GetType().Name;} + targetName = targetName.Trim("/"); + if(targetName.Contains("__")){targetName = "Anonymous";} + return targetName; + } + public static string GetMethodName(object method){ + string name = "Unknown"; + if(method is Delegate){ + var info = (Delegate)method; + string targetName = info.Method.DeclaringType.Name; + string methodName = info.Method.Name; + if(!info.Target.IsNull()){ + targetName = GetTargetName(info.Target); + } + if(targetName.Contains("__")){targetName = "Anonymous";} + if(methodName.Contains("__")){methodName = "Anonymous";} + name = targetName + "." + methodName + "()"; + } + return name; + } + public static bool CanDebug(object target,string name,int count){ + bool allowed = true; + var debug = Event.debug; + var scope = Event.debugScope; + allowed = target == Event.global ? scope.Has("Global") : scope.Has("Scoped"); + allowed = allowed && (count > 0 || debug.HasAny("CallEmpty")); + if(allowed && name.ContainsAny("On Update","On Late Update","On Fixed Update","On Editor Update","On GUI","On Camera","On Undo Flushing")){ + allowed = debug.Has("CallUpdate"); + } + if(allowed && !debug.Has("CallTimer") && debug.HasAny("Call","CallUpdate","CallDeep","CallEmpty")){ + string message = "[Events] : Calling " + name + " -- " + count + " events -- " + Event.GetTargetName(target); + Debug.Log(message,target as UnityObject); + } + return allowed; + } + public static void Clean(string ignoreName="",object target=null,object targetMethod=null){ + foreach(var eventListener in Event.listeners){ + string eventName = eventListener.name; + object eventTarget = eventListener.target; + object eventMethod = eventListener.method; + bool duplicate = eventName != ignoreName && eventTarget == target && eventMethod.Equals(targetMethod); + bool invalid = eventTarget.IsNull() || eventMethod.IsNull() || (!eventListener.isStatic && ((Delegate)eventMethod).Target.IsNull()); + if(duplicate || invalid){ + Utility.DelayCall(()=>Event.listeners.Remove(eventListener)); + if(Event.debug.Has("Remove")){ + string messageType = eventMethod.IsNull() ? "empty method" : "duplicate method"; + string message = "[Events] Removing " + messageType + " from -- " + eventTarget + "/" + eventName; + Debug.Log(message,target as UnityObject); + } + } + } + foreach(var current in Event.callers){ + object scope = current.Key; + if(scope.IsNull()){ + Utility.DelayCall(()=>Event.callers.Remove(scope)); + } + } + } + public static List GetEventNames(string type,object target=null){ + Utility.EditorCall(()=>Event.Clean()); + target = Event.Verify(target); + if(type.Contains("Listen",true)){ + return Event.listeners.ToList().FindAll(x=>x.target==target).Select(x=>x.name).ToList(); + } + if(Event.callers.ContainsKey(target)){ + return Event.callers[target]; + } + return new List(); + } + } + public static class ObjectEventExtensions{ + public static void RegisterEvent(this object current,string name,params object[] values){ + if(current.IsNull()){return;} + Event.Register(name,current); + } + public static EventListener AddEvent(this object current,string name,object method,int amount=-1){ + if(current.IsNull()){return Event.emptyListener;} + return Event.Add(name,method,amount,current); + } + public static void RemoveEvent(this object current,string name,object method){ + if(current.IsNull()){return;} + Event.Remove(name,method,current); + } + public static void RemoveAllEvents(this object current,string name,object method){ + if(current.IsNull()){return;} + Event.RemoveAll(current); + } + public static void DelayEvent(this object current,string name,float delay=0.5f,params object[] values){ + Event.DelayCall(current,current,name,delay,values); + } + public static void DelayEvent(this object current,object key,string name,float delay=0.5f,params object[] values){ + Event.DelayCall(current,key,name,delay,values); + } + public static void RestEvent(this object current,string name,float seconds){ + if(current.IsNull()){return;} + Event.Rest(current,name,seconds); + } + public static void CooldownEvent(this object current,string name,float seconds){ + if(current.IsNull()){return;} + Event.SetCooldown(current,name,seconds); + } + public static void CallEvent(this object current,string name,params object[] values){ + if(current.IsNull()){return;} + Event.Call(current,name,values); + } + public static void CallEventChildren(this object current,string name,bool self=true,params object[] values){ + if(current.IsNull()){return;} + Event.CallChildren(current,name,values,self); + } + public static void CallEventParents(this object current,string name,bool self=true,params object[] values){ + if(current.IsNull()){return;} + Event.CallParents(current,name,values,self); + } + public static void CallEventFamily(this object current,string name,bool self=true,params object[] values){ + if(current.IsNull()){return;} + Event.CallFamily(current,name,values,self); + } + } +} \ No newline at end of file diff --git a/Systems/Event/EventDetector.cs b/Systems/Event/EventDetector.cs new file mode 100644 index 000000000..0dc75b89d --- /dev/null +++ b/Systems/Event/EventDetector.cs @@ -0,0 +1,74 @@ +#pragma warning disable 0618 +using System; +using UnityEngine; +namespace Zios.Events{ + [AddComponentMenu("")][ExecuteInEditMode] + public class EventDetector : MonoBehaviour{ + private static bool showTime = false; + private float loadStart; + [NonSerialized] public static bool loading = true; + public void Loading(){ + this.loadStart = Time.realtimeSinceStartup; + EventDetector.loading = true; + } + public virtual void OnValidate(){ + this.Loading(); + Event.Call("On Validate"); + } + public virtual void Awake(){ + Event.Add("On Asset Modifying",this.Loading); + Event.Add("On Application Quit",this.Loading); + Event.Add("On Enter Play",this.Loading); + Event.Add("On Exit Play",this.Loading); + Event.Register("On Awake"); + Event.Register("On Start"); + Event.Register("On Update"); + Event.Register("On Fixed Update"); + Event.Register("On Late Update"); + Event.Register("On Enable"); + Event.Register("On Disable"); + Event.Register("On GUI"); + Event.Register("On Destroy"); + Event.Register("On Validate"); + Event.Register("On Reset"); + Event.Register("On Player Connected"); + Event.Register("On Player Disconnected"); + Event.Register("On Level Was Loaded"); + Event.Register("On Master Server Event"); + Event.Register("On Application Quit"); + Event.Register("On Application Focus"); + Event.Register("On Application Pause"); + Event.Register("On Disconnected From Server"); + Event.Call("On Awake"); + } + public virtual void Start(){Event.Call("On Start");} + public virtual void Update(){ + Utility.CheckLoaded(false); + if(!Application.isLoadingLevel && EventDetector.loading){ + Event.Call("On Level Was Loaded"); + float totalTime = Mathf.Max(Time.realtimeSinceStartup-this.loadStart,0); + if(EventDetector.showTime){ + Debug.Log("[Scene] : Load complete -- " + (totalTime) + " seconds."); + } + this.loadStart = 0; + EventDetector.loading = false; + } + Event.Call("On Update"); + } + public virtual void FixedUpdate(){Event.Call("On Fixed Update");} + public virtual void LateUpdate(){Event.Call("On Late Update");} + public virtual void OnPlayerConnected(){Event.Call("On Player Connected");} + public virtual void OnPlayerDisconnected(){Event.Call("On Player Disconnected");} + //public virtual void OnLevelWasLoaded(int level){Event.Call("On Level Was Loaded",level);} + public virtual void OnMasterServerEvent(){Event.Call("On Master Server Event");} + public virtual void OnApplicationQuit(){Event.Call("On Application Quit");} + public virtual void OnApplicationFocus(){Event.Call("On Application Focus");} + public virtual void OnApplicationPause(){Event.Call("On Application Pause");} + public virtual void OnDisconnectedFromServer(){Event.Call("On Disconnected From Server");} + public virtual void OnGUI(){Event.Call("On GUI");} + public virtual void OnEnable(){Event.Call("On Enable");} + public virtual void OnDisable(){Event.Call("On Disable");} + public virtual void OnDestroy(){Event.Call("On Destroy");} + public virtual void Reset(){Event.Call("On Reset");} + } +} \ No newline at end of file diff --git a/Systems/Event/EventListener.cs b/Systems/Event/EventListener.cs new file mode 100644 index 000000000..0d5e30b87 --- /dev/null +++ b/Systems/Event/EventListener.cs @@ -0,0 +1,91 @@ +using System; +using UnityEngine; +namespace Zios.Events{ + [Serializable] + public class EventListener{ + public object target; + public object method; + public string name; + public int occurrences; + public bool paused; + public bool permanent; + public bool unique; + public bool isStatic; + public float cooldown; + private float resting; + private bool warned; + private bool delayed; + public bool IsValid(){ + var method = this.method.As(); + bool nullTarget = this.target.IsNull() || (!this.isStatic && method.Target.IsNull()); + if(nullTarget && !this.warned && Event.debug.Has("Call")){ + Debug.LogWarning("[Events] Null call attempted -- " + this.name + " -- " + this.target + " -- " + Event.GetMethodName(method)); + this.warned = true; + } + return !nullTarget; + } + public bool IsResting(){return Time.realtimeSinceStartup < this.resting;} + public void Rest(float seconds=1){this.resting = Time.realtimeSinceStartup+seconds;} + public void SetCooldown(float seconds=1){this.cooldown = seconds;} + public void SetPaused(bool state=true){this.paused = state;} + public void SetPermanent(bool state=true){this.permanent = state;} + public void SetUnique(bool state=true){ + if(state){Event.unique.AddNew(this.target)[this.name] = this;} + this.unique = state; + } + public void Remove(){ + if(Event.cache.ContainsKey(this.target) && Event.cache[this.target].ContainsKey(this.name)){ + Event.cache[this.target][this.name].Remove(this.method); + } + if(Event.cache.AddNew(Event.all).ContainsKey(this.name)){ + Event.cache[Event.all][this.name].Remove(this.method); + } + Event.listeners.Remove(this); + this.paused = true; + } + public void Call(bool debugDeep,bool debugTime,object[] values){ + if(Utility.IsPaused() || this.paused || !this.IsValid()){return;} + if(this.IsResting()){ + if(!this.delayed){ + float remaining = this.resting-Time.realtimeSinceStartup; + Utility.DelayCall(this.method,()=>this.Call(debugDeep,debugTime,values),remaining); + this.delayed = true; + } + return; + } + this.delayed = false; + Event.stack.Add(this); + Event.AddHistory(this.name); + float duration = Time.realtimeSinceStartup; + if(this.cooldown > 0){this.Rest(this.cooldown);} + if(this.occurrences > 0){this.occurrences -= 1;} + if(this.occurrences == 0){this.Remove();} + if(values.Length < 1 || this.method is Method){ + ((Method)this.method)(); + } + else{ + object value = values[0]; + if(this.method is MethodFull){((MethodFull)this.method)(values);} + else if(value is object && this.method is MethodObject){((MethodObject)this.method)((object)value);} + else if(value is int && this.method is MethodInt){((MethodInt)this.method)((int)value);} + else if(value is float && this.method is MethodFloat){((MethodFloat)this.method)((float)value);} + else if(value is string && this.method is MethodString){((MethodString)this.method)((string)value);} + else if(value is bool && this.method is MethodBool){((MethodBool)this.method)((bool)value);} + else if(value is Vector2 && this.method is MethodVector2){((MethodVector2)this.method)((Vector2)value);} + else if(value is Vector3 && this.method is MethodVector3){((MethodVector3)this.method)((Vector3)value);} + } + if(debugDeep){ + string message = "[Events] : " + name + " -- " + Event.GetMethodName(this.method); + if(debugTime){ + duration = Time.realtimeSinceStartup - duration; + if(duration > 0.001f || Event.debug.Has("CallTimerZero")){ + string time = duration.ToString("F10").TrimRight("0",".").Trim() + " seconds."; + message = message + " -- " + time; + } + } + Debug.Log(message); + } + Event.stack.Remove(this); + } + } +} \ No newline at end of file diff --git a/Systems/Event/EventStepper.cs b/Systems/Event/EventStepper.cs new file mode 100644 index 000000000..9fa9e0f27 --- /dev/null +++ b/Systems/Event/EventStepper.cs @@ -0,0 +1,52 @@ +#pragma warning disable 0618 +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +namespace Zios.Events{ + [Serializable] + public class EventStepper{ + [NonSerialized] public static List instances = new List(); + [NonSerialized] public static EventStepper active; + [NonSerialized] public static string title; + [NonSerialized] public static string message; + public Method onComplete = ()=>{}; + public MethodStep method; + public IList collection; + public int index; + public int passes = 1; + public bool complete; + public EventStepper(MethodStep method,Method onComplete,IList collection,int passCount=1){ + this.method = method; + this.collection = collection; + this.passes = passCount; + this.onComplete = onComplete ?? this.onComplete; + EventStepper.instances.AddNew(this); + } + public void Step(){ + EventStepper.active = this; + var count = this.passes; + while(count > 0){ + count -= 1; + bool canceled = false; + if(this.index != -1){ + this.method(this.collection,this.index); + float percent = ((float)this.index)/this.collection.Count; + canceled = Utility.DisplayCancelableProgressBar(EventStepper.title,EventStepper.message,percent); + this.index += 1; + } + bool loading = Application.isLoadingLevel; + bool ended = (this.index > this.collection.Count-1) || this.index == -1; + if((loading || canceled || ended) && !this.complete){ + this.index = -1; + this.complete = true; + this.onComplete(); + EventStepper.instances.Remove(this); + Utility.ClearProgressBar(); + break; + } + } + EventStepper.active = null; + } + } +} \ No newline at end of file diff --git a/Systems/File/FileManager.cs b/Systems/File/FileManager.cs new file mode 100644 index 000000000..fc06c81b6 --- /dev/null +++ b/Systems/File/FileManager.cs @@ -0,0 +1,388 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using UnityEngine; +using UnityObject = UnityEngine.Object; +namespace Zios{ + using Events; + using Containers; + #if UNITY_EDITOR + using UnityEditor; + #endif + public static class FileManager{ + private static string path; + private static bool setup; + private static bool debug = false; + private static bool clock = false; + private static bool fullScan = true; + private static DateTime pathModifyTime; + public static Dictionary> filesByPath = new Dictionary>(StringComparer.InvariantCultureIgnoreCase); + public static Dictionary> filesByType = new Dictionary>(StringComparer.InvariantCultureIgnoreCase); + public static Dictionary folders = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + public static Dictionary cache = new Dictionary(); + public static Dictionary assets = new Dictionary(); + public static Hierarchy namedAssets = new Hierarchy(); + //=============== + // Storage + //=============== + public static void Load(){ + var time = Time.realtimeSinceStartup; + if(FileManager.Exists("FileManager.data")){ + int mode = 0; + string extension = ""; + string lastPath = ""; + var lines = File.ReadAllLines("FileManager.data"); + for(int index=0;index","?",":","|")){ + if(name[1] != ':') { + Debug.LogWarning("[FileManager] Path has invalid characters -- " + name); + return new FileData[0]; + } + } + if(!name.Contains(".") && name.EndsWith("*")){name = name + ".*";} + if(name.Contains("*")){firstOnly = false;} + else if(name.Contains(":")){firstOnly = true;} + string fileName = name.GetFileName(); + string path = name.GetDirectory(); + string type = name.GetFileExtension().ToLower(); + var results = new List(); + var types = new List(); + var allTypes = FileManager.filesByType.Keys; + if(type.IsEmpty() || type == "*"){types = allTypes.ToList();} + else if(type.StartsWith("*")){types.AddRange(allTypes.Where(x=>x.EndsWith(type.Remove("*"),true)));} + else if(type.EndsWith("*")){types.AddRange(allTypes.Where(x=>x.StartsWith(type.Remove("*"),true)));} + else if(FileManager.filesByType.ContainsKey(type)){types.Add(type);} + foreach(var typeName in types){ + FileManager.SearchType(fileName,typeName,path,firstOnly,ref results); + } + if(results.Count == 0){ + foreach(var item in FileManager.folders){ + FileData folder = item.Value; + string folderPath = item.Key; + if(folderPath.Matches(name,true)){ + results.Add(folder); + } + } + } + if(results.Count == 0 && showWarnings){Debug.LogWarning("[FileManager] Path [" + name + "] could not be found.");} + FileManager.cache[searchKey] = results.ToArray(); + if(FileManager.clock){Debug.Log("[FileManager] : Find [" + name + "] complete (" + results.Count + ") -- " + (Time.realtimeSinceStartup-time) + " seconds.");} + return results.ToArray(); + } + public static void SearchType(string name,string type,string path,bool firstOnly,ref List results){ + bool pathSearch = !path.IsEmpty() && FileManager.filesByPath.ContainsKey(path); + var files = pathSearch ? FileManager.filesByPath[path] : FileManager.filesByType[type]; + if(FileManager.debug){Debug.Log("[FileManager] Search -- " + name + " -- " + type + " -- " + path);} + foreach(FileData file in files){ + bool correctPath = pathSearch ? true : file.path.Contains(path,true); + bool correctType = !pathSearch ? true : file.extension.Matches(type,true); + bool wildcard = name.IsEmpty() || name == "*"; + wildcard = wildcard || name.StartsWith("*") && file.name.EndsWith(name.Remove("*"),true); + wildcard = wildcard || (name.EndsWith("*") && file.name.StartsWith(name.Remove("*"),true)); + if(correctPath && correctType && (wildcard || file.name.Matches(name,true))){ + results.Add(file); + if(firstOnly){return;} + } + } + } + public static string GetPath(UnityObject target,bool relative=true){ + if(!FileManager.setup){FileManager.Refresh();} + #if UNITY_EDITOR + if(Application.isEditor){ + string assetPath = AssetDatabase.GetAssetPath(target); + if(!relative){assetPath = Application.dataPath.Replace("Assets","") + assetPath;} + return assetPath; + } + #endif + return ""; + } + public static FileData GetFile(UnityObject target){ + return FileManager.Find(FileManager.GetPath(target,false)); + } + public static T GetAsset(UnityObject target){ + #if UNITY_EDITOR + if(Application.isEditor){ + if(!FileManager.setup){FileManager.Refresh();} + if(!FileManager.assets.ContainsKey(target)){ + string assetPath = FileManager.GetPath(target); + object asset = AssetDatabase.LoadAssetAtPath(assetPath,typeof(T)); + if(asset == null){return default(T);} + FileManager.assets[target] = Convert.ChangeType(asset,typeof(T)); + } + return (T)FileManager.assets[target]; + } + #endif + return default(T); + } + public static FileData Create(string path){ + path = Path.GetFullPath(path).Replace("\\","/"); + var data = new FileData(path); + if(!path.GetFileName().IsEmpty()){ + File.Create(path).Dispose(); + } + else{ + data.isFolder = true; + Directory.CreateDirectory(path); + } + data.BuildCache(); + return data; + } + public static void Delete(string path){ + var file = FileManager.Find(path); + if(!file.IsNull()){ + file.Delete(); + } + } + public static void WriteFile(string path,byte[] bytes){ + var folder = path.GetDirectory(); + if(!FileManager.Exists(folder)){FileManager.Create(folder);} + FileStream stream = new FileStream(path,FileMode.Create); + BinaryWriter file = new BinaryWriter(stream); + file.Write(bytes); + file.Close(); + stream.Close(); + } + //=============== + // Shorthand + //=============== + public static bool Exists(string path){return File.Exists(path) || Directory.Exists(path);} + public static FileData Find(string name,bool showWarnings=true){ + name = !name.ContainsAny("*") ? "!"+name : name; + var results = FileManager.FindAll(name,showWarnings,true); + if(results.Length > 0){return results[0];} + return null; + } + public static FileData Get(UnityObject target,bool showWarnings=false){ + string path = FileManager.GetPath(target,false); + return FileManager.Find(path,showWarnings); + } + public static string GetGUID(string name,bool showWarnings=true){ + FileData file = FileManager.Find(name,showWarnings); + if(file != null){return file.GetGUID();} + return ""; + } + public static T GetAsset(string name,bool showWarnings=true){ + FileData file = FileManager.Find(name,showWarnings); + if(file != null){return file.GetAsset();} + return default(T); + } + public static T[] GetAssets(string name="*",bool showWarnings=true){ + var files = FileManager.FindAll(name,true,showWarnings); + if(files.Length < 1){return new T[0];} + return files.Select(x=>x.GetAsset()).Where(x=>!x.IsNull()).ToArray(); + } + public static Dictionary GetNamedAssets(string name="*",bool showWarnings=true) where T : UnityObject{ + if(!FileManager.namedAssets.AddNew(typeof(T)).ContainsKey(name)){ + var files = FileManager.GetAssets(name,showWarnings).GroupBy(x=>x.name).Select(x=>x.First()); + FileManager.namedAssets[typeof(T)][name] = files.ToDictionary(x=>x.name,x=>(UnityObject)x); + } + return FileManager.namedAssets[typeof(T)][name].ToDictionary(x=>x.Key,x=>(T)x.Value); + } + } + [Serializable] + public class FileData{ + public string path; + public string directory; + public string name; + public string fullName; + public string extension; + public bool isFolder; + public FileData(){} + public FileData(string path,bool isFolder=false){ + this.path = path; + this.directory = path.GetDirectory(); + this.extension = path.GetFileExtension(); + this.name = path.GetFileName(); + this.fullName = this.name + "." + this.extension; + this.isFolder = isFolder; + } + public void BuildCache(){ + FileManager.cache["!"+this.path.ToLower()] = this.AsArray(); + if(!this.isFolder){ + FileManager.cache["!"+this.fullName.ToLower()] = this.AsArray(); + FileManager.filesByType.AddNew(this.extension).Add(this); + FileManager.filesByPath.AddNew(this.directory).Add(this); + return; + } + FileManager.folders[this.path] = this; + } + public string GetText(){ + return File.ReadAllText(this.path); + } + public void WriteText(string contents){ + File.WriteAllText(this.path,contents); + } + public void Delete(){ + foreach(var item in FileManager.cache.Copy()){ + if(item.Value.Contains(this)){ + FileManager.cache[item.Key] = item.Value.Remove(this); + if(FileManager.cache[item.Key].Length < 1){ + FileManager.cache.Remove(item.Key); + } + } + } + if(!this.isFolder){ + File.Delete(this.path); + FileManager.filesByType[this.extension].Remove(this); + FileManager.filesByPath[this.path].Remove(this); + return; + } + Directory.Delete(this.path); + FileManager.folders.Remove(this.path); + } + public void MarkDirty(){File.SetLastWriteTime(this.path,DateTime.Now);} + public string GetModifiedDate(string format="M-d-yy"){return File.GetLastWriteTime(this.path).ToString(format);} + public string GetAccessedDate(string format="M-d-yy"){return File.GetLastAccessTime(this.path).ToString(format);} + public string GetCreatedDate(string format="M-d-yy"){return File.GetCreationTime(this.path).ToString(format);} + public string GetChecksum(){return this.GetText().ToMD5();} + public T GetAsset(){ + #if UNITY_EDITOR + if(Application.isEditor && this.path.IndexOf("Assets") != -1){ + return (T)AssetDatabase.LoadAssetAtPath(this.GetAssetPath(),typeof(T)).Box(); + } + #endif + return default(T); + } + public string GetGUID(){ + #if UNITY_EDITOR + if(Application.isEditor){ + return AssetDatabase.AssetPathToGUID(this.GetAssetPath()); + } + #endif + return ""; + } + public string GetAssetPath(bool full=true){ + string path = this.path.Substring(this.path.IndexOf("Assets")); + if(full){return path;} + return path.Cut(0,path.LastIndexOf("/")); + } + public string GetFolderPath(){ + return this.path.Substring(0,this.path.LastIndexOf("/")) + "/"; + } + } +} \ No newline at end of file diff --git a/Systems/Interface/Style/Style.cs b/Systems/Interface/Style/Style.cs new file mode 100644 index 000000000..f14485c07 --- /dev/null +++ b/Systems/Interface/Style/Style.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using UnityEngine; +namespace Zios{ + [InitializeOnLoad] + public static partial class Style{ + public static Dictionary skins = new Dictionary(); + public static Dictionary> styles = new Dictionary>(); + public static GUISkin defaultSkin; + public static GUISkin GetSkin(string name="",bool defaultFallback=true){ + if(Style.skins.ContainsKey(name)){ + return Style.skins[name]; + } + /*var cssFile = FileManager.Find(name+".css",true,false); + if(!cssFile.IsNull()){ + return Style.LoadCSS(name,cssFile.GetText()); + }*/ + Style.defaultSkin = Style.defaultSkin ?? typeof(GUI).GetVariable("s_Skin"); + var defaultSkin = defaultFallback ? Style.defaultSkin : null; + return FileManager.GetAsset(name+".guiskin",defaultFallback) ?? defaultSkin; + } + public static GUIStyle Get(string skin,string name,bool copy=false){ + var guiSkin = Style.GetSkin(skin); + return Style.Get(guiSkin,name,copy); + } + public static GUIStyle Get(GUISkin skin,string name,bool copy=false){ + GUIStyle style; + if(Style.styles.AddNew(skin).ContainsKey(name)){ + style = Style.styles[skin][name]; + if(copy){return new GUIStyle(style);} + return style; + } + style = skin.GetStyle(name); + if(style != null){Style.styles[skin][name] = style;} + if(copy){return new GUIStyle(style);} + return style; + } + public static GUIStyle Get(string name,bool copy=false){ + return Style.Get(GUI.skin,name,copy); + } + } +} \ No newline at end of file diff --git a/Systems/Interface/Style/StyleCSS.cs b/Systems/Interface/Style/StyleCSS.cs new file mode 100644 index 000000000..c73dbb86b --- /dev/null +++ b/Systems/Interface/Style/StyleCSS.cs @@ -0,0 +1,120 @@ +using System; +using System.Text; +using System.Linq; +using System.Collections.Generic; +using UnityEngine; +namespace Zios{ + public static partial class Style{ + [NonSerialized] private static StringBuilder contents = new StringBuilder(); + public static void SaveCSS(string path,GUISkin skin){ + var output = new StringBuilder(); + var emptySkin = ScriptableObject.CreateInstance(); + foreach(var item in skin.GetVariables(null,ObjectExtension.publicFlags)){ + var defaultStyle = emptySkin.GetVariable(item.Key); + output.Append(Style.SaveCSS(item.Value,defaultStyle)); + } + foreach(var style in skin.customStyles){ + if(style.IsNull()){continue;} + output.Append(Style.SaveCSS(style,null,false)); + } + if(output.Length > 0){ + var file = FileManager.Create(path+"/"+skin.name.Split("-")[0]+"/"+skin.name+".css"); + file.WriteText(output.ToString().Trim()); + } + } + public static string SaveCSS(GUIStyle style,GUIStyle empty=null,bool asTag=true){ + empty = empty ?? GUIStyle.none; + var contents = Style.contents; + var styleName = asTag ? style.name : "."+style.name; + contents.Clear(); + if(!style.border.Matches(empty.border)){contents.AppendLine("\tborder : "+style.border.Serialize(" "));} + if(!style.margin.Matches(empty.margin)){contents.AppendLine("\tmargin : "+style.margin.Serialize(" "));} + if(!style.padding.Matches(empty.padding)){contents.AppendLine("\tpadding : "+style.padding.Serialize(" "));} + if(!style.overflow.Matches(empty.overflow)){contents.AppendLine("\toverflow : "+style.overflow.Serialize(" "));} + if(style.font != empty.font){contents.AppendLine("\tfont : "+style.font.name);} + if(style.fontSize != empty.fontSize){contents.AppendLine("\tfont-size : "+style.fontSize);} + if(style.fontStyle != empty.fontStyle){contents.AppendLine("\tfont-style : "+style.fontStyle.ToName().ToCamelCase());} + if(style.alignment != empty.alignment){contents.AppendLine("\talignment : "+style.alignment.ToName().ToCamelCase());} + if(style.wordWrap != empty.wordWrap){contents.AppendLine("\tword-wrap : "+style.wordWrap);} + if(style.richText != empty.richText){contents.AppendLine("\trich-text : "+style.richText);} + if(style.clipping != empty.clipping){contents.AppendLine("\ttext-clipping : "+style.clipping.ToName().ToCamelCase());} + if(style.imagePosition != empty.imagePosition){contents.AppendLine("\timage-position : "+style.imagePosition.ToName().ToCamelCase());} + if(!style.contentOffset.Equals(empty.contentOffset)){contents.AppendLine("\tcontent-offset : "+style.contentOffset.x+" "+style.contentOffset.y);} + if(style.fixedWidth != empty.fixedWidth){contents.AppendLine("\tfixed-width : "+style.fixedWidth);} + if(style.fixedHeight != empty.fixedHeight){contents.AppendLine("\tfixed-height : "+style.fixedHeight);} + if(style.stretchWidth != empty.stretchWidth){contents.AppendLine("\tstretch-width : "+style.stretchWidth);} + if(style.stretchHeight != empty.stretchHeight){contents.AppendLine("\tstretch-height : "+style.stretchHeight);} + if(contents.Length > 0){ + contents.Insert(0,styleName+"{\n"); + contents.AppendLine("}"); + } + foreach(var item in style.GetVariables(null,ObjectExtension.publicFlags)){ + var state = item.Value; + bool hasBackground = !state.background.IsNull(); + bool hasTextColor = state.textColor!=empty.normal.textColor; + if(!hasBackground && !hasTextColor){continue;} + contents.AppendLine(styleName+":"+item.Key+"{"); + if(hasBackground){contents.AppendLine("\tbackground : "+state.background.name);} + if(hasTextColor){contents.AppendLine("\ttext-color : "+state.textColor.ToHex(false));} + contents.AppendLine("}"); + } + return contents.ToString().Replace("True","true").Replace("False","false"); + } + public static GUISkin LoadCSS(string name,string contents){ + var skin = ScriptableObject.CreateInstance(); + var styles = new Dictionary(); + GUIStyle active = null; + GUIStyleState state = null; + var stateName = ""; + //var textures = FileManager.GetAssets().ToDictionary(x=>x.name,x=>x); + //var fonts = FileManager.GetAssets().ToDictionary(x=>x.name,x=>x); + var textures = FileManager.GetNamedAssets(); + var fonts = FileManager.GetNamedAssets(); + foreach(var current in contents.GetLines()){ + var line = current.TrimLeft(".").Trim(); + if(line.Contains("{")){ + state = null; + stateName = ""; + bool builtin = !current.StartsWith("."); + var styleName = line.Contains(":") ? line.Parse("",":") : line.Parse("","{"); + styleName = styleName.Trim(); + active = builtin ? skin.GetVariable(styleName) : styles.AddNew(styleName); + active.name = styleName; + if(line.Contains(":")){ + state = new GUIStyleState(); + stateName = line.Parse(":","{").Trim(); + active.SetVariable(stateName,state); + } + continue; + } + if(line.StartsWith("}")){continue;} + var term = line.Parse("",":").Trim(); + var value = line.Parse(":","").TrimRight(";").Trim(); + if(!state.IsNull()){ + if(term.Contains("background")){state.background = textures.ContainsKey(value) ? textures[value] : null;} + if(term.Contains("text-color")){state.textColor = value.ToColor();} + continue; + } + if(term.Contains("border")){active.border = value.ToRectOffset(" ");} + if(term.Contains("margin")){active.margin = value.ToRectOffset(" ");} + if(term.Contains("padding")){active.padding = value.ToRectOffset(" ");} + if(term.Contains("overflow")){active.overflow = value.ToRectOffset(" ");} + if(term.Contains("font")){active.font = fonts.ContainsKey(value) ? fonts[value] : null;} + if(term.Contains("font-size")){active.fontSize = value.ToInt();} + if(term.Contains("font-style")){active.fontStyle = (FontStyle)Enum.Parse(typeof(FontStyle),value);} + if(term.Contains("alignment")){active.alignment = (TextAnchor)Enum.Parse(typeof(TextAnchor),value);} + if(term.Contains("word-wrap")){active.wordWrap = value.ToBool();} + if(term.Contains("rich-text")){active.wordWrap = value.ToBool();} + if(term.Contains("text-clipping")){active.clipping = (TextClipping)Enum.Parse(typeof(TextClipping),value);} + if(term.Contains("image-position")){active.imagePosition = (ImagePosition)Enum.Parse(typeof(ImagePosition),value);} + if(term.Contains("content-offset")){active.imagePosition = (ImagePosition)Enum.Parse(typeof(ImagePosition),value);} + if(term.Contains("fixed-width")){active.fixedWidth = value.ToFloat();} + if(term.Contains("fixed-height")){active.fixedHeight = value.ToFloat();} + if(term.Contains("stretch-width")){active.stretchWidth = value.ToBool();} + if(term.Contains("stretch-height")){active.stretchHeight = value.ToBool();} + } + skin.customStyles = styles.Values.ToArray(); + return skin; + } + } +} \ No newline at end of file diff --git a/Systems/Interface/Themes/Editor/ThemeVerticalSpace.cs b/Systems/Interface/Themes/Editor/ThemeVerticalSpace.cs new file mode 100644 index 000000000..e6306b9a1 --- /dev/null +++ b/Systems/Interface/Themes/Editor/ThemeVerticalSpace.cs @@ -0,0 +1,23 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Linq; +using UnityObject = UnityEngine.Object; +namespace Zios{ + /*[CustomPropertyDrawer(typeof(object),true)] + public class ThemeVerticalSpace : PropertyDrawer{ + public override float GetPropertyHeight(SerializedProperty property,GUIContent label){return base.GetPropertyHeight(property,label) * Themes.verticalSpacing;} + public override void OnGUI(Rect area,SerializedProperty property,GUIContent label){typeof(EditorGUI).CallMethod("DefaultPropertyField",new object[]{area,property,label});} + } + [CustomPropertyDrawer(typeof(PropertyAttribute),true)] public class PropertyVerticalSpace : ThemeVerticalSpace{} + [CustomPropertyDrawer(typeof(SerializableAttribute),true)] public class SerializableVerticalSpace : ThemeVerticalSpace{} + [CustomPropertyDrawer(typeof(SerializedObject),true)] public class SerializedObjectVerticalSpace : ThemeVerticalSpace{} + [CustomPropertyDrawer(typeof(SerializedProperty),true)] public class SerializedPropertyVerticalSpace : ThemeVerticalSpace{} + [CustomPropertyDrawer(typeof(UnityObject),true)] public class UnityObjectVerticalSpace : ThemeVerticalSpace{} + [CustomPropertyDrawer(typeof(int),true)] public class IntVerticalSpace : ThemeVerticalSpace{} + [CustomPropertyDrawer(typeof(string),true)] public class StringVerticalSpace : ThemeVerticalSpace{} + [CustomPropertyDrawer(typeof(float),true)] public class FloatVerticalSpace : ThemeVerticalSpace{} + [CustomPropertyDrawer(typeof(bool),true)] public class BoolVerticalSpace : ThemeVerticalSpace{} + [CustomPropertyDrawer(typeof(Enumerable),true)] public class ListVerticalSpace : ThemeVerticalSpace{} + [CustomPropertyDrawer(typeof(Enum),true)] public class EnumVerticalSpace : ThemeVerticalSpace{}*/ +} \ No newline at end of file diff --git a/Systems/Interface/Themes/Themes.cs b/Systems/Interface/Themes/Themes.cs new file mode 100644 index 000000000..3d847e91d --- /dev/null +++ b/Systems/Interface/Themes/Themes.cs @@ -0,0 +1,442 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using UnityEngine; +namespace Zios{ + using Interface; + #if UNITY_EDITOR + using UnityEditor; + #if UNITY_EDITOR_WIN + using Microsoft.Win32; + #endif + [InitializeOnLoad] + public static partial class Themes{ + public static List palettes = new List(); + public static List all = new List(); + public static Theme active; + [NonSerialized] public static string revision = "1 [r485]"; + [NonSerialized] public static int themeIndex; + [NonSerialized] public static int paletteIndex; + [NonSerialized] public static string storagePath; + [NonSerialized] public static bool liveEdit; + [NonSerialized] public static bool responsive = true; + [NonSerialized] public static bool setup; + [NonSerialized] public static bool disabled; + [NonSerialized] public static bool debug; + [NonSerialized] public static bool needsRefresh; + [NonSerialized] public static bool needsRebuild; + [NonSerialized] private static float nextUpdate; + //[NonSerialized] public static float verticalSpacing = 2.0f; + static Themes(){ + EditorApplication.projectWindowItemOnGUI += (a,b)=>Themes.Setup(); + EditorApplication.hierarchyWindowItemOnGUI += (a,b)=>Themes.Setup(); + EditorApplication.playmodeStateChanged += ()=>Themes.nextUpdate = 0; + EditorApplication.update += ()=>{ + if(Time.realtimeSinceStartup < Themes.nextUpdate || !Themes.setup || Themes.disabled){return;} + Themes.UpdateColors(); + Utility.RepaintAll(); + Themes.nextUpdate = Time.realtimeSinceStartup + (Themes.responsive ? 0.01f : 0.5f); + }; + } + public static void Setup(){ + if(EditorApplication.isCompiling || EditorApplication.isUpdating){return;} + if(Themes.needsRefresh){ + Themes.UpdateSettings(); + Themes.ApplyContents(); + Utility.RepaintAll(); + Themes.needsRefresh = false; + } + if(Themes.needsRebuild){ + Themes.UpdateSettings(); + Themes.RebuildStyles(true); + Utility.RepaintAll(); + Themes.needsRebuild = false; + Themes.needsRefresh = true; + } + if(!Themes.setup){ + var themes = FileManager.Find("*.unitytheme",Themes.debug); + if(themes.IsNull()){ + Debug.LogWarning("[Themes] No .unityTheme files found. Disabling until refreshed."); + Themes.setup = true; + Themes.disabled = true; + return; + } + Themes.storagePath = themes.GetFolderPath().Trim("/","\\").GetDirectory()+"/"; + Themes.Load(); + Themes.UpdateSettings(); + Themes.UpdateColors(); + Utility.RepaintAll(); + if(!Themes.setup){ + Themes.setup = true; + Themes.needsRebuild = true; + } + } + } + public static void RebuildStyles(bool skipTree=false){ + var terms = new string[]{"Styles","styles","s_GOStyles","s_Current","s_Styles","m_Styles","ms_Styles","constants"}; + foreach(var type in typeof(UnityEditor.Editor).Assembly.GetTypes()){ + if(skipTree && type.Name == "TreeViewGUI"){continue;} + if(type.IsNull()){continue;} + foreach(var term in terms){ + type.ClearVariable(term,ObjectExtension.staticFlags); + } + } + typeof(EditorStyles).SetVariable("s_CachedStyles",null,0); + typeof(EditorStyles).SetVariable("s_CachedStyles",null,1); + typeof(EditorGUIUtility).CallMethod("SkinChanged"); + } + [MenuItem("Zios/Theme/Refresh _`1")] + public static void Refresh(){ + Debug.Log("[Themes] Forced Refresh."); + Themes.setup = false; + Themes.disabled = false; + Utility.RepaintAll(); + } + [MenuItem("Zios/Theme/Development/Toggle Live Edit _`2")] + public static void ToggleEdit(){ + Themes.liveEdit = !Themes.liveEdit; + Debug.Log("[Themes] Live editing : " + Themes.liveEdit); + Themes.Refresh(); + } + [MenuItem("Zios/Theme/Development/Toggle Debug _`3")] + public static void ToggleDebug(){ + Themes.debug = !Themes.debug; + Debug.Log("[Themes] Debug messages : " + Themes.debug); + } + [MenuItem("Zios/Theme/Next Palette &F2")] + public static void NextPalette(){Themes.AdjustPalette(1);} + [MenuItem("Zios/Theme/Previous Palette &F1")] + public static void PreviousPalette(){Themes.AdjustPalette(-1);} + public static void AdjustPalette(int adjust){ + if(Themes.active.allowCustomization && Themes.active.allowColorCustomization){ + Themes.paletteIndex = (Themes.paletteIndex + adjust) % Themes.palettes.Count; + if(Themes.paletteIndex < 0){Themes.paletteIndex = Themes.palettes.Count-1;} + var palette = Themes.palettes[Themes.paletteIndex]; + Themes.active.palette = new ThemePalette().Use(palette); + EditorPrefs.SetString("EditorPalette",palette.name); + Themes.SaveColors(); + Utility.RepaintAll(); + Themes.needsRefresh = true; + } + } + //================================= + // Menu Preferences + //================================= + [PreferenceItem("Themes")] + public static void ShowPreferences(){ + var theme = Themes.active; + var current = Themes.themeIndex; + if(theme.IsNull()){return;} + Themes.themeIndex = Themes.all.Select(x=>x.name).Draw(Themes.themeIndex,"Theme"); + GUILayout.Space(3); + if(theme.allowCustomization){ + bool altered = !theme.palette.Matches(Themes.palettes[Themes.paletteIndex]); + if(theme.allowColorCustomization){ + var paletteNames = Themes.palettes.Select(x=>x.name).ToList(); + if(altered){paletteNames[Themes.paletteIndex] = "[Custom]";} + GUI.enabled = !theme.useSystemColor; + Themes.paletteIndex = paletteNames.Draw(Themes.paletteIndex,"Palette"); + GUI.enabled = true; + GUILayout.Space(3); + if(EditorGUIExtension.lastChanged){ + var selectedPalette = Themes.palettes[Themes.paletteIndex]; + theme.palette = new ThemePalette().Use(selectedPalette); + EditorPrefs.SetString("EditorPalette",selectedPalette.name); + Themes.SaveColors(); + } + } + Themes.responsive = Themes.responsive.Draw("Responsive UI"); + EditorPrefs.SetBool("EditorTheme-ResponsiveUI",Themes.responsive); + GUILayout.Space(2); + foreach(var toggle in theme.variants.Where(x=>x.name.StartsWith("+"))){ + var toggleName = "EditorTheme-"+theme.name+toggle.name.Remove("+"); + EditorPrefs.SetBool(toggleName,EditorPrefs.GetBool(toggleName).Draw(toggle.name.Remove("+"))); + GUILayout.Space(2); + } + if(theme.allowColorCustomization){ + if(theme.allowSystemColor){ + theme.useSystemColor = theme.useSystemColor.Draw("Use System Color"); + EditorPrefs.SetBool("EditorTheme-"+theme.name+"-UseSystemColor",theme.useSystemColor); + if(EditorGUIExtension.lastChanged && !theme.useSystemColor){Themes.LoadColors();} + } + if(!theme.useSystemColor){ + theme.palette.background = theme.palette.background.Draw("Background"); + theme.palette.backgroundDark.value = theme.palette.backgroundDark.value.Draw("Background Dark"); + theme.palette.backgroundLight.value = theme.palette.backgroundLight.value.Draw("Background Light"); + if(altered){ + EditorGUILayout.BeginHorizontal(); + if(GUILayout.Button("Save",GUILayout.Width(100))){Themes.SavePalette();} + if(GUILayout.Button("Reset",GUILayout.Width(100))){Themes.LoadColors(true);} + EditorGUILayout.EndHorizontal(); + } + Themes.SaveColors(); + } + //Themes.verticalSpacing = Themes.verticalSpacing.Draw("Vertical Spacing"); + } + } + if(current != Themes.themeIndex){ + foreach(var content in Themes.active.contents){content.Revert();} + EditorPrefs.SetString("EditorTheme",Themes.all[Themes.themeIndex].name); + Themes.UpdateSettings(); + Themes.RebuildStyles(); + Utility.RebuildInspectors(); + } + if(GUI.changed){ + Themes.needsRefresh = true; + Utility.RepaintAll(); + } + } + public static void SaveColors(){ + var theme = Themes.active; + EditorPrefs.SetString("EditorTheme-"+theme.name+"Color",theme.palette.background.Serialize()); + EditorPrefs.SetString("EditorTheme-"+theme.name+"ColorDark",theme.palette.backgroundDark); + EditorPrefs.SetString("EditorTheme-"+theme.name+"ColorLight",theme.palette.backgroundLight); + } + public static void LoadColors(bool reset=false){ + var theme = Themes.active; + if(!theme.useSystemColor){ + if(reset){ + var original = Themes.palettes[Themes.paletteIndex]; + theme.palette = new ThemePalette().Use(original); + } + else if(theme.allowColorCustomization){ + theme.palette.background = EditorPrefs.GetString("EditorTheme-"+theme.name+"Color",theme.palette.background.Serialize()).Deserialize(); + theme.palette.backgroundDark = EditorPrefs.GetString("EditorTheme-"+theme.name+"ColorDark",theme.palette.backgroundDark); + theme.palette.backgroundLight = EditorPrefs.GetString("EditorTheme-"+theme.name+"ColorLight",theme.palette.backgroundLight); + } + } + } + //================================= + // Updating + //================================= + public static void UpdateColors(){ + var theme = Themes.active; + if(theme.allowSystemColor && theme.useSystemColor){ + theme.palette.backgroundDark.offset = 0.8f; + theme.palette.backgroundLight.offset = 1.3f; + Color color = theme.palette.background; + object key = null; + #if UNITY_EDITOR_WIN + key = Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\DWM\\","AccentColor",null); + key = key ?? Registry.GetValue("HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Accent","AccentColor",null); + if(!key.IsNull()){ + int value = key.As(); + color = value != -1 ? value.ToHex().ToColor(true) : color; + } + else{ + key = Registry.GetValue("HKEY_LOCAL_MACHINE\\Software\\Policies\\Microsoft\\Windows\\Personalization","PersonalColor_Accent",null); + key = key ?? Registry.GetValue("HKEY_CURRENT_USER\\Control Panel\\Colors","WindowFrame",null); + if(!key.IsNull()){ + string value = key.As(); + color = value != "" ? value.ToColor(" ",false,false) : color; + } + } + #endif + if(color != theme.palette.background){ + theme.palette.background = color; + theme.palette.backgroundDark.Update(theme.palette.background); + theme.palette.backgroundLight.Update(theme.palette.background); + Themes.needsRefresh = true; + Utility.RepaintAll(); + } + return; + } + theme.palette.backgroundDark.Update(theme.palette.background); + theme.palette.backgroundLight.Update(theme.palette.background); + } + public static void UpdateSettings(){ + var baseTheme = Themes.all[Themes.themeIndex]; + var theme = Themes.active = new Theme().Use(baseTheme); + theme.palette = new ThemePalette().Use(baseTheme.palette); + theme.useSystemColor = EditorPrefs.GetBool("EditorTheme-"+theme.name+"-UseSystemColor",false); + Themes.responsive = EditorPrefs.GetBool("EditorTheme-ResponsiveUI",true); + Themes.LoadColors(); + Themes.UpdateColors(); + foreach(Theme variant in theme.variants.Where(x=>x.name.StartsWith("+"))){ + var variantName = "EditorTheme-"+theme.name+variant.name.Remove("+"); + if(EditorPrefs.GetBool(variantName)){ + theme.Use(variant); + } + } + if(Themes.active.useColorAssets){ + theme.colorStyles.Clear(); + theme.colorImages.Clear(); + Themes.UpdateAssets("ThemeColor",theme.palette.background); + Themes.UpdateAssets("ThemeColorDark",theme.palette.backgroundDark); + Themes.UpdateAssets("ThemeColorLight",theme.palette.backgroundLight); + } + Themes.Apply(); + } + public static void UpdateAssets(string colorName,Color color){ + var theme = Themes.active; + FileManager.Create(theme.path+"/Background"); + var imagePath = theme.path+"/Background/"+theme.name+colorName+".png"; + var borderPath = theme.path+"/Background/"+theme.name+colorName+"Border.png"; + var image = (Texture2D)AssetDatabase.LoadAssetAtPath(imagePath,typeof(Texture2D)); + var border = (Texture2D)AssetDatabase.LoadAssetAtPath(borderPath,typeof(Texture2D)); + if(image.IsNull()){ + image = new Texture2D(1,1); + image.SaveAs(imagePath); + AssetDatabase.ImportAsset(imagePath); + } + if(border.IsNull()){ + border = new Texture2D(3,3); + border.SaveAs(borderPath); + AssetDatabase.ImportAsset(borderPath); + } + theme.colorStyles.AddNew().GetStates().ForEach(x=>x.background = image); + theme.colorImages.AddNew(image); + image.SetPixel(0,0,color); + image.Apply(); + border.SetPixels(new Color[]{color,color,color,color,Color.clear,color,color,color,color}); + border.Apply(); + image.SaveAs(imagePath); + border.SaveAs(borderPath); + } + public static void Apply(string themeName=""){ + var theme = Themes.active; + if(themeName.IsEmpty()){themeName = theme.name;} + var loadPath = Themes.storagePath+themeName; + var skin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector); + var main = Style.GetSkin(themeName,false); + if(Themes.debug && !main.IsNull() && main.customStyles.Length != skin.customStyles.Length){ + Debug.LogWarning("[Themes] Mismatched style count [" + skin.customStyles.Length + "] for main editor skin [" +main.customStyles.Length+"]. Possible version conflict."); + } + if(!main.IsNull()){ + skin.Use(main,!Themes.liveEdit); + skin.settings.cursorColor = theme.palette.backgroundLight; + skin.settings.selectionColor = theme.palette.backgroundDark; + skin.GetStyle("TabWindowBackground").normal.background = theme.windowBackgroundOverride; + Utility.GetUnityType("AppStatusBar").ClearVariable("background"); + Utility.GetUnityType("SceneRenderModeWindow.Styles").SetVariable("sMenuItem",skin.GetStyle("MenuItem")); + Utility.GetUnityType("SceneRenderModeWindow.Styles").SetVariable("sSeparator",skin.GetStyle("sv_iconselector_sep")); + Utility.GetUnityType("GameView.Styles").SetVariable("gizmoButtonStyle",skin.GetStyle("GV Gizmo DropDown")); + typeof(SceneView).SetVariable("s_DropDownStyle",skin.GetStyle("GV Gizmo DropDown")); + typeof(EditorGUIUtility).SetVariable("kDarkViewBackground",theme.palette.background); + typeof(EditorGUI).SetVariable("kCurveColor",theme.palette.backgroundDark.value); + typeof(EditorGUI).SetVariable("kCurveBGColor",theme.palette.backgroundLight.value); + var console = Utility.GetUnityType("ConsoleWindow.Constants"); + console.SetVariable("ms_Loaded",false); + console.CallMethod("Init"); + var hostView = Utility.GetUnityType("HostView"); + hostView.SetVariable("kViewColor",theme.palette.background); + foreach(var view in Resources.FindObjectsOfTypeAll(hostView)){ + view.ClearVariable("background"); + } + foreach(var window in Locate.GetAssets()){ + window.antiAlias = 1; + window.minSize = new Vector2(100,20); + window.wantsMouseMove = true; + window.autoRepaintOnSceneChange = true; + } + if(themeName != "@Default"){ + skin = Style.GetSkin(themeName+"/UnityEditor.EditorStyles.s_Current",false); + if(!skin.IsNull()){ + var preferences = Utility.GetUnityType("PreferencesWindow"); + var constants = preferences.GetVariable("constants"); + if(constants.IsNull()){ + var instance = Activator.CreateInstance(preferences.GetVariableType("constants")); + preferences.SetVariable("constants",instance); + } + preferences.GetVariable("constants").SetVariable("sectionHeader",skin.GetStyle("m_LargeLabel [LargeLabel]")); + } + } + } + foreach(var skinFile in FileManager.FindAll(loadPath+"/*.guiskin",true,false)){ + if(skinFile.name == themeName){continue;} + var field = skinFile.name.Split(".").Last(); + var parent = skinFile.name.Replace("."+field,""); + var typeDirect = Utility.GetUnityType(skinFile.name); + var typeParent = Utility.GetUnityType(parent); + var styles = skinFile.GetAsset().GetNamedStyles(false,true,true); + var flags = field.Contains("s_Current") ? ObjectExtension.privateFlags : ObjectExtension.staticFlags; + if(Themes.debug && typeDirect.IsNull() && (typeParent.IsNull() || !typeParent.HasVariable(field))){ + Debug.LogWarning("[Themes] No matching class/field found for GUISkin -- " + skinFile.name + ". Possible version conflict."); + continue; + } + Action,object> SetStyles = (current,scope)=>{ + if(Themes.debug && current.Count != 0 && current.Count != styles.Count){ + Debug.LogWarning("[Themes] Mismatched style count [" + current.Count + "] for -- " + skinFile.name + "[" +styles.Count+"]. Possible version conflict."); + } + foreach(var item in current){ + if(styles.ContainsKey(item.Key)){ + var baseName = styles[item.Key].name.Parse("[","]"); + var replacement = styles[item.Key]; + var newStyle = Themes.liveEdit ? replacement : new GUIStyle(replacement).Rename(baseName); + scope.SetVariable(item.Key,newStyle); + } + } + }; + if(!typeDirect.IsNull()){ + SetStyles(typeDirect.GetVariables(null,flags),typeDirect); + } + if(!typeParent.IsNull()){ + var target = typeParent.GetVariable(field); + if(target.IsNull()){ + try{target = Activator.CreateInstance(typeParent.GetVariableType(field));} + catch{continue;} + } + SetStyles(target.GetVariables(),target); + } + } + } + public static void ApplyContents(){ + Utility.DelayCall(()=>{foreach(var content in Themes.active.contents){content.Apply();}},0.5f); + } + } + public class ThemesAbout :EditorWindow{ + [MenuItem("Zios/Theme/About",false,1)] + public static void Init(){ + var window = ScriptableObject.CreateInstance(); + window.position = new Rect(100,100,1,1); + window.minSize = window.maxSize = new Vector2(190,100); + window.ShowAuxWindow(); + } + public void OnGUI(){ + var box = EditorStyles.label; + #if UNITY_5 + this.titleContent = "About Themes".ToContent(); + box = EditorStyles.helpBox; + #endif + string buildText = "Build "+Themes.revision+""; + EditorGUILayout.BeginVertical(box.Background("")); + buildText.ToLabel().DrawLabel(EditorStyles.miniLabel.RichText(true).Clipping("Overflow").FontSize(15).Alignment("UpperCenter")); + "Part of the Zios framework. Developed by Brad Smithee.".ToLabel().DrawLabel(EditorStyles.wordWrappedLabel.FontSize(12).RichText(true)); + if("Source Repository".ToLabel().DrawButton(GUI.skin.button.FixedWidth(150).Margin(12,0,5,0))){ + Application.OpenURL("https://github.com/zios/unity-themes"); + } + EditorGUILayout.EndVertical(); + } + } + public class RelativeColor{ + public Color value; + public float offset; + public static implicit operator RelativeColor(string value){ + return value.IsNumber() ? new RelativeColor(value.ToFloat()) : new RelativeColor(value.ToColor()); + } + public static implicit operator RelativeColor(float value){return new RelativeColor(value);} + public static implicit operator RelativeColor(Color value){return new RelativeColor(value);} + public static implicit operator Color(RelativeColor current){return current.value;} + public static implicit operator string(RelativeColor current){return current.Serialize();} + public RelativeColor(float offset){this.offset = offset;} + public RelativeColor(Color manual){this.value = manual;} + public string Serialize(){return this.offset != 0 ? this.offset.Serialize() : this.value.Serialize();} + public void Update(Color initial){this.value = this.offset != 0 ? initial.Multiply(this.offset) : this.value;} + } + public class ColorImportSettings : AssetPostprocessor{ + public static void OnPostprocessAllAssets(string[] imported,string[] deleted,string[] movedTo, string[] movedFrom){ + Themes.setup = false; + Utility.RepaintAll(); + } + public void OnPreprocessTexture(){ + TextureImporter importer = (TextureImporter)this.assetImporter; + if(importer.assetPath.Contains("ThemeColor")){ + importer.isReadable = true; + if(importer.assetPath.Contains("Border")){ + importer.filterMode = FilterMode.Point; + } + } + } + } + #endif +} \ No newline at end of file diff --git a/Systems/Interface/Themes/ThemesLoading.cs b/Systems/Interface/Themes/ThemesLoading.cs new file mode 100644 index 000000000..f907800c0 --- /dev/null +++ b/Systems/Interface/Themes/ThemesLoading.cs @@ -0,0 +1,208 @@ +using System.Collections.Generic; +using System; +using System.Linq; +using UnityEngine; +namespace Zios{ + #if UNITY_EDITOR + using UnityEditor; + public static partial class Themes{ + public static void Load(){ + Themes.all.Clear(); + Themes.palettes.Clear(); + Themes.ParsePalettes(); + Themes.ParseThemes(); + var activeThemeName = EditorPrefs.GetString("EditorTheme","@Default"); + var activePaletteName = EditorPrefs.GetString("EditorPalette","Slate"); + Themes.themeIndex = Themes.all.FindIndex(x=>x.name==activeThemeName).Max(0); + Themes.paletteIndex = Themes.palettes.FindIndex(x=>x.name==activePaletteName).Max(0); + } + public static void ParsePalettes(){ + foreach(var file in FileManager.FindAll("*.unitypalette")){ + var palette = Themes.palettes.AddNew(); + palette.name = file.name; + foreach(var line in file.GetText().GetLines()){ + if(line.Trim().IsEmpty()){continue;} + var term = line.Parse(""," ").Trim(); + var value = line.Parse(" ").Trim().Trim("=").Trim(); + if(term.Matches("Color",true)){palette.background = value.ToColor();} + else if(term.Matches("DarkColor",true)){palette.backgroundDark = value;} + else if(term.Matches("LightColor",true)){palette.backgroundLight = value;} + } + } + } + public static void ParseThemes(){ + var themes = FileManager.FindAll("*.unitytheme"); + foreach(var themeFile in themes){ + Theme theme = null; + Theme root = null; + foreach(var line in themeFile.GetText().GetLines()){ + if(line.Trim().IsEmpty()){continue;} + if(line.Contains("[")){ + string name = theme.IsNull() ? themeFile.name : line.Parse("[","]"); + theme = root.IsNull() ? Themes.all.AddNew() : root.variants.AddNew(); + theme.name = name.ToPascalCase(); + theme.path = themeFile.GetAssetPath().GetDirectory(); + if(root.IsNull()){ + root = theme; + Themes.ParseGUIContent(theme,themeFile.directory); + } + if(root.variants.Count > 0){ + root.variants.Last().Use(root); + } + continue; + } + if(theme.IsNull()){continue;} + var term = line.Parse(""," ").Trim(); + var value = line.Parse(" ").Trim().Trim("=").Trim(); + if(value.Matches("None",true)){continue;} + else if(term.Matches("AllowSystemColor",true)){theme.allowSystemColor = value.ToBool();} + else if(term.Matches("AllowCustomization",true)){theme.allowCustomization = value.ToBool();} + else if(term.Matches("AllowColorCustomization",true)){theme.allowColorCustomization = value.ToBool();} + else if(term.Matches("UseSystemColor",true)){theme.useSystemColor = value.ToBool();} + else if(term.Matches("UseColorAssets",true)){theme.useColorAssets = value.ToBool();} + else if(term.Matches("FontOverride",true)){theme.fontOverride = FileManager.GetAsset(value);} + else if(term.Matches("WindowBackgroundOverride",true)){theme.windowBackgroundOverride = FileManager.GetAsset(value);} + else if(term.Matches("FontScale",true)){theme.fontScale = value.ToFloat();} + else if(term.Matches("Palette",true)){theme.palette = palettes.Find(x=>x.name==value) ?? new ThemePalette();} + } + } + } + public static void ParseGUIContent(Theme theme,string path){ + if(theme.name == "@Default"){return;} + var contents = FileManager.FindAll(path+"/*.guiContent",true,false); + foreach(var contentFile in contents){ + var content = new ThemeContent(); + var contentName = ""; + foreach(var line in contentFile.GetText().GetLines()){ + if(line.Trim().IsEmpty()){continue;} + if(line.ContainsAll("[","]")){ + if(!contentName.IsEmpty()){ + content.Setup(contentFile.name,contentName); + theme.contents.Add(content); + } + content = new ThemeContent(); + contentName = line.Parse("[","]"); + } + else{ + var term = line.Parse("","=").Trim(); + var value = line.Parse("=").Trim(); + if(term == "image"){content.value.image = FileManager.GetAsset(path+"/GUIContent/"+value+".png");} + else if(term == "text"){content.value.text = value;} + else if(term == "tooltip"){content.value.tooltip = value;} + } + } + if(!contentName.IsEmpty()){ + content.Setup(contentFile.name,contentName); + theme.contents.Add(content); + } + } + } + } + public class Theme{ + [Internal] public string name; + [Internal] public string path; + [Internal] public bool useSystemColor; + [Internal] public List contents = new List(); + [Internal] public List colorImages = new List(); + [Internal] public List colorStyles = new List(); + [Internal] public ThemePalette palette = new ThemePalette(); + public List variants = new List(); + public bool allowCustomization; + public bool allowColorCustomization; + public bool allowSystemColor; + public bool useColorAssets = true; + public Texture2D windowBackgroundOverride; + public Font fontOverride; + public float fontScale = 1; + public float spacingScale = 1; + public Theme Use(Theme other){ + this.UseVariables(other,typeof(InternalAttribute).AsList()); + if(this.name.IsEmpty()){this.name = other.name;} + if(this.path.IsEmpty()){this.path = other.path;} + this.variants = other.variants; + this.contents = other.contents; + return this; + } + } + public class ThemeContent{ + public static Dictionary original = new Dictionary(); + public string name; + public string path; + public object scope; + public GUIContent target = new GUIContent(); + public GUIContent value = new GUIContent(); + public void Setup(string path,string contentName){ + this.path = path; + this.name = contentName; + } + public void SyncScope(){ + string field = this.path.Split(".").Last(); + string parent = this.path.Replace("."+field,""); + var typeDirect = Utility.GetUnityType(this.path); + var typeParent = Utility.GetUnityType(parent); + if(typeDirect.IsNull() && (typeParent.IsNull() || !typeParent.HasVariable(field))){ + if(Themes.debug){Debug.LogWarning("[Themes] No matching class/field found for GUIContent -- " + path);} + return; + } + this.scope = typeDirect ?? typeParent.GetVariable(field); + if(this.scope.IsNull()){ + try{ + this.scope = Activator.CreateInstance(typeParent.GetVariableType(field)); + typeParent.SetVariable(field,this.scope); + } + catch{} + } + } + public void SyncTarget(){ + if(this.scope.IsNull()){return;} + bool isArray = this.scope.GetType() == typeof(GUIContent[]); + if(isArray || this.scope.HasVariable(this.name)){ + this.target = this.scope.GetVariable(this.name); + if(this.target.IsNull()){ + this.target = new GUIContent(); + this.scope.SetVariable(this.name,this.target); + } + var path = this.path+"."+this.name; + if(!ThemeContent.original.ContainsKey(path)){ + ThemeContent.original[path] = new GUIContent(this.target); + } + } + } + public void Apply(){ + this.SyncScope(); + this.SyncTarget(); + this.target.text = this.value.text; + this.target.tooltip = this.value.tooltip; + this.target.image = this.value.image; + } + public void Revert(){ + this.SyncScope(); + this.SyncTarget(); + var path = this.path+"."+this.name; + if(!ThemeContent.original.ContainsKey(path)){return;} + var original = ThemeContent.original[path]; + this.target.text = original.text; + this.target.tooltip = original.tooltip; + this.target.image = original.image; + } + } + public class ThemePalette{ + public string name; + public Color background = new Color(0.7f,0.7f,0.7f); + public RelativeColor backgroundDark = 0.8f; + public RelativeColor backgroundLight = 1.3f; + public ThemePalette Use(ThemePalette other){ + this.background = other.background; + this.backgroundDark = other.backgroundDark.Serialize(); + this.backgroundLight = other.backgroundLight.Serialize(); + return this; + } + public bool Matches(ThemePalette other){ + var match = this.background == other.background; + var matchDark = this.backgroundDark.value == other.backgroundDark.value; + var matchLight = this.backgroundLight.value == other.backgroundLight.value; + return match && matchDark && matchLight; + } + } + #endif +} \ No newline at end of file diff --git a/Systems/Interface/Themes/ThemesSaving.cs b/Systems/Interface/Themes/ThemesSaving.cs new file mode 100644 index 000000000..a523f67e5 --- /dev/null +++ b/Systems/Interface/Themes/ThemesSaving.cs @@ -0,0 +1,159 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using UnityEngine; +namespace Zios{ + using Events; + using Containers; + #if UNITY_EDITOR + using UnityEditor; + public static partial class Themes{ + [NonSerialized] public static string createPath; + [NonSerialized] public static bool includeBuiltin; + [NonSerialized] public static Dictionary styleGroupBuffer = new Dictionary(); + [NonSerialized] public static Hierarchy contentBuffer = new Hierarchy(); + [MenuItem("Zios/Theme/Development/Save All [GUISkin + GUIContent]")] + public static void SaveGUIAll(){Themes.SaveGUI("",true);} + [MenuItem("Zios/Theme/Development/Save All [Assets]")] + public static void SaveAssetsAll(){Themes.SaveAssets("",true);} + [MenuItem("Zios/Theme/Development/Save [GUISkin + GUIContent]")] + public static void SaveGUI(){Themes.SaveGUI("");} + public static void SaveGUI(string path,bool includeBuiltin=false){ + Themes.includeBuiltin = includeBuiltin; + Themes.createPath = path.IsEmpty() ? EditorUtility.SaveFolderPanel("Save Theme [GUISkin/GUIContent]",Themes.storagePath,"@Default") : path; + var allTypes = typeof(Editor).Assembly.GetTypes().Where(x=>!x.IsNull()).ToArray(); + var stepper = new EventStepper(Themes.SaveGUIStep,Themes.SaveGUIComplete,allTypes,50); + EditorApplication.update += stepper.Step; + } + public static void SaveGUIStep(object collection,int itemIndex){ + var types = (Type[])collection; + var type = types[itemIndex]; + if(!type.Name.ContainsAny("$","__Anon","<","AudioMixerDraw")){ + EventStepper.title = "Scanning " + types.Length + " Types"; + EventStepper.message = "Analyzing : " + type.Name; + var terms = new string[]{"Styles","styles","s_GOStyles","s_Current","s_Styles","m_Styles","ms_Styles","constants"}; + foreach(var term in terms){ + if(!type.HasVariable(term,ObjectExtension.staticFlags)){continue;} + try{ + var styleGroup = type.GetVariable(term,-1,ObjectExtension.staticFlags) ?? Activator.CreateInstance(type.GetVariableType(term)); + Themes.styleGroupBuffer[type.FullName+"."+term] = styleGroup; + } + catch{} + } + try{ + var styles = type.GetVariables(null,ObjectExtension.staticFlags); + var content = type.GetVariables(null,ObjectExtension.staticFlags); + var contentGroups = type.GetVariables(null,ObjectExtension.staticFlags); + if(styles.Count > 0){Themes.styleGroupBuffer[type.FullName] = styles;} + if(content.Count > 0){Themes.contentBuffer[type.FullName] = content;} + foreach(var contentSet in contentGroups){ + if(contentSet.Value.IsNull() || contentSet.Value.Length < 1){continue;} + var contents = Themes.contentBuffer[type.FullName+"."+contentSet.Key] = new Dictionary(); + for(int index=0;index()); + } + foreach(var buffer in Themes.contentBuffer){ + var contentPath = savePath+"/"+buffer.Key+".guicontent"; + Themes.SaveGUIContent(contentPath,buffer.Value); + } + var skin = EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector); + skin = ScriptableObject.CreateInstance().Use(skin); + AssetDatabase.CreateAsset(skin,savePath+"/"+themeName+".guiskin"); + AssetDatabase.StopAssetEditing(); + Themes.styleGroupBuffer.Clear(); + Themes.contentBuffer.Clear(); + } + public static void SaveGUISkin(string path,KeyValuePair buffer){ + var customStyles = new List(); + var styles = buffer.Value is Dictionary ? (Dictionary)buffer.Value : buffer.Value.GetVariables().Distinct(); + foreach(var styleData in styles){ + var style = new GUIStyle(styleData.Value); + if(!buffer.Key.Contains("s_Current")){style.Rename(styleData.Key);} + customStyles.Add(style); + } + if(customStyles.Count > 0){ + GUISkin newSkin = ScriptableObject.CreateInstance(); + newSkin.name = buffer.Key; + newSkin.customStyles = customStyles.ToArray(); + AssetDatabase.CreateAsset(newSkin,path); + } + } + public static void SaveGUIContent(string path,Dictionary data){ + if(data.Count < 1){return;} + var contents = ""; + var keys = data.Keys.ToList(); + keys.Sort(); + foreach(var key in keys){ + if(key.ContainsAny("<",">")){continue;} + GUIContent value = data[key]; + contents = contents.AddLine("["+key+"]"); + if(value.IsNull()){continue;} + if(!value.text.IsEmpty()){contents = contents.AddLine("text = "+value.text);} + if(!value.image.IsNull()){ + var image = value.image; + var imagePath = path.GetDirectory()+"/GUIContent/"+image.name+".png"; + contents = contents.AddLine("image = "+image.name); + if(Themes.includeBuiltin || !FileManager.GetPath(image).Contains("unity editor resources")){ + if(!FileManager.Exists(imagePath)){ + image.SaveAs(imagePath,true); + } + } + } + if(!value.tooltip.IsEmpty()){contents = contents.AddLine("tooltip = "+value.tooltip);} + contents = contents.AddLine(""); + } + FileManager.Create(path).WriteText(contents.Trim()); + } + [MenuItem("Zios/Theme/Development/Save [Palette]")] + public static void SavePalette(){Themes.SavePalette("");} + public static void SavePalette(string path){ + path = path.IsEmpty() ? EditorUtility.SaveFilePanel("Save Theme [Palette]",Themes.storagePath+"@Palettes","TheColorsDuke","unitypalette") : path; + if(path.Length > 0){ + Themes.LoadColors(); + var palette = Themes.active.palette; + var file = FileManager.Create(path); + var contents = ""; + contents = contents.AddLine("Color "+palette.background.ToHex(false)); + contents = contents.AddLine("DarkColor "+palette.backgroundDark.value.Serialize()); + contents = contents.AddLine("LightColor "+palette.backgroundLight.value.Serialize()); + file.WriteText(contents); + EditorPrefs.SetString("EditorPalette",path.GetFileName()); + FileManager.Refresh(); + Themes.setup = false; + } + } + [MenuItem("Zios/Theme/Development/Save [Assets]")] + public static void SaveAssets(){Themes.SaveAssets("");} + public static void SaveAssets(string path,bool includeBuiltin=false){ + path = path.IsEmpty() ? EditorUtility.SaveFolderPanel("Save Theme [Assets]",Themes.storagePath,"").GetAssetPath() : path; + var files = FileManager.FindAll(path+"/*.guiSkin"); + FileManager.Create(path+"/Background"); + FileManager.Create(path+"/Font"); + AssetDatabase.StartAssetEditing(); + foreach(var file in files){ + var guiSkin = file.GetAsset(); + guiSkin.SaveFonts(path+"/Font",includeBuiltin); + guiSkin.SaveBackgrounds(path+"/Background",includeBuiltin); + } + AssetDatabase.StopAssetEditing(); + } + } + #endif +} \ No newline at end of file diff --git a/Systems/Interface/Themes/ThemesUtility.cs b/Systems/Interface/Themes/ThemesUtility.cs new file mode 100644 index 000000000..f12891f61 --- /dev/null +++ b/Systems/Interface/Themes/ThemesUtility.cs @@ -0,0 +1,87 @@ +using System; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +namespace Zios{ + #if UNITY_EDITOR + using UnityEditor; + public static partial class Themes{ + [MenuItem("Zios/Theme/Development/Sync Names [GUISkin]")] + public static void SyncSkinNames(){Themes.SyncSkinNames("");} + public static void SyncSkinNames(string path=""){ + path = path.IsEmpty() ? EditorUtility.SaveFolderPanel("Sync Names [GUISkin]",Themes.storagePath,"").GetAssetPath() : path; + var files = FileManager.FindAll(path+"/*.guiSkin"); + foreach(var file in files){ + var stylesSkin = file.GetAsset().customStyles; + var stylesReflected = file.name.Contains(".") ? Themes.ReflectStyles(file.name) : null; + var stylesInternal = file.name.Contains(".") ? stylesReflected.Values.ToArray() : EditorGUIUtility.GetBuiltinSkin(EditorSkin.Inspector).customStyles; + if(stylesSkin.Length == stylesInternal.Length){ + for(int index=0;index()); + continue; + } + Debug.LogWarning("[Themes] Mismatched number of styles -- " + file.name + ". Found " + stylesSkin.Length + ", but expected " + stylesInternal.Length + ". Possible version conflict."); + } + } + public static Dictionary ReflectStyles(string path,bool showWarnings=true){ + var empty = new Dictionary(); + var fieldName = path.Split(".").Last(); + var fieldFlags = fieldName.Contains("s_Current") ? ObjectExtension.privateFlags : ObjectExtension.staticFlags; + var typeStatic = Utility.GetUnityType(path); + var typeInstance = Utility.GetUnityType(path.Replace("."+fieldName,"")); + if(!typeStatic.IsNull()){ + return typeStatic.GetVariables(null,fieldFlags); + } + if(!typeInstance.IsNull()){ + var target = typeInstance.GetVariable(fieldName); + if(target.IsNull()){ + try{target = Activator.CreateInstance(typeInstance.GetVariableType(fieldName));} + catch{return empty;} + } + return target.GetVariables(); + } + if(showWarnings){Debug.LogWarning("[Themes] No matching class/field found for GUISkin -- " + path + ". Possible version conflict.");} + return empty; + } + [MenuItem("Zios/Theme/Development/Localize [Assets]")] + public static void LocalizeAssets(){Themes.LocalizeAssets("");} + public static void LocalizeAssets(string path="",bool includeBuiltin=false){ + path = path.IsEmpty() ? EditorUtility.SaveFolderPanel("Localize Theme [Assets]",Themes.storagePath,"").GetAssetPath() : path; + var files = FileManager.FindAll(path+"/*.guiSkin"); + foreach(var file in files){ + string assetPath = ""; + var skin = file.GetAsset(); + foreach(var style in skin.GetStyles()){ + if(!style.font.IsNull()){ + assetPath = path+"/Font/"+style.font.name; + if(!includeBuiltin && FileManager.GetPath(style.font).Contains("unity editor resources")){continue;} + var font = FileManager.GetAsset(assetPath+".ttf",false); + font = font ?? FileManager.GetAsset(assetPath+".otf",false); + style.font = font ?? style.font; + } + foreach(var state in style.GetStates()){ + if(state.background.IsNull()){continue;} + if(!includeBuiltin && FileManager.GetPath(state.background).Contains("unity editor resources")){continue;} + assetPath = path+"/Background/"+state.background.name+".png"; + state.background = FileManager.GetAsset(assetPath) ?? state.background; + } + } + Utility.SetDirty(skin,false,true); + } + AssetDatabase.SaveAssets(); + } + } + #endif +} \ No newline at end of file