// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) VRMADA, All rights reserved.
//
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using UltimateXR.Core;
using UnityEngine;
using UnityEngine.SceneManagement;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UltimateXR.Extensions.Unity
{
///
/// extensions.
///
public static class ComponentExt
{
#region Public Methods
///
/// Controls whether to show a given component in the inspector.
///
/// The component to show
/// Whether to show the component or now
public static void ShowInInspector(this Component self, bool show = true)
{
#if UNITY_EDITOR
if (Application.isEditor)
{
SerializedObject so = new SerializedObject(self);
SerializedProperty prop = so.FindProperty(UxrConstants.Editor.PropertyObjectHideFlags);
if (show)
{
prop.intValue &= (int)~HideFlags.HideInInspector;
}
else
{
prop.intValue |= (int)HideFlags.HideInInspector;
}
so.ApplyModifiedProperties();
return;
}
#endif
if (show)
{
self.hideFlags &= ~HideFlags.HideInInspector;
}
else
{
self.hideFlags |= HideFlags.HideInInspector;
}
}
///
/// Controls whether to show a given component in the inspector and whether it is editable.
///
/// The object to set
/// Whether to show it in the inspector
/// Whether it is editable
public static void ShowInInspector(this Component self, bool show, bool editable)
{
#if UNITY_EDITOR
SerializedObject so = new SerializedObject(self);
SerializedProperty prop = so.FindProperty(UxrConstants.Editor.PropertyObjectHideFlags);
if (show)
{
prop.intValue &= (int)~HideFlags.HideInInspector;
}
else
{
prop.intValue |= (int)HideFlags.HideInInspector;
}
if (editable)
{
prop.intValue &= (int)~HideFlags.NotEditable;
}
else
{
prop.intValue |= (int)HideFlags.NotEditable;
}
so.ApplyModifiedProperties();
#else
if (show)
{
self.hideFlags &= ~HideFlags.HideInInspector;
}
else
{
self.hideFlags |= HideFlags.HideInInspector;
}
if (editable)
{
self.hideFlags &= ~HideFlags.NotEditable;
}
else
{
self.hideFlags |= HideFlags.NotEditable;
}
#endif
}
///
/// Checks whether the component is in a prefab.
///
/// Component to check
/// Whether the component is in a prefab
public static bool IsInPrefab(this Component self)
{
return self.gameObject.IsInPrefab();
}
#if UNITY_EDITOR
///
/// Gets the GUID of the prefab the component is in, if it is in a prefab, or the GUID of the prefab the component was
/// instantiated from, if it was instantiated from a prefab.
/// If the component is not in a prefab and doesn't have a source prefab either, it will return string.Empty.
///
/// If the call was successful, returns the GUID or string.Empty
/// Whether the prefab GUID could be retrieved
///
/// The reason the call can be unsuccessful is because Unity for some reason will report
/// a null/empty asset path even though PrefabUtility.IsPartOfPrefabAsset() returns true.
/// This behaviour happens when in prefab isolation/context mode in the editor
///
public static bool GetPrefabGuid(this Component self, out string prefabGuid)
{
return self.gameObject.GetPrefabGuid(out prefabGuid, out string _);
}
///
/// Same as but it also returns the asset path if it exists.
///
/// If the call was successful, returns the GUID or string.Empty
/// If the call was successful, returns the asset path or string.Empty
/// Whether the prefab GUID could be retrieved
///
/// The reason the call can be unsuccessful is because Unity for some reason will report
/// a null/empty asset path even though PrefabUtility.IsPartOfPrefabAsset() returns true.
/// This behaviour happens when in prefab isolation/context mode in the editor
///
public static bool GetPrefabGuid(this Component self, out string prefabGuid, out string assetPath)
{
return self.gameObject.GetPrefabGuid(out prefabGuid, out assetPath);
}
#endif
///
/// Gets the Component of a given type. If it doesn't exist, it is added to the GameObject.
///
/// Component whose GameObject will be used to retrieve or add the given component type to
/// Component type to get or add
/// Existing component or newly added if it didn't exist before
public static T GetOrAddComponent(this Component self) where T : Component
{
T component = self.GetComponent();
if (component == null)
{
#if UNITY_EDITOR
if (Application.isPlaying)
{
component = self.gameObject.AddComponent();
}
else
{
component = Undo.AddComponent(self.gameObject);
}
#else
component = self.gameObject.AddComponent();
#endif
}
return component;
}
///
/// Gets the Component of a given type in the GameObject or any of its parents. It also works on prefabs, where regular
/// will not work:
/// https://issuetracker.unity3d.com/issues/getcomponentinparent-is-returning-null-when-the-gameobject-is-a-prefab
///
/// type to get
/// Component in same GameObject or any of its parents. Null if it wasn't found
public static T SafeGetComponentInParent(this Component self)
{
return self.GetComponentInParent() ?? self.GetComponentsInParent(true).FirstOrDefault();
}
///
/// Gets the full path under current scene, including all parents, but scene name, for the given component.
///
///
/// The path generated might not be unique. If that is the purpose, use instead.
///
/// to get the path for
///
/// Optional Transform to get the path relative to. If it's not the same Transform or a Transform up in the hierarchy
/// it will return the full path
///
/// Component path string
public static string GetPathUnderScene(this Component self, Transform relativeTo = null)
{
string path = self.transform.GetPathUnderScene(relativeTo);
return self is Transform ? path : $"{path}/{self.GetType().Name}";
}
///
/// Gets an unique path in the scene for the given component. It will include scene name, sibling and component indices
/// to make it unique.
///
/// to get the unique path for
///
/// Optional Transform to get the path relative to. If it's not the same Transform or a Transform up in the hierarchy
/// it will return the full path
///
/// Unique component path string
public static string GetUniqueScenePath(this Component self, Transform relativeTo = null)
{
string path = self.transform.GetUniqueScenePath(relativeTo);
return self is Transform ? path : $"{path}/{Array.IndexOf(self.GetComponents(), self):00}{self.GetType().Name}";
}
///
/// Gets an unique identifier string for the given component.
///
/// Generates an 8 characters hexadecimal hash code of .
/// to get UID for.
/// 8 characters hexadecimal unique identifier
public static string GetSceneUid(this Component self)
{
return self.GetUniqueScenePath().GetHashCode().ToString("x8");
}
///
/// Gets a list of all components of the given type in the open scenes
///
/// Type of component to look for
/// Whether to include inactive components or not
/// List of components
public static List GetAllComponentsInOpenScenes(bool includeInactive) where T : Component
{
List listResult = new List();
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene s = SceneManager.GetSceneAt(i);
if (s.isLoaded)
{
GameObject[] rootGameObjects = s.GetRootGameObjects();
foreach (GameObject go in rootGameObjects)
{
listResult.AddRange(go.GetComponentsInChildren(includeInactive));
}
}
}
return listResult;
}
///
/// From a set of components, returns which one of them has a transform that is a common root of all.
/// The transform must be the transform of a component in the list.
///
/// Components whose transforms to check
///
/// Returns which transform from all the components passed as parameters is a common root of all. If no component has a
/// transform that is a common root it returns null.
///
public static T GetCommonRootComponentFromSet(params T[] components) where T : Component
{
T commonRoot = null;
for (int i = 0; i < components.Length; i++)
{
if (i == 0)
{
commonRoot = components[i];
}
else
{
if (commonRoot == null || (components[i] != commonRoot && components[i].transform.HasParent(commonRoot.transform) == false))
{
bool found = true;
for (int j = 0; j < i - 1; j++)
{
if (components[i] != components[j] && components[j].transform.HasParent(components[i].transform) == false)
{
found = false;
}
}
commonRoot = found ? components[i] : null;
}
}
}
return commonRoot;
}
#endregion
}
}