TMP_CharacterPropertyDrawer.cs 10.8 KB
using UnityEngine;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using UnityEditor;
using System.Collections;


namespace TMPro.EditorUtilities
{
    [CustomPropertyDrawer(typeof(TMP_Character))]
    public class TMP_CharacterPropertyDrawer : PropertyDrawer
    {
        private string k_ColorProperty = "_Color";

        int m_GlyphSelectedForEditing = -1;

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            SerializedProperty prop_Unicode = property.FindPropertyRelative("m_Unicode");
            SerializedProperty prop_GlyphIndex = property.FindPropertyRelative("m_GlyphIndex");
            SerializedProperty prop_Scale = property.FindPropertyRelative("m_Scale");


            GUIStyle style = new GUIStyle(EditorStyles.label);
            style.richText = true;

            EditorGUIUtility.labelWidth = 40f;
            EditorGUIUtility.fieldWidth = 50;

            Rect rect = new Rect(position.x + 50, position.y, position.width, 49);

            // Display non-editable fields
            if (GUI.enabled == false)
            {
                int unicode = prop_Unicode.intValue;
                EditorGUI.LabelField(new Rect(rect.x, rect.y, 120f, 18), new GUIContent("Unicode: <color=#FFFF80>0x" + unicode.ToString("X") + "</color>"), style);
                EditorGUI.LabelField(new Rect(rect.x + 115, rect.y, 120f, 18), unicode <= 0xFFFF ? new GUIContent("UTF16: <color=#FFFF80>\\u" + unicode.ToString("X4") + "</color>") : new GUIContent("UTF32: <color=#FFFF80>\\U" + unicode.ToString("X8") + "</color>"), style);
                EditorGUI.LabelField(new Rect(rect.x, rect.y + 18, 120, 18), new GUIContent("Glyph ID: <color=#FFFF80>" + prop_GlyphIndex.intValue + "</color>"), style);
                EditorGUI.LabelField(new Rect(rect.x, rect.y + 36, 80, 18), new GUIContent("Scale: <color=#FFFF80>" + prop_Scale.floatValue + "</color>"), style);

                // Draw Glyph (if exists)
                DrawGlyph(position, property);
            }
            else // Display editable fields
            {
                EditorGUIUtility.labelWidth = 55f;
                GUI.SetNextControlName("Unicode Input");
                EditorGUI.BeginChangeCheck();
                string unicode = EditorGUI.TextField(new Rect(rect.x, rect.y, 120, 18), "Unicode:", prop_Unicode.intValue.ToString("X"));

                if (GUI.GetNameOfFocusedControl() == "Unicode Input")
                {
                    //Filter out unwanted characters.
                    char chr = Event.current.character;
                    if ((chr < '0' || chr > '9') && (chr < 'a' || chr > 'f') && (chr < 'A' || chr > 'F'))
                    {
                        Event.current.character = '\0';
                    }
                }

                if (EditorGUI.EndChangeCheck())
                {
                    // Update Unicode value
                    prop_Unicode.intValue = TMP_TextUtilities.StringHexToInt(unicode);
                }

                // Cache current glyph index in case it needs to be restored if the new glyph index is invalid.
                int currentGlyphIndex = prop_GlyphIndex.intValue;

                EditorGUIUtility.labelWidth = 59f;
                EditorGUI.BeginChangeCheck();
                EditorGUI.DelayedIntField(new Rect(rect.x, rect.y + 18, 100, 18), prop_GlyphIndex, new GUIContent("Glyph ID:"));
                if (EditorGUI.EndChangeCheck())
                {
                    // Get a reference to the font asset
                    TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset;

                    // Make sure new glyph index is valid.
                    int elementIndex = fontAsset.glyphTable.FindIndex(item => item.index == prop_GlyphIndex.intValue);

                    if (elementIndex == -1)
                        prop_GlyphIndex.intValue = currentGlyphIndex;
                    else
                        fontAsset.IsFontAssetLookupTablesDirty = true;
                }

                int glyphIndex = prop_GlyphIndex.intValue;

                // Reset glyph selection if new character has been selected.
                if (GUI.enabled && m_GlyphSelectedForEditing != glyphIndex)
                    m_GlyphSelectedForEditing = -1;

                // Display button to edit the glyph data.
                if (GUI.Button(new Rect(rect.x + 120, rect.y + 18, 75, 18), new GUIContent("Edit Glyph")))
                {
                    if (m_GlyphSelectedForEditing == -1)
                        m_GlyphSelectedForEditing = glyphIndex;
                    else
                        m_GlyphSelectedForEditing = -1;

                    // Button clicks should not result in potential change.
                    GUI.changed = false;
                }

                // Show the glyph property drawer if selected
                if (glyphIndex == m_GlyphSelectedForEditing && GUI.enabled)
                {
                    // Get a reference to the font asset
                    TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset;

                    if (fontAsset != null)
                    {
                        // Get the index of the glyph in the font asset glyph table.
                        int elementIndex = fontAsset.glyphTable.FindIndex(item => item.index == glyphIndex);

                        if (elementIndex != -1)
                        {
                            SerializedProperty prop_GlyphTable = property.serializedObject.FindProperty("m_GlyphTable");
                            SerializedProperty prop_Glyph = prop_GlyphTable.GetArrayElementAtIndex(elementIndex);

                            SerializedProperty prop_GlyphMetrics = prop_Glyph.FindPropertyRelative("m_Metrics");
                            SerializedProperty prop_GlyphRect = prop_Glyph.FindPropertyRelative("m_GlyphRect");

                            Rect newRect = EditorGUILayout.GetControlRect(false, 115);
                            EditorGUI.DrawRect(new Rect(newRect.x + 52, newRect.y - 20, newRect.width - 52, newRect.height - 5), new Color(0.1f, 0.1f, 0.1f, 0.45f));
                            EditorGUI.DrawRect(new Rect(newRect.x + 53, newRect.y - 19, newRect.width - 54, newRect.height - 7), new Color(0.3f, 0.3f, 0.3f, 0.8f));

                            // Display GlyphRect
                            newRect.x += 55;
                            newRect.y -= 18;
                            newRect.width += 5;
                            EditorGUI.PropertyField(newRect, prop_GlyphRect);

                            // Display GlyphMetrics
                            newRect.y += 45;
                            EditorGUI.PropertyField(newRect, prop_GlyphMetrics);

                            rect.y += 120;
                        }
                    }
                }

                EditorGUIUtility.labelWidth = 39f;
                EditorGUI.PropertyField(new Rect(rect.x, rect.y + 36, 80, 18), prop_Scale, new GUIContent("Scale:"));

                // Draw Glyph (if exists)
                DrawGlyph(position, property);
            }
        }

        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            return 58;
        }

        void DrawGlyph(Rect position, SerializedProperty property)
        {
            // Get a reference to the atlas texture
            TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset;

            if (fontAsset == null)
                return;

            // Get a reference to the Glyph Table
            SerializedProperty prop_GlyphTable = property.serializedObject.FindProperty("m_GlyphTable");
            int glyphIndex = property.FindPropertyRelative("m_GlyphIndex").intValue;
            int elementIndex = fontAsset.glyphTable.FindIndex(item => item.index == glyphIndex);

            // Return if we can't find the glyph
            if (elementIndex == -1)
                return;

            SerializedProperty prop_Glyph = prop_GlyphTable.GetArrayElementAtIndex(elementIndex);

            // Get reference to atlas texture.
            int atlasIndex = prop_Glyph.FindPropertyRelative("m_AtlasIndex").intValue;
            Texture2D atlasTexture = fontAsset.atlasTextures.Length > atlasIndex ? fontAsset.atlasTextures[atlasIndex] : null;

            if (atlasTexture == null)
                return;

            Material mat;
            if (((GlyphRasterModes)fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP)
            {
                mat = TMP_FontAssetEditor.internalBitmapMaterial;

                if (mat == null)
                    return;

                mat.mainTexture = atlasTexture;
                mat.SetColor(k_ColorProperty, Color.white);
            }
            else
            {
                mat = TMP_FontAssetEditor.internalSDFMaterial;

                if (mat == null)
                    return;

                mat.mainTexture = atlasTexture;
                mat.SetFloat(ShaderUtilities.ID_GradientScale, fontAsset.atlasPadding + 1);
            }

            // Draw glyph
            Rect glyphDrawPosition = new Rect(position.x, position.y, 48, 58);

            SerializedProperty glyphRectProperty = prop_Glyph.FindPropertyRelative("m_GlyphRect");

            int padding = fontAsset.atlasPadding;

            int glyphOriginX = glyphRectProperty.FindPropertyRelative("m_X").intValue - padding;
            int glyphOriginY = glyphRectProperty.FindPropertyRelative("m_Y").intValue - padding;
            int glyphWidth = glyphRectProperty.FindPropertyRelative("m_Width").intValue + padding * 2;
            int glyphHeight = glyphRectProperty.FindPropertyRelative("m_Height").intValue + padding * 2;

            float normalizedHeight = fontAsset.faceInfo.ascentLine - fontAsset.faceInfo.descentLine;
            float scale = glyphDrawPosition.width / normalizedHeight;

            // Compute the normalized texture coordinates
            Rect texCoords = new Rect((float)glyphOriginX / atlasTexture.width, (float)glyphOriginY / atlasTexture.height, (float)glyphWidth / atlasTexture.width, (float)glyphHeight / atlasTexture.height);

            if (Event.current.type == EventType.Repaint)
            {
                glyphDrawPosition.x += (glyphDrawPosition.width - glyphWidth * scale) / 2;
                glyphDrawPosition.y += (glyphDrawPosition.height - glyphHeight * scale) / 2;
                glyphDrawPosition.width = glyphWidth * scale;
                glyphDrawPosition.height = glyphHeight * scale;

                // Could switch to using the default material of the font asset which would require passing scale to the shader.
                Graphics.DrawTexture(glyphDrawPosition, atlasTexture, texCoords, 0, 0, 0, 0, new Color(1f, 1f, 1f), mat);
            }
        }

    }
}