Repository URL to install this package:
|
Version:
1.3.2 ▾
|
using System;
using System.Collections.Generic;
using System.Linq;
using Fluctio.FluctioSim.Utils.General;
using UnityEngine;
using Component = UnityEngine.Component;
using Object = UnityEngine.Object;
#if UNITY_EDITOR
using Fluctio.FluctioSim.EditorCore.EditorGeneral;
using UnityEditor;
#endif
namespace Fluctio.FluctioSim.Core.Components.Base
{
/// <summary>
/// Base class for editor components which have dependent components/objects.
/// Has ability to show/hide dependents and automatic enable/disable/destroy management.
/// </summary>
public abstract class InternalsEditorComponent : EditorComponent
{
#region Disable Reset
// Reset breaks saved references to created objects
[ContextMenu("Reset", true)]
private bool ResetValidate() => false;
[ContextMenu("Reset", false)]
private void ResetMenu() => throw new InvalidOperationException("Reset is not available on this component");
#endregion
#region Internals tracking
[SerializeField] public bool showInternals;
[SerializeField, HideInInspector] private List<GameObject> internalObjects = new();
[SerializeField, HideInInspector] private List<Behaviour> internalComponents = new();
protected GameObject CreateInternalObject(string objectName)
{
var createdGameObject = new GameObject(objectName);
#if UNITY_EDITOR
Undo.RegisterCreatedObjectUndo(createdGameObject, $"Create object {objectName}");
Undo.RegisterFullObjectHierarchyUndo(createdGameObject, $"Create object {objectName}");
#endif
createdGameObject.transform.SetParent(transform);
internalObjects.Add(createdGameObject);
return createdGameObject;
}
protected Component CreateInternalComponent(Type componentType, GameObject componentParent)
{
if (!componentType.IsSubclassOf(typeof(Behaviour)))
{
throw new ArgumentException(
$"Component type {componentType.Name} is not a subclass of Behaviour",
nameof(componentType));
}
var createdComponent = (Behaviour)componentParent.AddComponent(componentType);
#if UNITY_EDITOR
Undo.RegisterCreatedObjectUndo(createdComponent, $"Create component {componentType.Name}");
#endif
if (!internalObjects.Contains(componentParent))
{
internalComponents.Add(createdComponent);
}
return createdComponent;
}
protected T CreateInternalComponent<T>(GameObject componentParent) where T : Behaviour =>
CreateInternalComponent(typeof(T), componentParent) as T;
protected void DestroyInternal(Object internalObject)
{
#if UNITY_EDITOR
var internalObjectName = internalObject is GameObject ? internalObject.name : internalObject.GetType().Name;
Undo.RegisterFullObjectHierarchyUndo(internalObject, $"Delete {internalObjectName}");
#endif
EditorStubs.Destroy(internalObject);
}
#endregion
#if UNITY_EDITOR
#region Enabling and visibility
private void SetInternalComponentsVisible(bool isVisible)
{
foreach (var internalComponent in internalComponents)
{
internalComponent.hideFlags = Flags.GetUpdatedFlags(internalComponent.hideFlags, HideFlags.HideInInspector, !isVisible);
// There is a bug where hideFlags are not applied immediately
// The following workarounds did not work:
// * EditorSceneManagement.MarkSceneDirty() (https://issuetracker.unity3d.com/issues/gameobjects-in-hierarchy-window-are-not-hidden-when-using-hideflags-dot-hideinhierarchy)
// * gameObject.SetActive(false); gameObject.SetActive(true); (https://forum.unity.com/threads/hideflags-hideinhierarchy-not-working-broken.428056/, https://forum.unity.com/threads/hideflags-still-broken.107975/)
// * internalComponent.enabled = false; internalComponent.enabled = true;
// * UnityEditorInternal.InternalEditorUtility.RepaintAllViews() and EditorApplication.RepaintHierarchyWindow() (https://forum.unity.com/threads/bug-g-gameobject-hideflags-hideflags-hideinhierarchy-does-nut-update-hierarchy.704078/)
// * EditorApplication.RepaintHierarchyWindow() and EditorApplication.DirtyHierarchyWindowSorting() (https://discussions.unity.com/t/hideflags-hideinhierarchy-not-updating-hierarchy-in-edit-mode/177089)
// * ActiveEditorTracker.sharedTracker.activeEditors to repaint all active editors (https://discussions.unity.com/t/how-to-repaint-from-a-property-drawer/77599/7)
// * InspectorWindow.RepaintAllInsepctors() via reflection
// Do you want to try one more time or is it time to file a bug report already?
EditorApplication.delayCall += () =>
{
if (internalComponent == null)
{
return;
}
internalComponent.hideFlags = Flags.GetUpdatedFlags(internalComponent.hideFlags, HideFlags.HideInInspector, !isVisible);
};
}
}
private void SetInternalObjectsVisible(bool isVisible)
{
foreach (var internalObject in internalObjects)
{
internalObject.hideFlags = Flags.GetUpdatedFlags(internalObject.hideFlags, HideFlags.HideInHierarchy, !isVisible);
}
}
private void SetInternalComponentsEnabled(bool isEnabled)
{
foreach (var internalComponent in internalComponents)
{
internalComponent.enabled = isEnabled;
}
}
private void SetInternalObjectsEnabled(bool isEnabled)
{
foreach (var internalObject in internalObjects)
{
internalObject.SetActive(isEnabled);
}
}
#endregion
#region Unity message handers
public override void InitializePrefab()
{
base.InitializePrefab();
showInternals = GeneralSettings.DefaultShowInternals;
}
public override void OnSelfChanged()
{
base.OnSelfChanged();
SetInternalComponentsVisible(showInternals);
SetInternalObjectsVisible(showInternals);
}
protected virtual void OnEnable()
{
if (!ShouldExecute(allowInPlayMode: true))
{
return;
}
SetInternalComponentsEnabled(true);
SetInternalObjectsEnabled(true);
}
protected virtual void OnDisable()
{
if (!ShouldExecute(allowInPlayMode: true))
{
return;
}
SetInternalComponentsEnabled(false);
SetInternalObjectsEnabled(false);
}
protected override void OnDestroy()
{
base.OnDestroy();
var internalsToDestroy = Enumerable.Empty<Object>()
.Concat(internalObjects)
.Concat(internalComponents)
.Reverse();
var isPlaying = Application.isPlaying;
EditorApplication.delayCall += () =>
{
if (Application.isPlaying != isPlaying)
{
// Skip destroys caused by play mode change
// Without this check everything would be deleted in wrong mode due to delayCall
return;
}
foreach (var internalToDestroy in internalsToDestroy)
{
if (internalToDestroy == null)
{
continue;
}
Undo.RegisterFullObjectHierarchyUndo(internalToDestroy, $"Destroy {internalToDestroy.name}");
DestroyImmediate(internalToDestroy);
}
};
}
#endregion
#endif
}
}