rabidus-test/Assets/SCSM/SciFiShipController/Editor/SSCEditorHelper.cs

901 lines
35 KiB
C#

// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
public class SSCEditorHelper
{
#region Heading or Info Methods
/// <summary>
/// Display one of two technical preview messages in the editor
/// </summary>
/// <param name="radicalChangePossible"></param>
public static void InTechPreview(bool radicalChangePossible = false)
{
if (radicalChangePossible)
{
EditorGUILayout.HelpBox("This feature is in technical preview and could radically change without notice.", MessageType.Warning);
}
else
{
EditorGUILayout.HelpBox("This feature is currently in technical preview", MessageType.Warning);
}
}
/// <summary>
/// Display a in-development warning in the editor
/// </summary>
public static void InDevelopment()
{
EditorGUILayout.HelpBox("This feature is in development, is incomplete, and could radically change without notice.", MessageType.Warning);
}
public static void NotImplemented()
{
EditorGUILayout.HelpBox("This feature has not yet been implemented", MessageType.Warning);
}
public static void PerformanceImpact()
{
EditorGUILayout.HelpBox("This feature may negatively impact performance", MessageType.Warning);
}
/// <summary>
/// Draw the Sci-Fi Ship Controller version header in the inspector
/// </summary>
/// <param name="labelFieldRichText"></param>
public static void SSCVersionHeader (GUIStyle labelFieldRichText)
{
GUILayout.BeginVertical("HelpBox");
EditorGUILayout.LabelField("<b>Sci-Fi Ship Controller</b> Version " + SciFiShipController.ShipControlModule.SSCVersion + " " + SciFiShipController.ShipControlModule.SSCBetaVersion, labelFieldRichText);
GUILayout.EndVertical();
}
#endregion
#region Inspector GUIContent
public readonly static GUIContent gizmoToggleBtnContent = new GUIContent("G", "Toggle gizmos and visualisations on/off for all items in the scene view");
public readonly static GUIContent gizmoBtnContent = new GUIContent("G", "Toggle gizmos on/off in the scene view");
public readonly static GUIContent gizmoFindBtnContent = new GUIContent("F", "Find (select) in the scene view.");
public readonly static GUIContent gizmoLHBtnContent = new GUIContent("LH", "Show left-hand gizmos (default right-hand)");
public readonly static GUIContent btnResetContent = new GUIContent("R", "Reset to default value(s)");
public readonly static GUIContent debugModeIndent1Content = new GUIContent(" Debug Mode", "Use this to troubleshoot the data at runtime in the editor.");
public readonly static GUIContent debugIsInitialisedIndent1Content = new GUIContent(" Is Initialised?");
#endregion
#region Inspector Methods
/// <summary>
/// Draw a foldout and label on a single line
/// SSCEditorHelper.DrawSSCFoldout(showMessageSettingsInEditorProp, altMessageSettingsContent, foldoutStyleNoLabel, defaultEditorFieldWidth);
/// </summary>
/// <param name="serializedProperty"></param>
/// <param name="guiLabelContent"></param>
/// <param name="foldoutStyleNoLabel"></param>
/// <param name="defaultEditorFieldWidth"></param>
public static void DrawSSCFoldout(SerializedProperty serializedProperty, GUIContent guiLabelContent, GUIStyle foldoutStyleNoLabel, float defaultEditorFieldWidth)
{
GUILayout.BeginHorizontal();
EditorGUI.indentLevel += 1;
EditorGUIUtility.fieldWidth = 15f;
serializedProperty.boolValue = EditorGUILayout.Foldout(serializedProperty.boolValue, GUIContent.none, foldoutStyleNoLabel);
EditorGUI.indentLevel -= 1;
EditorGUIUtility.fieldWidth = defaultEditorFieldWidth;
EditorGUILayout.LabelField(guiLabelContent);
GUILayout.EndHorizontal();
}
/// <summary>
/// Draw a foldout with no label
/// Example: SSCEditorHelper.DrawSSCFoldout(isDisplayMessageListExpandedProp, foldoutStyleNoLabel, defaultEditorFieldWidth);
/// </summary>
/// <param name="serializedProperty"></param>
/// <param name="foldoutStyleNoLabel"></param>
/// <param name="defaultEditorFieldWidth"></param>
public static void DrawSSCFoldout(SerializedProperty serializedProperty, GUIStyle foldoutStyleNoLabel, float defaultEditorFieldWidth)
{
EditorGUI.indentLevel += 1;
EditorGUIUtility.fieldWidth = 15f;
serializedProperty.boolValue = EditorGUILayout.Foldout(serializedProperty.boolValue, GUIContent.none, foldoutStyleNoLabel);
EditorGUI.indentLevel -= 1;
EditorGUIUtility.fieldWidth = defaultEditorFieldWidth;
}
/// <summary>
/// Draw the standard set of Support, Discord, Help, and Tutorial buttons in the inspector
/// </summary>
/// <param name="buttonCompact"></param>
public static void DrawSSCGetHelpButtons(GUIStyle buttonCompact)
{
GUILayout.BeginHorizontal();
if (GUILayout.Button(SSCEditorHelper.btnTxtGetSupport, buttonCompact)) { Application.OpenURL(SSCEditorHelper.urlGetSupport); }
if (GUILayout.Button(SSCEditorHelper.btnDiscordContent, buttonCompact)) { Application.OpenURL(SSCEditorHelper.urlDiscordChannel); }
if (GUILayout.Button(SSCEditorHelper.btnHelpContent, buttonCompact)) { Application.OpenURL(SSCEditorHelper.GetHelpURL()); }
if (GUILayout.Button(SSCEditorHelper.tutorialsURLContent, buttonCompact)) { Application.OpenURL(SSCEditorHelper.urlTutorials); }
EditorGUILayout.EndHorizontal();
}
/// <summary>
/// Draw a horzontal gap. e.g DrawSSCHorizontalGap(2f) // 2 pixels high
/// </summary>
/// <param name="h"></param>
public static void DrawSSCHorizontalGap(float h)
{
GUILayoutUtility.GetRect(1f, h);
}
/// <summary>
/// Draw a sprite in the inspector. Set the height so that it displays an empty space if the sprite is null
/// </summary>
/// <param name="sprite"></param>
/// <param name="height"></param>
public static void DrawSprite(GUIContent guiContent, Sprite sprite, int height, float labelWidth)
{
GUILayout.BeginHorizontal();
if (guiContent != GUIContent.none) { GUILayout.Label(guiContent, GUILayout.Width(labelWidth)); }
GUILayout.Label(AssetPreview.GetAssetPreview(sprite), GUILayout.Height(height));
GUILayout.EndHorizontal();
}
/// <summary>
/// Draw an array in the inspector
/// </summary>
/// <param name="serializedProperty"></param>
/// <param name="guiContent"></param>
public static void DrawArray(SerializedProperty serializedProperty, GUIContent labelGUIContent, float labelWidth, string elementName)
{
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField(labelGUIContent, GUILayout.Width(labelWidth-1f));
EditorGUILayout.PropertyField(serializedProperty.FindPropertyRelative("Array.size"), GUIContent.none);
GUILayout.EndHorizontal();
EditorGUI.indentLevel++;
for (int arrayIdx = 0; arrayIdx < serializedProperty.arraySize; arrayIdx++)
{
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField(elementName + " " + (arrayIdx+1).ToString(), GUILayout.Width(labelWidth-16f));
EditorGUILayout.PropertyField(serializedProperty.GetArrayElementAtIndex(arrayIdx), GUIContent.none);
GUILayout.EndHorizontal();
}
EditorGUI.indentLevel--;
}
/// <summary>
/// WIP - only left implemented
/// Draw left and right indent sliders. Convert from/to -1.0 to 1.0 OffsetX
/// Returns true if the offsetX property has changed
/// </summary>
/// <param name="offsetXProp"></param>
/// <param name="widthProp"></param>
/// <param name="labelWidth"></param>
public static bool DrawOffsetLeftRight(SerializedProperty offsetXProp, SerializedProperty widthProp, float labelWidth)
{
float startoffsetX = offsetXProp.floatValue;
// Normalised offsetX then subtract half the panel width
float leftIndent = (startoffsetX + 1f) * 0.5f - (widthProp.floatValue * 0.5f);
float newLeftIndent = EditorGUILayout.Slider(new GUIContent(" Left Indent"), leftIndent, -1f, 1f);
if (newLeftIndent != leftIndent)
{
// Convert back into -1 to 1 value
float offsetX = newLeftIndent + widthProp.floatValue - 1f;
// Clamp -1.0 to 1.0
offsetXProp.floatValue = offsetX < -1f ? -1f : offsetX > 1f ? 1f : offsetX;
}
return offsetXProp.floatValue != startoffsetX;
}
/// <summary>
/// WIP - only bottom implemented
/// Draw top and bottom indent sliders. Convert from/to -1.0 to 1.0 OffsetY
/// Returns true if the offsetY property has changed
/// </summary>
/// <param name="offsetYProp"></param>
/// <param name="heightProp"></param>
/// <param name="labelWidth"></param>
public static bool DrawOffsetTopBottom(SerializedProperty offsetYProp, SerializedProperty heightProp, float labelWidth)
{
float startoffsetY = offsetYProp.floatValue;
// Normalised offsetX then subtract half the panel width
float bottomIndent = (startoffsetY + 1f) * 0.5f - (heightProp.floatValue * 0.5f);
float newBottomIndent = EditorGUILayout.Slider(new GUIContent(" Bottom Indent"), bottomIndent, -1f, 1f);
if (newBottomIndent != bottomIndent)
{
// Convert back into -1 to 1 value
float offsetY = newBottomIndent + heightProp.floatValue - 1f;
// Clamp -1.0 to 1.0
offsetYProp.floatValue = offsetY < -1f ? -1f : offsetY > 1f ? 1f : offsetY;
}
return offsetYProp.floatValue != startoffsetY;
}
/// <summary>
/// Useful util drawing lines in the editor
/// </summary>
/// <param name="color"></param>
/// <param name="thickness"></param>
/// <param name="padding"></param>
public static void DrawUILine(Color color, int thickness = 2, int padding = 10)
{
Rect r = EditorGUILayout.GetControlRect(GUILayout.Height(padding + thickness));
r.height = thickness;
r.y += padding / 2f;
r.x -= 2;
r.width += 6f;
EditorGUI.DrawRect(r, color);
}
/// <summary>
/// Returns a vector3 as text for debugging with 0,1,2 or 3 decimal places
/// </summary>
/// <param name="v3"></param>
/// <param name="decimalPlaces"></param>
/// <returns></returns>
public static string GetVector3Text(Vector3 v3, int decimalPlaces)
{
float multiplier = decimalPlaces == 0 ? 10 : decimalPlaces == 1 ? 100f : decimalPlaces == 2 ? 1000f : 10000f;
string sFormat = decimalPlaces == 0 ? "0" : decimalPlaces == 1 ? "0.0" : decimalPlaces == 2 ? "0.00" : "0.000";
float x = Mathf.RoundToInt(v3.x * multiplier) / multiplier;
float y = Mathf.RoundToInt(v3.y * multiplier) / multiplier;
float z = Mathf.RoundToInt(v3.z * multiplier) / multiplier;
return x.ToString(sFormat) + ", " + y.ToString(sFormat) + ", " + z.ToString(sFormat);
}
#endregion
#region Scene View Methods
/// <summary>
/// Attempt to display the point in the centre of the sceneview
/// zoomDistance is the distance in metres to zoom out from the point in the scene
/// </summary>
/// <param name="centrePoint"></param>
/// <param name="zoomDistance"></param>
/// <param name="sourceType"></param>
public static void PositionSceneView(Vector3 centrePoint, float zoomDistance, Type sourceType)
{
try
{
// Switch to the scene view
#if UNITY_2018_2_OR_NEWER
CallMenu("Window/General/Scene");
#else
CallMenu("Window/Scene");
#endif
SceneView sceneView = UnityEditor.SceneView.lastActiveSceneView;
// If the sceneView hasn't had the focus in this session, lastActiveSceneView will return null
if (sceneView != null)
{
sceneView.LookAt(centrePoint);
sceneView.size = zoomDistance;
}
}
catch (System.Exception ex)
{
Debug.LogError(sourceType.Name + ": Couldn't position scene view\n" + ex.Message);
}
}
/// <summary>
/// Given a mouse position in 2D space, get the 3D Worldspace position of the "nearest" line of sight object
/// in the scene view. Return 0,0,0 is something went wrong or no object in direct line.
/// </summary>
/// <param name="sceneView"></param>
/// <param name="mousePosition"></param>
/// <param name="normalOffset"></param>
/// <param name="worldspacePoint"></param>
/// <param name="showErrors"></param>
/// <returns></returns>
public static bool GetPositionFromMouse(SceneView sceneView, Vector2 mousePosition, float normalOffset, ref Vector3 worldspacePoint, bool showErrors)
{
bool isSuccessful = false;
worldspacePoint = Vector3.zero;
string methodName = "SSCEditorHelper.GetPositionFromMouse";
try
{
if (sceneView != null)
{
// Only process mouse positions over a scene view.
// This avoid the situation where scene view has focus but mouse is over another window
var win = EditorWindow.mouseOverWindow;
if (win != null && win.GetType() == typeof(SceneView))
{
Camera svCamera = sceneView.camera;
if (svCamera != null)
{
// Cast a ray from the scene view camera through the mouse point onto (hopefully) the nearest object.
// Mouse position Y in screen space is inverted
Ray ray = svCamera.ScreenPointToRay(new Vector3(mousePosition.x, svCamera.pixelHeight - mousePosition.y, svCamera.nearClipPlane));
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
// We hit something
worldspacePoint = hit.point + (hit.normal * normalOffset);
}
else
{
// If we didn't hit anything, create a point infront of the scene view camera
worldspacePoint = ray.GetPoint(100f);
}
isSuccessful = true;
}
}
}
}
catch (System.Exception ex) { Debug.LogWarning(methodName + " - sorry, something went wrong\n" + ex.Message); }
return isSuccessful;
}
/// <summary>
/// Given a mouse position in 2D space, get the 3D Worldspace position of the "nearest" line of sight object.
/// Return 0,0,0 is something went wrong.
/// </summary>
/// <param name="mousePosition"></param>
/// <param name="showErrors"></param>
/// <returns></returns>
public static Vector3 GetPositionFromMouse(Vector2 mousePosition, bool showErrors)
{
Vector3 locationPoint = Vector3.zero;
string methodName = "SSCEditorHelper.GetPositionFromMouse";
try
{
SceneView sceneView = SceneView.lastActiveSceneView;
bool isHit = false;
if (sceneView != null)
{
Camera svCamera = sceneView.camera;
if (svCamera != null)
{
// Cast a ray from the scene view camera through the mouse point onto (hopefully) the nearest object.
// Mouse position Y in screen space is inverted
Ray ray = svCamera.ScreenPointToRay(new Vector3(mousePosition.x, svCamera.pixelHeight - mousePosition.y, svCamera.nearClipPlane));
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
// We hit something
locationPoint = hit.point;
isHit = true;
}
if (!isHit)
{
// If we didn't hit anything, create a point infront of the scene view camera
locationPoint = ray.GetPoint(100f);
}
}
}
}
catch (System.Exception ex) { Debug.LogWarning(methodName + " - sorry, something went wrong\n" + ex.Message); }
return locationPoint;
}
#endregion
#region Menu Helper Methods
/// <summary>
/// Call an item from the Unity menu. Menu can also be one custom created.
/// USAGE: SSCEditorHelper.CallMenu("Edit/Project Settings/Player");
/// </summary>
/// <param name="menuItemPath"></param>
public static void CallMenu(string menuItemPath)
{
if (!string.IsNullOrEmpty(menuItemPath))
{
EditorApplication.ExecuteMenuItem(menuItemPath);
}
}
#endregion
#region Link Helper variables and Methods
// URL buttons
//public static readonly string urlAssetPage = "http://u3d.as/1oPf";
public static readonly string urlGetSupport = "http://forum.unity3d.com/threads/594448";
public static readonly string urlDiscordChannel = "https://discord.gg/CjzCK4b";
public static readonly string urlTutorials = "https://scsmmedia.com/sci-fi-ship-controller#938b782a-2c40-4590-9f23-de983ed33787";
//public static readonly string btnTxtAssetPage = "Asset Page";
public static readonly string btnTxtGetSupport = "Get Support";
public static readonly GUIContent tutorialsURLContent = new GUIContent("Tutorials", "Go to our website for a link to Video Tutorials about SSC concepts");
public static readonly GUIContent btnHelpContent = new GUIContent("Help", "Sci-Fi Ship Controller manual. Requires Adobe Reader\nAvailable from\nadobe.com/reader");
public static readonly GUIContent btnDiscordContent = new GUIContent("Discord", "Open SSC Discord Channel in browser");
/// <summary>
/// Get help PDF path to point to the local copy of the manual.
/// </summary>
/// <returns></returns>
public static string GetHelpURL()
{
// Build a link to the file manual.
#if UNITY_EDITOR_OSX
return "File:///" + Application.dataPath.Replace(" " ,"%20") + "/scsm/SciFiShipController/ssc_manual.pdf";
#else
return "file:///" + Application.dataPath + "/scsm/SciFiShipController/ssc_manual.pdf";
#endif
}
#endregion
#region Audio
/// <summary>
/// Play an audio clip in the editor
/// </summary>
/// <param name="audioClip"></param>
/// <param name="startSample"></param>
/// <param name="isLoop"></param>
public static void PlayAudioClip(AudioClip audioClip, int startSample = 0, bool isLoop = false)
{
try
{
System.Reflection.Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
System.Type audioUtilType = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
#if UNITY_2020_2_OR_NEWER
string playClipMethodName = "PlayPreviewClip";
#else
string playClipMethodName = "PlayClip";
#endif
// Get the method to play an audio clip in the editor
System.Reflection.MethodInfo playClipMethod = audioUtilType.GetMethod(playClipMethodName,
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public,
null, new System.Type[] { typeof(AudioClip), typeof(int), typeof(bool) }, null
);
playClipMethod.Invoke(null, new object[] { audioClip, startSample, isLoop });
}
catch (System.Exception ex)
{
#if UNITY_EDITOR
Debug.LogWarning("SSCEditorHelper PlayAudioClip - " + ex.Message);
#else
if (ex != null) { }
#endif
}
}
/// <summary>
/// Stop all audio clips playing in the editor
/// </summary>
public static void StopAllAudioClips()
{
try
{
System.Reflection.Assembly unityEditorAssembly = typeof(AudioImporter).Assembly;
System.Type audioUtilType = unityEditorAssembly.GetType("UnityEditor.AudioUtil");
#if UNITY_2020_2_OR_NEWER
string stopClipMethodName = "StopAllPreviewClips";
#else
string stopClipMethodName = "StopAllClips";
#endif
System.Reflection.MethodInfo stopClipsMethod = audioUtilType.GetMethod(stopClipMethodName,
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public,
null, new System.Type[] { }, null
);
stopClipsMethod.Invoke(null, new object[] { } );
}
catch (System.Exception ex)
{
#if UNITY_EDITOR
Debug.LogWarning("SSCEditorHelper StopAllAudioClips - " + ex.Message);
#else
if (ex != null) { }
#endif
}
}
#endregion
#region Camera Methods
// See SSCUtils.cs
#endregion
#region Gameobject and Transform Methods
/// <summary>
/// Find or create a child gameobject. If it doesn't already exist, optionally make it a child of the parent.
/// </summary>
/// <param name="parentGameObject"></param>
/// <param name="gameObjectName"></param>
/// <param name="isMakeChildOnCreate"></param>
/// <returns></returns>
public static GameObject GetOrCreateChildGameObject(GameObject parentGameObject, string gameObjectName, bool isMakeChildOnCreate)
{
GameObject gameObject = null;
if (parentGameObject != null)
{
Transform parentTfrm = parentGameObject.transform;
Transform trfm = parentTfrm.Find(gameObjectName);
// Not found so create a new gameobject
if (trfm == null)
{
gameObject = new GameObject(gameObjectName);
if (gameObject != null)
{
trfm = gameObject.transform;
if (isMakeChildOnCreate)
{
trfm.SetParent(parentTfrm, false);
}
}
}
else
{
gameObject = trfm.gameObject;
}
}
return gameObject;
}
#endregion
#region Project Helper Methods
/// <summary>
/// Reveal or hightlight the folder that in the Project window (if it is open and visible
/// on the panel it is on). Highlights the item in yellow for a second or two.
/// By default also selects the object. Not selecting the object can be useful if called
/// from a CustomEditor inspector script so as not to loose focus.
/// NOTE: The Project won't expand if it is already selected AND the user has collapsed it.
/// USAGE: SSCEditorHelper.HighlightFolderInProjectWindow(SSCSetup.effectsFolder, true, true);
/// </summary>
/// <param name="folderPath"></param>
/// <param name="selectFolder"></param>
/// <param name="showErrors"></param>
public static void HighlightFolderInProjectWindow(string folderPath, bool selectFolder, bool showErrors)
{
if (AssetDatabase.IsValidFolder(folderPath))
{
UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(folderPath, typeof(UnityEngine.Object));
if (obj != null)
{
// Highlight the item in yellow for a second or two.
EditorGUIUtility.PingObject(obj);
if (selectFolder) { UnityEditor.Selection.activeObject = obj; }
}
}
else { Debug.Log("SSCEditorHelper.HighLightFolder - the following folder does not exist: " + folderPath); }
}
#endregion
#region IO Helper Methods
/// <summary>
/// Get a full file path from the user.
/// Has the option to restrict to only files in the current Unity project.
/// fileExtensions format: { "Texture2D", "png,psd,jpg,jpeg", "All files", "*" }
/// </summary>
/// <param name="fileTypeName"></param>
/// <param name="relativeFolderToOpen"></param>
/// <param name="fileExtensions"></param>
/// <param name="restrictToProject"></param>
/// <param name="folderPath"></param>
/// <param name="fileName"></param>
/// <returns></returns>
public static bool GetFilePathFromUser(string fileTypeName, string relativeFolderToOpen, string[] fileExtensions, bool restrictToProject, ref string folderPath, ref string fileName)
{
bool isSuccessful = false;
// Returns the full absolute path
string path = EditorUtility.OpenFilePanelWithFilters(fileTypeName, relativeFolderToOpen, fileExtensions);
if (path.Contains(Application.dataPath) || (!restrictToProject && !string.IsNullOrEmpty(path)))
{
// Make sure the text field doesn't have the focus, else it won't update until user
// moves to another control.
GUI.FocusControl("");
folderPath = System.IO.Path.GetDirectoryName(path);
fileName = System.IO.Path.GetFileName(path);
isSuccessful = true;
}
// Did user cancel open file panel?
else if (string.IsNullOrEmpty(path)) { }
else
{
EditorUtility.DisplayDialog(fileTypeName + " File", "The file must be in the Assets folder of your project", "OK");
}
return isSuccessful;
}
/// <summary>
/// Get a folder from the user (EDITOR ONLY)
/// EXAMPLE: GetPathFromUser("Path Data", SSCSetup.sscFolder, false, ref pathFileFolder);
/// </summary>
/// <param name="dialogTitle"></param>
/// <param name="relativeFolderToOpen"></param>
/// <param name="restrictToProject"></param>
/// <param name="folderPath"></param>
/// <returns></returns>
public static bool GetPathFromUser(string dialogTitle, string relativeFolderToOpen, bool restrictToProject, ref string folderPath)
{
bool isSuccessful = false;
// Returns the full absolute path
string path = EditorUtility.OpenFolderPanel(dialogTitle, relativeFolderToOpen, "");
if ((!restrictToProject && !string.IsNullOrEmpty(path)) || path.Contains(Application.dataPath))
{
if (restrictToProject)
{
// Get the relative path from Assets
if (path.Length > Application.dataPath.Length) { path = path.Remove(0, Application.dataPath.Length); }
if (path.Length > 1)
{
if (path[0] == '/') { path = path.Remove(0, 1); }
}
}
// Make sure the text field doesn't have the focus, else it won't update until user
// moves to another control.
GUI.FocusControl("");
folderPath = path;
isSuccessful = true;
}
// Did user cancel open folder panel?
else if (string.IsNullOrEmpty(path)) { }
else
{
EditorUtility.DisplayDialog(dialogTitle + " Folder", "The folder must be in the Assets folder of your project", "OK");
}
return isSuccessful;
}
/// <summary>
/// Get the Project assets folder for an item in the Project folder given a full file path.
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
public static string GetAssetFolderFromFilePath(string filePath)
{
if (string.IsNullOrEmpty(filePath)) { return string.Empty; }
else
{
// First attempts to simply strip out the Application.dataPath, then tries to replace the forward slash with back slashes and then try stripping the dataPath again.
return "Assets" + System.IO.Path.GetDirectoryName(filePath).Replace(Application.dataPath, "").Replace(Application.dataPath.Replace("/", "\\"), "");
}
}
#endregion
#region Dialog Helpers
/// <summary>
/// Prompt user to respond Yes or No
/// </summary>
/// <param name="dialogTile"></param>
/// <param name="dialogText"></param>
/// <returns></returns>
public static bool PromptYesNo(string dialogTile, string dialogText)
{
return EditorUtility.DisplayDialog(dialogTile, dialogText, "Yes", "NO!");
}
/// <summary>
/// Prompt user to continue with an action.
/// </summary>
/// <param name="dialogTile"></param>
/// <param name="dialogText"></param>
/// <returns></returns>
public static bool PromptForContinue(string dialogTile, string dialogText)
{
return EditorUtility.DisplayDialog(dialogTile, dialogText, "Yes", "CANCEL!");
}
/// <summary>
/// Prompt the user to delete something or cancel.
/// if (SSCEditorHelper.PromptForDelete("Delete Items?", labelText)) {...}
/// </summary>
/// <param name="dialogTile"></param>
/// <param name="dialogText"></param>
/// <returns></returns>
public static bool PromptForDelete(string dialogTile, string dialogText)
{
return EditorUtility.DisplayDialog(dialogTile, dialogText, "Delete Now", "Cancel");
}
#endregion
#region Canvas Helpers
public static UnityEngine.UI.Image AddCanvasPanelIfMissing
(
UnityEngine.UI.Image[] uiImageArray,
string panelName,
float panelOffsetX, float panelOffsetY,
float panelWidth, float panelHeight,
float anchorMinX, float anchorMinY,
float anchorMaxX, float anchorMaxY,
Transform parentTrfm
)
{
UnityEngine.UI.Image panelImg = ArrayUtility.Find(uiImageArray, img => img.name == panelName);
if (panelImg == null)
{
GameObject panelGO = new GameObject(panelName);
panelGO.layer = 5;
panelGO.transform.SetParent(parentTrfm);
RectTransform rectTrfm = panelGO.AddComponent<RectTransform>();
rectTrfm.anchorMin = new Vector2(anchorMinX, anchorMinY);
rectTrfm.anchorMax = new Vector2(anchorMaxX, anchorMaxY);
rectTrfm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, panelWidth);
rectTrfm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, panelHeight);
panelGO.transform.position = new Vector3(panelOffsetX + (panelWidth * 0.5f), panelOffsetY + (panelHeight * 0.5f), 0f);
panelGO.AddComponent<CanvasRenderer>();
panelImg = panelGO.AddComponent<UnityEngine.UI.Image>();
panelImg.color = new Color(1f,1f,1f, 50f / 255f);
panelImg.sprite = AssetDatabase.GetBuiltinExtraResource<Sprite>("UI/Skin/UISprite.psd");
panelImg.type = UnityEngine.UI.Image.Type.Sliced;
panelImg.raycastTarget = false;
panelImg.fillCenter = false;
}
return panelImg;
}
public static UnityEngine.UI.RawImage AddCanvasRawPanelIfMissing
(
UnityEngine.UI.RawImage[] uiImageArray,
string panelName,
float panelOffsetX, float panelOffsetY,
float panelWidth, float panelHeight,
float anchorMinX, float anchorMinY,
float anchorMaxX, float anchorMaxY,
Texture imgTexture,
Transform parentTrfm,
Vector3 canvasScale
)
{
UnityEngine.UI.RawImage panelImg = ArrayUtility.Find(uiImageArray, img => img.name == panelName);
if (panelImg == null)
{
GameObject panelGO = new GameObject(panelName);
panelGO.layer = 5;
panelGO.transform.SetParent(parentTrfm);
RectTransform rectTrfm = panelGO.AddComponent<RectTransform>();
rectTrfm.anchorMin = new Vector2(anchorMinX, anchorMinY);
rectTrfm.anchorMax = new Vector2(anchorMaxX, anchorMaxY);
rectTrfm.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, panelWidth);
rectTrfm.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, panelHeight);
panelGO.transform.position = new Vector3((panelOffsetX * canvasScale.x) + (panelWidth * 0.5f * canvasScale.x), (panelOffsetY * canvasScale.y) + (panelHeight * 0.5f * canvasScale.y), 0f);
panelGO.AddComponent<CanvasRenderer>();
panelImg = panelGO.AddComponent<UnityEngine.UI.RawImage>();
// No transparency as the colour for the texture will set the transparency for each element
panelImg.color = new Color(1f,1f,1f, 255f / 255f);
panelImg.raycastTarget = false;
panelImg.texture = imgTexture;
}
return panelImg;
}
#endregion
#region Search Helpers
/// <summary>
/// Does the string value of the property match the search criteria.
/// Typically this would be the name value of a LocationData or PathData class instance.
/// If the search string is empty, always return true.
/// </summary>
/// <param name="locationDataToMatch"></param>
/// <param name="searchFilter"></param>
/// <returns></returns>
public static bool IsInSearchFilter(SerializedProperty serializedProperty, string searchFilter)
{
// If the search string is empty, always return true.
if (string.IsNullOrEmpty(searchFilter)) { return true; }
else
{
// If this property has no stringvalue (typically a name), it can't be a match
return !string.IsNullOrEmpty(serializedProperty.stringValue) && serializedProperty.stringValue.ToLower().Contains(searchFilter.ToLower());
}
}
/// <summary>
/// Draw the search filter control in the editor
/// </summary>
/// <param name="filterContent"></param>
/// <param name="labelStyle"></param>
/// <param name="searchFieldStyle"></param>
/// <param name="cancelButtonStyle"></param>
/// <param name="editorSearchFilter"></param>
public static void DrawSearchFilterControl(GUIContent filterContent, GUIStyle labelStyle, GUIStyle searchFieldStyle, GUIStyle cancelButtonStyle, ref string editorSearchFilter)
{
GUILayout.BeginHorizontal();
GUILayout.Label("", GUILayout.Width(2f));
GUILayout.Label(filterContent, labelStyle, GUILayout.Width(50f));
if (editorSearchFilter == null) { editorSearchFilter = string.Empty; }
editorSearchFilter = GUILayout.TextField(editorSearchFilter, searchFieldStyle);
// Sometimes the search text is reverse search almost seems invisible (small search icon and cancel cannot be seen)
// Not sure how this occurs.
if (GUILayout.Button("", cancelButtonStyle))
{
editorSearchFilter = string.Empty;
GUI.FocusControl(null);
}
//// Force buttons to be right justified
//GUILayoutUtility.GetRect(1f, 1f);
//// Toggle selection based on the first filtered one in the list
//if (GUILayout.Button(selectBtnContent, buttonCompact, GUILayout.MaxWidth(20f)))
//{
// ToggleSelectList(pathDataListProp, sscManager.editorSearchPathFilter);
//}
GUILayout.EndHorizontal();
// Provide space between previous controls and next ones
GUILayoutUtility.GetRect(1f, 3f);
}
#endregion
#region Prefab Helpers
/// <summary>
/// Return true if is part of a prefab in Project assets folder (or subfolders)
/// but is NOT an instance of the prefab a scene.
/// </summary>
/// <param name="go"></param>
/// <returns></returns>
public static bool IsPrefabAsset(GameObject go)
{
return PrefabUtility.IsPartOfPrefabAsset(go);
}
#endregion
#region Miscellaneous
#endregion
}