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 Fluctio.FluctioSim.Utils.General;
using UnityEngine;
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
	{
		
		[SerializeField] public bool showInternals;

		#if UNITY_EDITOR
		
		public override void InitializePrefab()
		{
			base.InitializePrefab();
			showInternals = GeneralSettings.DefaultShowInternals;
		}

		public override void OnSelfChanged()
		{
			base.OnSelfChanged();
			SetInternalComponentsVisible(showInternals);
			SetInternalObjectsVisible(showInternals);
		}
		
		#endif

		// 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");

		/**
		 * Should return objects which would be enabled/disabled together with the main component
		 * and shown/hidden depending on <see cref="showInternals"/> value
		 */
		protected virtual IEnumerable<GameObject> GetInternalObjects()
		{
			return Array.Empty<GameObject>();
		}

		/**
		 * Should return components which would be enabled/disabled together with the main component
		 * and shown/hidden depending on <see cref="showInternals"/> value
		 */
		protected virtual IEnumerable<MonoBehaviour> GetInternalComponents()
		{
			return Array.Empty<MonoBehaviour>();
		}

		/**
		 * Should return objects which were created inside <see cref="EditorComponent.InitializeOnce"/>
		 * and therefore should be deleted when user removes this component
		 */
		protected virtual IEnumerable<Object> GetCreatedInternals()
		{
			return Array.Empty<MonoBehaviour>();
		}

		#if UNITY_EDITOR
		
		private void SetInternalComponentsVisible(bool isVisible)
		{
			var internalComponents = GetInternalComponents();
			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)
		{
			var internalObjects = GetInternalObjects();
			foreach (var internalObject in internalObjects)
			{
				internalObject.hideFlags = Flags.GetUpdatedFlags(internalObject.hideFlags, HideFlags.HideInHierarchy, !isVisible);
			}
		}

		private void SetInternalComponentsEnabled(bool isEnabled)
		{
			var internalComponents = GetInternalComponents();
			foreach (var internalComponent in internalComponents)
			{
				internalComponent.enabled = isEnabled;
			}
		}

		private void SetInternalObjectsEnabled(bool isEnabled)
		{
			var internalObjects = GetInternalObjects();
			foreach (var internalObject in internalObjects)
			{
				internalObject.SetActive(isEnabled);
			}
		}

		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 virtual void OnDestroy()
		{
			var internalsToDestroy = GetCreatedInternals();
			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)
					{
						DestroyImmediate(internalToDestroy);
					}
				}
			};
		}
		
		#endif
		
	}
}