22using System . Collections . Generic ;
33using System . Linq ;
44using System . Reflection ;
5+ using UnityEditor ;
56using UnityEditor . IMGUI . Controls ;
67using UnityEngine ;
78using Object = UnityEngine . Object ;
@@ -16,22 +17,38 @@ public sealed class CollectionItemDropdown : AdvancedDropdown
1617
1718 private readonly Type itemType ;
1819 private readonly SOCItemEditorOptionsAttribute options ;
19- private readonly Object owner ;
20+ private readonly SerializedProperty serializedProperty ;
2021 private readonly MethodInfo validationMethod ;
22+
23+ private readonly MethodInfo onSelectCallbackMethod ;
2124
2225 public CollectionItemDropdown ( AdvancedDropdownState state , Type targetItemType ,
23- SOCItemEditorOptionsAttribute options , Object owner ) : base ( state )
26+ SOCItemEditorOptionsAttribute options , SerializedProperty serializedProperty ) : base ( state )
2427 {
2528 itemType = targetItemType ;
2629 collections = CollectionsRegistry . Instance . GetCollectionsByItemType ( itemType ) ;
2730 minimumSize = new Vector2 ( 200 , 300 ) ;
2831 this . options = options ;
29- this . owner = owner ;
32+ this . serializedProperty = serializedProperty ;
3033
31-
32- if ( options != null && ! string . IsNullOrEmpty ( options . ValidateMethod ) )
34+ if ( options != null )
3335 {
34- validationMethod = owner . GetType ( ) . GetMethod ( options . ValidateMethod , new [ ] { itemType } ) ;
36+ Object owner = serializedProperty . serializedObject . targetObject ;
37+ if ( ! string . IsNullOrEmpty ( options . ValidateMethod ) )
38+ validationMethod = owner . GetType ( ) . GetMethod ( options . ValidateMethod , new [ ] { itemType } ) ;
39+
40+ // If it's specified that a callback should be fired when an item is selected, cache that callback.
41+ if ( ! string . IsNullOrEmpty ( options . OnSelectCallbackMethod ) )
42+ {
43+ onSelectCallbackMethod = owner . GetType ( ) . GetMethod ( options . OnSelectCallbackMethod ,
44+ BindingFlags . Instance | BindingFlags . Static | BindingFlags . Public | BindingFlags . NonPublic ,
45+ null , new [ ] { itemType , itemType } , null ) ;
46+ if ( onSelectCallbackMethod == null )
47+ {
48+ Debug . LogWarning ( $ "Component '{ owner . name } ' wants selection callback " +
49+ $ "'{ options . OnSelectCallbackMethod } ' which is not a valid method.") ;
50+ }
51+ }
3552 }
3653 }
3754
@@ -42,19 +59,49 @@ protected override AdvancedDropdownItem BuildRoot()
4259 root . AddChild ( new AdvancedDropdownItem ( "None" ) ) ;
4360 root . AddSeparator ( ) ;
4461
62+ // If specified, limit the displayed items to those of a collection specified in a certain field.
63+ ScriptableObjectCollection collectionToConstrainTo = null ;
64+ if ( ! string . IsNullOrEmpty ( options . ConstrainToCollectionField ) )
65+ {
66+ SerializedProperty collectionField = serializedProperty . serializedObject . FindProperty (
67+ options . ConstrainToCollectionField ) ;
68+ if ( collectionField == null )
69+ {
70+ Debug . LogWarning ( $ "Tried to constrain dropdown to collection specified in field " +
71+ $ "'{ options . ConstrainToCollectionField } ' but no such field existed in " +
72+ $ "'{ serializedProperty . serializedObject . targetObject } '") ;
73+ return root ;
74+ }
75+
76+ collectionToConstrainTo = collectionField . objectReferenceValue as ScriptableObjectCollection ;
77+ if ( collectionToConstrainTo == null )
78+ {
79+ Debug . LogWarning ( $ "Tried to constrain dropdown to collection specified in field " +
80+ $ "'{ options . ConstrainToCollectionField } ' but no collection was specified.") ;
81+ return root ;
82+ }
83+ }
84+ bool shouldConstrainToCollection = collectionToConstrainTo != null ;
85+
4586 AdvancedDropdownItem targetParent = root ;
4687 bool multipleCollections = collections . Count > 1 ;
4788 for ( int i = 0 ; i < collections . Count ; i ++ )
4889 {
4990 ScriptableObjectCollection collection = collections [ i ] ;
91+
92+ // If we're meant to constrain the selection to a specific collection, enforce that now.
93+ if ( shouldConstrainToCollection && collectionToConstrainTo != collection )
94+ continue ;
5095
51- if ( multipleCollections )
96+ // If there are multiple collections, group them together.
97+ if ( multipleCollections && ! shouldConstrainToCollection )
5298 {
5399 AdvancedDropdownItem collectionParent = new AdvancedDropdownItem ( collection . name ) ;
54100 root . AddChild ( collectionParent ) ;
55101 targetParent = collectionParent ;
56102 }
57103
104+ // Add every individual item in the collection.
58105 for ( int j = 0 ; j < collection . Count ; j ++ )
59106 {
60107 ScriptableObject collectionItem = collection [ j ] ;
@@ -64,11 +111,12 @@ protected override AdvancedDropdownItem BuildRoot()
64111
65112 if ( validationMethod != null )
66113 {
67- bool result = ( bool ) validationMethod . Invoke ( owner , new object [ ] { collectionItem } ) ;
114+ bool result = ( bool ) validationMethod . Invoke (
115+ serializedProperty . serializedObject . targetObject , new object [ ] { collectionItem } ) ;
68116 if ( ! result )
69117 continue ;
70118 }
71-
119+
72120 targetParent . AddChild ( new CollectionItemDropdownItem ( collectionItem ) ) ;
73121 }
74122 }
@@ -81,22 +129,57 @@ protected override AdvancedDropdownItem BuildRoot()
81129 return root ;
82130 }
83131
132+ private void InvokeOnSelectCallback ( ScriptableObject from , ScriptableObject to )
133+ {
134+ if ( onSelectCallbackMethod == null )
135+ return ;
136+
137+ object [ ] arguments = { from , to } ;
138+
139+ // The method may be static, in which case there is no target.
140+ if ( onSelectCallbackMethod . IsStatic )
141+ {
142+ onSelectCallbackMethod . Invoke ( null , arguments ) ;
143+ return ;
144+ }
145+
146+ // Otherwise, fire the callback on every target.
147+ for ( int i = 0 ; i < serializedProperty . serializedObject . targetObjects . Length ; i ++ )
148+ {
149+ object target = serializedProperty . serializedObject . targetObjects [ i ] ;
150+ onSelectCallbackMethod . Invoke ( target , arguments ) ;
151+ }
152+ }
153+
84154 protected override void ItemSelected ( AdvancedDropdownItem item )
85155 {
86156 base . ItemSelected ( item ) ;
87157
158+ ScriptableObject previousValue = null ;
159+ if ( onSelectCallbackMethod != null )
160+ previousValue = serializedProperty . objectReferenceValue as ScriptableObject ;
161+
88162 if ( item . name . Equals ( CREATE_NEW_TEXT , StringComparison . OrdinalIgnoreCase ) )
89163 {
90164 ScriptableObjectCollection collection = collections . First ( ) ;
91165 ScriptableObject newItem = CollectionCustomEditor . AddNewItem ( collection , itemType ) ;
92166 callback . Invoke ( newItem ) ;
167+
168+ InvokeOnSelectCallback ( previousValue , newItem ) ;
169+
93170 return ;
94171 }
95172
96173 if ( item is CollectionItemDropdownItem dropdownItem )
174+ {
97175 callback . Invoke ( dropdownItem . CollectionItem ) ;
176+ InvokeOnSelectCallback ( previousValue , dropdownItem . CollectionItem ) ;
177+ }
98178 else
179+ {
99180 callback . Invoke ( null ) ;
181+ InvokeOnSelectCallback ( previousValue , null ) ;
182+ }
100183 }
101184
102185 public void Show ( Rect rect , Action < ScriptableObject > onSelectedCallback )
0 commit comments