hellbound/Assets/Sources/I2/Localization/Scripts/Editor/EditorTools.cs

754 lines
24 KiB
C#
Raw Normal View History

2021-11-26 11:16:25 +03:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
namespace I2.Loc
{
public class GUITools
{
static public Color White = Color.white;
static public Color LightGray = Color.Lerp(Color.gray, Color.white, 0.5f);
static public Color DarkGray = Color.Lerp(Color.gray, Color.white, 0.2f);
static public Color LightYellow = Color.Lerp(Color.yellow, Color.white, 0.5f);
static public GUILayoutOption DontExpandWidth = GUILayout.ExpandWidth(false);
static public GUIContent EmptyContent = new GUIContent ();
static List<Action> mDelayedEditorCallbacks = new List<Action>();
#region Delayed Editor Callback
public static void DelayedCall( Action action )
{
if (mDelayedEditorCallbacks.Count == 0)
EditorApplication.update += OnDelayedCall;
mDelayedEditorCallbacks.Add(action);
}
static void OnDelayedCall()
{
EditorApplication.update -= OnDelayedCall;
var calls = mDelayedEditorCallbacks.ToArray();
mDelayedEditorCallbacks.Clear();
foreach (var call in calls)
call();
}
#endregion
#region Header
public delegate void fnOnToggled(bool enabled);
static public bool DrawHeader (string text, string key, bool ShowToggle=false, bool ToggleState=false, fnOnToggled OnToggle = null, string HelpURL=default(string), Color disabledColor = default(Color))
{
bool state = EditorPrefs.GetBool(key, false);
bool newState = DrawHeader (text, state, ShowToggle, ToggleState, OnToggle, HelpURL, disabledColor);
if (state!=newState) EditorPrefs.SetBool(key, newState);
return newState;
}
static public bool DrawHeader (string text, bool state, bool ShowToggle=false, bool ToggleState=false, fnOnToggled OnToggle = null, string HelpURL=default(string), Color disabledColor = default(Color), bool allowCollapsing = true)
{
GUIStyle Style = new GUIStyle(EditorStyles.foldout);
Style.richText = true;
EditorStyles.foldout.richText = true;
if (state)
{
//GUI.backgroundColor=DarkGray;
GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea/*, GUILayout.Height(1)*/);
GUILayout.BeginHorizontal();
if (!string.IsNullOrEmpty(text))
{
if (allowCollapsing)
state = GUILayout.Toggle(state, text, Style, GUILayout.ExpandWidth(true));
else
GUILayout.Label(text, GUILayout.ExpandWidth(true));
}
if (!string.IsNullOrEmpty(HelpURL))
{
if (GUILayout.Button (Icon_Help, EditorStyles.label, GUILayout.ExpandWidth(false)))
Application.OpenURL(HelpURL);
}
if (ShowToggle)
{
GUI.changed = false;
bool newBool = GUILayout.Toggle(ToggleState, "", "OL Toggle", GUILayout.ExpandWidth(false));
if (GUI.changed && OnToggle!=null)
OnToggle(newBool);
}
GUILayout.EndHorizontal();
GUILayout.Space(2);
//GUI.backgroundColor = Color.white;
}
else
{
if (ShowToggle && !ToggleState)
GUI.color = disabledColor;
GUILayout.BeginHorizontal("Box");
//GUILayout.BeginHorizontal(EditorStyles.toolbar);
state = GUILayout.Toggle(state, text, Style, GUILayout.ExpandWidth(true));
if (ShowToggle)
{
GUI.changed = false;
bool newBool = GUILayout.Toggle(ToggleState, "", "OL Toggle", GUILayout.ExpandWidth(false));
if (GUI.changed && OnToggle!=null)
OnToggle(newBool);
}
GUILayout.EndHorizontal();
GUI.color = White;
}
return state;
}
static public void CloseHeader()
{
GUILayout.EndHorizontal();
}
public static void OnGUI_Footer(string pluginName, string pluginVersion, string helpURL, string documentationURL, string assetStoreURL)
{
GUILayout.BeginHorizontal();
string versionTip = "";
/*if (I2Analytics.HasNewVersion(pluginName))
{
versionTip = "There is a new version of " + pluginName + ".\nClick here for more details";
if (GUILayout.Button(new GUIContent("", versionTip), EditorStyles.label, GUILayout.Width(25)))
I2AboutWindow.DoShowScreen();
var rect = GUILayoutUtility.GetLastRect();
rect.yMin = rect.yMax - 25;
rect.xMax = rect.xMin + 25;
rect.y += 3;
GUITools.DrawSkinIcon(rect, "CN EntryWarnIcon", "CN EntryWarn");
}*/
if (GUILayout.Button(new GUIContent("v" + pluginVersion, versionTip), EditorStyles.miniLabel))
{
Application.OpenURL(assetStoreURL);
//I2AboutWindow.DoShowScreen();
}
GUILayout.FlexibleSpace();
if (GUILayout.Button("Ask a Question", EditorStyles.miniLabel))
Application.OpenURL(helpURL);
GUILayout.Space(10);
if (GUILayout.Button("Documentation", EditorStyles.miniLabel))
Application.OpenURL(documentationURL);
GUILayout.EndHorizontal();
}
#endregion
#region Content
static public void BeginContents ()
{
EditorGUILayout.BeginHorizontal(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.MinHeight(10f));
GUILayout.Space(2f);
EditorGUILayout.BeginVertical();
GUILayout.Space(2f);
}
static public void EndContents () { EndContents(true); }
static public void EndContents ( bool closeHeader )
{
GUILayout.Space(2f);
EditorGUILayout.EndVertical();
GUILayout.Space(3f);
GUILayout.EndHorizontal();
if (closeHeader) CloseHeader();
}
#endregion
#region Tabs
static public void DrawTabs( SerializedProperty mProperty, GUIStyle Style=null, int height=25 )
{
int curIndex = mProperty.enumValueIndex;
int newIndex = DrawTabs( curIndex, mProperty.enumNames, Style, height);
if (curIndex!=newIndex)
mProperty.enumValueIndex = newIndex;
}
static public int DrawTabs( int Index, string[] Tabs, GUIStyle Style=null, int height=25, bool expand = true)
{
GUIStyle MyStyle = new GUIStyle(Style!=null?Style:GUI.skin.FindStyle("dragtab"));
MyStyle.fixedHeight=0;
GUILayout.BeginHorizontal();
for (int i=0; i<Tabs.Length; ++i)
{
int idx = Tabs[i].IndexOf('|');
if (idx>0)
{
string text = Tabs[i].Substring(0, idx);
string tooltip = Tabs[i].Substring(idx+1);
if ( GUILayout.Toggle(Index==i, new GUIContent(text, tooltip), MyStyle, GUILayout.Height(height), GUILayout.ExpandWidth(expand)) && Index!=i)
{
Index=i;
GUI.FocusControl(string.Empty);
}
}
else
{
if ( GUILayout.Toggle(Index==i, Tabs[i], MyStyle, GUILayout.Height(height), GUILayout.ExpandWidth(expand)) && Index!=i)
{
Index=i;
GUI.FocusControl(string.Empty);
}
}
}
GUILayout.EndHorizontal();
return Index;
}
static public void DrawShadowedTabs( SerializedProperty mProperty, GUIStyle Style=null, int height=25, bool expand=true )
{
int curIndex = mProperty.enumValueIndex;
int newIndex = DrawShadowedTabs( curIndex, mProperty.enumNames, height, expand);
if (curIndex!=newIndex)
mProperty.enumValueIndex = newIndex;
}
static public int DrawShadowedTabs( int Index, string[] Tabs, int height = 25, bool expand=true )
{
GUI.backgroundColor=Color.Lerp (Color.gray, Color.white, 0.2f);
GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height(1));
GUI.backgroundColor=Color.white;
GUILayout.Space(2);
Index = DrawTabs( Index, Tabs, height: height, expand:expand );
GUILayout.EndVertical();
return Index;
}
static public int DrawTabs( int Index, Texture2D[] Tabs, GUIStyle Style, int height )
{
GUIStyle MyStyle = new GUIStyle(Style!=null?Style:GUI.skin.FindStyle("dragtab"));
MyStyle.fixedHeight=0;
//width = Mathf.Max (width, height * Tabs[0].width/(float)Tabs[0].height);
GUILayout.BeginHorizontal();
float width = (EditorGUIUtility.currentViewWidth-(MyStyle.border.left+MyStyle.border.right)*(Tabs.Length-1)) / Tabs.Length;
for (int i=0; i<Tabs.Length; ++i)
{
if ( GUILayout.Toggle(Index==i, Tabs[i], MyStyle, GUILayout.Height(height), GUILayout.Width(width)) && Index!=i)
{
Index=i;
GUI.changed = true;
}
}
GUILayout.EndHorizontal();
return Index;
}
#endregion
#region Object Array
static public bool DrawObjectsArray( SerializedProperty PropArray, bool allowDuplicates=false, bool allowResources=false, bool allowSceneObj=false, Object testAdd=null, Object testReplace=null, int testReplaceIndex=-1, int testDeleteIndex=-1 )
{
bool hasChanged = false;
GUILayout.BeginVertical();
int DeleteElement = -1, MoveUpElement = -1;
for (int i=0, imax=PropArray.arraySize; i<imax; ++i)
{
SerializedProperty Prop = PropArray.GetArrayElementAtIndex(i);
GUILayout.BeginHorizontal();
//--[ Delete Button ]-------------------
if (GUILayout.Button("X", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false)) || i == testDeleteIndex)
DeleteElement = i;
GUILayout.Space(2);
//--[ Object ]--------------------------
GUILayout.BeginHorizontal(EditorStyles.toolbar);
GUI.changed = false;
Object Obj = EditorGUILayout.ObjectField( Prop.objectReferenceValue, typeof(Object), allowSceneObj, GUILayout.ExpandWidth(true));
if (testReplaceIndex == i)
{
Obj = testReplace;
GUI.changed = true;
}
if (!allowResources && Obj != null)
{
var path = AssetDatabase.GetAssetPath(Obj);
if (path != null && path.Contains("/Resources/"))
Obj = null;
}
if (Obj==null)
DeleteElement = i;
else
if (GUI.changed && (allowDuplicates || !ObjectsArrayHasReference(PropArray, Obj)))
{
Prop.objectReferenceValue = Obj;
hasChanged = true;
}
GUILayout.EndHorizontal();
//--[ MoveUp Button ]-------------------
if (i==0)
{
if (imax>1)
GUILayout.Space (18);
}
else
{
if (GUILayout.Button( "\u25B2", EditorStyles.toolbarButton, GUILayout.Width(18)))
MoveUpElement = i;
}
GUILayout.EndHorizontal();
}
GUILayout.BeginHorizontal(EditorStyles.toolbar);
Object NewObj = EditorGUILayout.ObjectField( null, typeof(Object), allowSceneObj, GUILayout.ExpandWidth(true));
if (testAdd != null)
{
NewObj = testAdd;
}
if (!allowResources && NewObj != null)
{
var path = AssetDatabase.GetAssetPath(NewObj);
if (path != null && path.Contains("/Resources/"))
NewObj = null;
}
if (NewObj && (allowDuplicates || !ObjectsArrayHasReference(PropArray, NewObj)))
{
int Index = PropArray.arraySize;
PropArray.InsertArrayElementAtIndex( Index );
PropArray.GetArrayElementAtIndex(Index).objectReferenceValue = NewObj;
hasChanged = true;
}
GUILayout.EndHorizontal();
if (DeleteElement>=0)
{
PropArray.DeleteArrayElementAtIndex( DeleteElement );
//PropArray.DeleteArrayElementAtIndex( DeleteElement );
hasChanged = true;
}
if (MoveUpElement>=0)
{
PropArray.MoveArrayElement(MoveUpElement, MoveUpElement-1);
hasChanged = true;
}
GUILayout.EndVertical();
return hasChanged;
}
static public bool ObjectsArrayHasReference(SerializedProperty PropArray, Object obj)
{
for (int i = 0, imax = PropArray.arraySize; i < imax; ++i)
{
SerializedProperty Prop = PropArray.GetArrayElementAtIndex(i);
if (Prop.objectReferenceValue == obj)
return true;
}
return false;
}
#endregion
#region Toggle
static public int ToggleToolbar( int Index, string[] Options )
{
GUILayout.BeginHorizontal();
for (int i=0; i<Options.Length; ++i)
{
if ( GUILayout.Toggle(Index==i, Options[i], EditorStyles.toolbarButton))
Index=i;
}
GUILayout.EndHorizontal();
return Index;
}
static public void ToggleToolbar( SerializedProperty EnumVar )
{
int index = ToggleToolbar( EnumVar.enumValueIndex, EnumVar.enumNames);
if (EnumVar.enumValueIndex != index)
EnumVar.enumValueIndex = index;
}
#endregion
#region Misc
public static bool ObjectExistInScene( GameObject Obj )
{
return Obj.scene.IsValid() && Obj.scene.isLoaded;
/* //if (Obj.transform.root != Obj.transform)
// continue;
// We are only interested in GameObjects that are visible in the Hierachy panel and are persitent
if ((Obj.hideFlags & (HideFlags.DontSave|HideFlags.HideInHierarchy)) > 0)
return false;
// We are not interested in Prefab, unless they are Prefab Instances
PrefabType pfType = PrefabUtility.GetPrefabType(Obj);
if(pfType == PrefabType.Prefab || pfType == PrefabType.ModelPrefab)
return false;
// If the database contains the object then its not an scene object,
// but the previous test should get rid of them, so I will just comment this
// unless an false positive object is found in the future
//if (AssetDatabase.Contains(Obj))
// return false;
return true;*/
}
public static IEnumerable<GameObject> SceneRoots()
{
var prop = new HierarchyProperty(HierarchyType.GameObjects);
var expanded = new int[0];
while (prop.Next(expanded)) {
yield return prop.pptrValue as GameObject;
}
}
public static List<GameObject> SceneRootsList()
{
return new List<GameObject>(SceneRoots());
}
public static IEnumerable<Transform> AllSceneObjects()
{
var queue = new Queue<Transform>();
foreach (var root in SceneRoots()) {
var tf = root.transform;
yield return tf;
queue.Enqueue(tf);
}
while (queue.Count > 0) {
foreach (Transform child in queue.Dequeue()) {
yield return child;
queue.Enqueue(child);
}
}
}
public static string GetScenePath(Transform tr)
{
if (tr==null)
return string.Empty;
string path = tr.name;
while (tr.parent != null)
{
tr = tr.parent;
path = tr.name + "/" + path;
}
return path;
}
public static Transform FindObjectInEditor( string scenePath )
{
if (string.IsNullOrEmpty(scenePath))
return null;
int index = scenePath.IndexOfAny("/\\".ToCharArray());
string first = index<0 ? scenePath : scenePath.Substring(0, index);
foreach (var root in AllSceneObjects())
if (root.name==first)
{
if (index<0)
return root;
return root.Find(scenePath.Substring(index+1));
}
return null;
}
public static GUIContent Icon_Help {
get{
if (mIconHelp == null)
mIconHelp = EditorGUIUtility.IconContent("_Help");
return mIconHelp;
}
}
static GUIContent mIconHelp;
public static GUIStyle FindSkinStyle(string name)
{
var allStyles = GUI.skin.customStyles;
for (int i = 0, imax = allStyles.Length; i < imax; ++i)
{
if (allStyles[i].name == name)
return allStyles[i];
}
return null;
}
public static void DrawSkinIcon(Rect rect, params string[] iconNames)
{
foreach (var icon in iconNames)
{
var style = FindSkinStyle(icon);
if (style == null || style.normal == null || style.normal.background == null)
continue;
GUI.DrawTexture(rect, style.normal.background);
return;
}
//Debug.Log("unable to find icon");
}
#endregion
#region Angle Drawer
private static Vector2 mAngle_lastMousePosition;
static Texture mAngle_TextureCircle;
static Texture pAngle_TextureCircle {
get{
if (mAngle_TextureCircle) return mAngle_TextureCircle;
mAngle_TextureCircle = GUI.skin.GetStyle("CN CountBadge").normal.background;
return mAngle_TextureCircle;
}
}
public static float FloatAngle(Rect rect, float value)
{
return FloatAngle(rect, value, -1, -1, -1);
}
public static float FloatAngle(Rect rect, float value, float snap)
{
return FloatAngle(rect, value, snap, -1, -1);
}
public static float FloatAngle(Rect rect, float value, float snap, float min, float max)
{
int id = GUIUtility.GetControlID(FocusType.Passive, rect);
Rect knobRect = new Rect(rect.x, rect.y, rect.height, rect.height);
float delta;
if (min != max)
delta = (max - min) / 360;
else
delta = 1;
if (Event.current != null)
{
if (Event.current.type == EventType.MouseDown && knobRect.Contains(Event.current.mousePosition))
{
GUIUtility.hotControl = id;
mAngle_lastMousePosition = Event.current.mousePosition;
}
else if (Event.current.type == EventType.MouseUp && GUIUtility.hotControl == id)
GUIUtility.hotControl = -1;
else if (Event.current.type == EventType.MouseDrag && GUIUtility.hotControl == id)
{
Vector2 move = mAngle_lastMousePosition - Event.current.mousePosition;
value += delta * (-move.x - move.y);
if (snap > 0)
{
float mod = value % snap;
if (mod < delta * 3 || Mathf.Abs(mod - snap) < delta * 3)
value = Mathf.Round(value / snap) * snap;
}
mAngle_lastMousePosition = Event.current.mousePosition;
GUI.changed = true;
}
}
if (pAngle_TextureCircle) GUI.DrawTexture(knobRect, pAngle_TextureCircle);
Matrix4x4 matrix = GUI.matrix;
if (min != max)
GUIUtility.RotateAroundPivot(value * (360 / (max - min)), knobRect.center);
else
GUIUtility.RotateAroundPivot(value, knobRect.center);
knobRect.height = 5;
knobRect.width = 5;
if (pAngle_TextureCircle) GUI.DrawTexture(knobRect, pAngle_TextureCircle);
GUI.matrix = matrix;
Rect label = new Rect(rect.x + rect.height, rect.y + rect.height / 2 - 9, rect.height, 18);
value = EditorGUI.FloatField(label, value);
if (min != max)
value = Mathf.Clamp(value, min, max);
return value;
}
public static float AngleCircle(Rect rect, float angle, float snap, float min, float max, Texture background=null, Texture knobLine=null)
{
Rect knobRect = new Rect(rect.x, rect.y, rect.height, rect.height);
float delta;
if (min != max)
delta = (max - min) / 360;
else
delta = 1;
if (Event.current != null && GUIUtility.hotControl<=0 && (Event.current.type==EventType.MouseDown || Event.current.type==EventType.MouseDrag) && knobRect.Contains(Event.current.mousePosition))
{
angle = Vector2.Angle( Vector2.right, Event.current.mousePosition-knobRect.center);
if (Event.current.mousePosition.y<knobRect.center.y) angle = 360-angle;
if (Event.current.alt || Event.current.control)
snap = 5;
if (snap > 0)
{
float mod = Mathf.Repeat(angle, snap);
if (mod < delta * 3 || Mathf.Abs(mod - snap) < delta * 3)
angle = Mathf.Round(angle / snap) * snap;
}
GUI.changed = true;
}
if (background==null) background = pAngle_TextureCircle;
if (background) GUI.DrawTexture (knobRect, background);
Matrix4x4 matrix = GUI.matrix;
if (min != max)
GUIUtility.RotateAroundPivot(angle * (360 / (max - min))+90, knobRect.center);
else
GUIUtility.RotateAroundPivot(angle+90, knobRect.center);
float Radius = Mathf.Min (knobRect.width, knobRect.height) * 0.5f;
knobRect.x = knobRect.x + 0.5f * knobRect.width - 4;
knobRect.y += 2;
knobRect.width = 8;
knobRect.height = Radius+2;
if (knobLine == null)
knobLine = GUI.skin.FindStyle ("MeBlendPosition").normal.background;
if (knobLine) GUI.DrawTexture(knobRect, knobLine);
GUI.matrix = matrix;
return Mathf.Repeat(angle, 360);
}
#endregion
#region Unity Version branching
public static string Editor_GetCurrentScene()
{
#if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2
return EditorApplication.currentScene;
#else
return SceneManager.GetActiveScene().path;
#endif
}
public static void Editor_MarkSceneDirty()
{
#if UNITY_5_3 || UNITY_5_3_OR_NEWER
EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene());
#else
EditorApplication.MarkSceneDirty();
#endif
}
public static void Editor_SaveScene()
{
#if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2
EditorApplication.SaveScene ();
#else
EditorSceneManager.SaveOpenScenes();
#endif
}
public static void Editor_OpenScene( string sceneName )
{
#if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2
EditorApplication.OpenScene( sceneName );
#else
EditorSceneManager.OpenScene(sceneName);
#endif
}
#endregion
#region Reflection
static public object Reflection_InvokeMethod ( object instanceObject, string methodName, params object[] p_args )
{
BindingFlags _flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
MethodInfo mi = instanceObject.GetType().GetMethods( _flags ).Where( x => x.Name==methodName ).FirstOrDefault();
if (mi == null) return null;
return mi.Invoke( instanceObject, p_args );
}
static public object Reflection_InvokeMethod ( Type targetType, string methodName, params object[] p_args )
{
BindingFlags _flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
MethodInfo mi = targetType.GetMethods( _flags ).Where( x => x.Name==methodName ).FirstOrDefault();
if (mi == null) return null;
return mi.Invoke( null, p_args );
}
public static object s_RecycledEditor;
public static string TextField ( Rect position, string text, int maxLength, GUIStyle style, int controlID )
{
if (s_RecycledEditor==null)
{
FieldInfo info = typeof(EditorGUI).GetField("s_RecycledEditor", BindingFlags.NonPublic | BindingFlags.Static);
s_RecycledEditor = info.GetValue(null);
}
if (s_RecycledEditor == null)
return "";
return Reflection_InvokeMethod( typeof( EditorGUI ), "DoTextField", s_RecycledEditor, controlID, position, text, style, null, false, false, false, false ) as string;
}
static public void RepaintInspectors()
{
EditorApplication.update -= RepaintInspectors;
var assemblyEditor = Assembly.GetAssembly(typeof(Editor));
var typeInspectorWindow = assemblyEditor.GetType("UnityEditor.InspectorWindow");
typeInspectorWindow.GetMethod("RepaintAllInspectors", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null);
}
public static void ScheduleRepaintInspectors()
{
EditorApplication.update += RepaintInspectors;
}
#endregion
}
}