@@ -27,17 +27,20 @@ sealed class AudioContainerWindow : EditorWindow
2727 internal readonly AudioContainerWindowState State = new ( ) ;
2828
2929 /// <summary>
30- /// Holds the added list elements in the list interaction callbacks
30+ /// Holds the added list elements in the list interaction callbacks.
31+ /// Only used locally in these methods, but it's a global member to avoid GC.
3132 /// </summary>
3233 readonly List < AudioContainerElement > m_AddedElements = new ( ) ;
3334
35+ readonly string k_EmptyGuidString = Guid . Empty . ToString ( "N" ) ;
36+
3437 VisualElement m_ContainerRootVisualElement ;
3538 VisualElement m_Day0RootVisualElement ;
3639
3740 // Preview section
3841 Label m_AssetNameLabel ;
39- Button m_PlayButton ;
40- VisualElement m_PlayButtonImage ;
42+ Button m_PlayStopButton ;
43+ VisualElement m_PlayStopButtonImage ;
4144 Button m_SkipButton ;
4245 VisualElement m_SkipButtonImage ;
4346
@@ -94,7 +97,7 @@ sealed class AudioContainerWindow : EditorWindow
9497 bool m_IsInitializing ;
9598 bool m_Day0ElementsInitialized ;
9699 bool m_ContainerElementsInitialized ;
97- private bool m_ClipFieldProgressBarsAreCleared = true ;
100+ bool m_ClipFieldProgressBarsAreCleared = true ;
98101
99102 /// <summary>
100103 /// Holds the previous state of the list elements for undo/delete housekeeping
@@ -117,8 +120,6 @@ void OnEnable()
117120 {
118121 Instance = this ;
119122
120- Undo . undoRedoPerformed += OnUndoRedoPerformed ;
121-
122123 State . TargetChanged += OnTargetChanged ;
123124 State . TransportStateChanged += OnTransportStateChanged ;
124125 State . EditorPauseStateChanged += EditorPauseStateChanged ;
@@ -143,13 +144,14 @@ void OnDisable()
143144 UnsubscribeFromClipListCallbacksAndEvents ( ) ;
144145 UnsubscribeFromAutomaticTriggerCallbacksAndEvents ( ) ;
145146
146- Undo . undoRedoPerformed -= OnUndoRedoPerformed ;
147+ EditorApplication . update -= OneTimeEditorApplicationUpdate ;
147148
148149 State . TargetChanged -= OnTargetChanged ;
149150 State . TransportStateChanged -= OnTransportStateChanged ;
150151 State . EditorPauseStateChanged -= EditorPauseStateChanged ;
151152
152153 State . OnDestroy ( ) ;
154+
153155 m_CachedElements . Clear ( ) ;
154156 m_AddedElements . Clear ( ) ;
155157 }
@@ -333,8 +335,8 @@ static void InsertUnitFieldForFloatField(VisualElement field, string unit)
333335 void InitializePreviewElements ( )
334336 {
335337 m_AssetNameLabel = UIToolkitUtilities . GetChildByName < Label > ( m_ContainerRootVisualElement , "asset-name-label" ) ;
336- m_PlayButton = UIToolkitUtilities . GetChildByName < Button > ( m_ContainerRootVisualElement , "play-button" ) ;
337- m_PlayButtonImage = UIToolkitUtilities . GetChildByName < VisualElement > ( m_ContainerRootVisualElement , "play-button-image" ) ;
338+ m_PlayStopButton = UIToolkitUtilities . GetChildByName < Button > ( m_ContainerRootVisualElement , "play-button" ) ;
339+ m_PlayStopButtonImage = UIToolkitUtilities . GetChildByName < VisualElement > ( m_ContainerRootVisualElement , "play-button-image" ) ;
338340 m_SkipButton = UIToolkitUtilities . GetChildByName < Button > ( m_ContainerRootVisualElement , "skip-button" ) ;
339341 m_SkipButtonImage = UIToolkitUtilities . GetChildByName < VisualElement > ( m_ContainerRootVisualElement , "skip-button-image" ) ;
340342
@@ -344,17 +346,17 @@ void InitializePreviewElements()
344346
345347 void SubscribeToPreviewCallbacksAndEvents ( )
346348 {
347- m_PlayButton . clicked += OnPlayStopButtonClicked ;
349+ m_PlayStopButton . clicked += OnPlayStopButtonClicked ;
348350 m_SkipButton . clicked += OnSkipButtonClicked ;
349351 }
350352
351353 void UnsubscribeFromPreviewCallbacksAndEvents ( )
352354 {
353- if ( m_PlayButton != null )
354- m_PlayButton . clicked -= OnPlayStopButtonClicked ;
355+ if ( m_PlayStopButton != null )
356+ m_PlayStopButton . clicked -= OnPlayStopButtonClicked ;
355357
356- if ( m_PlayButton != null )
357- m_PlayButton . clicked -= OnSkipButtonClicked ;
358+ if ( m_SkipButton != null )
359+ m_SkipButton . clicked -= OnSkipButtonClicked ;
358360 }
359361
360362 void BindAndTrackPreviewProperties ( )
@@ -386,15 +388,15 @@ void UpdateTransportButtonStates()
386388 {
387389 var editorIsPaused = EditorApplication . isPaused ;
388390
389- m_PlayButton ? . SetEnabled ( State . IsReadyToPlay ( ) && ! editorIsPaused ) ;
391+ m_PlayStopButton ? . SetEnabled ( State . IsReadyToPlay ( ) && ! editorIsPaused ) ;
390392 m_SkipButton ? . SetEnabled ( State . IsPlayingOrPaused ( ) && State . AudioContainer . triggerMode == AudioRandomContainerTriggerMode . Automatic && ! editorIsPaused ) ;
391393
392394 var image =
393395 State . IsPlayingOrPaused ( )
394396 ? UIToolkitUtilities . LoadIcon ( "Stop" )
395397 : UIToolkitUtilities . LoadIcon ( "Play" ) ;
396398
397- m_PlayButtonImage . style . backgroundImage = new StyleBackground ( image ) ;
399+ m_PlayStopButtonImage . style . backgroundImage = new StyleBackground ( image ) ;
398400 }
399401
400402 void OnTransportStateChanged ( object sender , EventArgs e )
@@ -435,7 +437,6 @@ void InitializeVolumeElements()
435437 volumeRandomizationMaxField . label = "" ;
436438 volumeRandomizationMaxField . formatString = "0.#" ;
437439 InsertUnitFieldForFloatField ( volumeRandomizationMaxField , "dB" ) ;
438-
439440 }
440441
441442 void SubscribeToVolumeCallbacksAndEvents ( )
@@ -794,18 +795,16 @@ void OnListItemsAdded(IEnumerable<int> indices)
794795
795796 m_AddedElements . Clear ( ) ;
796797
797- var undoName = "Add AudioContainerElement " ;
798+ var undoName = $ "Add { nameof ( AudioRandomContainer ) } element ";
798799
799800 if ( indicesArray . Length > 1 )
800801 {
801802 undoName = $ "{ undoName } s";
802803 }
803804
804805 Undo . SetCurrentGroupName ( undoName ) ;
805- SetTitle ( ) ;
806+
806807 m_AddedElements . Clear ( ) ;
807- // Force a list rebuild when the list size has changed or it will not render correctly
808- EditorApplication . update += DoDelayedListRebuild ;
809808 }
810809
811810 void OnListItemsRemoved ( IEnumerable < int > indices )
@@ -826,27 +825,25 @@ void OnListItemsRemoved(IEnumerable<int> indices)
826825
827826 State . AudioContainer . NotifyObservers ( AudioRandomContainer . ChangeEventType . List ) ;
828827
829- var undoName = "Remove AudioContainerElement " ;
828+ var undoName = $ "Remove { nameof ( AudioRandomContainer ) } element ";
830829
831830 if ( indicesArray . Length > 1 )
832831 {
833832 undoName = $ "{ undoName } s";
834833 }
835834
836835 Undo . SetCurrentGroupName ( undoName ) ;
837- SetTitle ( ) ;
838- // Force a list rebuild when the list size has changed or it will not render correctly
839- EditorApplication . update += DoDelayedListRebuild ;
840836 }
841837
842838 void OnItemListIndexChanged ( int oldIndex , int newIndex )
843839 {
840+ Undo . SetCurrentGroupName ( $ "Reorder { nameof ( AudioRandomContainer ) } list") ;
844841 State . AudioContainer . NotifyObservers ( AudioRandomContainer . ChangeEventType . List ) ;
845842 }
846843
847844 void OnAudioClipDrag ( List < AudioClip > audioClips )
848845 {
849- var undoName = "Add AudioContainerElement " ;
846+ var undoName = $ "Add { nameof ( AudioRandomContainer ) } element ";
850847
851848 if ( audioClips . Count > 1 )
852849 undoName = $ "{ undoName } s";
@@ -875,17 +872,36 @@ void OnAudioClipDrag(List<AudioClip> audioClips)
875872 Undo . RegisterCreatedObjectUndo ( element , "Create AudioContainerElement" ) ;
876873
877874 m_AddedElements . Clear ( ) ;
878-
879- SetTitle ( ) ;
880875 Undo . SetCurrentGroupName ( undoName ) ;
881- // Force a list rebuild when the list size has changed or it will not render correctly
882- EditorApplication . update += DoDelayedListRebuild ;
883876 }
884877
885878 void OnAudioClipListChanged ( SerializedProperty property )
886879 {
887- UpdateTransportButtonStates ( ) ;
880+ // Do manual fixup of orphaned subassets after a possible undo of item removal
881+ // because the undo system does not play nice with RegisterCreatedObjectUndo.
882+ if ( m_CachedElements . Count < State . AudioContainer . elements . Length )
883+ {
884+ var elements = State . AudioContainer . elements ;
885+
886+ foreach ( var elm in elements )
887+ {
888+ AssetDatabase . TryGetGUIDAndLocalFileIdentifier ( elm , out var guid , out var localId ) ;
889+
890+ // An empty asset GUID means the subasset has lost the reference
891+ // to the main asset after an undo of item removal, so re-add it manually.
892+ if ( guid . Equals ( k_EmptyGuidString ) )
893+ AssetDatabase . AddObjectToAsset ( elm , State . AudioContainer ) ;
894+ }
895+ }
896+
897+ // Update the cached list of elements
888898 m_CachedElements = State . AudioContainer . elements . ToList ( ) ;
899+
900+ // Force a list rebuild when the list has changed or it will not always render correctly
901+ m_ClipsListView . Rebuild ( ) ;
902+
903+ UpdateTransportButtonStates ( ) ;
904+ SetTitle ( ) ;
889905 }
890906
891907 void UpdateClipFieldProgressBars ( )
@@ -939,12 +955,6 @@ void ClearClipFieldProgressBars()
939955 m_ClipFieldProgressBarsAreCleared = true ;
940956 }
941957
942- void DoDelayedListRebuild ( )
943- {
944- m_ClipsListView . Rebuild ( ) ;
945- EditorApplication . update -= DoDelayedListRebuild ;
946- }
947-
948958 #endregion
949959
950960 #region TriggerAndPlaybackMode
@@ -1185,24 +1195,6 @@ void OnCountRandomizationButtonClicked()
11851195
11861196 #region GlobalEditorCallbackHandlers
11871197
1188- void OnUndoRedoPerformed ( )
1189- {
1190- if ( m_ContainerRootVisualElement == null
1191- || m_ContainerRootVisualElement . style . display == DisplayStyle . None
1192- || State . AudioContainer == null )
1193- return ;
1194-
1195- if ( m_CachedElements . Count != State . AudioContainer . elements . Length )
1196- {
1197- // Force a list rebuild when the list size has increased or it will not render correctly
1198- // if (m_CachedElements.Count < State.AudioContainer.elements.Length)
1199- m_ClipsListView . Rebuild ( ) ;
1200-
1201- SetTitle ( ) ;
1202- m_CachedElements = State . AudioContainer . elements . ToList ( ) ;
1203- }
1204- }
1205-
12061198 void OnWillSaveAssets ( IEnumerable < string > paths )
12071199 {
12081200 if ( State . AudioContainer == null )
0 commit comments