-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathUIEventBackend.cs
More file actions
229 lines (194 loc) · 6.26 KB
/
UIEventBackend.cs
File metadata and controls
229 lines (194 loc) · 6.26 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
using UnityEngine;
using UnityEditor;
using RelationsInspector;
using RelationsInspector.Backend;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.EventSystems;
using UnityEngine.Events;
namespace EventInspector
{
// shows the Components that implement a UI event handler interface as well as the objects that listen to them
public class UIEventBackend : MinimalBackend<object, string>
{
static Object sceneObj = EditorGUIUtility.whiteTexture; // representing the scene, as a parent for all the UI GameObjects
// how to react to node-selection events
public enum OnNodeClick { Ignore, SelectComponent, SelectGameObject };
static OnNodeClick onNodeClick = OnNodeClick.SelectGameObject;
bool ignoreNextSelectionChange;
// derive graph nodes from target object
public override IEnumerable<object> Init( object target )
{
if ( target == sceneObj )
{
yield return sceneObj;
yield break;
}
// for GameObject targets, use only the event handler components inside them as seed nodes
var asGameObject = target as GameObject;
if ( asGameObject == null )
yield break;
var handlers = asGameObject.GetComponentsInChildren<IEventSystemHandler>();
foreach ( var handler in handlers )
yield return handler;
}
// connect the scene to all its event handlers
// connect the event handlers to all their listeners
public override IEnumerable<Relation<object, string>> GetRelations( object entity )
{
if ( entity == sceneObj )
{
foreach ( var component in GetUIEventHandlers() )
yield return new Relation<object, string>( entity, component, string.Empty );
yield break;
}
var asComponent = entity as Component;
if ( asComponent == null )
yield break;
// get all of the component's UI events, without duplicates
var eventReferences = UIEventUtilitly
.GetEventRefs( asComponent )
.GroupBy( r => r.name )
.Select( gr => gr.First() );
foreach ( var eventRef in eventReferences )
{
// extract event listener data
var listenerData = EventUtility
.GetListenerData( eventRef.value, eventRef.prop )
.Where( record => record.IsValid() );
// add it to the graph
foreach ( var record in listenerData )
yield return new Relation<object, string>( entity, record, eventRef.name );
}
}
public override Rect OnGUI()
{
// toolbar
GUILayout.BeginHorizontal( EditorStyles.toolbar );
{
// button: generate a new graph from the active scene
if ( GUILayout.Button( "Show all scene events", EditorStyles.toolbarButton, GUILayout.ExpandWidth( false ) ) )
{
api.ResetTargets( new object[] { sceneObj } );
}
GUILayout.Space( 35 );
// choice: how to handle node selection
EditorGUIUtility.labelWidth = 80;
GUILayout.Label( "On node click:", EditorStyles.miniLabel );
onNodeClick = (OnNodeClick) EditorGUILayout.EnumPopup( onNodeClick, EditorStyles.toolbarPopup, GUILayout.Width(120) );
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
return base.OnGUI();
}
// node context menu
// if a listener calls code in the game dlls, add the option to open the source file
public override void OnEntityContextClick( IEnumerable<object> entities, GenericMenu menu )
{
if ( entities.Count() == 1 )
{
var single = entities.First();
var asListener = single as ListenerData;
if ( asListener != null )
{
var targetMb = asListener.target as MonoBehaviour;
if ( targetMb != null )
{
var targetMbScript = MonoScript.FromMonoBehaviour( targetMb );
menu.AddItem(
new GUIContent( "open " + targetMbScript.name ),
false,
() => AssetDatabase.OpenAsset( targetMbScript )
);
}
}
}
}
// returns all scene components that implement a UI event interface
IEnumerable<Component> GetUIEventHandlers()
{
// this way we also get the inactive gameobjects
return Resources
.FindObjectsOfTypeAll<GameObject>()
.Where( go => !IsPrefab( go ) )
.SelectMany( go => go.GetComponents<IEventSystemHandler>() )
.Cast<Component>();
}
bool IsPrefab( GameObject go )
{
var type = PrefabUtility.GetPrefabType(go);
return type != PrefabType.None && type != PrefabType.PrefabInstance;
}
// node tooltip
public override string GetEntityTooltip( object entity )
{
if ( entity == sceneObj )
return "Scene";
var asObject = entity as Object;
if(asObject != null )
return asObject.name;
var asRecord = entity as ListenerData;
if ( asRecord != null )
{
return
"<b>Target</b>: " + asRecord.target +
"\n<b>Method</b>: " + asRecord.method +
"\n<b>Argument</b>: " + asRecord.argument;
}
return entity.ToString();
}
// handle node selection change, depending on the users choice (onNodeClick)
public override void OnEntitySelectionChange( object[] selection )
{
if ( ignoreNextSelectionChange )
{
ignoreNextSelectionChange = false;
return;
}
var selectedObjects = selection
.Except( new[] { sceneObj } )
.Select( x => GetEntityObject( x ) )
.Where( x => x != null )
.ToArray();
switch ( onNodeClick )
{
case OnNodeClick.Ignore:
return;
case OnNodeClick.SelectComponent:
Selection.objects = selectedObjects;
return;
case OnNodeClick.SelectGameObject:
base.OnEntitySelectionChange( selectedObjects );
return;
}
}
// adjust to object selection changes that happen outside the window
public override void OnUnitySelectionChange()
{
ignoreNextSelectionChange = true;
api.SelectEntityNodes( node =>
{
var asComponent = node as Component;
return asComponent == null ? false : Selection.objects.Contains( asComponent.gameObject );
} );
}
// returns GUIContent to be shown in the node's widget
public override GUIContent GetContent( object entity )
{
if ( entity == sceneObj )
return new GUIContent( "Scene", null, "Scene" );
var asObject = GetEntityObject( entity );
return base.GetContent( ( asObject != null ) ? asObject : entity );
}
Object GetEntityObject( object entity )
{
var asObject = entity as Object;
if ( asObject != null )
return asObject;
var asRecord = entity as ListenerData;
if ( asRecord != null )
return asRecord.target;
return null;
}
}
}