A game about forced loneliness, made by TACStudios
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}