2018-12-18 18:21:25 +08:00
using System.Collections.Generic ;
2018-12-17 09:08:40 +08:00
using UnityEngine ;
using UnityEngine.UI ;
using UnityEditor ;
using System.Linq ;
using System ;
using System.Reflection ;
using Object = UnityEngine . Object ;
2019-01-26 20:30:41 +08:00
using MaskIntr = UnityEngine . SpriteMaskInteraction ;
2018-12-17 09:08:40 +08:00
using System.IO ;
namespace Coffee.UIExtensions.Editors
{
/// <summary>
2018-12-18 18:21:25 +08:00
/// SoftMaskable editor.
2018-12-17 09:08:40 +08:00
/// </summary>
2018-12-18 18:21:25 +08:00
[CustomEditor (typeof (SoftMaskable))]
2018-12-17 09:08:40 +08:00
[CanEditMultipleObjects]
public class SoftMaskableEditor : Editor
{
2019-01-26 20:30:41 +08:00
//################################
// 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 ;
2018-12-18 18:21:25 +08:00
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 ;
2018-12-17 09:08:40 +08:00
static readonly List < Graphic > s_Graphics = new List < Graphic > ( ) ;
Shader _shader ;
Shader _mobileShader ;
Shader _spriteShader ;
2018-12-18 18:21:25 +08:00
List < MaterialEditor > _materialEditors = new List < MaterialEditor > ( ) ;
2019-01-26 20:30:41 +08:00
SerializedProperty _spMaskInteraction ;
2018-12-17 09:08:40 +08:00
private void OnEnable ( )
{
2019-01-26 20:30:41 +08:00
_spMaskInteraction = serializedObject . FindProperty ( "m_MaskInteraction" ) ;
_custom = ( maskInteraction = = MaskInteraction . Custom ) ;
2018-12-17 09:08:40 +08:00
ClearMaterialEditors ( ) ;
_shader = Shader . Find ( "TextMeshPro/Distance Field (SoftMaskable)" ) ;
_mobileShader = Shader . Find ( "TextMeshPro/Mobile/Distance Field (SoftMaskable)" ) ;
_spriteShader = Shader . Find ( "TextMeshPro/Sprite (SoftMaskable)" ) ;
2018-12-18 18:21:25 +08:00
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 ) ;
}
2019-01-26 20:30:41 +08:00
s_MaskWarning = new GUIContent ( EditorGUIUtility . FindTexture ( "console.warnicon.sml" ) , "This component is not SoftMask. Use SoftMask instead of Mask." ) ;
2018-12-17 09:08:40 +08:00
}
private void OnDisable ( )
{
ClearMaterialEditors ( ) ;
}
2019-01-26 20:30:41 +08:00
List < Mask > tmpMasks = new List < Mask > ( ) ;
void DrawMaskInteractions ( )
{
( target as SoftMaskable ) . GetComponentsInParent < Mask > ( 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 ) ;
}
}
2018-12-17 09:08:40 +08:00
public override void OnInspectorGUI ( )
{
base . OnInspectorGUI ( ) ;
2019-01-26 20:30:41 +08:00
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;
2018-12-17 09:08:40 +08:00
var current = target as SoftMaskable ;
current . GetComponentsInChildren < Graphic > ( true , s_Graphics ) ;
var fixTargets = s_Graphics . Where ( x = > x . gameObject ! = current . gameObject & & ! x . GetComponent < SoftMaskable > ( ) & & ( ! x . GetComponent < Mask > ( ) | | x . GetComponent < Mask > ( ) . showMaskGraphic ) ) . ToList ( ) ;
if ( 0 < fixTargets . Count )
{
GUILayout . BeginHorizontal ( ) ;
EditorGUILayout . HelpBox ( "There are child Graphicss 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 < SoftMaskable > ( ) ;
}
}
if ( GUILayout . Button ( "Ping" ) )
{
EditorGUIUtility . PingObject ( fixTargets [ 0 ] ) ;
}
GUILayout . EndVertical ( ) ;
GUILayout . EndHorizontal ( ) ;
}
2018-12-18 18:21:25 +08:00
if ( s_TypeTMPro ! = null )
2018-12-17 09:08:40 +08:00
{
2018-12-18 18:21:25 +08:00
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 ) ;
}
2018-12-17 09:08:40 +08:00
}
}
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 < Material > onCreatedMaterial )
{
var current = target as SoftMaskable ;
var textMeshPro = current . GetComponent ( s_TypeTMPro ) ;
2018-12-18 18:21:25 +08:00
if ( textMeshPro = = null )
2018-12-17 09:08:40 +08:00
{
return ;
}
2018-12-18 18:21:25 +08:00
Material fontSharedMaterial = s_PiFontSharedMaterial . GetValue ( textMeshPro , new object [ 0 ] ) as Material ;
2018-12-17 09:08:40 +08:00
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 ) ;
2018-12-18 18:21:25 +08:00
s_PiFontSharedMaterial . SetValue ( textMeshPro , m , new object [ 0 ] ) ;
2018-12-17 09:08:40 +08:00
}
EditorGUILayout . EndHorizontal ( ) ;
return ;
}
// Is the sprite asset for dissolve?
2018-12-18 18:21:25 +08:00
object spriteAsset = s_PiSpriteAsset . GetValue ( textMeshPro , new object [ 0 ] ) ? ? s_miGetSpriteAsset . Invoke ( null , new object [ 0 ] ) ;
2018-12-17 09:08:40 +08:00
//TMP_SpriteAsset spriteAsset = textMeshPro.spriteAsset ?? TMP_Settings.GetSpriteAsset ();
2018-12-18 18:21:25 +08:00
m = s_FiMaterial . GetValue ( spriteAsset ) as Material ;
2018-12-17 09:08:40 +08:00
bool hasSprite = ( bool ) s_PiRichText . GetValue ( textMeshPro , new object [ 0 ] ) & & ( s_PiText . GetValue ( textMeshPro , new object [ 0 ] ) as string ) . Contains ( "<sprite=" ) ;
if ( m & & m . shader ! = spriteShader & & hasSprite )
{
EditorGUILayout . BeginHorizontal ( ) ;
EditorGUILayout . HelpBox ( string . Format ( "{0} requires '{1}' as a shader for sprite asset." , GetType ( ) . Name , spriteShader . name ) , MessageType . Warning ) ;
if ( GUILayout . Button ( "Fix" ) )
{
current . GetComponentsInChildren ( s_TypeTMP_SubMesh ) . Select ( x = > x . gameObject ) . ToList ( ) . ForEach ( DestroyImmediate ) ;
current . GetComponentsInChildren ( s_TypeTMP_SubMeshUI ) . Select ( x = > x . gameObject ) . ToList ( ) . ForEach ( DestroyImmediate ) ;
2018-12-18 18:21:25 +08:00
spriteAsset = ModifyTMProSpriteAsset ( m , _spriteShader , mat = > { } ) ;
s_PiSpriteAsset . SetValue ( textMeshPro , spriteAsset , new object [ 0 ] ) ;
2018-12-17 09:08:40 +08:00
}
EditorGUILayout . EndHorizontal ( ) ;
return ;
}
}
Material ModifyTMProMaterialPreset ( Material baseMaterial , Shader shader , System . Action < Material > onCreatedMaterial )
{
string path = AssetDatabase . GetAssetPath ( baseMaterial ) ;
2018-12-18 18:21:25 +08:00
string filename = Path . GetFileNameWithoutExtension ( path ) + " (" + typeof ( SoftMaskable ) . Name + ")" ;
2018-12-17 09:08:40 +08:00
string defaultAssetPath = s_PiDefaultFontAssetPath . GetValue ( null , new object [ 0 ] ) as string ;
Material mat = Resources . Load < Material > ( 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 < Material > 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 ;
}
}
}