using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.EventSystems; namespace UnityEditor.Events { [CustomPreview(typeof(GameObject))] /// /// Custom preview drawing that will draw the intercepted events of a given object. /// class InterceptedEventsPreview : ObjectPreview { protected class ComponentInterceptedEvents { public GUIContent componentName; public int[] interceptedEvents; } class Styles { public GUIStyle labelStyle = new GUIStyle(EditorStyles.label); public GUIStyle componentName = new GUIStyle(EditorStyles.boldLabel); public Styles() { Color fontColor = new Color(0.7f, 0.7f, 0.7f); labelStyle.padding.right += 20; labelStyle.normal.textColor = fontColor; labelStyle.active.textColor = fontColor; labelStyle.focused.textColor = fontColor; labelStyle.hover.textColor = fontColor; labelStyle.onNormal.textColor = fontColor; labelStyle.onActive.textColor = fontColor; labelStyle.onFocused.textColor = fontColor; labelStyle.onHover.textColor = fontColor; componentName.normal.textColor = fontColor; componentName.active.textColor = fontColor; componentName.focused.textColor = fontColor; componentName.hover.textColor = fontColor; componentName.onNormal.textColor = fontColor; componentName.onActive.textColor = fontColor; componentName.onFocused.textColor = fontColor; componentName.onHover.textColor = fontColor; } } private Dictionary> m_TargetEvents; private bool m_InterceptsAnyEvent = false; private GUIContent m_Title; private Styles m_Styles; public override void Initialize(UnityEngine.Object[] targets) { Profiler.BeginSample("ComponentInterceptedEvents.Initialize"); base.Initialize(targets); m_TargetEvents = new Dictionary>(targets.Length); m_InterceptsAnyEvent = false; for (int i = 0; i < targets.Length; ++i) { GameObject go = targets[i] as GameObject; List interceptedEvents = GetEventsInfo(go); m_TargetEvents.Add(go, interceptedEvents); if (interceptedEvents.Any()) m_InterceptsAnyEvent = true; } Profiler.EndSample(); } public override GUIContent GetPreviewTitle() { if (m_Title == null) { m_Title = EditorGUIUtility.TrTextContent("Intercepted Events"); } return m_Title; } public override bool HasPreviewGUI() { return m_TargetEvents != null && m_InterceptsAnyEvent; } public override void OnPreviewGUI(Rect r, GUIStyle background) { if (Event.current.type != EventType.Repaint) return; Profiler.BeginSample("InterceptedEventsPreview.OnPreviewGUI"); if (m_Styles == null) m_Styles = new Styles(); Vector2 maxEventLabelSize = Vector2.zero; int totalInterceptedEvents = 0; List componentIncerceptedEvents = m_TargetEvents[target as GameObject]; // Find out the maximum size needed for any given label. foreach (ComponentInterceptedEvents componentInterceptedEvents in componentIncerceptedEvents) { foreach (int eventIndex in componentInterceptedEvents.interceptedEvents) { GUIContent eventContent = s_PossibleEvents[eventIndex]; ++totalInterceptedEvents; Vector2 labelSize = m_Styles.labelStyle.CalcSize(eventContent); if (maxEventLabelSize.x < labelSize.x) { maxEventLabelSize.x = labelSize.x; } if (maxEventLabelSize.y < labelSize.y) { maxEventLabelSize.y = labelSize.y; } } } // Apply padding RectOffset previewPadding = new RectOffset(-5, -5, -5, -5); r = previewPadding.Add(r); // Figure out how many rows and columns we can/should have int columns = Mathf.Max(Mathf.FloorToInt(r.width / maxEventLabelSize.x), 1); int rows = Mathf.Max(totalInterceptedEvents / columns, 1) + componentIncerceptedEvents.Count; // Centering float initialX = r.x + Mathf.Max(0, (r.width - (maxEventLabelSize.x * columns)) / 2); float initialY = r.y + Mathf.Max(0, (r.height - (maxEventLabelSize.y * rows)) / 2); Rect labelRect = new Rect(initialX, initialY, maxEventLabelSize.x, maxEventLabelSize.y); int currentColumn = 0; foreach (ComponentInterceptedEvents componentInterceptedEvents in componentIncerceptedEvents) { GUI.Label(labelRect, componentInterceptedEvents.componentName, m_Styles.componentName); labelRect.y += labelRect.height; labelRect.x = initialX; foreach (int eventIndex in componentInterceptedEvents.interceptedEvents) { GUIContent eventContent = s_PossibleEvents[eventIndex]; GUI.Label(labelRect, eventContent, m_Styles.labelStyle); if (currentColumn < columns - 1) { labelRect.x += labelRect.width; } else { labelRect.y += labelRect.height; labelRect.x = initialX; } currentColumn = (currentColumn + 1) % columns; } if (labelRect.x != initialX) { labelRect.y += labelRect.height; labelRect.x = initialX; } } Profiler.EndSample(); } //Lookup cache to avoid recalculating which types uses which events: //Caches all interfaces that inherit from IEventSystemHandler static List s_EventSystemInterfaces = null; //Caches all GUIContents in a single list to avoid creating too much GUIContent and strings. private static List s_PossibleEvents = null; //Caches all events used by each interface static Dictionary> s_InterfaceEventSystemEvents = null; //Caches each concrete type and it's events static readonly Dictionary s_ComponentEvents2 = new Dictionary(); protected static List GetEventsInfo(GameObject gameObject) { InitializeEvetnsInterfaceCacheIfNeeded(); List componentEvents = new List(); MonoBehaviour[] mbs = gameObject.GetComponents(); for (int i = 0, imax = mbs.Length; i < imax; ++i) { ComponentInterceptedEvents componentEvent = null; MonoBehaviour mb = mbs[i]; if (mb == null) continue; Type type = mb.GetType(); if (!s_ComponentEvents2.ContainsKey(type)) { List events = null; Profiler.BeginSample("ComponentInterceptedEvents.GetEventsInfo.NewType"); if (typeof(IEventSystemHandler).IsAssignableFrom(type)) { for (int index = 0; index < s_EventSystemInterfaces.Count; index++) { var eventInterface = s_EventSystemInterfaces[index]; if (!eventInterface.IsAssignableFrom(type)) continue; if (events == null) events = new List(); events.AddRange(s_InterfaceEventSystemEvents[eventInterface]); } } if (events != null) { componentEvent = new ComponentInterceptedEvents(); componentEvent.componentName = new GUIContent(type.Name); componentEvent.interceptedEvents = events.OrderBy(index => s_PossibleEvents[index].text).ToArray(); } s_ComponentEvents2.Add(type, componentEvent); Profiler.EndSample(); } else { componentEvent = s_ComponentEvents2[type]; } if (componentEvent != null) { componentEvents.Add(componentEvent); } } return componentEvents; } private static void InitializeEvetnsInterfaceCacheIfNeeded() { if (s_EventSystemInterfaces != null) return; s_EventSystemInterfaces = new List(); s_PossibleEvents = new List(); s_InterfaceEventSystemEvents = new Dictionary>(); TypeCache.TypeCollection types = TypeCache.GetTypesDerivedFrom(); foreach (var type in types) { if (!type.IsInterface) continue; s_EventSystemInterfaces.Add(type); List eventIndexList = new List(); MethodInfo[] methodInfos = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); for (int mi = 0; mi < methodInfos.Length; mi++) { MethodInfo methodInfo = methodInfos[mi]; eventIndexList.Add(s_PossibleEvents.Count); s_PossibleEvents.Add(new GUIContent(methodInfo.Name)); } s_InterfaceEventSystemEvents.Add(type, eventIndexList); } } } }