#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; 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(); 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 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 lstTmp = new List(); 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; } }