@@ -61,12 +61,14 @@ public abstract class UiConfigsEditor<TSet> : Editor
6161
6262 private const string UiSetExplanation =
6363 "UI Set Configurations\n \n " +
64- "UI Sets group multiple presenters that should be displayed together. " +
64+ "UI Sets group multiple presenter instances that should be displayed together. " +
6565 "When a set is activated via UiService, all its presenters are loaded and shown simultaneously. " +
66- "Presenters are loaded in the order listed (top to bottom)." ;
66+ "Presenters are loaded in the order listed (top to bottom).\n \n " +
67+ "You can add the same UI type multiple times with different instance addresses for multi-instance support." ;
6768
6869 private Dictionary < string , string > _assetPathLookup ;
6970 private List < string > _uiConfigsAddress ;
71+ private Dictionary < string , Type > _uiTypesByAddress ; // Maps addressable address to Type
7072 private UiConfigs _scriptableObject ;
7173 private SerializedProperty _configsProperty ;
7274 private SerializedProperty _setsProperty ;
@@ -214,12 +216,14 @@ private VisualElement CreateConfigElement()
214216 container . style . paddingTop = 2 ;
215217 container . style . paddingBottom = 2 ;
216218
219+ // UiPresenter Addressable Address
217220 var label = new Label ( ) ;
218221 label . style . flexGrow = 1 ;
219222 label . style . paddingLeft = 5 ;
220223 label . style . unityTextAlign = TextAnchor . MiddleLeft ;
221224 container . Add ( label ) ;
222225
226+ // Layer field
223227 var layerField = new IntegerField ( ) ;
224228 layerField . style . width = 80 ;
225229 layerField . style . marginRight = 5 ;
@@ -245,14 +249,23 @@ private VisualElement CreateSetPresenterElement()
245249 dragHandle . tooltip = "Drag to reorder" ;
246250 container . Add ( dragHandle ) ;
247251
248- // Dropdown for selecting UI presenter
252+ // Dropdown for selecting UI presenter type
249253 var dropdown = new DropdownField ( ) ;
250254 dropdown . choices = new List < string > ( _uiConfigsAddress ?? new List < string > ( ) ) ;
251255 dropdown . style . flexGrow = 1 ;
252256 dropdown . style . paddingTop = 3 ;
253257 dropdown . style . paddingBottom = 3 ;
258+ dropdown . name = "ui-type-dropdown" ;
254259 container . Add ( dropdown ) ;
255260
261+ // Instance address field (optional)
262+ var instanceField = new TextField ( ) ;
263+ instanceField . style . width = 120 ;
264+ instanceField . style . marginLeft = 5 ;
265+ instanceField . tooltip = "Optional instance address (leave empty for default instance)" ;
266+ instanceField . name = "instance-address-field" ;
267+ container . Add ( instanceField ) ;
268+
256269 // Delete button
257270 var deleteButton = new Button { text = "×" } ;
258271 deleteButton . style . width = 25 ;
@@ -286,9 +299,9 @@ private VisualElement CreateSetElement(string setName, int setIndex)
286299 header . style . marginBottom = 5 ;
287300 setContainer . Add ( header ) ;
288301
289- // Get the property for this set's UI configs
302+ // Get the property for this set's UI entries
290303 var setProperty = _setsProperty . GetArrayElementAtIndex ( setIndex ) ;
291- var uiConfigsAddressProperty = setProperty . FindPropertyRelative ( nameof ( UiSetConfigSerializable . UiConfigsAddress ) ) ;
304+ var uiEntriesProperty = setProperty . FindPropertyRelative ( nameof ( UiSetConfigSerializable . UiEntries ) ) ;
292305
293306 // ListView for presenters in this set
294307 var presenterListView = new ListView
@@ -301,13 +314,13 @@ private VisualElement CreateSetElement(string setName, int setIndex)
301314 fixedItemHeight = 28
302315 } ;
303316
304- presenterListView . BindProperty ( uiConfigsAddressProperty ) ;
317+ presenterListView . BindProperty ( uiEntriesProperty ) ;
305318
306319 presenterListView . makeItem = CreateSetPresenterElement ;
307- presenterListView . bindItem = ( element , index ) => BindSetPresenterElement ( element , index , uiConfigsAddressProperty , presenterListView ) ;
320+ presenterListView . bindItem = ( element , index ) => BindSetPresenterElement ( element , index , uiEntriesProperty , presenterListView ) ;
308321
309322 // Register callbacks to save changes when items are added, removed, or reordered
310- presenterListView . itemsAdded += indices => OnPresenterItemsAdded ( indices , uiConfigsAddressProperty ) ;
323+ presenterListView . itemsAdded += indices => OnPresenterItemsAdded ( indices , uiEntriesProperty ) ;
311324 presenterListView . itemsRemoved += _ => SaveSetChanges ( ) ;
312325 presenterListView . itemIndexChanged += ( _ , _ ) => SaveSetChanges ( ) ;
313326
@@ -316,21 +329,40 @@ private VisualElement CreateSetElement(string setName, int setIndex)
316329 return setContainer ;
317330 }
318331
319- private void BindSetPresenterElement ( VisualElement element , int index , SerializedProperty uiConfigsAddressProperty , ListView listView )
332+ private void BindSetPresenterElement ( VisualElement element , int index , SerializedProperty uiEntriesProperty , ListView listView )
320333 {
321- if ( index >= uiConfigsAddressProperty . arraySize )
334+ if ( index >= uiEntriesProperty . arraySize )
322335 return ;
323336
324- var dropdown = element . Q < DropdownField > ( ) ;
325- if ( dropdown == null )
337+ var dropdown = element . Q < DropdownField > ( "ui-type-dropdown" ) ;
338+ var instanceField = element . Q < TextField > ( "instance-address-field" ) ;
339+ if ( dropdown == null || instanceField == null )
326340 return ;
327341
328- var itemProperty = uiConfigsAddressProperty . GetArrayElementAtIndex ( index ) ;
342+ var entryProperty = uiEntriesProperty . GetArrayElementAtIndex ( index ) ;
343+ var typeNameProperty = entryProperty . FindPropertyRelative ( nameof ( UiSetEntry . UiTypeName ) ) ;
344+ var instanceAddressProperty = entryProperty . FindPropertyRelative ( nameof ( UiSetEntry . InstanceAddress ) ) ;
345+
346+ // Find the matching address for this type
347+ var currentTypeName = typeNameProperty . stringValue ;
348+ Type currentType = string . IsNullOrEmpty ( currentTypeName ) ? null : Type . GetType ( currentTypeName ) ;
349+
350+ // Find the address that matches this type
351+ string matchingAddress = null ;
352+ if ( currentType != null && _uiTypesByAddress != null )
353+ {
354+ foreach ( var kvp in _uiTypesByAddress )
355+ {
356+ if ( kvp . Value == currentType )
357+ {
358+ matchingAddress = kvp . Key ;
359+ break ;
360+ }
361+ }
362+ }
329363
330- // Find the index in our address list
331- var currentAddress = itemProperty . stringValue ;
332- var selectedIndex = string . IsNullOrEmpty ( currentAddress ) ? 0 :
333- _uiConfigsAddress . FindIndex ( address => address == currentAddress ) ;
364+ var selectedIndex = string . IsNullOrEmpty ( matchingAddress ) ? 0 :
365+ _uiConfigsAddress . FindIndex ( address => address == matchingAddress ) ;
334366
335367 if ( selectedIndex < 0 )
336368 selectedIndex = 0 ;
@@ -339,45 +371,87 @@ private void BindSetPresenterElement(VisualElement element, int index, Serialize
339371 {
340372 // Unbind to prevent stale property references
341373 dropdown . Unbind ( ) ;
374+ instanceField . Unbind ( ) ;
342375
343- // Set the current value
376+ // Set the current values
344377 dropdown . index = selectedIndex ;
378+ instanceField . value = instanceAddressProperty . stringValue ?? string . Empty ;
345379
346- // Register callback to store address when changed
380+ // Register callback to store type when changed
347381 dropdown . RegisterValueChangedCallback ( evt =>
348382 {
349383 var newIndex = dropdown . index ;
350384 if ( newIndex >= 0 && newIndex < _uiConfigsAddress . Count )
351385 {
352- itemProperty . stringValue = _uiConfigsAddress [ newIndex ] ;
353- SaveSetChanges ( ) ;
386+ var selectedAddress = _uiConfigsAddress [ newIndex ] ;
387+ if ( _uiTypesByAddress . TryGetValue ( selectedAddress , out var selectedType ) )
388+ {
389+ typeNameProperty . stringValue = selectedType . AssemblyQualifiedName ;
390+ SaveSetChanges ( ) ;
391+ }
354392 }
355393 } ) ;
356394
395+ // Register callback for instance address field
396+ instanceField . RegisterValueChangedCallback ( evt =>
397+ {
398+ instanceAddressProperty . stringValue = evt . newValue ?? string . Empty ;
399+ SaveSetChanges ( ) ;
400+ } ) ;
401+
357402 // Set initial value if property is empty
358- if ( string . IsNullOrEmpty ( itemProperty . stringValue ) && selectedIndex < _uiConfigsAddress . Count )
403+ if ( string . IsNullOrEmpty ( typeNameProperty . stringValue ) && selectedIndex < _uiConfigsAddress . Count )
404+ {
405+ var address = _uiConfigsAddress [ selectedIndex ] ;
406+ if ( _uiTypesByAddress . TryGetValue ( address , out var type ) )
407+ {
408+ typeNameProperty . stringValue = type . AssemblyQualifiedName ;
409+ serializedObject . ApplyModifiedProperties ( ) ;
410+ }
411+ }
412+ }
413+
414+ // Setup delete button to remove this item from the set
415+ var deleteButton = element . Q < Button > ( "delete-button" ) ;
416+ if ( deleteButton != null )
417+ {
418+ // Store the click handler in userData to unregister it later if needed
419+ if ( deleteButton . userData is EventCallback < ClickEvent > previousCallback )
359420 {
360- itemProperty . stringValue = _uiConfigsAddress [ selectedIndex ] ;
361- serializedObject . ApplyModifiedProperties ( ) ;
421+ deleteButton . UnregisterCallback ( previousCallback ) ;
362422 }
423+
424+ EventCallback < ClickEvent > clickHandler = _ =>
425+ {
426+ uiEntriesProperty . DeleteArrayElementAtIndex ( index ) ;
427+ SaveSetChanges ( ) ;
428+ } ;
429+
430+ deleteButton . userData = clickHandler ;
431+ deleteButton . RegisterCallback ( clickHandler ) ;
363432 }
364433 }
365434
366- private void OnPresenterItemsAdded ( IEnumerable < int > indices , SerializedProperty uiConfigsAddressProperty )
435+ private void OnPresenterItemsAdded ( IEnumerable < int > indices , SerializedProperty uiEntriesProperty )
367436 {
368- if ( _uiConfigsAddress == null || _uiConfigsAddress . Count == 0 )
437+ if ( _uiConfigsAddress == null || _uiConfigsAddress . Count == 0 || _uiTypesByAddress == null )
369438 {
370439 return ;
371440 }
372441
373442 var defaultAddress = _uiConfigsAddress [ 0 ] ;
443+ Type defaultType = _uiTypesByAddress . TryGetValue ( defaultAddress , out var type ) ? type : null ;
374444
375445 foreach ( var index in indices )
376446 {
377- if ( index < uiConfigsAddressProperty . arraySize )
447+ if ( index < uiEntriesProperty . arraySize )
378448 {
379- var itemProperty = uiConfigsAddressProperty . GetArrayElementAtIndex ( index ) ;
380- itemProperty . stringValue = defaultAddress ;
449+ var entryProperty = uiEntriesProperty . GetArrayElementAtIndex ( index ) ;
450+ var typeNameProperty = entryProperty . FindPropertyRelative ( nameof ( UiSetEntry . UiTypeName ) ) ;
451+ var instanceAddressProperty = entryProperty . FindPropertyRelative ( nameof ( UiSetEntry . InstanceAddress ) ) ;
452+
453+ typeNameProperty . stringValue = defaultType ? . AssemblyQualifiedName ?? string . Empty ;
454+ instanceAddressProperty . stringValue = string . Empty ;
381455 }
382456 }
383457
@@ -437,6 +511,16 @@ private void SyncConfigsWithAddressables()
437511 _scriptableObject . Configs = configs ;
438512 _uiConfigsAddress = uiConfigsAddress ;
439513 _assetPathLookup = assetPathLookup ;
514+
515+ // Build Type lookup dictionary
516+ _uiTypesByAddress = new Dictionary < string , Type > ( ) ;
517+ foreach ( var config in configs )
518+ {
519+ if ( ! string . IsNullOrEmpty ( config . AddressableAddress ) && config . UiType != null )
520+ {
521+ _uiTypesByAddress [ config . AddressableAddress ] = config . UiType ;
522+ }
523+ }
440524
441525 EditorUtility . SetDirty ( _scriptableObject ) ;
442526 AssetDatabase . SaveAssets ( ) ;
0 commit comments