using JetBrains.Annotations; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using UnityEngine; using UnityEngine.Rendering; namespace UnityEditor.Rendering { /// /// Utilities to remove implementing /// public static class RemoveAdditionalDataUtils { static int s_DialogToSkip = 0; /// /// Removes a and it's components defined by /// /// The command that is executing the removal /// If the command must prompt a display to get user confirmation /// If the given is not an public static void RemoveAdditionalData([DisallowNull] MenuCommand command, bool promptDisplay = true) { if (command.context is not Component component) return; //If the user agree to remove component, everything is removed in the current selection. //So other components will not trigger this (contextual menu implementation check component existance) //But if the user chose to cancel, we need to skip the prompt for a certain amount of component given by the selection size. if (ShouldPrompt()) RemoveAdditionalData(component, promptDisplay); } static void RemoveAdditionalData([DisallowNull] Component additionalDataComponent, bool promptDisplay = true) { using (ListPool.Get(out var componentTypesToRemove)) { if (!TryGetComponentsToRemove(additionalDataComponent as IAdditionalData, componentTypesToRemove, out var error)) throw error; if (!promptDisplay || EditorUtility.DisplayDialog( title: $"Are you sure you want to proceed?", message: $"This operation will also remove {string.Join($"{Environment.NewLine} - ", componentTypesToRemove)}.", ok: $"Remove everything", cancel: "Cancel")) { RemoveAdditionalDataComponentOnSelection(additionalDataComponent.GetType(), componentTypesToRemove); } else { IgnoreNextPromptsForThisSelection(); } } } static void IgnoreNextPromptsForThisSelection() => s_DialogToSkip = Selection.count - 1; static bool ShouldPrompt() { if (s_DialogToSkip > 0) { --s_DialogToSkip; return false; } return true; } static void RemoveAdditionalDataComponentOnSelection([DisallowNull] Type additionalDataType, [DisallowNull] List componentsTypeToRemove) { foreach (var selectedGameObject in Selection.gameObjects) { RemoveAdditionalDataComponent(selectedGameObject.GetComponent(additionalDataType), componentsTypeToRemove); } } static void RemoveAdditionalDataComponent([DisallowNull] Component additionalDataComponent, [DisallowNull] List componentsTypeToRemove) { using (ListPool.Get(out var components)) { // Fetch all components foreach (var type in componentsTypeToRemove) { components.AddRange(additionalDataComponent.GetComponents(type)); } // Remove all of them foreach (var mono in components) { RemoveComponentUtils.RemoveComponent(mono); } } } //internal for tests [MustUseReturnValue] internal static bool TryGetComponentsToRemove([DisallowNull] IAdditionalData additionalData, [DisallowNull] List componentsToRemove, [NotNullWhen(false)] out Exception error) { if (additionalData == null) { error = new ArgumentNullException(nameof(additionalData)); return false; } if (componentsToRemove == null) { error = new ArgumentNullException(nameof(componentsToRemove)); return false; } var type = additionalData.GetType(); var requiredComponents = type.GetCustomAttributes(typeof(RequireComponent), true).Cast(); if (!requiredComponents.Any()) { error = new Exception($"Missing attribute {typeof(RequireComponent).FullName} on type {type.FullName}"); return false; } foreach (var rc in requiredComponents) { componentsToRemove.Add(rc.m_Type0); if (rc.m_Type1 != null) componentsToRemove.Add(rc.m_Type1); if (rc.m_Type2 != null) componentsToRemove.Add(rc.m_Type2); } error = null; return true; } } }