Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
ai.fluctio.fluctio-sim / Core / Components / Base / InternalsEditorComponent.cs
Size: Mime:
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
		
	}
}