整合SLSUtilities
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace LunaWolfStudiosEditor.ScriptableSheets.Shared
|
||||
{
|
||||
public static class DrawUtility
|
||||
{
|
||||
public static void TableAssetPreview(Object obj, Rect propertyRect, AssetPreviewSettings assetPreviewSettings)
|
||||
{
|
||||
if (!assetPreviewSettings.Show || obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var preview = AssetPreview.GetAssetPreview(obj) ?? AssetPreview.GetMiniThumbnail(obj);
|
||||
if (preview == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var previewRect = propertyRect;
|
||||
previewRect.y += EditorGUIUtility.singleLineHeight;
|
||||
previewRect.height -= EditorGUIUtility.singleLineHeight;
|
||||
UnityEngine.GUI.DrawTexture(previewRect, preview, assetPreviewSettings.ScaleMode);
|
||||
}
|
||||
|
||||
public static class GUI
|
||||
{
|
||||
public static bool ToggleCenter(Rect propertyRect, bool value)
|
||||
{
|
||||
var centerPoint = (propertyRect.width - 10) / 2;
|
||||
propertyRect.x += centerPoint;
|
||||
propertyRect.width -= centerPoint;
|
||||
return EditorGUI.Toggle(propertyRect, value);
|
||||
}
|
||||
|
||||
public static uint UIntField(Rect propertyRect, uint value)
|
||||
{
|
||||
var textValue = EditorGUI.TextField(propertyRect, value.ToString());
|
||||
return uint.TryParse(textValue, out uint newValue) ? newValue : value;
|
||||
}
|
||||
|
||||
public static ulong ULongField(Rect propertyRect, ulong value)
|
||||
{
|
||||
var textValue = EditorGUI.TextField(propertyRect, value.ToString());
|
||||
return ulong.TryParse(textValue, out ulong newValue) ? newValue : value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f6f0a4625cdefd45bd99ab11d317c79
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284559
|
||||
packageName: Scriptable Sheets
|
||||
packageVersion: 1.8.0
|
||||
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Shared/Utilities/DrawUtility.cs
|
||||
uploadId: 823456
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace LunaWolfStudiosEditor.ScriptableSheets.Shared
|
||||
{
|
||||
public static class EnumUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the first set flag from a valid flagged enum. Returns the default value if no flags are set.
|
||||
/// </summary>
|
||||
public static T FirstFlagOrDefault<T>(this T flaggedEnum) where T : Enum
|
||||
{
|
||||
if (!Attribute.IsDefined(typeof(T), typeof(FlagsAttribute)))
|
||||
{
|
||||
throw new ArgumentException($"{typeof(T).FullName} is not a flagged enum.");
|
||||
}
|
||||
return Enum.GetValues(typeof(T)).Cast<T>().FirstOrDefault(f => (int) (object) f != 0 && flaggedEnum.HasFlag(f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa522b44eb54f184a846731609049939
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284559
|
||||
packageName: Scriptable Sheets
|
||||
packageVersion: 1.8.0
|
||||
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Shared/Utilities/EnumUtility.cs
|
||||
uploadId: 823456
|
||||
@@ -0,0 +1,27 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using PackageInfo = UnityEditor.PackageManager.PackageInfo;
|
||||
using PackageSource = UnityEditor.PackageManager.PackageSource;
|
||||
|
||||
namespace LunaWolfStudiosEditor.ScriptableSheets.Shared
|
||||
{
|
||||
public static class PackageUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns true if the provided Object is part of an immutable Package.
|
||||
/// </summary>
|
||||
public static bool IsAssetImmutable(Object asset)
|
||||
{
|
||||
return IsAssetImmutable(AssetDatabase.GetAssetPath(asset));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the provided asset path is part of an immutable Package.
|
||||
/// </summary>
|
||||
public static bool IsAssetImmutable(string assetPath)
|
||||
{
|
||||
var assetPackage = PackageInfo.FindForAssetPath(assetPath);
|
||||
return assetPackage != null && assetPackage.source != PackageSource.Embedded && assetPackage.source != PackageSource.Local;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91e48cc30d988f94283e05ecf2f8a8a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284559
|
||||
packageName: Scriptable Sheets
|
||||
packageVersion: 1.8.0
|
||||
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Shared/Utilities/PackageUtility.cs
|
||||
uploadId: 823456
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Profiling;
|
||||
|
||||
namespace LunaWolfStudiosEditor.ScriptableSheets.Shared
|
||||
{
|
||||
public static class ReflectionUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Searches all inherited types of the target type for the specified property path.
|
||||
/// Returns the field info for each part of the property path.
|
||||
/// </summary>
|
||||
public static FieldInfo[] GetNestedFieldInfo(Type rootType, string propertyPath)
|
||||
{
|
||||
Profiler.BeginSample(nameof(GetNestedFieldInfo));
|
||||
var targetType = rootType;
|
||||
var fieldNameParts = propertyPath.Replace(UnityConstants.ArrayPropertyPath, "[").Split('.');
|
||||
var nestedFieldInfo = new List<FieldInfo>();
|
||||
FieldInfo field = null;
|
||||
var index = 0;
|
||||
var collectionTypes = new Queue<Type>();
|
||||
while (targetType != null && index < fieldNameParts.Length)
|
||||
{
|
||||
var fieldNamePart = fieldNameParts[index];
|
||||
if (fieldNamePart.Contains('['))
|
||||
{
|
||||
// Get the field name before the array indexer.
|
||||
var fieldName = fieldNamePart.Substring(0, fieldNamePart.IndexOf('['));
|
||||
field = targetType.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
}
|
||||
else
|
||||
{
|
||||
field = targetType.GetField(fieldNamePart, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
}
|
||||
if (field != null)
|
||||
{
|
||||
index++;
|
||||
nestedFieldInfo.Add(field);
|
||||
if (nestedFieldInfo.Count < fieldNameParts.Length)
|
||||
{
|
||||
targetType = field.FieldType;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Field was not found. If the current type is a collection let's track its respective element or argument type.
|
||||
if (targetType.IsArray)
|
||||
{
|
||||
collectionTypes.Enqueue(targetType.GetElementType());
|
||||
}
|
||||
else if (targetType.IsGenericType)
|
||||
{
|
||||
collectionTypes.Enqueue(targetType.GetGenericArguments()[0]);
|
||||
}
|
||||
targetType = targetType.BaseType;
|
||||
if (targetType == null && collectionTypes.Count > 0)
|
||||
{
|
||||
// We've reached the end of this target type. Let's check the previous collection types.
|
||||
targetType = collectionTypes.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
Profiler.EndSample();
|
||||
if (nestedFieldInfo.Count > 0 && nestedFieldInfo.Count == fieldNameParts.Length)
|
||||
{
|
||||
return nestedFieldInfo.ToArray();
|
||||
}
|
||||
Debug.LogWarning($"Unable to find field at path '{propertyPath}' for type '{rootType}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Type GetNestedFieldType(Type rootType, string propertyPath)
|
||||
{
|
||||
var nestedFields = GetNestedFieldInfo(rootType, propertyPath);
|
||||
if (nestedFields != null)
|
||||
{
|
||||
var lastFieldType = nestedFields.Last().FieldType;
|
||||
if (lastFieldType.IsArray)
|
||||
{
|
||||
return lastFieldType.GetElementType();
|
||||
}
|
||||
else if (lastFieldType.IsGenericType)
|
||||
{
|
||||
return lastFieldType.GetGenericArguments()[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return lastFieldType;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b04c3fd57a218541860918496752161
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284559
|
||||
packageName: Scriptable Sheets
|
||||
packageVersion: 1.8.0
|
||||
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Shared/Utilities/ReflectionUtility.cs
|
||||
uploadId: 823456
|
||||
@@ -0,0 +1,824 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEditorInternal;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace LunaWolfStudiosEditor.ScriptableSheets.Shared
|
||||
{
|
||||
public static class SerializedPropertyUtility
|
||||
{
|
||||
public static readonly Object[] UnityBuiltInAssets;
|
||||
|
||||
private static readonly List<SerializedPropertyType> s_InputFieldPropertyTypes = new List<SerializedPropertyType>()
|
||||
{
|
||||
SerializedPropertyType.Integer,
|
||||
SerializedPropertyType.Float,
|
||||
SerializedPropertyType.String,
|
||||
SerializedPropertyType.ArraySize,
|
||||
SerializedPropertyType.Character,
|
||||
};
|
||||
|
||||
// Some Object fields are marked as editable when they shouldn't be. This list helps catch those.
|
||||
private static readonly List<string> s_ReadOnlyUnityFields = new List<string>() { UnityConstants.Field.Script };
|
||||
|
||||
// Unity has a duplicate StaticEditorFlags value for ContributeGI and LightmapStatic so we remove one of them.
|
||||
private static readonly string[] s_StaticEditorFlags = Enum.GetNames(typeof(StaticEditorFlags)).Where(name => name != "LightmapStatic").ToArray();
|
||||
private static Type s_StringTableCollectionType;
|
||||
|
||||
private static readonly GUIContent s_CreateButtonContent = EditorGUIUtility.IconContent("Toolbar Plus");
|
||||
private const float CreateButtonWidth = 20f;
|
||||
|
||||
static SerializedPropertyUtility()
|
||||
{
|
||||
UnityBuiltInAssets = AssetDatabase.LoadAllAssetsAtPath(UnityConstants.Path.BuiltInExtra);
|
||||
}
|
||||
|
||||
// Draw our own fields to eliminate unwanted header and text attributes that property field uses by default.
|
||||
public static void DrawProperty(this SerializedProperty property, Rect propertyRect, Object rootObject, bool isCustomField, out bool arraySizeChanged, bool showReadOnly, AssetPreviewSettings previewSettings, string defaultNewAssetPath, Action<ScriptableObject, Type> onObjectCreated)
|
||||
{
|
||||
arraySizeChanged = false;
|
||||
// There are some fields on other Unity Asset types that are inaccessible. So only draw our own custom fields.
|
||||
if (isCustomField)
|
||||
{
|
||||
var propertyType = property.propertyType;
|
||||
switch (propertyType)
|
||||
{
|
||||
case SerializedPropertyType.Integer:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
if (property.IsTypeInt())
|
||||
{
|
||||
property.intValue = EditorGUI.IntField(propertyRect, property.intValue);
|
||||
}
|
||||
else if (property.IsTypeLong())
|
||||
{
|
||||
if (!showReadOnly && property.propertyPath.EndsWith(UnityConstants.Field.TableEntryReferenceKeyId))
|
||||
{
|
||||
DrawTableEntryKeyDropdown(propertyRect, property);
|
||||
}
|
||||
else
|
||||
{
|
||||
property.longValue = EditorGUI.LongField(propertyRect, property.longValue);
|
||||
}
|
||||
}
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
else if (property.IsTypeUInt())
|
||||
{
|
||||
property.uintValue = DrawUtility.GUI.UIntField(propertyRect, property.uintValue);
|
||||
}
|
||||
else if (property.IsTypeULong())
|
||||
{
|
||||
property.ulongValue = DrawUtility.GUI.ULongField(propertyRect, property.ulongValue);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
property.intValue = EditorGUI.IntField(propertyRect, property.intValue);
|
||||
}
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.ArraySize:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
var previousValue = property.intValue;
|
||||
property.intValue = EditorGUI.IntField(propertyRect, property.intValue);
|
||||
if (previousValue != property.intValue)
|
||||
{
|
||||
arraySizeChanged = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.Boolean:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
property.boolValue = DrawUtility.GUI.ToggleCenter(propertyRect, property.boolValue);
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.Float:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
if (property.IsTypeFloat())
|
||||
{
|
||||
property.floatValue = EditorGUI.FloatField(propertyRect, property.floatValue);
|
||||
}
|
||||
else if (property.IsTypeDouble())
|
||||
{
|
||||
property.doubleValue = EditorGUI.DoubleField(propertyRect, property.floatValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
property.floatValue = EditorGUI.FloatField(propertyRect, property.floatValue);
|
||||
}
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.String:
|
||||
if (!showReadOnly && property.propertyPath.EndsWith(UnityConstants.Field.TableReferenceCollectionName))
|
||||
{
|
||||
DrawLocalizationTablePicker(propertyRect, property, previewSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
property.stringValue = EditorGUI.TextField(propertyRect, property.stringValue);
|
||||
}
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.Color:
|
||||
property.colorValue = EditorGUI.ColorField(propertyRect, GUIContent.none, property.colorValue, true, true, false);
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.ObjectReference:
|
||||
// Only draw asset previews if the height is greater than 1.
|
||||
if (propertyRect.height > EditorGUIUtility.singleLineHeight)
|
||||
{
|
||||
DrawUtility.TableAssetPreview(property.objectReferenceValue, propertyRect, previewSettings);
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
}
|
||||
if (!IsDefaultUnityEngineType(rootObject))
|
||||
{
|
||||
var objectType = ReflectionUtility.GetNestedFieldType(rootObject.GetType(), property.propertyPath);
|
||||
if (objectType != null)
|
||||
{
|
||||
// If the Object is null and it's a ScriptableObject then give the option to create a new instance of it directly from here.
|
||||
if (property.objectReferenceValue == null && typeof(ScriptableObject).IsAssignableFrom(objectType))
|
||||
{
|
||||
var objectRect = new Rect(propertyRect.x, propertyRect.y, propertyRect.width - CreateButtonWidth, propertyRect.height);
|
||||
var plusRect = new Rect(objectRect.xMax, propertyRect.y, CreateButtonWidth, propertyRect.height);
|
||||
property.objectReferenceValue = EditorGUI.ObjectField(objectRect, property.objectReferenceValue, objectType, false);
|
||||
s_CreateButtonContent.tooltip = $"Create new {objectType.Name} and auto-assign";
|
||||
if (GUI.Button(plusRect, s_CreateButtonContent))
|
||||
{
|
||||
var newObject = ScriptableObject.CreateInstance(objectType);
|
||||
var guids = AssetDatabase.FindAssets($"t:{objectType.Name}");
|
||||
var folderPath = defaultNewAssetPath;
|
||||
// First check if any existing asset path exists.
|
||||
if (guids != null && guids.Length > 0)
|
||||
{
|
||||
var fullPath = AssetDatabase.GUIDToAssetPath(guids[0]);
|
||||
var newFolderPath = System.IO.Path.GetDirectoryName(fullPath);
|
||||
// If they exist in a valid folder than use that. Otherwise use the default new asset path.
|
||||
if (AssetDatabase.IsValidFolder(newFolderPath))
|
||||
{
|
||||
folderPath = newFolderPath;
|
||||
}
|
||||
}
|
||||
var filename = $"New{objectType.Name}{UnityConstants.Extensions.Asset}";
|
||||
var uniquePath = AssetDatabase.GenerateUniqueAssetPath($"{folderPath}/{filename}");
|
||||
AssetDatabase.CreateAsset(newObject, uniquePath);
|
||||
AssetDatabase.SaveAssets();
|
||||
EditorGUIUtility.PingObject(newObject);
|
||||
Selection.activeObject = newObject;
|
||||
property.objectReferenceValue = newObject;
|
||||
onObjectCreated?.Invoke(newObject, objectType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
property.objectReferenceValue = EditorGUI.ObjectField(propertyRect, property.objectReferenceValue, objectType, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
|
||||
}
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.LayerMask:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
property.intValue = EditorGUI.MaskField(propertyRect, GUIContent.none, property.intValue, InternalEditorUtility.layers);
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.Enum:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
if (!IsDefaultUnityEngineType(rootObject, out string fullTypeName) && (!fullTypeName.StartsWith(UnityConstants.Type.TMPro) || IsAlignmentProperty(property.propertyPath)) && property.TryGetEnumType(rootObject, out Type enumType))
|
||||
{
|
||||
if (enumType.HasFlagsAttribute())
|
||||
{
|
||||
// Remove none and bitwise combination values.
|
||||
var filteredEnumNames = new List<string>();
|
||||
foreach (var value in Enum.GetValues(enumType))
|
||||
{
|
||||
var intValue = (int) value;
|
||||
if (intValue != 0 && (intValue & (intValue - 1)) == 0)
|
||||
{
|
||||
filteredEnumNames.Add(value.ToString());
|
||||
}
|
||||
}
|
||||
property.intValue = EditorGUI.MaskField(propertyRect, GUIContent.none, property.intValue, filteredEnumNames.ToArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
property.intValue = Convert.ToInt32(EditorGUI.EnumPopup(propertyRect, (Enum) Enum.ToObject(enumType, property.intValue)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Special case for UnityEngine.UI.Slider Component to remove extra space when rendering the Direction property.
|
||||
if (fullTypeName == UnityConstants.Type.UnityEngineUISlider && property.propertyPath == UnityConstants.Field.Direction)
|
||||
{
|
||||
propertyRect.y -= 8;
|
||||
}
|
||||
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
|
||||
}
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.Character:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
var stringValue = EditorGUI.TextField(propertyRect, ((char) property.intValue).ToString());
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
{
|
||||
property.intValue = stringValue[0];
|
||||
}
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.AnimationCurve:
|
||||
property.animationCurveValue = EditorGUI.CurveField(propertyRect, property.animationCurveValue);
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.Gradient:
|
||||
var internalPropertyHeight = EditorGUI.GetPropertyHeight(property, false);
|
||||
if (internalPropertyHeight > EditorGUIUtility.singleLineHeight)
|
||||
{
|
||||
var gradientValue = property.GetGradientValue();
|
||||
property.SetGradientValue(EditorGUI.GradientField(propertyRect, gradientValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Workaround for Unity bug where EditorGUI.GradientField bleeds outside of its rect.
|
||||
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
|
||||
}
|
||||
break;
|
||||
|
||||
case SerializedPropertyType.Generic:
|
||||
default:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw Unity layer and tag fields accordingly.
|
||||
switch (property.propertyPath)
|
||||
{
|
||||
case UnityConstants.Field.Layer:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
property.intValue = EditorGUI.LayerField(propertyRect, property.intValue);
|
||||
break;
|
||||
|
||||
case UnityConstants.Field.StaticEditorFlags:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
var newValue = EditorGUI.MaskField(propertyRect, property.intValue, s_StaticEditorFlags);
|
||||
// Unity uses -1 to represent everything in a mask field, but StaticEditorFlags uses int.MaxValue to represent everything.
|
||||
if (newValue <= -1)
|
||||
{
|
||||
newValue = int.MaxValue;
|
||||
}
|
||||
property.intValue = newValue;
|
||||
break;
|
||||
|
||||
case UnityConstants.Field.Tag:
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
property.stringValue = EditorGUI.TagField(propertyRect, property.stringValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (property.propertyType == SerializedPropertyType.Boolean)
|
||||
{
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
property.boolValue = DrawUtility.GUI.ToggleCenter(propertyRect, property.boolValue);
|
||||
}
|
||||
else if (property.propertyType == SerializedPropertyType.String)
|
||||
{
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
property.stringValue = EditorGUI.TextField(propertyRect, property.stringValue);
|
||||
}
|
||||
else if (property.propertyType == SerializedPropertyType.ObjectReference)
|
||||
{
|
||||
// Only draw asset previews if the height is greater than 1.
|
||||
if (propertyRect.height > EditorGUIUtility.singleLineHeight)
|
||||
{
|
||||
DrawUtility.TableAssetPreview(property.objectReferenceValue, propertyRect, previewSettings);
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
}
|
||||
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyRect.height = EditorGUIUtility.singleLineHeight;
|
||||
EditorGUI.PropertyField(propertyRect, property, GUIContent.none, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static SerializedPropertyType GetSheetsPropertyType(this SerializedProperty property, bool isScriptableObject)
|
||||
{
|
||||
if (isScriptableObject)
|
||||
{
|
||||
return property.propertyType;
|
||||
}
|
||||
// Handle special cases like Layers and Tags where Unity considers them int and string fields respectively.
|
||||
switch (property.propertyPath)
|
||||
{
|
||||
case UnityConstants.Field.Layer:
|
||||
return SerializedPropertyType.LayerMask;
|
||||
|
||||
case UnityConstants.Field.StaticEditorFlags:
|
||||
case UnityConstants.Field.Tag:
|
||||
return SerializedPropertyType.Enum;
|
||||
|
||||
default:
|
||||
return property.propertyType;
|
||||
}
|
||||
}
|
||||
|
||||
public static string FriendlyPropertyPath(this SerializedProperty property)
|
||||
{
|
||||
return FriendlyPropertyPath(property.propertyPath, property.displayName);
|
||||
}
|
||||
|
||||
public static string FriendlyPropertyPath(string propertyPath, string displayName)
|
||||
{
|
||||
var friendlyPath = propertyPath;
|
||||
if (friendlyPath.Contains('.'))
|
||||
{
|
||||
friendlyPath = friendlyPath.Replace(UnityConstants.ArrayPropertyPath, "[");
|
||||
friendlyPath = friendlyPath.Replace($".{UnityConstants.Field.Array}.{UnityConstants.Field.ArraySize}", $".{UnityConstants.Field.ArraySize}");
|
||||
friendlyPath = friendlyPath.Replace("m_", string.Empty);
|
||||
if (friendlyPath.Length > 1)
|
||||
{
|
||||
var lastIndex = friendlyPath.LastIndexOf('.');
|
||||
if (lastIndex > 0)
|
||||
{
|
||||
var secondLastIndex = friendlyPath.LastIndexOf('.', lastIndex - 1);
|
||||
if (secondLastIndex > -1)
|
||||
{
|
||||
friendlyPath = friendlyPath.Substring(secondLastIndex + 1);
|
||||
}
|
||||
else if (friendlyPath.Contains(']') && !friendlyPath.Contains("]."))
|
||||
{
|
||||
var lastSegment = friendlyPath.Substring(lastIndex + 1);
|
||||
friendlyPath = lastSegment;
|
||||
}
|
||||
}
|
||||
friendlyPath = friendlyPath.Replace('.', ' ').Trim();
|
||||
friendlyPath = Regex.Replace(friendlyPath, @"<([^>]+)>k__BackingField", "$1");
|
||||
friendlyPath = Regex.Replace(friendlyPath, @"(\p{Ll})(\p{Lu})", "$1 $2");
|
||||
friendlyPath = Regex.Replace(friendlyPath, @"\b[a-z]", c => c.Value.ToUpper());
|
||||
if (!string.IsNullOrEmpty(friendlyPath))
|
||||
{
|
||||
return friendlyPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
return displayName;
|
||||
}
|
||||
|
||||
// Needs to handle edge cases like:
|
||||
// Sprite PPtr<$Sprite>
|
||||
// PPtr<Material PPtr<Material>
|
||||
public static string FriendlyType(string type)
|
||||
{
|
||||
var lastOpen = type.LastIndexOf('<');
|
||||
var lastClose = type.LastIndexOf('>');
|
||||
if (lastOpen != -1 && lastClose != -1 && lastOpen < lastClose)
|
||||
{
|
||||
var lastValue = type.Substring(lastOpen + 1, lastClose - lastOpen - 1);
|
||||
return lastValue.Replace("$", string.Empty);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static string FriendlyType(this SerializedProperty property)
|
||||
{
|
||||
return FriendlyType(property.type);
|
||||
}
|
||||
|
||||
public static string GetFloatStringValue(this SerializedProperty property)
|
||||
{
|
||||
// We can't use SerializedProperty.numericType until 2022.1 so we can roll our own by checking type.
|
||||
switch (property.type)
|
||||
{
|
||||
case UnityConstants.Type.Float:
|
||||
return property.floatValue.ToString();
|
||||
|
||||
case UnityConstants.Type.Double:
|
||||
return property.doubleValue.ToString();
|
||||
|
||||
default:
|
||||
return property.floatValue.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetIntStringValue(this SerializedProperty property)
|
||||
{
|
||||
switch (property.type)
|
||||
{
|
||||
case UnityConstants.Type.Int:
|
||||
return property.intValue.ToString();
|
||||
|
||||
case UnityConstants.Type.Long:
|
||||
return property.longValue.ToString();
|
||||
|
||||
case UnityConstants.Type.UInt:
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
return property.uintValue.ToString();
|
||||
#else
|
||||
return property.intValue.ToString();
|
||||
#endif
|
||||
case UnityConstants.Type.ULong:
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
return property.ulongValue.ToString();
|
||||
#else
|
||||
return property.longValue.ToString();
|
||||
#endif
|
||||
default:
|
||||
return property.intValue.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TrySetFloatValue(this SerializedProperty property, string value)
|
||||
{
|
||||
switch (property.type)
|
||||
{
|
||||
case UnityConstants.Type.Float:
|
||||
if (float.TryParse(value, out float floatValue))
|
||||
{
|
||||
property.floatValue = floatValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
case UnityConstants.Type.Double:
|
||||
if (double.TryParse(value, out double doubleValue))
|
||||
{
|
||||
property.doubleValue = doubleValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
default:
|
||||
if (float.TryParse(value, out float defaultValue))
|
||||
{
|
||||
property.floatValue = defaultValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TrySetIntValue(this SerializedProperty property, string value)
|
||||
{
|
||||
switch (property.type)
|
||||
{
|
||||
case UnityConstants.Type.Int:
|
||||
if (int.TryParse(value, out int intValue))
|
||||
{
|
||||
property.intValue = intValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
case UnityConstants.Type.Long:
|
||||
if (long.TryParse(value, out long longValue))
|
||||
{
|
||||
property.longValue = longValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
case UnityConstants.Type.UInt:
|
||||
if (uint.TryParse(value, out uint uintValue))
|
||||
{
|
||||
property.uintValue = uintValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
case UnityConstants.Type.ULong:
|
||||
if (ulong.TryParse(value, out ulong ulongValue))
|
||||
{
|
||||
property.ulongValue = ulongValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
if (int.TryParse(value, out int defaultValue))
|
||||
{
|
||||
property.intValue = defaultValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Gradient GetGradientValue(this SerializedProperty property)
|
||||
{
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
return property.gradientValue;
|
||||
#else
|
||||
var propertyInfo = property.GetGradientPropertyInfo();
|
||||
return propertyInfo?.GetValue(property, null) as Gradient;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void SetGradientValue(this SerializedProperty property, Gradient newValue)
|
||||
{
|
||||
#if UNITY_2022_1_OR_NEWER
|
||||
property.gradientValue = newValue;
|
||||
#else
|
||||
var propertyInfo = property.GetGradientPropertyInfo();
|
||||
propertyInfo?.SetValue(property, newValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !UNITY_2022_1_OR_NEWER
|
||||
private static System.Reflection.PropertyInfo GetGradientPropertyInfo(this SerializedProperty property)
|
||||
{
|
||||
var propertyName = "gradientValue";
|
||||
var bindingFlags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
|
||||
System.Reflection.PropertyInfo propertyInfo = typeof(SerializedProperty).GetProperty(propertyName, bindingFlags);
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
return propertyInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Unable to find property '{propertyName}' for {nameof(SerializedProperty)} at {property.propertyPath}.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public static bool IsArraySizeOrElement(this SerializedProperty property)
|
||||
{
|
||||
return property.propertyType == SerializedPropertyType.ArraySize || property.IsArrayElement();
|
||||
}
|
||||
|
||||
public static bool IsArrayElement(this SerializedProperty property)
|
||||
{
|
||||
return IsArrayElement(property.propertyPath);
|
||||
}
|
||||
|
||||
public static bool IsArrayElement(string propertyPath)
|
||||
{
|
||||
return propertyPath.Contains('[');
|
||||
}
|
||||
|
||||
public static bool IsAssetReference(this SerializedProperty property)
|
||||
{
|
||||
return property.type.Contains(UnityConstants.Type.AssetReference) && property.FindPropertyRelative(UnityConstants.Field.AssetRefGuid) != null;
|
||||
}
|
||||
|
||||
public static bool IsInputFieldProperty(this SerializedProperty property, bool isScriptableObject)
|
||||
{
|
||||
var propertyType = property.GetSheetsPropertyType(isScriptableObject);
|
||||
return s_InputFieldPropertyTypes.Contains(propertyType);
|
||||
}
|
||||
|
||||
public static bool IsPropertyVisible(this SerializedProperty property, bool showArrays, bool showReadOnly, out bool isReadOnly)
|
||||
{
|
||||
var isArraySizeOrElement = property.IsArraySizeOrElement();
|
||||
isReadOnly = property.IsReadOnly();
|
||||
return (showArrays || !isArraySizeOrElement) && (showReadOnly || !isReadOnly) && !property.hasVisibleChildren
|
||||
&& property.propertyType != SerializedPropertyType.Generic && property.propertyType != SerializedPropertyType.ManagedReference;
|
||||
}
|
||||
|
||||
public static bool IsReadOnly(this SerializedProperty property)
|
||||
{
|
||||
return s_ReadOnlyUnityFields.Contains(property.name) || !property.editable;
|
||||
}
|
||||
|
||||
public static bool IsTypeDouble(this SerializedProperty property)
|
||||
{
|
||||
return property.type == UnityConstants.Type.Double;
|
||||
}
|
||||
|
||||
public static bool IsTypeFloat(this SerializedProperty property)
|
||||
{
|
||||
return property.type == UnityConstants.Type.Float;
|
||||
}
|
||||
|
||||
public static bool IsTypeInt(this SerializedProperty property)
|
||||
{
|
||||
return property.type == UnityConstants.Type.Int;
|
||||
}
|
||||
|
||||
public static bool IsTypeLong(this SerializedProperty property)
|
||||
{
|
||||
return property.type == UnityConstants.Type.Long;
|
||||
}
|
||||
|
||||
public static bool IsTypeUInt(this SerializedProperty property)
|
||||
{
|
||||
return property.type == UnityConstants.Type.UInt;
|
||||
}
|
||||
|
||||
public static bool IsTypeULong(this SerializedProperty property)
|
||||
{
|
||||
return property.type == UnityConstants.Type.ULong;
|
||||
}
|
||||
|
||||
public static bool TryGetEnumType(this SerializedProperty property, Object rootObject, out Type enumType)
|
||||
{
|
||||
var enumPropertyPath = property.propertyPath;
|
||||
enumType = ReflectionUtility.GetNestedFieldType(rootObject.GetType(), enumPropertyPath);
|
||||
if (enumType != null && enumType.IsEnum)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Property on {nameof(Object)} {rootObject.name} at path {enumPropertyPath} is not a valid type of enum.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Various default Unity Engine Components have different limitations on how backing fields are serialized and accessed.
|
||||
private static bool IsDefaultUnityEngineType(Object rootObject)
|
||||
{
|
||||
return IsDefaultUnityEngineType(rootObject, out string fullTypeName);
|
||||
}
|
||||
|
||||
private static bool IsDefaultUnityEngineType(Object rootObject, out string fullTypeName)
|
||||
{
|
||||
fullTypeName = rootObject.GetType().FullName;
|
||||
return fullTypeName.StartsWith(UnityConstants.Type.UnityEngine);
|
||||
}
|
||||
|
||||
// Handle special case for TMPro alignment enum properties.
|
||||
private static bool IsAlignmentProperty(string propertyPath)
|
||||
{
|
||||
return propertyPath == UnityConstants.Field.HorizontalAlignment || propertyPath == UnityConstants.Field.VerticalAlignment || propertyPath == UnityConstants.Field.TextAlignment;
|
||||
}
|
||||
|
||||
private static Type GetSharedTableCollectionType()
|
||||
{
|
||||
if (s_StringTableCollectionType == null)
|
||||
{
|
||||
s_StringTableCollectionType = Type.GetType($"{UnityConstants.Type.UnityLocalizationSharedTableData}, {UnityConstants.Type.UnityLocalization}");
|
||||
}
|
||||
return s_StringTableCollectionType;
|
||||
}
|
||||
|
||||
// Get GUID from "GUID:xxxx" or return null.
|
||||
private static string GetGuidFrom(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (!value.StartsWith(UnityConstants.Guid, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return value.Substring(UnityConstants.Guid.Length);
|
||||
}
|
||||
|
||||
private static void DrawLocalizationTablePicker(Rect rect, SerializedProperty property, AssetPreviewSettings previewSettings)
|
||||
{
|
||||
var sharedTableType = GetSharedTableCollectionType();
|
||||
|
||||
// If the localization package isn't installed then fallback to a simple text field.
|
||||
if (sharedTableType == null)
|
||||
{
|
||||
property.stringValue = EditorGUI.TextField(rect, property.stringValue);
|
||||
return;
|
||||
}
|
||||
|
||||
Object currentValue = null;
|
||||
var guid = GetGuidFrom(property.stringValue);
|
||||
if (!string.IsNullOrEmpty(guid))
|
||||
{
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
currentValue = AssetDatabase.LoadAssetAtPath(path, sharedTableType);
|
||||
}
|
||||
|
||||
if (rect.height > EditorGUIUtility.singleLineHeight)
|
||||
{
|
||||
DrawUtility.TableAssetPreview(currentValue, rect, previewSettings);
|
||||
rect.height = EditorGUIUtility.singleLineHeight;
|
||||
}
|
||||
|
||||
var newValue = EditorGUI.ObjectField(rect, currentValue, sharedTableType, false);
|
||||
if (currentValue != newValue)
|
||||
{
|
||||
if (newValue == null)
|
||||
{
|
||||
property.stringValue = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = AssetDatabase.GetAssetPath(newValue);
|
||||
var newGuid = AssetDatabase.AssetPathToGUID(path);
|
||||
property.stringValue = $"{UnityConstants.Guid}{newGuid}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawTableEntryKeyDropdown(Rect rect, SerializedProperty property)
|
||||
{
|
||||
var sharedTableType = GetSharedTableCollectionType();
|
||||
|
||||
// If the localization package isn't installed then fallback to a simple long field.
|
||||
if (sharedTableType == null)
|
||||
{
|
||||
property.longValue = EditorGUI.LongField(rect, property.longValue);
|
||||
return;
|
||||
}
|
||||
|
||||
var tableRefPath = property.propertyPath.Replace(UnityConstants.Field.TableEntryReferenceKeyId, UnityConstants.Field.TableReferenceCollectionName);
|
||||
var tableRefProp = property.serializedObject.FindProperty(tableRefPath);
|
||||
var guid = GetGuidFrom(tableRefProp.stringValue);
|
||||
|
||||
if (string.IsNullOrEmpty(guid))
|
||||
{
|
||||
EditorGUI.LabelField(rect, "<No Table Selected>");
|
||||
return;
|
||||
}
|
||||
|
||||
var path = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var sharedTable = AssetDatabase.LoadAssetAtPath(path, sharedTableType);
|
||||
if (sharedTable == null)
|
||||
{
|
||||
EditorGUI.LabelField(rect, "<Missing Table>");
|
||||
return;
|
||||
}
|
||||
|
||||
var sharedEntriesProp = sharedTableType.GetProperty(UnityConstants.Field.SharedTableDataEntries);
|
||||
if (sharedEntriesProp == null)
|
||||
{
|
||||
EditorGUI.LabelField(rect, "<Entries Not Found>");
|
||||
return;
|
||||
}
|
||||
|
||||
var entries = sharedEntriesProp.GetValue(sharedTable) as IEnumerable<object>;
|
||||
if (entries == null)
|
||||
{
|
||||
EditorGUI.LabelField(rect, "<No Entries>");
|
||||
return;
|
||||
}
|
||||
|
||||
var ids = new List<long>();
|
||||
var labels = new List<string>();
|
||||
|
||||
var entryType = entries.GetType().GetGenericArguments()[0];
|
||||
var idProp = entryType.GetProperty(UnityConstants.Field.SharedTableDataId);
|
||||
var keyProp = entryType.GetProperty(UnityConstants.Field.SharedTableDataKey);
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
var id = (long) idProp.GetValue(entry);
|
||||
ids.Add(id);
|
||||
|
||||
var key = (string) keyProp.GetValue(entry);
|
||||
labels.Add(key);
|
||||
}
|
||||
|
||||
if (ids.Count <= 0)
|
||||
{
|
||||
EditorGUI.LabelField(rect, "<Empty Table>");
|
||||
return;
|
||||
}
|
||||
|
||||
var currentIndex = ids.IndexOf(property.longValue);
|
||||
var newIndex = EditorGUI.Popup(rect, currentIndex, labels.ToArray());
|
||||
if (currentIndex != newIndex && newIndex >= 0 && newIndex < ids.Count)
|
||||
{
|
||||
property.longValue = ids[newIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cdc74d58b88c2c4f9dbc4e5db08d2d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284559
|
||||
packageName: Scriptable Sheets
|
||||
packageVersion: 1.8.0
|
||||
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Shared/Utilities/SerializedPropertyUtility.cs
|
||||
uploadId: 823456
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace LunaWolfStudiosEditor.ScriptableSheets.Shared
|
||||
{
|
||||
public static class StringUtility
|
||||
{
|
||||
public static string DecodeBase64(this string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
var bytes = Convert.FromBase64String(text);
|
||||
var decodedText = Encoding.UTF8.GetString(bytes);
|
||||
return decodedText;
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"Error decoding '{text}'.\n{ex.Message}");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public static string EncodeBase64(this string text)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(text);
|
||||
var encodedText = Convert.ToBase64String(bytes);
|
||||
return encodedText;
|
||||
}
|
||||
|
||||
public static string ExpandAll(this string text, int index, string type, int padding = 0)
|
||||
{
|
||||
return text.ExpandIndex(index, padding).ExpandType(type);
|
||||
}
|
||||
|
||||
public static string ExpandIndex(this string text, int index, int padding = 0)
|
||||
{
|
||||
return text.Replace("{i}", index.ToString(new string('0', padding)));
|
||||
}
|
||||
|
||||
public static string ExpandType(this string text, string type)
|
||||
{
|
||||
return text.Replace("{t}", type);
|
||||
}
|
||||
|
||||
public static string GetEscapedText(this string text)
|
||||
{
|
||||
text = text.Replace("\\", "\\\\");
|
||||
text = text.Replace("\r", "\\r");
|
||||
text = text.Replace("\n", "\\n");
|
||||
text = text.Replace("\t", "\\t");
|
||||
return text;
|
||||
}
|
||||
|
||||
public static string GetUnescapedText(this string text)
|
||||
{
|
||||
text = text.Replace("\\\\", "\\");
|
||||
text = text.Replace("\\r", "\r");
|
||||
text = text.Replace("\\n", "\n");
|
||||
text = text.Replace("\\t", "\t");
|
||||
return text;
|
||||
}
|
||||
|
||||
public static bool MatchesSearch(this string text, string searchTerm, SearchSettings settings)
|
||||
{
|
||||
var stringComparison = settings.CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
|
||||
if (settings.StartsWith)
|
||||
{
|
||||
return text.StartsWith(searchTerm, stringComparison);
|
||||
}
|
||||
else
|
||||
{
|
||||
return text.IndexOf(searchTerm, stringComparison) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static string UnwrapLayerMask(this string text)
|
||||
{
|
||||
// Unity wraps LayerMask values with 'LayerMask(#)' when copying from the Inspector.
|
||||
return text.Replace("LayerMask(", string.Empty).Replace(")", string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07224161ce7aed74ca00197f87183798
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284559
|
||||
packageName: Scriptable Sheets
|
||||
packageVersion: 1.8.0
|
||||
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Shared/Utilities/StringUtility.cs
|
||||
uploadId: 823456
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace LunaWolfStudiosEditor.ScriptableSheets.Shared
|
||||
{
|
||||
public static class TypeUtility
|
||||
{
|
||||
public static bool HasFlagsAttribute(this Type type)
|
||||
{
|
||||
return Attribute.IsDefined(type, typeof(FlagsAttribute));
|
||||
}
|
||||
|
||||
public static bool IsScriptableSingleton(this Type type)
|
||||
{
|
||||
while (type.IsValidUnityObjectSubclass())
|
||||
{
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ScriptableSingleton<>))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
type = type.BaseType;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsValidConcreteType(this Type type)
|
||||
{
|
||||
return !type.IsAbstract && !type.IsGenericType && !type.IsNested;
|
||||
}
|
||||
|
||||
public static bool IsValidUnityObjectSubclass(this Type type)
|
||||
{
|
||||
return type != null && type != typeof(ScriptableObject) && type != typeof(MonoBehaviour) && type != typeof(Object);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fb116e206a17fbe478001525e86e9da2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 284559
|
||||
packageName: Scriptable Sheets
|
||||
packageVersion: 1.8.0
|
||||
assetPath: Packages/com.lunawolfstudios.scriptablesheets/Editor/Modules/Shared/Utilities/TypeUtility.cs
|
||||
uploadId: 823456
|
||||
Reference in New Issue
Block a user