403 lines
13 KiB
C#
Executable File
403 lines
13 KiB
C#
Executable File
using UnityEngine;
|
|
using UnityEditor;
|
|
using System.Collections.Generic;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright 2015-2021 RenderHeads Ltd. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace RenderHeads.Media.AVProVideo.Editor
|
|
{
|
|
/// <summary>
|
|
/// Helper methods for editor components
|
|
/// </summary>
|
|
public static class EditorHelper
|
|
{
|
|
/// <summary>
|
|
/// Loads from EditorPrefs, converts a CSV string to List<string> and returns it
|
|
/// </summary>
|
|
internal static List<string> GetEditorPrefsToStringList(string key, char separator = ';')
|
|
{
|
|
string items = EditorPrefs.GetString(key, string.Empty);
|
|
return new List<string>(items.Split(new char[] { separator }, System.StringSplitOptions.RemoveEmptyEntries));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a List<string> into a CSV string and saves it in EditorPrefs
|
|
/// </summary>
|
|
internal static void SetEditorPrefsFromStringList(string key, List<string> items, char separator = ';')
|
|
{
|
|
string value = string.Empty;
|
|
if (items != null && items.Count > 0)
|
|
{
|
|
value = string.Join(separator.ToString(), items.ToArray());
|
|
}
|
|
EditorPrefs.SetString(key, value);
|
|
}
|
|
|
|
public static SerializedProperty CheckFindProperty(this UnityEditor.Editor editor, string propertyName)
|
|
{
|
|
SerializedProperty result = editor.serializedObject.FindProperty(propertyName);
|
|
Debug.Assert(result != null, "Missing property: " + propertyName);
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Only lets the property if the proposed path doesn't contain invalid characters
|
|
/// Also changes all backslash characters to forwardslash for better cross-platform compatability
|
|
/// </summary>
|
|
internal static bool SafeSetPathProperty(string path, SerializedProperty property)
|
|
{
|
|
bool result = false;
|
|
|
|
if (path == null)
|
|
{
|
|
path = string.Empty;
|
|
}
|
|
|
|
if (path != property.stringValue)
|
|
{
|
|
property.stringValue = path;
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether a define exists for a specific platform
|
|
/// </summary>
|
|
internal static bool HasScriptDefine(string define, BuildTargetGroup buildTarget = BuildTargetGroup.Unknown)
|
|
{
|
|
if (buildTarget == BuildTargetGroup.Unknown) { buildTarget = EditorUserBuildSettings.selectedBuildTargetGroup; }
|
|
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTarget);
|
|
return defines.Contains(define);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a define if it doesn't already exist for a specific platform
|
|
/// </summary>
|
|
internal static void AddScriptDefine(string define, BuildTargetGroup buildTarget = BuildTargetGroup.Unknown)
|
|
{
|
|
if (buildTarget == BuildTargetGroup.Unknown) { buildTarget = EditorUserBuildSettings.selectedBuildTargetGroup; }
|
|
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTarget);
|
|
if (!defines.Contains(define))
|
|
{
|
|
defines += ";" + define + ";";
|
|
PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTarget, defines);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a define if it exists for a specific platform
|
|
/// </summary>
|
|
internal static void RemoveScriptDefine(string define, BuildTargetGroup buildTarget = BuildTargetGroup.Unknown)
|
|
{
|
|
if (buildTarget == BuildTargetGroup.Unknown) { buildTarget = EditorUserBuildSettings.selectedBuildTargetGroup; }
|
|
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTarget);
|
|
if (defines.Contains(define))
|
|
{
|
|
defines = defines.Replace(define, "");
|
|
PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTarget, defines);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Given a partial file path and MediaLocation, return a directory path suitable for a file browse dialog to start in
|
|
/// </summary>
|
|
internal static string GetBrowsableFolder(string path, MediaPathType fileLocation)
|
|
{
|
|
// Try to resolve based on file path + file location
|
|
string result = Helper.GetFilePath(path, fileLocation);
|
|
if (!string.IsNullOrEmpty(result))
|
|
{
|
|
if (System.IO.File.Exists(result))
|
|
{
|
|
result = System.IO.Path.GetDirectoryName(result);
|
|
}
|
|
}
|
|
|
|
if (!System.IO.Directory.Exists(result))
|
|
{
|
|
// Just resolve on file location
|
|
result = Helper.GetPath(fileLocation);
|
|
}
|
|
if (string.IsNullOrEmpty(result))
|
|
{
|
|
// Fallback
|
|
result = Application.streamingAssetsPath;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal static bool OpenMediaFileDialog(string startPath, ref MediaPath mediaPath, ref string fullPath, string extensions)
|
|
{
|
|
bool result = false;
|
|
|
|
string path = UnityEditor.EditorUtility.OpenFilePanel("Browse Media File", startPath, extensions);
|
|
Debug.Log($"OpenMediaFileDialog - path: {path}");
|
|
if (!string.IsNullOrEmpty(path) && !path.EndsWith(".meta"))
|
|
{
|
|
mediaPath = GetMediaPathFromFullPath(path);
|
|
fullPath = path;
|
|
Debug.Log($"OpenMediaFileDialog - mediaPath.Path: {mediaPath.Path}, mediaPath.PathType: {mediaPath.PathType}");
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*private static bool IsPathWithin(string fullPath, string targetPath)
|
|
{
|
|
return fullPath.StartsWith(targetPath);
|
|
}*/
|
|
|
|
private static string GetPathRelativeTo(string root, string fullPath)
|
|
{
|
|
string result = fullPath.Remove(0, root.Length);
|
|
if (result.StartsWith(System.IO.Path.DirectorySeparatorChar.ToString()) || result.StartsWith(System.IO.Path.AltDirectorySeparatorChar.ToString()))
|
|
{
|
|
result = result.Remove(0, 1);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal static MediaPath GetMediaPathFromFullPath(string fullPath)
|
|
{
|
|
MediaPath result = null;
|
|
string projectRoot = System.IO.Path.GetFullPath(System.IO.Path.Combine(Application.dataPath, ".."));
|
|
projectRoot = projectRoot.Replace('\\', '/');
|
|
|
|
if (fullPath.StartsWith(projectRoot))
|
|
{
|
|
if (fullPath.StartsWith(Application.streamingAssetsPath))
|
|
{
|
|
// Must be StreamingAssets relative path
|
|
result = new MediaPath(GetPathRelativeTo(Application.streamingAssetsPath, fullPath), MediaPathType.RelativeToStreamingAssetsFolder);
|
|
}
|
|
else if (fullPath.StartsWith(Application.dataPath))
|
|
{
|
|
// Must be Assets relative path
|
|
result = new MediaPath(GetPathRelativeTo(Application.dataPath, fullPath), MediaPathType.RelativeToDataFolder);
|
|
}
|
|
else
|
|
{
|
|
// Must be project relative path
|
|
result = new MediaPath(GetPathRelativeTo(projectRoot, fullPath), MediaPathType.RelativeToProjectFolder);
|
|
}
|
|
}
|
|
else
|
|
// Must be persistant data
|
|
if (fullPath.StartsWith(Application.persistentDataPath))
|
|
{
|
|
result = new MediaPath(GetPathRelativeTo(Application.persistentDataPath, fullPath), MediaPathType.RelativeToPersistentDataFolder);
|
|
}
|
|
else
|
|
{
|
|
// Must be absolute path
|
|
result = new MediaPath(fullPath, MediaPathType.AbsolutePathOrURL);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal class IMGUI
|
|
{
|
|
private static GUIStyle _copyableStyle = null;
|
|
private static GUIStyle _wordWrappedTextAreaStyle = null;
|
|
private static GUIStyle _rightAlignedLabelStyle = null;
|
|
private static GUIStyle _centerAlignedLabelStyle = null;
|
|
|
|
/// <summary>
|
|
/// Displays an IMGUI warning text box inline
|
|
/// </summary>
|
|
internal static void WarningTextBox(string title, string body, Color bgColor, Color titleColor, Color bodyColor)
|
|
{
|
|
BeginWarningTextBox(title, body, bgColor, titleColor, bodyColor);
|
|
EndWarningTextBox();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays an IMGUI warning text box inline
|
|
/// </summary>
|
|
internal static void BeginWarningTextBox(string title, string body, Color bgColor, Color titleColor, Color bodyColor)
|
|
{
|
|
GUI.backgroundColor = bgColor;
|
|
EditorGUILayout.BeginVertical(GUI.skin.box);
|
|
if (!string.IsNullOrEmpty(title))
|
|
{
|
|
GUI.color = titleColor;
|
|
GUILayout.Label(title, EditorStyles.boldLabel);
|
|
}
|
|
if (!string.IsNullOrEmpty(body))
|
|
{
|
|
GUI.color = bodyColor;
|
|
GUILayout.Label(body, EditorStyles.wordWrappedLabel);
|
|
}
|
|
}
|
|
|
|
internal static void EndWarningTextBox()
|
|
{
|
|
EditorGUILayout.EndVertical();
|
|
GUI.backgroundColor = Color.white;
|
|
GUI.color = Color.white;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays an IMGUI box containing a copyable string that wraps
|
|
/// Usedful for very long strings eg file paths/urls
|
|
/// </summary>
|
|
internal static void CopyableFilename(string path)
|
|
{
|
|
// The box disappars unless it has some content
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
path = " ";
|
|
}
|
|
|
|
// Display the file name so it's easy to read and copy to the clipboard
|
|
if (!string.IsNullOrEmpty(path) && 0 > path.IndexOfAny(System.IO.Path.GetInvalidPathChars()))
|
|
{
|
|
// Some GUI hacks here because SelectableLabel wants to be double height and it doesn't want to be centered because it's an EditorGUILayout function...
|
|
string text = System.IO.Path.GetFileName(path);
|
|
|
|
if (_copyableStyle == null)
|
|
{
|
|
_copyableStyle = new GUIStyle(EditorStyles.wordWrappedLabel);
|
|
_copyableStyle.fontStyle = FontStyle.Bold;
|
|
_copyableStyle.stretchWidth = true;
|
|
_copyableStyle.stretchHeight = true;
|
|
_copyableStyle.alignment = TextAnchor.MiddleCenter;
|
|
_copyableStyle.margin.top = 8;
|
|
_copyableStyle.margin.bottom = 16;
|
|
}
|
|
|
|
float height = _copyableStyle.CalcHeight(new GUIContent(text), Screen.width)*1.5f;
|
|
EditorGUILayout.SelectableLabel(text, _copyableStyle, GUILayout.Height(height), GUILayout.ExpandHeight(false), GUILayout.ExpandWidth(true));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
internal static GUIStyle GetWordWrappedTextAreaStyle()
|
|
{
|
|
if (_wordWrappedTextAreaStyle == null)
|
|
{
|
|
_wordWrappedTextAreaStyle = new GUIStyle(EditorStyles.textArea);
|
|
_wordWrappedTextAreaStyle.wordWrap = true;
|
|
}
|
|
return _wordWrappedTextAreaStyle;
|
|
}
|
|
|
|
internal static GUIStyle GetRightAlignedLabelStyle()
|
|
{
|
|
if (_rightAlignedLabelStyle == null)
|
|
{
|
|
_rightAlignedLabelStyle = new GUIStyle(GUI.skin.label);
|
|
_rightAlignedLabelStyle.alignment = TextAnchor.UpperRight;
|
|
}
|
|
return _rightAlignedLabelStyle;
|
|
}
|
|
|
|
internal static GUIStyle GetCenterAlignedLabelStyle()
|
|
{
|
|
if (_centerAlignedLabelStyle == null)
|
|
{
|
|
_centerAlignedLabelStyle = new GUIStyle(GUI.skin.label);
|
|
_centerAlignedLabelStyle.alignment = TextAnchor.MiddleCenter;
|
|
}
|
|
return _centerAlignedLabelStyle;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays IMGUI box in red/yellow for errors/warnings
|
|
/// </summary>
|
|
internal static void NoticeBox(MessageType messageType, string message)
|
|
{
|
|
//GUI.backgroundColor = Color.yellow;
|
|
//EditorGUILayout.HelpBox(message, messageType);
|
|
|
|
switch (messageType)
|
|
{
|
|
case MessageType.Error:
|
|
GUI.color = Color.red;
|
|
message = "Error: " + message;
|
|
break;
|
|
case MessageType.Warning:
|
|
GUI.color = Color.yellow;
|
|
message = "Warning: " + message;
|
|
break;
|
|
}
|
|
|
|
//GUI.color = Color.yellow;
|
|
GUILayout.TextArea(message);
|
|
GUI.color = Color.white;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays IMGUI text centered horizontally
|
|
/// </summary>
|
|
internal static void CentreLabel(string text, GUIStyle style = null)
|
|
{
|
|
GUILayout.BeginHorizontal();
|
|
GUILayout.FlexibleSpace();
|
|
if (style == null)
|
|
{
|
|
GUILayout.Label(text);
|
|
}
|
|
else
|
|
{
|
|
GUILayout.Label(text, style);
|
|
}
|
|
GUILayout.FlexibleSpace();
|
|
GUILayout.EndHorizontal();
|
|
}
|
|
|
|
internal static bool ToggleScriptDefine(string label, string define)
|
|
{
|
|
EditorGUI.BeginChangeCheck();
|
|
bool isEnabled = EditorGUILayout.Toggle(label, EditorHelper.HasScriptDefine(define));
|
|
if (EditorGUI.EndChangeCheck())
|
|
{
|
|
if (isEnabled)
|
|
{
|
|
EditorHelper.AddScriptDefine(define);
|
|
}
|
|
else
|
|
{
|
|
EditorHelper.RemoveScriptDefine(define);
|
|
}
|
|
}
|
|
return isEnabled;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class HorizontalFlowScope : GUI.Scope
|
|
{
|
|
private float _windowWidth;
|
|
private float _width;
|
|
|
|
public HorizontalFlowScope(int windowWidth)
|
|
{
|
|
_windowWidth = windowWidth;
|
|
_width = _windowWidth;
|
|
GUILayout.BeginHorizontal();
|
|
}
|
|
|
|
protected override void CloseScope()
|
|
{
|
|
GUILayout.EndHorizontal();
|
|
}
|
|
|
|
public void AddItem(GUIContent content, GUIStyle style)
|
|
{
|
|
_width -= style.CalcSize(content).x + style.padding.horizontal;
|
|
if (_width <= 0f)
|
|
{
|
|
_width += Screen.width;
|
|
GUILayout.EndHorizontal();
|
|
GUILayout.BeginHorizontal();
|
|
}
|
|
}
|
|
}
|
|
} |