44using System . Linq ;
55using UnityEngine ;
66using UnityEditor ;
7+ using UnityEditor . Experimental . GraphView ;
78using System . Threading ;
89
910namespace JLChnToZ . EditorExtensions . UInspectorPlus {
10- internal class TypeMatcher : IDisposable {
11+ internal class NamespacedType {
12+ private static bool isRefreshing = true ;
13+ private static AppDomain currentDomain ;
14+ public static readonly NamespacedType root = new NamespacedType ( "global::" ) ;
15+ private static readonly HashSet < Type > allTypes = new HashSet < Type > ( ) ;
16+ private static readonly List < Assembly > pendingAssemblies = new List < Assembly > ( ) ;
17+ private Dictionary < string , NamespacedType > subNamespaces ;
18+ private List < Type > types ;
19+
20+ public readonly string namespaceName ;
21+
22+ public static ICollection < Type > AllTypes => allTypes ;
23+
24+ public ICollection < NamespacedType > SubNamespaces => subNamespaces . Values ;
25+
26+ public ICollection < Type > Types => types . AsReadOnly ( ) ;
27+
28+ static NamespacedType ( ) {
29+ new Thread ( Init ) { IsBackground = true } . Start ( ) ;
30+ }
31+
32+ private NamespacedType ( string name ) {
33+ namespaceName = name ;
34+ subNamespaces = new Dictionary < string , NamespacedType > ( ) ;
35+ types = new List < Type > ( ) ;
36+ }
37+
38+ private static void Init ( ) {
39+ if ( currentDomain == null ) {
40+ currentDomain = AppDomain . CurrentDomain ;
41+ AddTypes (
42+ from assembly in currentDomain . GetAssemblies ( )
43+ from type in assembly . LooseGetTypes ( )
44+ select type
45+ ) ;
46+ currentDomain . AssemblyLoad += OnAssemblyLoad ;
47+ currentDomain . DomainUnload += OnAppDomainUnload ;
48+ }
49+ Refresh ( ) ;
50+ }
51+
52+ private static void Refresh ( ) {
53+ if ( pendingAssemblies . Count > 0 ) {
54+ var buffer = pendingAssemblies . ToArray ( ) ;
55+ pendingAssemblies . Clear ( ) ;
56+ AddTypes (
57+ from assembly in buffer
58+ from type in assembly . LooseGetTypes ( )
59+ select type
60+ ) ;
61+ }
62+ isRefreshing = false ;
63+ }
64+
65+ private static void AddTypes ( IEnumerable < Type > types ) {
66+ foreach ( var type in types ) {
67+ if ( ! allTypes . Add ( type ) ) continue ;
68+ var current = root ;
69+ var ns = type . Namespace ;
70+ if ( ! string . IsNullOrEmpty ( ns ) )
71+ foreach ( var path in ns . Split ( '.' ) ) {
72+ if ( ! current . subNamespaces . TryGetValue ( path , out var child ) )
73+ current . subNamespaces . Add ( path , child = new NamespacedType ( path ) ) ;
74+ current = child ;
75+ }
76+ current . types . Add ( type ) ;
77+ }
78+ }
79+
80+ private static void OnAssemblyLoad ( object sender , AssemblyLoadEventArgs e ) {
81+ pendingAssemblies . Add ( e . LoadedAssembly ) ;
82+ if ( ! isRefreshing ) {
83+ isRefreshing = true ;
84+ new Thread ( Refresh ) { IsBackground = true } . Start ( ) ;
85+ }
86+ }
87+
88+ private static void OnAppDomainUnload ( object sender , EventArgs e ) {
89+ currentDomain = null ;
90+ }
91+ }
92+
93+ internal class TypeMatcher : ScriptableObject , ISearchWindowProvider {
1194 public Thread bgWorker ;
1295 public event Action OnRequestRedraw ;
1396 public event Action < Type > OnSelected ;
14- private readonly HashSet < Type > searchedTypes = new HashSet < Type > ( ) ;
15- private readonly List < Assembly > pendingAssemblies = new List < Assembly > ( ) ;
16- private AppDomain currentDomain ;
1797 private string searchText = string . Empty ;
1898 private Type [ ] searchTypeResult = null ;
1999 public Type genericType ;
@@ -41,14 +121,6 @@ public void Draw() {
41121 if ( searchTypeResult == null || searchTypeResult . Length == 0 ) return ;
42122 GUIContent temp = new GUIContent ( ) ;
43123 GUILayout . BeginVertical ( ) ;
44- /*
45- GUILayout.Space(8);
46- GUILayout.Label(
47- $"Type Search Result ({searchTypeResult.Length}):",
48- EditorStyles.boldLabel
49- );
50- GUILayout.Space(8);
51- */
52124 for ( int i = 0 ; i < Math . Min ( searchTypeResult . Length , 500 ) ; i ++ ) {
53125 Type type = searchTypeResult [ i ] ;
54126 temp . text = type . FullName ;
@@ -65,48 +137,16 @@ public void Draw() {
65137 GUILayout . Space ( 8 ) ;
66138 GUILayout . EndVertical ( ) ;
67139 }
68-
69- private void InitSearch ( ) {
70- if ( currentDomain == null ) {
71- currentDomain = AppDomain . CurrentDomain ;
72- searchedTypes . UnionWith (
73- from assembly in currentDomain . GetAssemblies ( )
74- from type in assembly . LooseGetTypes ( )
75- select type
76- ) ;
77- currentDomain . AssemblyLoad += OnAssemblyLoad ;
78- currentDomain . DomainUnload += OnAppDomainUnload ;
79- }
80- if ( pendingAssemblies . Count > 0 ) {
81- var buffer = pendingAssemblies . ToArray ( ) ;
82- pendingAssemblies . Clear ( ) ;
83- searchedTypes . UnionWith (
84- from assembly in buffer
85- from type in assembly . LooseGetTypes ( )
86- select type
87- ) ;
88- }
89- }
90-
91- private void OnAssemblyLoad ( object sender , AssemblyLoadEventArgs e ) {
92- pendingAssemblies . Add ( e . LoadedAssembly ) ;
93- }
94-
95- private void OnAppDomainUnload ( object sender , EventArgs e ) {
96- currentDomain = null ;
97- }
98-
99- ~ TypeMatcher ( ) => Dispose ( ) ;
140+
100141
101142 private void DoSearch ( ) {
102143 try {
103- InitSearch ( ) ;
104144 var searchText = this . searchText ;
105145 while ( true ) {
106146 Thread . Sleep ( 100 ) ;
107147 List < Type > searchTypeResult = new List < Type > ( ) ;
108148 if ( ! string . IsNullOrEmpty ( searchText ) )
109- foreach ( Type type in searchedTypes ) {
149+ foreach ( Type type in NamespacedType . AllTypes ) {
110150 if ( searchText != this . searchText ) break ;
111151 if ( type . AssemblyQualifiedName . IndexOf ( searchText , StringComparison . OrdinalIgnoreCase ) >= 0 && Helper . IsTypeRseolvable ( genericType , type ) )
112152 searchTypeResult . Add ( type ) ;
@@ -131,16 +171,7 @@ private void RequestRedraw() {
131171 if ( OnRequestRedraw != null ) OnRequestRedraw . Invoke ( ) ;
132172 }
133173
134- public void Dispose ( ) {
135- try {
136- if ( currentDomain != null ) {
137- currentDomain . AssemblyLoad -= OnAssemblyLoad ;
138- currentDomain . DomainUnload -= OnAppDomainUnload ;
139- }
140- } catch {
141- } finally {
142- currentDomain = null ;
143- }
174+ private void OnDestroy ( ) {
144175 try {
145176 if ( bgWorker != null && bgWorker . IsAlive )
146177 bgWorker . Abort ( ) ;
@@ -149,57 +180,49 @@ public void Dispose() {
149180 bgWorker = null ;
150181 }
151182 }
152- }
153-
154- internal class TypeMatcherPopup : PopupWindowContent {
155- readonly Type genericType ;
156- TypeMatcher typeMatcher ;
157- Vector2 scrollPos ;
158-
159- public event Action OnRequestRedraw ;
160-
161- public event Action < Type > OnSelected ;
162-
163- public TypeMatcherPopup ( Type genericType ) {
164- this . genericType = genericType ;
165- }
166-
167- public override Vector2 GetWindowSize ( ) => new Vector2 (
168- Mathf . Max ( 500 , typeMatcher ? . Width ?? 0 + 25 ) ,
169- EditorGUIUtility . singleLineHeight * Mathf . Clamp ( ( typeMatcher ? . SearchResultCount ?? 0 ) + 3 , 5 , 20 )
170- ) ;
171-
172- public override void OnGUI ( Rect rect ) {
173- if ( typeMatcher == null ) return ;
174- EditorGUILayout . BeginHorizontal ( EditorStyles . toolbar ) ;
175- typeMatcher . SearchText = Helper . ToolbarSearchField ( typeMatcher . SearchText ?? string . Empty ) ;
176- EditorGUILayout . EndHorizontal ( ) ;
177- scrollPos = EditorGUILayout . BeginScrollView ( scrollPos ) ;
178- typeMatcher . Draw ( ) ;
179- EditorGUILayout . EndScrollView ( ) ;
180- }
181183
182- public override void OnOpen ( ) {
183- if ( typeMatcher == null ) {
184- typeMatcher = new TypeMatcher {
185- genericType = genericType ,
186- } ;
187- if ( OnRequestRedraw != null ) typeMatcher . OnRequestRedraw += OnRequestRedraw ;
188- typeMatcher . OnSelected += Selected ;
189- scrollPos = Vector2 . zero ;
184+ public List < SearchTreeEntry > CreateSearchTree ( SearchWindowContext context ) {
185+ var searchTreeEntries = new List < SearchTreeEntry > {
186+ new SearchTreeGroupEntry ( new GUIContent ( "Types" ) ) ,
187+ } ;
188+ var typeIconContent = EditorGUIUtility . IconContent ( "Assembly Icon" ) ;
189+ if ( NamespacedType . root . Types . Count > 0 ) {
190+ searchTreeEntries . Add ( new SearchTreeGroupEntry ( new GUIContent ( NamespacedType . root . namespaceName ) , 1 ) ) ;
191+ foreach ( var type in NamespacedType . root . Types )
192+ searchTreeEntries . Add ( new SearchTreeEntry ( new GUIContent ( type . Name ) ) {
193+ level = 2 ,
194+ userData = type ,
195+ } ) ;
190196 }
191- }
192-
193- public override void OnClose ( ) {
194- if ( typeMatcher != null ) {
195- typeMatcher . Dispose ( ) ;
196- typeMatcher = null ;
197+ var stack = new Stack < ( Queue < NamespacedType > , ICollection < Type > ) > ( ) ;
198+ stack . Push ( ( new Queue < NamespacedType > ( NamespacedType . root . SubNamespaces ) , null ) ) ;
199+ while ( stack . Count > 0 ) {
200+ var ( pendingChildren , types ) = stack . Peek ( ) ;
201+ if ( pendingChildren . Count > 0 ) {
202+ var child = pendingChildren . Dequeue ( ) ;
203+ searchTreeEntries . Add ( new SearchTreeGroupEntry ( new GUIContent ( child . namespaceName ) , stack . Count ) ) ;
204+ stack . Push ( ( new Queue < NamespacedType > ( child . SubNamespaces ) , child . Types ) ) ;
205+ continue ;
206+ }
207+ if ( types != null ) {
208+ foreach ( var type in types ) {
209+ if ( ! Helper . IsTypeRseolvable ( genericType , type ) ) continue ;
210+ var objContent = type . IsSubclassOf ( typeof ( UnityEngine . Object ) ) ? EditorGUIUtility . ObjectContent ( null , type ) : typeIconContent ;
211+ var name = type . Name ;
212+ searchTreeEntries . Add ( new SearchTreeEntry ( new GUIContent ( objContent ) { text = name . Substring ( name . LastIndexOf ( '.' ) + 1 ) } ) {
213+ level = stack . Count ,
214+ userData = type ,
215+ } ) ;
216+ }
217+ }
218+ stack . Pop ( ) ;
197219 }
220+ return searchTreeEntries ;
198221 }
199222
200- void Selected ( Type type ) {
201- OnSelected ? . Invoke ( type ) ;
202- editorWindow . Close ( ) ;
223+ public bool OnSelectEntry ( SearchTreeEntry entry , SearchWindowContext context ) {
224+ OnSelected ? . Invoke ( entry . userData as Type ) ;
225+ return true ;
203226 }
204227 }
205228
@@ -246,12 +269,13 @@ public void Draw() {
246269 rect = EditorGUI . PrefixLabel ( rect , new GUIContent ( constraints [ i ] . Name ) ) ;
247270 if ( GUI . Button ( rect , resolvedTypes [ i ] != null ? $ "T: { resolvedTypes [ i ] . FullName } " : "" , EditorStyles . textField ) ) {
248271 int index = i ;
249- var typeMatcherPopup = new TypeMatcherPopup ( constraints [ i ] ) ;
250- typeMatcherPopup . OnSelected += type => {
272+ var typeMatcher = ScriptableObject . CreateInstance < TypeMatcher > ( ) ;
273+ typeMatcher . genericType = constraints [ i ] ;
274+ typeMatcher . OnSelected += type => {
251275 resolvedTypes [ index ] = type ;
252276 subGUI [ index ] = null ;
253277 } ;
254- PopupWindow . Show ( rect , typeMatcherPopup ) ;
278+ SearchWindow . Open ( new SearchWindowContext ( GUIUtility . GUIToScreenPoint ( Event . current . mousePosition ) ) , typeMatcher ) ;
255279 }
256280 if ( resolvedTypes [ i ] != null && resolvedTypes [ i ] . ContainsGenericParameters ) {
257281 if ( subGUI [ i ] == null ) subGUI [ i ] = new TypeResolverGUI ( resolvedTypes [ i ] ) ;
0 commit comments