using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using System.Linq;
using System;
using System.Reflection;
using Object = UnityEngine.Object;
using MaskIntr = UnityEngine.SpriteMaskInteraction;
using System.IO;
namespace Coffee.UIExtensions.Editors
{
///
/// SoftMaskable editor.
///
[CustomEditor (typeof (SoftMaskable))]
[CanEditMultipleObjects]
public class SoftMaskableEditor : Editor
{
//################################
// Constant or Static Members.
//################################
public enum MaskInteraction : int
{
VisibleInsideMask = (1 << 0) + (1 << 2) + (1 << 4) + (1 << 6),
VisibleOutsideMask = (2 << 0) + (2 << 2) + (2 << 4) + (2 << 6),
Custom = -1,
}
MaskInteraction maskInteraction
{
get
{
int value = _spMaskInteraction.intValue;
return _custom
? MaskInteraction.Custom
: System.Enum.IsDefined(typeof(MaskInteraction), value)
? (MaskInteraction)value
: MaskInteraction.Custom;
}
set
{
_custom = (value == MaskInteraction.Custom);
if (!_custom)
{
_spMaskInteraction.intValue = (int)value;
}
}
}
bool _custom = false;
static readonly Type s_TypeTMPro = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_Text");
static readonly Type s_TypeTMP_SpriteAsset = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_SpriteAsset");
static readonly Type s_TypeTMProSettings = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_Settings");
static readonly Type s_TypeTMP_SubMesh = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_SubMesh");
static readonly Type s_TypeTMP_SubMeshUI = AppDomain.CurrentDomain.GetAssemblies ().SelectMany (x => x.GetTypes ()).FirstOrDefault (x => x.Name == "TMP_SubMeshUI");
static PropertyInfo s_PiFontSharedMaterial;
static PropertyInfo s_PiFontSharedMaterials;
static PropertyInfo s_PiSpriteAsset;
static PropertyInfo s_PiRichText;
static PropertyInfo s_PiText;
static PropertyInfo s_PiDefaultFontAssetPath;
static PropertyInfo s_PiDefaultSpriteAssetPath;
static FieldInfo s_FiMaterial;
static MethodInfo s_miGetSpriteAsset;
static readonly List s_Graphics = new List ();
Shader _shader;
Shader _mobileShader;
Shader _spriteShader;
List _materialEditors = new List ();
SerializedProperty _spMaskInteraction;
void OnEnable ()
{
_spMaskInteraction = serializedObject.FindProperty("m_MaskInteraction");
_custom = (maskInteraction == MaskInteraction.Custom);
ClearMaterialEditors ();
_shader = Shader.Find ("TextMeshPro/Distance Field (SoftMaskable)");
_mobileShader = Shader.Find ("TextMeshPro/Mobile/Distance Field (SoftMaskable)");
_spriteShader = Shader.Find ("TextMeshPro/Sprite (SoftMaskable)");
if(s_TypeTMPro != null)
{
s_PiFontSharedMaterial = s_TypeTMPro.GetProperty ("fontSharedMaterial");
s_PiSpriteAsset = s_TypeTMPro.GetProperty ("spriteAsset");
s_PiRichText = s_TypeTMPro.GetProperty ("richText");
s_PiText = s_TypeTMPro.GetProperty ("text");
s_FiMaterial = s_TypeTMP_SpriteAsset.GetField ("material");
s_PiFontSharedMaterials = s_TypeTMPro.GetProperty ("fontSharedMaterials");
s_miGetSpriteAsset = s_TypeTMProSettings.GetMethod ("GetSpriteAsset", BindingFlags.Static | BindingFlags.Public);
s_PiDefaultFontAssetPath = s_TypeTMProSettings.GetProperty ("defaultFontAssetPath", BindingFlags.Static | BindingFlags.Public);
s_PiDefaultSpriteAssetPath = s_TypeTMProSettings.GetProperty ("defaultSpriteAssetPath", BindingFlags.Static | BindingFlags.Public);
}
s_MaskWarning = new GUIContent(EditorGUIUtility.FindTexture("console.warnicon.sml"), "This component is not SoftMask. Use SoftMask instead of Mask.");
}
void OnDisable ()
{
ClearMaterialEditors ();
}
List tmpMasks = new List();
void DrawMaskInteractions()
{
(target as SoftMaskable).GetComponentsInParent(true, tmpMasks);
tmpMasks.RemoveAll(x => !x.enabled);
tmpMasks.Reverse();
maskInteraction = (MaskInteraction)EditorGUILayout.EnumPopup("Mask Interaction", maskInteraction);
if (_custom)
{
var l = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 45;
using (var ccs = new EditorGUI.ChangeCheckScope())
{
int intr0 = DrawMaskInteraction(0);
int intr1 = DrawMaskInteraction(1);
int intr2 = DrawMaskInteraction(2);
int intr3 = DrawMaskInteraction(3);
if (ccs.changed) {
_spMaskInteraction.intValue = (intr0 << 0) + (intr1 << 2) + (intr2 << 4) + (intr3 << 6);
}
}
EditorGUIUtility.labelWidth = l;
}
}
static GUIContent s_MaskWarning = new GUIContent();
int DrawMaskInteraction(int layer)
{
Mask mask = layer < tmpMasks.Count ? tmpMasks[layer] : null;
MaskIntr intr = (MaskIntr)((_spMaskInteraction.intValue >> layer * 2) & 0x3);
if (!mask)
{
return (int)intr;
}
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.LabelField(mask is SoftMask ? GUIContent.none : s_MaskWarning, GUILayout.Width(16));
GUILayout.Space(-5);
EditorGUILayout.ObjectField("Mask " + layer, mask, typeof(Mask), false);
GUILayout.Space(-15);
return (int)(MaskIntr)EditorGUILayout.EnumPopup(intr);
}
}
public override void OnInspectorGUI ()
{
base.OnInspectorGUI ();
serializedObject.Update();
DrawMaskInteractions();
// maskInteraction = (MaskInteraction)EditorGUILayout.EnumPopup("Mask Interaction", maskInteraction);
serializedObject.ApplyModifiedProperties();
/*
EditorGUI.indentLevel++;
var l = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth = 60;
using (new EditorGUILayout.HorizontalScope())
{
EditorGUILayout.ObjectField("Mask 0", null, typeof(Mask), false);
EditorGUILayout.EnumPopup (MaskIntr.None);
}
EditorGUIUtility.labelWidth = l;
EditorGUI.indentLevel--;
var spMaskInteraction = serializedObject.FindProperty ("m_MaskInteraction");
MaskIntr intr0 = (MaskIntr)((spMaskInteraction.intValue >> 0) & 0x3);
MaskIntr intr1 = (MaskIntr)((spMaskInteraction.intValue >> 2) & 0x3);
MaskIntr intr2 = (MaskIntr)((spMaskInteraction.intValue >> 4) & 0x3);
MaskIntr intr3 = (MaskIntr)((spMaskInteraction.intValue >> 6) & 0x3);
using (var ccs = new EditorGUI.ChangeCheckScope ()) {
intr0 = (MaskIntr)EditorGUILayout.EnumPopup ("Layer 0", intr0);
intr1 = (MaskIntr)EditorGUILayout.EnumPopup ("Layer 1", intr1);
intr2 = (MaskIntr)EditorGUILayout.EnumPopup ("Layer 2", intr2);
intr3 = (MaskIntr)EditorGUILayout.EnumPopup ("Layer 3", intr3);
if (ccs.changed) {
current.SetMaskInteractions (intr0,intr1,intr2,intr3);
}
}
*/
// spMaskInteraction.intValue = (intr0 << 0) | (intr1 << 2) | (intr2 << 4) | (intr3 << 6);
//
// serializedObject.ApplyModifiedProperties ();
var current = target as SoftMaskable;
current.GetComponentsInChildren (true, s_Graphics);
var fixTargets = s_Graphics.Where (x => x.gameObject != current.gameObject && !x.GetComponent () && (!x.GetComponent () || x.GetComponent ().showMaskGraphic)).ToList ();
if (0 < fixTargets.Count)
{
GUILayout.BeginHorizontal ();
EditorGUILayout.HelpBox ("There are child Graphics that does not have a SoftMaskable component.\nAdd SoftMaskable component to them.", MessageType.Warning);
GUILayout.BeginVertical ();
if (GUILayout.Button ("Fix"))
{
foreach (var p in fixTargets)
{
p.gameObject.AddComponent ();
}
}
if (GUILayout.Button ("Ping"))
{
EditorGUIUtility.PingObject (fixTargets [0]);
}
GUILayout.EndVertical ();
GUILayout.EndHorizontal ();
}
if(s_TypeTMPro != null)
{
ShowTMProWarning (_shader, _mobileShader, _spriteShader, m => { });
var textMeshPro = current.GetComponent (s_TypeTMPro);
if (textMeshPro != null)
{
Material [] fontSharedMaterials = s_PiFontSharedMaterials.GetValue (textMeshPro, new object [0]) as Material [];
ShowMaterialEditors (fontSharedMaterials, 1, fontSharedMaterials.Length - 1);
}
}
if (!DetectMask (current.transform.parent))
{
GUILayout.BeginHorizontal ();
EditorGUILayout.HelpBox ("This is unnecessary SoftMaskable.\nCan't find any SoftMask components above.", MessageType.Warning);
if (GUILayout.Button ("Remove", GUILayout.Height (40)))
{
DestroyImmediate (current);
Utils.MarkPrefabDirty ();
}
GUILayout.EndHorizontal ();
}
}
static bool DetectMask (Transform transform)
{
if (transform == null)
{
return false;
}
if (transform.GetComponent () != null)
{
return true;
}
return DetectMask (transform.parent);
}
void ClearMaterialEditors ()
{
foreach (var e in _materialEditors)
{
if (e)
{
DestroyImmediate (e);
}
}
_materialEditors.Clear ();
}
protected void ShowMaterialEditors (Material [] materials, int startIndex, int count)
{
for (int i = 0; i < count; i++)
{
if (_materialEditors.Count == i)
{
_materialEditors.Add (null);
}
var mat = materials [startIndex + i];
var editor = _materialEditors [i];
if (editor && editor.target != mat)
{
DestroyImmediate (editor);
editor = null;
}
if (!editor)
{
editor = _materialEditors [i] = Editor.CreateEditor (mat) as MaterialEditor;
}
editor.DrawHeader ();
editor.OnInspectorGUI ();
}
}
public void ShowTMProWarning (Shader shader, Shader mobileShader, Shader spriteShader, System.Action onCreatedMaterial)
{
var current = target as SoftMaskable;
var textMeshPro = current.GetComponent (s_TypeTMPro);
if (textMeshPro == null)
{
return;
}
Material fontSharedMaterial = s_PiFontSharedMaterial.GetValue (textMeshPro, new object [0]) as Material;
if (fontSharedMaterial == null)
{
return;
}
// Is the material preset for dissolve?
Material m = fontSharedMaterial;
if (m.shader != shader && m.shader != mobileShader)
{
EditorGUILayout.BeginHorizontal ();
EditorGUILayout.HelpBox (string.Format ("{0} requires '{1}' or '{2}' as a shader for material preset.", current.GetType ().Name, shader.name, mobileShader.name), MessageType.Warning);
if (GUILayout.Button ("Fix"))
{
var correctShader = m.shader.name.Contains ("Mobile") ? mobileShader : shader;
m = ModifyTMProMaterialPreset (m, correctShader, onCreatedMaterial);
s_PiFontSharedMaterial.SetValue (textMeshPro, m, new object [0]);
}
EditorGUILayout.EndHorizontal ();
return;
}
// Is the sprite asset for dissolve?
object spriteAsset = s_PiSpriteAsset.GetValue (textMeshPro, new object [0]) ?? s_miGetSpriteAsset.Invoke (null, new object [0]);
//TMP_SpriteAsset spriteAsset = textMeshPro.spriteAsset ?? TMP_Settings.GetSpriteAsset ();
m = s_FiMaterial.GetValue (spriteAsset) as Material;
bool hasSprite = (bool)s_PiRichText.GetValue (textMeshPro, new object [0]) && (s_PiText.GetValue (textMeshPro, new object [0]) as string).Contains (" x.gameObject).ToList ().ForEach (DestroyImmediate);
current.GetComponentsInChildren (s_TypeTMP_SubMeshUI).Select (x => x.gameObject).ToList ().ForEach (DestroyImmediate);
spriteAsset = ModifyTMProSpriteAsset (m, _spriteShader, mat => { });
s_PiSpriteAsset.SetValue (textMeshPro, spriteAsset, new object [0]);
}
EditorGUILayout.EndHorizontal ();
return;
}
}
Material ModifyTMProMaterialPreset (Material baseMaterial, Shader shader, System.Action onCreatedMaterial)
{
string path = AssetDatabase.GetAssetPath (baseMaterial);
string filename = Path.GetFileNameWithoutExtension (path) + " (" + typeof (SoftMaskable).Name + ")";
string defaultAssetPath = s_PiDefaultFontAssetPath.GetValue (null, new object [0]) as string;
Material mat = Resources.Load (defaultAssetPath + filename);
if (!mat)
{
mat = new Material (baseMaterial)
{
shaderKeywords = baseMaterial.shaderKeywords,
shader = shader,
};
onCreatedMaterial (mat);
AssetDatabase.CreateAsset (mat, Path.GetDirectoryName (path) + "/" + filename + ".mat");
EditorUtility.FocusProjectWindow ();
EditorGUIUtility.PingObject (mat);
}
else
{
mat.shader = shader;
}
EditorUtility.SetDirty (mat);
return mat;
}
object ModifyTMProSpriteAsset (Material baseMaterial, Shader shader, System.Action onCreatedMaterial)
{
string path = AssetDatabase.GetAssetPath (baseMaterial);
string filename = Path.GetFileNameWithoutExtension (path) + " (" + typeof (SoftMaskable).Name + ")";
string defaultAssetPath = s_PiDefaultSpriteAssetPath.GetValue (null, new object [0]) as string;
Object spriteAsset = Resources.Load (defaultAssetPath + filename, s_TypeTMP_SpriteAsset);
if (spriteAsset == null)
{
AssetDatabase.CopyAsset (path, Path.GetDirectoryName (path) + "/" + filename + ".mat");
spriteAsset = Resources.Load (defaultAssetPath + filename, s_TypeTMP_SpriteAsset);
Material m = s_FiMaterial.GetValue (spriteAsset) as Material;
m.shader = shader;
m.name = shader.name;
onCreatedMaterial (m);
EditorUtility.FocusProjectWindow ();
EditorGUIUtility.PingObject (spriteAsset);
}
else
{
Material m = s_FiMaterial.GetValue (spriteAsset) as Material;
m.shader = shader;
}
EditorUtility.SetDirty (spriteAsset);
return spriteAsset;
}
}
}