You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

717 lines
23 KiB

#define MIN_CANDIDATE_WIDTH
using UnityEngine;
using UnityEditor;
using UnityEditor.AnimatedValues;
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor.SceneManagement;
using MapOfChange = System.Collections.Generic.Dictionary<string, AutoUIBinder.ChangeInfo>;
public class AutoUIBinder : EditorWindow
{
Vector2 scrollPos;
Vector2 scrollPos2;
Vector2 scrollPos3;
AutoUIManager m_manager = null;
Component m_component = null;
bool isNonDeclared = false;
bool hasNonPublic = false;
bool noAutoUIAttr = true;
bool autoSaveScene = false;
int idxMemberType = 1;
int idxValueType = 0;
//Color serializableColor = new Color(1.0f, 0.0f, 0.0f, 1.0f);
public class ChangeInfo
{
public struct Status
{
public bool is_bind;
public bool is_serializable;
public bool has_range;
public float min;
public float max;
}
public ChangeInfo(Status s)
{
m_oring_status = s;
}
public bool IsChanged()
{
return (m_oring_status.is_bind != is_bind || IsSerializableChanged() || IsHasRangeChanged() || IsRangeChanged());
}
public bool IsSerializableChanged()
{
return (m_oring_status.is_bind && (m_oring_status.is_serializable != is_serializable));
}
public bool IsHasRangeChanged()
{
return (m_oring_status.is_bind && (m_oring_status.has_range != has_range));
}
public bool IsRangeChanged()
{
return (m_oring_status.is_bind && m_oring_status.has_range && (m_oring_status.min != min || m_oring_status.max != max));
}
public Status origin_status
{
get
{
return m_oring_status;
}
}
public Component component;
public string member_name;
public bool is_bind;
public bool is_property;
public bool is_serializable;
public bool has_range;
public float min;
public float max;
private Status m_oring_status;
}
Type[] types;
string[] typeNames;
MapOfChange map_change;
[MenuItem("Window/UC/AutoUIBinder _F9")]
static void CreateAutoUIBinder()
{
// Get existing open window or if none, make a new one:
AutoUIBinder window = (AutoUIBinder)EditorWindow.GetWindow(typeof(AutoUIBinder));
window.minSize = new Vector2(filterWidth + candidateMinWidth + bindingMaxWidth + 20, windowMinHeight);
//window.maxSize = window.minSize;
}
private const int windowMinHeight = 388;
private const int filterWidth = 100;
#if MIN_CANDIDATE_WIDTH
private const int candidateMinWidth = 320;
#else
private const int candidateMinWidth = 460;
#endif
private const int bindingMaxWidth = 270;
internal class Styles
{
public GUIStyle filterBox = new GUIStyle("Box");
public GUIStyle filterSelectBox = new GUIStyle("PreferencesSectionBox");
public GUIStyle filterElement = new GUIStyle("PreferencesKeysElement");
public GUIStyle filterElementToggle = new GUIStyle(EditorStyles.miniButton);
public GUIStyle filterElementLabel = new GUIStyle(EditorStyles.boldLabel);
public GUIStyle styleAttri = new GUIStyle(GUI.skin.label);
public GUIStyle styleMemName = new GUIStyle(GUI.skin.label);
public Styles()
{
filterBox = new GUIStyle(GUI.skin.box);
filterBox.padding = new RectOffset(0, 1, 1, 0);
filterSelectBox.margin = new RectOffset(0, 0, 0, 0);
filterElement.alignment = TextAnchor.UpperRight;
//filterElementToggle.alignment = TextAnchor.UpperRight;
filterElementLabel.alignment = TextAnchor.UpperRight;
filterElementLabel.padding.right = 6;
filterElementLabel.border.right = 6;
filterElementLabel.margin.right = 6;
filterElementLabel.overflow.right = 6;
styleMemName.fontSize = 14;
styleMemName.fontStyle = FontStyle.Bold;
styleAttri.normal.textColor = Color.red;
}
}
private static Styles styles = null;
void OnGUI()
{
CheckInit();
if(m_manager == null)
m_manager = GameObject.FindObjectOfType<AutoUIManager>();
EditorGUI.BeginDisabledGroup(Application.isPlaying);
EditorGUILayout.Separator();
m_manager = EditorGUILayout.ObjectField("AutoUIManager", m_manager, typeof(AutoUIManager), true, GUILayout.MaxWidth(400)) as AutoUIManager;
if (m_manager == null)
{
EditorGUI.EndDisabledGroup();//Impotent!!!
return;
}
Component com_new = EditorGUILayout.ObjectField("Component", m_component, typeof(Component), true, GUILayout.MaxWidth(400)) as Component;
if (com_new != m_component)
{
map_change.Clear();
m_component = com_new;
}
EditorGUILayout.Separator();
using (var hh = new EditorGUILayout.HorizontalScope())
{
using (var v = new EditorGUILayout.VerticalScope())
{
EditorGUILayout.LabelField("Bind Filter", EditorStyles.boldLabel);
using (var hhh = new EditorGUILayout.HorizontalScope(styles.filterBox, GUILayout.ExpandWidth(true)))
{
using (var d = new EditorGUI.DisabledGroupScope(m_component == null))
{
using (var sv = new EditorGUILayout.ScrollViewScope(scrollPos3, false, false, GUIStyle.none, GUIStyle.none, styles.filterSelectBox, GUILayout.Width(filterWidth)))
{
sv.handleScrollWheel = false;
scrollPos3 = sv.scrollPosition;
DrawFilters();
}
}
if (m_component)
{
using (var sv = new EditorGUILayout.ScrollViewScope(scrollPos))
{
scrollPos = sv.scrollPosition;
DrawPropertyCandidates();
}
}
}
using (var h = new EditorGUILayout.HorizontalScope())
{
DrawActions();
}
GUILayoutUtility.GetRect(0, 4);
}
using (var v = new EditorGUILayout.VerticalScope(GUILayout.MaxWidth(bindingMaxWidth)))
{
EditorGUILayout.LabelField("Binding Information", EditorStyles.boldLabel);
using (var sv = new EditorGUILayout.ScrollViewScope(scrollPos2))
{
scrollPos2 = sv.scrollPosition;
DrawBindingInformations();
}
}
}
EditorGUI.EndDisabledGroup();
}
void DrawFilters()
{
GUILayoutOption widthSetting = GUILayout.Width(filterWidth);
EditorGUILayout.LabelField("Member Type", styles.filterElementLabel, widthSetting);
idxMemberType = GUILayout.SelectionGrid(idxMemberType, new string[] { "Field", "Property" }, 1, styles.filterElement, widthSetting);
EditorGUILayout.Separator();
EditorGUILayout.LabelField("Value Type", styles.filterElementLabel, widthSetting);
idxValueType = GUILayout.SelectionGrid(idxValueType, typeNames, 1, styles.filterElement, widthSetting);// EditorStyles.toolbarButton
EditorGUILayout.Separator();
EditorGUILayout.LabelField("Others", styles.filterElementLabel, widthSetting);
GUILayoutOption widthSetting2 = GUILayout.Width(filterWidth - 20);
using (var h = new EditorGUILayout.HorizontalScope(GUILayout.Width(filterWidth - 4)))
{
GUILayout.FlexibleSpace();
hasNonPublic = GUILayout.Toggle(hasNonPublic, "Non Public", styles.filterElementToggle, widthSetting2);
}
using (var h = new EditorGUILayout.HorizontalScope(GUILayout.Width(filterWidth - 4)))
{
GUILayout.FlexibleSpace();
noAutoUIAttr = GUILayout.Toggle(noAutoUIAttr, "Non AutoUI", styles.filterElementToggle, widthSetting2);
}
using (var h = new EditorGUILayout.HorizontalScope(GUILayout.Width(filterWidth - 4)))
{
GUILayout.FlexibleSpace();
isNonDeclared = GUILayout.Toggle(isNonDeclared, "Non Declared", styles.filterElementToggle, widthSetting2);
}
}
void DrawPropertyCandidates()
{
BindingFlags bindingFlag = BindingFlags.Instance | BindingFlags.Public;
if (!isNonDeclared)
{
bindingFlag |= BindingFlags.DeclaredOnly;
}
if (hasNonPublic)
{
bindingFlag |= BindingFlags.NonPublic;
}
switch (idxMemberType)
{
case 0:
mf_ShowFields(bindingFlag, m_component.GetType());
break;
case 1:
mf_ShowProperties(bindingFlag, m_component.GetType());
break;
}
}
void DrawActions()
{
autoSaveScene = GUILayout.Toggle(autoSaveScene, "Auto Save Scene");
GUILayout.FlexibleSpace();
using (var d = new EditorGUI.DisabledGroupScope(map_change.Count == 0))
{
if (GUILayout.Button("Apply"))
{
mf_ApplyChange();
}
if (GUILayout.Button("Revert"))
{
map_change.Clear();
}
}
}
void DrawBindingInformations()
{
IEnumerable<AutoUIManager.BindInfo> ienum = m_manager.GetBindInfos();
if (ienum == null)
return;
foreach (AutoUIManager.BindInfo info in ienum)
{
using (var v = new EditorGUILayout.VerticalScope("Button", GUILayout.MaxWidth(bindingMaxWidth - 10)))
{
if (GUI.Button(v.rect, GUIContent.none))
{
m_component = sf_FindTargetCompoent(EditorSceneManager.GetActiveScene(), info);
//idxValueType = mf_FindIdxValueType(m_component, info);
//idxMemberType = info.is_property ? 1 : 0;
}
if (!info.is_serializable)
{
GUILayout.Label("[ NonSerializable ]", styles.styleAttri);
}
if (info.has_range)
{
GUILayout.Label("[ Range " + info.minmax.ToString() + " ]", styles.styleAttri);
}
GUILayout.Label(info.gameobject_name + " (" + info.component_name + ")");
GUILayout.Label(info.member_name, styles.styleMemName);
}
}
}
void CheckInit()
{
if (types == null)
{
types = new Type[] { typeof(object), typeof(float), typeof(int), typeof(bool), typeof(string), typeof(Vector2), typeof(Vector3), typeof(Vector4), typeof(Enum) };
List<string> lstTmp = new List<string>();
lstTmp.Add("Any");
foreach (Type t in types)
{
if (t == typeof(object))
{
continue;
}
lstTmp.Add(t.Name.ToString());
}
typeNames = lstTmp.ToArray();
}
if(map_change == null)
map_change = new MapOfChange();
if (styles == null)
styles = new Styles();
}
void mf_ShowMemeberInfo(MemberInfo memberInfo, bool canHaveRange)
{
ObsoleteAttribute[] obsoletes = memberInfo.GetCustomAttributes(typeof(ObsoleteAttribute), true) as ObsoleteAttribute[];
if (obsoletes.Length > 0)
{
return; ;
}
AutoUIAttribute[] autouis = memberInfo.GetCustomAttributes(typeof(AutoUIAttribute), true) as AutoUIAttribute[];
string label_name;
if (autouis.Length > 0)
{
if (noAutoUIAttr)
{
return;
}
label_name = "[AutoUI] " + memberInfo.Name;
}
else
{
label_name = memberInfo.Name;
}
bool is_binding_old;
string key = AutoUIManager.GetUniqueKey(m_component, memberInfo.Name);
AutoUIManager.BindInfo bind_info;
bool is_binding = m_manager.TryGetBindInfo(key, out bind_info);
bool is_none_serializable_old;
bool has_range_old;
float min_old;
float max_old;
ChangeInfo change_info;
ChangeInfo.Status origin_status;
if (map_change.TryGetValue(key, out change_info))
{
origin_status = change_info.origin_status;
is_binding_old = change_info.is_bind;
is_none_serializable_old = !change_info.is_serializable;
has_range_old = change_info.has_range;
min_old = change_info.min;
max_old = change_info.max;
label_name += " *";
}
else
{
origin_status.is_bind = is_binding;
if (is_binding)
{
is_none_serializable_old = !bind_info.is_serializable;
has_range_old = bind_info.has_range;
min_old = bind_info.minmax.x;
max_old = bind_info.minmax.y;
}
else
{
is_none_serializable_old = false;
has_range_old = false;
min_old = 0.0f;
max_old = 0.0f;
}
origin_status.is_serializable = !is_none_serializable_old;
origin_status.has_range = has_range_old;
origin_status.min = min_old;
origin_status.max = max_old;
is_binding_old = is_binding;
}
m_manager.IsContainedBindInfo(m_component as Component, memberInfo.Name);
#if MIN_CANDIDATE_WIDTH
bool is_binding_new = EditorGUILayout.ToggleLeft(label_name, is_binding_old);
bool has_range_new = false;
float min_new = min_old;
float max_new = max_old;
bool is_none_serializable_new = false;
if (is_binding_new)
{
using (var h = new EditorGUILayout.HorizontalScope())
{
GUILayoutUtility.GetRect(30, 0);
is_none_serializable_new = GUILayout.Toggle(is_none_serializable_old, "NonSerializable", EditorStyles.miniButton, GUILayout.Width(94));
using (var d = new EditorGUI.DisabledScope(canHaveRange == false))
{
has_range_new = GUILayout.Toggle(has_range_old, "R", EditorStyles.miniButton, GUILayout.Width(24));
GUILayout.FlexibleSpace();
if (has_range_new)
{
EditorGUILayout.LabelField("min", GUILayout.Width(32));
min_new = EditorGUILayout.FloatField(min_old, GUILayout.Width(32));
EditorGUILayout.LabelField("max", GUILayout.Width(32));
max_new = EditorGUILayout.FloatField(max_old, GUILayout.Width(32));
}
}
}
EditorGUILayout.Separator();
}
#else
EditorGUILayout.BeginHorizontal( GUILayout.ExpandWidth(false) );
bool is_binding_new = EditorGUILayout.ToggleLeft(label_name, is_binding_old);
bool has_range_new = false;
float min_new = min_old;
float max_new = max_old;
if (is_binding_new && canHaveRange)
{
if (has_range_old)
{
using (var v = new EditorGUILayout.VerticalScope())
{
using (var h = new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("min", GUILayout.MaxWidth(32));
min_new = EditorGUILayout.FloatField(min_old);
}
using (var h = new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField("max", GUILayout.MaxWidth(32));
max_new = EditorGUILayout.FloatField(max_old);
}
}
}
has_range_new = GUILayout.Toggle(has_range_old, "R", EditorStyles.miniButton, GUILayout.Width(28));
}
bool is_none_serializable_new = false;
if (is_binding_new)
{
is_none_serializable_new = GUILayout.Toggle(is_none_serializable_old, "NonSerializable", EditorStyles.miniButton, GUILayout.Width(100));
}
EditorGUILayout.EndHorizontal();
if (has_range_new)
{
EditorGUILayout.Separator();
}
#endif
if (change_info != null)
{
if (is_binding_new)
{
change_info.is_serializable = !is_none_serializable_new;
change_info.has_range = has_range_new;
if (has_range_new)
{
change_info.min = min_new;
change_info.max = max_new;
}
}
change_info.is_bind = is_binding_new;
if (!change_info.IsChanged())
{
map_change.Remove(key);
}
}
else
{
if ( (is_binding_old != is_binding_new && is_binding != is_binding_new) ||
is_none_serializable_new != is_none_serializable_old || has_range_new != has_range_old || (has_range_new && (min_new != min_old || max_new != max_old)) )
{
change_info = new ChangeInfo(origin_status);
change_info.component = m_component;
change_info.member_name = memberInfo.Name;
change_info.is_bind = is_binding_new;
change_info.is_serializable = !(is_binding_new ? is_none_serializable_new : is_none_serializable_old);
change_info.is_property = memberInfo.MemberType == MemberTypes.Property;
change_info.has_range = (is_binding_new ? has_range_new : has_range_old);
change_info.min = (has_range_new ? min_new : min_old);
change_info.max = (has_range_new ? max_new : max_old);
map_change.Add(key, change_info);
}
}
}
void mf_ShowProperties(BindingFlags bindingFlag, Type typeComponet)
{
foreach (PropertyInfo info in typeComponet.GetProperties(bindingFlag))
{
if (!mf_IsFitType(info.PropertyType))
{
continue;
}
if (info.GetGetMethod() == null || info.GetSetMethod() == null)
{
continue;
}
mf_ShowMemeberInfo(info, mf_CanHaveRange(info.PropertyType));
}
}
void mf_ShowFields(BindingFlags bindingFlag, Type typeComponet)
{
foreach (FieldInfo info in typeComponet.GetFields(bindingFlag))
{
if (!mf_IsFitType(info.FieldType))
{
continue;
}
mf_ShowMemeberInfo(info, mf_CanHaveRange(info.FieldType));
}
}
bool mf_CanHaveRange(Type type)
{
return (type == typeof(int) || type == typeof(float));
}
bool mf_IsFitType(Type type)
{
if (idxValueType == 0)
{
if (!mf_IsSupportType(type))
{
return false;
}
}
else if (type != types[idxValueType])
{
return false;
}
return true;
}
bool mf_IsSupportType(Type type_check)
{
foreach(Type t in types)
{
if (t == typeof(object))
{
continue;
}
if (t == type_check)
{
return true;
}
}
return false;
}
private static Component sf_FindTargetCompoent(UnityEngine.SceneManagement.Scene scene, AutoUIManager.BindInfo bindinfo)
{
GameObject[] objs = scene.GetRootGameObjects();
foreach (GameObject obj in objs)
{
if (obj.name != bindinfo.gameobject_name)
{
continue;
}
Component component = obj.GetComponent(bindinfo.component_name);
if (component == null)
{
continue;
}
return component;
}
return null;
}
private void mf_ApplyChange()
{
foreach (ChangeInfo change_info in map_change.Values)
{
if (change_info.is_bind)
{
if (change_info.component == null)
{
continue;
}
AutoUIManager.BindInfo bind_info;
if (m_manager.TryGetBindInfo(change_info.component, change_info.member_name, out bind_info))
{
bind_info.is_serializable = change_info.is_serializable;
bind_info.has_range = change_info.has_range;
bind_info.minmax.x = change_info.min;
bind_info.minmax.y = change_info.max;
continue;
}
m_manager.AddBindInfo( change_info.component,
change_info.member_name,
change_info.is_property,
change_info.is_serializable,
change_info.has_range ? new RangeAttribute(change_info.min, change_info.max) : null);
}
else
{
m_manager.RemoveBindInfo(m_component, change_info.member_name);
}
}
map_change.Clear();
EditorUtility.SetDirty(m_manager);
EditorSceneManager.MarkSceneDirty(m_manager.gameObject.scene);
if (autoSaveScene)
{
EditorSceneManager.SaveScene(EditorSceneManager.GetActiveScene());
}
}
private int mf_FindIdxValueType(Component component, AutoUIManager.BindInfo bindinfo)
{
BindingFlags bindingFlag = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Public;
Type typeComponet = component.GetType();
Type typeValue = null;
if (bindinfo.is_property)
{
foreach (PropertyInfo info in typeComponet.GetProperties(bindingFlag))
{
if (info.Name != bindinfo.member_name)
{
continue;
}
typeValue = info.PropertyType;
break;
}
}
else
{
foreach (FieldInfo info in typeComponet.GetFields(bindingFlag))
{
if (info.Name != bindinfo.member_name)
{
continue;
}
typeValue = info.FieldType;
break;
}
}
if (typeValue == null)
{
return 0;
}
for (int i = 0; i < types.Length; ++i)
{
if (typeValue != types[i])
{
continue;
}
return i;
}
return 0;
}
}