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;
}
}
}