A game about forced loneliness, made by TACStudios
at master 136 lines 5.4 kB view raw
1using JetBrains.Annotations; 2using System; 3using System.Collections.Generic; 4using System.Diagnostics.CodeAnalysis; 5using System.Linq; 6using UnityEngine; 7using UnityEngine.Rendering; 8 9namespace UnityEditor.Rendering 10{ 11 /// <summary> 12 /// Utilities to remove <see cref="MonoBehaviour"/> implementing <see cref="IAdditionalData"/> 13 /// </summary> 14 public static class RemoveAdditionalDataUtils 15 { 16 static int s_DialogToSkip = 0; 17 18 /// <summary> 19 /// Removes a <see cref="IAdditionalData"/> and it's components defined by <see cref="RequireComponent"/> 20 /// </summary> 21 /// <param name="command">The command that is executing the removal</param> 22 /// <param name="promptDisplay">If the command must prompt a display to get user confirmation</param> 23 /// <exception cref="Exception">If the given <see cref="MonoBehaviour"/> is not an <see cref="IAdditionalData"/></exception> 24 public static void RemoveAdditionalData([DisallowNull] MenuCommand command, bool promptDisplay = true) 25 { 26 if (command.context is not Component component) 27 return; 28 29 //If the user agree to remove component, everything is removed in the current selection. 30 //So other components will not trigger this (contextual menu implementation check component existance) 31 //But if the user chose to cancel, we need to skip the prompt for a certain amount of component given by the selection size. 32 if (ShouldPrompt()) 33 RemoveAdditionalData(component, promptDisplay); 34 } 35 36 static void RemoveAdditionalData([DisallowNull] Component additionalDataComponent, bool promptDisplay = true) 37 { 38 using (ListPool<Type>.Get(out var componentTypesToRemove)) 39 { 40 if (!TryGetComponentsToRemove(additionalDataComponent as IAdditionalData, componentTypesToRemove, out var error)) 41 throw error; 42 43 if (!promptDisplay || EditorUtility.DisplayDialog( 44 title: $"Are you sure you want to proceed?", 45 message: $"This operation will also remove {string.Join($"{Environment.NewLine} - ", componentTypesToRemove)}.", 46 ok: $"Remove everything", 47 cancel: "Cancel")) 48 { 49 RemoveAdditionalDataComponentOnSelection(additionalDataComponent.GetType(), componentTypesToRemove); 50 } 51 else 52 { 53 IgnoreNextPromptsForThisSelection(); 54 } 55 } 56 } 57 58 static void IgnoreNextPromptsForThisSelection() 59 => s_DialogToSkip = Selection.count - 1; 60 61 static bool ShouldPrompt() 62 { 63 if (s_DialogToSkip > 0) 64 { 65 --s_DialogToSkip; 66 return false; 67 } 68 69 return true; 70 } 71 72 static void RemoveAdditionalDataComponentOnSelection([DisallowNull] Type additionalDataType, [DisallowNull] List<Type> componentsTypeToRemove) 73 { 74 foreach (var selectedGameObject in Selection.gameObjects) 75 { 76 RemoveAdditionalDataComponent(selectedGameObject.GetComponent(additionalDataType), componentsTypeToRemove); 77 } 78 } 79 80 static void RemoveAdditionalDataComponent([DisallowNull] Component additionalDataComponent, [DisallowNull] List<Type> componentsTypeToRemove) 81 { 82 using (ListPool<Component>.Get(out var components)) 83 { 84 // Fetch all components 85 foreach (var type in componentsTypeToRemove) 86 { 87 components.AddRange(additionalDataComponent.GetComponents(type)); 88 } 89 90 // Remove all of them 91 foreach (var mono in components) 92 { 93 RemoveComponentUtils.RemoveComponent(mono); 94 } 95 } 96 } 97 98 //internal for tests 99 [MustUseReturnValue] 100 internal static bool TryGetComponentsToRemove([DisallowNull] IAdditionalData additionalData, [DisallowNull] List<Type> componentsToRemove, [NotNullWhen(false)] out Exception error) 101 { 102 if (additionalData == null) 103 { 104 error = new ArgumentNullException(nameof(additionalData)); 105 return false; 106 } 107 108 if (componentsToRemove == null) 109 { 110 error = new ArgumentNullException(nameof(componentsToRemove)); 111 return false; 112 } 113 114 var type = additionalData.GetType(); 115 var requiredComponents = type.GetCustomAttributes(typeof(RequireComponent), true).Cast<RequireComponent>(); 116 117 if (!requiredComponents.Any()) 118 { 119 error = new Exception($"Missing attribute {typeof(RequireComponent).FullName} on type {type.FullName}"); 120 return false; 121 } 122 123 foreach (var rc in requiredComponents) 124 { 125 componentsToRemove.Add(rc.m_Type0); 126 if (rc.m_Type1 != null) 127 componentsToRemove.Add(rc.m_Type1); 128 if (rc.m_Type2 != null) 129 componentsToRemove.Add(rc.m_Type2); 130 } 131 132 error = null; 133 return true; 134 } 135 } 136}