This commit is contained in:
2021-06-13 10:28:03 +02:00
parent eb70603c85
commit df2d24cbd3
7487 changed files with 943244 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
namespace TMPro.EditorUtilities
{
[CustomPropertyDrawer(typeof(TMP_Dropdown.OptionDataList), true)]
class DropdownOptionListDrawer : PropertyDrawer
{
private ReorderableList m_ReorderableList;
private void Init(SerializedProperty property)
{
if (m_ReorderableList != null)
return;
SerializedProperty array = property.FindPropertyRelative("m_Options");
m_ReorderableList = new ReorderableList(property.serializedObject, array);
m_ReorderableList.drawElementCallback = DrawOptionData;
m_ReorderableList.drawHeaderCallback = DrawHeader;
m_ReorderableList.elementHeight += 16;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
Init(property);
m_ReorderableList.DoList(position);
}
private void DrawHeader(Rect rect)
{
GUI.Label(rect, "Options");
}
private void DrawOptionData(Rect rect, int index, bool isActive, bool isFocused)
{
SerializedProperty itemData = m_ReorderableList.serializedProperty.GetArrayElementAtIndex(index);
SerializedProperty itemText = itemData.FindPropertyRelative("m_Text");
SerializedProperty itemImage = itemData.FindPropertyRelative("m_Image");
RectOffset offset = new RectOffset(0, 0, -1, -3);
rect = offset.Add(rect);
rect.height = EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(rect, itemText, GUIContent.none);
rect.y += EditorGUIUtility.singleLineHeight;
EditorGUI.PropertyField(rect, itemImage, GUIContent.none);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
Init(property);
return m_ReorderableList.GetHeight();
}
}
}

View File

@@ -0,0 +1,61 @@
/*
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
[CustomPropertyDrawer(typeof(TMP_Glyph))]
public class GlyphInfoDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty prop_id = property.FindPropertyRelative("id");
SerializedProperty prop_x = property.FindPropertyRelative("x");
SerializedProperty prop_y = property.FindPropertyRelative("y");
SerializedProperty prop_width = property.FindPropertyRelative("width");
SerializedProperty prop_height = property.FindPropertyRelative("height");
SerializedProperty prop_xOffset = property.FindPropertyRelative("xOffset");
SerializedProperty prop_yOffset = property.FindPropertyRelative("yOffset");
SerializedProperty prop_xAdvance = property.FindPropertyRelative("xAdvance");
SerializedProperty prop_scale = property.FindPropertyRelative("scale");
// We get Rect since a valid position may not be provided by the caller.
Rect rect = GUILayoutUtility.GetRect(position.width, 48);
rect.y -= 15;
//GUI.enabled = false;
EditorGUIUtility.labelWidth = 40f;
EditorGUIUtility.fieldWidth = 45f;
bool prevGuiState = GUI.enabled;
GUI.enabled = true;
EditorGUI.LabelField(new Rect(rect.x + 5f, rect.y, 80f, 18), new GUIContent("Ascii: <color=#FFFF80>" + prop_id.intValue + "</color>"), TMP_UIStyleManager.label);
EditorGUI.LabelField(new Rect(rect.x + 90f, rect.y, 80f, 18), new GUIContent("Hex: <color=#FFFF80>" + prop_id.intValue.ToString("X") + "</color>"), TMP_UIStyleManager.label);
EditorGUI.LabelField(new Rect(rect.x + 170f, rect.y, 80, 18), "Char: [ <color=#FFFF80>" + (char)prop_id.intValue + "</color> ]", TMP_UIStyleManager.label);
GUI.enabled = prevGuiState;
EditorGUIUtility.labelWidth = 35f;
EditorGUIUtility.fieldWidth = 10f;
float width = (rect.width - 5f) / 4;
EditorGUI.PropertyField(new Rect(rect.x + 5f + width * 0, rect.y + 22, width - 5f, 18), prop_x, new GUIContent("X:"));
EditorGUI.PropertyField(new Rect(rect.x + 5f + width * 1, rect.y + 22, width - 5f, 18), prop_y, new GUIContent("Y:"));
EditorGUI.PropertyField(new Rect(rect.x + 5f + width * 2, rect.y + 22, width - 5f, 18), prop_width, new GUIContent("W:"));
EditorGUI.PropertyField(new Rect(rect.x + 5f + width * 3, rect.y + 22, width - 5f, 18), prop_height, new GUIContent("H:"));
//GUI.enabled = true;
EditorGUI.PropertyField(new Rect(rect.x + 5f + width * 0, rect.y + 44, width - 5f, 18), prop_xOffset, new GUIContent("OX:"));
EditorGUI.PropertyField(new Rect(rect.x + 5f + width * 1, rect.y + 44, width - 5f, 18), prop_yOffset, new GUIContent("OY:"));
//GUI.enabled = true;
EditorGUI.PropertyField(new Rect(rect.x + 5f + width * 2, rect.y + 44, width - 5f, 18), prop_xAdvance, new GUIContent("ADV:"));
EditorGUI.PropertyField(new Rect(rect.x + 5f + width * 3, rect.y + 44, width - 5f, 18), prop_scale, new GUIContent("SF:"));
}
}
}
*/

View File

@@ -0,0 +1,53 @@
using UnityEngine;
using UnityEngine.TextCore;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
[CustomPropertyDrawer(typeof(GlyphMetrics))]
public class GlyphMetricsPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty prop_Width = property.FindPropertyRelative("m_Width");
SerializedProperty prop_Height = property.FindPropertyRelative("m_Height");
SerializedProperty prop_HoriBearingX = property.FindPropertyRelative("m_HorizontalBearingX");
SerializedProperty prop_HoriBearingY = property.FindPropertyRelative("m_HorizontalBearingY");
SerializedProperty prop_HoriAdvance = property.FindPropertyRelative("m_HorizontalAdvance");
// We get Rect since a valid position may not be provided by the caller.
Rect rect = new Rect(position.x, position.y, position.width, 49);
EditorGUI.LabelField(new Rect(rect.x, rect.y - 2.5f, rect.width, 18), new GUIContent("Glyph Metrics"));
EditorGUIUtility.labelWidth = 50f;
EditorGUIUtility.fieldWidth = 15f;
//GUI.enabled = false;
float width = (rect.width - 75f) / 2;
EditorGUI.PropertyField(new Rect(rect.x + width * 0, rect.y + 20, width - 5f, 18), prop_Width, new GUIContent("W:"));
EditorGUI.PropertyField(new Rect(rect.x + width * 1, rect.y + 20, width - 5f, 18), prop_Height, new GUIContent("H:"));
//GUI.enabled = true;
width = (rect.width - 75f) / 3;
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(new Rect(rect.x + width * 0, rect.y + 40, width - 5f, 18), prop_HoriBearingX, new GUIContent("BX:"));
EditorGUI.PropertyField(new Rect(rect.x + width * 1, rect.y + 40, width - 5f, 18), prop_HoriBearingY, new GUIContent("BY:"));
EditorGUI.PropertyField(new Rect(rect.x + width * 2, rect.y + 40, width - 5f, 18), prop_HoriAdvance, new GUIContent("AD:"));
if (EditorGUI.EndChangeCheck())
{
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 65f;
}
}
}

View File

@@ -0,0 +1,44 @@
using UnityEngine;
using UnityEngine.TextCore;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
[CustomPropertyDrawer(typeof(GlyphRect))]
public class GlyphRectPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
//EditorGUI.BeginProperty(position, label, property);
SerializedProperty prop_X = property.FindPropertyRelative("m_X");
SerializedProperty prop_Y = property.FindPropertyRelative("m_Y");
SerializedProperty prop_Width = property.FindPropertyRelative("m_Width");
SerializedProperty prop_Height = property.FindPropertyRelative("m_Height");
// We get Rect since a valid position may not be provided by the caller.
Rect rect = new Rect(position.x, position.y, position.width, 49);
EditorGUI.LabelField(new Rect(rect.x, rect.y - 2.5f, rect.width, 18), new GUIContent("Glyph Rect"));
EditorGUIUtility.labelWidth = 50f;
EditorGUIUtility.fieldWidth = 20f;
//GUI.enabled = false;
float width = (rect.width - 75f) / 4;
EditorGUI.PropertyField(new Rect(rect.x + width * 0, rect.y + 20, width - 5f, 18), prop_X, new GUIContent("X:"));
EditorGUI.PropertyField(new Rect(rect.x + width * 1, rect.y + 20, width - 5f, 18), prop_Y, new GUIContent("Y:"));
EditorGUI.PropertyField(new Rect(rect.x + width * 2, rect.y + 20, width - 5f, 18), prop_Width, new GUIContent("W:"));
EditorGUI.PropertyField(new Rect(rect.x + width * 3, rect.y + 20, width - 5f, 18), prop_Height, new GUIContent("H:"));
//EditorGUI.EndProperty();
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 45f;
}
}
}

View File

@@ -0,0 +1,622 @@
using UnityEngine;
using UnityEditor;
namespace TMPro.EditorUtilities
{
/// <summary>Base class for TextMesh Pro shader GUIs.</summary>
public abstract class TMP_BaseShaderGUI : ShaderGUI
{
/// <summary>Representation of a #pragma shader_feature.</summary>
/// <description>It is assumed that the first feature option is for no keyword (underscores).</description>
protected class ShaderFeature
{
public string undoLabel;
public GUIContent label;
/// <summary>The keyword labels, for display. Include the no-keyword as the first option.</summary>
public GUIContent[] keywordLabels;
/// <summary>The shader keywords. Exclude the no-keyword option.</summary>
public string[] keywords;
int m_State;
public bool Active
{
get { return m_State >= 0; }
}
public int State
{
get { return m_State; }
}
public void ReadState(Material material)
{
for (int i = 0; i < keywords.Length; i++)
{
if (material.IsKeywordEnabled(keywords[i]))
{
m_State = i;
return;
}
}
m_State = -1;
}
public void SetActive(bool active, Material material)
{
m_State = active ? 0 : -1;
SetStateKeywords(material);
}
public void DoPopup(MaterialEditor editor, Material material)
{
EditorGUI.BeginChangeCheck();
int selection = EditorGUILayout.Popup(label, m_State + 1, keywordLabels);
if (EditorGUI.EndChangeCheck())
{
m_State = selection - 1;
editor.RegisterPropertyChangeUndo(undoLabel);
SetStateKeywords(material);
}
}
void SetStateKeywords(Material material)
{
for (int i = 0; i < keywords.Length; i++)
{
if (i == m_State)
{
material.EnableKeyword(keywords[i]);
}
else
{
material.DisableKeyword(keywords[i]);
}
}
}
}
static GUIContent s_TempLabel = new GUIContent();
protected static bool s_DebugExtended;
static int s_UndoRedoCount, s_LastSeenUndoRedoCount;
static float[][] s_TempFloats =
{
null, new float[1], new float[2], new float[3], new float[4]
};
protected static GUIContent[] s_XywhVectorLabels =
{
new GUIContent("X"),
new GUIContent("Y"),
new GUIContent("W", "Width"),
new GUIContent("H", "Height")
};
protected static GUIContent[] s_LbrtVectorLabels =
{
new GUIContent("L", "Left"),
new GUIContent("B", "Bottom"),
new GUIContent("R", "Right"),
new GUIContent("T", "Top")
};
protected static GUIContent[] s_CullingTypeLabels =
{
new GUIContent("Off"),
new GUIContent("Front"),
new GUIContent("Back")
};
static TMP_BaseShaderGUI()
{
// Keep track of how many undo/redo events happened.
Undo.undoRedoPerformed += () => s_UndoRedoCount += 1;
}
bool m_IsNewGUI = true;
float m_DragAndDropMinY;
protected MaterialEditor m_Editor;
protected Material m_Material;
protected MaterialProperty[] m_Properties;
void PrepareGUI()
{
m_IsNewGUI = false;
ShaderUtilities.GetShaderPropertyIDs();
// New GUI just got constructed. This happens in response to a selection,
// but also after undo/redo events.
if (s_LastSeenUndoRedoCount != s_UndoRedoCount)
{
// There's been at least one undo/redo since the last time this GUI got constructed.
// Maybe the undo/redo was for this material? Assume that is was.
TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material as Material);
}
s_LastSeenUndoRedoCount = s_UndoRedoCount;
}
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
m_Editor = materialEditor;
m_Material = materialEditor.target as Material;
this.m_Properties = properties;
if (m_IsNewGUI)
{
PrepareGUI();
}
DoDragAndDropBegin();
EditorGUI.BeginChangeCheck();
DoGUI();
if (EditorGUI.EndChangeCheck())
{
TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, m_Material);
}
DoDragAndDropEnd();
}
/// <summary>Override this method to create the specific shader GUI.</summary>
protected abstract void DoGUI();
static string[] s_PanelStateLabel = new string[] { "\t- <i>Click to collapse</i> -", "\t- <i>Click to expand</i> -" };
protected bool BeginPanel(string panel, bool expanded)
{
EditorGUI.indentLevel = 0;
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
Rect r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 18));
r.x += 20;
r.width += 6;
bool enabled = GUI.enabled;
GUI.enabled = true;
expanded = TMP_EditorUtility.EditorToggle(r, expanded, new GUIContent(panel), TMP_UIStyleManager.panelTitle);
r.width -= 30;
EditorGUI.LabelField(r, new GUIContent(expanded ? s_PanelStateLabel[0] : s_PanelStateLabel[1]), TMP_UIStyleManager.rightLabel);
GUI.enabled = enabled;
EditorGUI.indentLevel += 1;
EditorGUI.BeginDisabledGroup(false);
return expanded;
}
protected bool BeginPanel(string panel, ShaderFeature feature, bool expanded, bool readState = true)
{
EditorGUI.indentLevel = 0;
if (readState)
{
feature.ReadState(m_Material);
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.BeginHorizontal();
Rect r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 20, GUILayout.Width(20f)));
bool active = EditorGUI.Toggle(r, feature.Active);
if (EditorGUI.EndChangeCheck())
{
m_Editor.RegisterPropertyChangeUndo(feature.undoLabel);
feature.SetActive(active, m_Material);
}
r = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(20, 18));
r.width += 6;
bool enabled = GUI.enabled;
GUI.enabled = true;
expanded = TMP_EditorUtility.EditorToggle(r, expanded, new GUIContent(panel), TMP_UIStyleManager.panelTitle);
r.width -= 10;
EditorGUI.LabelField(r, new GUIContent(expanded ? s_PanelStateLabel[0] : s_PanelStateLabel[1]), TMP_UIStyleManager.rightLabel);
GUI.enabled = enabled;
GUILayout.EndHorizontal();
EditorGUI.indentLevel += 1;
EditorGUI.BeginDisabledGroup(!active);
return expanded;
}
public void EndPanel()
{
EditorGUI.EndDisabledGroup();
EditorGUI.indentLevel -= 1;
EditorGUILayout.EndVertical();
}
MaterialProperty BeginProperty(string name)
{
MaterialProperty property = FindProperty(name, m_Properties);
EditorGUI.BeginChangeCheck();
EditorGUI.showMixedValue = property.hasMixedValue;
m_Editor.BeginAnimatedCheck(Rect.zero, property);
return property;
}
bool EndProperty()
{
m_Editor.EndAnimatedCheck();
EditorGUI.showMixedValue = false;
return EditorGUI.EndChangeCheck();
}
protected void DoPopup(string name, string label, GUIContent[] options)
{
MaterialProperty property = BeginProperty(name);
s_TempLabel.text = label;
int index = EditorGUILayout.Popup(s_TempLabel, (int)property.floatValue, options);
if (EndProperty())
{
property.floatValue = index;
}
}
protected void DoCubeMap(string name, string label)
{
DoTexture(name, label, typeof(Cubemap));
}
protected void DoTexture2D(string name, string label, bool withTilingOffset = false, string[] speedNames = null)
{
DoTexture(name, label, typeof(Texture2D), withTilingOffset, speedNames);
}
void DoTexture(string name, string label, System.Type type, bool withTilingOffset = false, string[] speedNames = null)
{
float objFieldSize = 60f;
bool smallLayout = EditorGUIUtility.currentViewWidth <= 440f && (withTilingOffset || speedNames != null);
float controlHeight = smallLayout ? objFieldSize * 2 : objFieldSize;
MaterialProperty property = FindProperty(name, m_Properties);
m_Editor.BeginAnimatedCheck(Rect.zero, property);
Rect rect = EditorGUILayout.GetControlRect(true, controlHeight);
float totalWidth = rect.width;
rect.width = EditorGUIUtility.labelWidth + objFieldSize;
rect.height = objFieldSize;
s_TempLabel.text = label;
EditorGUI.BeginChangeCheck();
Object tex = EditorGUI.ObjectField(rect, s_TempLabel, property.textureValue, type, false);
if (EditorGUI.EndChangeCheck())
{
property.textureValue = tex as Texture;
}
float additionalHeight = controlHeight - objFieldSize;
float xOffset = smallLayout ? rect.width - objFieldSize : rect.width;
rect.y += additionalHeight;
rect.x += xOffset;
rect.width = totalWidth - xOffset;
rect.height = EditorGUIUtility.singleLineHeight;
if (withTilingOffset)
{
DoTilingOffset(rect, property);
rect.y += (rect.height + 2f) * 2f;
}
m_Editor.EndAnimatedCheck();
if (speedNames != null)
{
DoUVSpeed(rect, speedNames);
}
}
void DoTilingOffset(Rect rect, MaterialProperty property)
{
float labelWidth = EditorGUIUtility.labelWidth;
int indentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
EditorGUIUtility.labelWidth = Mathf.Min(37f, rect.width * 0.40f);
Vector4 vector = property.textureScaleAndOffset;
bool changed = false;
float[] values = s_TempFloats[2];
s_TempLabel.text = "Tiling";
Rect vectorRect = EditorGUI.PrefixLabel(rect, s_TempLabel);
values[0] = vector.x;
values[1] = vector.y;
EditorGUI.BeginChangeCheck();
EditorGUI.MultiFloatField(vectorRect, s_XywhVectorLabels, values);
if (EditorGUI.EndChangeCheck())
{
vector.x = values[0];
vector.y = values[1];
changed = true;
}
rect.y += rect.height + 2f;
s_TempLabel.text = "Offset";
vectorRect = EditorGUI.PrefixLabel(rect, s_TempLabel);
values[0] = vector.z;
values[1] = vector.w;
EditorGUI.BeginChangeCheck();
EditorGUI.MultiFloatField(vectorRect, s_XywhVectorLabels, values);
if (EditorGUI.EndChangeCheck())
{
vector.z = values[0];
vector.w = values[1];
changed = true;
}
if (changed)
{
property.textureScaleAndOffset = vector;
}
EditorGUIUtility.labelWidth = labelWidth;
EditorGUI.indentLevel = indentLevel;
}
protected void DoUVSpeed(Rect rect, string[] names)
{
float labelWidth = EditorGUIUtility.labelWidth;
int indentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
EditorGUIUtility.labelWidth = Mathf.Min(37f, rect.width * 0.40f);
s_TempLabel.text = "Speed";
rect = EditorGUI.PrefixLabel(rect, s_TempLabel);
EditorGUIUtility.labelWidth = 10f;
rect.width = rect.width * 0.5f - 2f;
if (names.Length == 1)
{
DoFloat2(rect, names[0]);
}
else
{
DoFloat(rect, names[0], "X");
rect.x += rect.width + 4f;
DoFloat(rect, names[1], "Y");
}
EditorGUIUtility.labelWidth = labelWidth;
EditorGUI.indentLevel = indentLevel;
}
protected void DoToggle(string name, string label)
{
MaterialProperty property = BeginProperty(name);
s_TempLabel.text = label;
bool value = EditorGUILayout.Toggle(s_TempLabel, property.floatValue == 1f);
if (EndProperty())
{
property.floatValue = value ? 1f : 0f;
}
}
protected void DoFloat(string name, string label)
{
MaterialProperty property = BeginProperty(name);
Rect rect = EditorGUILayout.GetControlRect();
rect.width = EditorGUIUtility.labelWidth + 55f;
s_TempLabel.text = label;
float value = EditorGUI.FloatField(rect, s_TempLabel, property.floatValue);
if (EndProperty())
{
property.floatValue = value;
}
}
protected void DoColor(string name, string label)
{
MaterialProperty property = BeginProperty(name);
s_TempLabel.text = label;
Color value = EditorGUI.ColorField(EditorGUILayout.GetControlRect(), s_TempLabel, property.colorValue, false, true, true);
if (EndProperty())
{
property.colorValue = value;
}
}
void DoFloat(Rect rect, string name, string label)
{
MaterialProperty property = BeginProperty(name);
s_TempLabel.text = label;
float value = EditorGUI.FloatField(rect, s_TempLabel, property.floatValue);
if (EndProperty())
{
property.floatValue = value;
}
}
void DoFloat2(Rect rect, string name)
{
MaterialProperty property = BeginProperty(name);
float x = EditorGUI.FloatField(rect, "X", property.vectorValue.x);
rect.x += rect.width + 4f;
float y = EditorGUI.FloatField(rect, "Y", property.vectorValue.y);
if (EndProperty())
{
property.vectorValue = new Vector2(x, y);
}
}
protected void DoSlider(string name, string label)
{
MaterialProperty property = BeginProperty(name);
Vector2 range = property.rangeLimits;
s_TempLabel.text = label;
float value = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, property.floatValue, range.x, range.y);
if (EndProperty())
{
property.floatValue = value;
}
}
protected void DoSlider(string propertyName, string propertyField, string label)
{
MaterialProperty property = BeginProperty(propertyName);
Vector2 range = property.rangeLimits;
s_TempLabel.text = label;
Vector4 value = property.vectorValue;
switch (propertyField)
{
case "X":
value.x = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.x, range.x, range.y);
break;
case "Y":
value.y = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.y, range.x, range.y);
break;
case "Z":
value.z = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.z, range.x, range.y);
break;
case "W":
value.w = EditorGUI.Slider(EditorGUILayout.GetControlRect(), s_TempLabel, value.w, range.x, range.y);
break;
}
if (EndProperty())
{
property.vectorValue = value;
}
}
protected void DoVector2(string name, string label)
{
MaterialProperty property = BeginProperty(name);
s_TempLabel.text = label;
Vector4 value = EditorGUILayout.Vector3Field(s_TempLabel, property.vectorValue);
if (EndProperty())
{
property.vectorValue = value;
}
}
protected void DoVector3(string name, string label)
{
MaterialProperty property = BeginProperty(name);
s_TempLabel.text = label;
Vector4 value = EditorGUILayout.Vector3Field(s_TempLabel, property.vectorValue);
if (EndProperty())
{
property.vectorValue = value;
}
}
protected void DoVector(string name, string label, GUIContent[] subLabels)
{
MaterialProperty property = BeginProperty(name);
Rect rect = EditorGUILayout.GetControlRect();
s_TempLabel.text = label;
rect = EditorGUI.PrefixLabel(rect, s_TempLabel);
Vector4 vector = property.vectorValue;
float[] values = s_TempFloats[subLabels.Length];
for (int i = 0; i < subLabels.Length; i++)
{
values[i] = vector[i];
}
EditorGUI.MultiFloatField(rect, subLabels, values);
if (EndProperty())
{
for (int i = 0; i < subLabels.Length; i++)
{
vector[i] = values[i];
}
property.vectorValue = vector;
}
}
void DoDragAndDropBegin()
{
m_DragAndDropMinY = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true)).y;
}
void DoDragAndDropEnd()
{
Rect rect = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
Event evt = Event.current;
if (evt.type == EventType.DragUpdated)
{
DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
evt.Use();
}
else if (
evt.type == EventType.DragPerform &&
Rect.MinMaxRect(rect.xMin, m_DragAndDropMinY, rect.xMax, rect.yMax).Contains(evt.mousePosition)
)
{
DragAndDrop.AcceptDrag();
evt.Use();
Material droppedMaterial = DragAndDrop.objectReferences[0] as Material;
if (droppedMaterial && droppedMaterial != m_Material)
{
PerformDrop(droppedMaterial);
}
}
}
void PerformDrop(Material droppedMaterial)
{
Texture droppedTex = droppedMaterial.GetTexture(ShaderUtilities.ID_MainTex);
if (!droppedTex)
{
return;
}
Texture currentTex = m_Material.GetTexture(ShaderUtilities.ID_MainTex);
TMP_FontAsset requiredFontAsset = null;
if (droppedTex != currentTex)
{
requiredFontAsset = TMP_EditorUtility.FindMatchingFontAsset(droppedMaterial);
if (!requiredFontAsset)
{
return;
}
}
foreach (GameObject o in Selection.gameObjects)
{
if (requiredFontAsset)
{
TMP_Text textComponent = o.GetComponent<TMP_Text>();
if (textComponent)
{
Undo.RecordObject(textComponent, "Font Asset Change");
textComponent.font = requiredFontAsset;
}
}
TMPro_EventManager.ON_DRAG_AND_DROP_MATERIAL_CHANGED(o, m_Material, droppedMaterial);
EditorUtility.SetDirty(o);
}
}
}
}

View File

@@ -0,0 +1,93 @@
using UnityEngine;
using UnityEditor;
namespace TMPro.EditorUtilities
{
public class TMP_BitmapShaderGUI : TMP_BaseShaderGUI
{
static bool s_Face = true;
protected override void DoGUI()
{
s_Face = BeginPanel("Face", s_Face);
if (s_Face)
{
DoFacePanel();
}
EndPanel();
s_DebugExtended = BeginPanel("Debug Settings", s_DebugExtended);
if (s_DebugExtended)
{
DoDebugPanel();
}
EndPanel();
}
void DoFacePanel()
{
EditorGUI.indentLevel += 1;
if (m_Material.HasProperty(ShaderUtilities.ID_FaceTex))
{
DoColor("_FaceColor", "Color");
DoTexture2D("_FaceTex", "Texture", true);
}
else
{
DoColor("_Color", "Color");
DoSlider("_DiffusePower", "Diffuse Power");
}
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoDebugPanel()
{
EditorGUI.indentLevel += 1;
DoTexture2D("_MainTex", "Font Atlas");
if (m_Material.HasProperty(ShaderUtilities.ID_VertexOffsetX))
{
if (m_Material.HasProperty(ShaderUtilities.ID_Padding))
{
EditorGUILayout.Space();
DoFloat("_Padding", "Padding");
}
EditorGUILayout.Space();
DoFloat("_VertexOffsetX", "Offset X");
DoFloat("_VertexOffsetY", "Offset Y");
}
if (m_Material.HasProperty(ShaderUtilities.ID_MaskSoftnessX))
{
EditorGUILayout.Space();
DoFloat("_MaskSoftnessX", "Softness X");
DoFloat("_MaskSoftnessY", "Softness Y");
DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels);
}
if (m_Material.HasProperty(ShaderUtilities.ID_StencilID))
{
EditorGUILayout.Space();
DoFloat("_Stencil", "Stencil ID");
DoFloat("_StencilComp", "Stencil Comp");
}
if (m_Material.HasProperty(ShaderUtilities.ShaderTag_CullMode))
{
EditorGUILayout.Space();
DoPopup("_CullMode", "Cull Mode", s_CullingTypeLabels);
}
EditorGUILayout.Space();
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
}
}

View File

@@ -0,0 +1,235 @@
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);
}
}
}
}

View File

@@ -0,0 +1,50 @@
using UnityEditor;
using UnityEngine;
using System.IO;
using System.Collections;
namespace TMPro.EditorUtilities
{
public static class TMP_ColorGradientAssetMenu
{
[MenuItem("Assets/Create/TextMeshPro/Color Gradient", false, 115)]
public static void CreateColorGradient(MenuCommand context)
{
string filePath;
if (Selection.assetGUIDs.Length == 0)
filePath = "Assets/New TMP Color Gradient.asset";
else
filePath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
if (Directory.Exists(filePath))
{
filePath += "/New TMP Color Gradient.asset";
}
else
{
filePath = Path.GetDirectoryName(filePath) + "/New TMP Color Gradient.asset";
}
filePath = AssetDatabase.GenerateUniqueAssetPath(filePath);
// Create new Color Gradient Asset.
TMP_ColorGradient colorGradient = ScriptableObject.CreateInstance<TMP_ColorGradient>();
// Create Asset
AssetDatabase.CreateAsset(colorGradient, filePath);
//EditorUtility.SetDirty(colorGradient);
AssetDatabase.SaveAssets();
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(colorGradient));
EditorUtility.FocusProjectWindow();
EditorGUIUtility.PingObject(colorGradient);
}
}
}

View File

@@ -0,0 +1,146 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
[CustomEditor(typeof(TMP_ColorGradient))]
public class TMP_ColorGradientEditor : Editor
{
SerializedProperty m_ColorMode;
SerializedProperty m_TopLeftColor;
SerializedProperty m_TopRightColor;
SerializedProperty m_BottomLeftColor;
SerializedProperty m_BottomRightColor;
void OnEnable()
{
m_ColorMode = serializedObject.FindProperty("colorMode");
m_TopLeftColor = serializedObject.FindProperty("topLeft");
m_TopRightColor = serializedObject.FindProperty("topRight");
m_BottomLeftColor = serializedObject.FindProperty("bottomLeft");
m_BottomRightColor = serializedObject.FindProperty("bottomRight");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_ColorMode, new GUIContent("Color Mode"));
if (EditorGUI.EndChangeCheck())
{
switch ((ColorMode)m_ColorMode.enumValueIndex)
{
case ColorMode.Single:
m_TopRightColor.colorValue = m_TopLeftColor.colorValue;
m_BottomLeftColor.colorValue = m_TopLeftColor.colorValue;
m_BottomRightColor.colorValue = m_TopLeftColor.colorValue;
break;
case ColorMode.HorizontalGradient:
m_BottomLeftColor.colorValue = m_TopLeftColor.colorValue;
m_BottomRightColor.colorValue = m_TopRightColor.colorValue;
break;
case ColorMode.VerticalGradient:
m_TopRightColor.colorValue = m_TopLeftColor.colorValue;
m_BottomRightColor.colorValue = m_BottomLeftColor.colorValue;
break;
}
}
Rect rect;
switch ((ColorMode)m_ColorMode.enumValueIndex)
{
case ColorMode.Single:
EditorGUI.BeginChangeCheck();
rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2));
EditorGUI.PrefixLabel(rect, new GUIContent("Colors"));
rect.x += EditorGUIUtility.labelWidth;
rect.width = (rect.width - EditorGUIUtility.labelWidth) / (EditorGUIUtility.wideMode ? 1f : 2f);
TMP_EditorUtility.DrawColorProperty(rect, m_TopLeftColor);
if (EditorGUI.EndChangeCheck())
{
m_TopRightColor.colorValue = m_TopLeftColor.colorValue;
m_BottomLeftColor.colorValue = m_TopLeftColor.colorValue;
m_BottomRightColor.colorValue = m_TopLeftColor.colorValue;
}
break;
case ColorMode.HorizontalGradient:
rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2));
EditorGUI.PrefixLabel(rect, new GUIContent("Colors"));
rect.x += EditorGUIUtility.labelWidth;
rect.width = (rect.width - EditorGUIUtility.labelWidth) / 2f;
EditorGUI.BeginChangeCheck();
TMP_EditorUtility.DrawColorProperty(rect, m_TopLeftColor);
if (EditorGUI.EndChangeCheck())
{
m_BottomLeftColor.colorValue = m_TopLeftColor.colorValue;
}
rect.x += rect.width;
EditorGUI.BeginChangeCheck();
TMP_EditorUtility.DrawColorProperty(rect, m_TopRightColor);
if (EditorGUI.EndChangeCheck())
{
m_BottomRightColor.colorValue = m_TopRightColor.colorValue;
}
break;
case ColorMode.VerticalGradient:
rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2));
EditorGUI.PrefixLabel(rect, new GUIContent("Colors"));
rect.x += EditorGUIUtility.labelWidth;
rect.width = (rect.width - EditorGUIUtility.labelWidth) / (EditorGUIUtility.wideMode ? 1f : 2f);
rect.height = EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2);
EditorGUI.BeginChangeCheck();
TMP_EditorUtility.DrawColorProperty(rect, m_TopLeftColor);
if (EditorGUI.EndChangeCheck())
{
m_TopRightColor.colorValue = m_TopLeftColor.colorValue;
}
rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2));
rect.x += EditorGUIUtility.labelWidth;
rect.width = (rect.width - EditorGUIUtility.labelWidth) / (EditorGUIUtility.wideMode ? 1f : 2f);
rect.height = EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2);
EditorGUI.BeginChangeCheck();
TMP_EditorUtility.DrawColorProperty(rect, m_BottomLeftColor);
if (EditorGUI.EndChangeCheck())
{
m_BottomRightColor.colorValue = m_BottomLeftColor.colorValue;
}
break;
case ColorMode.FourCornersGradient:
rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2));
EditorGUI.PrefixLabel(rect, new GUIContent("Colors"));
rect.x += EditorGUIUtility.labelWidth;
rect.width = (rect.width - EditorGUIUtility.labelWidth) / 2f;
rect.height = EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2);
TMP_EditorUtility.DrawColorProperty(rect, m_TopLeftColor);
rect.x += rect.width;
TMP_EditorUtility.DrawColorProperty(rect, m_TopRightColor);
rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2));
rect.x += EditorGUIUtility.labelWidth;
rect.width = (rect.width - EditorGUIUtility.labelWidth) / 2f;
rect.height = EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2);
TMP_EditorUtility.DrawColorProperty(rect, m_BottomLeftColor);
rect.x += rect.width;
TMP_EditorUtility.DrawColorProperty(rect, m_BottomRightColor);
break;
}
if (serializedObject.ApplyModifiedProperties())
TMPro_EventManager.ON_COLOR_GRADIENT_PROPERTY_CHANGED(target as TMP_ColorGradient);
}
}
}

View File

@@ -0,0 +1,57 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.UI;
using UnityEngine.UI;
namespace TMPro.EditorUtilities
{
[CustomEditor(typeof(TMP_Dropdown), true)]
[CanEditMultipleObjects]
public class DropdownEditor : SelectableEditor
{
SerializedProperty m_Template;
SerializedProperty m_CaptionText;
SerializedProperty m_CaptionImage;
SerializedProperty m_Placeholder;
SerializedProperty m_ItemText;
SerializedProperty m_ItemImage;
SerializedProperty m_OnSelectionChanged;
SerializedProperty m_Value;
SerializedProperty m_AlphaFadeSpeed;
SerializedProperty m_Options;
protected override void OnEnable()
{
base.OnEnable();
m_Template = serializedObject.FindProperty("m_Template");
m_CaptionText = serializedObject.FindProperty("m_CaptionText");
m_CaptionImage = serializedObject.FindProperty("m_CaptionImage");
m_Placeholder = serializedObject.FindProperty("m_Placeholder");
m_ItemText = serializedObject.FindProperty("m_ItemText");
m_ItemImage = serializedObject.FindProperty("m_ItemImage");
m_OnSelectionChanged = serializedObject.FindProperty("m_OnValueChanged");
m_Value = serializedObject.FindProperty("m_Value");
m_AlphaFadeSpeed = serializedObject.FindProperty("m_AlphaFadeSpeed");
m_Options = serializedObject.FindProperty("m_Options");
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
EditorGUILayout.Space();
serializedObject.Update();
EditorGUILayout.PropertyField(m_Template);
EditorGUILayout.PropertyField(m_CaptionText);
EditorGUILayout.PropertyField(m_CaptionImage);
EditorGUILayout.PropertyField(m_Placeholder);
EditorGUILayout.PropertyField(m_ItemText);
EditorGUILayout.PropertyField(m_ItemImage);
EditorGUILayout.PropertyField(m_Value);
EditorGUILayout.PropertyField(m_AlphaFadeSpeed);
EditorGUILayout.PropertyField(m_Options);
EditorGUILayout.PropertyField(m_OnSelectionChanged);
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@@ -0,0 +1,96 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace TMPro.EditorUtilities
{
/// <summary>
/// Simple implementation of coroutine working in the Unity Editor.
/// </summary>
public class TMP_EditorCoroutine
{
//private static Dictionary<int, EditorCoroutine> s_ActiveCoroutines;
readonly IEnumerator coroutine;
/// <summary>
/// Constructor
/// </summary>
/// <param name="routine"></param>
TMP_EditorCoroutine(IEnumerator routine)
{
this.coroutine = routine;
}
/// <summary>
/// Starts a new EditorCoroutine.
/// </summary>
/// <param name="newCoroutine">Coroutine</param>
/// <returns>new EditorCoroutine</returns>
public static TMP_EditorCoroutine StartCoroutine(IEnumerator routine)
{
TMP_EditorCoroutine coroutine = new TMP_EditorCoroutine(routine);
coroutine.Start();
// Add coroutine to tracking list
//if (s_ActiveCoroutines == null)
// s_ActiveCoroutines = new Dictionary<int, EditorCoroutine>();
// Add new instance of editor coroutine to dictionary.
//s_ActiveCoroutines.Add(coroutine.GetHashCode(), coroutine);
return coroutine;
}
/// <summary>
/// Clear delegate list
/// </summary>
//public static void StopAllEditorCoroutines()
//{
// EditorApplication.update = null;
//}
/// <summary>
/// Register callback for editor updates
/// </summary>
void Start()
{
EditorApplication.update += EditorUpdate;
}
/// <summary>
/// Unregister callback for editor updates.
/// </summary>
public void Stop()
{
if (EditorApplication.update != null)
EditorApplication.update -= EditorUpdate;
//s_ActiveCoroutines.Remove(this.GetHashCode());
}
/// <summary>
/// Delegate function called on editor updates.
/// </summary>
void EditorUpdate()
{
// Stop editor coroutine if it does not continue.
if (coroutine.MoveNext() == false)
Stop();
// Process the different types of EditorCoroutines.
if (coroutine.Current != null)
{
}
}
}
}

View File

@@ -0,0 +1,202 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.Presets;
namespace TMPro.EditorUtilities
{
[CustomEditor(typeof(TextMeshPro), true), CanEditMultipleObjects]
public class TMP_EditorPanel : TMP_BaseEditorPanel
{
static readonly GUIContent k_SortingLayerLabel = new GUIContent("Sorting Layer", "Name of the Renderer's sorting layer.");
static readonly GUIContent k_OrderInLayerLabel = new GUIContent("Order in Layer", "Renderer's order within a sorting layer.");
static readonly GUIContent k_OrthographicLabel = new GUIContent("Orthographic Mode", "Should be enabled when using an orthographic camera. Instructs the shader to not perform any perspective correction.");
static readonly GUIContent k_VolumetricLabel = new GUIContent("Volumetric Setup", "Use cubes rather than quads to render the text. Allows for volumetric rendering when combined with a compatible shader.");
private static string[] k_SortingLayerNames;
bool IsPreset;
SerializedProperty m_IsVolumetricTextProp;
SerializedProperty m_IsOrthographicProp;
Object[] m_Renderers;
SerializedObject m_RendererSerializedObject;
SerializedProperty m_RendererSortingLayerProp;
SerializedProperty m_RendererSortingLayerIDProp;
SerializedProperty m_RendererSortingOrderProp;
SerializedProperty m_TextSortingLayerProp;
SerializedProperty m_TextSortingLayerIDProp;
SerializedProperty m_TextSortingOrderProp;
protected override void OnEnable()
{
base.OnEnable();
// Determine if the inspected object is a Preset
IsPreset = (int)(target as Component).gameObject.hideFlags == 93;
m_IsOrthographicProp = serializedObject.FindProperty("m_isOrthographic");
m_IsVolumetricTextProp = serializedObject.FindProperty("m_isVolumetricText");
m_Renderers = new Object[targets.Length];
for (int i = 0; i < m_Renderers.Length; i++)
m_Renderers[i] = (targets[i] as TextMeshPro)?.GetComponent<Renderer>();
m_RendererSerializedObject = new SerializedObject(m_Renderers);
m_RendererSortingLayerProp = m_RendererSerializedObject.FindProperty("m_SortingLayer");
m_RendererSortingLayerIDProp = m_RendererSerializedObject.FindProperty("m_SortingLayerID");
m_RendererSortingOrderProp = m_RendererSerializedObject.FindProperty("m_SortingOrder");
m_TextSortingLayerProp = serializedObject.FindProperty("_SortingLayer");
m_TextSortingLayerIDProp = serializedObject.FindProperty("_SortingLayerID");
m_TextSortingOrderProp = serializedObject.FindProperty("_SortingOrder");
// Populate Sorting Layer Names
k_SortingLayerNames = SortingLayerHelper.sortingLayerNames;
}
protected override void DrawExtraSettings()
{
Rect rect = EditorGUILayout.GetControlRect(false, 24);
if (GUI.Button(rect, new GUIContent("<b>Extra Settings</b>"), TMP_UIStyleManager.sectionHeader))
Foldout.extraSettings = !Foldout.extraSettings;
GUI.Label(rect, (Foldout.extraSettings ? "" : k_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);
if (Foldout.extraSettings)
{
//EditorGUI.indentLevel += 1;
DrawMargins();
DrawSortingLayer();
DrawGeometrySorting();
DrawIsTextObjectScaleStatic();
DrawOrthographicMode();
DrawRichText();
DrawParsing();
DrawSpriteAsset();
DrawStyleSheet();
//DrawVolumetricSetup();
DrawKerning();
DrawPadding();
//EditorGUI.indentLevel -= 1;
}
}
private void DrawSortingLayer()
{
m_RendererSerializedObject.Update();
Rect rect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight);
// Special handling for Presets where the sorting layer, id and order is serialized with the text object instead of on the MeshRenderer.
SerializedProperty sortingLayerProp = IsPreset ? m_TextSortingLayerProp : m_RendererSortingLayerProp;
SerializedProperty sortingLayerIDProp = IsPreset ? m_TextSortingLayerIDProp : m_RendererSortingLayerIDProp;
EditorGUI.BeginProperty(rect, k_SortingLayerLabel, sortingLayerIDProp);
EditorGUI.BeginChangeCheck();
int newLayerIndex = EditorGUI.Popup(rect, k_SortingLayerLabel, sortingLayerProp.intValue, k_SortingLayerNames);
if (EditorGUI.EndChangeCheck())
{
sortingLayerProp.intValue = newLayerIndex;
sortingLayerIDProp.intValue = SortingLayer.NameToID(k_SortingLayerNames[newLayerIndex]);
m_HavePropertiesChanged = true;
// Sync Sorting Layer ID change on potential sub text object.
TextMeshPro textComponent = m_TextComponent as TextMeshPro;
textComponent.UpdateSubMeshSortingLayerID(sortingLayerIDProp.intValue);
}
EditorGUI.EndProperty();
// Sorting Order
SerializedProperty sortingOrderLayerProp = IsPreset ? m_TextSortingOrderProp : m_RendererSortingOrderProp;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(sortingOrderLayerProp, k_OrderInLayerLabel);
if (EditorGUI.EndChangeCheck())
{
m_HavePropertiesChanged = true;
TextMeshPro textComponent = m_TextComponent as TextMeshPro;
textComponent.UpdateSubMeshSortingOrder(sortingOrderLayerProp.intValue);
}
m_RendererSerializedObject.ApplyModifiedProperties();
EditorGUILayout.Space();
}
private void DrawOrthographicMode()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_IsOrthographicProp, k_OrthographicLabel);
if (EditorGUI.EndChangeCheck())
m_HavePropertiesChanged = true;
}
protected void DrawVolumetricSetup()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_IsVolumetricTextProp, k_VolumetricLabel);
if (EditorGUI.EndChangeCheck())
{
m_HavePropertiesChanged = true;
m_TextComponent.textInfo.ResetVertexLayout(m_IsVolumetricTextProp.boolValue);
}
EditorGUILayout.Space();
}
// Method to handle multi object selection
protected override bool IsMixSelectionTypes()
{
GameObject[] objects = Selection.gameObjects;
if (objects.Length > 1)
{
for (int i = 0; i < objects.Length; i++)
{
if (objects[i].GetComponent<TextMeshPro>() == null)
return true;
}
}
return false;
}
protected override void OnUndoRedo()
{
int undoEventId = Undo.GetCurrentGroup();
int lastUndoEventId = s_EventId;
if (undoEventId != lastUndoEventId)
{
for (int i = 0; i < targets.Length; i++)
{
//Debug.Log("Undo & Redo Performed detected in Editor Panel. Event ID:" + Undo.GetCurrentGroup());
TMPro_EventManager.ON_TEXTMESHPRO_PROPERTY_CHANGED(true, targets[i] as TextMeshPro);
s_EventId = undoEventId;
}
}
}
}
}

View File

@@ -0,0 +1,127 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
namespace TMPro.EditorUtilities
{
[CustomEditor(typeof(TextMeshProUGUI), true), CanEditMultipleObjects]
public class TMP_EditorPanelUI : TMP_BaseEditorPanel
{
static readonly GUIContent k_RaycastTargetLabel = new GUIContent("Raycast Target", "Whether the text blocks raycasts from the Graphic Raycaster.");
static readonly GUIContent k_MaskableLabel = new GUIContent("Maskable", "Determines if the text object will be affected by UI Mask.");
SerializedProperty m_RaycastTargetProp;
private SerializedProperty m_MaskableProp;
protected override void OnEnable()
{
base.OnEnable();
m_RaycastTargetProp = serializedObject.FindProperty("m_RaycastTarget");
m_MaskableProp = serializedObject.FindProperty("m_Maskable");
}
protected override void DrawExtraSettings()
{
Rect rect = EditorGUILayout.GetControlRect(false, 24);
if (GUI.Button(rect, new GUIContent("<b>Extra Settings</b>"), TMP_UIStyleManager.sectionHeader))
Foldout.extraSettings = !Foldout.extraSettings;
GUI.Label(rect, (Foldout.extraSettings ? k_UiStateLabel[0] : k_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);
if (Foldout.extraSettings)
{
//EditorGUI.indentLevel += 1;
DrawMargins();
DrawGeometrySorting();
DrawIsTextObjectScaleStatic();
DrawRichText();
DrawRaycastTarget();
DrawMaskable();
DrawParsing();
DrawSpriteAsset();
DrawStyleSheet();
DrawKerning();
DrawPadding();
//EditorGUI.indentLevel -= 1;
}
}
protected void DrawRaycastTarget()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_RaycastTargetProp, k_RaycastTargetLabel);
if (EditorGUI.EndChangeCheck())
{
// Change needs to propagate to the child sub objects.
Graphic[] graphicComponents = m_TextComponent.GetComponentsInChildren<Graphic>();
for (int i = 1; i < graphicComponents.Length; i++)
graphicComponents[i].raycastTarget = m_RaycastTargetProp.boolValue;
m_HavePropertiesChanged = true;
}
}
protected void DrawMaskable()
{
if (m_MaskableProp == null)
return;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_MaskableProp, k_MaskableLabel);
if (EditorGUI.EndChangeCheck())
{
m_TextComponent.maskable = m_MaskableProp.boolValue;
// Change needs to propagate to the child sub objects.
MaskableGraphic[] maskableGraphics = m_TextComponent.GetComponentsInChildren<MaskableGraphic>();
for (int i = 1; i < maskableGraphics.Length; i++)
maskableGraphics[i].maskable = m_MaskableProp.boolValue;
m_HavePropertiesChanged = true;
}
}
// Method to handle multi object selection
protected override bool IsMixSelectionTypes()
{
GameObject[] objects = Selection.gameObjects;
if (objects.Length > 1)
{
for (int i = 0; i < objects.Length; i++)
{
if (objects[i].GetComponent<TextMeshProUGUI>() == null)
return true;
}
}
return false;
}
protected override void OnUndoRedo()
{
int undoEventId = Undo.GetCurrentGroup();
int lastUndoEventId = s_EventId;
if (undoEventId != lastUndoEventId)
{
for (int i = 0; i < targets.Length; i++)
{
//Debug.Log("Undo & Redo Performed detected in Editor Panel. Event ID:" + Undo.GetCurrentGroup());
TMPro_EventManager.ON_TEXTMESHPRO_UGUI_PROPERTY_CHANGED(true, targets[i] as TextMeshProUGUI);
s_EventId = undoEventId;
}
}
}
}
}

View File

@@ -0,0 +1,451 @@
using UnityEngine;
using UnityEditor;
using System.Text;
using System.IO;
using System.Collections;
using System.Collections.Generic;
namespace TMPro.EditorUtilities
{
public static class TMP_EditorUtility
{
/// <summary>
/// Returns the relative path of the package.
/// </summary>
public static string packageRelativePath
{
get
{
if (string.IsNullOrEmpty(m_PackagePath))
m_PackagePath = GetPackageRelativePath();
return m_PackagePath;
}
}
[SerializeField]
private static string m_PackagePath;
/// <summary>
/// Returns the fully qualified path of the package.
/// </summary>
public static string packageFullPath
{
get
{
if (string.IsNullOrEmpty(m_PackageFullPath))
m_PackageFullPath = GetPackageFullPath();
return m_PackageFullPath;
}
}
[SerializeField]
private static string m_PackageFullPath;
// Static Fields Related to locating the TextMesh Pro Asset
private static string folderPath = "Not Found";
private static EditorWindow Gameview;
private static bool isInitialized = false;
private static void GetGameview()
{
System.Reflection.Assembly assembly = typeof(UnityEditor.EditorWindow).Assembly;
System.Type type = assembly.GetType("UnityEditor.GameView");
Gameview = EditorWindow.GetWindow(type);
}
public static void RepaintAll()
{
if (isInitialized == false)
{
GetGameview();
isInitialized = true;
}
SceneView.RepaintAll();
Gameview.Repaint();
}
/// <summary>
/// Create and return a new asset in a smart location based on the current selection and then select it.
/// </summary>
/// <param name="name">
/// Name of the new asset. Do not include the .asset extension.
/// </param>
/// <returns>
/// The new asset.
/// </returns>
public static T CreateAsset<T>(string name) where T : ScriptableObject
{
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
if (path.Length == 0)
{
// no asset selected, place in asset root
path = "Assets/" + name + ".asset";
}
else if (Directory.Exists(path))
{
// place in currently selected directory
path += "/" + name + ".asset";
}
else {
// place in current selection's containing directory
path = Path.GetDirectoryName(path) + "/" + name + ".asset";
}
T asset = ScriptableObject.CreateInstance<T>();
AssetDatabase.CreateAsset(asset, AssetDatabase.GenerateUniqueAssetPath(path));
EditorUtility.FocusProjectWindow();
Selection.activeObject = asset;
return asset;
}
// Function used to find all materials which reference a font atlas so we can update all their references.
public static Material[] FindMaterialReferences(TMP_FontAsset fontAsset)
{
List<Material> refs = new List<Material>();
Material mat = fontAsset.material;
refs.Add(mat);
// Get materials matching the search pattern.
string searchPattern = "t:Material" + " " + fontAsset.name.Split(new char[] { ' ' })[0];
string[] materialAssetGUIDs = AssetDatabase.FindAssets(searchPattern);
for (int i = 0; i < materialAssetGUIDs.Length; i++)
{
string materialPath = AssetDatabase.GUIDToAssetPath(materialAssetGUIDs[i]);
Material targetMaterial = AssetDatabase.LoadAssetAtPath<Material>(materialPath);
if (targetMaterial.HasProperty(ShaderUtilities.ID_MainTex) && targetMaterial.GetTexture(ShaderUtilities.ID_MainTex) != null && mat.GetTexture(ShaderUtilities.ID_MainTex) != null && targetMaterial.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID() == mat.GetTexture(ShaderUtilities.ID_MainTex).GetInstanceID())
{
if (!refs.Contains(targetMaterial))
refs.Add(targetMaterial);
}
else
{
// TODO: Find a more efficient method to unload resources.
//Resources.UnloadAsset(targetMaterial.GetTexture(ShaderUtilities.ID_MainTex));
}
}
return refs.ToArray();
}
// Function used to find the Font Asset which matches the given Material Preset and Font Atlas Texture.
public static TMP_FontAsset FindMatchingFontAsset(Material mat)
{
if (mat.GetTexture(ShaderUtilities.ID_MainTex) == null) return null;
// Find the dependent assets of this material.
string[] dependentAssets = AssetDatabase.GetDependencies(AssetDatabase.GetAssetPath(mat), false);
for (int i = 0; i < dependentAssets.Length; i++)
{
TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(dependentAssets[i]);
if (fontAsset != null)
return fontAsset;
}
return null;
}
private static string GetPackageRelativePath()
{
// Check for potential UPM package
string packagePath = Path.GetFullPath("Packages/com.unity.textmeshpro");
if (Directory.Exists(packagePath))
{
return "Packages/com.unity.textmeshpro";
}
packagePath = Path.GetFullPath("Assets/..");
if (Directory.Exists(packagePath))
{
// Search default location for development package
if (Directory.Exists(packagePath + "/Assets/Packages/com.unity.TextMeshPro/Editor Resources"))
{
return "Assets/Packages/com.unity.TextMeshPro";
}
// Search for default location of normal TextMesh Pro AssetStore package
if (Directory.Exists(packagePath + "/Assets/TextMesh Pro/Editor Resources"))
{
return "Assets/TextMesh Pro";
}
// Search for potential alternative locations in the user project
string[] matchingPaths = Directory.GetDirectories(packagePath, "TextMesh Pro", SearchOption.AllDirectories);
packagePath = ValidateLocation(matchingPaths, packagePath);
if (packagePath != null) return packagePath;
}
return null;
}
private static string GetPackageFullPath()
{
// Check for potential UPM package
string packagePath = Path.GetFullPath("Packages/com.unity.textmeshpro");
if (Directory.Exists(packagePath))
{
return packagePath;
}
packagePath = Path.GetFullPath("Assets/..");
if (Directory.Exists(packagePath))
{
// Search default location for development package
if (Directory.Exists(packagePath + "/Assets/Packages/com.unity.TextMeshPro/Editor Resources"))
{
return packagePath + "/Assets/Packages/com.unity.TextMeshPro";
}
// Search for default location of normal TextMesh Pro AssetStore package
if (Directory.Exists(packagePath + "/Assets/TextMesh Pro/Editor Resources"))
{
return packagePath + "/Assets/TextMesh Pro";
}
// Search for potential alternative locations in the user project
string[] matchingPaths = Directory.GetDirectories(packagePath, "TextMesh Pro", SearchOption.AllDirectories);
string path = ValidateLocation(matchingPaths, packagePath);
if (path != null) return packagePath + path;
}
return null;
}
/// <summary>
/// Method to validate the location of the asset folder by making sure the GUISkins folder exists.
/// </summary>
/// <param name="paths"></param>
/// <returns></returns>
private static string ValidateLocation(string[] paths, string projectPath)
{
for (int i = 0; i < paths.Length; i++)
{
// Check if any of the matching directories contain a GUISkins directory.
if (Directory.Exists(paths[i] + "/Editor Resources"))
{
folderPath = paths[i].Replace(projectPath, "");
folderPath = folderPath.TrimStart('\\', '/');
return folderPath;
}
}
return null;
}
/// <summary>
/// Function which returns a string containing a sequence of Decimal character ranges.
/// </summary>
/// <param name="characterSet"></param>
/// <returns></returns>
public static string GetDecimalCharacterSequence(int[] characterSet)
{
if (characterSet == null || characterSet.Length == 0)
return string.Empty;
string characterSequence = string.Empty;
int count = characterSet.Length;
int first = characterSet[0];
int last = first;
for (int i = 1; i < count; i++)
{
if (characterSet[i - 1] + 1 == characterSet[i])
{
last = characterSet[i];
}
else
{
if (first == last)
characterSequence += first + ",";
else
characterSequence += first + "-" + last + ",";
first = last = characterSet[i];
}
}
// handle the final group
if (first == last)
characterSequence += first;
else
characterSequence += first + "-" + last;
return characterSequence;
}
/// <summary>
/// Function which returns a string containing a sequence of Unicode (Hex) character ranges.
/// </summary>
/// <param name="characterSet"></param>
/// <returns></returns>
public static string GetUnicodeCharacterSequence(int[] characterSet)
{
if (characterSet == null || characterSet.Length == 0)
return string.Empty;
string characterSequence = string.Empty;
int count = characterSet.Length;
int first = characterSet[0];
int last = first;
for (int i = 1; i < count; i++)
{
if (characterSet[i - 1] + 1 == characterSet[i])
{
last = characterSet[i];
}
else
{
if (first == last)
characterSequence += first.ToString("X2") + ",";
else
characterSequence += first.ToString("X2") + "-" + last.ToString("X2") + ",";
first = last = characterSet[i];
}
}
// handle the final group
if (first == last)
characterSequence += first.ToString("X2");
else
characterSequence += first.ToString("X2") + "-" + last.ToString("X2");
return characterSequence;
}
/// <summary>
///
/// </summary>
/// <param name="rect"></param>
/// <param name="thickness"></param>
/// <param name="color"></param>
public static void DrawBox(Rect rect, float thickness, Color color)
{
EditorGUI.DrawRect(new Rect(rect.x - thickness, rect.y + thickness, rect.width + thickness * 2, thickness), color);
EditorGUI.DrawRect(new Rect(rect.x - thickness, rect.y + thickness, thickness, rect.height - thickness * 2), color);
EditorGUI.DrawRect(new Rect(rect.x - thickness, rect.y + rect.height - thickness * 2, rect.width + thickness * 2, thickness), color);
EditorGUI.DrawRect(new Rect(rect.x + rect.width, rect.y + thickness, thickness, rect.height - thickness * 2), color);
}
/// <summary>
/// Function to return the horizontal alignment grid value.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static int GetHorizontalAlignmentGridValue(int value)
{
if ((value & 0x1) == 0x1)
return 0;
else if ((value & 0x2) == 0x2)
return 1;
else if ((value & 0x4) == 0x4)
return 2;
else if ((value & 0x8) == 0x8)
return 3;
else if ((value & 0x10) == 0x10)
return 4;
else if ((value & 0x20) == 0x20)
return 5;
return 0;
}
/// <summary>
/// Function to return the vertical alignment grid value.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static int GetVerticalAlignmentGridValue(int value)
{
if ((value & 0x100) == 0x100)
return 0;
if ((value & 0x200) == 0x200)
return 1;
if ((value & 0x400) == 0x400)
return 2;
if ((value & 0x800) == 0x800)
return 3;
if ((value & 0x1000) == 0x1000)
return 4;
if ((value & 0x2000) == 0x2000)
return 5;
return 0;
}
public static void DrawColorProperty(Rect rect, SerializedProperty property)
{
int oldIndent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
if (EditorGUIUtility.wideMode)
{
EditorGUI.PropertyField(new Rect(rect.x, rect.y, 50f, rect.height), property, GUIContent.none);
rect.x += 50f;
rect.width = Mathf.Min(100f, rect.width - 55f);
}
else
{
rect.height /= 2f;
rect.width = Mathf.Min(100f, rect.width - 5f);
EditorGUI.PropertyField(rect, property, GUIContent.none);
rect.y += rect.height;
}
EditorGUI.BeginChangeCheck();
string colorString = EditorGUI.TextField(rect, string.Format("#{0}", ColorUtility.ToHtmlStringRGBA(property.colorValue)));
if (EditorGUI.EndChangeCheck())
{
Color color;
if (ColorUtility.TryParseHtmlString(colorString, out color))
{
property.colorValue = color;
}
}
EditorGUI.indentLevel = oldIndent;
}
public static bool EditorToggle(Rect position, bool value, GUIContent content, GUIStyle style)
{
var id = GUIUtility.GetControlID(content, FocusType.Keyboard, position);
var evt = Event.current;
// Toggle selected toggle on space or return key
if (GUIUtility.keyboardControl == id && evt.type == EventType.KeyDown && (evt.keyCode == KeyCode.Space || evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter))
{
value = !value;
evt.Use();
GUI.changed = true;
}
if (evt.type == EventType.MouseDown && position.Contains(Event.current.mousePosition))
{
GUIUtility.keyboardControl = id;
EditorGUIUtility.editingTextField = false;
HandleUtility.Repaint();
}
return GUI.Toggle(position, id, value, content, style);
}
}
}

View File

@@ -0,0 +1,258 @@
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using TMPro;
namespace TMPro
{
public static class TMP_FontAsset_CreationMenu
{
[MenuItem("Assets/Create/TextMeshPro/Font Asset Variant", false, 105)]
public static void CreateFontAssetVariant()
{
Object target = Selection.activeObject;
// Make sure the selection is a font file
if (target == null || target.GetType() != typeof(TMP_FontAsset))
{
Debug.LogWarning("A Font file must first be selected in order to create a Font Asset.");
return;
}
// Make sure TMP Essential Resources have been imported in the user project.
if (TMP_Settings.instance == null)
{
Debug.Log("Unable to create font asset. Please import the TMP Essential Resources.");
return;
}
TMP_FontAsset sourceFontAsset = (TMP_FontAsset)target;
string sourceFontFilePath = AssetDatabase.GetAssetPath(target);
string folderPath = Path.GetDirectoryName(sourceFontFilePath);
string assetName = Path.GetFileNameWithoutExtension(sourceFontFilePath);
string newAssetFilePathWithName = AssetDatabase.GenerateUniqueAssetPath(folderPath + "/" + assetName + " - Variant.asset");
// Set Texture and Material reference to the source font asset.
TMP_FontAsset fontAsset = ScriptableObject.Instantiate<TMP_FontAsset>(sourceFontAsset);
AssetDatabase.CreateAsset(fontAsset, newAssetFilePathWithName);
fontAsset.atlasPopulationMode = AtlasPopulationMode.Static;
// Initialize array for the font atlas textures.
fontAsset.atlasTextures = sourceFontAsset.atlasTextures;
fontAsset.material = sourceFontAsset.material;
// Not sure if this is still necessary in newer versions of Unity.
EditorUtility.SetDirty(fontAsset);
AssetDatabase.SaveAssets();
}
/*
[MenuItem("Assets/Create/TextMeshPro/Font Asset Fallback", false, 105)]
public static void CreateFallbackFontAsset()
{
Object target = Selection.activeObject;
// Make sure the selection is a font file
if (target == null || target.GetType() != typeof(TMP_FontAsset))
{
Debug.LogWarning("A Font file must first be selected in order to create a Font Asset.");
return;
}
TMP_FontAsset sourceFontAsset = (TMP_FontAsset)target;
string sourceFontFilePath = AssetDatabase.GetAssetPath(target);
string folderPath = Path.GetDirectoryName(sourceFontFilePath);
string assetName = Path.GetFileNameWithoutExtension(sourceFontFilePath);
string newAssetFilePathWithName = AssetDatabase.GenerateUniqueAssetPath(folderPath + "/" + assetName + " - Fallback.asset");
//// Create new TM Font Asset.
TMP_FontAsset fontAsset = ScriptableObject.CreateInstance<TMP_FontAsset>();
AssetDatabase.CreateAsset(fontAsset, newAssetFilePathWithName);
fontAsset.version = "1.1.0";
fontAsset.faceInfo = sourceFontAsset.faceInfo;
fontAsset.m_SourceFontFileGUID = sourceFontAsset.m_SourceFontFileGUID;
fontAsset.m_SourceFontFile_EditorRef = sourceFontAsset.m_SourceFontFile_EditorRef;
fontAsset.atlasPopulationMode = TMP_FontAsset.AtlasPopulationMode.Dynamic;
int atlasWidth = fontAsset.atlasWidth = sourceFontAsset.atlasWidth;
int atlasHeight = fontAsset.atlasHeight = sourceFontAsset.atlasHeight;
int atlasPadding = fontAsset.atlasPadding = sourceFontAsset.atlasPadding;
fontAsset.atlasRenderMode = sourceFontAsset.atlasRenderMode;
// Initialize array for the font atlas textures.
fontAsset.atlasTextures = new Texture2D[1];
// Create and add font atlas texture
Texture2D texture = new Texture2D(atlasWidth, atlasHeight, TextureFormat.Alpha8, false);
Color32[] colors = new Color32[atlasWidth * atlasHeight];
texture.SetPixels32(colors);
texture.name = assetName + " Atlas";
fontAsset.atlasTextures[0] = texture;
AssetDatabase.AddObjectToAsset(texture, fontAsset);
// Add free rectangle of the size of the texture.
int packingModifier = ((GlyphRasterModes)fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP ? 0 : 1;
fontAsset.m_FreeGlyphRects = new List<GlyphRect>() { new GlyphRect(0, 0, atlasWidth - packingModifier, atlasHeight - packingModifier) };
fontAsset.m_UsedGlyphRects = new List<GlyphRect>();
// Create new Material and Add it as Sub-Asset
Material tmp_material = new Material(sourceFontAsset.material);
tmp_material.name = texture.name + " Material";
tmp_material.SetTexture(ShaderUtilities.ID_MainTex, texture);
tmp_material.SetFloat(ShaderUtilities.ID_TextureWidth, atlasWidth);
tmp_material.SetFloat(ShaderUtilities.ID_TextureHeight, atlasHeight);
tmp_material.SetFloat(ShaderUtilities.ID_GradientScale, atlasPadding + packingModifier);
tmp_material.SetFloat(ShaderUtilities.ID_WeightNormal, fontAsset.normalStyle);
tmp_material.SetFloat(ShaderUtilities.ID_WeightBold, fontAsset.boldStyle);
fontAsset.material = tmp_material;
AssetDatabase.AddObjectToAsset(tmp_material, fontAsset);
// Add Font Asset Creation Settings
// TODO
// Not sure if this is still necessary in newer versions of Unity.
EditorUtility.SetDirty(fontAsset);
AssetDatabase.SaveAssets();
}
*/
//[MenuItem("Assets/Create/TextMeshPro/Font Asset #%F12", true)]
//public static bool CreateFontAssetMenuValidation()
//{
// return false;
//}
[MenuItem("Assets/Create/TextMeshPro/Font Asset #%F12", false, 100)]
public static void CreateFontAsset()
{
Object[] targets = Selection.objects;
if (targets == null)
{
Debug.LogWarning("A Font file must first be selected in order to create a Font Asset.");
return;
}
for (int i = 0; i < targets.Length; i++)
{
Object target = targets[i];
// Make sure the selection is a font file
if (target == null || target.GetType() != typeof(Font))
{
Debug.LogWarning("Selected Object [" + target.name + "] is not a Font file. A Font file must be selected in order to create a Font Asset.", target);
continue;
}
CreateFontAssetFromSelectedObject(target);
}
}
static void CreateFontAssetFromSelectedObject(Object target)
{
Font sourceFont = (Font)target;
string sourceFontFilePath = AssetDatabase.GetAssetPath(target);
string folderPath = Path.GetDirectoryName(sourceFontFilePath);
string assetName = Path.GetFileNameWithoutExtension(sourceFontFilePath);
string newAssetFilePathWithName = AssetDatabase.GenerateUniqueAssetPath(folderPath + "/" + assetName + " SDF.asset");
// Initialize FontEngine
FontEngine.InitializeFontEngine();
// Load Font Face
if (FontEngine.LoadFontFace(sourceFont, 90) != FontEngineError.Success)
{
Debug.LogWarning("Unable to load font face for [" + sourceFont.name + "]. Make sure \"Include Font Data\" is enabled in the Font Import Settings.", sourceFont);
return;
}
// Create new Font Asset
TMP_FontAsset fontAsset = ScriptableObject.CreateInstance<TMP_FontAsset>();
AssetDatabase.CreateAsset(fontAsset, newAssetFilePathWithName);
fontAsset.version = "1.1.0";
fontAsset.faceInfo = FontEngine.GetFaceInfo();
// Set font reference and GUID
fontAsset.m_SourceFontFileGUID = AssetDatabase.AssetPathToGUID(sourceFontFilePath);
fontAsset.m_SourceFontFile_EditorRef = sourceFont;
fontAsset.atlasPopulationMode = AtlasPopulationMode.Dynamic;
// Default atlas resolution is 1024 x 1024.
int atlasWidth = fontAsset.atlasWidth = 1024;
int atlasHeight = fontAsset.atlasHeight = 1024;
int atlasPadding = fontAsset.atlasPadding = 9;
fontAsset.atlasRenderMode = GlyphRenderMode.SDFAA;
// Initialize array for the font atlas textures.
fontAsset.atlasTextures = new Texture2D[1];
// Create atlas texture of size zero.
Texture2D texture = new Texture2D(0, 0, TextureFormat.Alpha8, false);
texture.name = assetName + " Atlas";
fontAsset.atlasTextures[0] = texture;
AssetDatabase.AddObjectToAsset(texture, fontAsset);
// Add free rectangle of the size of the texture.
int packingModifier = ((GlyphRasterModes)fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_BITMAP) == GlyphRasterModes.RASTER_MODE_BITMAP ? 0 : 1;
fontAsset.freeGlyphRects = new List<GlyphRect>() { new GlyphRect(0, 0, atlasWidth - packingModifier, atlasHeight - packingModifier) };
fontAsset.usedGlyphRects = new List<GlyphRect>();
// Create new Material and Add it as Sub-Asset
Shader default_Shader = Shader.Find("TextMeshPro/Distance Field");
Material tmp_material = new Material(default_Shader);
tmp_material.name = texture.name + " Material";
tmp_material.SetTexture(ShaderUtilities.ID_MainTex, texture);
tmp_material.SetFloat(ShaderUtilities.ID_TextureWidth, atlasWidth);
tmp_material.SetFloat(ShaderUtilities.ID_TextureHeight, atlasHeight);
tmp_material.SetFloat(ShaderUtilities.ID_GradientScale, atlasPadding + packingModifier);
tmp_material.SetFloat(ShaderUtilities.ID_WeightNormal, fontAsset.normalStyle);
tmp_material.SetFloat(ShaderUtilities.ID_WeightBold, fontAsset.boldStyle);
fontAsset.material = tmp_material;
AssetDatabase.AddObjectToAsset(tmp_material, fontAsset);
// Add Font Asset Creation Settings
fontAsset.creationSettings = new FontAssetCreationSettings(fontAsset.m_SourceFontFileGUID, fontAsset.faceInfo.pointSize, 0, atlasPadding, 0, 1024, 1024, 7, string.Empty, (int)GlyphRenderMode.SDFAA);
// Not sure if this is still necessary in newer versions of Unity.
EditorUtility.SetDirty(fontAsset);
AssetDatabase.SaveAssets();
}
}
}

View File

@@ -0,0 +1,391 @@
using UnityEngine;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using UnityEditor;
using System.Collections;
using System.Text.RegularExpressions;
namespace TMPro.EditorUtilities
{
[CustomPropertyDrawer(typeof(TMP_GlyphPairAdjustmentRecord))]
public class TMP_GlyphPairAdjustmentRecordPropertyDrawer : PropertyDrawer
{
private bool isEditingEnabled = false;
private bool isSelectable = false;
private string m_FirstCharacter = string.Empty;
private string m_SecondCharacter = string.Empty;
private string m_PreviousInput;
static GUIContent s_CharacterTextFieldLabel = new GUIContent("Char:", "Enter the character or its UTF16 or UTF32 Unicode character escape sequence. For UTF16 use \"\\uFF00\" and for UTF32 use \"\\UFF00FF00\" representation.");
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty prop_FirstAdjustmentRecord = property.FindPropertyRelative("m_FirstAdjustmentRecord");
SerializedProperty prop_SecondAdjustmentRecord = property.FindPropertyRelative("m_SecondAdjustmentRecord");
SerializedProperty prop_FirstGlyphIndex = prop_FirstAdjustmentRecord.FindPropertyRelative("m_GlyphIndex");
SerializedProperty prop_FirstGlyphValueRecord = prop_FirstAdjustmentRecord.FindPropertyRelative("m_GlyphValueRecord");
SerializedProperty prop_SecondGlyphIndex = prop_SecondAdjustmentRecord.FindPropertyRelative("m_GlyphIndex");
SerializedProperty prop_SecondGlyphValueRecord = prop_SecondAdjustmentRecord.FindPropertyRelative("m_GlyphValueRecord");
SerializedProperty prop_FontFeatureLookupFlags = property.FindPropertyRelative("m_FeatureLookupFlags");
position.yMin += 2;
float width = position.width / 2;
float padding = 5.0f;
Rect rect;
isEditingEnabled = GUI.enabled;
isSelectable = label.text == "Selectable" ? true : false;
if (isSelectable)
GUILayoutUtility.GetRect(position.width, 75);
else
GUILayoutUtility.GetRect(position.width, 55);
GUIStyle style = new GUIStyle(EditorStyles.label);
style.richText = true;
// First Glyph
GUI.enabled = isEditingEnabled;
if (isSelectable)
{
rect = new Rect(position.x + 70, position.y, position.width, 49);
float labelWidth = GUI.skin.label.CalcSize(new GUIContent("ID: " + prop_FirstGlyphIndex.intValue)).x;
EditorGUI.LabelField(new Rect(position.x + (64 - labelWidth) / 2, position.y + 60, 64f, 18f), new GUIContent("ID: <color=#FFFF80>" + prop_FirstGlyphIndex.intValue + "</color>"), style);
GUI.enabled = isEditingEnabled;
EditorGUIUtility.labelWidth = 30f;
rect = new Rect(position.x + 70, position.y + 10, (width - 70) - padding, 18);
EditorGUI.PropertyField(rect, prop_FirstGlyphValueRecord.FindPropertyRelative("m_XPlacement"), new GUIContent("OX:"));
rect.y += 20;
EditorGUI.PropertyField(rect, prop_FirstGlyphValueRecord.FindPropertyRelative("m_YPlacement"), new GUIContent("OY:"));
rect.y += 20;
EditorGUI.PropertyField(rect, prop_FirstGlyphValueRecord.FindPropertyRelative("m_XAdvance"), new GUIContent("AX:"));
//rect.y += 20;
//EditorGUI.PropertyField(rect, prop_FirstGlyphValueRecord.FindPropertyRelative("m_YAdvance"), new GUIContent("AY:"));
DrawGlyph((uint)prop_FirstGlyphIndex.intValue, new Rect(position.x, position.y, position.width, position.height), property);
}
else
{
rect = new Rect(position.x, position.y, width / 2 * 0.8f - padding, 18);
EditorGUIUtility.labelWidth = 40f;
// First Character Lookup
GUI.SetNextControlName("FirstCharacterField");
EditorGUI.BeginChangeCheck();
string firstCharacter = EditorGUI.TextField(rect, s_CharacterTextFieldLabel, m_FirstCharacter);
if (GUI.GetNameOfFocusedControl() == "FirstCharacterField")
{
if (ValidateInput(firstCharacter))
{
//Debug.Log("1st Unicode value: [" + firstCharacter + "]");
uint unicode = GetUnicodeCharacter(firstCharacter);
// Lookup glyph index
TMP_SerializedPropertyHolder propertyHolder = property.serializedObject.targetObject as TMP_SerializedPropertyHolder;
TMP_FontAsset fontAsset = propertyHolder.fontAsset;
if (fontAsset != null)
{
prop_FirstGlyphIndex.intValue = (int)fontAsset.GetGlyphIndex(unicode);
propertyHolder.firstCharacter = unicode;
}
}
}
if (EditorGUI.EndChangeCheck())
m_FirstCharacter = firstCharacter;
// First Glyph Index
rect.x += width / 2 * 0.8f;
EditorGUIUtility.labelWidth = 25f;
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(rect, prop_FirstGlyphIndex, new GUIContent("ID:"));
if (EditorGUI.EndChangeCheck())
{
}
GUI.enabled = isEditingEnabled;
EditorGUIUtility.labelWidth = 25f;
rect = new Rect(position.x, position.y + 20, width * 0.5f - padding, 18);
EditorGUI.PropertyField(rect, prop_FirstGlyphValueRecord.FindPropertyRelative("m_XPlacement"), new GUIContent("OX"));
rect.x += width * 0.5f;
EditorGUI.PropertyField(rect, prop_FirstGlyphValueRecord.FindPropertyRelative("m_YPlacement"), new GUIContent("OY"));
rect.x = position.x;
rect.y += 20;
EditorGUI.PropertyField(rect, prop_FirstGlyphValueRecord.FindPropertyRelative("m_XAdvance"), new GUIContent("AX"));
//rect.x += width * 0.5f;
//EditorGUI.PropertyField(rect, prop_FirstGlyphAdjustment.FindPropertyRelative("m_YAdvance"), new GUIContent("AY"));
}
// Second Glyph
GUI.enabled = isEditingEnabled;
if (isSelectable)
{
float labelWidth = GUI.skin.label.CalcSize(new GUIContent("ID: " + prop_SecondGlyphIndex.intValue)).x;
EditorGUI.LabelField(new Rect(position.width / 2 + 20 + (64 - labelWidth) / 2, position.y + 60, 64f, 18f), new GUIContent("ID: <color=#FFFF80>" + prop_SecondGlyphIndex.intValue + "</color>"), style);
GUI.enabled = isEditingEnabled;
EditorGUIUtility.labelWidth = 30f;
rect = new Rect(position.width / 2 + 20 + 70, position.y + 10, (width - 70) - padding, 18);
EditorGUI.PropertyField(rect, prop_SecondGlyphValueRecord.FindPropertyRelative("m_XPlacement"), new GUIContent("OX:"));
rect.y += 20;
EditorGUI.PropertyField(rect, prop_SecondGlyphValueRecord.FindPropertyRelative("m_YPlacement"), new GUIContent("OY:"));
rect.y += 20;
EditorGUI.PropertyField(rect, prop_SecondGlyphValueRecord.FindPropertyRelative("m_XAdvance"), new GUIContent("AX:"));
//rect.y += 20;
//EditorGUI.PropertyField(rect, prop_SecondGlyphAdjustment.FindPropertyRelative("m_YAdvance"), new GUIContent("AY"));
DrawGlyph((uint)prop_SecondGlyphIndex.intValue, new Rect(position.width / 2 + 20, position.y, position.width, position.height), property);
}
else
{
rect = new Rect(position.width / 2 + 20, position.y, width / 2 * 0.8f - padding, 18);
EditorGUIUtility.labelWidth = 40f;
// Second Character Lookup
GUI.SetNextControlName("SecondCharacterField");
EditorGUI.BeginChangeCheck();
string secondCharacter = EditorGUI.TextField(rect, s_CharacterTextFieldLabel, m_SecondCharacter);
if (GUI.GetNameOfFocusedControl() == "SecondCharacterField")
{
if (ValidateInput(secondCharacter))
{
//Debug.Log("2nd Unicode value: [" + secondCharacter + "]");
uint unicode = GetUnicodeCharacter(secondCharacter);
// Lookup glyph index
TMP_SerializedPropertyHolder propertyHolder = property.serializedObject.targetObject as TMP_SerializedPropertyHolder;
TMP_FontAsset fontAsset = propertyHolder.fontAsset;
if (fontAsset != null)
{
prop_SecondGlyphIndex.intValue = (int)fontAsset.GetGlyphIndex(unicode);
propertyHolder.secondCharacter = unicode;
}
}
}
if (EditorGUI.EndChangeCheck())
m_SecondCharacter = secondCharacter;
// Second Glyph Index
rect.x += width / 2 * 0.8f;
EditorGUIUtility.labelWidth = 25f;
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(rect, prop_SecondGlyphIndex, new GUIContent("ID:"));
if (EditorGUI.EndChangeCheck())
{
}
GUI.enabled = isEditingEnabled;
EditorGUIUtility.labelWidth = 25f;
rect = new Rect(position.width / 2 + 20, position.y + 20, width * 0.5f - padding, 18);
EditorGUI.PropertyField(rect, prop_SecondGlyphValueRecord.FindPropertyRelative("m_XPlacement"), new GUIContent("OX"));
rect.x += width * 0.5f;
EditorGUI.PropertyField(rect, prop_SecondGlyphValueRecord.FindPropertyRelative("m_YPlacement"), new GUIContent("OY"));
rect.x = position.width / 2 + 20;
rect.y += 20;
EditorGUI.PropertyField(rect, prop_SecondGlyphValueRecord.FindPropertyRelative("m_XAdvance"), new GUIContent("AX"));
//rect.x += width * 0.5f;
//EditorGUI.PropertyField(rect, prop_SecondGlyphAdjustment.FindPropertyRelative("m_YAdvance"), new GUIContent("AY"));
}
// Font Feature Lookup Flags
if (isSelectable)
{
EditorGUIUtility.labelWidth = 55f;
rect.x = position.width - 255;
rect.y += 23;
rect.width = 270; // width - 70 - padding;
FontFeatureLookupFlags flags = (FontFeatureLookupFlags)prop_FontFeatureLookupFlags.intValue;
EditorGUI.BeginChangeCheck();
flags = (FontFeatureLookupFlags)EditorGUI.EnumFlagsField(rect, new GUIContent("Options:"), flags);
if (EditorGUI.EndChangeCheck())
{
prop_FontFeatureLookupFlags.intValue = (int)flags;
}
}
}
bool ValidateInput(string source)
{
int length = string.IsNullOrEmpty(source) ? 0 : source.Length;
////Filter out unwanted characters.
Event evt = Event.current;
char c = evt.character;
if (c != '\0')
{
switch (length)
{
case 0:
break;
case 1:
if (source != m_PreviousInput)
return true;
if ((source[0] == '\\' && (c == 'u' || c == 'U')) == false)
evt.character = '\0';
break;
case 2:
case 3:
case 4:
case 5:
if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F'))
evt.character = '\0';
break;
case 6:
case 7:
case 8:
case 9:
if (source[1] == 'u' || (c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' || c > 'F'))
evt.character = '\0';
// Validate input
if (length == 6 && source[1] == 'u' && source != m_PreviousInput)
return true;
break;
case 10:
if (source != m_PreviousInput)
return true;
evt.character = '\0';
break;
}
}
m_PreviousInput = source;
return false;
}
uint GetUnicodeCharacter (string source)
{
uint unicode;
if (source.Length == 1)
unicode = source[0];
else if (source.Length == 6)
unicode = (uint)TMP_TextUtilities.StringHexToInt(source.Replace("\\u", ""));
else
unicode = (uint)TMP_TextUtilities.StringHexToInt(source.Replace("\\U", ""));
return unicode;
}
void DrawGlyph(uint glyphIndex, Rect position, SerializedProperty property)
{
// Get a reference to the font asset
TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset;
if (fontAsset == null)
return;
Glyph glyph;
// Check if glyph is present in the atlas texture.
if (!fontAsset.glyphLookupTable.TryGetValue(glyphIndex, out glyph))
return;
// Get the atlas index of the glyph and lookup its atlas texture
int atlasIndex = glyph.atlasIndex;
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;
}
else
{
mat = TMP_FontAssetEditor.internalSDFMaterial;
if (mat == null)
return;
mat.mainTexture = atlasTexture;
mat.SetFloat(ShaderUtilities.ID_GradientScale, fontAsset.atlasPadding + 1);
}
// Draw glyph from atlas texture.
Rect glyphDrawPosition = new Rect(position.x, position.y + 2, 64, 60);
GlyphRect glyphRect = glyph.glyphRect;
int padding = fontAsset.atlasPadding;
int glyphOriginX = glyphRect.x - padding;
int glyphOriginY = glyphRect.y - padding;
int glyphWidth = glyphRect.width + padding * 2;
int glyphHeight = glyphRect.height + 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);
}
}
}
}

View File

@@ -0,0 +1,122 @@
using UnityEngine;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
[CustomPropertyDrawer(typeof(Glyph))]
public class TMP_GlyphPropertyDrawer : PropertyDrawer
{
private string k_ColorProperty = "_Color";
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty prop_GlyphIndex = property.FindPropertyRelative("m_Index");
SerializedProperty prop_GlyphMetrics = property.FindPropertyRelative("m_Metrics");
SerializedProperty prop_GlyphRect = property.FindPropertyRelative("m_GlyphRect");
SerializedProperty prop_Scale = property.FindPropertyRelative("m_Scale");
SerializedProperty prop_AtlasIndex = property.FindPropertyRelative("m_AtlasIndex");
GUIStyle style = new GUIStyle(EditorStyles.label);
style.richText = true;
Rect rect = new Rect(position.x + 70, position.y, position.width, 49);
float labelWidth = GUI.skin.label.CalcSize(new GUIContent("ID: " + prop_GlyphIndex.intValue)).x;
EditorGUI.LabelField(new Rect(position.x + (64 - labelWidth) / 2, position.y + 85, 64f, 18f), new GUIContent("ID: <color=#FFFF80>" + prop_GlyphIndex.intValue + "</color>"), style);
//EditorGUIUtility.labelWidth = 22f;
//EditorGUI.DelayedIntField(new Rect(position.x + (64 - labelWidth) / 2, position.y + 89, 58f, 18f), prop_GlyphIndex, new GUIContent("ID:"));
// We get Rect since a valid position may not be provided by the caller.
EditorGUI.PropertyField(new Rect(rect.x, rect.y, position.width, 49), prop_GlyphRect);
rect.y += 45;
EditorGUI.PropertyField(rect, prop_GlyphMetrics);
EditorGUIUtility.labelWidth = 40f;
EditorGUI.PropertyField(new Rect(rect.x, rect.y + 65, 75, 18), prop_Scale, new GUIContent("Scale:")); // new GUIContent("Scale: <color=#FFFF80>" + prop_Scale.floatValue + "</color>"), style);
EditorGUIUtility.labelWidth = 74f;
EditorGUI.PropertyField(new Rect(rect.x + 85, rect.y + 65, 95, 18), prop_AtlasIndex, new GUIContent("Atlas Index:")); // new GUIContent("Atlas Index: <color=#FFFF80>" + prop_AtlasIndex.intValue + "</color>"), style);
DrawGlyph(position, property);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 130f;
}
void DrawGlyph(Rect position, SerializedProperty property)
{
// Get a reference to the sprite texture
TMP_FontAsset fontAsset = property.serializedObject.targetObject as TMP_FontAsset;
if (fontAsset == null)
return;
// Get reference to atlas texture.
int atlasIndex = property.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 from atlas texture.
Rect glyphDrawPosition = new Rect(position.x, position.y + 2, 64, 80);
SerializedProperty glyphRectProperty = property.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);
}
}
}
}

View File

@@ -0,0 +1,283 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEditor;
using UnityEditor.UI;
using UnityEditor.AnimatedValues;
namespace TMPro.EditorUtilities
{
[CanEditMultipleObjects]
[CustomEditor(typeof(TMP_InputField), true)]
public class TMP_InputFieldEditor : SelectableEditor
{
private struct m_foldout
{ // Track Inspector foldout panel states, globally.
public static bool textInput = true;
public static bool fontSettings = true;
public static bool extraSettings = true;
//public static bool shadowSetting = false;
//public static bool materialEditor = true;
}
SerializedProperty m_TextViewport;
SerializedProperty m_TextComponent;
SerializedProperty m_Text;
SerializedProperty m_ContentType;
SerializedProperty m_LineType;
SerializedProperty m_LineLimit;
SerializedProperty m_InputType;
SerializedProperty m_CharacterValidation;
SerializedProperty m_InputValidator;
SerializedProperty m_RegexValue;
SerializedProperty m_KeyboardType;
SerializedProperty m_CharacterLimit;
SerializedProperty m_CaretBlinkRate;
SerializedProperty m_CaretWidth;
SerializedProperty m_CaretColor;
SerializedProperty m_CustomCaretColor;
SerializedProperty m_SelectionColor;
SerializedProperty m_HideMobileKeyboard;
SerializedProperty m_HideMobileInput;
SerializedProperty m_Placeholder;
SerializedProperty m_VerticalScrollbar;
SerializedProperty m_ScrollbarScrollSensitivity;
SerializedProperty m_OnValueChanged;
SerializedProperty m_OnEndEdit;
SerializedProperty m_OnSelect;
SerializedProperty m_OnDeselect;
SerializedProperty m_ReadOnly;
SerializedProperty m_RichText;
SerializedProperty m_RichTextEditingAllowed;
SerializedProperty m_ResetOnDeActivation;
SerializedProperty m_RestoreOriginalTextOnEscape;
SerializedProperty m_OnFocusSelectAll;
SerializedProperty m_GlobalPointSize;
SerializedProperty m_GlobalFontAsset;
AnimBool m_CustomColor;
//TMP_InputValidator m_ValidationScript;
protected override void OnEnable()
{
base.OnEnable();
m_TextViewport = serializedObject.FindProperty("m_TextViewport");
m_TextComponent = serializedObject.FindProperty("m_TextComponent");
m_Text = serializedObject.FindProperty("m_Text");
m_ContentType = serializedObject.FindProperty("m_ContentType");
m_LineType = serializedObject.FindProperty("m_LineType");
m_LineLimit = serializedObject.FindProperty("m_LineLimit");
m_InputType = serializedObject.FindProperty("m_InputType");
m_CharacterValidation = serializedObject.FindProperty("m_CharacterValidation");
m_InputValidator = serializedObject.FindProperty("m_InputValidator");
m_RegexValue = serializedObject.FindProperty("m_RegexValue");
m_KeyboardType = serializedObject.FindProperty("m_KeyboardType");
m_CharacterLimit = serializedObject.FindProperty("m_CharacterLimit");
m_CaretBlinkRate = serializedObject.FindProperty("m_CaretBlinkRate");
m_CaretWidth = serializedObject.FindProperty("m_CaretWidth");
m_CaretColor = serializedObject.FindProperty("m_CaretColor");
m_CustomCaretColor = serializedObject.FindProperty("m_CustomCaretColor");
m_SelectionColor = serializedObject.FindProperty("m_SelectionColor");
m_HideMobileKeyboard = serializedObject.FindProperty("m_HideSoftKeyboard");
m_HideMobileInput = serializedObject.FindProperty("m_HideMobileInput");
m_Placeholder = serializedObject.FindProperty("m_Placeholder");
m_VerticalScrollbar = serializedObject.FindProperty("m_VerticalScrollbar");
m_ScrollbarScrollSensitivity = serializedObject.FindProperty("m_ScrollSensitivity");
m_OnValueChanged = serializedObject.FindProperty("m_OnValueChanged");
m_OnEndEdit = serializedObject.FindProperty("m_OnEndEdit");
m_OnSelect = serializedObject.FindProperty("m_OnSelect");
m_OnDeselect = serializedObject.FindProperty("m_OnDeselect");
m_ReadOnly = serializedObject.FindProperty("m_ReadOnly");
m_RichText = serializedObject.FindProperty("m_RichText");
m_RichTextEditingAllowed = serializedObject.FindProperty("m_isRichTextEditingAllowed");
m_ResetOnDeActivation = serializedObject.FindProperty("m_ResetOnDeActivation");
m_RestoreOriginalTextOnEscape = serializedObject.FindProperty("m_RestoreOriginalTextOnEscape");
m_OnFocusSelectAll = serializedObject.FindProperty("m_OnFocusSelectAll");
m_GlobalPointSize = serializedObject.FindProperty("m_GlobalPointSize");
m_GlobalFontAsset = serializedObject.FindProperty("m_GlobalFontAsset");
m_CustomColor = new AnimBool(m_CustomCaretColor.boolValue);
m_CustomColor.valueChanged.AddListener(Repaint);
}
protected override void OnDisable()
{
base.OnDisable();
m_CustomColor.valueChanged.RemoveListener(Repaint);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
base.OnInspectorGUI();
EditorGUILayout.Space();
EditorGUILayout.PropertyField(m_TextViewport);
EditorGUILayout.PropertyField(m_TextComponent);
TextMeshProUGUI text = null;
if (m_TextComponent != null && m_TextComponent.objectReferenceValue != null)
{
text = m_TextComponent.objectReferenceValue as TextMeshProUGUI;
//if (text.supportRichText)
//{
// EditorGUILayout.HelpBox("Using Rich Text with input is unsupported.", MessageType.Warning);
//}
}
EditorGUI.BeginDisabledGroup(m_TextComponent == null || m_TextComponent.objectReferenceValue == null);
// TEXT INPUT BOX
EditorGUILayout.PropertyField(m_Text);
// INPUT FIELD SETTINGS
#region INPUT FIELD SETTINGS
m_foldout.fontSettings = EditorGUILayout.Foldout(m_foldout.fontSettings, "Input Field Settings", true, TMP_UIStyleManager.boldFoldout);
if (m_foldout.fontSettings)
{
EditorGUI.indentLevel++;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_GlobalFontAsset, new GUIContent("Font Asset", "Set the Font Asset for both Placeholder and Input Field text object."));
if (EditorGUI.EndChangeCheck())
{
TMP_InputField inputField = target as TMP_InputField;
inputField.SetGlobalFontAsset(m_GlobalFontAsset.objectReferenceValue as TMP_FontAsset);
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_GlobalPointSize, new GUIContent("Point Size", "Set the point size of both Placeholder and Input Field text object."));
if (EditorGUI.EndChangeCheck())
{
TMP_InputField inputField = target as TMP_InputField;
inputField.SetGlobalPointSize(m_GlobalPointSize.floatValue);
}
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_CharacterLimit);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(m_ContentType);
if (!m_ContentType.hasMultipleDifferentValues)
{
EditorGUI.indentLevel++;
if (m_ContentType.enumValueIndex == (int)TMP_InputField.ContentType.Standard ||
m_ContentType.enumValueIndex == (int)TMP_InputField.ContentType.Autocorrected ||
m_ContentType.enumValueIndex == (int)TMP_InputField.ContentType.Custom)
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_LineType);
if (EditorGUI.EndChangeCheck())
{
if (text != null)
{
if (m_LineType.enumValueIndex == (int)TMP_InputField.LineType.SingleLine)
text.enableWordWrapping = false;
else
{
text.enableWordWrapping = true;
}
}
}
if (m_LineType.enumValueIndex != (int)TMP_InputField.LineType.SingleLine)
{
EditorGUILayout.PropertyField(m_LineLimit);
}
}
if (m_ContentType.enumValueIndex == (int)TMP_InputField.ContentType.Custom)
{
EditorGUILayout.PropertyField(m_InputType);
EditorGUILayout.PropertyField(m_KeyboardType);
EditorGUILayout.PropertyField(m_CharacterValidation);
if (m_CharacterValidation.enumValueIndex == (int)TMP_InputField.CharacterValidation.Regex)
{
EditorGUILayout.PropertyField(m_RegexValue);
}
else if (m_CharacterValidation.enumValueIndex == (int)TMP_InputField.CharacterValidation.CustomValidator)
{
EditorGUILayout.PropertyField(m_InputValidator);
}
}
EditorGUI.indentLevel--;
}
EditorGUILayout.Space();
EditorGUILayout.PropertyField(m_Placeholder);
EditorGUILayout.PropertyField(m_VerticalScrollbar);
if (m_VerticalScrollbar.objectReferenceValue != null)
EditorGUILayout.PropertyField(m_ScrollbarScrollSensitivity);
EditorGUILayout.PropertyField(m_CaretBlinkRate);
EditorGUILayout.PropertyField(m_CaretWidth);
EditorGUILayout.PropertyField(m_CustomCaretColor);
m_CustomColor.target = m_CustomCaretColor.boolValue;
if (EditorGUILayout.BeginFadeGroup(m_CustomColor.faded))
{
EditorGUILayout.PropertyField(m_CaretColor);
}
EditorGUILayout.EndFadeGroup();
EditorGUILayout.PropertyField(m_SelectionColor);
EditorGUI.indentLevel--;
}
#endregion
// CONTROL SETTINGS
#region CONTROL SETTINGS
m_foldout.extraSettings = EditorGUILayout.Foldout(m_foldout.extraSettings, "Control Settings", true, TMP_UIStyleManager.boldFoldout);
if (m_foldout.extraSettings)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_OnFocusSelectAll, new GUIContent("OnFocus - Select All", "Should all the text be selected when the Input Field is selected."));
EditorGUILayout.PropertyField(m_ResetOnDeActivation, new GUIContent("Reset On DeActivation", "Should the Text and Caret position be reset when Input Field is DeActivated."));
EditorGUILayout.PropertyField(m_RestoreOriginalTextOnEscape, new GUIContent("Restore On ESC Key", "Should the original text be restored when pressing ESC."));
EditorGUILayout.PropertyField(m_HideMobileKeyboard, new GUIContent("Hide Soft Keyboard", "Controls the visibility of the mobile virtual keyboard."));
EditorGUILayout.PropertyField(m_HideMobileInput, new GUIContent("Hide Mobile Input", "Controls the visibility of the editable text field above the mobile virtual keyboard."));
EditorGUILayout.PropertyField(m_ReadOnly);
EditorGUILayout.PropertyField(m_RichText);
EditorGUILayout.PropertyField(m_RichTextEditingAllowed, new GUIContent("Allow Rich Text Editing"));
EditorGUI.indentLevel--;
}
#endregion
EditorGUILayout.Space();
EditorGUILayout.PropertyField(m_OnValueChanged);
EditorGUILayout.PropertyField(m_OnEndEdit);
EditorGUILayout.PropertyField(m_OnSelect);
EditorGUILayout.PropertyField(m_OnDeselect);
EditorGUI.EndDisabledGroup();
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@@ -0,0 +1,76 @@
// When enabled, allows setting the material by dropping a material onto the MeshRenderer inspector component.
// The drawback is that the MeshRenderer inspector will not have properties for light probes, so if you need light probe support, do not enable this.
//#define ALLOW_MESHRENDERER_MATERIAL_DRAG_N_DROP
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
// Disabled for compatibility reason as lightprobe setup isn't supported due to inability to inherit from MeshRendererEditor class
#if ALLOW_MESHRENDERER_MATERIAL_DRAG_N_DROP
[CanEditMultipleObjects]
[CustomEditor(typeof(MeshRenderer))]
public class TMP_MeshRendererEditor : Editor
{
private SerializedProperty m_Materials;
void OnEnable()
{
m_Materials = serializedObject.FindProperty("m_Materials");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
// Get a reference to the current material.
SerializedProperty material_prop = m_Materials.GetArrayElementAtIndex(0);
Material currentMaterial = material_prop.objectReferenceValue as Material;
EditorGUI.BeginChangeCheck();
base.OnInspectorGUI();
if (EditorGUI.EndChangeCheck())
{
material_prop = m_Materials.GetArrayElementAtIndex(0);
TMP_FontAsset newFontAsset = null;
Material newMaterial = null;
if (material_prop != null)
newMaterial = material_prop.objectReferenceValue as Material;
// Check if the new material is referencing a different font atlas texture.
if (newMaterial != null && currentMaterial.GetInstanceID() != newMaterial.GetInstanceID())
{
// Search for the Font Asset matching the new font atlas texture.
newFontAsset = TMP_EditorUtility.FindMatchingFontAsset(newMaterial);
}
GameObject[] objects = Selection.gameObjects;
for (int i = 0; i < objects.Length; i++)
{
// Assign new font asset
if (newFontAsset != null)
{
TMP_Text textComponent = objects[i].GetComponent<TMP_Text>();
if (textComponent != null)
{
Undo.RecordObject(textComponent, "Font Asset Change");
textComponent.font = newFontAsset;
}
}
TMPro_EventManager.ON_DRAG_AND_DROP_MATERIAL_CHANGED(objects[i], currentMaterial, newMaterial);
}
}
}
}
#endif
}

View File

@@ -0,0 +1,29 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;
namespace TMPro
{
public class TMP_PostBuildProcessHandler
{
[PostProcessBuildAttribute(10000)]
public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
{
if (target == BuildTarget.iOS)
{
// Try loading the TMP Settings
TMP_Settings settings = Resources.Load<TMP_Settings>("TMP Settings");
if (settings == null)
return;
string file = Path.Combine(pathToBuiltProject, "Classes/UI/Keyboard.mm");
string content = File.ReadAllText(file);
content = content.Replace("FILTER_EMOJIS_IOS_KEYBOARD 1", "FILTER_EMOJIS_IOS_KEYBOARD 0");
File.WriteAllText(file, content);
}
}
}
}

View File

@@ -0,0 +1,31 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
namespace TMPro
{
public class TMP_PreBuildProcessor : IPreprocessBuildWithReport
{
public int callbackOrder { get { return 0; } }
public void OnPreprocessBuild(BuildReport report)
{
// Find all font assets in the project
string searchPattern = "t:TMP_FontAsset";
string[] fontAssetGUIDs = AssetDatabase.FindAssets(searchPattern);
for (int i = 0; i < fontAssetGUIDs.Length; i++)
{
string fontAssetPath = AssetDatabase.GUIDToAssetPath(fontAssetGUIDs[i]);
TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(fontAssetPath);
if (fontAsset != null && fontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic && fontAsset.clearDynamicDataOnBuild && fontAsset.atlasTexture.width != 0)
{
//Debug.Log("Clearing [" + fontAsset.name + "] dynamic font asset data.");
fontAsset.ClearFontAssetDataInternal();
}
}
}
}
}

View File

@@ -0,0 +1,43 @@
#if !UNITY_2018_3_OR_NEWER
using UnityEditor;
namespace TMPro
{
public static class TMP_ProjectTextSettings
{
// Open Project Text Settings
[MenuItem("Edit/Project Settings/TextMeshPro Settings", false, 309)]
public static void SelectProjectTextSettings()
{
TMP_Settings textSettings = TMP_Settings.instance;
if (textSettings)
{
Selection.activeObject = textSettings;
// TODO: Do we want to ping the Project Text Settings asset in the Project Inspector
EditorUtility.FocusProjectWindow();
EditorGUIUtility.PingObject(textSettings);
}
else
TMPro_EventManager.RESOURCE_LOAD_EVENT.Add(ON_RESOURCES_LOADED);
}
// Event received when TMP resources have been loaded.
static void ON_RESOURCES_LOADED()
{
TMPro_EventManager.RESOURCE_LOAD_EVENT.Remove(ON_RESOURCES_LOADED);
TMP_Settings textSettings = TMP_Settings.instance;
Selection.activeObject = textSettings;
// TODO: Do we want to ping the Project Text Settings asset in the Project Inspector
EditorUtility.FocusProjectWindow();
EditorGUIUtility.PingObject(textSettings);
}
}
}
#endif

View File

@@ -0,0 +1,68 @@
using UnityEditor;
using UnityEngine;
using System.Collections;
namespace TMPro.EditorUtilities
{
//[InitializeOnLoad]
class TMP_ResourcesLoader
{
/// <summary>
/// Function to pre-load the TMP Resources
/// </summary>
public static void LoadTextMeshProResources()
{
//TMP_Settings.LoadDefaultSettings();
//TMP_StyleSheet.LoadDefaultStyleSheet();
}
static TMP_ResourcesLoader()
{
//Debug.Log("Loading TMP Resources...");
// Get current targetted platform
//string Settings = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone);
//TMPro.TMP_Settings.LoadDefaultSettings();
//TMPro.TMP_StyleSheet.LoadDefaultStyleSheet();
}
//[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
//static void OnBeforeSceneLoaded()
//{
//Debug.Log("Before scene is loaded.");
// //TMPro.TMP_Settings.LoadDefaultSettings();
// //TMPro.TMP_StyleSheet.LoadDefaultStyleSheet();
// //ShaderVariantCollection collection = new ShaderVariantCollection();
// //Shader s0 = Shader.Find("TextMeshPro/Mobile/Distance Field");
// //ShaderVariantCollection.ShaderVariant tmp_Variant = new ShaderVariantCollection.ShaderVariant(s0, UnityEngine.Rendering.PassType.Normal, string.Empty);
// //collection.Add(tmp_Variant);
// //collection.WarmUp();
//}
}
//static class TMP_ProjectSettings
//{
// [InitializeOnLoadMethod]
// static void SetProjectDefineSymbols()
// {
// string currentBuildSettings = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
// //Check for and inject TMP_INSTALLED
// if (!currentBuildSettings.Contains("TMP_PRESENT"))
// {
// PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, currentBuildSettings + ";TMP_PRESENT");
// }
// }
//}
}

View File

@@ -0,0 +1,506 @@
using UnityEngine;
using UnityEditor;
namespace TMPro.EditorUtilities
{
public class TMP_SDFShaderGUI : TMP_BaseShaderGUI
{
static ShaderFeature s_OutlineFeature, s_UnderlayFeature, s_BevelFeature, s_GlowFeature, s_MaskFeature;
static bool s_Face = true, s_Outline = true, s_Outline2, s_Underlay, s_Lighting, s_Glow, s_Bevel, s_Light, s_Bump, s_Env;
static string[]
s_FaceUVSpeedName = { "_FaceUVSpeed" },
s_FaceUvSpeedNames = { "_FaceUVSpeedX", "_FaceUVSpeedY" },
s_OutlineUvSpeedNames = { "_OutlineUVSpeedX", "_OutlineUVSpeedY" };
static TMP_SDFShaderGUI()
{
s_OutlineFeature = new ShaderFeature()
{
undoLabel = "Outline",
keywords = new[] { "OUTLINE_ON" }
};
s_UnderlayFeature = new ShaderFeature()
{
undoLabel = "Underlay",
keywords = new[] { "UNDERLAY_ON", "UNDERLAY_INNER" },
label = new GUIContent("Underlay Type"),
keywordLabels = new[]
{
new GUIContent("None"), new GUIContent("Normal"), new GUIContent("Inner")
}
};
s_BevelFeature = new ShaderFeature()
{
undoLabel = "Bevel",
keywords = new[] { "BEVEL_ON" }
};
s_GlowFeature = new ShaderFeature()
{
undoLabel = "Glow",
keywords = new[] { "GLOW_ON" }
};
s_MaskFeature = new ShaderFeature()
{
undoLabel = "Mask",
keywords = new[] { "MASK_HARD", "MASK_SOFT" },
label = new GUIContent("Mask"),
keywordLabels = new[]
{
new GUIContent("Mask Off"), new GUIContent("Mask Hard"), new GUIContent("Mask Soft")
}
};
}
protected override void DoGUI()
{
s_Face = BeginPanel("Face", s_Face);
if (s_Face)
{
DoFacePanel();
}
EndPanel();
s_Outline = m_Material.HasProperty(ShaderUtilities.ID_OutlineTex) ? BeginPanel("Outline", s_Outline) : BeginPanel("Outline", s_OutlineFeature, s_Outline);
if (s_Outline)
{
DoOutlinePanel();
}
EndPanel();
if (m_Material.HasProperty(ShaderUtilities.ID_Outline2Color))
{
s_Outline2 = BeginPanel("Outline 2", s_OutlineFeature, s_Outline2);
if (s_Outline2)
{
DoOutline2Panel();
}
EndPanel();
}
if (m_Material.HasProperty(ShaderUtilities.ID_UnderlayColor))
{
s_Underlay = BeginPanel("Underlay", s_UnderlayFeature, s_Underlay);
if (s_Underlay)
{
DoUnderlayPanel();
}
EndPanel();
}
if (m_Material.HasProperty("_SpecularColor"))
{
s_Lighting = BeginPanel("Lighting", s_BevelFeature, s_Lighting);
if (s_Lighting)
{
s_Bevel = BeginPanel("Bevel", s_Bevel);
if (s_Bevel)
{
DoBevelPanel();
}
EndPanel();
s_Light = BeginPanel("Local Lighting", s_Light);
if (s_Light)
{
DoLocalLightingPanel();
}
EndPanel();
s_Bump = BeginPanel("Bump Map", s_Bump);
if (s_Bump)
{
DoBumpMapPanel();
}
EndPanel();
s_Env = BeginPanel("Environment Map", s_Env);
if (s_Env)
{
DoEnvMapPanel();
}
EndPanel();
}
EndPanel();
}
else if (m_Material.HasProperty("_SpecColor"))
{
s_Bevel = BeginPanel("Bevel", s_Bevel);
if (s_Bevel)
{
DoBevelPanel();
}
EndPanel();
s_Light = BeginPanel("Surface Lighting", s_Light);
if (s_Light)
{
DoSurfaceLightingPanel();
}
EndPanel();
s_Bump = BeginPanel("Bump Map", s_Bump);
if (s_Bump)
{
DoBumpMapPanel();
}
EndPanel();
s_Env = BeginPanel("Environment Map", s_Env);
if (s_Env)
{
DoEnvMapPanel();
}
EndPanel();
}
if (m_Material.HasProperty(ShaderUtilities.ID_GlowColor))
{
s_Glow = BeginPanel("Glow", s_GlowFeature, s_Glow);
if (s_Glow)
{
DoGlowPanel();
}
EndPanel();
}
s_DebugExtended = BeginPanel("Debug Settings", s_DebugExtended);
if (s_DebugExtended)
{
DoDebugPanel();
}
EndPanel();
}
void DoFacePanel()
{
EditorGUI.indentLevel += 1;
DoColor("_FaceColor", "Color");
if (m_Material.HasProperty(ShaderUtilities.ID_FaceTex))
{
if (m_Material.HasProperty("_FaceUVSpeedX"))
{
DoTexture2D("_FaceTex", "Texture", true, s_FaceUvSpeedNames);
}
else if (m_Material.HasProperty("_FaceUVSpeed"))
{
DoTexture2D("_FaceTex", "Texture", true, s_FaceUVSpeedName);
}
else
{
DoTexture2D("_FaceTex", "Texture", true);
}
}
if (m_Material.HasProperty("_FaceSoftness"))
{
DoSlider("_FaceSoftness", "X", "Softness");
}
if (m_Material.HasProperty("_OutlineSoftness"))
{
DoSlider("_OutlineSoftness", "Softness");
}
if (m_Material.HasProperty(ShaderUtilities.ID_FaceDilate))
{
DoSlider("_FaceDilate", "Dilate");
if (m_Material.HasProperty(ShaderUtilities.ID_Shininess))
{
DoSlider("_FaceShininess", "Gloss");
}
}
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoOutlinePanel()
{
EditorGUI.indentLevel += 1;
DoColor("_OutlineColor", "Color");
if (m_Material.HasProperty(ShaderUtilities.ID_OutlineTex))
{
if (m_Material.HasProperty("_OutlineUVSpeedX"))
{
DoTexture2D("_OutlineTex", "Texture", true, s_OutlineUvSpeedNames);
}
else
{
DoTexture2D("_OutlineTex", "Texture", true);
}
}
DoSlider("_OutlineWidth", "Thickness");
if (m_Material.HasProperty("_OutlineShininess"))
{
DoSlider("_OutlineShininess", "Gloss");
}
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoOutline2Panel()
{
EditorGUI.indentLevel += 1;
DoColor("_Outline2Color", "Color");
//if (m_Material.HasProperty(ShaderUtilities.ID_OutlineTex))
//{
// if (m_Material.HasProperty("_OutlineUVSpeedX"))
// {
// DoTexture2D("_OutlineTex", "Texture", true, s_OutlineUvSpeedNames);
// }
// else
// {
// DoTexture2D("_OutlineTex", "Texture", true);
// }
//}
DoSlider("_Outline2Width", "Thickness");
//if (m_Material.HasProperty("_OutlineShininess"))
//{
// DoSlider("_OutlineShininess", "Gloss");
//}
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoUnderlayPanel()
{
EditorGUI.indentLevel += 1;
s_UnderlayFeature.DoPopup(m_Editor, m_Material);
DoColor("_UnderlayColor", "Color");
DoSlider("_UnderlayOffsetX", "Offset X");
DoSlider("_UnderlayOffsetY", "Offset Y");
DoSlider("_UnderlayDilate", "Dilate");
DoSlider("_UnderlaySoftness", "Softness");
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
static GUIContent[] s_BevelTypeLabels =
{
new GUIContent("Outer Bevel"),
new GUIContent("Inner Bevel")
};
void DoBevelPanel()
{
EditorGUI.indentLevel += 1;
DoPopup("_ShaderFlags", "Type", s_BevelTypeLabels);
DoSlider("_Bevel", "Amount");
DoSlider("_BevelOffset", "Offset");
DoSlider("_BevelWidth", "Width");
DoSlider("_BevelRoundness", "Roundness");
DoSlider("_BevelClamp", "Clamp");
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoLocalLightingPanel()
{
EditorGUI.indentLevel += 1;
DoSlider("_LightAngle", "Light Angle");
DoColor("_SpecularColor", "Specular Color");
DoSlider("_SpecularPower", "Specular Power");
DoSlider("_Reflectivity", "Reflectivity Power");
DoSlider("_Diffuse", "Diffuse Shadow");
DoSlider("_Ambient", "Ambient Shadow");
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoSurfaceLightingPanel()
{
EditorGUI.indentLevel += 1;
DoColor("_SpecColor", "Specular Color");
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoBumpMapPanel()
{
EditorGUI.indentLevel += 1;
DoTexture2D("_BumpMap", "Texture");
DoSlider("_BumpFace", "Face");
DoSlider("_BumpOutline", "Outline");
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoEnvMapPanel()
{
EditorGUI.indentLevel += 1;
DoColor("_ReflectFaceColor", "Face Color");
DoColor("_ReflectOutlineColor", "Outline Color");
DoCubeMap("_Cube", "Texture");
DoVector3("_EnvMatrixRotation", "Rotation");
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoGlowPanel()
{
EditorGUI.indentLevel += 1;
DoColor("_GlowColor", "Color");
DoSlider("_GlowOffset", "Offset");
DoSlider("_GlowInner", "Inner");
DoSlider("_GlowOuter", "Outer");
DoSlider("_GlowPower", "Power");
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoDebugPanel()
{
EditorGUI.indentLevel += 1;
DoTexture2D("_MainTex", "Font Atlas");
DoFloat("_GradientScale", "Gradient Scale");
DoFloat("_TextureWidth", "Texture Width");
DoFloat("_TextureHeight", "Texture Height");
EditorGUILayout.Space();
DoFloat("_ScaleX", "Scale X");
DoFloat("_ScaleY", "Scale Y");
if (m_Material.HasProperty(ShaderUtilities.ID_Sharpness))
DoSlider("_Sharpness", "Sharpness");
DoSlider("_PerspectiveFilter", "Perspective Filter");
EditorGUILayout.Space();
DoFloat("_VertexOffsetX", "Offset X");
DoFloat("_VertexOffsetY", "Offset Y");
if (m_Material.HasProperty(ShaderUtilities.ID_MaskCoord))
{
EditorGUILayout.Space();
s_MaskFeature.ReadState(m_Material);
s_MaskFeature.DoPopup(m_Editor, m_Material);
if (s_MaskFeature.Active)
{
DoMaskSubgroup();
}
EditorGUILayout.Space();
DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels);
}
else if (m_Material.HasProperty("_MaskTex"))
{
DoMaskTexSubgroup();
}
else if (m_Material.HasProperty(ShaderUtilities.ID_MaskSoftnessX))
{
EditorGUILayout.Space();
DoFloat("_MaskSoftnessX", "Softness X");
DoFloat("_MaskSoftnessY", "Softness Y");
DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels);
}
if (m_Material.HasProperty(ShaderUtilities.ID_StencilID))
{
EditorGUILayout.Space();
DoFloat("_Stencil", "Stencil ID");
DoFloat("_StencilComp", "Stencil Comp");
}
EditorGUILayout.Space();
EditorGUI.BeginChangeCheck();
bool useRatios = EditorGUILayout.Toggle("Use Ratios", !m_Material.IsKeywordEnabled("RATIOS_OFF"));
if (EditorGUI.EndChangeCheck())
{
m_Editor.RegisterPropertyChangeUndo("Use Ratios");
if (useRatios)
{
m_Material.DisableKeyword("RATIOS_OFF");
}
else
{
m_Material.EnableKeyword("RATIOS_OFF");
}
}
if (m_Material.HasProperty(ShaderUtilities.ShaderTag_CullMode))
{
EditorGUILayout.Space();
DoPopup("_CullMode", "Cull Mode", s_CullingTypeLabels);
}
EditorGUILayout.Space();
EditorGUI.BeginDisabledGroup(true);
DoFloat("_ScaleRatioA", "Scale Ratio A");
DoFloat("_ScaleRatioB", "Scale Ratio B");
DoFloat("_ScaleRatioC", "Scale Ratio C");
EditorGUI.EndDisabledGroup();
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
void DoMaskSubgroup()
{
DoVector("_MaskCoord", "Mask Bounds", s_XywhVectorLabels);
if (Selection.activeGameObject != null)
{
Renderer renderer = Selection.activeGameObject.GetComponent<Renderer>();
if (renderer != null)
{
Rect rect = EditorGUILayout.GetControlRect();
rect.x += EditorGUIUtility.labelWidth;
rect.width -= EditorGUIUtility.labelWidth;
if (GUI.Button(rect, "Match Renderer Bounds"))
{
FindProperty("_MaskCoord", m_Properties).vectorValue = new Vector4(
0,
0,
Mathf.Round(renderer.bounds.extents.x * 1000) / 1000,
Mathf.Round(renderer.bounds.extents.y * 1000) / 1000
);
}
}
}
if (s_MaskFeature.State == 1)
{
DoFloat("_MaskSoftnessX", "Softness X");
DoFloat("_MaskSoftnessY", "Softness Y");
}
}
void DoMaskTexSubgroup()
{
EditorGUILayout.Space();
DoTexture2D("_MaskTex", "Mask Texture");
DoToggle("_MaskInverse", "Inverse Mask");
DoColor("_MaskEdgeColor", "Edge Color");
DoSlider("_MaskEdgeSoftness", "Edge Softness");
DoSlider("_MaskWipeControl", "Wipe Position");
DoFloat("_MaskSoftnessX", "Softness X");
DoFloat("_MaskSoftnessY", "Softness Y");
DoVector("_ClipRect", "Clip Rect", s_LbrtVectorLabels);
}
}
}

View File

@@ -0,0 +1,14 @@
using UnityEngine;
using UnityEditor;
namespace TMPro
{
class TMP_SerializedPropertyHolder : ScriptableObject
{
public TMP_FontAsset fontAsset;
public uint firstCharacter;
public uint secondCharacter;
public TMP_GlyphPairAdjustmentRecord glyphPairAdjustmentRecord = new TMP_GlyphPairAdjustmentRecord(new TMP_GlyphAdjustmentRecord(), new TMP_GlyphAdjustmentRecord());
}
}

View File

@@ -0,0 +1,381 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
#pragma warning disable 0414 // Disabled a few warnings for not yet implemented features.
namespace TMPro.EditorUtilities
{
[CustomEditor(typeof(TMP_Settings))]
public class TMP_SettingsEditor : Editor
{
internal class Styles
{
public static readonly GUIContent defaultFontAssetLabel = new GUIContent("Default Font Asset", "The Font Asset that will be assigned by default to newly created text objects when no Font Asset is specified.");
public static readonly GUIContent defaultFontAssetPathLabel = new GUIContent("Path: Resources/", "The relative path to a Resources folder where the Font Assets and Material Presets are located.\nExample \"Fonts & Materials/\"");
public static readonly GUIContent fallbackFontAssetsLabel = new GUIContent("Fallback Font Assets", "The Font Assets that will be searched to locate and replace missing characters from a given Font Asset.");
public static readonly GUIContent fallbackFontAssetsListLabel = new GUIContent("Fallback Font Assets List", "The Font Assets that will be searched to locate and replace missing characters from a given Font Asset.");
public static readonly GUIContent fallbackMaterialSettingsLabel = new GUIContent("Fallback Material Settings");
public static readonly GUIContent matchMaterialPresetLabel = new GUIContent("Match Material Presets");
public static readonly GUIContent containerDefaultSettingsLabel = new GUIContent("Text Container Default Settings");
public static readonly GUIContent textMeshProLabel = new GUIContent("TextMeshPro");
public static readonly GUIContent textMeshProUiLabel = new GUIContent("TextMeshPro UI");
public static readonly GUIContent enableRaycastTarget = new GUIContent("Enable Raycast Target");
public static readonly GUIContent autoSizeContainerLabel = new GUIContent("Auto Size Text Container", "Set the size of the text container to match the text.");
public static readonly GUIContent isTextObjectScaleStaticLabel = new GUIContent("Is Object Scale Static", "Disables calling InternalUpdate() when enabled. This can improve performance when text object scale is static.");
public static readonly GUIContent textComponentDefaultSettingsLabel = new GUIContent("Text Component Default Settings");
public static readonly GUIContent defaultFontSize = new GUIContent("Default Font Size");
public static readonly GUIContent autoSizeRatioLabel = new GUIContent("Text Auto Size Ratios");
public static readonly GUIContent minLabel = new GUIContent("Min");
public static readonly GUIContent maxLabel = new GUIContent("Max");
public static readonly GUIContent wordWrappingLabel = new GUIContent("Word Wrapping");
public static readonly GUIContent kerningLabel = new GUIContent("Kerning");
public static readonly GUIContent extraPaddingLabel = new GUIContent("Extra Padding");
public static readonly GUIContent tintAllSpritesLabel = new GUIContent("Tint All Sprites");
public static readonly GUIContent parseEscapeCharactersLabel = new GUIContent("Parse Escape Sequence");
public static readonly GUIContent dynamicFontSystemSettingsLabel = new GUIContent("Dynamic Font System Settings");
public static readonly GUIContent getFontFeaturesAtRuntime = new GUIContent("Get Font Features at Runtime", "Determines if Glyph Adjustment Data will be retrieved from font files at runtime when new characters and glyphs are added to font assets.");
public static readonly GUIContent dynamicAtlasTextureGroup = new GUIContent("Dynamic Atlas Texture Group");
public static readonly GUIContent missingGlyphLabel = new GUIContent("Missing Character Unicode", "The character to be displayed when the requested character is not found in any font asset or fallbacks.");
public static readonly GUIContent disableWarningsLabel = new GUIContent("Disable warnings", "Disable warning messages in the Console.");
public static readonly GUIContent defaultSpriteAssetLabel = new GUIContent("Default Sprite Asset", "The Sprite Asset that will be assigned by default when using the <sprite> tag when no Sprite Asset is specified.");
public static readonly GUIContent missingSpriteCharacterUnicodeLabel = new GUIContent("Missing Sprite Unicode", "The unicode value for the sprite character to be displayed when the requested sprite character is not found in any sprite assets or fallbacks.");
public static readonly GUIContent enableEmojiSupportLabel = new GUIContent("iOS Emoji Support", "Enables Emoji support for Touch Screen Keyboards on target devices.");
//public static readonly GUIContent spriteRelativeScale = new GUIContent("Relative Scaling", "Determines if the sprites will be scaled relative to the primary font asset assigned to the text object or relative to the current font asset.");
public static readonly GUIContent spriteAssetsPathLabel = new GUIContent("Path: Resources/", "The relative path to a Resources folder where the Sprite Assets are located.\nExample \"Sprite Assets/\"");
public static readonly GUIContent defaultStyleSheetLabel = new GUIContent("Default Style Sheet", "The Style Sheet that will be used for all text objects in this project.");
public static readonly GUIContent styleSheetResourcePathLabel = new GUIContent("Path: Resources/", "The relative path to a Resources folder where the Style Sheets are located.\nExample \"Style Sheets/\"");
public static readonly GUIContent colorGradientPresetsLabel = new GUIContent("Color Gradient Presets", "The relative path to a Resources folder where the Color Gradient Presets are located.\nExample \"Color Gradient Presets/\"");
public static readonly GUIContent colorGradientsPathLabel = new GUIContent("Path: Resources/", "The relative path to a Resources folder where the Color Gradient Presets are located.\nExample \"Color Gradient Presets/\"");
public static readonly GUIContent lineBreakingLabel = new GUIContent("Line Breaking for Asian languages", "The text assets that contain the Leading and Following characters which define the rules for line breaking with Asian languages.");
public static readonly GUIContent koreanSpecificRules = new GUIContent("Korean Language Options");
}
SerializedProperty m_PropFontAsset;
SerializedProperty m_PropDefaultFontAssetPath;
SerializedProperty m_PropDefaultFontSize;
SerializedProperty m_PropDefaultAutoSizeMinRatio;
SerializedProperty m_PropDefaultAutoSizeMaxRatio;
SerializedProperty m_PropDefaultTextMeshProTextContainerSize;
SerializedProperty m_PropDefaultTextMeshProUITextContainerSize;
SerializedProperty m_PropAutoSizeTextContainer;
SerializedProperty m_PropEnableRaycastTarget;
SerializedProperty m_PropIsTextObjectScaleStatic;
SerializedProperty m_PropSpriteAsset;
SerializedProperty m_PropMissingSpriteCharacterUnicode;
//SerializedProperty m_PropSpriteRelativeScaling;
SerializedProperty m_PropEnableEmojiSupport;
SerializedProperty m_PropSpriteAssetPath;
SerializedProperty m_PropStyleSheet;
SerializedProperty m_PropStyleSheetsResourcePath;
ReorderableList m_List;
SerializedProperty m_PropColorGradientPresetsPath;
SerializedProperty m_PropMatchMaterialPreset;
SerializedProperty m_PropWordWrapping;
SerializedProperty m_PropKerning;
SerializedProperty m_PropExtraPadding;
SerializedProperty m_PropTintAllSprites;
SerializedProperty m_PropParseEscapeCharacters;
SerializedProperty m_PropMissingGlyphCharacter;
//SerializedProperty m_DynamicAtlasTextureManager;
SerializedProperty m_GetFontFeaturesAtRuntime;
SerializedProperty m_PropWarningsDisabled;
SerializedProperty m_PropLeadingCharacters;
SerializedProperty m_PropFollowingCharacters;
SerializedProperty m_PropUseModernHangulLineBreakingRules;
private const string k_UndoRedo = "UndoRedoPerformed";
public void OnEnable()
{
if (target == null)
return;
m_PropFontAsset = serializedObject.FindProperty("m_defaultFontAsset");
m_PropDefaultFontAssetPath = serializedObject.FindProperty("m_defaultFontAssetPath");
m_PropDefaultFontSize = serializedObject.FindProperty("m_defaultFontSize");
m_PropDefaultAutoSizeMinRatio = serializedObject.FindProperty("m_defaultAutoSizeMinRatio");
m_PropDefaultAutoSizeMaxRatio = serializedObject.FindProperty("m_defaultAutoSizeMaxRatio");
m_PropDefaultTextMeshProTextContainerSize = serializedObject.FindProperty("m_defaultTextMeshProTextContainerSize");
m_PropDefaultTextMeshProUITextContainerSize = serializedObject.FindProperty("m_defaultTextMeshProUITextContainerSize");
m_PropAutoSizeTextContainer = serializedObject.FindProperty("m_autoSizeTextContainer");
m_PropEnableRaycastTarget = serializedObject.FindProperty("m_EnableRaycastTarget");
m_PropIsTextObjectScaleStatic = serializedObject.FindProperty("m_IsTextObjectScaleStatic");
m_PropSpriteAsset = serializedObject.FindProperty("m_defaultSpriteAsset");
m_PropMissingSpriteCharacterUnicode = serializedObject.FindProperty("m_MissingCharacterSpriteUnicode");
//m_PropSpriteRelativeScaling = serializedObject.FindProperty("m_SpriteRelativeScaling");
m_PropEnableEmojiSupport = serializedObject.FindProperty("m_enableEmojiSupport");
m_PropSpriteAssetPath = serializedObject.FindProperty("m_defaultSpriteAssetPath");
m_PropStyleSheet = serializedObject.FindProperty("m_defaultStyleSheet");
m_PropStyleSheetsResourcePath = serializedObject.FindProperty("m_StyleSheetsResourcePath");
m_PropColorGradientPresetsPath = serializedObject.FindProperty("m_defaultColorGradientPresetsPath");
m_List = new ReorderableList(serializedObject, serializedObject.FindProperty("m_fallbackFontAssets"), true, true, true, true);
m_List.drawElementCallback = (rect, index, isActive, isFocused) =>
{
var element = m_List.serializedProperty.GetArrayElementAtIndex(index);
rect.y += 2;
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
};
m_List.drawHeaderCallback = rect =>
{
EditorGUI.LabelField(rect, Styles.fallbackFontAssetsListLabel);
};
m_PropMatchMaterialPreset = serializedObject.FindProperty("m_matchMaterialPreset");
m_PropWordWrapping = serializedObject.FindProperty("m_enableWordWrapping");
m_PropKerning = serializedObject.FindProperty("m_enableKerning");
m_PropExtraPadding = serializedObject.FindProperty("m_enableExtraPadding");
m_PropTintAllSprites = serializedObject.FindProperty("m_enableTintAllSprites");
m_PropParseEscapeCharacters = serializedObject.FindProperty("m_enableParseEscapeCharacters");
m_PropMissingGlyphCharacter = serializedObject.FindProperty("m_missingGlyphCharacter");
m_PropWarningsDisabled = serializedObject.FindProperty("m_warningsDisabled");
//m_DynamicAtlasTextureManager = serializedObject.FindProperty("m_DynamicAtlasTextureGroup");
m_GetFontFeaturesAtRuntime = serializedObject.FindProperty("m_GetFontFeaturesAtRuntime");
m_PropLeadingCharacters = serializedObject.FindProperty("m_leadingCharacters");
m_PropFollowingCharacters = serializedObject.FindProperty("m_followingCharacters");
m_PropUseModernHangulLineBreakingRules = serializedObject.FindProperty("m_UseModernHangulLineBreakingRules");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
string evt_cmd = Event.current.commandName;
float labelWidth = EditorGUIUtility.labelWidth;
float fieldWidth = EditorGUIUtility.fieldWidth;
// TextMeshPro Font Info Panel
EditorGUI.indentLevel = 0;
// FONT ASSET
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label(Styles.defaultFontAssetLabel, EditorStyles.boldLabel);
EditorGUI.indentLevel = 1;
EditorGUILayout.PropertyField(m_PropFontAsset, Styles.defaultFontAssetLabel);
EditorGUILayout.PropertyField(m_PropDefaultFontAssetPath, Styles.defaultFontAssetPathLabel);
EditorGUI.indentLevel = 0;
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
// FALLBACK FONT ASSETs
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label(Styles.fallbackFontAssetsLabel, EditorStyles.boldLabel);
m_List.DoLayoutList();
GUILayout.Label(Styles.fallbackMaterialSettingsLabel, EditorStyles.boldLabel);
EditorGUI.indentLevel = 1;
EditorGUILayout.PropertyField(m_PropMatchMaterialPreset, Styles.matchMaterialPresetLabel);
EditorGUI.indentLevel = 0;
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
// MISSING GLYPHS
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label(Styles.dynamicFontSystemSettingsLabel, EditorStyles.boldLabel);
EditorGUI.indentLevel = 1;
EditorGUILayout.PropertyField(m_GetFontFeaturesAtRuntime, Styles.getFontFeaturesAtRuntime);
EditorGUILayout.PropertyField(m_PropMissingGlyphCharacter, Styles.missingGlyphLabel);
EditorGUILayout.PropertyField(m_PropWarningsDisabled, Styles.disableWarningsLabel);
//EditorGUILayout.PropertyField(m_DynamicAtlasTextureManager, Styles.dynamicAtlasTextureManager);
EditorGUI.indentLevel = 0;
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
// TEXT OBJECT DEFAULT PROPERTIES
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label(Styles.containerDefaultSettingsLabel, EditorStyles.boldLabel);
EditorGUI.indentLevel = 1;
EditorGUILayout.PropertyField(m_PropDefaultTextMeshProTextContainerSize, Styles.textMeshProLabel);
EditorGUILayout.PropertyField(m_PropDefaultTextMeshProUITextContainerSize, Styles.textMeshProUiLabel);
EditorGUILayout.PropertyField(m_PropEnableRaycastTarget, Styles.enableRaycastTarget);
EditorGUILayout.PropertyField(m_PropAutoSizeTextContainer, Styles.autoSizeContainerLabel);
EditorGUILayout.PropertyField(m_PropIsTextObjectScaleStatic, Styles.isTextObjectScaleStaticLabel);
EditorGUI.indentLevel = 0;
EditorGUILayout.Space();
GUILayout.Label(Styles.textComponentDefaultSettingsLabel, EditorStyles.boldLabel);
EditorGUI.indentLevel = 1;
EditorGUILayout.PropertyField(m_PropDefaultFontSize, Styles.defaultFontSize);
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.PrefixLabel(Styles.autoSizeRatioLabel);
EditorGUIUtility.labelWidth = 32;
EditorGUIUtility.fieldWidth = 10;
EditorGUI.indentLevel = 0;
EditorGUILayout.PropertyField(m_PropDefaultAutoSizeMinRatio, Styles.minLabel);
EditorGUILayout.PropertyField(m_PropDefaultAutoSizeMaxRatio, Styles.maxLabel);
EditorGUI.indentLevel = 1;
}
EditorGUILayout.EndHorizontal();
EditorGUIUtility.labelWidth = labelWidth;
EditorGUIUtility.fieldWidth = fieldWidth;
EditorGUILayout.PropertyField(m_PropWordWrapping, Styles.wordWrappingLabel);
EditorGUILayout.PropertyField(m_PropKerning, Styles.kerningLabel);
EditorGUILayout.PropertyField(m_PropExtraPadding, Styles.extraPaddingLabel);
EditorGUILayout.PropertyField(m_PropTintAllSprites, Styles.tintAllSpritesLabel);
EditorGUILayout.PropertyField(m_PropParseEscapeCharacters, Styles.parseEscapeCharactersLabel);
EditorGUI.indentLevel = 0;
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
// SPRITE ASSET
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label(Styles.defaultSpriteAssetLabel, EditorStyles.boldLabel);
EditorGUI.indentLevel = 1;
EditorGUILayout.PropertyField(m_PropSpriteAsset, Styles.defaultSpriteAssetLabel);
EditorGUILayout.PropertyField(m_PropMissingSpriteCharacterUnicode, Styles.missingSpriteCharacterUnicodeLabel);
EditorGUILayout.PropertyField(m_PropEnableEmojiSupport, Styles.enableEmojiSupportLabel);
//EditorGUILayout.PropertyField(m_PropSpriteRelativeScaling, Styles.spriteRelativeScale);
EditorGUILayout.PropertyField(m_PropSpriteAssetPath, Styles.spriteAssetsPathLabel);
EditorGUI.indentLevel = 0;
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
// STYLE SHEET
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label(Styles.defaultStyleSheetLabel, EditorStyles.boldLabel);
EditorGUI.indentLevel = 1;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_PropStyleSheet, Styles.defaultStyleSheetLabel);
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
TMP_StyleSheet styleSheet = m_PropStyleSheet.objectReferenceValue as TMP_StyleSheet;
if (styleSheet != null)
styleSheet.RefreshStyles();
}
EditorGUILayout.PropertyField(m_PropStyleSheetsResourcePath, Styles.styleSheetResourcePathLabel);
EditorGUI.indentLevel = 0;
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
// COLOR GRADIENT PRESETS
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label(Styles.colorGradientPresetsLabel, EditorStyles.boldLabel);
EditorGUI.indentLevel = 1;
EditorGUILayout.PropertyField(m_PropColorGradientPresetsPath, Styles.colorGradientsPathLabel);
EditorGUI.indentLevel = 0;
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
// LINE BREAKING RULE
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label(Styles.lineBreakingLabel, EditorStyles.boldLabel);
EditorGUI.indentLevel = 1;
EditorGUILayout.PropertyField(m_PropLeadingCharacters);
EditorGUILayout.PropertyField(m_PropFollowingCharacters);
EditorGUILayout.Space();
GUILayout.Label(Styles.koreanSpecificRules, EditorStyles.boldLabel);
EditorGUILayout.PropertyField(m_PropUseModernHangulLineBreakingRules, new GUIContent("Use Modern Line Breaking", "Determines if traditional or modern line breaking rules will be used to control line breaking. Traditional line breaking rules use the Leading and Following Character rules whereas Modern uses spaces for line breaking."));
EditorGUI.indentLevel = 0;
EditorGUILayout.Space();
EditorGUILayout.EndVertical();
if (serializedObject.ApplyModifiedProperties() || evt_cmd == k_UndoRedo)
{
EditorUtility.SetDirty(target);
TMPro_EventManager.ON_TMP_SETTINGS_CHANGED();
}
}
}
#if UNITY_2018_3_OR_NEWER
class TMP_ResourceImporterProvider : SettingsProvider
{
TMP_PackageResourceImporter m_ResourceImporter;
public TMP_ResourceImporterProvider()
: base("Project/TextMesh Pro", SettingsScope.Project)
{
}
public override void OnGUI(string searchContext)
{
// Lazy creation that supports domain reload
if (m_ResourceImporter == null)
m_ResourceImporter = new TMP_PackageResourceImporter();
m_ResourceImporter.OnGUI();
}
public override void OnDeactivate()
{
if (m_ResourceImporter != null)
m_ResourceImporter.OnDestroy();
}
static UnityEngine.Object GetTMPSettings()
{
return Resources.Load<TMP_Settings>("TMP Settings");
}
[SettingsProviderGroup]
static SettingsProvider[] CreateTMPSettingsProvider()
{
var providers = new List<SettingsProvider> { new TMP_ResourceImporterProvider() };
if (GetTMPSettings() != null)
{
var provider = new AssetSettingsProvider("Project/TextMesh Pro/Settings", GetTMPSettings);
provider.PopulateSearchKeywordsFromGUIContentProperties<TMP_SettingsEditor.Styles>();
providers.Add(provider);
}
return providers.ToArray();
}
}
#endif
}

View File

@@ -0,0 +1,951 @@
using UnityEngine;
using UnityEngine.TextCore;
using UnityEditor;
using UnityEditorInternal;
using System.Collections.Generic;
namespace TMPro.EditorUtilities
{
[CustomEditor(typeof(TMP_SpriteAsset))]
public class TMP_SpriteAssetEditor : Editor
{
struct UI_PanelState
{
public static bool spriteAssetFaceInfoPanel = true;
public static bool spriteAtlasInfoPanel = true;
public static bool fallbackSpriteAssetPanel = true;
public static bool spriteCharacterTablePanel;
public static bool spriteGlyphTablePanel;
}
private static string[] s_UiStateLabel = new string[] { "<i>(Click to collapse)</i> ", "<i>(Click to expand)</i> " };
int m_moveToIndex;
int m_selectedElement = -1;
int m_CurrentCharacterPage;
int m_CurrentGlyphPage;
const string k_UndoRedo = "UndoRedoPerformed";
string m_CharacterSearchPattern;
List<int> m_CharacterSearchList;
bool m_IsCharacterSearchDirty;
string m_GlyphSearchPattern;
List<int> m_GlyphSearchList;
bool m_IsGlyphSearchDirty;
SerializedProperty m_FaceInfoProperty;
SerializedProperty m_PointSizeProperty;
SerializedProperty m_ScaleProperty;
SerializedProperty m_LineHeightProperty;
SerializedProperty m_AscentLineProperty;
SerializedProperty m_BaselineProperty;
SerializedProperty m_DescentLineProperty;
SerializedProperty m_spriteAtlas_prop;
SerializedProperty m_material_prop;
SerializedProperty m_SpriteCharacterTableProperty;
SerializedProperty m_SpriteGlyphTableProperty;
ReorderableList m_fallbackSpriteAssetList;
TMP_SpriteAsset m_SpriteAsset;
bool isAssetDirty;
float m_xOffset;
float m_yOffset;
float m_xAdvance;
float m_scale;
public void OnEnable()
{
m_SpriteAsset = target as TMP_SpriteAsset;
m_FaceInfoProperty = serializedObject.FindProperty("m_FaceInfo");
m_PointSizeProperty = m_FaceInfoProperty.FindPropertyRelative("m_PointSize");
m_ScaleProperty = m_FaceInfoProperty.FindPropertyRelative("m_Scale");
m_LineHeightProperty = m_FaceInfoProperty.FindPropertyRelative("m_LineHeight");
m_AscentLineProperty = m_FaceInfoProperty.FindPropertyRelative("m_AscentLine");
m_BaselineProperty = m_FaceInfoProperty.FindPropertyRelative("m_Baseline");
m_DescentLineProperty = m_FaceInfoProperty.FindPropertyRelative("m_DescentLine");
m_spriteAtlas_prop = serializedObject.FindProperty("spriteSheet");
m_material_prop = serializedObject.FindProperty("material");
m_SpriteCharacterTableProperty = serializedObject.FindProperty("m_SpriteCharacterTable");
m_SpriteGlyphTableProperty = serializedObject.FindProperty("m_SpriteGlyphTable");
// Fallback TMP Sprite Asset list
m_fallbackSpriteAssetList = new ReorderableList(serializedObject, serializedObject.FindProperty("fallbackSpriteAssets"), true, true, true, true);
m_fallbackSpriteAssetList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
{
var element = m_fallbackSpriteAssetList.serializedProperty.GetArrayElementAtIndex(index);
rect.y += 2;
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
};
m_fallbackSpriteAssetList.drawHeaderCallback = rect =>
{
EditorGUI.LabelField(rect, new GUIContent("Fallback Sprite Asset List", "Select the Sprite Assets that will be searched and used as fallback when a given sprite is missing from this sprite asset."));
};
}
public override void OnInspectorGUI()
{
//Debug.Log("OnInspectorGUI Called.");
Event currentEvent = Event.current;
string evt_cmd = currentEvent.commandName; // Get Current Event CommandName to check for Undo Events
serializedObject.Update();
// TEXTMESHPRO SPRITE INFO PANEL
#region Display Sprite Asset Face Info
Rect rect = EditorGUILayout.GetControlRect(false, 24);
GUI.Label(rect, new GUIContent("<b>Face Info</b> - v" + m_SpriteAsset.version), TMP_UIStyleManager.sectionHeader);
rect.x += rect.width - 132f;
rect.y += 2;
rect.width = 130f;
rect.height = 18f;
if (GUI.Button(rect, new GUIContent("Update Sprite Asset")))
{
TMP_SpriteAssetMenu.UpdateSpriteAsset(m_SpriteAsset);
}
EditorGUI.indentLevel = 1;
EditorGUILayout.PropertyField(m_PointSizeProperty);
EditorGUILayout.PropertyField(m_ScaleProperty);
//EditorGUILayout.PropertyField(m_LineHeightProperty);
EditorGUILayout.PropertyField(m_AscentLineProperty);
EditorGUILayout.PropertyField(m_BaselineProperty);
EditorGUILayout.PropertyField(m_DescentLineProperty);
EditorGUILayout.Space();
#endregion
// ATLAS TEXTURE & MATERIAL
#region Display Atlas Texture and Material
rect = EditorGUILayout.GetControlRect(false, 24);
if (GUI.Button(rect, new GUIContent("<b>Atlas & Material</b>"), TMP_UIStyleManager.sectionHeader))
UI_PanelState.spriteAtlasInfoPanel = !UI_PanelState.spriteAtlasInfoPanel;
GUI.Label(rect, (UI_PanelState.spriteAtlasInfoPanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);
if (UI_PanelState.spriteAtlasInfoPanel)
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_spriteAtlas_prop, new GUIContent("Sprite Atlas"));
if (EditorGUI.EndChangeCheck())
{
// Assign the new sprite atlas texture to the current material
Texture2D tex = m_spriteAtlas_prop.objectReferenceValue as Texture2D;
if (tex != null)
{
Material mat = m_material_prop.objectReferenceValue as Material;
if (mat != null)
mat.mainTexture = tex;
}
}
EditorGUILayout.PropertyField(m_material_prop, new GUIContent("Default Material"));
EditorGUILayout.Space();
}
#endregion
// FALLBACK SPRITE ASSETS
#region Display Sprite Fallbacks
rect = EditorGUILayout.GetControlRect(false, 24);
EditorGUI.indentLevel = 0;
if (GUI.Button(rect, new GUIContent("<b>Fallback Sprite Assets</b>", "Select the Sprite Assets that will be searched and used as fallback when a given sprite is missing from this sprite asset."), TMP_UIStyleManager.sectionHeader))
UI_PanelState.fallbackSpriteAssetPanel = !UI_PanelState.fallbackSpriteAssetPanel;
GUI.Label(rect, (UI_PanelState.fallbackSpriteAssetPanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);
if (UI_PanelState.fallbackSpriteAssetPanel)
{
m_fallbackSpriteAssetList.DoLayoutList();
EditorGUILayout.Space();
}
#endregion
// SPRITE CHARACTER TABLE
#region Display Sprite Character Table
EditorGUI.indentLevel = 0;
rect = EditorGUILayout.GetControlRect(false, 24);
if (GUI.Button(rect, new GUIContent("<b>Sprite Character Table</b>", "List of sprite characters contained in this sprite asset."), TMP_UIStyleManager.sectionHeader))
UI_PanelState.spriteCharacterTablePanel = !UI_PanelState.spriteCharacterTablePanel;
GUI.Label(rect, (UI_PanelState.spriteCharacterTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);
if (UI_PanelState.spriteCharacterTablePanel)
{
int arraySize = m_SpriteCharacterTableProperty.arraySize;
int itemsPerPage = 10;
// Display Glyph Management Tools
EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.ExpandWidth(true));
{
// Search Bar implementation
#region DISPLAY SEARCH BAR
EditorGUILayout.BeginHorizontal();
{
EditorGUIUtility.labelWidth = 110f;
EditorGUI.BeginChangeCheck();
string searchPattern = EditorGUILayout.TextField("Sprite Search", m_CharacterSearchPattern, "SearchTextField");
if (EditorGUI.EndChangeCheck() || m_IsCharacterSearchDirty)
{
if (string.IsNullOrEmpty(searchPattern) == false)
{
//GUIUtility.keyboardControl = 0;
m_CharacterSearchPattern = searchPattern.ToLower(System.Globalization.CultureInfo.InvariantCulture).Trim();
// Search Glyph Table for potential matches
SearchCharacterTable(m_CharacterSearchPattern, ref m_CharacterSearchList);
}
else
m_CharacterSearchPattern = null;
m_IsCharacterSearchDirty = false;
}
string styleName = string.IsNullOrEmpty(m_CharacterSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
if (GUILayout.Button(GUIContent.none, styleName))
{
GUIUtility.keyboardControl = 0;
m_CharacterSearchPattern = string.Empty;
}
}
EditorGUILayout.EndHorizontal();
#endregion
// Display Page Navigation
if (!string.IsNullOrEmpty(m_CharacterSearchPattern))
arraySize = m_CharacterSearchList.Count;
// Display Page Navigation
DisplayPageNavigation(ref m_CurrentCharacterPage, arraySize, itemsPerPage);
}
EditorGUILayout.EndVertical();
if (arraySize > 0)
{
// Display each SpriteInfo entry using the SpriteInfo property drawer.
for (int i = itemsPerPage * m_CurrentCharacterPage; i < arraySize && i < itemsPerPage * (m_CurrentCharacterPage + 1); i++)
{
// Define the start of the selection region of the element.
Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
int elementIndex = i;
if (!string.IsNullOrEmpty(m_CharacterSearchPattern))
elementIndex = m_CharacterSearchList[i];
SerializedProperty spriteCharacterProperty = m_SpriteCharacterTableProperty.GetArrayElementAtIndex(elementIndex);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
{
EditorGUI.BeginDisabledGroup(i != m_selectedElement);
{
EditorGUILayout.PropertyField(spriteCharacterProperty);
}
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.EndVertical();
// Define the end of the selection region of the element.
Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
// Check for Item selection
Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
if (DoSelectionCheck(selectionArea))
{
if (m_selectedElement == i)
{
m_selectedElement = -1;
}
else
{
m_selectedElement = i;
GUIUtility.keyboardControl = 0;
}
}
// Draw & Handle Section Area
if (m_selectedElement == i)
{
// Draw selection highlight
TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));
// Draw options to MoveUp, MoveDown, Add or Remove Sprites
Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
controlRect.width /= 8;
// Move sprite up.
bool guiEnabled = GUI.enabled;
if (i == 0) { GUI.enabled = false; }
if (GUI.Button(controlRect, "Up"))
{
SwapCharacterElements(i, i - 1);
}
GUI.enabled = guiEnabled;
// Move sprite down.
controlRect.x += controlRect.width;
if (i == arraySize - 1) { GUI.enabled = false; }
if (GUI.Button(controlRect, "Down"))
{
SwapCharacterElements(i, i + 1);
}
GUI.enabled = guiEnabled;
// Move sprite to new index
controlRect.x += controlRect.width * 2;
//if (i == arraySize - 1) { GUI.enabled = false; }
m_moveToIndex = EditorGUI.IntField(controlRect, m_moveToIndex);
controlRect.x -= controlRect.width;
if (GUI.Button(controlRect, "Goto"))
{
MoveCharacterToIndex(i, m_moveToIndex);
}
//controlRect.x += controlRect.width;
GUI.enabled = guiEnabled;
// Add new Sprite
controlRect.x += controlRect.width * 4;
if (GUI.Button(controlRect, "+"))
{
m_SpriteCharacterTableProperty.arraySize += 1;
int index = m_SpriteCharacterTableProperty.arraySize - 1;
SerializedProperty spriteInfo_prop = m_SpriteCharacterTableProperty.GetArrayElementAtIndex(index);
// Copy properties of the selected element
CopyCharacterSerializedProperty(m_SpriteCharacterTableProperty.GetArrayElementAtIndex(elementIndex), ref spriteInfo_prop);
//spriteInfo_prop.FindPropertyRelative("m_Index").intValue = index;
serializedObject.ApplyModifiedProperties();
m_IsCharacterSearchDirty = true;
}
// Delete selected Sprite
controlRect.x += controlRect.width;
if (m_selectedElement == -1) GUI.enabled = false;
if (GUI.Button(controlRect, "-"))
{
m_SpriteCharacterTableProperty.DeleteArrayElementAtIndex(elementIndex);
m_selectedElement = -1;
serializedObject.ApplyModifiedProperties();
m_IsCharacterSearchDirty = true;
return;
}
}
}
}
DisplayPageNavigation(ref m_CurrentCharacterPage, arraySize, itemsPerPage);
EditorGUIUtility.labelWidth = 40f;
EditorGUIUtility.fieldWidth = 20f;
GUILayout.Space(5f);
// GLOBAL TOOLS
#region Global Tools
/*
GUI.enabled = true;
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
rect = EditorGUILayout.GetControlRect(false, 40);
float width = (rect.width - 75f) / 4;
EditorGUI.LabelField(rect, "Global Offsets & Scale", EditorStyles.boldLabel);
rect.x += 70;
bool old_ChangedState = GUI.changed;
GUI.changed = false;
m_xOffset = EditorGUI.FloatField(new Rect(rect.x + 5f + width * 0, rect.y + 20, width - 5f, 18), new GUIContent("OX:"), m_xOffset);
if (GUI.changed) UpdateGlobalProperty("m_HorizontalBearingX", m_xOffset);
m_yOffset = EditorGUI.FloatField(new Rect(rect.x + 5f + width * 1, rect.y + 20, width - 5f, 18), new GUIContent("OY:"), m_yOffset);
if (GUI.changed) UpdateGlobalProperty("m_HorizontalBearingY", m_yOffset);
m_xAdvance = EditorGUI.FloatField(new Rect(rect.x + 5f + width * 2, rect.y + 20, width - 5f, 18), new GUIContent("ADV."), m_xAdvance);
if (GUI.changed) UpdateGlobalProperty("m_HorizontalAdvance", m_xAdvance);
m_scale = EditorGUI.FloatField(new Rect(rect.x + 5f + width * 3, rect.y + 20, width - 5f, 18), new GUIContent("SF."), m_scale);
if (GUI.changed) UpdateGlobalProperty("m_Scale", m_scale);
EditorGUILayout.EndVertical();
GUI.changed = old_ChangedState;
*/
#endregion
}
#endregion
// SPRITE GLYPH TABLE
#region Display Sprite Glyph Table
EditorGUI.indentLevel = 0;
rect = EditorGUILayout.GetControlRect(false, 24);
if (GUI.Button(rect, new GUIContent("<b>Sprite Glyph Table</b>", "A list of the SpriteGlyphs contained in this sprite asset."), TMP_UIStyleManager.sectionHeader))
UI_PanelState.spriteGlyphTablePanel = !UI_PanelState.spriteGlyphTablePanel;
GUI.Label(rect, (UI_PanelState.spriteGlyphTablePanel ? "" : s_UiStateLabel[1]), TMP_UIStyleManager.rightLabel);
if (UI_PanelState.spriteGlyphTablePanel)
{
int arraySize = m_SpriteGlyphTableProperty.arraySize;
int itemsPerPage = 10;
// Display Glyph Management Tools
EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.ExpandWidth(true));
{
// Search Bar implementation
#region DISPLAY SEARCH BAR
EditorGUILayout.BeginHorizontal();
{
EditorGUIUtility.labelWidth = 110f;
EditorGUI.BeginChangeCheck();
string searchPattern = EditorGUILayout.TextField("Sprite Search", m_GlyphSearchPattern, "SearchTextField");
if (EditorGUI.EndChangeCheck() || m_IsGlyphSearchDirty)
{
if (string.IsNullOrEmpty(searchPattern) == false)
{
//GUIUtility.keyboardControl = 0;
m_GlyphSearchPattern = searchPattern.ToLower(System.Globalization.CultureInfo.InvariantCulture).Trim();
// Search Glyph Table for potential matches
SearchCharacterTable(m_GlyphSearchPattern, ref m_GlyphSearchList);
}
else
m_GlyphSearchPattern = null;
m_IsGlyphSearchDirty = false;
}
string styleName = string.IsNullOrEmpty(m_GlyphSearchPattern) ? "SearchCancelButtonEmpty" : "SearchCancelButton";
if (GUILayout.Button(GUIContent.none, styleName))
{
GUIUtility.keyboardControl = 0;
m_GlyphSearchPattern = string.Empty;
}
}
EditorGUILayout.EndHorizontal();
#endregion
// Display Page Navigation
if (!string.IsNullOrEmpty(m_GlyphSearchPattern))
arraySize = m_GlyphSearchList.Count;
// Display Page Navigation
DisplayPageNavigation(ref m_CurrentGlyphPage, arraySize, itemsPerPage);
}
EditorGUILayout.EndVertical();
if (arraySize > 0)
{
// Display each SpriteInfo entry using the SpriteInfo property drawer.
for (int i = itemsPerPage * m_CurrentGlyphPage; i < arraySize && i < itemsPerPage * (m_CurrentGlyphPage + 1); i++)
{
// Define the start of the selection region of the element.
Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
int elementIndex = i;
if (!string.IsNullOrEmpty(m_GlyphSearchPattern))
elementIndex = m_GlyphSearchList[i];
SerializedProperty spriteGlyphProperty = m_SpriteGlyphTableProperty.GetArrayElementAtIndex(elementIndex);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
{
EditorGUI.BeginDisabledGroup(i != m_selectedElement);
{
EditorGUILayout.PropertyField(spriteGlyphProperty);
}
EditorGUI.EndDisabledGroup();
}
EditorGUILayout.EndVertical();
// Define the end of the selection region of the element.
Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
// Check for Item selection
Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
if (DoSelectionCheck(selectionArea))
{
if (m_selectedElement == i)
{
m_selectedElement = -1;
}
else
{
m_selectedElement = i;
GUIUtility.keyboardControl = 0;
}
}
// Draw & Handle Section Area
if (m_selectedElement == i)
{
// Draw selection highlight
TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));
// Draw options to MoveUp, MoveDown, Add or Remove Sprites
Rect controlRect = EditorGUILayout.GetControlRect(true, EditorGUIUtility.singleLineHeight * 1f);
controlRect.width /= 8;
// Move sprite up.
bool guiEnabled = GUI.enabled;
if (i == 0) { GUI.enabled = false; }
if (GUI.Button(controlRect, "Up"))
{
SwapGlyphElements(i, i - 1);
}
GUI.enabled = guiEnabled;
// Move sprite down.
controlRect.x += controlRect.width;
if (i == arraySize - 1) { GUI.enabled = false; }
if (GUI.Button(controlRect, "Down"))
{
SwapGlyphElements(i, i + 1);
}
GUI.enabled = guiEnabled;
// Move sprite to new index
controlRect.x += controlRect.width * 2;
//if (i == arraySize - 1) { GUI.enabled = false; }
m_moveToIndex = EditorGUI.IntField(controlRect, m_moveToIndex);
controlRect.x -= controlRect.width;
if (GUI.Button(controlRect, "Goto"))
{
MoveGlyphToIndex(i, m_moveToIndex);
}
//controlRect.x += controlRect.width;
GUI.enabled = guiEnabled;
// Add new Sprite
controlRect.x += controlRect.width * 4;
if (GUI.Button(controlRect, "+"))
{
m_SpriteGlyphTableProperty.arraySize += 1;
int index = m_SpriteGlyphTableProperty.arraySize - 1;
SerializedProperty newSpriteGlyphProperty = m_SpriteGlyphTableProperty.GetArrayElementAtIndex(index);
// Copy properties of the selected element
CopyGlyphSerializedProperty(m_SpriteGlyphTableProperty.GetArrayElementAtIndex(elementIndex), ref newSpriteGlyphProperty);
newSpriteGlyphProperty.FindPropertyRelative("m_Index").intValue = index;
serializedObject.ApplyModifiedProperties();
m_IsGlyphSearchDirty = true;
//m_SpriteAsset.UpdateLookupTables();
}
// Delete selected Sprite
controlRect.x += controlRect.width;
if (m_selectedElement == -1) GUI.enabled = false;
if (GUI.Button(controlRect, "-"))
{
SerializedProperty selectedSpriteGlyphProperty = m_SpriteGlyphTableProperty.GetArrayElementAtIndex(elementIndex);
int selectedGlyphIndex = selectedSpriteGlyphProperty.FindPropertyRelative("m_Index").intValue;
m_SpriteGlyphTableProperty.DeleteArrayElementAtIndex(elementIndex);
// Remove all Sprite Characters referencing this glyph.
for (int j = 0; j < m_SpriteCharacterTableProperty.arraySize; j++)
{
int glyphIndex = m_SpriteCharacterTableProperty.GetArrayElementAtIndex(j).FindPropertyRelative("m_GlyphIndex").intValue;
if (glyphIndex == selectedGlyphIndex)
{
// Remove character
m_SpriteCharacterTableProperty.DeleteArrayElementAtIndex(j);
}
}
m_selectedElement = -1;
serializedObject.ApplyModifiedProperties();
m_IsGlyphSearchDirty = true;
//m_SpriteAsset.UpdateLookupTables();
return;
}
}
}
}
DisplayPageNavigation(ref m_CurrentGlyphPage, arraySize, itemsPerPage);
EditorGUIUtility.labelWidth = 40f;
EditorGUIUtility.fieldWidth = 20f;
GUILayout.Space(5f);
// GLOBAL TOOLS
#region Global Tools
GUI.enabled = true;
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("Global Offsets & Scale", EditorStyles.boldLabel);
bool old_ChangedState = GUI.changed;
GUI.changed = false;
EditorGUILayout.BeginHorizontal();
GUILayout.Space(25f);
m_xOffset = EditorGUILayout.FloatField(new GUIContent("OX:"), m_xOffset);
if (GUI.changed) UpdateGlobalProperty("m_HorizontalBearingX", m_xOffset);
m_yOffset = EditorGUILayout.FloatField(new GUIContent("OY:"), m_yOffset);
if (GUI.changed) UpdateGlobalProperty("m_HorizontalBearingY", m_yOffset);
m_xAdvance = EditorGUILayout.FloatField(new GUIContent("ADV."), m_xAdvance);
if (GUI.changed) UpdateGlobalProperty("m_HorizontalAdvance", m_xAdvance);
m_scale = EditorGUILayout.FloatField(new GUIContent("SF."), m_scale);
if (GUI.changed) UpdateGlobalProperty("m_Scale", m_scale);
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
#endregion
GUI.changed = old_ChangedState;
}
#endregion
if (serializedObject.ApplyModifiedProperties() || evt_cmd == k_UndoRedo || isAssetDirty)
{
if (m_SpriteAsset.m_IsSpriteAssetLookupTablesDirty || evt_cmd == k_UndoRedo)
m_SpriteAsset.UpdateLookupTables();
TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, m_SpriteAsset);
isAssetDirty = false;
EditorUtility.SetDirty(target);
}
// Clear selection if mouse event was not consumed.
GUI.enabled = true;
if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0)
m_selectedElement = -1;
}
/// <summary>
///
/// </summary>
/// <param name="arraySize"></param>
/// <param name="itemsPerPage"></param>
void DisplayPageNavigation(ref int currentPage, int arraySize, int itemsPerPage)
{
Rect pagePos = EditorGUILayout.GetControlRect(false, 20);
pagePos.width /= 3;
int shiftMultiplier = Event.current.shift ? 10 : 1; // Page + Shift goes 10 page forward
// Previous Page
GUI.enabled = currentPage > 0;
if (GUI.Button(pagePos, "Previous Page"))
{
currentPage -= 1 * shiftMultiplier;
//m_isNewPage = true;
}
// Page Counter
GUI.enabled = true;
pagePos.x += pagePos.width;
int totalPages = (int)(arraySize / (float)itemsPerPage + 0.999f);
GUI.Label(pagePos, "Page " + (currentPage + 1) + " / " + totalPages, TMP_UIStyleManager.centeredLabel);
// Next Page
pagePos.x += pagePos.width;
GUI.enabled = itemsPerPage * (currentPage + 1) < arraySize;
if (GUI.Button(pagePos, "Next Page"))
{
currentPage += 1 * shiftMultiplier;
//m_isNewPage = true;
}
// Clamp page range
currentPage = Mathf.Clamp(currentPage, 0, arraySize / itemsPerPage);
GUI.enabled = true;
}
/// <summary>
/// Method to update the properties of all sprites
/// </summary>
/// <param name="property"></param>
/// <param name="value"></param>
void UpdateGlobalProperty(string property, float value)
{
int arraySize = m_SpriteGlyphTableProperty.arraySize;
for (int i = 0; i < arraySize; i++)
{
// Get a reference to the sprite glyph.
SerializedProperty spriteGlyphProperty = m_SpriteGlyphTableProperty.GetArrayElementAtIndex(i);
if (property == "m_Scale")
{
spriteGlyphProperty.FindPropertyRelative(property).floatValue = value;
}
else
{
SerializedProperty glyphMetricsProperty = spriteGlyphProperty.FindPropertyRelative("m_Metrics");
glyphMetricsProperty.FindPropertyRelative(property).floatValue = value;
}
}
GUI.changed = false;
}
// Check if any of the Style elements were clicked on.
private bool DoSelectionCheck(Rect selectionArea)
{
Event currentEvent = Event.current;
switch (currentEvent.type)
{
case EventType.MouseDown:
if (selectionArea.Contains(currentEvent.mousePosition) && currentEvent.button == 0)
{
currentEvent.Use();
return true;
}
break;
}
return false;
}
/// <summary>
/// Swap the sprite item at the currently selected array index to another index.
/// </summary>
/// <param name="selectedIndex">Selected index.</param>
/// <param name="newIndex">New index.</param>
void SwapCharacterElements(int selectedIndex, int newIndex)
{
m_SpriteCharacterTableProperty.MoveArrayElement(selectedIndex, newIndex);
m_selectedElement = newIndex;
m_IsCharacterSearchDirty = true;
m_SpriteAsset.m_IsSpriteAssetLookupTablesDirty = true;
}
/// <summary>
/// Move Sprite Element at selected index to another index and reorder sprite list.
/// </summary>
/// <param name="selectedIndex"></param>
/// <param name="newIndex"></param>
void MoveCharacterToIndex(int selectedIndex, int newIndex)
{
int arraySize = m_SpriteCharacterTableProperty.arraySize;
if (newIndex >= arraySize)
newIndex = arraySize - 1;
m_SpriteCharacterTableProperty.MoveArrayElement(selectedIndex, newIndex);
m_selectedElement = newIndex;
m_IsCharacterSearchDirty = true;
m_SpriteAsset.m_IsSpriteAssetLookupTablesDirty = true;
// TODO: Need to handle switching pages if the character or glyph is moved to a different page.
}
/// <summary>
///
/// </summary>
/// <param name="selectedIndex"></param>
/// <param name="newIndex"></param>
void SwapGlyphElements(int selectedIndex, int newIndex)
{
m_SpriteGlyphTableProperty.MoveArrayElement(selectedIndex, newIndex);
m_selectedElement = newIndex;
m_IsGlyphSearchDirty = true;
m_SpriteAsset.m_IsSpriteAssetLookupTablesDirty = true;
}
/// <summary>
/// Move Sprite Element at selected index to another index and reorder sprite list.
/// </summary>
/// <param name="selectedIndex"></param>
/// <param name="newIndex"></param>
void MoveGlyphToIndex(int selectedIndex, int newIndex)
{
int arraySize = m_SpriteGlyphTableProperty.arraySize;
if (newIndex >= arraySize)
newIndex = arraySize - 1;
m_SpriteGlyphTableProperty.MoveArrayElement(selectedIndex, newIndex);
m_selectedElement = newIndex;
m_IsGlyphSearchDirty = true;
m_SpriteAsset.m_IsSpriteAssetLookupTablesDirty = true;
// TODO: Need to handle switching pages if the character or glyph is moved to a different page.
}
/// <summary>
///
/// </summary>
/// <param name="source"></param>
/// <param name="target"></param>
void CopyCharacterSerializedProperty(SerializedProperty source, ref SerializedProperty target)
{
target.FindPropertyRelative("m_Name").stringValue = source.FindPropertyRelative("m_Name").stringValue;
target.FindPropertyRelative("m_HashCode").intValue = source.FindPropertyRelative("m_HashCode").intValue;
target.FindPropertyRelative("m_Unicode").intValue = source.FindPropertyRelative("m_Unicode").intValue;
target.FindPropertyRelative("m_GlyphIndex").intValue = source.FindPropertyRelative("m_GlyphIndex").intValue;
target.FindPropertyRelative("m_Scale").floatValue = source.FindPropertyRelative("m_Scale").floatValue;
}
void CopyGlyphSerializedProperty(SerializedProperty srcGlyph, ref SerializedProperty dstGlyph)
{
// TODO : Should make a generic function which copies each of the properties.
// Index
dstGlyph.FindPropertyRelative("m_Index").intValue = srcGlyph.FindPropertyRelative("m_Index").intValue;
// GlyphMetrics
SerializedProperty srcGlyphMetrics = srcGlyph.FindPropertyRelative("m_Metrics");
SerializedProperty dstGlyphMetrics = dstGlyph.FindPropertyRelative("m_Metrics");
dstGlyphMetrics.FindPropertyRelative("m_Width").floatValue = srcGlyphMetrics.FindPropertyRelative("m_Width").floatValue;
dstGlyphMetrics.FindPropertyRelative("m_Height").floatValue = srcGlyphMetrics.FindPropertyRelative("m_Height").floatValue;
dstGlyphMetrics.FindPropertyRelative("m_HorizontalBearingX").floatValue = srcGlyphMetrics.FindPropertyRelative("m_HorizontalBearingX").floatValue;
dstGlyphMetrics.FindPropertyRelative("m_HorizontalBearingY").floatValue = srcGlyphMetrics.FindPropertyRelative("m_HorizontalBearingY").floatValue;
dstGlyphMetrics.FindPropertyRelative("m_HorizontalAdvance").floatValue = srcGlyphMetrics.FindPropertyRelative("m_HorizontalAdvance").floatValue;
// GlyphRect
SerializedProperty srcGlyphRect = srcGlyph.FindPropertyRelative("m_GlyphRect");
SerializedProperty dstGlyphRect = dstGlyph.FindPropertyRelative("m_GlyphRect");
dstGlyphRect.FindPropertyRelative("m_X").intValue = srcGlyphRect.FindPropertyRelative("m_X").intValue;
dstGlyphRect.FindPropertyRelative("m_Y").intValue = srcGlyphRect.FindPropertyRelative("m_Y").intValue;
dstGlyphRect.FindPropertyRelative("m_Width").intValue = srcGlyphRect.FindPropertyRelative("m_Width").intValue;
dstGlyphRect.FindPropertyRelative("m_Height").intValue = srcGlyphRect.FindPropertyRelative("m_Height").intValue;
dstGlyph.FindPropertyRelative("m_Scale").floatValue = srcGlyph.FindPropertyRelative("m_Scale").floatValue;
dstGlyph.FindPropertyRelative("m_AtlasIndex").intValue = srcGlyph.FindPropertyRelative("m_AtlasIndex").intValue;
}
/// <summary>
///
/// </summary>
/// <param name="searchPattern"></param>
/// <returns></returns>
void SearchCharacterTable(string searchPattern, ref List<int> searchResults)
{
if (searchResults == null) searchResults = new List<int>();
searchResults.Clear();
int arraySize = m_SpriteCharacterTableProperty.arraySize;
for (int i = 0; i < arraySize; i++)
{
SerializedProperty sourceSprite = m_SpriteCharacterTableProperty.GetArrayElementAtIndex(i);
// Check for potential match against array index
if (i.ToString().Contains(searchPattern))
{
searchResults.Add(i);
continue;
}
// Check for potential match against decimal id
int id = sourceSprite.FindPropertyRelative("m_GlyphIndex").intValue;
if (id.ToString().Contains(searchPattern))
{
searchResults.Add(i);
continue;
}
// Check for potential match against name
string name = sourceSprite.FindPropertyRelative("m_Name").stringValue.ToLower(System.Globalization.CultureInfo.InvariantCulture).Trim();
if (name.Contains(searchPattern))
{
searchResults.Add(i);
continue;
}
}
}
void SearchGlyphTable(string searchPattern, ref List<int> searchResults)
{
if (searchResults == null) searchResults = new List<int>();
searchResults.Clear();
int arraySize = m_SpriteGlyphTableProperty.arraySize;
for (int i = 0; i < arraySize; i++)
{
SerializedProperty sourceSprite = m_SpriteGlyphTableProperty.GetArrayElementAtIndex(i);
// Check for potential match against array index
if (i.ToString().Contains(searchPattern))
{
searchResults.Add(i);
continue;
}
// Check for potential match against decimal id
int id = sourceSprite.FindPropertyRelative("m_GlyphIndex").intValue;
if (id.ToString().Contains(searchPattern))
{
searchResults.Add(i);
continue;
}
// Check for potential match against name
string name = sourceSprite.FindPropertyRelative("m_Name").stringValue.ToLower(System.Globalization.CultureInfo.InvariantCulture).Trim();
if (name.Contains(searchPattern))
{
searchResults.Add(i);
continue;
}
}
}
}
}

View File

@@ -0,0 +1,256 @@
using System;
using UnityEngine;
using UnityEngine.TextCore;
using UnityEditor;
using System.IO;
using System.Collections.Generic;
using TMPro.EditorUtilities;
using TMPro.SpriteAssetUtilities;
namespace TMPro
{
public class TMP_SpriteAssetImporter : EditorWindow
{
// Create Sprite Asset Editor Window
[MenuItem("Window/TextMeshPro/Sprite Importer", false, 2026)]
public static void ShowFontAtlasCreatorWindow()
{
var window = GetWindow<TMP_SpriteAssetImporter>();
window.titleContent = new GUIContent("Sprite Importer");
window.Focus();
}
Texture2D m_SpriteAtlas;
SpriteAssetImportFormats m_SpriteDataFormat = SpriteAssetImportFormats.TexturePackerJsonArray;
TextAsset m_JsonFile;
string m_CreationFeedback;
TMP_SpriteAsset m_SpriteAsset;
/// <summary>
///
/// </summary>
void OnEnable()
{
// Set Editor Window Size
SetEditorWindowSize();
}
/// <summary>
///
/// </summary>
public void OnGUI()
{
DrawEditorPanel();
}
/// <summary>
///
/// </summary>
private void OnDisable()
{
// Clean up sprite asset object that may have been created and not saved.
if (m_SpriteAsset != null && !EditorUtility.IsPersistent(m_SpriteAsset))
DestroyImmediate(m_SpriteAsset);
}
/// <summary>
///
/// </summary>
void DrawEditorPanel()
{
// label
GUILayout.Label("Import Settings", EditorStyles.boldLabel);
EditorGUI.BeginChangeCheck();
// Sprite Texture Selection
m_JsonFile = EditorGUILayout.ObjectField("Sprite Data Source", m_JsonFile, typeof(TextAsset), false) as TextAsset;
m_SpriteDataFormat = (SpriteAssetImportFormats)EditorGUILayout.EnumPopup("Import Format", m_SpriteDataFormat);
// Sprite Texture Selection
m_SpriteAtlas = EditorGUILayout.ObjectField("Sprite Texture Atlas", m_SpriteAtlas, typeof(Texture2D), false) as Texture2D;
if (EditorGUI.EndChangeCheck())
{
m_CreationFeedback = string.Empty;
}
GUILayout.Space(10);
GUI.enabled = m_JsonFile != null && m_SpriteAtlas != null && m_SpriteDataFormat != SpriteAssetImportFormats.None;
// Create Sprite Asset
if (GUILayout.Button("Create Sprite Asset"))
{
m_CreationFeedback = string.Empty;
// Clean up sprite asset object that may have been previously created.
if (m_SpriteAsset != null && !EditorUtility.IsPersistent(m_SpriteAsset))
DestroyImmediate(m_SpriteAsset);
// Read json data file
if (m_JsonFile != null)
{
switch (m_SpriteDataFormat)
{
case SpriteAssetImportFormats.TexturePackerJsonArray:
TexturePacker_JsonArray.SpriteDataObject jsonData = null;
try
{
jsonData = JsonUtility.FromJson<TexturePacker_JsonArray.SpriteDataObject>(m_JsonFile.text);
}
catch
{
m_CreationFeedback = "The Sprite Data Source file [" + m_JsonFile.name + "] appears to be invalid or incorrectly formatted.";
}
if (jsonData != null && jsonData.frames != null && jsonData.frames.Count > 0)
{
int spriteCount = jsonData.frames.Count;
// Update import results
m_CreationFeedback = "<b>Import Results</b>\n--------------------\n";
m_CreationFeedback += "<color=#C0ffff><b>" + spriteCount + "</b></color> Sprites were imported from file.";
// Create new Sprite Asset
m_SpriteAsset = CreateInstance<TMP_SpriteAsset>();
// Assign sprite sheet / atlas texture to sprite asset
m_SpriteAsset.spriteSheet = m_SpriteAtlas;
List<TMP_SpriteGlyph> spriteGlyphTable = new List<TMP_SpriteGlyph>();
List<TMP_SpriteCharacter> spriteCharacterTable = new List<TMP_SpriteCharacter>();
PopulateSpriteTables(jsonData, spriteCharacterTable, spriteGlyphTable);
m_SpriteAsset.spriteCharacterTable = spriteCharacterTable;
m_SpriteAsset.spriteGlyphTable = spriteGlyphTable;
}
break;
}
}
}
GUI.enabled = true;
// Creation Feedback
GUILayout.Space(5);
GUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.Height(60));
{
EditorGUILayout.TextArea(m_CreationFeedback, TMP_UIStyleManager.label);
}
GUILayout.EndVertical();
GUILayout.Space(5);
GUI.enabled = m_JsonFile != null && m_SpriteAtlas && m_SpriteAsset != null;
if (GUILayout.Button("Save Sprite Asset") && m_JsonFile != null)
{
string filePath = EditorUtility.SaveFilePanel("Save Sprite Asset File", new FileInfo(AssetDatabase.GetAssetPath(m_JsonFile)).DirectoryName, m_JsonFile.name, "asset");
if (filePath.Length == 0)
return;
SaveSpriteAsset(filePath);
}
GUI.enabled = true;
}
/// <summary>
///
/// </summary>
/// <param name="spriteDataObject"></param>
/// <param name="spriteCharacterTable"></param>
/// <param name="spriteGlyphTable"></param>
private static void PopulateSpriteTables(TexturePacker_JsonArray.SpriteDataObject spriteDataObject, List<TMP_SpriteCharacter> spriteCharacterTable, List<TMP_SpriteGlyph> spriteGlyphTable)
{
List<TexturePacker_JsonArray.Frame> importedSprites = spriteDataObject.frames;
float atlasHeight = spriteDataObject.meta.size.h;
for (int i = 0; i < importedSprites.Count; i++)
{
TexturePacker_JsonArray.Frame spriteData = importedSprites[i];
TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
spriteGlyph.index = (uint)i;
spriteGlyph.metrics = new GlyphMetrics((int)spriteData.frame.w, (int)spriteData.frame.h, -spriteData.frame.w * spriteData.pivot.x, spriteData.frame.h * spriteData.pivot.y, (int)spriteData.frame.w);
spriteGlyph.glyphRect = new GlyphRect((int)spriteData.frame.x, (int)(atlasHeight - spriteData.frame.h - spriteData.frame.y), (int)spriteData.frame.w, (int)spriteData.frame.h);
spriteGlyph.scale = 1.0f;
spriteGlyphTable.Add(spriteGlyph);
TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0, spriteGlyph);
spriteCharacter.name = spriteData.filename.Split('.')[0];
spriteCharacter.unicode = 0xFFFE;
spriteCharacter.scale = 1.0f;
spriteCharacterTable.Add(spriteCharacter);
}
}
/// <summary>
///
/// </summary>
/// <param name="filePath"></param>
void SaveSpriteAsset(string filePath)
{
filePath = filePath.Substring(0, filePath.Length - 6); // Trim file extension from filePath.
string dataPath = Application.dataPath;
if (filePath.IndexOf(dataPath, System.StringComparison.InvariantCultureIgnoreCase) == -1)
{
Debug.LogError("You're saving the font asset in a directory outside of this project folder. This is not supported. Please select a directory under \"" + dataPath + "\"");
return;
}
string relativeAssetPath = filePath.Substring(dataPath.Length - 6);
string dirName = Path.GetDirectoryName(relativeAssetPath);
string fileName = Path.GetFileNameWithoutExtension(relativeAssetPath);
string pathNoExt = dirName + "/" + fileName;
// Save Sprite Asset
AssetDatabase.CreateAsset(m_SpriteAsset, pathNoExt + ".asset");
// Set version number
m_SpriteAsset.version = "1.1.0";
// Compute the hash code for the sprite asset.
m_SpriteAsset.hashCode = TMP_TextUtilities.GetSimpleHashCode(m_SpriteAsset.name);
// Add new default material for sprite asset.
AddDefaultMaterial(m_SpriteAsset);
}
/// <summary>
/// Create and add new default material to sprite asset.
/// </summary>
/// <param name="spriteAsset"></param>
static void AddDefaultMaterial(TMP_SpriteAsset spriteAsset)
{
Shader shader = Shader.Find("TextMeshPro/Sprite");
Material material = new Material(shader);
material.SetTexture(ShaderUtilities.ID_MainTex, spriteAsset.spriteSheet);
spriteAsset.material = material;
material.hideFlags = HideFlags.HideInHierarchy;
AssetDatabase.AddObjectToAsset(material, spriteAsset);
}
/// <summary>
/// Limits the minimum size of the editor window.
/// </summary>
void SetEditorWindowSize()
{
EditorWindow editorWindow = this;
Vector2 currentWindowSize = editorWindow.minSize;
editorWindow.minSize = new Vector2(Mathf.Max(230, currentWindowSize.x), Mathf.Max(300, currentWindowSize.y));
}
}
}

View File

@@ -0,0 +1,401 @@
using UnityEngine;
using UnityEngine.TextCore;
using UnityEngine.U2D;
using UnityEditor;
using System.Linq;
using System.IO;
using System.Collections;
using System.Collections.Generic;
namespace TMPro.EditorUtilities
{
public static class TMP_SpriteAssetMenu
{
// Add a Context Menu to the Sprite Asset Editor Panel to Create and Add a Default Material.
[MenuItem("CONTEXT/TMP_SpriteAsset/Add Default Material", true, 2200)]
static bool AddDefaultMaterialValidate(MenuCommand command)
{
return AssetDatabase.IsOpenForEdit(command.context);
}
[MenuItem("CONTEXT/TMP_SpriteAsset/Add Default Material", false, 2200)]
static void AddDefaultMaterial(MenuCommand command)
{
TMP_SpriteAsset spriteAsset = (TMP_SpriteAsset)command.context;
// Make sure the sprite asset already contains a default material
if (spriteAsset != null && spriteAsset.material == null)
{
// Add new default material for sprite asset.
AddDefaultMaterial(spriteAsset);
}
}
// Add a Context Menu to the Sprite Asset Editor Panel to update existing sprite assets.
[MenuItem("CONTEXT/TMP_SpriteAsset/Update Sprite Asset", true, 2100)]
static bool UpdateSpriteAssetValidate(MenuCommand command)
{
return AssetDatabase.IsOpenForEdit(command.context);
}
[MenuItem("CONTEXT/TMP_SpriteAsset/Update Sprite Asset", false, 2100)]
static void UpdateSpriteAsset(MenuCommand command)
{
TMP_SpriteAsset spriteAsset = (TMP_SpriteAsset)command.context;
if (spriteAsset == null)
return;
UpdateSpriteAsset(spriteAsset);
}
internal static void UpdateSpriteAsset(TMP_SpriteAsset spriteAsset)
{
// Get a list of all the sprites contained in the texture referenced by the sprite asset.
// This only works if the texture is set to sprite mode.
string filePath = AssetDatabase.GetAssetPath(spriteAsset.spriteSheet);
if (string.IsNullOrEmpty(filePath))
return;
// Get all the sprites defined in the sprite sheet texture referenced by this sprite asset.
Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(filePath).Select(x => x as Sprite).Where(x => x != null).ToArray();
// Return if sprite sheet texture does not have any sprites defined in it.
if (sprites.Length == 0)
{
Debug.Log("Sprite Asset <color=#FFFF80>[" + spriteAsset.name + "]</color>'s atlas texture does not appear to have any sprites defined in it. Use the Unity Sprite Editor to define sprites for this texture.", spriteAsset.spriteSheet);
return;
}
List<TMP_SpriteGlyph> spriteGlyphTable = spriteAsset.spriteGlyphTable;
// Find available glpyh indexes
uint[] existingGlyphIndexes = spriteGlyphTable.Select(x => x.index).ToArray();
List<uint> availableGlyphIndexes = new List<uint>();
uint lastGlyphIndex = existingGlyphIndexes.Length > 0 ? existingGlyphIndexes.Last() : 0;
int elementIndex = 0;
for (uint i = 0; i < lastGlyphIndex; i++)
{
uint existingGlyphIndex = existingGlyphIndexes[elementIndex];
if (i == existingGlyphIndex)
elementIndex += 1;
else
availableGlyphIndexes.Add(i);
}
// Iterate over sprites contained in the updated sprite sheet to identify new and / or modified sprites.
for (int i = 0; i < sprites.Length; i++)
{
Sprite sprite = sprites[i];
// Check if current sprites is already contained in the sprite glyph table of the sprite asset.
TMP_SpriteGlyph spriteGlyph = spriteGlyphTable.FirstOrDefault(x => x.sprite == sprite);
if (spriteGlyph != null)
{
// update existing sprite glyph
if (spriteGlyph.glyphRect.x != sprite.rect.x || spriteGlyph.glyphRect.y != sprite.rect.y || spriteGlyph.glyphRect.width != sprite.rect.width || spriteGlyph.glyphRect.height != sprite.rect.height)
spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
}
else
{
TMP_SpriteCharacter spriteCharacter;
// Check if this sprite potentially exists under the same name in the sprite character table.
if (spriteAsset.spriteCharacterTable != null && spriteAsset.spriteCharacterTable.Count > 0)
{
spriteCharacter = spriteAsset.spriteCharacterTable.FirstOrDefault(x => x.name == sprite.name);
spriteGlyph = spriteCharacter != null ? spriteGlyphTable[(int)spriteCharacter.glyphIndex] : null;
if (spriteGlyph != null)
{
// Update sprite reference and data
spriteGlyph.sprite = sprite;
if (spriteGlyph.glyphRect.x != sprite.rect.x || spriteGlyph.glyphRect.y != sprite.rect.y || spriteGlyph.glyphRect.width != sprite.rect.width || spriteGlyph.glyphRect.height != sprite.rect.height)
spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
}
}
// Add new Sprite Glyph to the table
spriteGlyph = new TMP_SpriteGlyph();
// Get available glyph index
if (availableGlyphIndexes.Count > 0)
{
spriteGlyph.index = availableGlyphIndexes[0];
availableGlyphIndexes.RemoveAt(0);
}
else
spriteGlyph.index = (uint)spriteGlyphTable.Count;
spriteGlyph.metrics = new GlyphMetrics(sprite.rect.width, sprite.rect.height, -sprite.pivot.x, sprite.rect.height - sprite.pivot.y, sprite.rect.width);
spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
spriteGlyph.scale = 1.0f;
spriteGlyph.sprite = sprite;
spriteGlyphTable.Add(spriteGlyph);
spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph);
spriteCharacter.name = sprite.name;
spriteCharacter.scale = 1.0f;
spriteAsset.spriteCharacterTable.Add(spriteCharacter);
}
}
// Update Sprite Character Table to replace unicode 0x0 by 0xFFFE
for (int i = 0; i < spriteAsset.spriteCharacterTable.Count; i++)
{
TMP_SpriteCharacter spriteCharacter = spriteAsset.spriteCharacterTable[i];
if (spriteCharacter.unicode == 0)
spriteCharacter.unicode = 0xFFFE;
}
// Sort glyph table by glyph index
spriteAsset.SortGlyphTable();
spriteAsset.UpdateLookupTables();
TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, spriteAsset);
}
[MenuItem("Assets/Create/TextMeshPro/Sprite Asset", false, 110)]
public static void CreateSpriteAsset()
{
Object target = Selection.activeObject;
if (target == null || target.GetType() != typeof(Texture2D)) // && target.GetType() != typeof(SpriteAtlas)))
{
Debug.LogWarning("A texture must first be selected in order to create a TextMesh Pro Sprite Asset.");
return;
}
// Get the path to the selected asset.
string filePathWithName = AssetDatabase.GetAssetPath(target);
string fileNameWithExtension = Path.GetFileName(filePathWithName);
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePathWithName);
string filePath = filePathWithName.Replace(fileNameWithExtension, "");
// Create new Sprite Asset
TMP_SpriteAsset spriteAsset = ScriptableObject.CreateInstance<TMP_SpriteAsset>();
AssetDatabase.CreateAsset(spriteAsset, filePath + fileNameWithoutExtension + ".asset");
spriteAsset.version = "1.1.0";
// Compute the hash code for the sprite asset.
spriteAsset.hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteAsset.name);
List<TMP_SpriteGlyph> spriteGlyphTable = new List<TMP_SpriteGlyph>();
List<TMP_SpriteCharacter> spriteCharacterTable = new List<TMP_SpriteCharacter>();
if (target.GetType() == typeof(Texture2D))
{
Texture2D sourceTex = target as Texture2D;
// Assign new Sprite Sheet texture to the Sprite Asset.
spriteAsset.spriteSheet = sourceTex;
PopulateSpriteTables(sourceTex, ref spriteCharacterTable, ref spriteGlyphTable);
spriteAsset.spriteCharacterTable = spriteCharacterTable;
spriteAsset.spriteGlyphTable = spriteGlyphTable;
// Add new default material for sprite asset.
AddDefaultMaterial(spriteAsset);
}
else if (target.GetType() == typeof(SpriteAtlas))
{
//SpriteAtlas spriteAtlas = target as SpriteAtlas;
//PopulateSpriteTables(spriteAtlas, ref spriteCharacterTable, ref spriteGlyphTable);
//spriteAsset.spriteCharacterTable = spriteCharacterTable;
//spriteAsset.spriteGlyphTable = spriteGlyphTable;
//spriteAsset.spriteSheet = spriteGlyphTable[0].sprite.texture;
//// Add new default material for sprite asset.
//AddDefaultMaterial(spriteAsset);
}
// Update Lookup tables.
spriteAsset.UpdateLookupTables();
// Get the Sprites contained in the Sprite Sheet
EditorUtility.SetDirty(spriteAsset);
//spriteAsset.sprites = sprites;
// Set source texture back to Not Readable.
//texImporter.isReadable = false;
AssetDatabase.SaveAssets();
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(spriteAsset)); // Re-import font asset to get the new updated version.
//AssetDatabase.Refresh();
}
private static void PopulateSpriteTables(Texture source, ref List<TMP_SpriteCharacter> spriteCharacterTable, ref List<TMP_SpriteGlyph> spriteGlyphTable)
{
//Debug.Log("Creating new Sprite Asset.");
string filePath = AssetDatabase.GetAssetPath(source);
// Get all the Sprites sorted by Index
Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(filePath).Select(x => x as Sprite).Where(x => x != null).OrderByDescending(x => x.rect.y).ThenBy(x => x.rect.x).ToArray();
for (int i = 0; i < sprites.Length; i++)
{
Sprite sprite = sprites[i];
TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
spriteGlyph.index = (uint)i;
spriteGlyph.metrics = new GlyphMetrics(sprite.rect.width, sprite.rect.height, -sprite.pivot.x, sprite.rect.height - sprite.pivot.y, sprite.rect.width);
spriteGlyph.glyphRect = new GlyphRect(sprite.rect);
spriteGlyph.scale = 1.0f;
spriteGlyph.sprite = sprite;
spriteGlyphTable.Add(spriteGlyph);
TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph);
spriteCharacter.name = sprite.name;
spriteCharacter.scale = 1.0f;
spriteCharacterTable.Add(spriteCharacter);
}
}
private static void PopulateSpriteTables(SpriteAtlas spriteAtlas, ref List<TMP_SpriteCharacter> spriteCharacterTable, ref List<TMP_SpriteGlyph> spriteGlyphTable)
{
// Get number of sprites contained in the sprite atlas.
int spriteCount = spriteAtlas.spriteCount;
Sprite[] sprites = new Sprite[spriteCount];
// Get all the sprites
spriteAtlas.GetSprites(sprites);
for (int i = 0; i < sprites.Length; i++)
{
Sprite sprite = sprites[i];
TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
spriteGlyph.index = (uint)i;
spriteGlyph.metrics = new GlyphMetrics(sprite.textureRect.width, sprite.textureRect.height, -sprite.pivot.x, sprite.textureRect.height - sprite.pivot.y, sprite.textureRect.width);
spriteGlyph.glyphRect = new GlyphRect(sprite.textureRect);
spriteGlyph.scale = 1.0f;
spriteGlyph.sprite = sprite;
spriteGlyphTable.Add(spriteGlyph);
TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter(0xFFFE, spriteGlyph);
spriteCharacter.name = sprite.name;
spriteCharacter.scale = 1.0f;
spriteCharacterTable.Add(spriteCharacter);
}
}
/// <summary>
/// Create and add new default material to sprite asset.
/// </summary>
/// <param name="spriteAsset"></param>
private static void AddDefaultMaterial(TMP_SpriteAsset spriteAsset)
{
Shader shader = Shader.Find("TextMeshPro/Sprite");
Material material = new Material(shader);
material.SetTexture(ShaderUtilities.ID_MainTex, spriteAsset.spriteSheet);
spriteAsset.material = material;
material.hideFlags = HideFlags.HideInHierarchy;
AssetDatabase.AddObjectToAsset(material, spriteAsset);
}
// Update existing SpriteInfo
private static List<TMP_Sprite> UpdateSpriteInfo(TMP_SpriteAsset spriteAsset)
{
//Debug.Log("Updating Sprite Asset.");
string filePath = AssetDatabase.GetAssetPath(spriteAsset.spriteSheet);
// Get all the Sprites sorted Left to Right / Top to Bottom
Sprite[] sprites = AssetDatabase.LoadAllAssetsAtPath(filePath).Select(x => x as Sprite).Where(x => x != null).OrderByDescending(x => x.rect.y).ThenBy(x => x.rect.x).ToArray();
for (int i = 0; i < sprites.Length; i++)
{
Sprite sprite = sprites[i];
// Check if the sprite is already contained in the SpriteInfoList
int index = -1;
if (spriteAsset.spriteInfoList.Count > i && spriteAsset.spriteInfoList[i].sprite != null)
index = spriteAsset.spriteInfoList.FindIndex(item => item.sprite.GetInstanceID() == sprite.GetInstanceID());
// Use existing SpriteInfo if it already exists
TMP_Sprite spriteInfo = index == -1 ? new TMP_Sprite() : spriteAsset.spriteInfoList[index];
Rect spriteRect = sprite.rect;
spriteInfo.x = spriteRect.x;
spriteInfo.y = spriteRect.y;
spriteInfo.width = spriteRect.width;
spriteInfo.height = spriteRect.height;
// Get Sprite Pivot
Vector2 pivot = new Vector2(0 - (sprite.bounds.min.x) / (sprite.bounds.extents.x * 2), 0 - (sprite.bounds.min.y) / (sprite.bounds.extents.y * 2));
// The position of the pivot influences the Offset position.
spriteInfo.pivot = new Vector2(0 - pivot.x * spriteRect.width, spriteRect.height - pivot.y * spriteRect.height);
if (index == -1)
{
// Find the next available index for this Sprite
int[] ids = spriteAsset.spriteInfoList.Select(item => item.id).ToArray();
int id = 0;
for (int j = 0; j < ids.Length; j++ )
{
if (ids[0] != 0) break;
if (j > 0 && (ids[j] - ids[j - 1]) > 1)
{
id = ids[j - 1] + 1;
break;
}
id = j + 1;
}
spriteInfo.sprite = sprite;
spriteInfo.name = sprite.name;
spriteInfo.hashCode = TMP_TextUtilities.GetSimpleHashCode(spriteInfo.name);
spriteInfo.id = id;
spriteInfo.xAdvance = spriteRect.width;
spriteInfo.scale = 1.0f;
spriteInfo.xOffset = spriteInfo.pivot.x;
spriteInfo.yOffset = spriteInfo.pivot.y;
spriteAsset.spriteInfoList.Add(spriteInfo);
// Sort the Sprites by ID
spriteAsset.spriteInfoList = spriteAsset.spriteInfoList.OrderBy(s => s.id).ToList();
}
else
{
spriteAsset.spriteInfoList[index] = spriteInfo;
}
}
return spriteAsset.spriteInfoList;
}
}
}

View File

@@ -0,0 +1,227 @@
using UnityEngine;
using UnityEngine.TextCore;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
[CustomPropertyDrawer(typeof(TMP_SpriteCharacter))]
public class TMP_SpriteCharacterPropertyDrawer : PropertyDrawer
{
int m_GlyphSelectedForEditing = -1;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty prop_SpriteName = property.FindPropertyRelative("m_Name");
SerializedProperty prop_SpriteNameHashCode = property.FindPropertyRelative("m_HashCode");
SerializedProperty prop_SpriteUnicode = property.FindPropertyRelative("m_Unicode");
SerializedProperty prop_SpriteGlyphIndex = property.FindPropertyRelative("m_GlyphIndex");
SerializedProperty prop_SpriteScale = 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 + 60, position.y, position.width, 49);
// Display non-editable fields
if (GUI.enabled == false)
{
// Sprite Character Index
int spriteCharacterIndex;
int.TryParse(property.displayName.Split(' ')[1], out spriteCharacterIndex);
EditorGUI.LabelField(new Rect(rect.x, rect.y, 75f, 18), new GUIContent("Index: <color=#FFFF80>" + spriteCharacterIndex + "</color>"), style);
EditorGUI.LabelField(new Rect(rect.x + 75f, rect.y, 120f, 18), new GUIContent("Unicode: <color=#FFFF80>0x" + prop_SpriteUnicode.intValue.ToString("X") + "</color>"), style);
EditorGUI.LabelField(new Rect(rect.x + 195f, rect.y, rect.width - 255, 18), new GUIContent("Name: <color=#FFFF80>" + prop_SpriteName.stringValue + "</color>"), style);
EditorGUI.LabelField(new Rect(rect.x, rect.y + 18, 120, 18), new GUIContent("Glyph ID: <color=#FFFF80>" + prop_SpriteGlyphIndex.intValue + "</color>"), style);
// Draw Sprite Glyph (if exists)
DrawSpriteGlyph(position, property);
EditorGUI.LabelField(new Rect(rect.x, rect.y + 36, 80, 18), new GUIContent("Scale: <color=#FFFF80>" + prop_SpriteScale.floatValue + "</color>"), style);
}
else // Display editable fields
{
// Get a reference to the underlying Sprite Asset
TMP_SpriteAsset spriteAsset = property.serializedObject.targetObject as TMP_SpriteAsset;
// Sprite Character Index
int spriteCharacterIndex;
int.TryParse(property.displayName.Split(' ')[1], out spriteCharacterIndex);
EditorGUI.LabelField(new Rect(rect.x, rect.y, 75f, 18), new GUIContent("Index: <color=#FFFF80>" + spriteCharacterIndex + "</color>"), style);
EditorGUIUtility.labelWidth = 55f;
GUI.SetNextControlName("Unicode Input");
EditorGUI.BeginChangeCheck();
string unicode = EditorGUI.DelayedTextField(new Rect(rect.x + 75f, rect.y, 120, 18), "Unicode:", prop_SpriteUnicode.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_SpriteUnicode.intValue = TMP_TextUtilities.StringHexToInt(unicode);
spriteAsset.m_IsSpriteAssetLookupTablesDirty = true;
}
EditorGUIUtility.labelWidth = 41f;
EditorGUI.BeginChangeCheck();
EditorGUI.DelayedTextField(new Rect(rect.x + 195f, rect.y, rect.width - 255, 18), prop_SpriteName, new GUIContent("Name:"));
if (EditorGUI.EndChangeCheck())
{
// Recompute hashCode for new name
prop_SpriteNameHashCode.intValue = TMP_TextUtilities.GetSimpleHashCode(prop_SpriteName.stringValue);
spriteAsset.m_IsSpriteAssetLookupTablesDirty = true;
}
EditorGUIUtility.labelWidth = 59f;
EditorGUI.BeginChangeCheck();
EditorGUI.DelayedIntField(new Rect(rect.x, rect.y + 18, 100, 18), prop_SpriteGlyphIndex, new GUIContent("Glyph ID:"));
if (EditorGUI.EndChangeCheck())
{
spriteAsset.m_IsSpriteAssetLookupTablesDirty = true;
}
// Draw Sprite Glyph (if exists)
DrawSpriteGlyph(position, property);
int glyphIndex = prop_SpriteGlyphIndex.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)
{
if (spriteAsset != null)
{
// Lookup glyph and draw glyph (if available)
int elementIndex = spriteAsset.spriteGlyphTable.FindIndex(item => item.index == glyphIndex);
if (elementIndex != -1)
{
// Get a reference to the Sprite Glyph Table
SerializedProperty prop_SpriteGlyphTable = property.serializedObject.FindProperty("m_SpriteGlyphTable");
SerializedProperty prop_SpriteGlyph = prop_SpriteGlyphTable.GetArrayElementAtIndex(elementIndex);
SerializedProperty prop_GlyphMetrics = prop_SpriteGlyph.FindPropertyRelative("m_Metrics");
SerializedProperty prop_GlyphRect = prop_SpriteGlyph.FindPropertyRelative("m_GlyphRect");
Rect newRect = EditorGUILayout.GetControlRect(false, 115);
EditorGUI.DrawRect(new Rect(newRect.x + 62, newRect.y - 20, newRect.width - 62, newRect.height - 5), new Color(0.1f, 0.1f, 0.1f, 0.45f));
EditorGUI.DrawRect(new Rect(newRect.x + 63, newRect.y - 19, newRect.width - 64, newRect.height - 7), new Color(0.3f, 0.3f, 0.3f, 0.8f));
// Display GlyphRect
newRect.x += 65;
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_SpriteScale, new GUIContent("Scale:"));
}
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 58;
}
void DrawSpriteGlyph(Rect position, SerializedProperty property)
{
// Get a reference to the sprite glyph table
TMP_SpriteAsset spriteAsset = property.serializedObject.targetObject as TMP_SpriteAsset;
if (spriteAsset == null)
return;
int glyphIndex = property.FindPropertyRelative("m_GlyphIndex").intValue;
// Lookup glyph and draw glyph (if available)
int elementIndex = spriteAsset.spriteGlyphTable.FindIndex(item => item.index == glyphIndex);
if (elementIndex != -1)
{
// Get a reference to the Sprite Glyph Table
SerializedProperty prop_SpriteGlyphTable = property.serializedObject.FindProperty("m_SpriteGlyphTable");
SerializedProperty prop_SpriteGlyph = prop_SpriteGlyphTable.GetArrayElementAtIndex(elementIndex);
SerializedProperty prop_GlyphRect = prop_SpriteGlyph.FindPropertyRelative("m_GlyphRect");
// Get a reference to the sprite texture
Texture tex = spriteAsset.spriteSheet;
// Return if we don't have a texture assigned to the sprite asset.
if (tex == null)
{
Debug.LogWarning("Please assign a valid Sprite Atlas texture to the [" + spriteAsset.name + "] Sprite Asset.", spriteAsset);
return;
}
Vector2 spriteTexPosition = new Vector2(position.x, position.y);
Vector2 spriteSize = new Vector2(48, 48);
Vector2 alignmentOffset = new Vector2((58 - spriteSize.x) / 2, (58 - spriteSize.y) / 2);
float x = prop_GlyphRect.FindPropertyRelative("m_X").intValue;
float y = prop_GlyphRect.FindPropertyRelative("m_Y").intValue;
float spriteWidth = prop_GlyphRect.FindPropertyRelative("m_Width").intValue;
float spriteHeight = prop_GlyphRect.FindPropertyRelative("m_Height").intValue;
if (spriteWidth >= spriteHeight)
{
spriteSize.y = spriteHeight * spriteSize.x / spriteWidth;
spriteTexPosition.y += (spriteSize.x - spriteSize.y) / 2;
}
else
{
spriteSize.x = spriteWidth * spriteSize.y / spriteHeight;
spriteTexPosition.x += (spriteSize.y - spriteSize.x) / 2;
}
// Compute the normalized texture coordinates
Rect texCoords = new Rect(x / tex.width, y / tex.height, spriteWidth / tex.width, spriteHeight / tex.height);
GUI.DrawTextureWithTexCoords(new Rect(spriteTexPosition.x + alignmentOffset.x, spriteTexPosition.y + alignmentOffset.y, spriteSize.x, spriteSize.y), tex, texCoords, true);
}
}
}
}

View File

@@ -0,0 +1,94 @@
using UnityEngine;
using UnityEngine.TextCore;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
[CustomPropertyDrawer(typeof(TMP_SpriteGlyph))]
public class TMP_SpriteGlyphPropertyDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty prop_GlyphIndex = property.FindPropertyRelative("m_Index");
SerializedProperty prop_GlyphMetrics = property.FindPropertyRelative("m_Metrics");
SerializedProperty prop_GlyphRect = property.FindPropertyRelative("m_GlyphRect");
SerializedProperty prop_Scale = property.FindPropertyRelative("m_Scale");
SerializedProperty prop_AtlasIndex = property.FindPropertyRelative("m_AtlasIndex");
GUIStyle style = new GUIStyle(EditorStyles.label);
style.richText = true;
Rect rect = new Rect(position.x + 70, position.y, position.width, 49);
// Draw GlyphRect
EditorGUI.PropertyField(rect, prop_GlyphRect);
// Draw GlyphMetrics
rect.y += 45;
EditorGUI.PropertyField(rect, prop_GlyphMetrics);
EditorGUIUtility.labelWidth = 40f;
EditorGUI.PropertyField(new Rect(rect.x, rect.y + 65, 75, 18), prop_Scale, new GUIContent("Scale:"));
EditorGUIUtility.labelWidth = 74f;
EditorGUI.PropertyField(new Rect(rect.x + 85, rect.y + 65, 95, 18), prop_AtlasIndex, new GUIContent("Atlas Index:"));
DrawGlyph(position, property);
int spriteCharacterIndex;
int.TryParse(property.displayName.Split(' ')[1], out spriteCharacterIndex);
EditorGUI.LabelField(new Rect(position.x, position.y + 5, 64f, 18f), new GUIContent("#" + spriteCharacterIndex), style);
float labelWidthID = GUI.skin.label.CalcSize(new GUIContent("ID: " + prop_GlyphIndex.intValue)).x;
EditorGUI.LabelField(new Rect(position.x + (64 - labelWidthID) / 2, position.y + 110, 64f, 18f), new GUIContent("ID: <color=#FFFF80>" + prop_GlyphIndex.intValue + "</color>"), style);
}
void DrawGlyph(Rect position, SerializedProperty property)
{
// Get a reference to the sprite texture
Texture tex = (property.serializedObject.targetObject as TMP_SpriteAsset).spriteSheet;
// Return if we don't have a texture assigned to the sprite asset.
if (tex == null)
{
Debug.LogWarning("Please assign a valid Sprite Atlas texture to the [" + property.serializedObject.targetObject.name + "] Sprite Asset.", property.serializedObject.targetObject);
return;
}
Vector2 spriteTexPosition = new Vector2(position.x, position.y);
Vector2 spriteSize = new Vector2(65, 65);
SerializedProperty prop_GlyphRect = property.FindPropertyRelative("m_GlyphRect");
int spriteImageX = prop_GlyphRect.FindPropertyRelative("m_X").intValue;
int spriteImageY = prop_GlyphRect.FindPropertyRelative("m_Y").intValue;
int spriteImageWidth = prop_GlyphRect.FindPropertyRelative("m_Width").intValue;
int spriteImageHeight = prop_GlyphRect.FindPropertyRelative("m_Height").intValue;
if (spriteImageWidth >= spriteImageHeight)
{
spriteSize.y = spriteImageHeight * spriteSize.x / spriteImageWidth;
spriteTexPosition.y += (spriteSize.x - spriteSize.y) / 2;
}
else
{
spriteSize.x = spriteImageWidth * spriteSize.y / spriteImageHeight;
spriteTexPosition.x += (spriteSize.y - spriteSize.x) / 2;
}
// Compute the normalized texture coordinates
Rect texCoords = new Rect((float)spriteImageX / tex.width, (float)spriteImageY / tex.height, (float)spriteImageWidth / tex.width, (float)spriteImageHeight / tex.height);
GUI.DrawTextureWithTexCoords(new Rect(spriteTexPosition.x + 5, spriteTexPosition.y + 32f, spriteSize.x, spriteSize.y), tex, texCoords, true);
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return 130f;
}
}
}

View File

@@ -0,0 +1,56 @@
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections;
namespace TMPro.EditorUtilities
{
public static class TMP_StyleAssetMenu
{
[MenuItem("Assets/Create/TextMeshPro/Style Sheet", false, 120)]
public static void CreateTextMeshProObjectPerform()
{
string filePath;
if (Selection.assetGUIDs.Length == 0)
{
// No asset selected.
filePath = "Assets";
}
else
{
// Get the path of the selected folder or asset.
filePath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
// Get the file extension of the selected asset as it might need to be removed.
string fileExtension = Path.GetExtension(filePath);
if (fileExtension != "")
{
filePath = Path.GetDirectoryName(filePath);
}
}
string filePathWithName = AssetDatabase.GenerateUniqueAssetPath(filePath + "/Text StyleSheet.asset");
//// Create new Style Sheet Asset.
TMP_StyleSheet styleSheet = ScriptableObject.CreateInstance<TMP_StyleSheet>();
// Create Normal default style
TMP_Style style = new TMP_Style("Normal", string.Empty, string.Empty);
styleSheet.styles.Add(style);
AssetDatabase.CreateAsset(styleSheet, filePathWithName);
EditorUtility.SetDirty(styleSheet);
AssetDatabase.SaveAssets();
EditorUtility.FocusProjectWindow();
EditorGUIUtility.PingObject(styleSheet);
}
}
}

View File

@@ -0,0 +1,321 @@
using System;
using UnityEngine;
using UnityEditor;
namespace TMPro.EditorUtilities
{
[CustomPropertyDrawer(typeof(TMP_Style))]
public class StyleDrawer : PropertyDrawer
{
public static readonly float height = 95f;
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return height;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
SerializedProperty nameProperty = property.FindPropertyRelative("m_Name");
SerializedProperty hashCodeProperty = property.FindPropertyRelative("m_HashCode");
SerializedProperty openingDefinitionProperty = property.FindPropertyRelative("m_OpeningDefinition");
SerializedProperty closingDefinitionProperty = property.FindPropertyRelative("m_ClosingDefinition");
SerializedProperty openingDefinitionArray = property.FindPropertyRelative("m_OpeningTagArray");
SerializedProperty closingDefinitionArray = property.FindPropertyRelative("m_ClosingTagArray");
EditorGUIUtility.labelWidth = 86;
position.height = EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
float labelHeight = position.height + 2f;
EditorGUI.BeginChangeCheck();
Rect rect0 = new Rect(position.x, position.y, (position.width) / 2 + 5, position.height);
EditorGUI.PropertyField(rect0, nameProperty);
if (EditorGUI.EndChangeCheck())
{
// Recompute HashCode if name has changed.
hashCodeProperty.intValue = TMP_TextUtilities.GetSimpleHashCode(nameProperty.stringValue);
property.serializedObject.ApplyModifiedProperties();
// Dictionary needs to be updated since HashCode has changed.
TMP_StyleSheet styleSheet = property.serializedObject.targetObject as TMP_StyleSheet;
styleSheet.RefreshStyles();
}
// HashCode
Rect rect1 = new Rect(rect0.x + rect0.width + 5, position.y, 65, position.height);
GUI.Label(rect1, "HashCode");
GUI.enabled = false;
rect1.x += 65;
rect1.width = position.width / 2 - 75;
EditorGUI.PropertyField(rect1, hashCodeProperty, GUIContent.none);
GUI.enabled = true;
// Text Tags
EditorGUI.BeginChangeCheck();
// Opening Tags
position.y += labelHeight;
GUI.Label(position, "Opening Tags");
Rect textRect1 = new Rect(110, position.y, position.width - 86, 35);
openingDefinitionProperty.stringValue = EditorGUI.TextArea(textRect1, openingDefinitionProperty.stringValue);
if (EditorGUI.EndChangeCheck())
{
// If any properties have changed, we need to update the Opening and Closing Arrays.
int size = openingDefinitionProperty.stringValue.Length;
// Adjust array size to match new string length.
if (openingDefinitionArray.arraySize != size) openingDefinitionArray.arraySize = size;
for (int i = 0; i < size; i++)
{
SerializedProperty element = openingDefinitionArray.GetArrayElementAtIndex(i);
element.intValue = openingDefinitionProperty.stringValue[i];
}
}
EditorGUI.BeginChangeCheck();
// Closing Tags
position.y += 38;
GUI.Label(position, "Closing Tags");
Rect textRect2 = new Rect(110, position.y, position.width - 86, 35);
closingDefinitionProperty.stringValue = EditorGUI.TextArea(textRect2, closingDefinitionProperty.stringValue);
if (EditorGUI.EndChangeCheck())
{
// If any properties have changed, we need to update the Opening and Closing Arrays.
int size = closingDefinitionProperty.stringValue.Length;
// Adjust array size to match new string length.
if (closingDefinitionArray.arraySize != size) closingDefinitionArray.arraySize = size;
for (int i = 0; i < size; i++)
{
SerializedProperty element = closingDefinitionArray.GetArrayElementAtIndex(i);
element.intValue = closingDefinitionProperty.stringValue[i];
}
}
}
}
[CustomEditor(typeof(TMP_StyleSheet)), CanEditMultipleObjects]
public class TMP_StyleEditor : Editor
{
TMP_StyleSheet m_StyleSheet;
SerializedProperty m_StyleListProp;
int m_SelectedElement = -1;
int m_Page;
bool m_IsStyleSheetDirty;
void OnEnable()
{
m_StyleSheet = target as TMP_StyleSheet;
m_StyleListProp = serializedObject.FindProperty("m_StyleList");
}
public override void OnInspectorGUI()
{
Event currentEvent = Event.current;
serializedObject.Update();
m_IsStyleSheetDirty = false;
int elementCount = m_StyleListProp.arraySize;
int itemsPerPage = (Screen.height - 100) / 110;
if (elementCount > 0)
{
// Display each Style entry using the StyleDrawer PropertyDrawer.
for (int i = itemsPerPage * m_Page; i < elementCount && i < itemsPerPage * (m_Page + 1); i++)
{
// Define the start of the selection region of the element.
Rect elementStartRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
SerializedProperty styleProperty = m_StyleListProp.GetArrayElementAtIndex(i);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(styleProperty);
EditorGUILayout.EndVertical();
if (EditorGUI.EndChangeCheck())
{
//
}
// Define the end of the selection region of the element.
Rect elementEndRegion = GUILayoutUtility.GetRect(0f, 0f, GUILayout.ExpandWidth(true));
// Check for Item selection
Rect selectionArea = new Rect(elementStartRegion.x, elementStartRegion.y, elementEndRegion.width, elementEndRegion.y - elementStartRegion.y);
if (DoSelectionCheck(selectionArea))
{
if (m_SelectedElement == i)
{
m_SelectedElement = -1;
}
else
{
m_SelectedElement = i;
GUIUtility.keyboardControl = 0;
}
}
// Handle Selection Highlighting
if (m_SelectedElement == i)
TMP_EditorUtility.DrawBox(selectionArea, 2f, new Color32(40, 192, 255, 255));
}
}
// STYLE LIST MANAGEMENT
Rect rect = EditorGUILayout.GetControlRect(false, 20);
float totalWidth = rect.width;
rect.width = totalWidth * 0.175f;
// Move Style up.
bool guiEnabled = GUI.enabled;
if (m_SelectedElement == -1 || m_SelectedElement == 0) { GUI.enabled = false; }
if (GUI.Button(rect, "Up"))
{
SwapStyleElements(m_SelectedElement, m_SelectedElement - 1);
}
GUI.enabled = guiEnabled;
// Move Style down.
rect.x += rect.width;
if (m_SelectedElement == elementCount - 1) { GUI.enabled = false; }
if (GUI.Button(rect, "Down"))
{
SwapStyleElements(m_SelectedElement, m_SelectedElement + 1);
}
GUI.enabled = guiEnabled;
// Add Style
rect.x += rect.width + totalWidth * 0.3f;
if (GUI.Button(rect, "+"))
{
int index = m_SelectedElement == -1 ? elementCount : m_SelectedElement;
if (index > elementCount)
index = elementCount;
// Copy selected element
m_StyleListProp.InsertArrayElementAtIndex(index);
// Select newly inserted element
m_SelectedElement = index + 1;
serializedObject.ApplyModifiedProperties();
m_StyleSheet.RefreshStyles();
}
// Delete style
rect.x += rect.width;
if (m_SelectedElement == -1 || m_SelectedElement >= elementCount) GUI.enabled = false;
if (GUI.Button(rect, "-"))
{
int index = m_SelectedElement == -1 ? 0 : m_SelectedElement;
m_StyleListProp.DeleteArrayElementAtIndex(index);
m_SelectedElement = -1;
serializedObject.ApplyModifiedProperties();
m_StyleSheet.RefreshStyles();
return;
}
// Return if we can't display any items.
if (itemsPerPage == 0) return;
// DISPLAY PAGE CONTROLS
int shiftMultiplier = currentEvent.shift ? 10 : 1; // Page + Shift goes 10 page forward
Rect pagePos = EditorGUILayout.GetControlRect(false, 20);
pagePos.width = totalWidth * 0.35f;
// Previous Page
if (m_Page > 0) GUI.enabled = true;
else GUI.enabled = false;
if (GUI.Button(pagePos, "Previous"))
m_Page -= 1 * shiftMultiplier;
// PAGE COUNTER
GUI.enabled = true;
pagePos.x += pagePos.width;
pagePos.width = totalWidth * 0.30f;
int totalPages = (int)(elementCount / (float)itemsPerPage + 0.999f);
GUI.Label(pagePos, "Page " + (m_Page + 1) + " / " + totalPages, TMP_UIStyleManager.centeredLabel);
// Next Page
pagePos.x += pagePos.width;
pagePos.width = totalWidth * 0.35f;
if (itemsPerPage * (m_Page + 1) < elementCount) GUI.enabled = true;
else GUI.enabled = false;
if (GUI.Button(pagePos, "Next"))
m_Page += 1 * shiftMultiplier;
// Clamp page range
m_Page = Mathf.Clamp(m_Page, 0, elementCount / itemsPerPage);
if (serializedObject.ApplyModifiedProperties())
{
TMPro_EventManager.ON_TEXT_STYLE_PROPERTY_CHANGED(true);
if (m_IsStyleSheetDirty)
{
m_IsStyleSheetDirty = false;
m_StyleSheet.RefreshStyles();
}
}
// Clear selection if mouse event was not consumed.
GUI.enabled = true;
if (currentEvent.type == EventType.MouseDown && currentEvent.button == 0)
m_SelectedElement = -1;
}
// Check if any of the Style elements were clicked on.
static bool DoSelectionCheck(Rect selectionArea)
{
Event currentEvent = Event.current;
switch (currentEvent.type)
{
case EventType.MouseDown:
if (selectionArea.Contains(currentEvent.mousePosition) && currentEvent.button == 0)
{
currentEvent.Use();
return true;
}
break;
}
return false;
}
void SwapStyleElements(int selectedIndex, int newIndex)
{
m_StyleListProp.MoveArrayElement(selectedIndex, newIndex);
m_SelectedElement = newIndex;
m_IsStyleSheetDirty = true;
}
}
}

View File

@@ -0,0 +1,104 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
[CustomEditor(typeof(TMP_SubMeshUI)), CanEditMultipleObjects]
public class TMP_SubMeshUI_Editor : Editor
{
private struct m_foldout
{ // Track Inspector foldout panel states, globally.
//public static bool textInput = true;
public static bool fontSettings = true;
//public static bool extraSettings = false;
//public static bool shadowSetting = false;
//public static bool materialEditor = true;
}
private SerializedProperty fontAsset_prop;
private SerializedProperty spriteAsset_prop;
//private TMP_SubMeshUI m_SubMeshComponent;
//private CanvasRenderer m_canvasRenderer;
private Editor m_materialEditor;
private Material m_targetMaterial;
public void OnEnable()
{
fontAsset_prop = serializedObject.FindProperty("m_fontAsset");
spriteAsset_prop = serializedObject.FindProperty("m_spriteAsset");
//m_SubMeshComponent = target as TMP_SubMeshUI;
//m_rectTransform = m_SubMeshComponent.rectTransform;
//m_canvasRenderer = m_SubMeshComponent.canvasRenderer;
// Create new Material Editor if one does not exists
/*
if (m_canvasRenderer != null && m_canvasRenderer.GetMaterial() != null)
{
m_materialEditor = Editor.CreateEditor(m_canvasRenderer.GetMaterial());
m_targetMaterial = m_canvasRenderer.GetMaterial();
}
*/
}
public void OnDisable()
{
// Destroy material editor if one exists
/*
if (m_materialEditor != null)
{
//Debug.Log("Destroying Inline Material Editor.");
DestroyImmediate(m_materialEditor);
}
*/
}
public override void OnInspectorGUI()
{
GUI.enabled = false;
EditorGUILayout.PropertyField(fontAsset_prop);
EditorGUILayout.PropertyField(spriteAsset_prop);
GUI.enabled = true;
EditorGUILayout.Space();
// If a Custom Material Editor exists, we use it.
/*
if (m_canvasRenderer != null && m_canvasRenderer.GetMaterial() != null)
{
Material mat = m_canvasRenderer.GetMaterial();
//Debug.Log(mat + " " + m_targetMaterial);
if (mat != m_targetMaterial)
{
// Destroy previous Material Instance
//Debug.Log("New Material has been assigned.");
m_targetMaterial = mat;
DestroyImmediate(m_materialEditor);
}
if (m_materialEditor == null)
{
m_materialEditor = Editor.CreateEditor(mat);
}
m_materialEditor.DrawHeader();
m_materialEditor.OnInspectorGUI();
}
*/
}
}
}

View File

@@ -0,0 +1,71 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
[CustomEditor(typeof(TMP_SubMesh)), CanEditMultipleObjects]
public class TMP_SubMesh_Editor : Editor
{
private struct m_foldout
{ // Track Inspector foldout panel states, globally.
//public static bool textInput = true;
public static bool fontSettings = true;
//public static bool extraSettings = false;
//public static bool shadowSetting = false;
//public static bool materialEditor = true;
}
private SerializedProperty fontAsset_prop;
private SerializedProperty spriteAsset_prop;
private TMP_SubMesh m_SubMeshComponent;
private Renderer m_Renderer;
private string[] m_SortingLayerNames;
public void OnEnable()
{
fontAsset_prop = serializedObject.FindProperty("m_fontAsset");
spriteAsset_prop = serializedObject.FindProperty("m_spriteAsset");
m_SubMeshComponent = target as TMP_SubMesh;
m_Renderer = m_SubMeshComponent.renderer;
m_SortingLayerNames = SortingLayerHelper.sortingLayerNames;
}
public override void OnInspectorGUI()
{
EditorGUI.indentLevel = 0;
GUI.enabled = false;
EditorGUILayout.PropertyField(fontAsset_prop);
EditorGUILayout.PropertyField(spriteAsset_prop);
GUI.enabled = true;
EditorGUI.BeginChangeCheck();
// Look up the layer name using the current layer ID
string oldName = SortingLayer.IDToName(m_Renderer.sortingLayerID);
// Use the name to look up our array index into the names list
int oldLayerIndex = System.Array.IndexOf(m_SortingLayerNames, oldName);
// Show the pop-up for the names
int newLayerIndex = EditorGUILayout.Popup("Sorting Layer", oldLayerIndex, m_SortingLayerNames);
// If the index changes, look up the ID for the new index to store as the new ID
if (newLayerIndex != oldLayerIndex)
m_Renderer.sortingLayerID = SortingLayer.NameToID(m_SortingLayerNames[newLayerIndex]);
// Expose the manual sorting order
int newSortingLayerOrder = EditorGUILayout.IntField("Order in Layer", m_Renderer.sortingOrder);
if (newSortingLayerOrder != m_Renderer.sortingOrder)
m_Renderer.sortingOrder = newSortingLayerOrder;
}
}
}

View File

@@ -0,0 +1,273 @@
using UnityEngine;
using UnityEditor;
namespace TMPro.EditorUtilities
{
[CustomPropertyDrawer(typeof(TextAlignmentOptions))]
public class TMP_TextAlignmentDrawer : PropertyDrawer
{
const int k_AlignmentButtonWidth = 24;
const int k_AlignmentButtonHeight = 20;
const int k_WideViewWidth = 504;
const int k_ControlsSpacing = 6;
const int k_GroupWidth = k_AlignmentButtonWidth * 6;
static readonly int k_TextAlignmentHash = "DoTextAligmentControl".GetHashCode();
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.currentViewWidth > k_WideViewWidth ? k_AlignmentButtonHeight : k_AlignmentButtonHeight * 2 + 3;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var id = GUIUtility.GetControlID(k_TextAlignmentHash, FocusType.Keyboard, position);
EditorGUI.BeginProperty(position, label, property);
{
var controlArea = EditorGUI.PrefixLabel(position, id, label);
var horizontalAligment = new Rect(controlArea.x, controlArea.y, k_GroupWidth, k_AlignmentButtonHeight);
var verticalAligment = new Rect(!(EditorGUIUtility.currentViewWidth > k_WideViewWidth) ? controlArea.x : horizontalAligment.xMax + k_ControlsSpacing, !(EditorGUIUtility.currentViewWidth > k_WideViewWidth) ? controlArea.y + k_AlignmentButtonHeight + 3 : controlArea.y, k_GroupWidth, k_AlignmentButtonHeight);
EditorGUI.BeginChangeCheck();
var selectedHorizontal = DoHorizontalAligmentControl(horizontalAligment, property);
var selectedVertical = DoVerticalAligmentControl(verticalAligment, property);
if (EditorGUI.EndChangeCheck())
{
var value = (0x1 << selectedHorizontal) | (0x100 << selectedVertical);
property.intValue = value;
}
}
EditorGUI.EndProperty();
}
static int DoHorizontalAligmentControl(Rect position, SerializedProperty alignment)
{
var selected = TMP_EditorUtility.GetHorizontalAlignmentGridValue(alignment.intValue);
var values = new bool[6];
values[selected] = true;
if (alignment.hasMultipleDifferentValues)
{
foreach (var obj in alignment.serializedObject.targetObjects)
{
var text = obj as TMP_Text;
if (text != null)
{
values[TMP_EditorUtility.GetHorizontalAlignmentGridValue((int)text.alignment)] = true;
}
}
}
position.width = k_AlignmentButtonWidth;
for (var i = 0; i < values.Length; i++)
{
var oldValue = values[i];
var newValue = TMP_EditorUtility.EditorToggle(position, oldValue, TMP_UIStyleManager.alignContentA[i], i == 0 ? TMP_UIStyleManager.alignmentButtonLeft : (i == 5 ? TMP_UIStyleManager.alignmentButtonRight : TMP_UIStyleManager.alignmentButtonMid));
if (newValue != oldValue)
{
selected = i;
}
position.x += position.width;
}
return selected;
}
static int DoVerticalAligmentControl(Rect position, SerializedProperty alignment)
{
var selected = TMP_EditorUtility.GetVerticalAlignmentGridValue(alignment.intValue);
var values = new bool[6];
values[selected] = true;
if (alignment.hasMultipleDifferentValues)
{
foreach (var obj in alignment.serializedObject.targetObjects)
{
var text = obj as TMP_Text;
if (text != null)
{
values[TMP_EditorUtility.GetVerticalAlignmentGridValue((int)text.alignment)] = true;
}
}
}
position.width = k_AlignmentButtonWidth;
for (var i = 0; i < values.Length; i++)
{
var oldValue = values[i];
var newValue = TMP_EditorUtility.EditorToggle(position, oldValue, TMP_UIStyleManager.alignContentB[i], i == 0 ? TMP_UIStyleManager.alignmentButtonLeft : (i == 5 ? TMP_UIStyleManager.alignmentButtonRight : TMP_UIStyleManager.alignmentButtonMid));
if (newValue != oldValue)
{
selected = i;
}
position.x += position.width;
}
return selected;
}
}
[CustomPropertyDrawer(typeof(HorizontalAlignmentOptions))]
public class TMP_HorizontalAlignmentDrawer : PropertyDrawer
{
const int k_AlignmentButtonWidth = 24;
const int k_AlignmentButtonHeight = 20;
const int k_WideViewWidth = 504;
const int k_ControlsSpacing = 6;
const int k_GroupWidth = k_AlignmentButtonWidth * 6;
static readonly int k_TextAlignmentHash = "DoTextAligmentControl".GetHashCode();
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.currentViewWidth > k_WideViewWidth ? k_AlignmentButtonHeight : k_AlignmentButtonHeight * 2 + 3;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var id = GUIUtility.GetControlID(k_TextAlignmentHash, FocusType.Keyboard, position);
EditorGUI.BeginProperty(position, label, property);
{
var controlArea = EditorGUI.PrefixLabel(position, id, label);
var horizontalAligment = new Rect(controlArea.x, controlArea.y, k_GroupWidth, k_AlignmentButtonHeight);
//var verticalAligment = new Rect(!(EditorGUIUtility.currentViewWidth > k_WideViewWidth) ? controlArea.x : horizontalAligment.xMax + k_ControlsSpacing, !(EditorGUIUtility.currentViewWidth > k_WideViewWidth) ? controlArea.y + k_AlignmentButtonHeight + 3 : controlArea.y, k_GroupWidth, k_AlignmentButtonHeight);
EditorGUI.BeginChangeCheck();
var selectedHorizontal = DoHorizontalAligmentControl(horizontalAligment, property);
if (EditorGUI.EndChangeCheck())
{
var value = 0x1 << selectedHorizontal;
property.intValue = value;
}
}
EditorGUI.EndProperty();
}
static int DoHorizontalAligmentControl(Rect position, SerializedProperty alignment)
{
var selected = TMP_EditorUtility.GetHorizontalAlignmentGridValue(alignment.intValue);
var values = new bool[6];
values[selected] = true;
if (alignment.hasMultipleDifferentValues)
{
foreach (var obj in alignment.serializedObject.targetObjects)
{
var text = obj as TMP_Text;
if (text != null)
{
values[TMP_EditorUtility.GetHorizontalAlignmentGridValue((int)text.horizontalAlignment)] = true;
}
}
}
position.width = k_AlignmentButtonWidth;
for (var i = 0; i < values.Length; i++)
{
var oldValue = values[i];
var newValue = TMP_EditorUtility.EditorToggle(position, oldValue, TMP_UIStyleManager.alignContentA[i], i == 0 ? TMP_UIStyleManager.alignmentButtonLeft : (i == 5 ? TMP_UIStyleManager.alignmentButtonRight : TMP_UIStyleManager.alignmentButtonMid));
if (newValue != oldValue)
{
selected = i;
}
position.x += position.width;
}
return selected;
}
}
[CustomPropertyDrawer(typeof(VerticalAlignmentOptions))]
public class TMP_VerticalAlignmentDrawer : PropertyDrawer
{
const int k_AlignmentButtonWidth = 24;
const int k_AlignmentButtonHeight = 20;
const int k_WideViewWidth = 504;
const int k_ControlsSpacing = 6;
const int k_GroupWidth = k_AlignmentButtonWidth * 6;
static readonly int k_TextAlignmentHash = "DoTextAligmentControl".GetHashCode();
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUIUtility.currentViewWidth > k_WideViewWidth ? k_AlignmentButtonHeight : k_AlignmentButtonHeight * 2 + 3;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
var id = GUIUtility.GetControlID(k_TextAlignmentHash, FocusType.Keyboard, position);
EditorGUI.BeginProperty(position, label, property);
{
var controlArea = EditorGUI.PrefixLabel(position, id, label);
var horizontalAligment = new Rect(controlArea.x, controlArea.y, k_GroupWidth, k_AlignmentButtonHeight);
var verticalAligment = new Rect(!(EditorGUIUtility.currentViewWidth > k_WideViewWidth) ? controlArea.x : horizontalAligment.xMax + k_ControlsSpacing, !(EditorGUIUtility.currentViewWidth > k_WideViewWidth) ? controlArea.y + k_AlignmentButtonHeight + 3 : controlArea.y, k_GroupWidth, k_AlignmentButtonHeight);
EditorGUI.BeginChangeCheck();
//var selectedHorizontal = DoHorizontalAligmentControl(horizontalAligment, property);
var selectedVertical = DoVerticalAligmentControl(verticalAligment, property);
if (EditorGUI.EndChangeCheck())
{
var value = 0x100 << selectedVertical;
property.intValue = value;
}
}
EditorGUI.EndProperty();
}
static int DoVerticalAligmentControl(Rect position, SerializedProperty alignment)
{
var selected = TMP_EditorUtility.GetVerticalAlignmentGridValue(alignment.intValue);
var values = new bool[6];
values[selected] = true;
if (alignment.hasMultipleDifferentValues)
{
foreach (var obj in alignment.serializedObject.targetObjects)
{
var text = obj as TMP_Text;
if (text != null)
{
values[TMP_EditorUtility.GetVerticalAlignmentGridValue((int)text.verticalAlignment)] = true;
}
}
}
position.width = k_AlignmentButtonWidth;
for (var i = 0; i < values.Length; i++)
{
var oldValue = values[i];
var newValue = TMP_EditorUtility.EditorToggle(position, oldValue, TMP_UIStyleManager.alignContentB[i], i == 0 ? TMP_UIStyleManager.alignmentButtonLeft : (i == 5 ? TMP_UIStyleManager.alignmentButtonRight : TMP_UIStyleManager.alignmentButtonMid));
if (newValue != oldValue)
{
selected = i;
}
position.x += position.width;
}
return selected;
}
}
}

View File

@@ -0,0 +1,134 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
public static class TMP_UIStyleManager
{
public static GUIStyle label;
public static GUIStyle textAreaBoxWindow;
public static GUIStyle boldFoldout;
public static GUIStyle panelTitle;
public static GUIStyle sectionHeader;
public static GUIStyle centeredLabel;
public static GUIStyle rightLabel;
public static GUIStyle wrappingTextArea;
public static GUIStyle alignmentButtonLeft;
public static GUIStyle alignmentButtonMid;
public static GUIStyle alignmentButtonRight;
// Alignment Button Textures
public static Texture2D alignLeft;
public static Texture2D alignCenter;
public static Texture2D alignRight;
public static Texture2D alignJustified;
public static Texture2D alignFlush;
public static Texture2D alignGeoCenter;
public static Texture2D alignTop;
public static Texture2D alignMiddle;
public static Texture2D alignBottom;
public static Texture2D alignBaseline;
public static Texture2D alignMidline;
public static Texture2D alignCapline;
public static Texture2D sectionHeaderTexture;
public static GUIContent[] alignContentA;
public static GUIContent[] alignContentB;
static TMP_UIStyleManager()
{
// Find to location of the TextMesh Pro Asset Folder (as users may have moved it)
var tmproAssetFolderPath = TMP_EditorUtility.packageRelativePath;
if (EditorGUIUtility.isProSkin)
{
alignLeft = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignLeft.psd", typeof(Texture2D)) as Texture2D;
alignCenter = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignCenter.psd", typeof(Texture2D)) as Texture2D;
alignRight = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignRight.psd", typeof(Texture2D)) as Texture2D;
alignJustified = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignJustified.psd", typeof(Texture2D)) as Texture2D;
alignFlush = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignFlush.psd", typeof(Texture2D)) as Texture2D;
alignGeoCenter = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignCenterGeo.psd", typeof(Texture2D)) as Texture2D;
alignTop = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignTop.psd", typeof(Texture2D)) as Texture2D;
alignMiddle = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignMiddle.psd", typeof(Texture2D)) as Texture2D;
alignBottom = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignBottom.psd", typeof(Texture2D)) as Texture2D;
alignBaseline = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignBaseLine.psd", typeof(Texture2D)) as Texture2D;
alignMidline = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignMidLine.psd", typeof(Texture2D)) as Texture2D;
alignCapline = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignCapLine.psd", typeof(Texture2D)) as Texture2D;
sectionHeaderTexture = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/SectionHeader_Dark.psd", typeof(Texture2D)) as Texture2D;
}
else
{
alignLeft = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignLeft_Light.psd", typeof(Texture2D)) as Texture2D;
alignCenter = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignCenter_Light.psd", typeof(Texture2D)) as Texture2D;
alignRight = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignRight_Light.psd", typeof(Texture2D)) as Texture2D;
alignJustified = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignJustified_Light.psd", typeof(Texture2D)) as Texture2D;
alignFlush = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignFlush_Light.psd", typeof(Texture2D)) as Texture2D;
alignGeoCenter = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignCenterGeo_Light.psd", typeof(Texture2D)) as Texture2D;
alignTop = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignTop_Light.psd", typeof(Texture2D)) as Texture2D;
alignMiddle = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignMiddle_Light.psd", typeof(Texture2D)) as Texture2D;
alignBottom = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignBottom_Light.psd", typeof(Texture2D)) as Texture2D;
alignBaseline = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignBaseLine_Light.psd", typeof(Texture2D)) as Texture2D;
alignMidline = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignMidLine_Light.psd", typeof(Texture2D)) as Texture2D;
alignCapline = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/btn_AlignCapLine_Light.psd", typeof(Texture2D)) as Texture2D;
sectionHeaderTexture = AssetDatabase.LoadAssetAtPath(tmproAssetFolderPath + "/Editor Resources/Textures/SectionHeader_Light.psd", typeof(Texture2D)) as Texture2D;
}
label = new GUIStyle(EditorStyles.label) { richText = true, wordWrap = true, stretchWidth = true };
textAreaBoxWindow = new GUIStyle(EditorStyles.textArea) { richText = true };
boldFoldout = new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold };
panelTitle = new GUIStyle(EditorStyles.label) { fontStyle = FontStyle.Bold };
sectionHeader = new GUIStyle(EditorStyles.label) { fixedHeight = 22, richText = true, border = new RectOffset(9, 9, 0, 0), overflow = new RectOffset(9, 0, 0, 0), padding = new RectOffset(0, 0, 4, 0) };
sectionHeader.normal.background = sectionHeaderTexture;
centeredLabel = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleCenter};
rightLabel = new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleRight, richText = true };
alignmentButtonLeft = new GUIStyle(EditorStyles.miniButtonLeft);
alignmentButtonLeft.padding.left = 4;
alignmentButtonLeft.padding.right = 4;
alignmentButtonLeft.padding.top = 2;
alignmentButtonLeft.padding.bottom = 2;
alignmentButtonMid = new GUIStyle(EditorStyles.miniButtonMid);
alignmentButtonMid.padding.left = 4;
alignmentButtonMid.padding.right = 4;
alignmentButtonLeft.padding.top = 2;
alignmentButtonLeft.padding.bottom = 2;
alignmentButtonRight = new GUIStyle(EditorStyles.miniButtonRight);
alignmentButtonRight.padding.left = 4;
alignmentButtonRight.padding.right = 4;
alignmentButtonLeft.padding.top = 2;
alignmentButtonLeft.padding.bottom = 2;
wrappingTextArea = new GUIStyle(EditorStyles.textArea);
wrappingTextArea.wordWrap = true;
alignContentA = new []
{
new GUIContent(alignLeft, "Left"),
new GUIContent(alignCenter, "Center"),
new GUIContent(alignRight, "Right"),
new GUIContent(alignJustified, "Justified"),
new GUIContent(alignFlush, "Flush"),
new GUIContent(alignGeoCenter, "Geometry Center")
};
alignContentB = new []
{
new GUIContent(alignTop, "Top"),
new GUIContent(alignMiddle, "Middle"),
new GUIContent(alignBottom, "Bottom"),
new GUIContent(alignBaseline, "Baseline"),
new GUIContent(alignMidline, "Midline"),
new GUIContent(alignCapline, "Capline")
};
}
}
}

View File

@@ -0,0 +1,400 @@
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Collections;
namespace TMPro.EditorUtilities
{
public class TMP_ContextMenus : Editor
{
private static Texture m_copiedTexture;
private static Material m_copiedProperties;
private static Material m_copiedAtlasProperties;
// Add a Context Menu to the Texture Editor Panel to allow Copy / Paste of Texture.
#if !TEXTCORE_1_0_OR_NEWER
[MenuItem("CONTEXT/Texture/Copy", false, 2000)]
static void CopyTexture(MenuCommand command)
{
m_copiedTexture = command.context as Texture;
}
// Select the currently assigned material or material preset.
[MenuItem("CONTEXT/Material/Select Material", false, 500)]
static void SelectMaterial(MenuCommand command)
{
Material mat = command.context as Material;
// Select current material
EditorUtility.FocusProjectWindow();
EditorGUIUtility.PingObject(mat);
}
#endif
// Add a Context Menu to allow easy duplication of the Material.
[MenuItem("CONTEXT/Material/Create Material Preset", false)]
static void DuplicateMaterial(MenuCommand command)
{
// Get the type of text object
// If material is not a base material, we get material leaks...
Material source_Mat = (Material)command.context;
if (!EditorUtility.IsPersistent(source_Mat))
{
Debug.LogWarning("Material is an instance and cannot be converted into a persistent asset.");
return;
}
string assetPath = AssetDatabase.GetAssetPath(source_Mat).Split('.')[0];
if (assetPath.IndexOf("Assets/", System.StringComparison.InvariantCultureIgnoreCase) == -1)
{
Debug.LogWarning("Material Preset cannot be created from a material that is located outside the project.");
return;
}
Material duplicate = new Material(source_Mat);
// Need to manually copy the shader keywords
duplicate.shaderKeywords = source_Mat.shaderKeywords;
AssetDatabase.CreateAsset(duplicate, AssetDatabase.GenerateUniqueAssetPath(assetPath + ".mat"));
GameObject[] selectedObjects = Selection.gameObjects;
// Assign new Material Preset to selected text objects.
for (int i = 0; i < selectedObjects.Length; i++)
{
TMP_Text textObject = selectedObjects[i].GetComponent<TMP_Text>();
if (textObject != null)
{
textObject.fontSharedMaterial = duplicate;
}
else
{
TMP_SubMesh subMeshObject = selectedObjects[i].GetComponent<TMP_SubMesh>();
if (subMeshObject != null)
subMeshObject.sharedMaterial = duplicate;
else
{
TMP_SubMeshUI subMeshUIObject = selectedObjects[i].GetComponent<TMP_SubMeshUI>();
if (subMeshUIObject != null)
subMeshUIObject.sharedMaterial = duplicate;
}
}
}
// Ping newly created Material Preset.
EditorUtility.FocusProjectWindow();
EditorGUIUtility.PingObject(duplicate);
}
// COPY MATERIAL PROPERTIES
#if !TEXTCORE_1_0_OR_NEWER
[MenuItem("CONTEXT/Material/Copy Material Properties", false)]
static void CopyMaterialProperties(MenuCommand command)
{
Material mat = null;
if (command.context.GetType() == typeof(Material))
mat = (Material)command.context;
else
{
mat = Selection.activeGameObject.GetComponent<CanvasRenderer>().GetMaterial();
}
m_copiedProperties = new Material(mat);
m_copiedProperties.shaderKeywords = mat.shaderKeywords;
m_copiedProperties.hideFlags = HideFlags.DontSave;
}
// PASTE MATERIAL PROPERTIES
[MenuItem("CONTEXT/Material/Paste Material Properties", true)]
static bool PasteMaterialPropertiesValidate(MenuCommand command)
{
if (m_copiedProperties == null)
return false;
return AssetDatabase.IsOpenForEdit(command.context);
}
[MenuItem("CONTEXT/Material/Paste Material Properties", false)]
static void PasteMaterialProperties(MenuCommand command)
{
if (m_copiedProperties == null)
{
Debug.LogWarning("No Material Properties to Paste. Use Copy Material Properties first.");
return;
}
Material mat = null;
if (command.context.GetType() == typeof(Material))
mat = (Material)command.context;
else
{
mat = Selection.activeGameObject.GetComponent<CanvasRenderer>().GetMaterial();
}
Undo.RecordObject(mat, "Paste Material");
ShaderUtilities.GetShaderPropertyIDs(); // Make sure we have valid Property IDs
if (mat.HasProperty(ShaderUtilities.ID_GradientScale))
{
// Preserve unique SDF properties from destination material.
m_copiedProperties.SetTexture(ShaderUtilities.ID_MainTex, mat.GetTexture(ShaderUtilities.ID_MainTex));
m_copiedProperties.SetFloat(ShaderUtilities.ID_GradientScale, mat.GetFloat(ShaderUtilities.ID_GradientScale));
m_copiedProperties.SetFloat(ShaderUtilities.ID_TextureWidth, mat.GetFloat(ShaderUtilities.ID_TextureWidth));
m_copiedProperties.SetFloat(ShaderUtilities.ID_TextureHeight, mat.GetFloat(ShaderUtilities.ID_TextureHeight));
}
EditorShaderUtilities.CopyMaterialProperties(m_copiedProperties, mat);
// Copy ShaderKeywords from one material to the other.
mat.shaderKeywords = m_copiedProperties.shaderKeywords;
// Let TextMeshPro Objects that this mat has changed.
TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, mat);
}
// Enable Resetting of Material properties without losing unique properties of the font atlas.
[MenuItem("CONTEXT/Material/Reset", true, 2100)]
static bool ResetSettingsValidate(MenuCommand command)
{
return AssetDatabase.IsOpenForEdit(command.context);
}
[MenuItem("CONTEXT/Material/Reset", false, 2100)]
static void ResetSettings(MenuCommand command)
{
Material mat = null;
if (command.context.GetType() == typeof(Material))
mat = (Material)command.context;
else
{
mat = Selection.activeGameObject.GetComponent<CanvasRenderer>().GetMaterial();
}
Undo.RecordObject(mat, "Reset Material");
ShaderUtilities.GetShaderPropertyIDs(); // Make sure we have valid Property IDs
if (mat.HasProperty(ShaderUtilities.ID_GradientScale))
{
// Copy unique properties of the SDF Material
var texture = mat.GetTexture(ShaderUtilities.ID_MainTex);
var gradientScale = mat.GetFloat(ShaderUtilities.ID_GradientScale);
var texWidth = mat.GetFloat(ShaderUtilities.ID_TextureWidth);
var texHeight = mat.GetFloat(ShaderUtilities.ID_TextureHeight);
var stencilId = 0.0f;
var stencilComp = 0.0f;
if (mat.HasProperty(ShaderUtilities.ID_StencilID))
{
stencilId = mat.GetFloat(ShaderUtilities.ID_StencilID);
stencilComp = mat.GetFloat(ShaderUtilities.ID_StencilComp);
}
var normalWeight = mat.GetFloat(ShaderUtilities.ID_WeightNormal);
var boldWeight = mat.GetFloat(ShaderUtilities.ID_WeightBold);
// Reset the material
Unsupported.SmartReset(mat);
// Reset ShaderKeywords
mat.shaderKeywords = new string[0]; // { "BEVEL_OFF", "GLOW_OFF", "UNDERLAY_OFF" };
// Copy unique material properties back to the material.
mat.SetTexture(ShaderUtilities.ID_MainTex, texture);
mat.SetFloat(ShaderUtilities.ID_GradientScale, gradientScale);
mat.SetFloat(ShaderUtilities.ID_TextureWidth, texWidth);
mat.SetFloat(ShaderUtilities.ID_TextureHeight, texHeight);
if (mat.HasProperty(ShaderUtilities.ID_StencilID))
{
mat.SetFloat(ShaderUtilities.ID_StencilID, stencilId);
mat.SetFloat(ShaderUtilities.ID_StencilComp, stencilComp);
}
mat.SetFloat(ShaderUtilities.ID_WeightNormal, normalWeight);
mat.SetFloat(ShaderUtilities.ID_WeightBold, boldWeight);
}
else
{
Unsupported.SmartReset(mat);
}
TMPro_EventManager.ON_MATERIAL_PROPERTY_CHANGED(true, mat);
}
//This function is used for debugging and fixing potentially broken font atlas links.
[MenuItem("CONTEXT/Material/Copy Atlas", false, 2000)]
static void CopyAtlas(MenuCommand command)
{
Material mat = command.context as Material;
m_copiedAtlasProperties = new Material(mat);
m_copiedAtlasProperties.hideFlags = HideFlags.DontSave;
}
// This function is used for debugging and fixing potentially broken font atlas links
[MenuItem("CONTEXT/Material/Paste Atlas", true, 2001)]
static bool PasteAtlasValidate(MenuCommand command)
{
if (m_copiedAtlasProperties == null && m_copiedTexture == null)
return false;
return AssetDatabase.IsOpenForEdit(command.context);
}
[MenuItem("CONTEXT/Material/Paste Atlas", false, 2001)]
static void PasteAtlas(MenuCommand command)
{
Material mat = command.context as Material;
if (mat == null)
return;
if (m_copiedAtlasProperties != null)
{
Undo.RecordObject(mat, "Paste Texture");
ShaderUtilities.GetShaderPropertyIDs(); // Make sure we have valid Property IDs
if (m_copiedAtlasProperties.HasProperty(ShaderUtilities.ID_MainTex))
mat.SetTexture(ShaderUtilities.ID_MainTex, m_copiedAtlasProperties.GetTexture(ShaderUtilities.ID_MainTex));
if (m_copiedAtlasProperties.HasProperty(ShaderUtilities.ID_GradientScale))
{
mat.SetFloat(ShaderUtilities.ID_GradientScale, m_copiedAtlasProperties.GetFloat(ShaderUtilities.ID_GradientScale));
mat.SetFloat(ShaderUtilities.ID_TextureWidth, m_copiedAtlasProperties.GetFloat(ShaderUtilities.ID_TextureWidth));
mat.SetFloat(ShaderUtilities.ID_TextureHeight, m_copiedAtlasProperties.GetFloat(ShaderUtilities.ID_TextureHeight));
}
}
else if (m_copiedTexture != null)
{
Undo.RecordObject(mat, "Paste Texture");
mat.SetTexture(ShaderUtilities.ID_MainTex, m_copiedTexture);
}
//DestroyImmediate(m_copiedAtlasProperties);
}
#endif
// Context Menus for TMPro Font Assets
//This function is used for debugging and fixing potentially broken font atlas links.
[MenuItem("CONTEXT/TMP_FontAsset/Extract Atlas", false, 2100)]
static void ExtractAtlas(MenuCommand command)
{
TMP_FontAsset font = command.context as TMP_FontAsset;
string fontPath = AssetDatabase.GetAssetPath(font);
string texPath = Path.GetDirectoryName(fontPath) + "/" + Path.GetFileNameWithoutExtension(fontPath) + " Atlas.png";
// Create a Serialized Object of the texture to allow us to make it readable.
SerializedObject texprop = new SerializedObject(font.material.GetTexture(ShaderUtilities.ID_MainTex));
texprop.FindProperty("m_IsReadable").boolValue = true;
texprop.ApplyModifiedProperties();
// Create a copy of the texture.
Texture2D tex = Instantiate(font.material.GetTexture(ShaderUtilities.ID_MainTex)) as Texture2D;
// Set the texture to not readable again.
texprop.FindProperty("m_IsReadable").boolValue = false;
texprop.ApplyModifiedProperties();
Debug.Log(texPath);
// Saving File for Debug
var pngData = tex.EncodeToPNG();
File.WriteAllBytes(texPath, pngData);
AssetDatabase.Refresh();
DestroyImmediate(tex);
}
/// <summary>
///
/// </summary>
/// <param name="command"></param>
[MenuItem("CONTEXT/TMP_FontAsset/Update Atlas Texture...", false, 2000)]
static void RegenerateFontAsset(MenuCommand command)
{
TMP_FontAsset fontAsset = command.context as TMP_FontAsset;
if (fontAsset != null)
{
TMPro_FontAssetCreatorWindow.ShowFontAtlasCreatorWindow(fontAsset);
}
}
[MenuItem("CONTEXT/TMP_FontAsset/Force Upgrade To Version 1.1.0...", false, 2010)]
static void ForceFontAssetUpgrade(MenuCommand command)
{
TMP_FontAsset fontAsset = command.context as TMP_FontAsset;
if (fontAsset != null)
{
fontAsset.UpgradeFontAsset();
TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset);
}
}
/// <summary>
/// Clear Dynamic Font Asset data such as glyph, character and font features.
/// </summary>
/// <param name="command"></param>
[MenuItem("CONTEXT/TMP_FontAsset/Reset", true, 100)]
static bool ClearFontAssetDataValidate(MenuCommand command)
{
return AssetDatabase.IsOpenForEdit(command.context);
}
[MenuItem("CONTEXT/TMP_FontAsset/Reset", false, 100)]
static void ClearFontAssetData(MenuCommand command)
{
TMP_FontAsset fontAsset = command.context as TMP_FontAsset;
if (fontAsset != null && Selection.activeObject != fontAsset)
{
Selection.activeObject = fontAsset;
}
fontAsset.ClearFontAssetData(true);
TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset);
}
[MenuItem("CONTEXT/TrueTypeFontImporter/Create TMP Font Asset...", false, 200)]
static void CreateFontAsset(MenuCommand command)
{
TrueTypeFontImporter importer = command.context as TrueTypeFontImporter;
if (importer != null)
{
Font sourceFontFile = AssetDatabase.LoadAssetAtPath<Font>(importer.assetPath);
if (sourceFontFile)
TMPro_FontAssetCreatorWindow.ShowFontAtlasCreatorWindow(sourceFontFile);
}
}
}
}

View File

@@ -0,0 +1,398 @@
using UnityEngine;
using UnityEditor;
using UnityEditor.Presets;
using UnityEditor.SceneManagement;
//#if !UNITY_2021_2_OR_NEWER
using UnityEditor.Experimental.SceneManagement;
//#endif
using UnityEngine.UI;
using UnityEngine.EventSystems;
namespace TMPro.EditorUtilities
{
public static class TMPro_CreateObjectMenu
{
/// <summary>
/// Create a TextMeshPro object that works with the Mesh Renderer
/// </summary>
/// <param name="command"></param>
[MenuItem("GameObject/3D Object/Text - TextMeshPro", false, 30)]
static void CreateTextMeshProObjectPerform(MenuCommand command)
{
GameObject go = ObjectFactory.CreateGameObject("Text (TMP)");
// Add support for new prefab mode
StageUtility.PlaceGameObjectInCurrentStage(go);
TextMeshPro textComponent = ObjectFactory.AddComponent<TextMeshPro>(go);
if (textComponent.m_isWaitingOnResourceLoad == false)
{
// Get reference to potential Presets for <TextMeshPro> component
Preset[] presets = Preset.GetDefaultPresetsForObject(textComponent);
if (presets == null || presets.Length == 0)
{
textComponent.text = "Sample text";
textComponent.alignment = TextAlignmentOptions.TopLeft;
}
else
{
textComponent.renderer.sortingLayerID = textComponent._SortingLayerID;
textComponent.renderer.sortingOrder = textComponent._SortingOrder;
}
if (TMP_Settings.autoSizeTextContainer)
{
Vector2 size = textComponent.GetPreferredValues(TMP_Math.FLOAT_MAX, TMP_Math.FLOAT_MAX);
textComponent.rectTransform.sizeDelta = size;
}
else
{
textComponent.rectTransform.sizeDelta = TMP_Settings.defaultTextMeshProTextContainerSize;
}
}
else
{
textComponent.text = "Sample text";
textComponent.alignment = TextAlignmentOptions.TopLeft;
}
Undo.RegisterCreatedObjectUndo(go, "Create " + go.name);
GameObject contextObject = command.context as GameObject;
if (contextObject != null)
{
GameObjectUtility.SetParentAndAlign(go, contextObject);
Undo.SetTransformParent(go.transform, contextObject.transform, "Parent " + go.name);
}
Selection.activeGameObject = go;
}
/// <summary>
/// Create a TextMeshPro object that works with the CanvasRenderer
/// </summary>
/// <param name="command"></param>
[MenuItem("GameObject/UI/Text - TextMeshPro", false, 2001)]
static void CreateTextMeshProGuiObjectPerform(MenuCommand menuCommand)
{
GameObject go = TMP_DefaultControls.CreateText(GetStandardResources());
// Override text color and font size
TextMeshProUGUI textComponent = go.GetComponent<TextMeshProUGUI>();
if (textComponent.m_isWaitingOnResourceLoad == false)
{
// Get reference to potential Presets for <TextMeshProUGUI> component
Preset[] presets = Preset.GetDefaultPresetsForObject(textComponent);
if (presets == null || presets.Length == 0)
{
textComponent.fontSize = TMP_Settings.defaultFontSize;
textComponent.color = Color.white;
textComponent.text = "New Text";
}
if (TMP_Settings.autoSizeTextContainer)
{
Vector2 size = textComponent.GetPreferredValues(TMP_Math.FLOAT_MAX, TMP_Math.FLOAT_MAX);
textComponent.rectTransform.sizeDelta = size;
}
else
{
textComponent.rectTransform.sizeDelta = TMP_Settings.defaultTextMeshProUITextContainerSize;
}
}
else
{
textComponent.fontSize = -99;
textComponent.color = Color.white;
textComponent.text = "New Text";
}
PlaceUIElementRoot(go, menuCommand);
}
[MenuItem("GameObject/UI/Button - TextMeshPro", false, 2031)]
public static void AddButton(MenuCommand menuCommand)
{
GameObject go = TMP_DefaultControls.CreateButton(GetStandardResources());
// Override font size
TMP_Text textComponent = go.GetComponentInChildren<TMP_Text>();
textComponent.fontSize = 24;
PlaceUIElementRoot(go, menuCommand);
}
[MenuItem("GameObject/UI/Input Field - TextMeshPro", false, 2037)]
static void AddTextMeshProInputField(MenuCommand menuCommand)
{
GameObject go = TMP_DefaultControls.CreateInputField(GetStandardResources());
PlaceUIElementRoot(go, menuCommand);
}
[MenuItem("GameObject/UI/Dropdown - TextMeshPro", false, 2036)]
public static void AddDropdown(MenuCommand menuCommand)
{
GameObject go = TMP_DefaultControls.CreateDropdown(GetStandardResources());
PlaceUIElementRoot(go, menuCommand);
}
private const string kUILayerName = "UI";
private const string kStandardSpritePath = "UI/Skin/UISprite.psd";
private const string kBackgroundSpritePath = "UI/Skin/Background.psd";
private const string kInputFieldBackgroundPath = "UI/Skin/InputFieldBackground.psd";
private const string kKnobPath = "UI/Skin/Knob.psd";
private const string kCheckmarkPath = "UI/Skin/Checkmark.psd";
private const string kDropdownArrowPath = "UI/Skin/DropdownArrow.psd";
private const string kMaskPath = "UI/Skin/UIMask.psd";
private static TMP_DefaultControls.Resources s_StandardResources;
private static TMP_DefaultControls.Resources GetStandardResources()
{
if (s_StandardResources.standard == null)
{
s_StandardResources.standard = AssetDatabase.GetBuiltinExtraResource<Sprite>(kStandardSpritePath);
s_StandardResources.background = AssetDatabase.GetBuiltinExtraResource<Sprite>(kBackgroundSpritePath);
s_StandardResources.inputField = AssetDatabase.GetBuiltinExtraResource<Sprite>(kInputFieldBackgroundPath);
s_StandardResources.knob = AssetDatabase.GetBuiltinExtraResource<Sprite>(kKnobPath);
s_StandardResources.checkmark = AssetDatabase.GetBuiltinExtraResource<Sprite>(kCheckmarkPath);
s_StandardResources.dropdown = AssetDatabase.GetBuiltinExtraResource<Sprite>(kDropdownArrowPath);
s_StandardResources.mask = AssetDatabase.GetBuiltinExtraResource<Sprite>(kMaskPath);
}
return s_StandardResources;
}
private static void SetPositionVisibleinSceneView(RectTransform canvasRTransform, RectTransform itemTransform)
{
// Find the best scene view
SceneView sceneView = SceneView.lastActiveSceneView;
if (sceneView == null && SceneView.sceneViews.Count > 0)
sceneView = SceneView.sceneViews[0] as SceneView;
// Couldn't find a SceneView. Don't set position.
if (sceneView == null || sceneView.camera == null)
return;
// Create world space Plane from canvas position.
Camera camera = sceneView.camera;
Vector3 position = Vector3.zero;
Vector2 localPlanePosition;
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRTransform, new Vector2(camera.pixelWidth / 2, camera.pixelHeight / 2), camera, out localPlanePosition))
{
// Adjust for canvas pivot
localPlanePosition.x = localPlanePosition.x + canvasRTransform.sizeDelta.x * canvasRTransform.pivot.x;
localPlanePosition.y = localPlanePosition.y + canvasRTransform.sizeDelta.y * canvasRTransform.pivot.y;
localPlanePosition.x = Mathf.Clamp(localPlanePosition.x, 0, canvasRTransform.sizeDelta.x);
localPlanePosition.y = Mathf.Clamp(localPlanePosition.y, 0, canvasRTransform.sizeDelta.y);
// Adjust for anchoring
position.x = localPlanePosition.x - canvasRTransform.sizeDelta.x * itemTransform.anchorMin.x;
position.y = localPlanePosition.y - canvasRTransform.sizeDelta.y * itemTransform.anchorMin.y;
Vector3 minLocalPosition;
minLocalPosition.x = canvasRTransform.sizeDelta.x * (0 - canvasRTransform.pivot.x) + itemTransform.sizeDelta.x * itemTransform.pivot.x;
minLocalPosition.y = canvasRTransform.sizeDelta.y * (0 - canvasRTransform.pivot.y) + itemTransform.sizeDelta.y * itemTransform.pivot.y;
Vector3 maxLocalPosition;
maxLocalPosition.x = canvasRTransform.sizeDelta.x * (1 - canvasRTransform.pivot.x) - itemTransform.sizeDelta.x * itemTransform.pivot.x;
maxLocalPosition.y = canvasRTransform.sizeDelta.y * (1 - canvasRTransform.pivot.y) - itemTransform.sizeDelta.y * itemTransform.pivot.y;
position.x = Mathf.Clamp(position.x, minLocalPosition.x, maxLocalPosition.x);
position.y = Mathf.Clamp(position.y, minLocalPosition.y, maxLocalPosition.y);
}
itemTransform.anchoredPosition = position;
itemTransform.localRotation = Quaternion.identity;
itemTransform.localScale = Vector3.one;
}
private static void PlaceUIElementRoot(GameObject element, MenuCommand menuCommand)
{
GameObject parent = menuCommand.context as GameObject;
bool explicitParentChoice = true;
if (parent == null)
{
parent = GetOrCreateCanvasGameObject();
explicitParentChoice = false;
// If in Prefab Mode, Canvas has to be part of Prefab contents,
// otherwise use Prefab root instead.
PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
if (prefabStage != null && !prefabStage.IsPartOfPrefabContents(parent))
parent = prefabStage.prefabContentsRoot;
}
if (parent.GetComponentsInParent<Canvas>(true).Length == 0)
{
// Create canvas under context GameObject,
// and make that be the parent which UI element is added under.
GameObject canvas = CreateNewUI();
Undo.SetTransformParent(canvas.transform, parent.transform, "");
parent = canvas;
}
GameObjectUtility.EnsureUniqueNameForSibling(element);
SetParentAndAlign(element, parent);
if (!explicitParentChoice) // not a context click, so center in sceneview
SetPositionVisibleinSceneView(parent.GetComponent<RectTransform>(), element.GetComponent<RectTransform>());
// This call ensure any change made to created Objects after they where registered will be part of the Undo.
Undo.RegisterFullObjectHierarchyUndo(parent == null ? element : parent, "");
// We have to fix up the undo name since the name of the object was only known after reparenting it.
Undo.SetCurrentGroupName("Create " + element.name);
Selection.activeGameObject = element;
}
private static void SetParentAndAlign(GameObject child, GameObject parent)
{
if (parent == null)
return;
Undo.SetTransformParent(child.transform, parent.transform, "");
RectTransform rectTransform = child.transform as RectTransform;
if (rectTransform)
{
rectTransform.anchoredPosition = Vector2.zero;
Vector3 localPosition = rectTransform.localPosition;
localPosition.z = 0;
rectTransform.localPosition = localPosition;
}
else
{
child.transform.localPosition = Vector3.zero;
}
child.transform.localRotation = Quaternion.identity;
child.transform.localScale = Vector3.one;
SetLayerRecursively(child, parent.layer);
}
private static void SetLayerRecursively(GameObject go, int layer)
{
go.layer = layer;
Transform t = go.transform;
for (int i = 0; i < t.childCount; i++)
SetLayerRecursively(t.GetChild(i).gameObject, layer);
}
public static GameObject CreateNewUI()
{
// Root for the UI
var root = new GameObject("Canvas");
root.layer = LayerMask.NameToLayer(kUILayerName);
Canvas canvas = root.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
root.AddComponent<CanvasScaler>();
root.AddComponent<GraphicRaycaster>();
// Works for all stages.
StageUtility.PlaceGameObjectInCurrentStage(root);
bool customScene = false;
PrefabStage prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
if (prefabStage != null)
{
root.transform.SetParent(prefabStage.prefabContentsRoot.transform, false);
customScene = true;
}
Undo.RegisterCreatedObjectUndo(root, "Create " + root.name);
// If there is no event system add one...
// No need to place event system in custom scene as these are temporary anyway.
// It can be argued for or against placing it in the user scenes,
// but let's not modify scene user is not currently looking at.
if (!customScene)
CreateEventSystem(false);
return root;
}
private static void CreateEventSystem(bool select)
{
CreateEventSystem(select, null);
}
private static void CreateEventSystem(bool select, GameObject parent)
{
var esys = Object.FindObjectOfType<EventSystem>();
if (esys == null)
{
var eventSystem = new GameObject("EventSystem");
GameObjectUtility.SetParentAndAlign(eventSystem, parent);
esys = eventSystem.AddComponent<EventSystem>();
eventSystem.AddComponent<StandaloneInputModule>();
Undo.RegisterCreatedObjectUndo(eventSystem, "Create " + eventSystem.name);
}
if (select && esys != null)
{
Selection.activeGameObject = esys.gameObject;
}
}
// Helper function that returns a Canvas GameObject; preferably a parent of the selection, or other existing Canvas.
public static GameObject GetOrCreateCanvasGameObject()
{
GameObject selectedGo = Selection.activeGameObject;
// Try to find a gameobject that is the selected GO or one if its parents.
Canvas canvas = (selectedGo != null) ? selectedGo.GetComponentInParent<Canvas>() : null;
if (IsValidCanvas(canvas))
return canvas.gameObject;
// No canvas in selection or its parents? Then use any valid canvas.
// We have to find all loaded Canvases, not just the ones in main scenes.
Canvas[] canvasArray = StageUtility.GetCurrentStageHandle().FindComponentsOfType<Canvas>();
for (int i = 0; i < canvasArray.Length; i++)
if (IsValidCanvas(canvasArray[i]))
return canvasArray[i].gameObject;
// No canvas in the scene at all? Then create a new one.
return CreateNewUI();
}
static bool IsValidCanvas(Canvas canvas)
{
if (canvas == null || !canvas.gameObject.activeInHierarchy)
return false;
// It's important that the non-editable canvas from a prefab scene won't be rejected,
// but canvases not visible in the Hierarchy at all do. Don't check for HideAndDontSave.
if (EditorUtility.IsPersistent(canvas) || (canvas.hideFlags & HideFlags.HideInHierarchy) != 0)
return false;
if (StageUtility.GetStageHandle(canvas.gameObject) != StageUtility.GetCurrentStageHandle())
return false;
return true;
}
}
}

View File

@@ -0,0 +1,53 @@
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Collections;
namespace TMPro.EditorUtilities
{
public static class EditorShaderUtilities
{
/// <summary>
/// Copy Shader properties from source to destination material.
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static void CopyMaterialProperties(Material source, Material destination)
{
MaterialProperty[] source_prop = MaterialEditor.GetMaterialProperties(new Material[] { source });
for (int i = 0; i < source_prop.Length; i++)
{
int property_ID = Shader.PropertyToID(source_prop[i].name);
if (destination.HasProperty(property_ID))
{
//Debug.Log(source_prop[i].name + " Type:" + ShaderUtil.GetPropertyType(source.shader, i));
switch (ShaderUtil.GetPropertyType(source.shader, i))
{
case ShaderUtil.ShaderPropertyType.Color:
destination.SetColor(property_ID, source.GetColor(property_ID));
break;
case ShaderUtil.ShaderPropertyType.Float:
destination.SetFloat(property_ID, source.GetFloat(property_ID));
break;
case ShaderUtil.ShaderPropertyType.Range:
destination.SetFloat(property_ID, source.GetFloat(property_ID));
break;
case ShaderUtil.ShaderPropertyType.TexEnv:
destination.SetTexture(property_ID, source.GetTexture(property_ID));
break;
case ShaderUtil.ShaderPropertyType.Vector:
destination.SetVector(property_ID, source.GetVector(property_ID));
break;
}
}
}
}
}
}

View File

@@ -0,0 +1,115 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
using System;
using System.Runtime.InteropServices;
namespace TMPro.EditorUtilities
{
/*
public class TMPro_FontPlugin
{
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void DebugLog(string log);
private static readonly DebugLog debugLog = DebugWrapper;
private static readonly IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(debugLog);
private static void DebugWrapper(string log)
{
Debug.Log(log);
}
public static void LinkDebugLog()
{
LinkDebug(functionPointer);
}
[DllImport("TMPro_Plugin")]
private static extern void LinkDebug([MarshalAs(UnmanagedType.FunctionPtr)]IntPtr debugCall);
[DllImport("TMPro_Plugin")]
public static extern
int Initialize_FontEngine();
[DllImport("TMPro_Plugin")]
public static extern
int Destroy_FontEngine();
[DllImport("TMPro_Plugin")]
public static extern
int Load_TrueType_Font(string fontPath);
[DllImport("TMPro_Plugin")]
public static extern
int FT_Size_Font(int fontSize);
[DllImport("TMPro_Plugin")]
public static extern
int Render_Character(byte[] buffer_fill, byte[] buffer_edge, int buffer_width, int buffer_height, int offset, int asc, FaceStyles style, float thickness, RenderModes rasterMode, ref FT_GlyphInfo glyphInfo);
[DllImport("TMPro_Plugin")]
public static extern
int Render_Characters(byte[] buffer, int buffer_width, int buffer_height, int character_padding, int[] asc_set, int char_count, FaceStyles style, float style_mod, bool autoSize, RenderModes renderMode, int method, ref FT_FaceInfo fontData, FT_GlyphInfo[] Output);
[DllImport("TMPro_Plugin")]
public static extern
int FT_GetKerningPairs(string fontPath, int[] characterSet, int setCount, FT_KerningPair[] kerningPairs);
[DllImport("TMPro_Plugin")]
public static extern
float Check_RenderProgress();
[DllImport("TMPro_Plugin")]
internal static extern
void SendCancellationRequest(CancellationRequestType request);
}
public enum FaceStyles { Normal, Bold, Italic, Bold_Italic, Outline, Bold_Sim };
public enum RenderModes { HintedSmooth = 0, Smooth = 1, RasterHinted = 2, Raster = 3, DistanceField16 = 6, DistanceField32 = 7 }; // SignedDistanceField64 = 8
internal enum CancellationRequestType : byte { None = 0x0, CancelInProgess = 0x1, WindowClosed = 0x2 };
[StructLayout(LayoutKind.Sequential)]
public struct FT_KerningPair
{
public int ascII_Left;
public int ascII_Right;
public float xAdvanceOffset;
}
[StructLayout(LayoutKind.Sequential)]
public struct FT_GlyphInfo
{
public int id;
public float x;
public float y;
public float width;
public float height;
public float xOffset;
public float yOffset;
public float xAdvance;
}
[StructLayout(LayoutKind.Sequential)]
public struct FT_FaceInfo
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
public string name;
public int pointSize;
public int padding;
public float lineHeight;
public float baseline;
public float ascender;
public float descender;
public float centerLine;
public float underline;
public float underlineThickness;
public int characterCount;
public int atlasWidth;
public int atlasHeight;
}
*/
}

View File

@@ -0,0 +1,32 @@
using UnityEngine;
namespace TMPro
{
// Helpers used by the different sorting layer classes.
public static class SortingLayerHelper
{
// Gets an array of sorting layer names.
public static string[] sortingLayerNames
{
get
{
return GetSortingLayerNames();
}
}
static string[] GetSortingLayerNames()
{
int layerCount = SortingLayer.layers.Length;
string[] layerNames = new string[layerCount];
for (int i = 0; i < layerCount; i++)
{
layerNames[i] = SortingLayer.layers[i].name;
}
return layerNames;
}
}
}

View File

@@ -0,0 +1,235 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
[CustomEditor(typeof(TextContainer)), CanEditMultipleObjects]
public class TMPro_TextContainerEditor : Editor
{
// Serialized Properties
private SerializedProperty anchorPosition_prop;
private SerializedProperty pivot_prop;
private SerializedProperty rectangle_prop;
private SerializedProperty margins_prop;
private TextContainer m_textContainer;
//private Transform m_transform;
//private Vector3[] m_Rect_handlePoints = new Vector3[4];
//private Vector3[] m_Margin_handlePoints = new Vector3[4];
//private Vector2 m_anchorPosition;
//private Vector3 m_mousePreviousPOS;
//private Vector2 m_previousStartPOS;
//private int m_mouseDragFlag = 0;
//private static Transform m_visualHelper;
void OnEnable()
{
// Serialized Properties
anchorPosition_prop = serializedObject.FindProperty("m_anchorPosition");
pivot_prop = serializedObject.FindProperty("m_pivot");
rectangle_prop = serializedObject.FindProperty("m_rect");
margins_prop = serializedObject.FindProperty("m_margins");
m_textContainer = (TextContainer)target;
//m_transform = m_textContainer.transform;
/*
if (m_visualHelper == null)
{
m_visualHelper = GameObject.CreatePrimitive(PrimitiveType.Sphere).transform;
m_visualHelper.localScale = new Vector3(0.25f, 0.25f, 0.25f);
}
*/
}
void OnDisable()
{
/*
if (m_visualHelper != null)
DestroyImmediate (m_visualHelper.gameObject);
*/
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(anchorPosition_prop);
if (anchorPosition_prop.enumValueIndex == 9)
{
EditorGUI.indentLevel += 1;
EditorGUILayout.PropertyField(pivot_prop, new GUIContent("Pivot Position"));
EditorGUI.indentLevel -= 1;
}
DrawDimensionProperty(rectangle_prop, "Dimensions");
DrawMaginProperty(margins_prop, "Margins");
if (EditorGUI.EndChangeCheck())
{
// Re-compute pivot position when changes are made.
if (anchorPosition_prop.enumValueIndex != 9)
pivot_prop.vector2Value = GetAnchorPosition(anchorPosition_prop.enumValueIndex);
m_textContainer.hasChanged = true;
}
serializedObject.ApplyModifiedProperties();
EditorGUILayout.Space();
}
private void DrawDimensionProperty(SerializedProperty property, string label)
{
float old_LabelWidth = EditorGUIUtility.labelWidth;
float old_FieldWidth = EditorGUIUtility.fieldWidth;
Rect rect = EditorGUILayout.GetControlRect(false, 18);
Rect pos0 = new Rect(rect.x, rect.y + 2, rect.width, 18);
float width = rect.width + 3;
pos0.width = old_LabelWidth;
GUI.Label(pos0, label);
Rect rectangle = property.rectValue;
float width_B = width - old_LabelWidth;
float fieldWidth = width_B / 4;
pos0.width = fieldWidth - 5;
pos0.x = old_LabelWidth + 15;
GUI.Label(pos0, "Width");
pos0.x += fieldWidth;
rectangle.width = EditorGUI.FloatField(pos0, GUIContent.none, rectangle.width);
pos0.x += fieldWidth;
GUI.Label(pos0, "Height");
pos0.x += fieldWidth;
rectangle.height = EditorGUI.FloatField(pos0, GUIContent.none, rectangle.height);
property.rectValue = rectangle;
EditorGUIUtility.labelWidth = old_LabelWidth;
EditorGUIUtility.fieldWidth = old_FieldWidth;
}
private void DrawMaginProperty(SerializedProperty property, string label)
{
float old_LabelWidth = EditorGUIUtility.labelWidth;
float old_FieldWidth = EditorGUIUtility.fieldWidth;
Rect rect = EditorGUILayout.GetControlRect(false, 2 * 18);
Rect pos0 = new Rect(rect.x, rect.y + 2, rect.width, 18);
float width = rect.width + 3;
pos0.width = old_LabelWidth;
GUI.Label(pos0, label);
//Vector4 vec = property.vector4Value;
Vector4 vec = Vector4.zero;
vec.x = property.FindPropertyRelative("x").floatValue;
vec.y = property.FindPropertyRelative("y").floatValue;
vec.z = property.FindPropertyRelative("z").floatValue;
vec.w = property.FindPropertyRelative("w").floatValue;
float widthB = width - old_LabelWidth;
float fieldWidth = widthB / 4;
pos0.width = fieldWidth - 5;
// Labels
pos0.x = old_LabelWidth + 15;
GUI.Label(pos0, "Left");
pos0.x += fieldWidth;
GUI.Label(pos0, "Top");
pos0.x += fieldWidth;
GUI.Label(pos0, "Right");
pos0.x += fieldWidth;
GUI.Label(pos0, "Bottom");
pos0.y += 18;
pos0.x = old_LabelWidth + 15;
vec.x = EditorGUI.FloatField(pos0, GUIContent.none, vec.x);
pos0.x += fieldWidth;
vec.y = EditorGUI.FloatField(pos0, GUIContent.none, vec.y);
pos0.x += fieldWidth;
vec.z = EditorGUI.FloatField(pos0, GUIContent.none, vec.z);
pos0.x += fieldWidth;
vec.w = EditorGUI.FloatField(pos0, GUIContent.none, vec.w);
//property.vector4Value = vec;
property.FindPropertyRelative("x").floatValue = vec.x;
property.FindPropertyRelative("y").floatValue = vec.y;
property.FindPropertyRelative("z").floatValue = vec.z;
property.FindPropertyRelative("w").floatValue = vec.w;
EditorGUIUtility.labelWidth = old_LabelWidth;
EditorGUIUtility.fieldWidth = old_FieldWidth;
}
Vector2 GetAnchorPosition(int index)
{
Vector2 anchorPosition = Vector2.zero;
switch (index)
{
case 0: // TOP LEFT
anchorPosition = new Vector2(0, 1);
break;
case 1: // TOP
anchorPosition = new Vector2(0.5f, 1);
break;
case 2: // TOP RIGHT
anchorPosition = new Vector2(1, 1);
break;
case 3: // LEFT
anchorPosition = new Vector2(0, 0.5f);
break;
case 4: // MIDDLE
anchorPosition = new Vector2(0.5f, 0.5f);
break;
case 5: // RIGHT
anchorPosition = new Vector2(1, 0.5f);
break;
case 6: // BOTTOM LEFT
anchorPosition = new Vector2(0, 0);
break;
case 7: // BOTTOM
anchorPosition = new Vector2(0.5f, 0);
break;
case 8: // BOTTOM RIGHT
anchorPosition = new Vector2(1, 0);
break;
}
return anchorPosition;
}
}
}

View File

@@ -0,0 +1,91 @@
using System;
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace TMPro.EditorUtilities
{
/// <summary>
/// Asset post processor used to handle text assets changes.
/// This includes tracking of changes to textures used by sprite assets as well as font assets potentially getting updated outside of the Unity editor.
/// </summary>
public class TMPro_TexturePostProcessor : AssetPostprocessor
{
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (var asset in importedAssets)
{
// Return if imported asset path is outside of the project.
if (asset.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase) == false)
continue;
if (AssetDatabase.GetMainAssetTypeAtPath(asset) == typeof(TMP_FontAsset))
{
TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath(asset, typeof(TMP_FontAsset)) as TMP_FontAsset;
if (fontAsset != null)
TMP_EditorResourceManager.RegisterFontAssetForDefinitionRefresh(fontAsset);
}
if (AssetDatabase.GetMainAssetTypeAtPath(asset) == typeof(Texture2D))
{
Texture2D tex = AssetDatabase.LoadAssetAtPath(asset, typeof(Texture2D)) as Texture2D;
if (tex != null)
TMPro_EventManager.ON_SPRITE_ASSET_PROPERTY_CHANGED(true, tex);
}
}
}
}
//public class TMPro_PackageImportPostProcessor : AssetPostprocessor
//{
// static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
// {
// for (int i = 0; i < importedAssets.Length; i++)
// {
// if (importedAssets[i].Contains("TextMesh Pro/Resources/TMP Settings.asset"))
// {
// Debug.Log("New TMP Settings file was just imported.");
// // TMP Settings file was just re-imported.
// // Check if project already contains
// }
// if (importedAssets[i].Contains("com.unity.TextMeshPro/Examples"))
// {
// //Debug.Log("New TMP Examples folder was just imported.");
// }
// //Debug.Log("[" + importedAssets[i] + "] was just imported.");
// }
// //for (int i = 0; i < deletedAssets.Length; i++)
// //{
// // if (deletedAssets[i] == "Assets/TextMesh Pro")
// // {
// // //Debug.Log("Asset [" + deletedAssets[i] + "] has been deleted.");
// // string currentBuildSettings = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
// // //Check for and inject TMP_PRESENT
// // if (currentBuildSettings.Contains("TMP_PRESENT;"))
// // {
// // currentBuildSettings = currentBuildSettings.Replace("TMP_PRESENT;", "");
// // PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, currentBuildSettings);
// // }
// // else if (currentBuildSettings.Contains("TMP_PRESENT"))
// // {
// // currentBuildSettings = currentBuildSettings.Replace("TMP_PRESENT", "");
// // PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, currentBuildSettings);
// // }
// // }
// //}
// }
//}
}

View File

@@ -0,0 +1,23 @@
{
"name": "Unity.TextMeshPro.Editor",
"references": [
"Unity.TextMeshPro"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.textcore",
"expression": "1.0.0-preview.0",
"define": "TEXTCORE_1_0_OR_NEWER"
}
],
"noEngineReferences": false
}

View File

@@ -0,0 +1,11 @@
using System.Runtime.CompilerServices;
// Allow internal visibility for testing purposes.
[assembly: InternalsVisibleTo("Unity.TextCore")]
[assembly: InternalsVisibleTo("Unity.FontEngine.Tests")]
#if UNITY_EDITOR
[assembly: InternalsVisibleTo("Unity.TextCore.Editor")]
[assembly: InternalsVisibleTo("Unity.TextMeshPro.Editor")]
#endif

View File

@@ -0,0 +1,150 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace TMPro
{
public class FastAction
{
LinkedList<System.Action> delegates = new LinkedList<System.Action>();
Dictionary<System.Action, LinkedListNode<System.Action>> lookup = new Dictionary<System.Action, LinkedListNode<System.Action>>();
public void Add(System.Action rhs)
{
if (lookup.ContainsKey(rhs)) return;
lookup[rhs] = delegates.AddLast(rhs);
}
public void Remove(System.Action rhs)
{
LinkedListNode<System.Action> node;
if (lookup.TryGetValue(rhs, out node))
{
lookup.Remove(rhs);
delegates.Remove(node);
}
}
public void Call()
{
var node = delegates.First;
while (node != null)
{
node.Value();
node = node.Next;
}
}
}
public class FastAction<A>
{
LinkedList<System.Action<A>> delegates = new LinkedList<System.Action<A>>();
Dictionary<System.Action<A>, LinkedListNode<System.Action<A>>> lookup = new Dictionary<System.Action<A>, LinkedListNode<System.Action<A>>>();
public void Add(System.Action<A> rhs)
{
if (lookup.ContainsKey(rhs)) return;
lookup[rhs] = delegates.AddLast(rhs);
}
public void Remove(System.Action<A> rhs)
{
LinkedListNode<System.Action<A>> node;
if (lookup.TryGetValue(rhs, out node))
{
lookup.Remove(rhs);
delegates.Remove(node);
}
}
public void Call(A a)
{
var node = delegates.First;
while (node != null)
{
node.Value(a);
node = node.Next;
}
}
}
public class FastAction<A, B>
{
LinkedList<System.Action<A, B>> delegates = new LinkedList<System.Action<A, B>>();
Dictionary<System.Action<A, B>, LinkedListNode<System.Action<A, B>>> lookup = new Dictionary<System.Action<A, B>, LinkedListNode<System.Action<A, B>>>();
public void Add(System.Action<A, B> rhs)
{
if (lookup.ContainsKey(rhs)) return;
lookup[rhs] = delegates.AddLast(rhs);
}
public void Remove(System.Action<A, B> rhs)
{
LinkedListNode<System.Action<A, B>> node;
if (lookup.TryGetValue(rhs, out node))
{
lookup.Remove(rhs);
delegates.Remove(node);
}
}
public void Call(A a, B b)
{
var node = delegates.First;
while (node != null)
{
node.Value(a, b);
node = node.Next;
}
}
}
public class FastAction<A, B, C>
{
LinkedList<System.Action<A, B, C>> delegates = new LinkedList<System.Action<A, B, C>>();
Dictionary<System.Action<A, B, C>, LinkedListNode<System.Action<A, B, C>>> lookup = new Dictionary<System.Action<A, B, C>, LinkedListNode<System.Action<A, B, C>>>();
public void Add(System.Action<A, B, C> rhs)
{
if (lookup.ContainsKey(rhs)) return;
lookup[rhs] = delegates.AddLast(rhs);
}
public void Remove(System.Action<A, B, C> rhs)
{
LinkedListNode<System.Action<A, B, C>> node;
if (lookup.TryGetValue(rhs, out node))
{
lookup.Remove(rhs);
delegates.Remove(node);
}
}
public void Call(A a, B b, C c)
{
var node = delegates.First;
while (node != null)
{
node.Value(a, b, c);
node = node.Next;
}
}
}
}

View File

@@ -0,0 +1,17 @@

namespace TMPro
{
/// <summary>
/// Interface used for preprocessing and shaping of text.
/// </summary>
public interface ITextPreprocessor
{
/// <summary>
/// Function used for preprocessing of text
/// </summary>
/// <param name="text">Source text to be processed</param>
/// <returns>Processed text</returns>
string PreprocessText(string text);
}
}

View File

@@ -0,0 +1,625 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace TMPro
{
public class MaterialReferenceManager
{
private static MaterialReferenceManager s_Instance;
// Dictionaries used to track Asset references.
private Dictionary<int, Material> m_FontMaterialReferenceLookup = new Dictionary<int, Material>();
private Dictionary<int, TMP_FontAsset> m_FontAssetReferenceLookup = new Dictionary<int, TMP_FontAsset>();
private Dictionary<int, TMP_SpriteAsset> m_SpriteAssetReferenceLookup = new Dictionary<int, TMP_SpriteAsset>();
private Dictionary<int, TMP_ColorGradient> m_ColorGradientReferenceLookup = new Dictionary<int, TMP_ColorGradient>();
/// <summary>
/// Get a singleton instance of the registry
/// </summary>
public static MaterialReferenceManager instance
{
get
{
if (MaterialReferenceManager.s_Instance == null)
MaterialReferenceManager.s_Instance = new MaterialReferenceManager();
return MaterialReferenceManager.s_Instance;
}
}
/// <summary>
/// Add new font asset reference to dictionary.
/// </summary>
/// <param name="fontAsset"></param>
public static void AddFontAsset(TMP_FontAsset fontAsset)
{
MaterialReferenceManager.instance.AddFontAssetInternal(fontAsset);
}
/// <summary>
/// Add new Font Asset reference to dictionary.
/// </summary>
/// <param name="fontAsset"></param>
private void AddFontAssetInternal(TMP_FontAsset fontAsset)
{
if (m_FontAssetReferenceLookup.ContainsKey(fontAsset.hashCode)) return;
// Add reference to the font asset.
m_FontAssetReferenceLookup.Add(fontAsset.hashCode, fontAsset);
// Add reference to the font material.
m_FontMaterialReferenceLookup.Add(fontAsset.materialHashCode, fontAsset.material);
}
/// <summary>
/// Add new Sprite Asset to dictionary.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="spriteAsset"></param>
public static void AddSpriteAsset(TMP_SpriteAsset spriteAsset)
{
MaterialReferenceManager.instance.AddSpriteAssetInternal(spriteAsset);
}
/// <summary>
/// Internal method to add a new sprite asset to the dictionary.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="spriteAsset"></param>
private void AddSpriteAssetInternal(TMP_SpriteAsset spriteAsset)
{
if (m_SpriteAssetReferenceLookup.ContainsKey(spriteAsset.hashCode)) return;
// Add reference to sprite asset.
m_SpriteAssetReferenceLookup.Add(spriteAsset.hashCode, spriteAsset);
// Adding reference to the sprite asset material as well
m_FontMaterialReferenceLookup.Add(spriteAsset.hashCode, spriteAsset.material);
}
/// <summary>
/// Add new Sprite Asset to dictionary.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="spriteAsset"></param>
public static void AddSpriteAsset(int hashCode, TMP_SpriteAsset spriteAsset)
{
MaterialReferenceManager.instance.AddSpriteAssetInternal(hashCode, spriteAsset);
}
/// <summary>
/// Internal method to add a new sprite asset to the dictionary.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="spriteAsset"></param>
private void AddSpriteAssetInternal(int hashCode, TMP_SpriteAsset spriteAsset)
{
if (m_SpriteAssetReferenceLookup.ContainsKey(hashCode)) return;
// Add reference to Sprite Asset.
m_SpriteAssetReferenceLookup.Add(hashCode, spriteAsset);
// Add reference to Sprite Asset using the asset hashcode.
m_FontMaterialReferenceLookup.Add(hashCode, spriteAsset.material);
// Compatibility check
if (spriteAsset.hashCode == 0) spriteAsset.hashCode = hashCode;
}
/// <summary>
/// Add new Material reference to dictionary.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="material"></param>
public static void AddFontMaterial(int hashCode, Material material)
{
MaterialReferenceManager.instance.AddFontMaterialInternal(hashCode, material);
}
/// <summary>
/// Add new material reference to dictionary.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="material"></param>
private void AddFontMaterialInternal(int hashCode, Material material)
{
// Since this function is called after checking if the material is
// contained in the dictionary, there is no need to check again.
m_FontMaterialReferenceLookup.Add(hashCode, material);
}
/// <summary>
/// Add new Color Gradient Preset to dictionary.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="spriteAsset"></param>
public static void AddColorGradientPreset(int hashCode, TMP_ColorGradient spriteAsset)
{
MaterialReferenceManager.instance.AddColorGradientPreset_Internal(hashCode, spriteAsset);
}
/// <summary>
/// Internal method to add a new Color Gradient Preset to the dictionary.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="spriteAsset"></param>
private void AddColorGradientPreset_Internal(int hashCode, TMP_ColorGradient spriteAsset)
{
if (m_ColorGradientReferenceLookup.ContainsKey(hashCode)) return;
// Add reference to Color Gradient Preset Asset.
m_ColorGradientReferenceLookup.Add(hashCode, spriteAsset);
}
/// <summary>
/// Add new material reference and return the index of this new reference in the materialReferences array.
/// </summary>
/// <param name="material"></param>
/// <param name="materialHashCode"></param>
/// <param name="fontAsset"></param>
//public int AddMaterial(Material material, int materialHashCode, TMP_FontAsset fontAsset)
//{
// if (!m_MaterialReferenceLookup.ContainsKey(materialHashCode))
// {
// int index = m_MaterialReferenceLookup.Count;
// materialReferences[index].fontAsset = fontAsset;
// materialReferences[index].material = material;
// materialReferences[index].isDefaultMaterial = material.GetInstanceID() == fontAsset.material.GetInstanceID() ? true : false;
// materialReferences[index].index = index;
// materialReferences[index].referenceCount = 0;
// m_MaterialReferenceLookup[materialHashCode] = index;
// // Compute Padding value and store it
// // TODO
// int fontAssetHashCode = fontAsset.hashCode;
// if (!m_FontAssetReferenceLookup.ContainsKey(fontAssetHashCode))
// m_FontAssetReferenceLookup.Add(fontAssetHashCode, fontAsset);
// m_countInternal += 1;
// return index;
// }
// else
// {
// return m_MaterialReferenceLookup[materialHashCode];
// }
//}
/// <summary>
/// Add new material reference and return the index of this new reference in the materialReferences array.
/// </summary>
/// <param name="material"></param>
/// <param name="materialHashCode"></param>
/// <param name="spriteAsset"></param>
/// <returns></returns>
//public int AddMaterial(Material material, int materialHashCode, TMP_SpriteAsset spriteAsset)
//{
// if (!m_MaterialReferenceLookup.ContainsKey(materialHashCode))
// {
// int index = m_MaterialReferenceLookup.Count;
// materialReferences[index].fontAsset = materialReferences[0].fontAsset;
// materialReferences[index].spriteAsset = spriteAsset;
// materialReferences[index].material = material;
// materialReferences[index].isDefaultMaterial = true;
// materialReferences[index].index = index;
// materialReferences[index].referenceCount = 0;
// m_MaterialReferenceLookup[materialHashCode] = index;
// int spriteAssetHashCode = spriteAsset.hashCode;
// if (!m_SpriteAssetReferenceLookup.ContainsKey(spriteAssetHashCode))
// m_SpriteAssetReferenceLookup.Add(spriteAssetHashCode, spriteAsset);
// m_countInternal += 1;
// return index;
// }
// else
// {
// return m_MaterialReferenceLookup[materialHashCode];
// }
//}
/// <summary>
/// Function to check if the font asset is already referenced.
/// </summary>
/// <param name="font"></param>
/// <returns></returns>
public bool Contains(TMP_FontAsset font)
{
return m_FontAssetReferenceLookup.ContainsKey(font.hashCode);
}
/// <summary>
/// Function to check if the sprite asset is already referenced.
/// </summary>
/// <param name="font"></param>
/// <returns></returns>
public bool Contains(TMP_SpriteAsset sprite)
{
return m_FontAssetReferenceLookup.ContainsKey(sprite.hashCode);
}
/// <summary>
/// Function returning the Font Asset corresponding to the provided hash code.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="fontAsset"></param>
/// <returns></returns>
public static bool TryGetFontAsset(int hashCode, out TMP_FontAsset fontAsset)
{
return MaterialReferenceManager.instance.TryGetFontAssetInternal(hashCode, out fontAsset);
}
/// <summary>
/// Internal Function returning the Font Asset corresponding to the provided hash code.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="fontAsset"></param>
/// <returns></returns>
private bool TryGetFontAssetInternal(int hashCode, out TMP_FontAsset fontAsset)
{
fontAsset = null;
return m_FontAssetReferenceLookup.TryGetValue(hashCode, out fontAsset);
}
/// <summary>
/// Function returning the Sprite Asset corresponding to the provided hash code.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="spriteAsset"></param>
/// <returns></returns>
public static bool TryGetSpriteAsset(int hashCode, out TMP_SpriteAsset spriteAsset)
{
return MaterialReferenceManager.instance.TryGetSpriteAssetInternal(hashCode, out spriteAsset);
}
/// <summary>
/// Internal function returning the Sprite Asset corresponding to the provided hash code.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="fontAsset"></param>
/// <returns></returns>
private bool TryGetSpriteAssetInternal(int hashCode, out TMP_SpriteAsset spriteAsset)
{
spriteAsset = null;
return m_SpriteAssetReferenceLookup.TryGetValue(hashCode, out spriteAsset);
}
/// <summary>
/// Function returning the Color Gradient Preset corresponding to the provided hash code.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="gradientPreset"></param>
/// <returns></returns>
public static bool TryGetColorGradientPreset(int hashCode, out TMP_ColorGradient gradientPreset)
{
return MaterialReferenceManager.instance.TryGetColorGradientPresetInternal(hashCode, out gradientPreset);
}
/// <summary>
/// Internal function returning the Color Gradient Preset corresponding to the provided hash code.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="fontAsset"></param>
/// <returns></returns>
private bool TryGetColorGradientPresetInternal(int hashCode, out TMP_ColorGradient gradientPreset)
{
gradientPreset = null;
return m_ColorGradientReferenceLookup.TryGetValue(hashCode, out gradientPreset);
}
/// <summary>
/// Function returning the Font Material corresponding to the provided hash code.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="material"></param>
/// <returns></returns>
public static bool TryGetMaterial(int hashCode, out Material material)
{
return MaterialReferenceManager.instance.TryGetMaterialInternal(hashCode, out material);
}
/// <summary>
/// Internal function returning the Font Material corresponding to the provided hash code.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="material"></param>
/// <returns></returns>
private bool TryGetMaterialInternal(int hashCode, out Material material)
{
material = null;
return m_FontMaterialReferenceLookup.TryGetValue(hashCode, out material);
}
/// <summary>
/// Function to lookup a material based on hash code and returning the MaterialReference containing this material.
/// </summary>
/// <param name="hashCode"></param>
/// <param name="material"></param>
/// <returns></returns>
//public bool TryGetMaterial(int hashCode, out MaterialReference materialReference)
//{
// int materialIndex = -1;
// if (m_MaterialReferenceLookup.TryGetValue(hashCode, out materialIndex))
// {
// materialReference = materialReferences[materialIndex];
// return true;
// }
// materialReference = new MaterialReference();
// return false;
//}
/// <summary>
///
/// </summary>
/// <param name="fontAsset"></param>
/// <returns></returns>
//public int GetMaterialIndex(TMP_FontAsset fontAsset)
//{
// if (m_MaterialReferenceLookup.ContainsKey(fontAsset.materialHashCode))
// return m_MaterialReferenceLookup[fontAsset.materialHashCode];
// return -1;
//}
/// <summary>
///
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
//public TMP_FontAsset GetFontAsset(int index)
//{
// if (index >= 0 && index < materialReferences.Length)
// return materialReferences[index].fontAsset;
// return null;
//}
/// <summary>
///
/// </summary>
/// <param name="material"></param>
/// <param name="materialHashCode"></param>
/// <param name="fontAsset"></param>
//public void SetDefaultMaterial(Material material, int materialHashCode, TMP_FontAsset fontAsset)
//{
// if (!m_MaterialReferenceLookup.ContainsKey(materialHashCode))
// {
// materialReferences[0].fontAsset = fontAsset;
// materialReferences[0].material = material;
// materialReferences[0].index = 0;
// materialReferences[0].isDefaultMaterial = material.GetInstanceID() == fontAsset.material.GetInstanceID() ? true : false;
// materialReferences[0].referenceCount = 0;
// m_MaterialReferenceLookup[materialHashCode] = 0;
// // Compute Padding value and store it
// // TODO
// int fontHashCode = fontAsset.hashCode;
// if (!m_FontAssetReferenceLookup.ContainsKey(fontHashCode))
// m_FontAssetReferenceLookup.Add(fontHashCode, fontAsset);
// }
// else
// {
// materialReferences[0].fontAsset = fontAsset;
// materialReferences[0].material = material;
// materialReferences[0].index = 0;
// materialReferences[0].referenceCount = 0;
// m_MaterialReferenceLookup[materialHashCode] = 0;
// }
// // Compute padding
// // TODO
// m_countInternal = 1;
//}
/// <summary>
///
/// </summary>
//public void Clear()
//{
// //m_currentIndex = 0;
// m_MaterialReferenceLookup.Clear();
// m_SpriteAssetReferenceLookup.Clear();
// m_FontAssetReferenceLookup.Clear();
//}
/// <summary>
/// Function to clear the reference count for each of the material references.
/// </summary>
//public void ClearReferenceCount()
//{
// m_countInternal = 0;
// for (int i = 0; i < materialReferences.Length; i++)
// {
// if (materialReferences[i].fontAsset == null)
// return;
// materialReferences[i].referenceCount = 0;
// }
//}
}
public struct TMP_MaterialReference
{
public Material material;
public int referenceCount;
}
public struct MaterialReference
{
public int index;
public TMP_FontAsset fontAsset;
public TMP_SpriteAsset spriteAsset;
public Material material;
public bool isDefaultMaterial;
public bool isFallbackMaterial;
public Material fallbackMaterial;
public float padding;
public int referenceCount;
/// <summary>
/// Constructor for new Material Reference.
/// </summary>
/// <param name="index"></param>
/// <param name="fontAsset"></param>
/// <param name="spriteAsset"></param>
/// <param name="material"></param>
/// <param name="padding"></param>
public MaterialReference(int index, TMP_FontAsset fontAsset, TMP_SpriteAsset spriteAsset, Material material, float padding)
{
this.index = index;
this.fontAsset = fontAsset;
this.spriteAsset = spriteAsset;
this.material = material;
this.isDefaultMaterial = material.GetInstanceID() == fontAsset.material.GetInstanceID() ? true : false;
this.isFallbackMaterial = false;
this.fallbackMaterial = null;
this.padding = padding;
this.referenceCount = 0;
}
/// <summary>
/// Function to check if a certain font asset is contained in the material reference array.
/// </summary>
/// <param name="materialReferences"></param>
/// <param name="fontAsset"></param>
/// <returns></returns>
public static bool Contains(MaterialReference[] materialReferences, TMP_FontAsset fontAsset)
{
int id = fontAsset.GetInstanceID();
for (int i = 0; i < materialReferences.Length && materialReferences[i].fontAsset != null; i++)
{
if (materialReferences[i].fontAsset.GetInstanceID() == id)
return true;
}
return false;
}
/// <summary>
/// Function to add a new material reference and returning its index in the material reference array.
/// </summary>
/// <param name="material"></param>
/// <param name="fontAsset"></param>
/// <param name="materialReferences"></param>
/// <param name="materialReferenceIndexLookup"></param>
/// <returns></returns>
public static int AddMaterialReference(Material material, TMP_FontAsset fontAsset, ref MaterialReference[] materialReferences, Dictionary<int, int> materialReferenceIndexLookup)
{
int materialID = material.GetInstanceID();
int index;
if (materialReferenceIndexLookup.TryGetValue(materialID, out index))
return index;
index = materialReferenceIndexLookup.Count;
// Add new reference index
materialReferenceIndexLookup[materialID] = index;
if (index >= materialReferences.Length)
System.Array.Resize(ref materialReferences, Mathf.NextPowerOfTwo(index + 1));
materialReferences[index].index = index;
materialReferences[index].fontAsset = fontAsset;
materialReferences[index].spriteAsset = null;
materialReferences[index].material = material;
materialReferences[index].isDefaultMaterial = materialID == fontAsset.material.GetInstanceID() ? true : false;
//materialReferences[index].padding = 0;
materialReferences[index].referenceCount = 0;
return index;
}
/// <summary>
///
/// </summary>
/// <param name="material"></param>
/// <param name="spriteAsset"></param>
/// <param name="materialReferences"></param>
/// <param name="materialReferenceIndexLookup"></param>
/// <returns></returns>
public static int AddMaterialReference(Material material, TMP_SpriteAsset spriteAsset, ref MaterialReference[] materialReferences, Dictionary<int, int> materialReferenceIndexLookup)
{
int materialID = material.GetInstanceID();
int index;
if (materialReferenceIndexLookup.TryGetValue(materialID, out index))
return index;
index = materialReferenceIndexLookup.Count;
// Add new reference index
materialReferenceIndexLookup[materialID] = index;
if (index >= materialReferences.Length)
System.Array.Resize(ref materialReferences, Mathf.NextPowerOfTwo(index + 1));
materialReferences[index].index = index;
materialReferences[index].fontAsset = materialReferences[0].fontAsset;
materialReferences[index].spriteAsset = spriteAsset;
materialReferences[index].material = material;
materialReferences[index].isDefaultMaterial = true;
//materialReferences[index].padding = 0;
materialReferences[index].referenceCount = 0;
return index;
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using UnityEngine;
namespace TMPro
{
// Base class inherited by the various TextMeshPro Assets.
[Serializable]
public abstract class TMP_Asset : ScriptableObject
{
/// <summary>
/// Instance ID of the TMP Asset
/// </summary>
public int instanceID
{
get
{
if (m_InstanceID == 0)
m_InstanceID = GetInstanceID();
return m_InstanceID;
}
}
private int m_InstanceID;
/// <summary>
/// HashCode based on the name of the asset.
/// </summary>
public int hashCode;
/// <summary>
/// The material used by this asset.
/// </summary>
public Material material;
/// <summary>
/// HashCode based on the name of the material assigned to this asset.
/// </summary>
public int materialHashCode;
}
}

View File

@@ -0,0 +1,70 @@
using System;
using UnityEngine.TextCore;
namespace TMPro
{
/// <summary>
/// A basic element of text.
/// </summary>
[Serializable]
public class TMP_Character : TMP_TextElement
{
/// <summary>
/// Default constructor.
/// </summary>
public TMP_Character()
{
m_ElementType = TextElementType.Character;
this.scale = 1.0f;
}
/// <summary>
/// Constructor for new character
/// </summary>
/// <param name="unicode">Unicode value.</param>
/// <param name="glyph">Glyph</param>
public TMP_Character(uint unicode, Glyph glyph)
{
m_ElementType = TextElementType.Character;
this.unicode = unicode;
this.textAsset = null;
this.glyph = glyph;
this.glyphIndex = glyph.index;
this.scale = 1.0f;
}
/// <summary>
/// Constructor for new character
/// </summary>
/// <param name="unicode">Unicode value.</param>
/// <param name="fontAsset">The font asset to which this character belongs.</param>
/// <param name="glyph">Glyph</param>
public TMP_Character(uint unicode, TMP_FontAsset fontAsset, Glyph glyph)
{
m_ElementType = TextElementType.Character;
this.unicode = unicode;
this.textAsset = fontAsset;
this.glyph = glyph;
this.glyphIndex = glyph.index;
this.scale = 1.0f;
}
/// <summary>
/// Constructor for new character
/// </summary>
/// <param name="unicode">Unicode value.</param>
/// <param name="glyphIndex">Glyph index.</param>
internal TMP_Character(uint unicode, uint glyphIndex)
{
m_ElementType = TextElementType.Character;
this.unicode = unicode;
this.textAsset = null;
this.glyph = null;
this.glyphIndex = glyphIndex;
this.scale = 1.0f;
}
}
}

View File

@@ -0,0 +1,220 @@
using System.Diagnostics;
using UnityEngine;
namespace TMPro
{
public struct TMP_Vertex
{
public Vector3 position;
public Vector2 uv;
public Vector2 uv2;
public Vector2 uv4;
public Color32 color;
public static TMP_Vertex zero { get { return k_Zero; } }
//public Vector3 normal;
//public Vector4 tangent;
static readonly TMP_Vertex k_Zero = new TMP_Vertex();
}
/// <summary>
///
/// </summary>
public struct TMP_Offset
{
public float left { get { return m_Left; } set { m_Left = value; } }
public float right { get { return m_Right; } set { m_Right = value; } }
public float top { get { return m_Top; } set { m_Top = value; } }
public float bottom { get { return m_Bottom; } set { m_Bottom = value; } }
public float horizontal { get { return m_Left; } set { m_Left = value; m_Right = value; } }
public float vertical { get { return m_Top; } set { m_Top = value; m_Bottom = value; } }
/// <summary>
///
/// </summary>
public static TMP_Offset zero { get { return k_ZeroOffset; } }
// =============================================
// Private backing fields for public properties.
// =============================================
float m_Left;
float m_Right;
float m_Top;
float m_Bottom;
static readonly TMP_Offset k_ZeroOffset = new TMP_Offset(0F, 0F, 0F, 0F);
/// <summary>
///
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
/// <param name="top"></param>
/// <param name="bottom"></param>
public TMP_Offset(float left, float right, float top, float bottom)
{
m_Left = left;
m_Right = right;
m_Top = top;
m_Bottom = bottom;
}
/// <summary>
///
/// </summary>
/// <param name="horizontal"></param>
/// <param name="vertical"></param>
public TMP_Offset(float horizontal, float vertical)
{
m_Left = horizontal;
m_Right = horizontal;
m_Top = vertical;
m_Bottom = vertical;
}
public static bool operator ==(TMP_Offset lhs, TMP_Offset rhs)
{
return lhs.m_Left == rhs.m_Left &&
lhs.m_Right == rhs.m_Right &&
lhs.m_Top == rhs.m_Top &&
lhs.m_Bottom == rhs.m_Bottom;
}
public static bool operator !=(TMP_Offset lhs, TMP_Offset rhs)
{
return !(lhs == rhs);
}
public static TMP_Offset operator *(TMP_Offset a, float b)
{
return new TMP_Offset(a.m_Left * b, a.m_Right * b, a.m_Top * b, a.m_Bottom * b);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public bool Equals(TMP_Offset other)
{
return base.Equals(other);
}
}
/// <summary>
///
/// </summary>
public struct HighlightState
{
public Color32 color;
public TMP_Offset padding;
public HighlightState(Color32 color, TMP_Offset padding)
{
this.color = color;
this.padding = padding;
}
public static bool operator ==(HighlightState lhs, HighlightState rhs)
{
return lhs.color.Compare(rhs.color) && lhs.padding == rhs.padding;
}
public static bool operator !=(HighlightState lhs, HighlightState rhs)
{
return !(lhs == rhs);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public bool Equals(HighlightState other)
{
return base.Equals(other);
}
}
/// <summary>
/// Structure containing information about individual text elements (character or sprites).
/// </summary>
[DebuggerDisplay("Unicode '{character}' ({((uint)character).ToString(\"X\")})")]
public struct TMP_CharacterInfo
{
public char character; // Should be changed to an uint to handle UTF32
/// <summary>
/// Index of the character in the raw string.
/// </summary>
public int index; // Index of the character in the input string.
public int stringLength;
public TMP_TextElementType elementType;
public TMP_TextElement textElement;
public TMP_FontAsset fontAsset;
public TMP_SpriteAsset spriteAsset;
public int spriteIndex;
public Material material;
public int materialReferenceIndex;
public bool isUsingAlternateTypeface;
public float pointSize;
//public short wordNumber;
public int lineNumber;
//public short charNumber;
public int pageNumber;
public int vertexIndex;
public TMP_Vertex vertex_BL;
public TMP_Vertex vertex_TL;
public TMP_Vertex vertex_TR;
public TMP_Vertex vertex_BR;
public Vector3 topLeft;
public Vector3 bottomLeft;
public Vector3 topRight;
public Vector3 bottomRight;
public float origin;
public float xAdvance;
public float ascender;
public float baseLine;
public float descender;
internal float adjustedAscender;
internal float adjustedDescender;
public float aspectRatio;
public float scale;
public Color32 color;
public Color32 underlineColor;
public int underlineVertexIndex;
public Color32 strikethroughColor;
public int strikethroughVertexIndex;
public Color32 highlightColor;
public HighlightState highlightState;
public FontStyles style;
public bool isVisible;
//public bool isIgnoringAlignment;
}
}

View File

@@ -0,0 +1,68 @@
using UnityEngine;
using System.Collections;
namespace TMPro
{
public enum ColorMode
{
Single,
HorizontalGradient,
VerticalGradient,
FourCornersGradient
}
[System.Serializable][ExcludeFromPresetAttribute]
public class TMP_ColorGradient : ScriptableObject
{
public ColorMode colorMode = ColorMode.FourCornersGradient;
public Color topLeft;
public Color topRight;
public Color bottomLeft;
public Color bottomRight;
const ColorMode k_DefaultColorMode = ColorMode.FourCornersGradient;
static readonly Color k_DefaultColor = Color.white;
/// <summary>
/// Default Constructor which sets each of the colors as white.
/// </summary>
public TMP_ColorGradient()
{
colorMode = k_DefaultColorMode;
topLeft = k_DefaultColor;
topRight = k_DefaultColor;
bottomLeft = k_DefaultColor;
bottomRight = k_DefaultColor;
}
/// <summary>
/// Constructor allowing to set the default color of the Color Gradient.
/// </summary>
/// <param name="color"></param>
public TMP_ColorGradient(Color color)
{
colorMode = k_DefaultColorMode;
topLeft = color;
topRight = color;
bottomLeft = color;
bottomRight = color;
}
/// <summary>
/// The vertex colors at the corners of the characters.
/// </summary>
/// <param name="color0">Top left color.</param>
/// <param name="color1">Top right color.</param>
/// <param name="color2">Bottom left color.</param>
/// <param name="color3">Bottom right color.</param>
public TMP_ColorGradient(Color color0, Color color1, Color color2, Color color3)
{
colorMode = k_DefaultColorMode;
this.topLeft = color0;
this.topRight = color1;
this.bottomLeft = color2;
this.bottomRight = color3;
}
}
}

View File

@@ -0,0 +1,74 @@
using UnityEngine;
using System.Collections;
namespace TMPro
{
// Class used to convert scenes and objects saved in version 0.1.44 to the new Text Container
public static class TMP_Compatibility
{
public enum AnchorPositions { TopLeft, Top, TopRight, Left, Center, Right, BottomLeft, Bottom, BottomRight, BaseLine, None };
/// <summary>
/// Function used to convert text alignment option enumeration format.
/// </summary>
/// <param name="oldValue"></param>
/// <returns></returns>
public static TextAlignmentOptions ConvertTextAlignmentEnumValues(TextAlignmentOptions oldValue)
{
switch ((int)oldValue)
{
case 0:
return TextAlignmentOptions.TopLeft;
case 1:
return TextAlignmentOptions.Top;
case 2:
return TextAlignmentOptions.TopRight;
case 3:
return TextAlignmentOptions.TopJustified;
case 4:
return TextAlignmentOptions.Left;
case 5:
return TextAlignmentOptions.Center;
case 6:
return TextAlignmentOptions.Right;
case 7:
return TextAlignmentOptions.Justified;
case 8:
return TextAlignmentOptions.BottomLeft;
case 9:
return TextAlignmentOptions.Bottom;
case 10:
return TextAlignmentOptions.BottomRight;
case 11:
return TextAlignmentOptions.BottomJustified;
case 12:
return TextAlignmentOptions.BaselineLeft;
case 13:
return TextAlignmentOptions.Baseline;
case 14:
return TextAlignmentOptions.BaselineRight;
case 15:
return TextAlignmentOptions.BaselineJustified;
case 16:
return TextAlignmentOptions.MidlineLeft;
case 17:
return TextAlignmentOptions.Midline;
case 18:
return TextAlignmentOptions.MidlineRight;
case 19:
return TextAlignmentOptions.MidlineJustified;
case 20:
return TextAlignmentOptions.CaplineLeft;
case 21:
return TextAlignmentOptions.Capline;
case 22:
return TextAlignmentOptions.CaplineRight;
case 23:
return TextAlignmentOptions.CaplineJustified;
}
return TextAlignmentOptions.TopLeft;
}
}
}

View File

@@ -0,0 +1,246 @@
using UnityEngine;
using UnityEngine.Events;
using System.Collections;
namespace TMPro
{
// Base interface for tweeners,
// using an interface instead of
// an abstract class as we want the
// tweens to be structs.
internal interface ITweenValue
{
void TweenValue(float floatPercentage);
bool ignoreTimeScale { get; }
float duration { get; }
bool ValidTarget();
}
// Color tween class, receives the
// TweenValue callback and then sets
// the value on the target.
internal struct ColorTween : ITweenValue
{
public enum ColorTweenMode
{
All,
RGB,
Alpha
}
public class ColorTweenCallback : UnityEvent<Color> { }
private ColorTweenCallback m_Target;
private Color m_StartColor;
private Color m_TargetColor;
private ColorTweenMode m_TweenMode;
private float m_Duration;
private bool m_IgnoreTimeScale;
public Color startColor
{
get { return m_StartColor; }
set { m_StartColor = value; }
}
public Color targetColor
{
get { return m_TargetColor; }
set { m_TargetColor = value; }
}
public ColorTweenMode tweenMode
{
get { return m_TweenMode; }
set { m_TweenMode = value; }
}
public float duration
{
get { return m_Duration; }
set { m_Duration = value; }
}
public bool ignoreTimeScale
{
get { return m_IgnoreTimeScale; }
set { m_IgnoreTimeScale = value; }
}
public void TweenValue(float floatPercentage)
{
if (!ValidTarget())
return;
var newColor = Color.Lerp(m_StartColor, m_TargetColor, floatPercentage);
if (m_TweenMode == ColorTweenMode.Alpha)
{
newColor.r = m_StartColor.r;
newColor.g = m_StartColor.g;
newColor.b = m_StartColor.b;
}
else if (m_TweenMode == ColorTweenMode.RGB)
{
newColor.a = m_StartColor.a;
}
m_Target.Invoke(newColor);
}
public void AddOnChangedCallback(UnityAction<Color> callback)
{
if (m_Target == null)
m_Target = new ColorTweenCallback();
m_Target.AddListener(callback);
}
public bool GetIgnoreTimescale()
{
return m_IgnoreTimeScale;
}
public float GetDuration()
{
return m_Duration;
}
public bool ValidTarget()
{
return m_Target != null;
}
}
// Float tween class, receives the
// TweenValue callback and then sets
// the value on the target.
internal struct FloatTween : ITweenValue
{
public class FloatTweenCallback : UnityEvent<float> { }
private FloatTweenCallback m_Target;
private float m_StartValue;
private float m_TargetValue;
private float m_Duration;
private bool m_IgnoreTimeScale;
public float startValue
{
get { return m_StartValue; }
set { m_StartValue = value; }
}
public float targetValue
{
get { return m_TargetValue; }
set { m_TargetValue = value; }
}
public float duration
{
get { return m_Duration; }
set { m_Duration = value; }
}
public bool ignoreTimeScale
{
get { return m_IgnoreTimeScale; }
set { m_IgnoreTimeScale = value; }
}
public void TweenValue(float floatPercentage)
{
if (!ValidTarget())
return;
var newValue = Mathf.Lerp(m_StartValue, m_TargetValue, floatPercentage);
m_Target.Invoke(newValue);
}
public void AddOnChangedCallback(UnityAction<float> callback)
{
if (m_Target == null)
m_Target = new FloatTweenCallback();
m_Target.AddListener(callback);
}
public bool GetIgnoreTimescale()
{
return m_IgnoreTimeScale;
}
public float GetDuration()
{
return m_Duration;
}
public bool ValidTarget()
{
return m_Target != null;
}
}
// Tween runner, executes the given tween.
// The coroutine will live within the given
// behaviour container.
internal class TweenRunner<T> where T : struct, ITweenValue
{
protected MonoBehaviour m_CoroutineContainer;
protected IEnumerator m_Tween;
// utility function for starting the tween
private static IEnumerator Start(T tweenInfo)
{
if (!tweenInfo.ValidTarget())
yield break;
var elapsedTime = 0.0f;
while (elapsedTime < tweenInfo.duration)
{
elapsedTime += tweenInfo.ignoreTimeScale ? Time.unscaledDeltaTime : Time.deltaTime;
var percentage = Mathf.Clamp01(elapsedTime / tweenInfo.duration);
tweenInfo.TweenValue(percentage);
yield return null;
}
tweenInfo.TweenValue(1.0f);
}
public void Init(MonoBehaviour coroutineContainer)
{
m_CoroutineContainer = coroutineContainer;
}
public void StartTween(T info)
{
if (m_CoroutineContainer == null)
{
Debug.LogWarning("Coroutine container not configured... did you forget to call Init?");
return;
}
StopTween();
if (!m_CoroutineContainer.gameObject.activeInHierarchy)
{
info.TweenValue(1.0f);
return;
}
m_Tween = Start(info);
m_CoroutineContainer.StartCoroutine(m_Tween);
}
public void StopTween()
{
if (m_Tween != null)
{
m_CoroutineContainer.StopCoroutine(m_Tween);
m_Tween = null;
}
}
}
}

View File

@@ -0,0 +1,400 @@
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace TMPro
{
public static class TMP_DefaultControls
{
public struct Resources
{
public Sprite standard;
public Sprite background;
public Sprite inputField;
public Sprite knob;
public Sprite checkmark;
public Sprite dropdown;
public Sprite mask;
}
private const float kWidth = 160f;
private const float kThickHeight = 30f;
private const float kThinHeight = 20f;
private static Vector2 s_TextElementSize = new Vector2(100f, 100f);
private static Vector2 s_ThickElementSize = new Vector2(kWidth, kThickHeight);
private static Vector2 s_ThinElementSize = new Vector2(kWidth, kThinHeight);
//private static Vector2 s_ImageElementSize = new Vector2(100f, 100f);
private static Color s_DefaultSelectableColor = new Color(1f, 1f, 1f, 1f);
//private static Color s_PanelColor = new Color(1f, 1f, 1f, 0.392f);
private static Color s_TextColor = new Color(50f / 255f, 50f / 255f, 50f / 255f, 1f);
private static GameObject CreateUIElementRoot(string name, Vector2 size)
{
GameObject child = new GameObject(name);
RectTransform rectTransform = child.AddComponent<RectTransform>();
rectTransform.sizeDelta = size;
return child;
}
static GameObject CreateUIObject(string name, GameObject parent)
{
GameObject go = new GameObject(name);
go.AddComponent<RectTransform>();
SetParentAndAlign(go, parent);
return go;
}
private static void SetDefaultTextValues(TMP_Text lbl)
{
// Set text values we want across UI elements in default controls.
// Don't set values which are the same as the default values for the Text component,
// since there's no point in that, and it's good to keep them as consistent as possible.
lbl.color = s_TextColor;
lbl.fontSize = 14;
}
private static void SetDefaultColorTransitionValues(Selectable slider)
{
ColorBlock colors = slider.colors;
colors.highlightedColor = new Color(0.882f, 0.882f, 0.882f);
colors.pressedColor = new Color(0.698f, 0.698f, 0.698f);
colors.disabledColor = new Color(0.521f, 0.521f, 0.521f);
}
private static void SetParentAndAlign(GameObject child, GameObject parent)
{
if (parent == null)
return;
child.transform.SetParent(parent.transform, false);
SetLayerRecursively(child, parent.layer);
}
private static void SetLayerRecursively(GameObject go, int layer)
{
go.layer = layer;
Transform t = go.transform;
for (int i = 0; i < t.childCount; i++)
SetLayerRecursively(t.GetChild(i).gameObject, layer);
}
// Actual controls
public static GameObject CreateScrollbar(Resources resources)
{
// Create GOs Hierarchy
GameObject scrollbarRoot = CreateUIElementRoot("Scrollbar", s_ThinElementSize);
GameObject sliderArea = CreateUIObject("Sliding Area", scrollbarRoot);
GameObject handle = CreateUIObject("Handle", sliderArea);
Image bgImage = scrollbarRoot.AddComponent<Image>();
bgImage.sprite = resources.background;
bgImage.type = Image.Type.Sliced;
bgImage.color = s_DefaultSelectableColor;
Image handleImage = handle.AddComponent<Image>();
handleImage.sprite = resources.standard;
handleImage.type = Image.Type.Sliced;
handleImage.color = s_DefaultSelectableColor;
RectTransform sliderAreaRect = sliderArea.GetComponent<RectTransform>();
sliderAreaRect.sizeDelta = new Vector2(-20, -20);
sliderAreaRect.anchorMin = Vector2.zero;
sliderAreaRect.anchorMax = Vector2.one;
RectTransform handleRect = handle.GetComponent<RectTransform>();
handleRect.sizeDelta = new Vector2(20, 20);
Scrollbar scrollbar = scrollbarRoot.AddComponent<Scrollbar>();
scrollbar.handleRect = handleRect;
scrollbar.targetGraphic = handleImage;
SetDefaultColorTransitionValues(scrollbar);
return scrollbarRoot;
}
public static GameObject CreateButton(Resources resources)
{
GameObject buttonRoot = CreateUIElementRoot("Button", s_ThickElementSize);
GameObject childText = new GameObject("Text (TMP)");
childText.AddComponent<RectTransform>();
SetParentAndAlign(childText, buttonRoot);
Image image = buttonRoot.AddComponent<Image>();
image.sprite = resources.standard;
image.type = Image.Type.Sliced;
image.color = s_DefaultSelectableColor;
Button bt = buttonRoot.AddComponent<Button>();
SetDefaultColorTransitionValues(bt);
TextMeshProUGUI text = childText.AddComponent<TextMeshProUGUI>();
text.text = "Button";
text.alignment = TextAlignmentOptions.Center;
SetDefaultTextValues(text);
RectTransform textRectTransform = childText.GetComponent<RectTransform>();
textRectTransform.anchorMin = Vector2.zero;
textRectTransform.anchorMax = Vector2.one;
textRectTransform.sizeDelta = Vector2.zero;
return buttonRoot;
}
public static GameObject CreateText(Resources resources)
{
GameObject go = null;
#if UNITY_EDITOR
go = ObjectFactory.CreateGameObject("Text (TMP)");
ObjectFactory.AddComponent<TextMeshProUGUI>(go);
#else
go = CreateUIElementRoot("Text (TMP)", s_TextElementSize);
go.AddComponent<TextMeshProUGUI>();
#endif
return go;
}
public static GameObject CreateInputField(Resources resources)
{
GameObject root = CreateUIElementRoot("InputField (TMP)", s_ThickElementSize);
GameObject textArea = CreateUIObject("Text Area", root);
GameObject childPlaceholder = CreateUIObject("Placeholder", textArea);
GameObject childText = CreateUIObject("Text", textArea);
Image image = root.AddComponent<Image>();
image.sprite = resources.inputField;
image.type = Image.Type.Sliced;
image.color = s_DefaultSelectableColor;
TMP_InputField inputField = root.AddComponent<TMP_InputField>();
SetDefaultColorTransitionValues(inputField);
// Use UI.Mask for Unity 5.0 - 5.1 and 2D RectMask for Unity 5.2 and up
RectMask2D rectMask = textArea.AddComponent<RectMask2D>();
#if UNITY_2019_4_OR_NEWER
rectMask.padding = new Vector4(-8, -5, -8, -5);
#endif
RectTransform textAreaRectTransform = textArea.GetComponent<RectTransform>();
textAreaRectTransform.anchorMin = Vector2.zero;
textAreaRectTransform.anchorMax = Vector2.one;
textAreaRectTransform.sizeDelta = Vector2.zero;
textAreaRectTransform.offsetMin = new Vector2(10, 6);
textAreaRectTransform.offsetMax = new Vector2(-10, -7);
TextMeshProUGUI text = childText.AddComponent<TextMeshProUGUI>();
text.text = "";
text.enableWordWrapping = false;
text.extraPadding = true;
text.richText = true;
SetDefaultTextValues(text);
TextMeshProUGUI placeholder = childPlaceholder.AddComponent<TextMeshProUGUI>();
placeholder.text = "Enter text...";
placeholder.fontSize = 14;
placeholder.fontStyle = FontStyles.Italic;
placeholder.enableWordWrapping = false;
placeholder.extraPadding = true;
// Make placeholder color half as opaque as normal text color.
Color placeholderColor = text.color;
placeholderColor.a *= 0.5f;
placeholder.color = placeholderColor;
// Add Layout component to placeholder.
placeholder.gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
RectTransform textRectTransform = childText.GetComponent<RectTransform>();
textRectTransform.anchorMin = Vector2.zero;
textRectTransform.anchorMax = Vector2.one;
textRectTransform.sizeDelta = Vector2.zero;
textRectTransform.offsetMin = new Vector2(0, 0);
textRectTransform.offsetMax = new Vector2(0, 0);
RectTransform placeholderRectTransform = childPlaceholder.GetComponent<RectTransform>();
placeholderRectTransform.anchorMin = Vector2.zero;
placeholderRectTransform.anchorMax = Vector2.one;
placeholderRectTransform.sizeDelta = Vector2.zero;
placeholderRectTransform.offsetMin = new Vector2(0, 0);
placeholderRectTransform.offsetMax = new Vector2(0, 0);
inputField.textViewport = textAreaRectTransform;
inputField.textComponent = text;
inputField.placeholder = placeholder;
inputField.fontAsset = text.font;
return root;
}
public static GameObject CreateDropdown(Resources resources)
{
GameObject root = CreateUIElementRoot("Dropdown", s_ThickElementSize);
GameObject label = CreateUIObject("Label", root);
GameObject arrow = CreateUIObject("Arrow", root);
GameObject template = CreateUIObject("Template", root);
GameObject viewport = CreateUIObject("Viewport", template);
GameObject content = CreateUIObject("Content", viewport);
GameObject item = CreateUIObject("Item", content);
GameObject itemBackground = CreateUIObject("Item Background", item);
GameObject itemCheckmark = CreateUIObject("Item Checkmark", item);
GameObject itemLabel = CreateUIObject("Item Label", item);
// Sub controls.
GameObject scrollbar = CreateScrollbar(resources);
scrollbar.name = "Scrollbar";
SetParentAndAlign(scrollbar, template);
Scrollbar scrollbarScrollbar = scrollbar.GetComponent<Scrollbar>();
scrollbarScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);
RectTransform vScrollbarRT = scrollbar.GetComponent<RectTransform>();
vScrollbarRT.anchorMin = Vector2.right;
vScrollbarRT.anchorMax = Vector2.one;
vScrollbarRT.pivot = Vector2.one;
vScrollbarRT.sizeDelta = new Vector2(vScrollbarRT.sizeDelta.x, 0);
// Setup item UI components.
TextMeshProUGUI itemLabelText = itemLabel.AddComponent<TextMeshProUGUI>();
SetDefaultTextValues(itemLabelText);
itemLabelText.alignment = TextAlignmentOptions.Left;
Image itemBackgroundImage = itemBackground.AddComponent<Image>();
itemBackgroundImage.color = new Color32(245, 245, 245, 255);
Image itemCheckmarkImage = itemCheckmark.AddComponent<Image>();
itemCheckmarkImage.sprite = resources.checkmark;
Toggle itemToggle = item.AddComponent<Toggle>();
itemToggle.targetGraphic = itemBackgroundImage;
itemToggle.graphic = itemCheckmarkImage;
itemToggle.isOn = true;
// Setup template UI components.
Image templateImage = template.AddComponent<Image>();
templateImage.sprite = resources.standard;
templateImage.type = Image.Type.Sliced;
ScrollRect templateScrollRect = template.AddComponent<ScrollRect>();
templateScrollRect.content = (RectTransform)content.transform;
templateScrollRect.viewport = (RectTransform)viewport.transform;
templateScrollRect.horizontal = false;
templateScrollRect.movementType = ScrollRect.MovementType.Clamped;
templateScrollRect.verticalScrollbar = scrollbarScrollbar;
templateScrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
templateScrollRect.verticalScrollbarSpacing = -3;
Mask scrollRectMask = viewport.AddComponent<Mask>();
scrollRectMask.showMaskGraphic = false;
Image viewportImage = viewport.AddComponent<Image>();
viewportImage.sprite = resources.mask;
viewportImage.type = Image.Type.Sliced;
// Setup dropdown UI components.
TextMeshProUGUI labelText = label.AddComponent<TextMeshProUGUI>();
SetDefaultTextValues(labelText);
labelText.alignment = TextAlignmentOptions.Left;
Image arrowImage = arrow.AddComponent<Image>();
arrowImage.sprite = resources.dropdown;
Image backgroundImage = root.AddComponent<Image>();
backgroundImage.sprite = resources.standard;
backgroundImage.color = s_DefaultSelectableColor;
backgroundImage.type = Image.Type.Sliced;
TMP_Dropdown dropdown = root.AddComponent<TMP_Dropdown>();
dropdown.targetGraphic = backgroundImage;
SetDefaultColorTransitionValues(dropdown);
dropdown.template = template.GetComponent<RectTransform>();
dropdown.captionText = labelText;
dropdown.itemText = itemLabelText;
// Setting default Item list.
itemLabelText.text = "Option A";
dropdown.options.Add(new TMP_Dropdown.OptionData {text = "Option A" });
dropdown.options.Add(new TMP_Dropdown.OptionData {text = "Option B" });
dropdown.options.Add(new TMP_Dropdown.OptionData {text = "Option C" });
dropdown.RefreshShownValue();
// Set up RectTransforms.
RectTransform labelRT = label.GetComponent<RectTransform>();
labelRT.anchorMin = Vector2.zero;
labelRT.anchorMax = Vector2.one;
labelRT.offsetMin = new Vector2(10, 6);
labelRT.offsetMax = new Vector2(-25, -7);
RectTransform arrowRT = arrow.GetComponent<RectTransform>();
arrowRT.anchorMin = new Vector2(1, 0.5f);
arrowRT.anchorMax = new Vector2(1, 0.5f);
arrowRT.sizeDelta = new Vector2(20, 20);
arrowRT.anchoredPosition = new Vector2(-15, 0);
RectTransform templateRT = template.GetComponent<RectTransform>();
templateRT.anchorMin = new Vector2(0, 0);
templateRT.anchorMax = new Vector2(1, 0);
templateRT.pivot = new Vector2(0.5f, 1);
templateRT.anchoredPosition = new Vector2(0, 2);
templateRT.sizeDelta = new Vector2(0, 150);
RectTransform viewportRT = viewport.GetComponent<RectTransform>();
viewportRT.anchorMin = new Vector2(0, 0);
viewportRT.anchorMax = new Vector2(1, 1);
viewportRT.sizeDelta = new Vector2(-18, 0);
viewportRT.pivot = new Vector2(0, 1);
RectTransform contentRT = content.GetComponent<RectTransform>();
contentRT.anchorMin = new Vector2(0f, 1);
contentRT.anchorMax = new Vector2(1f, 1);
contentRT.pivot = new Vector2(0.5f, 1);
contentRT.anchoredPosition = new Vector2(0, 0);
contentRT.sizeDelta = new Vector2(0, 28);
RectTransform itemRT = item.GetComponent<RectTransform>();
itemRT.anchorMin = new Vector2(0, 0.5f);
itemRT.anchorMax = new Vector2(1, 0.5f);
itemRT.sizeDelta = new Vector2(0, 20);
RectTransform itemBackgroundRT = itemBackground.GetComponent<RectTransform>();
itemBackgroundRT.anchorMin = Vector2.zero;
itemBackgroundRT.anchorMax = Vector2.one;
itemBackgroundRT.sizeDelta = Vector2.zero;
RectTransform itemCheckmarkRT = itemCheckmark.GetComponent<RectTransform>();
itemCheckmarkRT.anchorMin = new Vector2(0, 0.5f);
itemCheckmarkRT.anchorMax = new Vector2(0, 0.5f);
itemCheckmarkRT.sizeDelta = new Vector2(20, 20);
itemCheckmarkRT.anchoredPosition = new Vector2(10, 0);
RectTransform itemLabelRT = itemLabel.GetComponent<RectTransform>();
itemLabelRT.anchorMin = Vector2.zero;
itemLabelRT.anchorMax = Vector2.one;
itemLabelRT.offsetMin = new Vector2(20, 1);
itemLabelRT.offsetMax = new Vector2(-10, -2);
template.SetActive(false);
return root;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,184 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace TMPro
{
public class TMP_EditorResourceManager
{
private static TMP_EditorResourceManager s_Instance;
private readonly List<Object> m_ObjectUpdateQueue = new List<Object>();
private HashSet<int> m_ObjectUpdateQueueLookup = new HashSet<int>();
private readonly List<Object> m_ObjectReImportQueue = new List<Object>();
private HashSet<int> m_ObjectReImportQueueLookup = new HashSet<int>();
private readonly List<TMP_FontAsset> m_FontAssetDefinitionRefreshQueue = new List<TMP_FontAsset>();
private HashSet<int> m_FontAssetDefinitionRefreshQueueLookup = new HashSet<int>();
/// <summary>
/// Get a singleton instance of the manager.
/// </summary>
public static TMP_EditorResourceManager instance
{
get
{
if (s_Instance == null)
s_Instance = new TMP_EditorResourceManager();
return s_Instance;
}
}
/// <summary>
/// Register to receive rendering callbacks.
/// </summary>
private TMP_EditorResourceManager()
{
Camera.onPostRender += OnCameraPostRender;
Canvas.willRenderCanvases += OnPreRenderCanvases;
}
void OnCameraPostRender(Camera cam)
{
// Exclude the PreRenderCamera
if (cam.cameraType != CameraType.SceneView)
return;
DoPostRenderUpdates();
}
void OnPreRenderCanvases()
{
DoPreRenderUpdates();
}
/// <summary>
/// Register resource for re-import.
/// </summary>
/// <param name="obj"></param>
internal static void RegisterResourceForReimport(Object obj)
{
instance.InternalRegisterResourceForReimport(obj);
}
private void InternalRegisterResourceForReimport(Object obj)
{
int id = obj.GetInstanceID();
if (m_ObjectReImportQueueLookup.Contains(id))
return;
m_ObjectReImportQueueLookup.Add(id);
m_ObjectReImportQueue.Add(obj);
}
/// <summary>
/// Register resource to be updated.
/// </summary>
/// <param name="textObject"></param>
internal static void RegisterResourceForUpdate(Object obj)
{
instance.InternalRegisterResourceForUpdate(obj);
}
private void InternalRegisterResourceForUpdate(Object obj)
{
int id = obj.GetInstanceID();
if (m_ObjectUpdateQueueLookup.Contains(id))
return;
m_ObjectUpdateQueueLookup.Add(id);
m_ObjectUpdateQueue.Add(obj);
}
/// <summary>
///
/// </summary>
/// <param name="fontAsset"></param>
internal static void RegisterFontAssetForDefinitionRefresh(TMP_FontAsset fontAsset)
{
instance.InternalRegisterFontAssetForDefinitionRefresh(fontAsset);
}
private void InternalRegisterFontAssetForDefinitionRefresh(TMP_FontAsset fontAsset)
{
int id = fontAsset.GetInstanceID();
if (m_FontAssetDefinitionRefreshQueueLookup.Contains(id))
return;
m_FontAssetDefinitionRefreshQueueLookup.Add(id);
m_FontAssetDefinitionRefreshQueue.Add(fontAsset);
}
void DoPostRenderUpdates()
{
// Handle objects that need updating
int objUpdateCount = m_ObjectUpdateQueue.Count;
for (int i = 0; i < objUpdateCount; i++)
{
Object obj = m_ObjectUpdateQueue[i];
if (obj != null)
{
EditorUtility.SetDirty(obj);
}
}
if (objUpdateCount > 0)
{
//Debug.Log("Saving assets");
//AssetDatabase.SaveAssets();
m_ObjectUpdateQueue.Clear();
m_ObjectUpdateQueueLookup.Clear();
}
// Handle objects that need re-importing
int objReImportCount = m_ObjectReImportQueue.Count;
for (int i = 0; i < objReImportCount; i++)
{
Object obj = m_ObjectReImportQueue[i];
if (obj != null)
{
AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(obj));
}
}
if (objReImportCount > 0)
{
m_ObjectReImportQueue.Clear();
m_ObjectReImportQueueLookup.Clear();
}
}
void DoPreRenderUpdates()
{
// Handle Font Asset Definition Refresh
for (int i = 0; i < m_FontAssetDefinitionRefreshQueue.Count; i++)
{
TMP_FontAsset fontAsset = m_FontAssetDefinitionRefreshQueue[i];
if (fontAsset != null)
{
fontAsset.ReadFontAssetDefinition();
TMPro_EventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset);
}
}
if (m_FontAssetDefinitionRefreshQueue.Count > 0)
{
m_FontAssetDefinitionRefreshQueue.Clear();
m_FontAssetDefinitionRefreshQueueLookup.Clear();
}
}
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,456 @@
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace TMPro
{
/// <summary>
/// Class that contains the basic information about the font.
/// </summary>
[Serializable]
public class FaceInfo_Legacy
{
public string Name;
public float PointSize;
public float Scale;
public int CharacterCount;
public float LineHeight;
public float Baseline;
public float Ascender;
public float CapHeight;
public float Descender;
public float CenterLine;
public float SuperscriptOffset;
public float SubscriptOffset;
public float SubSize;
public float Underline;
public float UnderlineThickness;
public float strikethrough;
public float strikethroughThickness;
public float TabWidth;
public float Padding;
public float AtlasWidth;
public float AtlasHeight;
}
// Class which contains the Glyph Info / Character definition for each character contained in the font asset.
[Serializable]
public class TMP_Glyph : TMP_TextElement_Legacy
{
/// <summary>
/// Function to create a deep copy of a GlyphInfo.
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static TMP_Glyph Clone(TMP_Glyph source)
{
TMP_Glyph copy = new TMP_Glyph();
copy.id = source.id;
copy.x = source.x;
copy.y = source.y;
copy.width = source.width;
copy.height = source.height;
copy.xOffset = source.xOffset;
copy.yOffset = source.yOffset;
copy.xAdvance = source.xAdvance;
copy.scale = source.scale;
return copy;
}
}
// Structure which holds the font creation settings
[Serializable]
public struct FontAssetCreationSettings
{
public string sourceFontFileName;
public string sourceFontFileGUID;
public int pointSizeSamplingMode;
public int pointSize;
public int padding;
public int packingMode;
public int atlasWidth;
public int atlasHeight;
public int characterSetSelectionMode;
public string characterSequence;
public string referencedFontAssetGUID;
public string referencedTextAssetGUID;
public int fontStyle;
public float fontStyleModifier;
public int renderMode;
public bool includeFontFeatures;
internal FontAssetCreationSettings(string sourceFontFileGUID, int pointSize, int pointSizeSamplingMode, int padding, int packingMode, int atlasWidth, int atlasHeight, int characterSelectionMode, string characterSet, int renderMode)
{
this.sourceFontFileName = string.Empty;
this.sourceFontFileGUID = sourceFontFileGUID;
this.pointSize = pointSize;
this.pointSizeSamplingMode = pointSizeSamplingMode;
this.padding = padding;
this.packingMode = packingMode;
this.atlasWidth = atlasWidth;
this.atlasHeight = atlasHeight;
this.characterSequence = characterSet;
this.characterSetSelectionMode = characterSelectionMode;
this.renderMode = renderMode;
this.referencedFontAssetGUID = string.Empty;
this.referencedTextAssetGUID = string.Empty;
this.fontStyle = 0;
this.fontStyleModifier = 0;
this.includeFontFeatures = false;
}
}
/// <summary>
/// Contains the font assets for the regular and italic styles associated with a given font weight.
/// </summary>
[Serializable]
public struct TMP_FontWeightPair
{
public TMP_FontAsset regularTypeface;
public TMP_FontAsset italicTypeface;
}
public struct KerningPairKey
{
public uint ascii_Left;
public uint ascii_Right;
public uint key;
public KerningPairKey(uint ascii_left, uint ascii_right)
{
ascii_Left = ascii_left;
ascii_Right = ascii_right;
key = (ascii_right << 16) + ascii_left;
}
}
/// <summary>
/// Positional adjustments of a glyph
/// </summary>
[Serializable]
public struct GlyphValueRecord_Legacy
{
public float xPlacement;
public float yPlacement;
public float xAdvance;
public float yAdvance;
internal GlyphValueRecord_Legacy(UnityEngine.TextCore.LowLevel.GlyphValueRecord valueRecord)
{
this.xPlacement = valueRecord.xPlacement;
this.yPlacement = valueRecord.yPlacement;
this.xAdvance = valueRecord.xAdvance;
this.yAdvance = valueRecord.yAdvance;
}
public static GlyphValueRecord_Legacy operator +(GlyphValueRecord_Legacy a, GlyphValueRecord_Legacy b)
{
GlyphValueRecord_Legacy c;
c.xPlacement = a.xPlacement + b.xPlacement;
c.yPlacement = a.yPlacement + b.yPlacement;
c.xAdvance = a.xAdvance + b.xAdvance;
c.yAdvance = a.yAdvance + b.yAdvance;
return c;
}
}
[Serializable]
public class KerningPair
{
/// <summary>
/// The first glyph part of a kerning pair.
/// </summary>
public uint firstGlyph
{
get { return m_FirstGlyph; }
set { m_FirstGlyph = value; }
}
[FormerlySerializedAs("AscII_Left")]
[SerializeField]
private uint m_FirstGlyph;
/// <summary>
/// The positional adjustment of the first glyph.
/// </summary>
public GlyphValueRecord_Legacy firstGlyphAdjustments
{
get { return m_FirstGlyphAdjustments; }
}
[SerializeField]
private GlyphValueRecord_Legacy m_FirstGlyphAdjustments;
/// <summary>
/// The second glyph part of a kerning pair.
/// </summary>
public uint secondGlyph
{
get { return m_SecondGlyph; }
set { m_SecondGlyph = value; }
}
[FormerlySerializedAs("AscII_Right")]
[SerializeField]
private uint m_SecondGlyph;
/// <summary>
/// The positional adjustment of the second glyph.
/// </summary>
public GlyphValueRecord_Legacy secondGlyphAdjustments
{
get { return m_SecondGlyphAdjustments; }
}
[SerializeField]
private GlyphValueRecord_Legacy m_SecondGlyphAdjustments;
[FormerlySerializedAs("XadvanceOffset")]
public float xOffset;
internal static KerningPair empty = new KerningPair(0, new GlyphValueRecord_Legacy(), 0, new GlyphValueRecord_Legacy());
/// <summary>
/// Determines if the Character Spacing property of the text object will affect the kerning pair.
/// This is mostly relevant when using Diacritical marks to prevent Character Spacing from altering the
/// </summary>
public bool ignoreSpacingAdjustments
{
get { return m_IgnoreSpacingAdjustments; }
}
[SerializeField]
private bool m_IgnoreSpacingAdjustments = false;
public KerningPair()
{
m_FirstGlyph = 0;
m_FirstGlyphAdjustments = new GlyphValueRecord_Legacy();
m_SecondGlyph = 0;
m_SecondGlyphAdjustments = new GlyphValueRecord_Legacy();
}
public KerningPair(uint left, uint right, float offset)
{
firstGlyph = left;
m_SecondGlyph = right;
xOffset = offset;
}
public KerningPair(uint firstGlyph, GlyphValueRecord_Legacy firstGlyphAdjustments, uint secondGlyph, GlyphValueRecord_Legacy secondGlyphAdjustments)
{
m_FirstGlyph = firstGlyph;
m_FirstGlyphAdjustments = firstGlyphAdjustments;
m_SecondGlyph = secondGlyph;
m_SecondGlyphAdjustments = secondGlyphAdjustments;
}
internal void ConvertLegacyKerningData()
{
m_FirstGlyphAdjustments.xAdvance = xOffset;
//xOffset = 0;
}
}
[Serializable]
public class KerningTable
{
public List<KerningPair> kerningPairs;
public KerningTable()
{
kerningPairs = new List<KerningPair>();
}
public void AddKerningPair()
{
if (kerningPairs.Count == 0)
{
kerningPairs.Add(new KerningPair(0, 0, 0));
}
else
{
uint left = kerningPairs.Last().firstGlyph;
uint right = kerningPairs.Last().secondGlyph;
float xoffset = kerningPairs.Last().xOffset;
kerningPairs.Add(new KerningPair(left, right, xoffset));
}
}
/// <summary>
/// Add Kerning Pair
/// </summary>
/// <param name="first">First glyph</param>
/// <param name="second">Second glyph</param>
/// <param name="offset">xAdvance value</param>
/// <returns></returns>
public int AddKerningPair(uint first, uint second, float offset)
{
int index = kerningPairs.FindIndex(item => item.firstGlyph == first && item.secondGlyph == second);
if (index == -1)
{
kerningPairs.Add(new KerningPair(first, second, offset));
return 0;
}
// Return -1 if Kerning Pair already exists.
return -1;
}
/// <summary>
/// Add Glyph pair adjustment record
/// </summary>
/// <param name="firstGlyph">The first glyph</param>
/// <param name="firstGlyphAdjustments">Adjustment record for the first glyph</param>
/// <param name="secondGlyph">The second glyph</param>
/// <param name="secondGlyphAdjustments">Adjustment record for the second glyph</param>
/// <returns></returns>
public int AddGlyphPairAdjustmentRecord(uint first, GlyphValueRecord_Legacy firstAdjustments, uint second, GlyphValueRecord_Legacy secondAdjustments)
{
int index = kerningPairs.FindIndex(item => item.firstGlyph == first && item.secondGlyph == second);
if (index == -1)
{
kerningPairs.Add(new KerningPair(first, firstAdjustments, second, secondAdjustments));
return 0;
}
// Return -1 if Kerning Pair already exists.
return -1;
}
public void RemoveKerningPair(int left, int right)
{
int index = kerningPairs.FindIndex(item => item.firstGlyph == left && item.secondGlyph == right);
if (index != -1)
kerningPairs.RemoveAt(index);
}
public void RemoveKerningPair(int index)
{
kerningPairs.RemoveAt(index);
}
public void SortKerningPairs()
{
// Sort List of Kerning Info
if (kerningPairs.Count > 0)
kerningPairs = kerningPairs.OrderBy(s => s.firstGlyph).ThenBy(s => s.secondGlyph).ToList();
}
}
public static class TMP_FontUtilities
{
private static List<int> k_searchedFontAssets;
/// <summary>
/// Search through the given font and its fallbacks for the specified character.
/// </summary>
/// <param name="font">The font asset to search for the given character.</param>
/// <param name="unicode">The character to find.</param>
/// <param name="character">out parameter containing the glyph for the specified character (if found).</param>
/// <returns></returns>
public static TMP_FontAsset SearchForCharacter(TMP_FontAsset font, uint unicode, out TMP_Character character)
{
if (k_searchedFontAssets == null)
k_searchedFontAssets = new List<int>();
k_searchedFontAssets.Clear();
return SearchForCharacterInternal(font, unicode, out character);
}
/// <summary>
/// Search through the given list of fonts and their possible fallbacks for the specified character.
/// </summary>
/// <param name="fonts"></param>
/// <param name="unicode"></param>
/// <param name="character"></param>
/// <returns></returns>
public static TMP_FontAsset SearchForCharacter(List<TMP_FontAsset> fonts, uint unicode, out TMP_Character character)
{
return SearchForCharacterInternal(fonts, unicode, out character);
}
private static TMP_FontAsset SearchForCharacterInternal(TMP_FontAsset font, uint unicode, out TMP_Character character)
{
character = null;
if (font == null) return null;
if (font.characterLookupTable.TryGetValue(unicode, out character))
{
return font;
}
else if (font.fallbackFontAssetTable != null && font.fallbackFontAssetTable.Count > 0)
{
for (int i = 0; i < font.fallbackFontAssetTable.Count && character == null; i++)
{
TMP_FontAsset temp = font.fallbackFontAssetTable[i];
if (temp == null) continue;
int id = temp.GetInstanceID();
// Skip over the fallback font asset in the event it is null or if already searched.
if (k_searchedFontAssets.Contains(id)) continue;
// Add to list of font assets already searched.
k_searchedFontAssets.Add(id);
temp = SearchForCharacterInternal(temp, unicode, out character);
if (temp != null)
return temp;
}
}
return null;
}
private static TMP_FontAsset SearchForCharacterInternal(List<TMP_FontAsset> fonts, uint unicode, out TMP_Character character)
{
character = null;
if (fonts != null && fonts.Count > 0)
{
for (int i = 0; i < fonts.Count; i++)
{
TMP_FontAsset fontAsset = SearchForCharacterInternal(fonts[i], unicode, out character);
if (fontAsset != null)
return fontAsset;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,442 @@
using System.Collections.Generic;
using UnityEngine.TextCore;
using UnityEngine.TextCore.LowLevel;
namespace TMPro
{
public class TMP_FontAssetUtilities
{
private static readonly TMP_FontAssetUtilities s_Instance = new TMP_FontAssetUtilities();
/// <summary>
/// Default constructor
/// </summary>
static TMP_FontAssetUtilities() { }
/// <summary>
/// Get a singleton instance of the Font Asset Utilities class.
/// </summary>
public static TMP_FontAssetUtilities instance
{
get { return s_Instance; }
}
/// <summary>
/// HashSet containing instance ID of font assets already searched.
/// </summary>
private static HashSet<int> k_SearchedAssets;
/// <summary>
/// Returns the text element (character) for the given unicode value taking into consideration the requested font style and weight.
/// Function searches the source font asset, its list of font assets assigned as alternative typefaces and potentially its fallbacks.
/// The font asset out parameter contains a reference to the font asset containing the character.
/// The typeface type indicates whether the returned font asset is the source font asset, an alternative typeface or fallback font asset.
/// </summary>
/// <param name="unicode">The unicode value of the requested character</param>
/// <param name="sourceFontAsset">The font asset to be searched</param>
/// <param name="includeFallbacks">Include the fallback font assets in the search</param>
/// <param name="fontStyle">The font style</param>
/// <param name="fontWeight">The font weight</param>
/// <param name="isAlternativeTypeface">Indicates if the OUT font asset is an alternative typeface or fallback font asset</param>
/// <param name="fontAsset">The font asset that contains the requested character</param>
/// <returns></returns>
public static TMP_Character GetCharacterFromFontAsset(uint unicode, TMP_FontAsset sourceFontAsset, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface)
{
if (includeFallbacks)
{
if (k_SearchedAssets == null)
k_SearchedAssets = new HashSet<int>();
else
k_SearchedAssets.Clear();
}
return GetCharacterFromFontAsset_Internal(unicode, sourceFontAsset, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface);
}
/// <summary>
/// Internal function returning the text element character for the given unicode value taking into consideration the font style and weight.
/// Function searches the source font asset, list of font assets assigned as alternative typefaces and list of fallback font assets.
/// </summary>
private static TMP_Character GetCharacterFromFontAsset_Internal(uint unicode, TMP_FontAsset sourceFontAsset, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface)
{
isAlternativeTypeface = false;
TMP_Character character = null;
#region FONT WEIGHT AND FONT STYLE HANDLING
// Determine if a font weight or style is used. If so check if an alternative typeface is assigned for the given weight and / or style.
bool isItalic = (fontStyle & FontStyles.Italic) == FontStyles.Italic;
if (isItalic || fontWeight != FontWeight.Regular)
{
// Get reference to the font weight pairs of the given font asset.
TMP_FontWeightPair[] fontWeights = sourceFontAsset.fontWeightTable;
int fontWeightIndex = 4;
switch (fontWeight)
{
case FontWeight.Thin:
fontWeightIndex = 1;
break;
case FontWeight.ExtraLight:
fontWeightIndex = 2;
break;
case FontWeight.Light:
fontWeightIndex = 3;
break;
case FontWeight.Regular:
fontWeightIndex = 4;
break;
case FontWeight.Medium:
fontWeightIndex = 5;
break;
case FontWeight.SemiBold:
fontWeightIndex = 6;
break;
case FontWeight.Bold:
fontWeightIndex = 7;
break;
case FontWeight.Heavy:
fontWeightIndex = 8;
break;
case FontWeight.Black:
fontWeightIndex = 9;
break;
}
TMP_FontAsset temp = isItalic ? fontWeights[fontWeightIndex].italicTypeface : fontWeights[fontWeightIndex].regularTypeface;
if (temp != null)
{
if (temp.characterLookupTable.TryGetValue(unicode, out character))
{
isAlternativeTypeface = true;
return character;
}
if (temp.atlasPopulationMode == AtlasPopulationMode.Dynamic)
{
if (temp.TryAddCharacterInternal(unicode, out character))
{
isAlternativeTypeface = true;
return character;
}
// Check if the source font file contains the requested character.
//if (TryGetCharacterFromFontFile(unicode, fontAsset, out characterData))
//{
// isAlternativeTypeface = true;
// return characterData;
//}
// If we find the requested character, we add it to the font asset character table
// and return its character data.
// We also add this character to the list of characters we will need to add to the font atlas.
// We assume the font atlas has room otherwise this font asset should not be marked as dynamic.
// Alternatively, we could also add multiple pages of font atlas textures (feature consideration).
}
// At this point, we were not able to find the requested character in the alternative typeface
// so we check the source font asset and its potential fallbacks.
}
}
#endregion
// Search the source font asset for the requested character.
if (sourceFontAsset.characterLookupTable.TryGetValue(unicode, out character))
return character;
if (sourceFontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic)
{
if (sourceFontAsset.TryAddCharacterInternal(unicode, out character))
return character;
}
// Search fallback font assets if we still don't have a valid character and include fallback is set to true.
if (character == null && includeFallbacks && sourceFontAsset.fallbackFontAssetTable != null)
{
// Get reference to the list of fallback font assets.
List<TMP_FontAsset> fallbackFontAssets = sourceFontAsset.fallbackFontAssetTable;
int fallbackCount = fallbackFontAssets.Count;
if (fallbackCount == 0)
return null;
for (int i = 0; i < fallbackCount; i++)
{
TMP_FontAsset temp = fallbackFontAssets[i];
if (temp == null)
continue;
int id = temp.instanceID;
// Try adding font asset to search list. If already present skip to the next one otherwise check if it contains the requested character.
if (k_SearchedAssets.Add(id) == false)
continue;
// Add reference to this search query
sourceFontAsset.FallbackSearchQueryLookup.Add(id);
character = GetCharacterFromFontAsset_Internal(unicode, temp, true, fontStyle, fontWeight, out isAlternativeTypeface);
if (character != null)
return character;
}
}
return null;
}
/// <summary>
/// Returns the text element (character) for the given unicode value taking into consideration the requested font style and weight.
/// Function searches the provided list of font assets, the list of font assets assigned as alternative typefaces to them as well as their fallbacks.
/// The font asset out parameter contains a reference to the font asset containing the character.
/// The typeface type indicates whether the returned font asset is the source font asset, an alternative typeface or fallback font asset.
/// </summary>
/// <param name="unicode">The unicode value of the requested character</param>
/// <param name="sourceFontAsset">The font asset originating the search query</param>
/// <param name="fontAssets">The list of font assets to search</param>
/// <param name="includeFallbacks">Determines if the fallback of each font assets on the list will be searched</param>
/// <param name="fontStyle">The font style</param>
/// <param name="fontWeight">The font weight</param>
/// <param name="isAlternativeTypeface">Determines if the OUT font asset is an alternative typeface or fallback font asset</param>
/// <returns></returns>
public static TMP_Character GetCharacterFromFontAssets(uint unicode, TMP_FontAsset sourceFontAsset, List<TMP_FontAsset> fontAssets, bool includeFallbacks, FontStyles fontStyle, FontWeight fontWeight, out bool isAlternativeTypeface)
{
isAlternativeTypeface = false;
// Make sure font asset list is valid
if (fontAssets == null || fontAssets.Count == 0)
return null;
if (includeFallbacks)
{
if (k_SearchedAssets == null)
k_SearchedAssets = new HashSet<int>();
else
k_SearchedAssets.Clear();
}
int fontAssetCount = fontAssets.Count;
for (int i = 0; i < fontAssetCount; i++)
{
TMP_FontAsset fontAsset = fontAssets[i];
if (fontAsset == null) continue;
// Add reference to this search query
sourceFontAsset.FallbackSearchQueryLookup.Add(fontAsset.instanceID);
TMP_Character character = GetCharacterFromFontAsset_Internal(unicode, fontAsset, includeFallbacks, fontStyle, fontWeight, out isAlternativeTypeface);
if (character != null)
return character;
}
return null;
}
// =====================================================================
// SPRITE ASSET - Functions
// =====================================================================
/// <summary>
///
/// </summary>
/// <param name="unicode"></param>
/// <param name="spriteAsset"></param>
/// <param name="includeFallbacks"></param>
/// <returns></returns>
public static TMP_SpriteCharacter GetSpriteCharacterFromSpriteAsset(uint unicode, TMP_SpriteAsset spriteAsset, bool includeFallbacks)
{
// Make sure we have a valid sprite asset to search
if (spriteAsset == null)
return null;
TMP_SpriteCharacter spriteCharacter;
// Search sprite asset for potential sprite character for the given unicode value
if (spriteAsset.spriteCharacterLookupTable.TryGetValue(unicode, out spriteCharacter))
return spriteCharacter;
if (includeFallbacks)
{
// Clear searched assets
if (k_SearchedAssets == null)
k_SearchedAssets = new HashSet<int>();
else
k_SearchedAssets.Clear();
// Add current sprite asset to already searched assets.
k_SearchedAssets.Add(spriteAsset.instanceID);
List<TMP_SpriteAsset> fallbackSpriteAsset = spriteAsset.fallbackSpriteAssets;
if (fallbackSpriteAsset != null && fallbackSpriteAsset.Count > 0)
{
int fallbackCount = fallbackSpriteAsset.Count;
for (int i = 0; i < fallbackCount; i++)
{
TMP_SpriteAsset temp = fallbackSpriteAsset[i];
if (temp == null)
continue;
int id = temp.instanceID;
// Try adding asset to search list. If already present skip to the next one otherwise check if it contains the requested character.
if (k_SearchedAssets.Add(id) == false)
continue;
spriteCharacter = GetSpriteCharacterFromSpriteAsset_Internal(unicode, temp, true);
if (spriteCharacter != null)
return spriteCharacter;
}
}
}
return null;
}
/// <summary>
///
/// </summary>
/// <param name="unicode"></param>
/// <param name="spriteAsset"></param>
/// <param name="includeFallbacks"></param>
/// <returns></returns>
static TMP_SpriteCharacter GetSpriteCharacterFromSpriteAsset_Internal(uint unicode, TMP_SpriteAsset spriteAsset, bool includeFallbacks)
{
TMP_SpriteCharacter spriteCharacter;
// Search sprite asset for potential sprite character for the given unicode value
if (spriteAsset.spriteCharacterLookupTable.TryGetValue(unicode, out spriteCharacter))
return spriteCharacter;
if (includeFallbacks)
{
List<TMP_SpriteAsset> fallbackSpriteAsset = spriteAsset.fallbackSpriteAssets;
if (fallbackSpriteAsset != null && fallbackSpriteAsset.Count > 0)
{
int fallbackCount = fallbackSpriteAsset.Count;
for (int i = 0; i < fallbackCount; i++)
{
TMP_SpriteAsset temp = fallbackSpriteAsset[i];
if (temp == null)
continue;
int id = temp.instanceID;
// Try adding asset to search list. If already present skip to the next one otherwise check if it contains the requested character.
if (k_SearchedAssets.Add(id) == false)
continue;
spriteCharacter = GetSpriteCharacterFromSpriteAsset_Internal(unicode, temp, true);
if (spriteCharacter != null)
return spriteCharacter;
}
}
}
return null;
}
// =====================================================================
// FONT ENGINE & FONT FILE MANAGEMENT - Fields, Properties and Functions
// =====================================================================
private static bool k_IsFontEngineInitialized;
/*
private static bool TryGetCharacterFromFontFile(uint unicode, TMP_FontAsset fontAsset, out TMP_Character character)
{
character = null;
// Initialize Font Engine library if not already initialized
if (k_IsFontEngineInitialized == false)
{
FontEngineError error = FontEngine.InitializeFontEngine();
if (error == 0)
k_IsFontEngineInitialized = true;
}
// Load the font face for the given font asset.
// TODO: Add manager to keep track of which font faces are currently loaded.
FontEngine.LoadFontFace(fontAsset.sourceFontFile, fontAsset.faceInfo.pointSize);
Glyph glyph = null;
uint glyphIndex = FontEngine.GetGlyphIndex(unicode);
// Check if glyph is already contained in the font asset as the same glyph might be referenced by multiple character.
if (fontAsset.glyphLookupTable.TryGetValue(glyphIndex, out glyph))
{
character = fontAsset.AddCharacter_Internal(unicode, glyph);
return true;
}
GlyphLoadFlags glyphLoadFlags = ((GlyphRasterModes)fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_HINTED) == GlyphRasterModes.RASTER_MODE_HINTED ? GlyphLoadFlags.LOAD_RENDER : GlyphLoadFlags.LOAD_RENDER | GlyphLoadFlags.LOAD_NO_HINTING;
if (FontEngine.TryGetGlyphWithUnicodeValue(unicode, glyphLoadFlags, out glyph))
{
// Add new character to font asset (if needed)
character = fontAsset.AddCharacter_Internal(unicode, glyph);
return true;
}
return false;
}
public static bool TryGetGlyphFromFontFile(uint glyphIndex, TMP_FontAsset fontAsset, out Glyph glyph)
{
glyph = null;
// Initialize Font Engine library if not already initialized
if (k_IsFontEngineInitialized == false)
{
FontEngineError error = FontEngine.InitializeFontEngine();
if (error == 0)
k_IsFontEngineInitialized = true;
}
// Load the font face for the given font asset.
// TODO: Add manager to keep track of which font faces are currently loaded.
FontEngine.LoadFontFace(fontAsset.sourceFontFile, fontAsset.faceInfo.pointSize);
GlyphLoadFlags glyphLoadFlags = ((GlyphRasterModes)fontAsset.atlasRenderMode & GlyphRasterModes.RASTER_MODE_HINTED) == GlyphRasterModes.RASTER_MODE_HINTED ? GlyphLoadFlags.LOAD_RENDER : GlyphLoadFlags.LOAD_RENDER | GlyphLoadFlags.LOAD_NO_HINTING;
if (FontEngine.TryGetGlyphWithIndexValue(glyphIndex, glyphLoadFlags, out glyph))
{
// Add new glyph to font asset (if needed)
//fontAsset.AddGlyph_Internal(glyph);
return true;
}
return false;
}
*/
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
namespace TMPro
{
/// <summary>
/// Table that contains the various font features available for the given font asset.
/// </summary>
[Serializable]
public class TMP_FontFeatureTable
{
/// <summary>
/// List that contains the glyph pair adjustment records.
/// </summary>
public List<TMP_GlyphPairAdjustmentRecord> glyphPairAdjustmentRecords
{
get { return m_GlyphPairAdjustmentRecords; }
set { m_GlyphPairAdjustmentRecords = value; }
}
[SerializeField]
internal List<TMP_GlyphPairAdjustmentRecord> m_GlyphPairAdjustmentRecords;
/// <summary>
///
/// </summary>
internal Dictionary<uint, TMP_GlyphPairAdjustmentRecord> m_GlyphPairAdjustmentRecordLookupDictionary;
// =============================================
// Constructor(s)
// =============================================
public TMP_FontFeatureTable()
{
m_GlyphPairAdjustmentRecords = new List<TMP_GlyphPairAdjustmentRecord>();
m_GlyphPairAdjustmentRecordLookupDictionary = new Dictionary<uint, TMP_GlyphPairAdjustmentRecord>();
}
// =============================================
// Utility Functions
// =============================================
/// <summary>
/// Sort the glyph pair adjustment records by glyph index.
/// </summary>
public void SortGlyphPairAdjustmentRecords()
{
// Sort List of Kerning Info
if (m_GlyphPairAdjustmentRecords.Count > 0)
m_GlyphPairAdjustmentRecords = m_GlyphPairAdjustmentRecords.OrderBy(s => s.firstAdjustmentRecord.glyphIndex).ThenBy(s => s.secondAdjustmentRecord.glyphIndex).ToList();
}
}
}

View File

@@ -0,0 +1,227 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.TextCore.LowLevel;
namespace TMPro
{
[Flags]
public enum FontFeatureLookupFlags
{
None = 0x0,
IgnoreLigatures = 0x004,
IgnoreSpacingAdjustments = 0x100,
}
/// <summary>
/// The values used to adjust the position of a glyph or set of glyphs.
/// </summary>
[Serializable]
public struct TMP_GlyphValueRecord
{
/// <summary>
/// The positional adjustment affecting the horizontal bearing X of the glyph.
/// </summary>
public float xPlacement { get { return m_XPlacement; } set { m_XPlacement = value; } }
/// <summary>
/// The positional adjustment affecting the horizontal bearing Y of the glyph.
/// </summary>
public float yPlacement { get { return m_YPlacement; } set { m_YPlacement = value; } }
/// <summary>
/// The positional adjustment affecting the horizontal advance of the glyph.
/// </summary>
public float xAdvance { get { return m_XAdvance; } set { m_XAdvance = value; } }
/// <summary>
/// The positional adjustment affecting the vertical advance of the glyph.
/// </summary>
public float yAdvance { get { return m_YAdvance; } set { m_YAdvance = value; } }
// =============================================
// Private backing fields for public properties.
// =============================================
[SerializeField]
internal float m_XPlacement;
[SerializeField]
internal float m_YPlacement;
[SerializeField]
internal float m_XAdvance;
[SerializeField]
internal float m_YAdvance;
/// <summary>
/// Constructor
/// </summary>
/// <param name="xPlacement">The positional adjustment affecting the horizontal bearing X of the glyph.</param>
/// <param name="yPlacement">The positional adjustment affecting the horizontal bearing Y of the glyph.</param>
/// <param name="xAdvance">The positional adjustment affecting the horizontal advance of the glyph.</param>
/// <param name="yAdvance">The positional adjustment affecting the vertical advance of the glyph.</param>
public TMP_GlyphValueRecord(float xPlacement, float yPlacement, float xAdvance, float yAdvance)
{
m_XPlacement = xPlacement;
m_YPlacement = yPlacement;
m_XAdvance = xAdvance;
m_YAdvance = yAdvance;
}
internal TMP_GlyphValueRecord(GlyphValueRecord_Legacy valueRecord)
{
m_XPlacement = valueRecord.xPlacement;
m_YPlacement = valueRecord.yPlacement;
m_XAdvance = valueRecord.xAdvance;
m_YAdvance = valueRecord.yAdvance;
}
internal TMP_GlyphValueRecord(GlyphValueRecord valueRecord)
{
m_XPlacement = valueRecord.xPlacement;
m_YPlacement = valueRecord.yPlacement;
m_XAdvance = valueRecord.xAdvance;
m_YAdvance = valueRecord.yAdvance;
}
public static TMP_GlyphValueRecord operator +(TMP_GlyphValueRecord a, TMP_GlyphValueRecord b)
{
TMP_GlyphValueRecord c;
c.m_XPlacement = a.xPlacement + b.xPlacement;
c.m_YPlacement = a.yPlacement + b.yPlacement;
c.m_XAdvance = a.xAdvance + b.xAdvance;
c.m_YAdvance = a.yAdvance + b.yAdvance;
return c;
}
}
/// <summary>
/// The positional adjustment values of a glyph.
/// </summary>
[Serializable]
public struct TMP_GlyphAdjustmentRecord
{
/// <summary>
/// The index of the glyph in the source font file.
/// </summary>
public uint glyphIndex { get { return m_GlyphIndex; } set { m_GlyphIndex = value; } }
/// <summary>
/// The GlyphValueRecord contains the positional adjustments of the glyph.
/// </summary>
public TMP_GlyphValueRecord glyphValueRecord { get { return m_GlyphValueRecord; } set { m_GlyphValueRecord = value; } }
// =============================================
// Private backing fields for public properties.
// =============================================
[SerializeField]
internal uint m_GlyphIndex;
[SerializeField]
internal TMP_GlyphValueRecord m_GlyphValueRecord;
/// <summary>
/// Constructor
/// </summary>
/// <param name="glyphIndex">The index of the glyph in the source font file.</param>
/// <param name="glyphValueRecord">The GlyphValueRecord contains the positional adjustments of the glyph.</param>
public TMP_GlyphAdjustmentRecord(uint glyphIndex, TMP_GlyphValueRecord glyphValueRecord)
{
m_GlyphIndex = glyphIndex;
m_GlyphValueRecord = glyphValueRecord;
}
internal TMP_GlyphAdjustmentRecord(GlyphAdjustmentRecord adjustmentRecord)
{
m_GlyphIndex = adjustmentRecord.glyphIndex;
m_GlyphValueRecord = new TMP_GlyphValueRecord(adjustmentRecord.glyphValueRecord);
}
}
/// <summary>
/// The positional adjustment values for a pair of glyphs.
/// </summary>
[Serializable]
public class TMP_GlyphPairAdjustmentRecord
{
/// <summary>
/// Contains the positional adjustment values for the first glyph.
/// </summary>
public TMP_GlyphAdjustmentRecord firstAdjustmentRecord { get { return m_FirstAdjustmentRecord; } set { m_FirstAdjustmentRecord = value; } }
/// <summary>
/// Contains the positional adjustment values for the second glyph.
/// </summary>
public TMP_GlyphAdjustmentRecord secondAdjustmentRecord { get { return m_SecondAdjustmentRecord; } set { m_SecondAdjustmentRecord = value; } }
/// <summary>
///
/// </summary>
public FontFeatureLookupFlags featureLookupFlags { get { return m_FeatureLookupFlags; } set { m_FeatureLookupFlags = value; } }
// =============================================
// Private backing fields for public properties.
// =============================================
[SerializeField]
internal TMP_GlyphAdjustmentRecord m_FirstAdjustmentRecord;
[SerializeField]
internal TMP_GlyphAdjustmentRecord m_SecondAdjustmentRecord;
[SerializeField]
internal FontFeatureLookupFlags m_FeatureLookupFlags;
/// <summary>
/// Constructor
/// </summary>
/// <param name="firstAdjustmentRecord">First glyph adjustment record.</param>
/// <param name="secondAdjustmentRecord">Second glyph adjustment record.</param>
public TMP_GlyphPairAdjustmentRecord(TMP_GlyphAdjustmentRecord firstAdjustmentRecord, TMP_GlyphAdjustmentRecord secondAdjustmentRecord)
{
m_FirstAdjustmentRecord = firstAdjustmentRecord;
m_SecondAdjustmentRecord = secondAdjustmentRecord;
m_FeatureLookupFlags = FontFeatureLookupFlags.None;
}
/// <summary>
/// Internal constructor
/// </summary>
/// <param name="firstAdjustmentRecord"></param>
/// <param name="secondAdjustmentRecord"></param>
internal TMP_GlyphPairAdjustmentRecord(GlyphPairAdjustmentRecord glyphPairAdjustmentRecord)
{
m_FirstAdjustmentRecord = new TMP_GlyphAdjustmentRecord(glyphPairAdjustmentRecord.firstAdjustmentRecord);
m_SecondAdjustmentRecord = new TMP_GlyphAdjustmentRecord(glyphPairAdjustmentRecord.secondAdjustmentRecord);
m_FeatureLookupFlags = FontFeatureLookupFlags.None;
}
}
public struct GlyphPairKey
{
public uint firstGlyphIndex;
public uint secondGlyphIndex;
public uint key;
public GlyphPairKey(uint firstGlyphIndex, uint secondGlyphIndex)
{
this.firstGlyphIndex = firstGlyphIndex;
this.secondGlyphIndex = secondGlyphIndex;
key = secondGlyphIndex << 16 | firstGlyphIndex;
}
internal GlyphPairKey(TMP_GlyphPairAdjustmentRecord record)
{
firstGlyphIndex = record.firstAdjustmentRecord.glyphIndex;
secondGlyphIndex = record.secondAdjustmentRecord.glyphIndex;
key = secondGlyphIndex << 16 | firstGlyphIndex;
}
}
}

View File

@@ -0,0 +1,15 @@
using UnityEngine;
using System.Collections;
namespace TMPro
{
/// <summary>
/// Custom text input validator where user can implement their own custom character validation.
/// </summary>
[System.Serializable]
public abstract class TMP_InputValidator : ScriptableObject
{
public abstract char Validate(ref string text, ref int pos, char ch);
}
}

View File

@@ -0,0 +1,51 @@
namespace TMPro
{
/// <summary>
/// Structure which contains information about the individual lines of text.
/// </summary>
public struct TMP_LineInfo
{
internal int controlCharacterCount;
public int characterCount;
public int visibleCharacterCount;
public int spaceCount;
public int wordCount;
public int firstCharacterIndex;
public int firstVisibleCharacterIndex;
public int lastCharacterIndex;
public int lastVisibleCharacterIndex;
public float length;
public float lineHeight;
public float ascender;
public float baseline;
public float descender;
public float maxAdvance;
public float width;
public float marginLeft;
public float marginRight;
public HorizontalAlignmentOptions alignment;
public Extents lineExtents;
/// <summary>
/// Function returning the current line of text.
/// </summary>
/// <returns></returns>
//public string GetLineText()
//{
// string word = string.Empty;
// TMP_CharacterInfo[] charInfo = textComponent.textInfo.characterInfo;
// for (int i = firstCharacterIndex; i < lastCharacterIndex + 1; i++)
// {
// word += charInfo[i].character;
// }
// return word;
//}
}
}

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace TMPro
{
internal static class TMP_ListPool<T>
{
// Object pool to avoid allocations.
private static readonly TMP_ObjectPool<List<T>> s_ListPool = new TMP_ObjectPool<List<T>>(null, l => l.Clear());
public static List<T> Get()
{
return s_ListPool.Get();
}
public static void Release(List<T> toRelease)
{
s_ListPool.Release(toRelease);
}
}
}

View File

@@ -0,0 +1,682 @@
//#define TMP_DEBUG_MODE
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
namespace TMPro
{
public static class TMP_MaterialManager
{
private static List<MaskingMaterial> m_materialList = new List<MaskingMaterial>();
private static Dictionary<long, FallbackMaterial> m_fallbackMaterials = new Dictionary<long, FallbackMaterial>();
private static Dictionary<int, long> m_fallbackMaterialLookup = new Dictionary<int, long>();
private static List<FallbackMaterial> m_fallbackCleanupList = new List<FallbackMaterial>();
private static bool isFallbackListDirty;
static TMP_MaterialManager()
{
Canvas.willRenderCanvases += OnPreRender;
}
static void OnPreRender()
{
if (isFallbackListDirty)
{
//Debug.Log("2 - Cleaning up Fallback Materials.");
CleanupFallbackMaterials();
isFallbackListDirty = false;
}
}
/// <summary>
/// Create a Masking Material Instance for the given ID
/// </summary>
/// <param name="baseMaterial"></param>
/// <param name="stencilID"></param>
/// <returns></returns>
public static Material GetStencilMaterial(Material baseMaterial, int stencilID)
{
// Check if Material supports masking
if (!baseMaterial.HasProperty(ShaderUtilities.ID_StencilID))
{
Debug.LogWarning("Selected Shader does not support Stencil Masking. Please select the Distance Field or Mobile Distance Field Shader.");
return baseMaterial;
}
int baseMaterialID = baseMaterial.GetInstanceID();
// If baseMaterial already has a corresponding masking material, return it.
for (int i = 0; i < m_materialList.Count; i++)
{
if (m_materialList[i].baseMaterial.GetInstanceID() == baseMaterialID && m_materialList[i].stencilID == stencilID)
{
m_materialList[i].count += 1;
#if TMP_DEBUG_MODE
ListMaterials();
#endif
return m_materialList[i].stencilMaterial;
}
}
// No matching masking material found. Create and return a new one.
Material stencilMaterial;
//Create new Masking Material Instance for this Base Material
stencilMaterial = new Material(baseMaterial);
stencilMaterial.hideFlags = HideFlags.HideAndDontSave;
#if UNITY_EDITOR
stencilMaterial.name += " Masking ID:" + stencilID;
#endif
stencilMaterial.shaderKeywords = baseMaterial.shaderKeywords;
// Set Stencil Properties
ShaderUtilities.GetShaderPropertyIDs();
stencilMaterial.SetFloat(ShaderUtilities.ID_StencilID, stencilID);
//stencilMaterial.SetFloat(ShaderUtilities.ID_StencilOp, 0);
stencilMaterial.SetFloat(ShaderUtilities.ID_StencilComp, 4);
//stencilMaterial.SetFloat(ShaderUtilities.ID_StencilReadMask, stencilID);
//stencilMaterial.SetFloat(ShaderUtilities.ID_StencilWriteMask, 0);
MaskingMaterial temp = new MaskingMaterial();
temp.baseMaterial = baseMaterial;
temp.stencilMaterial = stencilMaterial;
temp.stencilID = stencilID;
temp.count = 1;
m_materialList.Add(temp);
#if TMP_DEBUG_MODE
ListMaterials();
#endif
return stencilMaterial;
}
/// <summary>
/// Function to release the stencil material.
/// </summary>
/// <param name="stencilMaterial"></param>
public static void ReleaseStencilMaterial(Material stencilMaterial)
{
int stencilMaterialID = stencilMaterial.GetInstanceID();
for (int i = 0; i < m_materialList.Count; i++)
{
if (m_materialList[i].stencilMaterial.GetInstanceID() == stencilMaterialID)
{
if (m_materialList[i].count > 1)
m_materialList[i].count -= 1;
else
{
Object.DestroyImmediate(m_materialList[i].stencilMaterial);
m_materialList.RemoveAt(i);
stencilMaterial = null;
}
break;
}
}
#if TMP_DEBUG_MODE
ListMaterials();
#endif
}
// Function which returns the base material associated with a Masking Material
public static Material GetBaseMaterial(Material stencilMaterial)
{
// Check if maskingMaterial already has a base material associated with it.
int index = m_materialList.FindIndex(item => item.stencilMaterial == stencilMaterial);
if (index == -1)
return null;
else
return m_materialList[index].baseMaterial;
}
/// <summary>
/// Function to set the Material Stencil ID
/// </summary>
/// <param name="material"></param>
/// <param name="stencilID"></param>
/// <returns></returns>
public static Material SetStencil(Material material, int stencilID)
{
material.SetFloat(ShaderUtilities.ID_StencilID, stencilID);
if (stencilID == 0)
material.SetFloat(ShaderUtilities.ID_StencilComp, 8);
else
material.SetFloat(ShaderUtilities.ID_StencilComp, 4);
return material;
}
public static void AddMaskingMaterial(Material baseMaterial, Material stencilMaterial, int stencilID)
{
// Check if maskingMaterial already has a base material associated with it.
int index = m_materialList.FindIndex(item => item.stencilMaterial == stencilMaterial);
if (index == -1)
{
MaskingMaterial temp = new MaskingMaterial();
temp.baseMaterial = baseMaterial;
temp.stencilMaterial = stencilMaterial;
temp.stencilID = stencilID;
temp.count = 1;
m_materialList.Add(temp);
}
else
{
stencilMaterial = m_materialList[index].stencilMaterial;
m_materialList[index].count += 1;
}
}
public static void RemoveStencilMaterial(Material stencilMaterial)
{
// Check if maskingMaterial is already on the list.
int index = m_materialList.FindIndex(item => item.stencilMaterial == stencilMaterial);
if (index != -1)
{
m_materialList.RemoveAt(index);
}
#if TMP_DEBUG_MODE
ListMaterials();
#endif
}
public static void ReleaseBaseMaterial(Material baseMaterial)
{
// Check if baseMaterial already has a masking material associated with it.
int index = m_materialList.FindIndex(item => item.baseMaterial == baseMaterial);
if (index == -1)
{
Debug.Log("No Masking Material exists for " + baseMaterial.name);
}
else
{
if (m_materialList[index].count > 1)
{
m_materialList[index].count -= 1;
Debug.Log("Removed (1) reference to " + m_materialList[index].stencilMaterial.name + ". There are " + m_materialList[index].count + " references left.");
}
else
{
Debug.Log("Removed last reference to " + m_materialList[index].stencilMaterial.name + " with ID " + m_materialList[index].stencilMaterial.GetInstanceID());
Object.DestroyImmediate(m_materialList[index].stencilMaterial);
m_materialList.RemoveAt(index);
}
}
#if TMP_DEBUG_MODE
ListMaterials();
#endif
}
public static void ClearMaterials()
{
if (m_materialList.Count == 0)
{
Debug.Log("Material List has already been cleared.");
return;
}
for (int i = 0; i < m_materialList.Count; i++)
{
//Material baseMaterial = m_materialList[i].baseMaterial;
Material stencilMaterial = m_materialList[i].stencilMaterial;
Object.DestroyImmediate(stencilMaterial);
}
m_materialList.Clear();
}
/// <summary>
/// Function to get the Stencil ID
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static int GetStencilID(GameObject obj)
{
// Implementation is almost copied from Unity UI
var count = 0;
var transform = obj.transform;
var stopAfter = FindRootSortOverrideCanvas(transform);
if (transform == stopAfter)
return count;
var t = transform.parent;
var components = TMP_ListPool<Mask>.Get();
while (t != null)
{
t.GetComponents<Mask>(components);
for (var i = 0; i < components.Count; ++i)
{
var mask = components[i];
if (mask != null && mask.MaskEnabled() && mask.graphic.IsActive())
{
++count;
break;
}
}
if (t == stopAfter)
break;
t = t.parent;
}
TMP_ListPool<Mask>.Release(components);
return Mathf.Min((1 << count) - 1, 255);
}
public static Material GetMaterialForRendering(MaskableGraphic graphic, Material baseMaterial)
{
if (baseMaterial == null)
return null;
var modifiers = TMP_ListPool<IMaterialModifier>.Get();
graphic.GetComponents(modifiers);
var result = baseMaterial;
for (int i = 0; i < modifiers.Count; i++)
result = modifiers[i].GetModifiedMaterial(result);
TMP_ListPool<IMaterialModifier>.Release(modifiers);
return result;
}
private static Transform FindRootSortOverrideCanvas(Transform start)
{
// Implementation is copied from Unity UI
var canvasList = TMP_ListPool<Canvas>.Get();
start.GetComponentsInParent(false, canvasList);
Canvas canvas = null;
for (int i = 0; i < canvasList.Count; ++i)
{
canvas = canvasList[i];
// We found the canvas we want to use break
if (canvas.overrideSorting)
break;
}
TMP_ListPool<Canvas>.Release(canvasList);
return canvas != null ? canvas.transform : null;
}
internal static Material GetFallbackMaterial(TMP_FontAsset fontAsset, Material sourceMaterial, int atlasIndex)
{
int sourceMaterialID = sourceMaterial.GetInstanceID();
Texture tex = fontAsset.atlasTextures[atlasIndex];
int texID = tex.GetInstanceID();
long key = (long)sourceMaterialID << 32 | (long)(uint)texID;
FallbackMaterial fallback;
if (m_fallbackMaterials.TryGetValue(key, out fallback))
{
// Check if source material properties have changed.
int sourceMaterialCRC = sourceMaterial.ComputeCRC();
if (sourceMaterialCRC == fallback.sourceMaterialCRC)
return fallback.fallbackMaterial;
CopyMaterialPresetProperties(sourceMaterial, fallback.fallbackMaterial);
fallback.sourceMaterialCRC = sourceMaterialCRC;
return fallback.fallbackMaterial;
}
// Create new material from the source material and assign relevant atlas texture
Material fallbackMaterial = new Material(sourceMaterial);
fallbackMaterial.SetTexture(ShaderUtilities.ID_MainTex, tex);
fallbackMaterial.hideFlags = HideFlags.HideAndDontSave;
#if UNITY_EDITOR
fallbackMaterial.name += " + " + tex.name;
#endif
fallback = new FallbackMaterial();
fallback.fallbackID = key;
fallback.sourceMaterial = fontAsset.material;
fallback.sourceMaterialCRC = sourceMaterial.ComputeCRC();
fallback.fallbackMaterial = fallbackMaterial;
fallback.count = 0;
m_fallbackMaterials.Add(key, fallback);
m_fallbackMaterialLookup.Add(fallbackMaterial.GetInstanceID(), key);
#if TMP_DEBUG_MODE
ListFallbackMaterials();
#endif
return fallbackMaterial;
}
/// <summary>
/// This function returns a material instance using the material properties of a previous material but using the font atlas texture of the new font asset.
/// </summary>
/// <param name="sourceMaterial">The material containing the source material properties to be copied to the new material.</param>
/// <param name="targetMaterial">The font atlas texture that should be assigned to the new material.</param>
/// <returns></returns>
public static Material GetFallbackMaterial (Material sourceMaterial, Material targetMaterial)
{
int sourceID = sourceMaterial.GetInstanceID();
Texture tex = targetMaterial.GetTexture(ShaderUtilities.ID_MainTex);
int texID = tex.GetInstanceID();
long key = (long)sourceID << 32 | (long)(uint)texID;
FallbackMaterial fallback;
if (m_fallbackMaterials.TryGetValue(key, out fallback))
{
// Check if source material properties have changed.
int sourceMaterialCRC = sourceMaterial.ComputeCRC();
if (sourceMaterialCRC == fallback.sourceMaterialCRC)
return fallback.fallbackMaterial;
CopyMaterialPresetProperties(sourceMaterial, fallback.fallbackMaterial);
fallback.sourceMaterialCRC = sourceMaterialCRC;
return fallback.fallbackMaterial;
}
// Create new material from the source material and copy properties if using distance field shaders.
Material fallbackMaterial;
if (sourceMaterial.HasProperty(ShaderUtilities.ID_GradientScale) && targetMaterial.HasProperty(ShaderUtilities.ID_GradientScale))
{
fallbackMaterial = new Material(sourceMaterial);
fallbackMaterial.hideFlags = HideFlags.HideAndDontSave;
#if UNITY_EDITOR
fallbackMaterial.name += " + " + tex.name;
//Debug.Log("Creating new fallback material for " + fallbackMaterial.name);
#endif
fallbackMaterial.SetTexture(ShaderUtilities.ID_MainTex, tex);
// Retain material properties unique to target material.
fallbackMaterial.SetFloat(ShaderUtilities.ID_GradientScale, targetMaterial.GetFloat(ShaderUtilities.ID_GradientScale));
fallbackMaterial.SetFloat(ShaderUtilities.ID_TextureWidth, targetMaterial.GetFloat(ShaderUtilities.ID_TextureWidth));
fallbackMaterial.SetFloat(ShaderUtilities.ID_TextureHeight, targetMaterial.GetFloat(ShaderUtilities.ID_TextureHeight));
fallbackMaterial.SetFloat(ShaderUtilities.ID_WeightNormal, targetMaterial.GetFloat(ShaderUtilities.ID_WeightNormal));
fallbackMaterial.SetFloat(ShaderUtilities.ID_WeightBold, targetMaterial.GetFloat(ShaderUtilities.ID_WeightBold));
}
else
{
fallbackMaterial = new Material(targetMaterial);
}
fallback = new FallbackMaterial();
fallback.fallbackID = key;
fallback.sourceMaterial = sourceMaterial;
fallback.sourceMaterialCRC = sourceMaterial.ComputeCRC();
fallback.fallbackMaterial = fallbackMaterial;
fallback.count = 0;
m_fallbackMaterials.Add(key, fallback);
m_fallbackMaterialLookup.Add(fallbackMaterial.GetInstanceID(), key);
#if TMP_DEBUG_MODE
ListFallbackMaterials();
#endif
return fallbackMaterial;
}
/// <summary>
///
/// </summary>
/// <param name="targetMaterial"></param>
public static void AddFallbackMaterialReference(Material targetMaterial)
{
if (targetMaterial == null) return;
int sourceID = targetMaterial.GetInstanceID();
long key;
// Lookup key to retrieve
if (m_fallbackMaterialLookup.TryGetValue(sourceID, out key))
{
FallbackMaterial fallback;
if (m_fallbackMaterials.TryGetValue(key, out fallback))
{
//Debug.Log("Adding Fallback material " + fallback.fallbackMaterial.name + " with reference count of " + (fallback.count + 1));
fallback.count += 1;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="targetMaterial"></param>
public static void RemoveFallbackMaterialReference(Material targetMaterial)
{
if (targetMaterial == null) return;
int sourceID = targetMaterial.GetInstanceID();
long key;
// Lookup key to retrieve
if (m_fallbackMaterialLookup.TryGetValue(sourceID, out key))
{
FallbackMaterial fallback;
if (m_fallbackMaterials.TryGetValue(key, out fallback))
{
fallback.count -= 1;
if (fallback.count < 1)
m_fallbackCleanupList.Add(fallback);
}
}
}
/// <summary>
///
/// </summary>
public static void CleanupFallbackMaterials()
{
// Return if the list is empty.
if (m_fallbackCleanupList.Count == 0) return;
for (int i = 0; i < m_fallbackCleanupList.Count; i++)
{
FallbackMaterial fallback = m_fallbackCleanupList[i];
if (fallback.count < 1)
{
//Debug.Log("Cleaning up " + fallback.fallbackMaterial.name);
Material mat = fallback.fallbackMaterial;
m_fallbackMaterials.Remove(fallback.fallbackID);
m_fallbackMaterialLookup.Remove(mat.GetInstanceID());
Object.DestroyImmediate(mat);
mat = null;
}
}
m_fallbackCleanupList.Clear();
}
/// <summary>
/// Function to release the fallback material.
/// </summary>
/// <param name="fallbackMaterial">Material to be released.</param>
public static void ReleaseFallbackMaterial(Material fallbackMaterial)
{
if (fallbackMaterial == null) return;
int materialID = fallbackMaterial.GetInstanceID();
long key;
if (m_fallbackMaterialLookup.TryGetValue(materialID, out key))
{
FallbackMaterial fallback;
if (m_fallbackMaterials.TryGetValue(key, out fallback))
{
//Debug.Log("Releasing Fallback material " + fallback.fallbackMaterial.name + " with remaining reference count of " + (fallback.count - 1));
fallback.count -= 1;
if (fallback.count < 1)
m_fallbackCleanupList.Add(fallback);
}
}
isFallbackListDirty = true;
#if TMP_DEBUG_MODE
ListFallbackMaterials();
#endif
}
private class FallbackMaterial
{
public long fallbackID;
public Material sourceMaterial;
internal int sourceMaterialCRC;
public Material fallbackMaterial;
public int count;
}
private class MaskingMaterial
{
public Material baseMaterial;
public Material stencilMaterial;
public int count;
public int stencilID;
}
/// <summary>
/// Function to copy the properties of a source material preset to another while preserving the unique font asset properties of the destination material.
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
public static void CopyMaterialPresetProperties(Material source, Material destination)
{
if (!source.HasProperty(ShaderUtilities.ID_GradientScale) || !destination.HasProperty(ShaderUtilities.ID_GradientScale))
return;
// Save unique material properties
Texture dst_texture = destination.GetTexture(ShaderUtilities.ID_MainTex);
float dst_gradientScale = destination.GetFloat(ShaderUtilities.ID_GradientScale);
float dst_texWidth = destination.GetFloat(ShaderUtilities.ID_TextureWidth);
float dst_texHeight = destination.GetFloat(ShaderUtilities.ID_TextureHeight);
float dst_weightNormal = destination.GetFloat(ShaderUtilities.ID_WeightNormal);
float dst_weightBold = destination.GetFloat(ShaderUtilities.ID_WeightBold);
// Copy all material properties
destination.CopyPropertiesFromMaterial(source);
// Copy shader keywords
destination.shaderKeywords = source.shaderKeywords;
// Restore unique material properties
destination.SetTexture(ShaderUtilities.ID_MainTex, dst_texture);
destination.SetFloat(ShaderUtilities.ID_GradientScale, dst_gradientScale);
destination.SetFloat(ShaderUtilities.ID_TextureWidth, dst_texWidth);
destination.SetFloat(ShaderUtilities.ID_TextureHeight, dst_texHeight);
destination.SetFloat(ShaderUtilities.ID_WeightNormal, dst_weightNormal);
destination.SetFloat(ShaderUtilities.ID_WeightBold, dst_weightBold);
}
#if TMP_DEBUG_MODE
/// <summary>
///
/// </summary>
public static void ListMaterials()
{
if (m_materialList.Count == 0)
{
Debug.Log("Material List is empty.");
return;
}
//Debug.Log("List contains " + m_materialList.Count() + " items.");
for (int i = 0; i < m_materialList.Count; i++)
{
Material baseMaterial = m_materialList[i].baseMaterial;
Material stencilMaterial = m_materialList[i].stencilMaterial;
Debug.Log("Item #" + (i + 1) + " - Base Material is [" + baseMaterial.name + "] with ID " + baseMaterial.GetInstanceID() + " is associated with [" + (stencilMaterial != null ? stencilMaterial.name : "Null") + "] Stencil ID " + m_materialList[i].stencilID + " with ID " + (stencilMaterial != null ? stencilMaterial.GetInstanceID() : 0) + " and is referenced " + m_materialList[i].count + " time(s).");
}
}
/// <summary>
///
/// </summary>
public static void ListFallbackMaterials()
{
if (m_fallbackMaterials.Count == 0)
{
Debug.Log("Material List is empty.");
return;
}
Debug.Log("List contains " + m_fallbackMaterials.Count + " items.");
int count = 0;
foreach (var fallback in m_fallbackMaterials)
{
Material baseMaterial = fallback.Value.baseMaterial;
Material fallbackMaterial = fallback.Value.fallbackMaterial;
string output = "Item #" + (count++);
if (baseMaterial != null)
output += " - Base Material is [" + baseMaterial.name + "] with ID " + baseMaterial.GetInstanceID();
if (fallbackMaterial != null)
output += " is associated with [" + fallbackMaterial.name + "] with ID " + fallbackMaterial.GetInstanceID() + " and is referenced " + fallback.Value.count + " time(s).";
Debug.Log(output);
}
}
#endif
}
}

View File

@@ -0,0 +1,675 @@
using UnityEngine;
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Rendering;
namespace TMPro
{
public enum VertexSortingOrder { Normal, Reverse };
/// <summary>
/// Structure which contains the vertex attributes (geometry) of the text object.
/// </summary>
public struct TMP_MeshInfo
{
private static readonly Color32 s_DefaultColor = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
private static readonly Vector3 s_DefaultNormal = new Vector3(0.0f, 0.0f, -1f);
private static readonly Vector4 s_DefaultTangent = new Vector4(-1f, 0.0f, 0.0f, 1f);
private static readonly Bounds s_DefaultBounds = new Bounds();
public Mesh mesh;
public int vertexCount;
public Vector3[] vertices;
public Vector3[] normals;
public Vector4[] tangents;
public Vector2[] uvs0;
public Vector2[] uvs2;
//public Vector2[] uvs4;
public Color32[] colors32;
public int[] triangles;
public Material material;
/// <summary>
/// Function to pre-allocate vertex attributes for a mesh of size X.
/// </summary>
/// <param name="mesh"></param>
/// <param name="size"></param>
public TMP_MeshInfo(Mesh mesh, int size)
{
// Reference to the TMP Text Component.
//this.textComponent = null;
// Clear existing mesh data
if (mesh == null)
mesh = new Mesh();
else
mesh.Clear();
this.mesh = mesh;
// Limit the mesh to less than 65535 vertices which is the limit for Unity's Mesh.
size = Mathf.Min(size, 16383);
int sizeX4 = size * 4;
int sizeX6 = size * 6;
this.vertexCount = 0;
this.vertices = new Vector3[sizeX4];
this.uvs0 = new Vector2[sizeX4];
this.uvs2 = new Vector2[sizeX4];
//this.uvs4 = new Vector2[sizeX4]; // SDF scale data
this.colors32 = new Color32[sizeX4];
this.normals = new Vector3[sizeX4];
this.tangents = new Vector4[sizeX4];
this.triangles = new int[sizeX6];
int index_X6 = 0;
int index_X4 = 0;
while (index_X4 / 4 < size)
{
for (int i = 0; i < 4; i++)
{
this.vertices[index_X4 + i] = Vector3.zero;
this.uvs0[index_X4 + i] = Vector2.zero;
this.uvs2[index_X4 + i] = Vector2.zero;
//this.uvs4[index_X4 + i] = Vector2.zero;
this.colors32[index_X4 + i] = s_DefaultColor;
this.normals[index_X4 + i] = s_DefaultNormal;
this.tangents[index_X4 + i] = s_DefaultTangent;
}
this.triangles[index_X6 + 0] = index_X4 + 0;
this.triangles[index_X6 + 1] = index_X4 + 1;
this.triangles[index_X6 + 2] = index_X4 + 2;
this.triangles[index_X6 + 3] = index_X4 + 2;
this.triangles[index_X6 + 4] = index_X4 + 3;
this.triangles[index_X6 + 5] = index_X4 + 0;
index_X4 += 4;
index_X6 += 6;
}
// Pre-assign base vertex attributes.
this.mesh.vertices = this.vertices;
this.mesh.normals = this.normals;
this.mesh.tangents = this.tangents;
this.mesh.triangles = this.triangles;
this.mesh.bounds = s_DefaultBounds;
this.material = null;
}
/// <summary>
/// Function to pre-allocate vertex attributes for a mesh of size X.
/// </summary>
/// <param name="mesh"></param>
/// <param name="size"></param>
/// <param name="isVolumetric"></param>
public TMP_MeshInfo(Mesh mesh, int size, bool isVolumetric)
{
// Reference to the TMP Text Component.
//this.textComponent = null;
// Clear existing mesh data
if (mesh == null)
mesh = new Mesh();
else
mesh.Clear();
this.mesh = mesh;
int s0 = !isVolumetric ? 4 : 8;
int s1 = !isVolumetric ? 6 : 36;
// Limit the mesh to less than 65535 vertices which is the limit for Unity's Mesh.
size = Mathf.Min(size, 65532 / s0);
int size_x_s0 = size * s0;
int size_x_s1 = size * s1;
this.vertexCount = 0;
this.vertices = new Vector3[size_x_s0];
this.uvs0 = new Vector2[size_x_s0];
this.uvs2 = new Vector2[size_x_s0];
//this.uvs4 = new Vector2[sizeX8]; // SDF scale data
this.colors32 = new Color32[size_x_s0];
this.normals = new Vector3[size_x_s0];
this.tangents = new Vector4[size_x_s0];
this.triangles = new int[size_x_s1];
int index_x_s0 = 0;
int index_x_s1 = 0;
while (index_x_s0 / s0 < size)
{
for (int i = 0; i < s0; i++)
{
this.vertices[index_x_s0 + i] = Vector3.zero;
this.uvs0[index_x_s0 + i] = Vector2.zero;
this.uvs2[index_x_s0 + i] = Vector2.zero;
//this.uvs4[index_X4 + i] = Vector2.zero;
this.colors32[index_x_s0 + i] = s_DefaultColor;
this.normals[index_x_s0 + i] = s_DefaultNormal;
this.tangents[index_x_s0 + i] = s_DefaultTangent;
}
// Front Face
this.triangles[index_x_s1 + 0] = index_x_s0 + 0;
this.triangles[index_x_s1 + 1] = index_x_s0 + 1;
this.triangles[index_x_s1 + 2] = index_x_s0 + 2;
this.triangles[index_x_s1 + 3] = index_x_s0 + 2;
this.triangles[index_x_s1 + 4] = index_x_s0 + 3;
this.triangles[index_x_s1 + 5] = index_x_s0 + 0;
if (isVolumetric)
{
// Left Face
this.triangles[index_x_s1 + 6] = index_x_s0 + 4;
this.triangles[index_x_s1 + 7] = index_x_s0 + 5;
this.triangles[index_x_s1 + 8] = index_x_s0 + 1;
this.triangles[index_x_s1 + 9] = index_x_s0 + 1;
this.triangles[index_x_s1 + 10] = index_x_s0 + 0;
this.triangles[index_x_s1 + 11] = index_x_s0 + 4;
// Right Face
this.triangles[index_x_s1 + 12] = index_x_s0 + 3;
this.triangles[index_x_s1 + 13] = index_x_s0 + 2;
this.triangles[index_x_s1 + 14] = index_x_s0 + 6;
this.triangles[index_x_s1 + 15] = index_x_s0 + 6;
this.triangles[index_x_s1 + 16] = index_x_s0 + 7;
this.triangles[index_x_s1 + 17] = index_x_s0 + 3;
// Top Face
this.triangles[index_x_s1 + 18] = index_x_s0 + 1;
this.triangles[index_x_s1 + 19] = index_x_s0 + 5;
this.triangles[index_x_s1 + 20] = index_x_s0 + 6;
this.triangles[index_x_s1 + 21] = index_x_s0 + 6;
this.triangles[index_x_s1 + 22] = index_x_s0 + 2;
this.triangles[index_x_s1 + 23] = index_x_s0 + 1;
// Bottom Face
this.triangles[index_x_s1 + 24] = index_x_s0 + 4;
this.triangles[index_x_s1 + 25] = index_x_s0 + 0;
this.triangles[index_x_s1 + 26] = index_x_s0 + 3;
this.triangles[index_x_s1 + 27] = index_x_s0 + 3;
this.triangles[index_x_s1 + 28] = index_x_s0 + 7;
this.triangles[index_x_s1 + 29] = index_x_s0 + 4;
// Back Face
this.triangles[index_x_s1 + 30] = index_x_s0 + 7;
this.triangles[index_x_s1 + 31] = index_x_s0 + 6;
this.triangles[index_x_s1 + 32] = index_x_s0 + 5;
this.triangles[index_x_s1 + 33] = index_x_s0 + 5;
this.triangles[index_x_s1 + 34] = index_x_s0 + 4;
this.triangles[index_x_s1 + 35] = index_x_s0 + 7;
}
index_x_s0 += s0;
index_x_s1 += s1;
}
// Pre-assign base vertex attributes.
this.mesh.vertices = this.vertices;
this.mesh.normals = this.normals;
this.mesh.tangents = this.tangents;
this.mesh.triangles = this.triangles;
this.mesh.bounds = s_DefaultBounds;
this.material = null;
}
/// <summary>
/// Function to resized the content of MeshData and re-assign normals, tangents and triangles.
/// </summary>
/// <param name="meshData"></param>
/// <param name="size"></param>
public void ResizeMeshInfo(int size)
{
// If the requested size will exceed the 16 bit mesh limit, switch mesh to use 32 bit.
//if (size > 16383 && this.mesh.indexFormat == IndexFormat.UInt16)
// this.mesh.indexFormat = IndexFormat.UInt32;
size = Mathf.Min(size, 16383);
int size_X4 = size * 4;
int size_X6 = size * 6;
int previousSize = this.vertices.Length / 4;
Array.Resize(ref this.vertices, size_X4);
Array.Resize(ref this.normals, size_X4);
Array.Resize(ref this.tangents, size_X4);
Array.Resize(ref this.uvs0, size_X4);
Array.Resize(ref this.uvs2, size_X4);
//Array.Resize(ref this.uvs4, size_X4);
Array.Resize(ref this.colors32, size_X4);
Array.Resize(ref this.triangles, size_X6);
// Re-assign Normals, Tangents and Triangles
if (size <= previousSize)
{
this.mesh.triangles = this.triangles;
this.mesh.vertices = this.vertices;
this.mesh.normals = this.normals;
this.mesh.tangents = this.tangents;
return;
}
for (int i = previousSize; i < size; i++)
{
int index_X4 = i * 4;
int index_X6 = i * 6;
this.normals[0 + index_X4] = s_DefaultNormal;
this.normals[1 + index_X4] = s_DefaultNormal;
this.normals[2 + index_X4] = s_DefaultNormal;
this.normals[3 + index_X4] = s_DefaultNormal;
this.tangents[0 + index_X4] = s_DefaultTangent;
this.tangents[1 + index_X4] = s_DefaultTangent;
this.tangents[2 + index_X4] = s_DefaultTangent;
this.tangents[3 + index_X4] = s_DefaultTangent;
// Setup Triangles
this.triangles[0 + index_X6] = 0 + index_X4;
this.triangles[1 + index_X6] = 1 + index_X4;
this.triangles[2 + index_X6] = 2 + index_X4;
this.triangles[3 + index_X6] = 2 + index_X4;
this.triangles[4 + index_X6] = 3 + index_X4;
this.triangles[5 + index_X6] = 0 + index_X4;
}
this.mesh.vertices = this.vertices;
this.mesh.normals = this.normals;
this.mesh.tangents = this.tangents;
this.mesh.triangles = this.triangles;
}
/// <summary>
/// Function to resized the content of MeshData and re-assign normals, tangents and triangles.
/// </summary>
/// <param name="size"></param>
/// <param name="isVolumetric"></param>
public void ResizeMeshInfo(int size, bool isVolumetric)
{
int s0 = !isVolumetric ? 4 : 8;
int s1 = !isVolumetric ? 6 : 36;
// Limit the mesh to less than 65535 vertices which is the limit for Unity's Mesh.
size = Mathf.Min(size, 65532 / s0);
int size_X4 = size * s0;
int size_X6 = size * s1;
int previousSize = this.vertices.Length / s0;
Array.Resize(ref this.vertices, size_X4);
Array.Resize(ref this.normals, size_X4);
Array.Resize(ref this.tangents, size_X4);
Array.Resize(ref this.uvs0, size_X4);
Array.Resize(ref this.uvs2, size_X4);
//Array.Resize(ref this.uvs4, size_X4);
Array.Resize(ref this.colors32, size_X4);
Array.Resize(ref this.triangles, size_X6);
// Re-assign Normals, Tangents and Triangles
if (size <= previousSize)
{
this.mesh.triangles = this.triangles;
this.mesh.vertices = this.vertices;
this.mesh.normals = this.normals;
this.mesh.tangents = this.tangents;
return;
}
for (int i = previousSize; i < size; i++)
{
int index_X4 = i * s0;
int index_X6 = i * s1;
this.normals[0 + index_X4] = s_DefaultNormal;
this.normals[1 + index_X4] = s_DefaultNormal;
this.normals[2 + index_X4] = s_DefaultNormal;
this.normals[3 + index_X4] = s_DefaultNormal;
this.tangents[0 + index_X4] = s_DefaultTangent;
this.tangents[1 + index_X4] = s_DefaultTangent;
this.tangents[2 + index_X4] = s_DefaultTangent;
this.tangents[3 + index_X4] = s_DefaultTangent;
if (isVolumetric)
{
this.normals[4 + index_X4] = s_DefaultNormal;
this.normals[5 + index_X4] = s_DefaultNormal;
this.normals[6 + index_X4] = s_DefaultNormal;
this.normals[7 + index_X4] = s_DefaultNormal;
this.tangents[4 + index_X4] = s_DefaultTangent;
this.tangents[5 + index_X4] = s_DefaultTangent;
this.tangents[6 + index_X4] = s_DefaultTangent;
this.tangents[7 + index_X4] = s_DefaultTangent;
}
// Setup Triangles
this.triangles[0 + index_X6] = 0 + index_X4;
this.triangles[1 + index_X6] = 1 + index_X4;
this.triangles[2 + index_X6] = 2 + index_X4;
this.triangles[3 + index_X6] = 2 + index_X4;
this.triangles[4 + index_X6] = 3 + index_X4;
this.triangles[5 + index_X6] = 0 + index_X4;
if (isVolumetric)
{
// Left Face
this.triangles[index_X6 + 6] = index_X4 + 4;
this.triangles[index_X6 + 7] = index_X4 + 5;
this.triangles[index_X6 + 8] = index_X4 + 1;
this.triangles[index_X6 + 9] = index_X4 + 1;
this.triangles[index_X6 + 10] = index_X4 + 0;
this.triangles[index_X6 + 11] = index_X4 + 4;
// Right Face
this.triangles[index_X6 + 12] = index_X4 + 3;
this.triangles[index_X6 + 13] = index_X4 + 2;
this.triangles[index_X6 + 14] = index_X4 + 6;
this.triangles[index_X6 + 15] = index_X4 + 6;
this.triangles[index_X6 + 16] = index_X4 + 7;
this.triangles[index_X6 + 17] = index_X4 + 3;
// Top Face
this.triangles[index_X6 + 18] = index_X4 + 1;
this.triangles[index_X6 + 19] = index_X4 + 5;
this.triangles[index_X6 + 20] = index_X4 + 6;
this.triangles[index_X6 + 21] = index_X4 + 6;
this.triangles[index_X6 + 22] = index_X4 + 2;
this.triangles[index_X6 + 23] = index_X4 + 1;
// Bottom Face
this.triangles[index_X6 + 24] = index_X4 + 4;
this.triangles[index_X6 + 25] = index_X4 + 0;
this.triangles[index_X6 + 26] = index_X4 + 3;
this.triangles[index_X6 + 27] = index_X4 + 3;
this.triangles[index_X6 + 28] = index_X4 + 7;
this.triangles[index_X6 + 29] = index_X4 + 4;
// Back Face
this.triangles[index_X6 + 30] = index_X4 + 7;
this.triangles[index_X6 + 31] = index_X4 + 6;
this.triangles[index_X6 + 32] = index_X4 + 5;
this.triangles[index_X6 + 33] = index_X4 + 5;
this.triangles[index_X6 + 34] = index_X4 + 4;
this.triangles[index_X6 + 35] = index_X4 + 7;
}
}
this.mesh.vertices = this.vertices;
this.mesh.normals = this.normals;
this.mesh.tangents = this.tangents;
this.mesh.triangles = this.triangles;
}
/// <summary>
/// Function to clear the vertices while preserving the Triangles, Normals and Tangents.
/// </summary>
public void Clear()
{
if (this.vertices == null) return;
Array.Clear(this.vertices, 0, this.vertices.Length);
this.vertexCount = 0;
if (this.mesh != null)
this.mesh.vertices = this.vertices;
}
/// <summary>
/// Function to clear the vertices while preserving the Triangles, Normals and Tangents.
/// </summary>
public void Clear(bool uploadChanges)
{
if (this.vertices == null) return;
Array.Clear(this.vertices, 0, this.vertices.Length);
this.vertexCount = 0;
if (uploadChanges && this.mesh != null)
this.mesh.vertices = this.vertices;
if (this.mesh != null)
this.mesh.bounds = s_DefaultBounds;
}
/// <summary>
/// Function to clear the vertices while preserving the Triangles, Normals and Tangents.
/// </summary>
public void ClearUnusedVertices()
{
int length = vertices.Length - vertexCount;
if (length > 0)
Array.Clear(vertices, vertexCount, length);
}
/// <summary>
/// Function used to mark unused vertices as degenerate.
/// </summary>
/// <param name="startIndex"></param>
public void ClearUnusedVertices(int startIndex)
{
int length = this.vertices.Length - startIndex;
if (length > 0)
Array.Clear(this.vertices, startIndex, length);
}
/// <summary>
/// Function used to mark unused vertices as degenerate an upload resulting data to the mesh.
/// </summary>
/// <param name="startIndex"></param>
public void ClearUnusedVertices(int startIndex, bool updateMesh)
{
int length = this.vertices.Length - startIndex;
if (length > 0)
Array.Clear(this.vertices, startIndex, length);
if (updateMesh && mesh != null)
this.mesh.vertices = this.vertices;
}
public void SortGeometry (VertexSortingOrder order)
{
switch (order)
{
case VertexSortingOrder.Normal:
// Do nothing
break;
case VertexSortingOrder.Reverse:
int size = vertexCount / 4;
for (int i = 0; i < size; i++)
{
int src = i * 4;
int dst = (size - i - 1) * 4;
if (src < dst)
SwapVertexData(src, dst);
}
break;
//case VertexSortingOrder.Depth:
// break;
}
}
/// <summary>
/// Function to rearrange the quads of the text object to change their rendering order.
/// </summary>
/// <param name="sortingOrder"></param>
public void SortGeometry(IList<int> sortingOrder)
{
// Make sure the sorting order array is not larger than the vertices array.
int indexCount = sortingOrder.Count;
if (indexCount * 4 > vertices.Length) return;
int src_index;
for (int dst_index = 0; dst_index < indexCount; dst_index++)
{
src_index = sortingOrder[dst_index];
while (src_index < dst_index)
{
src_index = sortingOrder[src_index];
}
// Swap items
if (src_index != dst_index)
SwapVertexData(src_index * 4, dst_index * 4);
//Debug.Log("Swap element [" + dst_index + "] with [" + src_index + "]. Vertex[" + dst_index + "] is " + vertices[dst_index * 4].z);
}
}
/// <summary>
/// Method to swap the vertex attributes between src and dst quads.
/// </summary>
/// <param name="src">Index of the first vertex attribute of the source character / quad.</param>
/// <param name="dst">Index of the first vertex attribute of the destination character / quad.</param>
public void SwapVertexData(int src, int dst)
{
int src_Index = src; // * 4;
int dst_Index = dst; // * 4;
// Swap vertices
Vector3 vertex;
vertex = vertices[dst_Index + 0];
vertices[dst_Index + 0] = vertices[src_Index + 0];
vertices[src_Index + 0] = vertex;
vertex = vertices[dst_Index + 1];
vertices[dst_Index + 1] = vertices[src_Index + 1];
vertices[src_Index + 1] = vertex;
vertex = vertices[dst_Index + 2];
vertices[dst_Index + 2] = vertices[src_Index + 2];
vertices[src_Index + 2] = vertex;
vertex = vertices[dst_Index + 3];
vertices[dst_Index + 3] = vertices[src_Index + 3];
vertices[src_Index + 3] = vertex;
//Swap UVs0
Vector2 uvs;
uvs = uvs0[dst_Index + 0];
uvs0[dst_Index + 0] = uvs0[src_Index + 0];
uvs0[src_Index + 0] = uvs;
uvs = uvs0[dst_Index + 1];
uvs0[dst_Index + 1] = uvs0[src_Index + 1];
uvs0[src_Index + 1] = uvs;
uvs = uvs0[dst_Index + 2];
uvs0[dst_Index + 2] = uvs0[src_Index + 2];
uvs0[src_Index + 2] = uvs;
uvs = uvs0[dst_Index + 3];
uvs0[dst_Index + 3] = uvs0[src_Index + 3];
uvs0[src_Index + 3] = uvs;
// Swap UVs2
uvs = uvs2[dst_Index + 0];
uvs2[dst_Index + 0] = uvs2[src_Index + 0];
uvs2[src_Index + 0] = uvs;
uvs = uvs2[dst_Index + 1];
uvs2[dst_Index + 1] = uvs2[src_Index + 1];
uvs2[src_Index + 1] = uvs;
uvs = uvs2[dst_Index + 2];
uvs2[dst_Index + 2] = uvs2[src_Index + 2];
uvs2[src_Index + 2] = uvs;
uvs = uvs2[dst_Index + 3];
uvs2[dst_Index + 3] = uvs2[src_Index + 3];
uvs2[src_Index + 3] = uvs;
// Vertex Colors
Color32 color;
color = colors32[dst_Index + 0];
colors32[dst_Index + 0] = colors32[src_Index + 0];
colors32[src_Index + 0] = color;
color = colors32[dst_Index + 1];
colors32[dst_Index + 1] = colors32[src_Index + 1];
colors32[src_Index + 1] = color;
color = colors32[dst_Index + 2];
colors32[dst_Index + 2] = colors32[src_Index + 2];
colors32[src_Index + 2] = color;
color = colors32[dst_Index + 3];
colors32[dst_Index + 3] = colors32[src_Index + 3];
colors32[src_Index + 3] = color;
}
//int Partition (int start, int end)
//{
// float pivot = vertices[end].z;
// int partitionIndex = start;
// for (int i = start; i < end; i++)
// {
// if (vertices[i].z <= pivot)
// {
// Swap(vertices[i], vertices[partitionIndex]);
// partitionIndex += 1;
// }
// }
// Swap(vertices[partitionIndex], vertices[end]);
// return partitionIndex;
//}
//void Swap(Vector3 a, Vector3 b)
//{
// Vector3 temp = a;
// a = b;
// b = a;
//}
}
}

View File

@@ -0,0 +1,51 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace TMPro
{
internal class TMP_ObjectPool<T> where T : new()
{
private readonly Stack<T> m_Stack = new Stack<T>();
private readonly UnityAction<T> m_ActionOnGet;
private readonly UnityAction<T> m_ActionOnRelease;
public int countAll { get; private set; }
public int countActive { get { return countAll - countInactive; } }
public int countInactive { get { return m_Stack.Count; } }
public TMP_ObjectPool(UnityAction<T> actionOnGet, UnityAction<T> actionOnRelease)
{
m_ActionOnGet = actionOnGet;
m_ActionOnRelease = actionOnRelease;
}
public T Get()
{
T element;
if (m_Stack.Count == 0)
{
element = new T();
countAll++;
}
else
{
element = m_Stack.Pop();
}
if (m_ActionOnGet != null)
m_ActionOnGet(element);
return element;
}
public void Release(T element)
{
if (m_Stack.Count > 0 && ReferenceEquals(m_Stack.Peek(), element))
Debug.LogError("Internal error. Trying to destroy object that is already released to pool.");
if (m_ActionOnRelease != null)
m_ActionOnRelease(element);
m_Stack.Push(element);
}
}
}

View File

@@ -0,0 +1,238 @@
#if UNITY_EDITOR
using System;
using System.IO;
using UnityEngine;
using UnityEditor;
namespace TMPro
{
[System.Serializable]
public class TMP_PackageResourceImporter
{
bool m_EssentialResourcesImported;
bool m_ExamplesAndExtrasResourcesImported;
internal bool m_IsImportingExamples;
public TMP_PackageResourceImporter() { }
public void OnDestroy()
{
}
public void OnGUI()
{
// Check if the resources state has changed.
m_EssentialResourcesImported = File.Exists("Assets/TextMesh Pro/Resources/TMP Settings.asset");
m_ExamplesAndExtrasResourcesImported = Directory.Exists("Assets/TextMesh Pro/Examples & Extras");
GUILayout.BeginVertical();
{
// Display options to import Essential resources
GUILayout.BeginVertical(EditorStyles.helpBox);
{
GUILayout.Label("TMP Essentials", EditorStyles.boldLabel);
GUILayout.Label("This appears to be the first time you access TextMesh Pro, as such we need to add resources to your project that are essential for using TextMesh Pro. These new resources will be placed at the root of your project in the \"TextMesh Pro\" folder.", new GUIStyle(EditorStyles.label) { wordWrap = true } );
GUILayout.Space(5f);
GUI.enabled = !m_EssentialResourcesImported;
if (GUILayout.Button("Import TMP Essentials"))
{
AssetDatabase.importPackageCompleted += ImportCallback;
string packageFullPath = GetPackageFullPath();
AssetDatabase.ImportPackage(packageFullPath + "/Package Resources/TMP Essential Resources.unitypackage", false);
}
GUILayout.Space(5f);
GUI.enabled = true;
}
GUILayout.EndVertical();
// Display options to import Examples & Extras
GUILayout.BeginVertical(EditorStyles.helpBox);
{
GUILayout.Label("TMP Examples & Extras", EditorStyles.boldLabel);
GUILayout.Label("The Examples & Extras package contains addition resources and examples that will make discovering and learning about TextMesh Pro's powerful features easier. These additional resources will be placed in the same folder as the TMP essential resources.", new GUIStyle(EditorStyles.label) { wordWrap = true });
GUILayout.Space(5f);
GUI.enabled = m_EssentialResourcesImported && !m_ExamplesAndExtrasResourcesImported;
if (GUILayout.Button("Import TMP Examples & Extras"))
{
// Set flag to get around importing scripts as per of this package which results in an assembly reload which in turn prevents / clears any callbacks.
m_IsImportingExamples = true;
// Disable AssetDatabase refresh until examples have been imported.
//AssetDatabase.DisallowAutoRefresh();
string packageFullPath = GetPackageFullPath();
AssetDatabase.ImportPackage(packageFullPath + "/Package Resources/TMP Examples & Extras.unitypackage", false);
}
GUILayout.Space(5f);
GUI.enabled = true;
}
GUILayout.EndVertical();
}
GUILayout.EndVertical();
GUILayout.Space(5f);
}
internal void RegisterResourceImportCallback()
{
AssetDatabase.importPackageCompleted += ImportCallback;
}
/// <summary>
///
/// </summary>
/// <param name="packageName"></param>
void ImportCallback(string packageName)
{
if (packageName == "TMP Essential Resources")
{
m_EssentialResourcesImported = true;
TMPro_EventManager.ON_RESOURCES_LOADED();
#if UNITY_2018_3_OR_NEWER
SettingsService.NotifySettingsProviderChanged();
#endif
}
else if (packageName == "TMP Examples & Extras")
{
m_ExamplesAndExtrasResourcesImported = true;
m_IsImportingExamples = false;
//AssetDatabase.AllowAutoRefresh();
}
Debug.Log("[" + packageName + "] have been imported.");
AssetDatabase.importPackageCompleted -= ImportCallback;
}
static string GetPackageFullPath()
{
// Check for potential UPM package
string packagePath = Path.GetFullPath("Packages/com.unity.textmeshpro");
if (Directory.Exists(packagePath))
{
return packagePath;
}
packagePath = Path.GetFullPath("Assets/..");
if (Directory.Exists(packagePath))
{
// Search default location for development package
if (Directory.Exists(packagePath + "/Assets/Packages/com.unity.TextMeshPro/Editor Resources"))
{
return packagePath + "/Assets/Packages/com.unity.TextMeshPro";
}
// Search for default location of normal TextMesh Pro AssetStore package
if (Directory.Exists(packagePath + "/Assets/TextMesh Pro/Editor Resources"))
{
return packagePath + "/Assets/TextMesh Pro";
}
// Search for potential alternative locations in the user project
string[] matchingPaths = Directory.GetDirectories(packagePath, "TextMesh Pro", SearchOption.AllDirectories);
string path = ValidateLocation(matchingPaths, packagePath);
if (path != null) return packagePath + path;
}
return null;
}
static string ValidateLocation(string[] paths, string projectPath)
{
for (int i = 0; i < paths.Length; i++)
{
// Check if the Editor Resources folder exists.
if (Directory.Exists(paths[i] + "/Editor Resources"))
{
string folderPath = paths[i].Replace(projectPath, "");
folderPath = folderPath.TrimStart('\\', '/');
return folderPath;
}
}
return null;
}
/// <summary>
/// Imports the specified TMP resources.
/// </summary>
/// <param name="importEssentials">Should import the TMP Essential Resources.</param>
/// <param name="importExamples">Should import the TMP Examples & Extras.</param>
/// <param name="interactive">If interactive is true, an import package dialog will be opened, else all assets in the package will be imported into the current project silently.</param>
public static void ImportResources(bool importEssentials, bool importExamples, bool interactive)
{
string packageFullPath = GetPackageFullPath();
if (importEssentials)
AssetDatabase.ImportPackage(packageFullPath + "/Package Resources/TMP Essential Resources.unitypackage", interactive);
if (importExamples)
AssetDatabase.ImportPackage(packageFullPath + "/Package Resources/TMP Examples & Extras.unitypackage", interactive);
}
}
public class TMP_PackageResourceImporterWindow : EditorWindow
{
[SerializeField]
TMP_PackageResourceImporter m_ResourceImporter;
static TMP_PackageResourceImporterWindow m_ImporterWindow;
public static void ShowPackageImporterWindow()
{
if (m_ImporterWindow == null)
{
m_ImporterWindow = GetWindow<TMP_PackageResourceImporterWindow>();
m_ImporterWindow.titleContent = new GUIContent("TMP Importer");
m_ImporterWindow.Focus();
}
}
void OnEnable()
{
// Set Editor Window Size
SetEditorWindowSize();
if (m_ResourceImporter == null)
m_ResourceImporter = new TMP_PackageResourceImporter();
if (m_ResourceImporter.m_IsImportingExamples)
m_ResourceImporter.RegisterResourceImportCallback();
}
void OnDestroy()
{
m_ResourceImporter.OnDestroy();
}
void OnGUI()
{
m_ResourceImporter.OnGUI();
}
void OnInspectorUpdate()
{
Repaint();
}
/// <summary>
/// Limits the minimum size of the editor window.
/// </summary>
void SetEditorWindowSize()
{
EditorWindow editorWindow = this;
Vector2 windowSize = new Vector2(640, 210);
editorWindow.minSize = windowSize;
editorWindow.maxSize = windowSize;
}
}
}
#endif

View File

@@ -0,0 +1,90 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace TMPro
{
/// <summary>
///
/// </summary>
public class TMP_ResourceManager
{
private static readonly TMP_ResourceManager s_instance = new TMP_ResourceManager();
static TMP_ResourceManager() { }
// ======================================================
// TEXT SETTINGS MANAGEMENT
// ======================================================
private static TMP_Settings s_TextSettings;
internal static TMP_Settings GetTextSettings()
{
if (s_TextSettings == null)
{
// Try loading the TMP Settings from a Resources folder in the user project.
s_TextSettings = Resources.Load<TMP_Settings>("TextSettings"); // ?? ScriptableObject.CreateInstance<TMP_Settings>();
#if UNITY_EDITOR
if (s_TextSettings == null)
{
// Open TMP Resources Importer to enable the user to import the TMP Essential Resources and option TMP Examples & Extras
TMP_PackageResourceImporterWindow.ShowPackageImporterWindow();
}
#endif
}
return s_TextSettings;
}
// ======================================================
// FONT ASSET MANAGEMENT - Fields, Properties and Functions
// ======================================================
private static readonly List<TMP_FontAsset> s_FontAssetReferences = new List<TMP_FontAsset>();
private static readonly Dictionary<int, TMP_FontAsset> s_FontAssetReferenceLookup = new Dictionary<int, TMP_FontAsset>();
/// <summary>
///
/// </summary>
/// <param name="fontAsset"></param>
public static void AddFontAsset(TMP_FontAsset fontAsset)
{
int hashcode = fontAsset.hashCode;
if (s_FontAssetReferenceLookup.ContainsKey(hashcode))
return;
s_FontAssetReferences.Add(fontAsset);
s_FontAssetReferenceLookup.Add(hashcode, fontAsset);
}
/// <summary>
///
/// </summary>
/// <param name="hashcode"></param>
/// <param name="fontAsset"></param>
/// <returns></returns>
public static bool TryGetFontAsset(int hashcode, out TMP_FontAsset fontAsset)
{
fontAsset = null;
return s_FontAssetReferenceLookup.TryGetValue(hashcode, out fontAsset);
}
internal static void RebuildFontAssetCache(int instanceID)
{
// Iterate over loaded font assets to update affected font assets
for (int i = 0; i < s_FontAssetReferences.Count; i++)
{
TMP_FontAsset fontAsset = s_FontAssetReferences[i];
if (fontAsset.FallbackSearchQueryLookup.Contains(instanceID))
fontAsset.ReadFontAssetDefinition();
}
}
}
}

View File

@@ -0,0 +1,202 @@
using System;
using UnityEngine.Bindings;
namespace TMPro
{
/// <summary>
/// Rich Text Tags and Attribute definitions and their respective HashCode values.
/// </summary>
internal enum MarkupTag : int
{
// Rich Text Tags
BOLD = 66, // <b>
SLASH_BOLD = 1613, // </b>
ITALIC = 73, // <i>
SLASH_ITALIC = 1606, // </i>
UNDERLINE = 85, // <u>
SLASH_UNDERLINE = 1626, // </u>
STRIKETHROUGH = 83, // <s>
SLASH_STRIKETHROUGH = 1628, // </s>
MARK = 2699125, // <mark>
SLASH_MARK = 57644506, // </mark>
SUBSCRIPT = 92132, // <sub>
SLASH_SUBSCRIPT = 1770219, // </sub>
SUPERSCRIPT = 92150, // <sup>
SLASH_SUPERSCRIPT = 1770233, // </sup>
COLOR = 81999901, // <color>
SLASH_COLOR = 1909026194, // </color>
ALPHA = 75165780, // <alpha>
A = 65, // <a>
SLASH_A = 1614, // </a>
SIZE = 3061285, // <size>
SLASH_SIZE = 58429962, // </size>
SPRITE = -991527447, // <sprite>
NO_BREAK = 2856657, // <nobr>
SLASH_NO_BREAK = 57477502, // </nobr>
STYLE = 100252951, // <style>
SLASH_STYLE = 1927738392, // </style>
FONT = 2586451, // <font>
SLASH_FONT = 57747708, // </font>
SLASH_MATERIAL = -1100708252, // </material>
LINK = 2656128, // <link>
SLASH_LINK = 57686191, // </link>
FONT_WEIGHT = -1889896162, // <font-weight=xxx>
SLASH_FONT_WEIGHT = -757976431, // </font-weight>
NO_PARSE = -408011596, // <noparse>
SLASH_NO_PARSE = -294095813, // </noparse>
POSITION = 85420, // <pos>
SLASH_POSITION = 1777699, // </pos>
VERTICAL_OFFSET = 1952379995, // <voffset>
SLASH_VERTICAL_OFFSET = -11107948, // </voffset>
SPACE = 100083556, // <space>
SLASH_SPACE = 1927873067, // </space>
PAGE = 2808691, // <page>
SLASH_PAGE = 58683868, // </page>
ALIGN = 75138797, // <align>
SLASH_ALIGN = 1916026786, // </align>
WIDTH = 105793766, // <width>
SLASH_WIDTH = 1923459625, // </width>
GRADIENT = -1999759898, // <gradient>
SLASH_GRADIENT = -1854491959, // </gradient>
CHARACTER_SPACE = -1584382009, // <cspace>
SLASH_CHARACTER_SPACE = -1394426712,// </cspace>
MONOSPACE = -1340221943, // <mspace>
SLASH_MONOSPACE = -1638865562, // </mspace>
CLASS = 82115566, // <class>
INDENT = -1514123076, // <indent>
SLASH_INDENT = -1496889389, // </indent>
LINE_INDENT = -844305121, // <line-indent>
SLASH_LINE_INDENT = 93886352, // </line-indent>
MARGIN = -1355614050, // <margin>
SLASH_MARGIN = -1649644303, // </margin>
MARGIN_LEFT = -272933656, // <margin-left>
MARGIN_RIGHT = -447416589, // <margin-right>
LINE_HEIGHT = -799081892, // <line-height>
SLASH_LINE_HEIGHT = 200452819, // </line-height>
ACTION = -1827519330, // <action>
SLASH_ACTION = -1187217679, // </action>
SCALE = 100553336, // <scale>
SLASH_SCALE = 1928413879, // </scale>
ROTATE = -1000007783, // <rotate>
SLASH_ROTATE = -764695562, // </rotate>
LOWERCASE = -1506899689, // <lowercase>
SLASH_LOWERCASE = -1451284584, // </lowercase>
ALLCAPS = 218273952, // <allcaps>
SLASH_ALLCAPS = -797437649, // </allcaps>
UPPERCASE = -305409418, // <uppercase>
SLASH_UPPERCASE = -582368199, // </uppercase>
SMALLCAPS = -766062114, // <smallcaps>
SLASH_SMALLCAPS = 199921873, // </smallcaps>
// Font Features
LIGA = 2655971, // <liga>
SLASH_LIGA = 57686604, // </liga>
FRAC = 2598518, // <frac>
SLASH_FRAC = 57774681, // </frac>
// Attributes
NAME = 2875623, // <sprite name="Name of Sprite">
INDEX = 84268030, // <sprite index=7>
TINT = 2960519, // <tint=bool>
ANIM = 2283339, // <anim="first frame, last frame, frame rate">
MATERIAL = 825491659, // <font="Name of font asset" material="Name of material">
HREF = 2535353, // <a href="url">text to be displayed.</a>
ANGLE = 75347905, // <i angle="40">Italic Slant Angle</i>
// Named Colors
RED = 91635,
GREEN = 87065851,
BLUE = 2457214,
YELLOW = -882444668,
ORANGE = -1108587920,
BLACK = 81074727,
WHITE = 105680263,
PURPLE = -1250222130,
// Unicode Characters
BR = 2256, // <br> Line Feed (LF) \u0A
ZWSP = 3288238, // <zwsp> Zero Width Space \u200B
NBSP = 2869039, // <nbsp> Non Breaking Space \u00A0
SHY = 92674, // <SHY> Soft Hyphen \u00AD
// Alignment
LEFT = 2660507, // <align=left>
RIGHT = 99937376, // <align=right>
CENTER = -1591113269, // <align=center>
JUSTIFIED = 817091359, // <align=justified>
FLUSH = 85552164, // <align=flush>
// Prefix and Unit suffix
NONE = 2857034,
PLUS = 43,
MINUS = 45,
PX = 2568,
PLUS_PX = 49507,
MINUS_PX = 47461,
EM = 2216,
PLUS_EM = 49091,
MINUS_EM = 46789,
PCT = 85031,
PLUS_PCT = 1634348,
MINUS_PCT = 1567082,
PERCENTAGE = 37,
PLUS_PERCENTAGE = 1454,
MINUS_PERCENTAGE = 1512,
TRUE = 2932022,
FALSE = 85422813,
INVALID = 1585415185,
NORMAL = -1183493901, // <style="Normal">
DEFAULT = -620974005, // <font="Default">
}
/// <summary>
/// Defines the type of value used by a rich text tag or tag attribute.
/// </summary>
public enum TagValueType
{
None = 0x0,
NumericalValue = 0x1,
StringValue = 0x2,
ColorValue = 0x4,
}
public enum TagUnitType
{
Pixels = 0x0,
FontUnits = 0x1,
Percentage = 0x2,
}
/// <summary>
/// Commonly referenced Unicode characters in the text generation process.
/// </summary>
internal static class CodePoint
{
public const uint SPACE = 0x20;
public const uint DOUBLE_QUOTE = 0x22;
public const uint NUMBER_SIGN = 0x23;
public const uint PERCENTAGE = 0x25;
public const uint PLUS = 0x2B;
public const uint MINUS = 0x2D;
public const uint PERIOD = 0x2E;
public const uint HYPHEN_MINUS = 0x2D;
public const uint SOFT_HYPHEN = 0xAD;
public const uint HYPHEN = 0x2010;
public const uint NON_BREAKING_HYPHEN = 0x2011;
public const uint ZERO_WIDTH_SPACE = 0x200B;
public const uint RIGHT_SINGLE_QUOTATION = 0x2019;
public const uint APOSTROPHE = 0x27;
public const uint WORD_JOINER = 0x2060;
public const uint HIGH_SURROGATE_START = 0xD800;
public const uint HIGH_SURROGATE_END = 0xDBFF;
public const uint LOW_SURROGATE_START = 0xDC00;
public const uint LOW_SURROGATE_END = 0xDFFF;
public const uint UNICODE_PLANE01_START = 0x10000;
}
}

View File

@@ -0,0 +1,31 @@
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using System;
namespace TMPro
{
public class TMP_ScrollbarEventHandler : MonoBehaviour, IPointerClickHandler, ISelectHandler, IDeselectHandler
{
public bool isSelected;
public void OnPointerClick(PointerEventData eventData)
{
Debug.Log("Scrollbar click...");
}
public void OnSelect(BaseEventData eventData)
{
Debug.Log("Scrollbar selected");
isSelected = true;
}
public void OnDeselect(BaseEventData eventData)
{
Debug.Log("Scrollbar De-Selected");
isSelected = false;
}
}
}

View File

@@ -0,0 +1,37 @@
using UnityEngine;
using UnityEngine.UI;
namespace TMPro
{
/// <summary>
/// A simple component that can be added to a newly created object where inheriting from MaskableGraphic is needed.
/// </summary>
[RequireComponent(typeof(CanvasRenderer))]
public class TMP_SelectionCaret : MaskableGraphic
{
/// <summary>
/// Override to Cull function of MaskableGraphic to prevent Culling.
/// </summary>
/// <param name="clipRect"></param>
/// <param name="validRect"></param>
public override void Cull(Rect clipRect, bool validRect)
{
//Debug.Log("***** Cull (" + clipRect + ") Valid Rect: " + validRect + " Cull: " + canvasRenderer.cull + " *****");
if (validRect)
{
canvasRenderer.cull = false;
CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);
return;
}
base.Cull(clipRect, validRect);
}
protected override void UpdateGeometry()
{
// Function overridden as Caret and text Selection Highlight is controlled by the Input Field.
}
}
}

View File

@@ -0,0 +1,520 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
#pragma warning disable 0649 // Disabled warnings related to serialized fields not assigned in this script but used in the editor.
namespace TMPro
{
/// <summary>
/// Scaling options for the sprites
/// </summary>
//public enum SpriteRelativeScaling
//{
// RelativeToPrimary = 0x1,
// RelativeToCurrent = 0x2,
//}
[System.Serializable][ExcludeFromPresetAttribute]
public class TMP_Settings : ScriptableObject
{
private static TMP_Settings s_Instance;
/// <summary>
/// Returns the release version of the product.
/// </summary>
public static string version
{
get { return "1.4.0"; }
}
/// <summary>
/// Controls if Word Wrapping will be enabled on newly created text objects by default.
/// </summary>
public static bool enableWordWrapping
{
get { return instance.m_enableWordWrapping; }
}
[SerializeField]
private bool m_enableWordWrapping;
/// <summary>
/// Controls if Kerning is enabled on newly created text objects by default.
/// </summary>
public static bool enableKerning
{
get { return instance.m_enableKerning; }
}
[SerializeField]
private bool m_enableKerning;
/// <summary>
/// Controls if Extra Padding is enabled on newly created text objects by default.
/// </summary>
public static bool enableExtraPadding
{
get { return instance.m_enableExtraPadding; }
}
[SerializeField]
private bool m_enableExtraPadding;
/// <summary>
/// Controls if TintAllSprites is enabled on newly created text objects by default.
/// </summary>
public static bool enableTintAllSprites
{
get { return instance.m_enableTintAllSprites; }
}
[SerializeField]
private bool m_enableTintAllSprites;
/// <summary>
/// Controls if Escape Characters will be parsed in the Text Input Box on newly created text objects.
/// </summary>
public static bool enableParseEscapeCharacters
{
get { return instance.m_enableParseEscapeCharacters; }
}
[SerializeField]
private bool m_enableParseEscapeCharacters;
/// <summary>
/// Controls if Raycast Target is enabled by default on newly created text objects.
/// </summary>
public static bool enableRaycastTarget
{
get { return instance.m_EnableRaycastTarget; }
}
[SerializeField]
private bool m_EnableRaycastTarget = true;
/// <summary>
/// Determines if OpenType Font Features should be retrieved at runtime from the source font file.
/// </summary>
public static bool getFontFeaturesAtRuntime
{
get { return instance.m_GetFontFeaturesAtRuntime; }
}
[SerializeField]
private bool m_GetFontFeaturesAtRuntime = true;
/// <summary>
/// The character that will be used as a replacement for missing glyphs in a font asset.
/// </summary>
public static int missingGlyphCharacter
{
get { return instance.m_missingGlyphCharacter; }
set { instance.m_missingGlyphCharacter = value; }
}
[SerializeField]
private int m_missingGlyphCharacter;
/// <summary>
/// Controls the display of warning message in the console.
/// </summary>
public static bool warningsDisabled
{
get { return instance.m_warningsDisabled; }
}
[SerializeField]
private bool m_warningsDisabled;
/// <summary>
/// Returns the Default Font Asset to be used by newly created text objects.
/// </summary>
public static TMP_FontAsset defaultFontAsset
{
get { return instance.m_defaultFontAsset; }
}
[SerializeField]
private TMP_FontAsset m_defaultFontAsset;
/// <summary>
/// The relative path to a Resources folder in the project.
/// </summary>
public static string defaultFontAssetPath
{
get { return instance.m_defaultFontAssetPath; }
}
[SerializeField]
private string m_defaultFontAssetPath;
/// <summary>
/// The Default Point Size of newly created text objects.
/// </summary>
public static float defaultFontSize
{
get { return instance.m_defaultFontSize; }
}
[SerializeField]
private float m_defaultFontSize;
/// <summary>
/// The multiplier used to computer the default Min point size when Text Auto Sizing is used.
/// </summary>
public static float defaultTextAutoSizingMinRatio
{
get { return instance.m_defaultAutoSizeMinRatio; }
}
[SerializeField]
private float m_defaultAutoSizeMinRatio;
/// <summary>
/// The multiplier used to computer the default Max point size when Text Auto Sizing is used.
/// </summary>
public static float defaultTextAutoSizingMaxRatio
{
get { return instance.m_defaultAutoSizeMaxRatio; }
}
[SerializeField]
private float m_defaultAutoSizeMaxRatio;
/// <summary>
/// The Default Size of the Text Container of a TextMeshPro object.
/// </summary>
public static Vector2 defaultTextMeshProTextContainerSize
{
get { return instance.m_defaultTextMeshProTextContainerSize; }
}
[SerializeField]
private Vector2 m_defaultTextMeshProTextContainerSize;
/// <summary>
/// The Default Width of the Text Container of a TextMeshProUI object.
/// </summary>
public static Vector2 defaultTextMeshProUITextContainerSize
{
get { return instance.m_defaultTextMeshProUITextContainerSize; }
}
[SerializeField]
private Vector2 m_defaultTextMeshProUITextContainerSize;
/// <summary>
/// Set the size of the text container of newly created text objects to match the size of the text.
/// </summary>
public static bool autoSizeTextContainer
{
get { return instance.m_autoSizeTextContainer; }
}
[SerializeField]
private bool m_autoSizeTextContainer;
/// <summary>
/// Disables InternalUpdate() calls when true. This can improve performance when the scale of the text object is static.
/// </summary>
public static bool isTextObjectScaleStatic
{
get { return instance.m_IsTextObjectScaleStatic; }
set { instance.m_IsTextObjectScaleStatic = value; }
}
[SerializeField]
private bool m_IsTextObjectScaleStatic;
/// <summary>
/// Returns the list of Fallback Fonts defined in the TMP Settings file.
/// </summary>
public static List<TMP_FontAsset> fallbackFontAssets
{
get { return instance.m_fallbackFontAssets; }
}
[SerializeField]
private List<TMP_FontAsset> m_fallbackFontAssets;
/// <summary>
/// Controls whether or not TMP will create a matching material preset or use the default material of the fallback font asset.
/// </summary>
public static bool matchMaterialPreset
{
get { return instance.m_matchMaterialPreset; }
}
[SerializeField]
private bool m_matchMaterialPreset;
/// <summary>
/// The Default Sprite Asset to be used by default.
/// </summary>
public static TMP_SpriteAsset defaultSpriteAsset
{
get { return instance.m_defaultSpriteAsset; }
}
[SerializeField]
private TMP_SpriteAsset m_defaultSpriteAsset;
/// <summary>
/// The relative path to a Resources folder in the project.
/// </summary>
public static string defaultSpriteAssetPath
{
get { return instance.m_defaultSpriteAssetPath; }
}
[SerializeField]
private string m_defaultSpriteAssetPath;
/// <summary>
/// Determines if Emoji support is enabled in the Input Field TouchScreenKeyboard.
/// </summary>
public static bool enableEmojiSupport
{
get { return instance.m_enableEmojiSupport; }
set { instance.m_enableEmojiSupport = value; }
}
[SerializeField]
private bool m_enableEmojiSupport;
/// <summary>
/// The unicode value of the sprite that will be used when the requested sprite is missing from the sprite asset and potential fallbacks.
/// </summary>
public static uint missingCharacterSpriteUnicode
{
get { return instance.m_MissingCharacterSpriteUnicode; }
set { instance.m_MissingCharacterSpriteUnicode = value; }
}
[SerializeField]
private uint m_MissingCharacterSpriteUnicode;
/// <summary>
/// Determines if sprites will be scaled relative to the primary font asset assigned to the text object or relative to the current font asset.
/// </summary>
//public static SpriteRelativeScaling spriteRelativeScaling
//{
// get { return instance.m_SpriteRelativeScaling; }
// set { instance.m_SpriteRelativeScaling = value; }
//}
//[SerializeField]
//private SpriteRelativeScaling m_SpriteRelativeScaling = SpriteRelativeScaling.RelativeToCurrent;
/// <summary>
/// The relative path to a Resources folder in the project that contains Color Gradient Presets.
/// </summary>
public static string defaultColorGradientPresetsPath
{
get { return instance.m_defaultColorGradientPresetsPath; }
}
[SerializeField]
private string m_defaultColorGradientPresetsPath;
/// <summary>
/// The Default Style Sheet used by the text objects.
/// </summary>
public static TMP_StyleSheet defaultStyleSheet
{
get { return instance.m_defaultStyleSheet; }
}
[SerializeField]
private TMP_StyleSheet m_defaultStyleSheet;
/// <summary>
/// The relative path to a Resources folder in the project that contains the TMP Style Sheets.
/// </summary>
public static string styleSheetsResourcePath
{
get { return instance.m_StyleSheetsResourcePath; }
}
[SerializeField]
private string m_StyleSheetsResourcePath;
/// <summary>
/// Text file that contains the leading characters used for line breaking for Asian languages.
/// </summary>
public static TextAsset leadingCharacters
{
get { return instance.m_leadingCharacters; }
}
[SerializeField]
private TextAsset m_leadingCharacters;
/// <summary>
/// Text file that contains the following characters used for line breaking for Asian languages.
/// </summary>
public static TextAsset followingCharacters
{
get { return instance.m_followingCharacters; }
}
[SerializeField]
private TextAsset m_followingCharacters;
/// <summary>
///
/// </summary>
public static LineBreakingTable linebreakingRules
{
get
{
if (instance.m_linebreakingRules == null)
LoadLinebreakingRules();
return instance.m_linebreakingRules;
}
}
[SerializeField]
private LineBreakingTable m_linebreakingRules;
// TODO : Potential new feature to explore where multiple font assets share the same atlas texture.
//internal static TMP_DynamicAtlasTextureGroup managedAtlasTextures
//{
// get
// {
// if (instance.m_DynamicAtlasTextureGroup == null)
// {
// instance.m_DynamicAtlasTextureGroup = TMP_DynamicAtlasTextureGroup.CreateDynamicAtlasTextureGroup();
// }
// return instance.m_DynamicAtlasTextureGroup;
// }
//}
//[SerializeField]
//private TMP_DynamicAtlasTextureGroup m_DynamicAtlasTextureGroup;
/// <summary>
/// Determines if Modern or Traditional line breaking rules should be used for Korean text.
/// </summary>
public static bool useModernHangulLineBreakingRules
{
get { return instance.m_UseModernHangulLineBreakingRules; }
set { instance.m_UseModernHangulLineBreakingRules = value; }
}
[SerializeField]
private bool m_UseModernHangulLineBreakingRules;
/// <summary>
/// Get a singleton instance of the settings class.
/// </summary>
public static TMP_Settings instance
{
get
{
if (TMP_Settings.s_Instance == null)
{
TMP_Settings.s_Instance = Resources.Load<TMP_Settings>("TMP Settings");
#if UNITY_EDITOR
// Make sure TextMesh Pro UPM packages resources have been added to the user project
if (TMP_Settings.s_Instance == null)
{
// Open TMP Resources Importer
TMP_PackageResourceImporterWindow.ShowPackageImporterWindow();
}
#endif
}
return TMP_Settings.s_Instance;
}
}
/// <summary>
/// Static Function to load the TMP Settings file.
/// </summary>
/// <returns></returns>
public static TMP_Settings LoadDefaultSettings()
{
if (s_Instance == null)
{
// Load settings from TMP_Settings file
TMP_Settings settings = Resources.Load<TMP_Settings>("TMP Settings");
if (settings != null)
s_Instance = settings;
}
return s_Instance;
}
/// <summary>
/// Returns the Sprite Asset defined in the TMP Settings file.
/// </summary>
/// <returns></returns>
public static TMP_Settings GetSettings()
{
if (TMP_Settings.instance == null) return null;
return TMP_Settings.instance;
}
/// <summary>
/// Returns the Font Asset defined in the TMP Settings file.
/// </summary>
/// <returns></returns>
public static TMP_FontAsset GetFontAsset()
{
if (TMP_Settings.instance == null) return null;
return TMP_Settings.instance.m_defaultFontAsset;
}
/// <summary>
/// Returns the Sprite Asset defined in the TMP Settings file.
/// </summary>
/// <returns></returns>
public static TMP_SpriteAsset GetSpriteAsset()
{
if (TMP_Settings.instance == null) return null;
return TMP_Settings.instance.m_defaultSpriteAsset;
}
/// <summary>
/// Returns the Style Sheet defined in the TMP Settings file.
/// </summary>
/// <returns></returns>
public static TMP_StyleSheet GetStyleSheet()
{
if (TMP_Settings.instance == null) return null;
return TMP_Settings.instance.m_defaultStyleSheet;
}
public static void LoadLinebreakingRules()
{
//Debug.Log("Loading Line Breaking Rules for Asian Languages.");
if (TMP_Settings.instance == null) return;
if (s_Instance.m_linebreakingRules == null)
s_Instance.m_linebreakingRules = new LineBreakingTable();
s_Instance.m_linebreakingRules.leadingCharacters = GetCharacters(s_Instance.m_leadingCharacters);
s_Instance.m_linebreakingRules.followingCharacters = GetCharacters(s_Instance.m_followingCharacters);
}
/// <summary>
/// Get the characters from the line breaking files
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
private static Dictionary<int, char> GetCharacters(TextAsset file)
{
Dictionary<int, char> dict = new Dictionary<int, char>();
string text = file.text;
for (int i = 0; i < text.Length; i++)
{
char c = text[i];
// Check to make sure we don't include duplicates
if (dict.ContainsKey((int)c) == false)
{
dict.Add((int)c, c);
//Debug.Log("Adding [" + (int)c + "] to dictionary.");
}
//else
// Debug.Log("Character [" + text[i] + "] is a duplicate.");
}
return dict;
}
public class LineBreakingTable
{
public Dictionary<int, char> leadingCharacters;
public Dictionary<int, char> followingCharacters;
}
}
}

View File

@@ -0,0 +1,603 @@
using UnityEngine;
using System.Linq;
using System.Collections;
namespace TMPro
{
public static class ShaderUtilities
{
// Shader Property IDs
public static int ID_MainTex;
public static int ID_FaceTex;
public static int ID_FaceColor;
public static int ID_FaceDilate;
public static int ID_Shininess;
public static int ID_UnderlayColor;
public static int ID_UnderlayOffsetX;
public static int ID_UnderlayOffsetY;
public static int ID_UnderlayDilate;
public static int ID_UnderlaySoftness;
/// <summary>
/// Property ID for the _UnderlayOffset shader property used by URP and HDRP shaders
/// </summary>
public static int ID_UnderlayOffset;
/// <summary>
/// Property ID for the _UnderlayIsoPerimeter shader property used by URP and HDRP shaders
/// </summary>
public static int ID_UnderlayIsoPerimeter;
public static int ID_WeightNormal;
public static int ID_WeightBold;
public static int ID_OutlineTex;
public static int ID_OutlineWidth;
public static int ID_OutlineSoftness;
public static int ID_OutlineColor;
public static int ID_Outline2Color;
public static int ID_Outline2Width;
public static int ID_Padding;
public static int ID_GradientScale;
public static int ID_ScaleX;
public static int ID_ScaleY;
public static int ID_PerspectiveFilter;
public static int ID_Sharpness;
public static int ID_TextureWidth;
public static int ID_TextureHeight;
public static int ID_BevelAmount;
public static int ID_GlowColor;
public static int ID_GlowOffset;
public static int ID_GlowPower;
public static int ID_GlowOuter;
public static int ID_GlowInner;
public static int ID_LightAngle;
public static int ID_EnvMap;
public static int ID_EnvMatrix;
public static int ID_EnvMatrixRotation;
//public static int ID_MaskID;
public static int ID_MaskCoord;
public static int ID_ClipRect;
public static int ID_MaskSoftnessX;
public static int ID_MaskSoftnessY;
public static int ID_VertexOffsetX;
public static int ID_VertexOffsetY;
public static int ID_UseClipRect;
public static int ID_StencilID;
public static int ID_StencilOp;
public static int ID_StencilComp;
public static int ID_StencilReadMask;
public static int ID_StencilWriteMask;
public static int ID_ShaderFlags;
public static int ID_ScaleRatio_A;
public static int ID_ScaleRatio_B;
public static int ID_ScaleRatio_C;
public static string Keyword_Bevel = "BEVEL_ON";
public static string Keyword_Glow = "GLOW_ON";
public static string Keyword_Underlay = "UNDERLAY_ON";
public static string Keyword_Ratios = "RATIOS_OFF";
//public static string Keyword_MASK_OFF = "MASK_OFF";
public static string Keyword_MASK_SOFT = "MASK_SOFT";
public static string Keyword_MASK_HARD = "MASK_HARD";
public static string Keyword_MASK_TEX = "MASK_TEX";
public static string Keyword_Outline = "OUTLINE_ON";
public static string ShaderTag_ZTestMode = "unity_GUIZTestMode";
public static string ShaderTag_CullMode = "_CullMode";
private static float m_clamp = 1.0f;
public static bool isInitialized = false;
/// <summary>
/// Returns a reference to the mobile distance field shader.
/// </summary>
internal static Shader ShaderRef_MobileSDF
{
get
{
if (k_ShaderRef_MobileSDF == null)
k_ShaderRef_MobileSDF = Shader.Find("TextMeshPro/Mobile/Distance Field");
return k_ShaderRef_MobileSDF;
}
}
static Shader k_ShaderRef_MobileSDF;
/// <summary>
/// Returns a reference to the mobile bitmap shader.
/// </summary>
internal static Shader ShaderRef_MobileBitmap
{
get
{
if (k_ShaderRef_MobileBitmap == null)
k_ShaderRef_MobileBitmap = Shader.Find("TextMeshPro/Mobile/Bitmap");
return k_ShaderRef_MobileBitmap;
}
}
static Shader k_ShaderRef_MobileBitmap;
/// <summary>
///
/// </summary>
static ShaderUtilities()
{
GetShaderPropertyIDs();
}
/// <summary>
///
/// </summary>
public static void GetShaderPropertyIDs()
{
if (isInitialized == false)
{
//Debug.Log("Getting Shader property IDs");
isInitialized = true;
ID_MainTex = Shader.PropertyToID("_MainTex");
ID_FaceTex = Shader.PropertyToID("_FaceTex");
ID_FaceColor = Shader.PropertyToID("_FaceColor");
ID_FaceDilate = Shader.PropertyToID("_FaceDilate");
ID_Shininess = Shader.PropertyToID("_FaceShininess");
ID_UnderlayColor = Shader.PropertyToID("_UnderlayColor");
ID_UnderlayOffsetX = Shader.PropertyToID("_UnderlayOffsetX");
ID_UnderlayOffsetY = Shader.PropertyToID("_UnderlayOffsetY");
ID_UnderlayDilate = Shader.PropertyToID("_UnderlayDilate");
ID_UnderlaySoftness = Shader.PropertyToID("_UnderlaySoftness");
ID_UnderlayOffset = Shader.PropertyToID("_UnderlayOffset");
ID_UnderlayIsoPerimeter = Shader.PropertyToID("_UnderlayIsoPerimeter");
ID_WeightNormal = Shader.PropertyToID("_WeightNormal");
ID_WeightBold = Shader.PropertyToID("_WeightBold");
ID_OutlineTex = Shader.PropertyToID("_OutlineTex");
ID_OutlineWidth = Shader.PropertyToID("_OutlineWidth");
ID_OutlineSoftness = Shader.PropertyToID("_OutlineSoftness");
ID_OutlineColor = Shader.PropertyToID("_OutlineColor");
ID_Outline2Color = Shader.PropertyToID("_Outline2Color");
ID_Outline2Width = Shader.PropertyToID("_Outline2Width");
ID_Padding = Shader.PropertyToID("_Padding");
ID_GradientScale = Shader.PropertyToID("_GradientScale");
ID_ScaleX = Shader.PropertyToID("_ScaleX");
ID_ScaleY = Shader.PropertyToID("_ScaleY");
ID_PerspectiveFilter = Shader.PropertyToID("_PerspectiveFilter");
ID_Sharpness = Shader.PropertyToID("_Sharpness");
ID_TextureWidth = Shader.PropertyToID("_TextureWidth");
ID_TextureHeight = Shader.PropertyToID("_TextureHeight");
ID_BevelAmount = Shader.PropertyToID("_Bevel");
ID_LightAngle = Shader.PropertyToID("_LightAngle");
ID_EnvMap = Shader.PropertyToID("_Cube");
ID_EnvMatrix = Shader.PropertyToID("_EnvMatrix");
ID_EnvMatrixRotation = Shader.PropertyToID("_EnvMatrixRotation");
ID_GlowColor = Shader.PropertyToID("_GlowColor");
ID_GlowOffset = Shader.PropertyToID("_GlowOffset");
ID_GlowPower = Shader.PropertyToID("_GlowPower");
ID_GlowOuter = Shader.PropertyToID("_GlowOuter");
ID_GlowInner = Shader.PropertyToID("_GlowInner");
//ID_MaskID = Shader.PropertyToID("_MaskID");
ID_MaskCoord = Shader.PropertyToID("_MaskCoord");
ID_ClipRect = Shader.PropertyToID("_ClipRect");
ID_UseClipRect = Shader.PropertyToID("_UseClipRect");
ID_MaskSoftnessX = Shader.PropertyToID("_MaskSoftnessX");
ID_MaskSoftnessY = Shader.PropertyToID("_MaskSoftnessY");
ID_VertexOffsetX = Shader.PropertyToID("_VertexOffsetX");
ID_VertexOffsetY = Shader.PropertyToID("_VertexOffsetY");
ID_StencilID = Shader.PropertyToID("_Stencil");
ID_StencilOp = Shader.PropertyToID("_StencilOp");
ID_StencilComp = Shader.PropertyToID("_StencilComp");
ID_StencilReadMask = Shader.PropertyToID("_StencilReadMask");
ID_StencilWriteMask = Shader.PropertyToID("_StencilWriteMask");
ID_ShaderFlags = Shader.PropertyToID("_ShaderFlags");
ID_ScaleRatio_A = Shader.PropertyToID("_ScaleRatioA");
ID_ScaleRatio_B = Shader.PropertyToID("_ScaleRatioB");
ID_ScaleRatio_C = Shader.PropertyToID("_ScaleRatioC");
// Set internal shader references
if (k_ShaderRef_MobileSDF == null)
k_ShaderRef_MobileSDF = Shader.Find("TextMeshPro/Mobile/Distance Field");
if (k_ShaderRef_MobileBitmap == null)
k_ShaderRef_MobileBitmap = Shader.Find("TextMeshPro/Mobile/Bitmap");
}
}
// Scale Ratios to ensure property ranges are optimum in Material Editor
public static void UpdateShaderRatios(Material mat)
{
//Debug.Log("UpdateShaderRatios() called.");
float ratio_A = 1;
float ratio_B = 1;
float ratio_C = 1;
bool isRatioEnabled = !mat.shaderKeywords.Contains(Keyword_Ratios);
if (!mat.HasProperty(ID_GradientScale) || !mat.HasProperty(ID_FaceDilate))
return;
// Compute Ratio A
float scale = mat.GetFloat(ID_GradientScale);
float faceDilate = mat.GetFloat(ID_FaceDilate);
float outlineThickness = mat.GetFloat(ID_OutlineWidth);
float outlineSoftness = mat.GetFloat(ID_OutlineSoftness);
float weight = Mathf.Max(mat.GetFloat(ID_WeightNormal), mat.GetFloat(ID_WeightBold)) / 4.0f;
float t = Mathf.Max(1, weight + faceDilate + outlineThickness + outlineSoftness);
ratio_A = isRatioEnabled ? (scale - m_clamp) / (scale * t) : 1;
//float ratio_A_old = mat.GetFloat(ID_ScaleRatio_A);
// Only set the ratio if it has changed.
//if (ratio_A != ratio_A_old)
mat.SetFloat(ID_ScaleRatio_A, ratio_A);
// Compute Ratio B
if (mat.HasProperty(ID_GlowOffset))
{
float glowOffset = mat.GetFloat(ID_GlowOffset);
float glowOuter = mat.GetFloat(ID_GlowOuter);
float range = (weight + faceDilate) * (scale - m_clamp);
t = Mathf.Max(1, glowOffset + glowOuter);
ratio_B = isRatioEnabled ? Mathf.Max(0, scale - m_clamp - range) / (scale * t) : 1;
//float ratio_B_old = mat.GetFloat(ID_ScaleRatio_B);
// Only set the ratio if it has changed.
//if (ratio_B != ratio_B_old)
mat.SetFloat(ID_ScaleRatio_B, ratio_B);
}
// Compute Ratio C
if (mat.HasProperty(ID_UnderlayOffsetX))
{
float underlayOffsetX = mat.GetFloat(ID_UnderlayOffsetX);
float underlayOffsetY = mat.GetFloat(ID_UnderlayOffsetY);
float underlayDilate = mat.GetFloat(ID_UnderlayDilate);
float underlaySoftness = mat.GetFloat(ID_UnderlaySoftness);
float range = (weight + faceDilate) * (scale - m_clamp);
t = Mathf.Max(1, Mathf.Max(Mathf.Abs(underlayOffsetX), Mathf.Abs(underlayOffsetY)) + underlayDilate + underlaySoftness);
ratio_C = isRatioEnabled ? Mathf.Max(0, scale - m_clamp - range) / (scale * t) : 1;
//float ratio_C_old = mat.GetFloat(ID_ScaleRatio_C);
// Only set the ratio if it has changed.
//if (ratio_C != ratio_C_old)
mat.SetFloat(ID_ScaleRatio_C, ratio_C);
}
}
// Function to calculate padding required for Outline Width & Dilation for proper text alignment
public static Vector4 GetFontExtent(Material material)
{
// Revised implementation where style no longer affects alignment
return Vector4.zero;
/*
if (material == null || !material.HasProperty(ShaderUtilities.ID_GradientScale))
return Vector4.zero; // We are using an non SDF Shader.
float scaleRatioA = material.GetFloat(ID_ScaleRatio_A);
float faceDilate = material.GetFloat(ID_FaceDilate) * scaleRatioA;
float outlineThickness = material.GetFloat(ID_OutlineWidth) * scaleRatioA;
float extent = Mathf.Min(1, faceDilate + outlineThickness);
extent *= material.GetFloat(ID_GradientScale);
return new Vector4(extent, extent, extent, extent);
*/
}
// Function to check if Masking is enabled
public static bool IsMaskingEnabled(Material material)
{
if (material == null || !material.HasProperty(ShaderUtilities.ID_ClipRect))
return false;
if (material.shaderKeywords.Contains(ShaderUtilities.Keyword_MASK_SOFT) || material.shaderKeywords.Contains(ShaderUtilities.Keyword_MASK_HARD) || material.shaderKeywords.Contains(ShaderUtilities.Keyword_MASK_TEX))
return true;
return false;
}
// Function to determine how much extra padding is required as a result of material properties like dilate, outline thickness, softness, glow, etc...
public static float GetPadding(Material material, bool enableExtraPadding, bool isBold)
{
//Debug.Log("GetPadding() called.");
if (isInitialized == false)
GetShaderPropertyIDs();
// Return if Material is null
if (material == null) return 0;
int extraPadding = enableExtraPadding ? 4 : 0;
// Check if we are using a non Distance Field Shader
if (material.HasProperty(ID_GradientScale) == false)
{
if (material.HasProperty(ID_Padding))
extraPadding += (int)material.GetFloat(ID_Padding);
return extraPadding + 1.0f;
}
Vector4 padding = Vector4.zero;
Vector4 maxPadding = Vector4.zero;
//float weight = 0;
float faceDilate = 0;
float faceSoftness = 0;
float outlineThickness = 0;
float scaleRatio_A = 0;
float scaleRatio_B = 0;
float scaleRatio_C = 0;
float glowOffset = 0;
float glowOuter = 0;
float uniformPadding = 0;
// Iterate through each of the assigned materials to find the max values to set the padding.
// Update Shader Ratios prior to computing padding
UpdateShaderRatios(material);
string[] shaderKeywords = material.shaderKeywords;
if (material.HasProperty(ID_ScaleRatio_A))
scaleRatio_A = material.GetFloat(ID_ScaleRatio_A);
//weight = 0; // Mathf.Max(material.GetFloat(ID_WeightNormal), material.GetFloat(ID_WeightBold)) / 2.0f * scaleRatio_A;
if (material.HasProperty(ID_FaceDilate))
faceDilate = material.GetFloat(ID_FaceDilate) * scaleRatio_A;
if (material.HasProperty(ID_OutlineSoftness))
faceSoftness = material.GetFloat(ID_OutlineSoftness) * scaleRatio_A;
if (material.HasProperty(ID_OutlineWidth))
outlineThickness = material.GetFloat(ID_OutlineWidth) * scaleRatio_A;
uniformPadding = outlineThickness + faceSoftness + faceDilate;
// Glow padding contribution
if (material.HasProperty(ID_GlowOffset) && shaderKeywords.Contains(Keyword_Glow)) // Generates GC
{
if (material.HasProperty(ID_ScaleRatio_B))
scaleRatio_B = material.GetFloat(ID_ScaleRatio_B);
glowOffset = material.GetFloat(ID_GlowOffset) * scaleRatio_B;
glowOuter = material.GetFloat(ID_GlowOuter) * scaleRatio_B;
}
uniformPadding = Mathf.Max(uniformPadding, faceDilate + glowOffset + glowOuter);
// Underlay padding contribution
if (material.HasProperty(ID_UnderlaySoftness) && shaderKeywords.Contains(Keyword_Underlay)) // Generates GC
{
if (material.HasProperty(ID_ScaleRatio_C))
scaleRatio_C = material.GetFloat(ID_ScaleRatio_C);
float offsetX = 0;
float offsetY = 0;
float dilate = 0;
float softness = 0;
if (material.HasProperty(ID_UnderlayOffset))
{
Vector2 underlayOffset = material.GetVector(ID_UnderlayOffset);
offsetX = underlayOffset.x;
offsetY = underlayOffset.y;
dilate = material.GetFloat(ID_UnderlayIsoPerimeter);
softness = material.GetFloat(ID_UnderlaySoftness);
}
else if (material.HasProperty(ID_UnderlayOffsetX))
{
offsetX = material.GetFloat(ID_UnderlayOffsetX) * scaleRatio_C;
offsetY = material.GetFloat(ID_UnderlayOffsetY) * scaleRatio_C;
dilate = material.GetFloat(ID_UnderlayDilate) * scaleRatio_C;
softness = material.GetFloat(ID_UnderlaySoftness) * scaleRatio_C;
}
padding.x = Mathf.Max(padding.x, faceDilate + dilate + softness - offsetX);
padding.y = Mathf.Max(padding.y, faceDilate + dilate + softness - offsetY);
padding.z = Mathf.Max(padding.z, faceDilate + dilate + softness + offsetX);
padding.w = Mathf.Max(padding.w, faceDilate + dilate + softness + offsetY);
}
padding.x = Mathf.Max(padding.x, uniformPadding);
padding.y = Mathf.Max(padding.y, uniformPadding);
padding.z = Mathf.Max(padding.z, uniformPadding);
padding.w = Mathf.Max(padding.w, uniformPadding);
padding.x += extraPadding;
padding.y += extraPadding;
padding.z += extraPadding;
padding.w += extraPadding;
padding.x = Mathf.Min(padding.x, 1);
padding.y = Mathf.Min(padding.y, 1);
padding.z = Mathf.Min(padding.z, 1);
padding.w = Mathf.Min(padding.w, 1);
maxPadding.x = maxPadding.x < padding.x ? padding.x : maxPadding.x;
maxPadding.y = maxPadding.y < padding.y ? padding.y : maxPadding.y;
maxPadding.z = maxPadding.z < padding.z ? padding.z : maxPadding.z;
maxPadding.w = maxPadding.w < padding.w ? padding.w : maxPadding.w;
float gradientScale = material.GetFloat(ID_GradientScale);
padding *= gradientScale;
// Set UniformPadding to the maximum value of any of its components.
uniformPadding = Mathf.Max(padding.x, padding.y);
uniformPadding = Mathf.Max(padding.z, uniformPadding);
uniformPadding = Mathf.Max(padding.w, uniformPadding);
return uniformPadding + 1.25f;
}
// Function to determine how much extra padding is required as a result of material properties like dilate, outline thickness, softness, glow, etc...
public static float GetPadding(Material[] materials, bool enableExtraPadding, bool isBold)
{
//Debug.Log("GetPadding() called.");
if (isInitialized == false)
GetShaderPropertyIDs();
// Return if Material is null
if (materials == null) return 0;
int extraPadding = enableExtraPadding ? 4 : 0;
// Check if we are using a Bitmap Shader
if (materials[0].HasProperty(ID_Padding))
return extraPadding + materials[0].GetFloat(ID_Padding);
Vector4 padding = Vector4.zero;
Vector4 maxPadding = Vector4.zero;
float faceDilate = 0;
float faceSoftness = 0;
float outlineThickness = 0;
float scaleRatio_A = 0;
float scaleRatio_B = 0;
float scaleRatio_C = 0;
float glowOffset = 0;
float glowOuter = 0;
float uniformPadding = 0;
// Iterate through each of the assigned materials to find the max values to set the padding.
for (int i = 0; i < materials.Length; i++)
{
// Update Shader Ratios prior to computing padding
ShaderUtilities.UpdateShaderRatios(materials[i]);
string[] shaderKeywords = materials[i].shaderKeywords;
if (materials[i].HasProperty(ShaderUtilities.ID_ScaleRatio_A))
scaleRatio_A = materials[i].GetFloat(ShaderUtilities.ID_ScaleRatio_A);
if (materials[i].HasProperty(ShaderUtilities.ID_FaceDilate))
faceDilate = materials[i].GetFloat(ShaderUtilities.ID_FaceDilate) * scaleRatio_A;
if (materials[i].HasProperty(ShaderUtilities.ID_OutlineSoftness))
faceSoftness = materials[i].GetFloat(ShaderUtilities.ID_OutlineSoftness) * scaleRatio_A;
if (materials[i].HasProperty(ShaderUtilities.ID_OutlineWidth))
outlineThickness = materials[i].GetFloat(ShaderUtilities.ID_OutlineWidth) * scaleRatio_A;
uniformPadding = outlineThickness + faceSoftness + faceDilate;
// Glow padding contribution
if (materials[i].HasProperty(ShaderUtilities.ID_GlowOffset) && shaderKeywords.Contains(ShaderUtilities.Keyword_Glow))
{
if (materials[i].HasProperty(ShaderUtilities.ID_ScaleRatio_B))
scaleRatio_B = materials[i].GetFloat(ShaderUtilities.ID_ScaleRatio_B);
glowOffset = materials[i].GetFloat(ShaderUtilities.ID_GlowOffset) * scaleRatio_B;
glowOuter = materials[i].GetFloat(ShaderUtilities.ID_GlowOuter) * scaleRatio_B;
}
uniformPadding = Mathf.Max(uniformPadding, faceDilate + glowOffset + glowOuter);
// Underlay padding contribution
if (materials[i].HasProperty(ShaderUtilities.ID_UnderlaySoftness) && shaderKeywords.Contains(ShaderUtilities.Keyword_Underlay))
{
if (materials[i].HasProperty(ShaderUtilities.ID_ScaleRatio_C))
scaleRatio_C = materials[i].GetFloat(ShaderUtilities.ID_ScaleRatio_C);
float offsetX = materials[i].GetFloat(ShaderUtilities.ID_UnderlayOffsetX) * scaleRatio_C;
float offsetY = materials[i].GetFloat(ShaderUtilities.ID_UnderlayOffsetY) * scaleRatio_C;
float dilate = materials[i].GetFloat(ShaderUtilities.ID_UnderlayDilate) * scaleRatio_C;
float softness = materials[i].GetFloat(ShaderUtilities.ID_UnderlaySoftness) * scaleRatio_C;
padding.x = Mathf.Max(padding.x, faceDilate + dilate + softness - offsetX);
padding.y = Mathf.Max(padding.y, faceDilate + dilate + softness - offsetY);
padding.z = Mathf.Max(padding.z, faceDilate + dilate + softness + offsetX);
padding.w = Mathf.Max(padding.w, faceDilate + dilate + softness + offsetY);
}
padding.x = Mathf.Max(padding.x, uniformPadding);
padding.y = Mathf.Max(padding.y, uniformPadding);
padding.z = Mathf.Max(padding.z, uniformPadding);
padding.w = Mathf.Max(padding.w, uniformPadding);
padding.x += extraPadding;
padding.y += extraPadding;
padding.z += extraPadding;
padding.w += extraPadding;
padding.x = Mathf.Min(padding.x, 1);
padding.y = Mathf.Min(padding.y, 1);
padding.z = Mathf.Min(padding.z, 1);
padding.w = Mathf.Min(padding.w, 1);
maxPadding.x = maxPadding.x < padding.x ? padding.x : maxPadding.x;
maxPadding.y = maxPadding.y < padding.y ? padding.y : maxPadding.y;
maxPadding.z = maxPadding.z < padding.z ? padding.z : maxPadding.z;
maxPadding.w = maxPadding.w < padding.w ? padding.w : maxPadding.w;
}
float gradientScale = materials[0].GetFloat(ShaderUtilities.ID_GradientScale);
padding *= gradientScale;
// Set UniformPadding to the maximum value of any of its components.
uniformPadding = Mathf.Max(padding.x, padding.y);
uniformPadding = Mathf.Max(padding.z, uniformPadding);
uniformPadding = Mathf.Max(padding.w, uniformPadding);
return uniformPadding + 0.25f;
}
}
}

View File

@@ -0,0 +1,31 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace TMPro
{
// Class which contains the Sprite Info for each sprite contained in the sprite asset.
[Serializable]
public class TMP_Sprite : TMP_TextElement_Legacy
{
//public int fileID;
//public int id;
public string name;
public int hashCode;
public int unicode;
//public float x;
//public float y;
//public float width;
//public float height;
public Vector2 pivot;
//public float xOffset; // Pivot X
//public float yOffset; // Pivot Y
//public float xAdvance;
//public float scale;
public Sprite sprite;
}
}

View File

@@ -0,0 +1,157 @@
using UnityEngine;
using UnityEngine.TextCore;
using System.Collections;
using System.Collections.Generic;
namespace TMPro
{
[DisallowMultipleComponent]
public class TMP_SpriteAnimator : MonoBehaviour
{
private Dictionary<int, bool> m_animations = new Dictionary<int, bool>(16);
//private bool isPlaying = false;
private TMP_Text m_TextComponent;
void Awake()
{
m_TextComponent = GetComponent<TMP_Text>();
}
void OnEnable()
{
//m_playAnimations = true;
}
void OnDisable()
{
//m_playAnimations = false;
}
public void StopAllAnimations()
{
StopAllCoroutines();
m_animations.Clear();
}
public void DoSpriteAnimation(int currentCharacter, TMP_SpriteAsset spriteAsset, int start, int end, int framerate)
{
bool isPlaying;
// Need to add tracking of coroutines that have been lunched for this text object.
if (!m_animations.TryGetValue(currentCharacter, out isPlaying))
{
StartCoroutine(DoSpriteAnimationInternal(currentCharacter, spriteAsset, start, end, framerate));
m_animations.Add(currentCharacter, true);
}
}
IEnumerator DoSpriteAnimationInternal(int currentCharacter, TMP_SpriteAsset spriteAsset, int start, int end, int framerate)
{
if (m_TextComponent == null) yield break;
// We yield otherwise this gets called before the sprite has rendered.
yield return null;
int currentFrame = start;
// Make sure end frame does not exceed the number of sprites in the sprite asset.
if (end > spriteAsset.spriteCharacterTable.Count)
end = spriteAsset.spriteCharacterTable.Count - 1;
// Get a reference to the current character's info
TMP_CharacterInfo charInfo = m_TextComponent.textInfo.characterInfo[currentCharacter];
int materialIndex = charInfo.materialReferenceIndex;
int vertexIndex = charInfo.vertexIndex;
TMP_MeshInfo meshInfo = m_TextComponent.textInfo.meshInfo[materialIndex];
float baseSpriteScale = spriteAsset.spriteCharacterTable[start].scale * spriteAsset.spriteCharacterTable[start].glyph.scale;
float elapsedTime = 0;
float targetTime = 1f / Mathf.Abs(framerate);
while (true)
{
if (elapsedTime > targetTime)
{
elapsedTime = 0;
// Return if sprite was truncated or replaced by the Ellipsis character.
char character = m_TextComponent.textInfo.characterInfo[currentCharacter].character;
if (character == 0x03 || character == 0x2026)
{
m_animations.Remove(currentCharacter);
yield break;
}
// Get a reference to the current sprite
TMP_SpriteCharacter spriteCharacter = spriteAsset.spriteCharacterTable[currentFrame];
// Update the vertices for the new sprite
Vector3[] vertices = meshInfo.vertices;
Vector2 origin = new Vector2(charInfo.origin, charInfo.baseLine);
float spriteScale = charInfo.scale / baseSpriteScale * spriteCharacter.scale * spriteCharacter.glyph.scale;
Vector3 bl = new Vector3(origin.x + spriteCharacter.glyph.metrics.horizontalBearingX * spriteScale, origin.y + (spriteCharacter.glyph.metrics.horizontalBearingY - spriteCharacter.glyph.metrics.height) * spriteScale);
Vector3 tl = new Vector3(bl.x, origin.y + spriteCharacter.glyph.metrics.horizontalBearingY * spriteScale);
Vector3 tr = new Vector3(origin.x + (spriteCharacter.glyph.metrics.horizontalBearingX + spriteCharacter.glyph.metrics.width) * spriteScale, tl.y);
Vector3 br = new Vector3(tr.x, bl.y);
vertices[vertexIndex + 0] = bl;
vertices[vertexIndex + 1] = tl;
vertices[vertexIndex + 2] = tr;
vertices[vertexIndex + 3] = br;
// Update the UV to point to the new sprite
Vector2[] uvs0 = meshInfo.uvs0;
Vector2 uv0 = new Vector2((float)spriteCharacter.glyph.glyphRect.x / spriteAsset.spriteSheet.width, (float)spriteCharacter.glyph.glyphRect.y / spriteAsset.spriteSheet.height);
Vector2 uv1 = new Vector2(uv0.x, (float)(spriteCharacter.glyph.glyphRect.y + spriteCharacter.glyph.glyphRect.height) / spriteAsset.spriteSheet.height);
Vector2 uv2 = new Vector2((float)(spriteCharacter.glyph.glyphRect.x + spriteCharacter.glyph.glyphRect.width) / spriteAsset.spriteSheet.width, uv1.y);
Vector2 uv3 = new Vector2(uv2.x, uv0.y);
uvs0[vertexIndex + 0] = uv0;
uvs0[vertexIndex + 1] = uv1;
uvs0[vertexIndex + 2] = uv2;
uvs0[vertexIndex + 3] = uv3;
// Update the modified vertex attributes
meshInfo.mesh.vertices = vertices;
meshInfo.mesh.uv = uvs0;
m_TextComponent.UpdateGeometry(meshInfo.mesh, materialIndex);
if (framerate > 0)
{
if (currentFrame < end)
currentFrame += 1;
else
currentFrame = start;
}
else
{
if (currentFrame > start)
currentFrame -= 1;
else
currentFrame = end;
}
}
elapsedTime += Time.deltaTime;
yield return null;
}
}
}
}

View File

@@ -0,0 +1,587 @@
using UnityEngine;
using UnityEngine.TextCore;
using System.Collections.Generic;
using System.Linq;
namespace TMPro
{
[ExcludeFromPresetAttribute]
public class TMP_SpriteAsset : TMP_Asset
{
internal Dictionary<int, int> m_NameLookup;
internal Dictionary<uint, int> m_GlyphIndexLookup;
/// <summary>
/// The version of the sprite asset class.
/// Version 1.1.0 updates the asset data structure to be compatible with new font asset structure.
/// </summary>
public string version
{
get { return m_Version; }
internal set { m_Version = value; }
}
[SerializeField]
private string m_Version;
/// <summary>
/// Information about the sprite asset's face.
/// </summary>
public FaceInfo faceInfo
{
get { return m_FaceInfo; }
internal set { m_FaceInfo = value; }
}
[SerializeField]
internal FaceInfo m_FaceInfo;
// The texture which contains the sprites.
public Texture spriteSheet;
/// <summary>
///
/// </summary>
public List<TMP_SpriteCharacter> spriteCharacterTable
{
get
{
if (m_GlyphIndexLookup == null)
UpdateLookupTables();
return m_SpriteCharacterTable;
}
internal set { m_SpriteCharacterTable = value; }
}
[SerializeField]
private List<TMP_SpriteCharacter> m_SpriteCharacterTable = new List<TMP_SpriteCharacter>();
/// <summary>
/// Dictionary used to lookup sprite characters by their unicode value.
/// </summary>
public Dictionary<uint, TMP_SpriteCharacter> spriteCharacterLookupTable
{
get
{
if (m_SpriteCharacterLookup == null)
UpdateLookupTables();
return m_SpriteCharacterLookup;
}
internal set { m_SpriteCharacterLookup = value; }
}
internal Dictionary<uint, TMP_SpriteCharacter> m_SpriteCharacterLookup;
public List<TMP_SpriteGlyph> spriteGlyphTable
{
get { return m_SpriteGlyphTable; }
internal set { m_SpriteGlyphTable = value; }
}
[SerializeField]
private List<TMP_SpriteGlyph> m_SpriteGlyphTable = new List<TMP_SpriteGlyph>();
internal Dictionary<uint, TMP_SpriteGlyph> m_SpriteGlyphLookup;
// List which contains the SpriteInfo for the sprites contained in the sprite sheet.
public List<TMP_Sprite> spriteInfoList;
/// <summary>
/// List which contains the Fallback font assets for this font.
/// </summary>
[SerializeField]
public List<TMP_SpriteAsset> fallbackSpriteAssets;
internal bool m_IsSpriteAssetLookupTablesDirty = false;
void Awake()
{
// Check version number of sprite asset to see if it needs to be upgraded.
if (this.material != null && string.IsNullOrEmpty(m_Version))
UpgradeSpriteAsset();
}
/// <summary>
/// Create a material for the sprite asset.
/// </summary>
/// <returns></returns>
Material GetDefaultSpriteMaterial()
{
//isEditingAsset = true;
ShaderUtilities.GetShaderPropertyIDs();
// Add a new material
Shader shader = Shader.Find("TextMeshPro/Sprite");
Material tempMaterial = new Material(shader);
tempMaterial.SetTexture(ShaderUtilities.ID_MainTex, spriteSheet);
tempMaterial.hideFlags = HideFlags.HideInHierarchy;
#if UNITY_EDITOR
UnityEditor.AssetDatabase.AddObjectToAsset(tempMaterial, this);
UnityEditor.AssetDatabase.ImportAsset(UnityEditor.AssetDatabase.GetAssetPath(this));
#endif
//isEditingAsset = false;
return tempMaterial;
}
/// <summary>
/// Function to update the sprite name and unicode lookup tables.
/// This function should be called when a sprite's name or unicode value changes or when a new sprite is added.
/// </summary>
public void UpdateLookupTables()
{
//Debug.Log("Updating [" + this.name + "] Lookup tables.");
// Check version number of sprite asset to see if it needs to be upgraded.
if (this.material != null && string.IsNullOrEmpty(m_Version))
UpgradeSpriteAsset();
// Initialize / Clear glyph index lookup dictionary.
if (m_GlyphIndexLookup == null)
m_GlyphIndexLookup = new Dictionary<uint, int>();
else
m_GlyphIndexLookup.Clear();
//
if (m_SpriteGlyphLookup == null)
m_SpriteGlyphLookup = new Dictionary<uint, TMP_SpriteGlyph>();
else
m_SpriteGlyphLookup.Clear();
// Initialize SpriteGlyphLookup
for (int i = 0; i < m_SpriteGlyphTable.Count; i++)
{
TMP_SpriteGlyph spriteGlyph = m_SpriteGlyphTable[i];
uint glyphIndex = spriteGlyph.index;
if (m_GlyphIndexLookup.ContainsKey(glyphIndex) == false)
m_GlyphIndexLookup.Add(glyphIndex, i);
if (m_SpriteGlyphLookup.ContainsKey(glyphIndex) == false)
m_SpriteGlyphLookup.Add(glyphIndex, spriteGlyph);
}
// Initialize name lookup
if (m_NameLookup == null)
m_NameLookup = new Dictionary<int, int>();
else
m_NameLookup.Clear();
// Initialize character lookup
if (m_SpriteCharacterLookup == null)
m_SpriteCharacterLookup = new Dictionary<uint, TMP_SpriteCharacter>();
else
m_SpriteCharacterLookup.Clear();
// Populate Sprite Character lookup tables
for (int i = 0; i < m_SpriteCharacterTable.Count; i++)
{
TMP_SpriteCharacter spriteCharacter = m_SpriteCharacterTable[i];
// Make sure sprite character is valid
if (spriteCharacter == null)
continue;
uint glyphIndex = spriteCharacter.glyphIndex;
// Lookup the glyph for this character
if (m_SpriteGlyphLookup.ContainsKey(glyphIndex) == false)
continue;
// Assign glyph and text asset to this character
spriteCharacter.glyph = m_SpriteGlyphLookup[glyphIndex];
spriteCharacter.textAsset = this;
int nameHashCode = m_SpriteCharacterTable[i].hashCode;
if (m_NameLookup.ContainsKey(nameHashCode) == false)
m_NameLookup.Add(nameHashCode, i);
uint unicode = m_SpriteCharacterTable[i].unicode;
if (unicode != 0xFFFE && m_SpriteCharacterLookup.ContainsKey(unicode) == false)
m_SpriteCharacterLookup.Add(unicode, spriteCharacter);
}
m_IsSpriteAssetLookupTablesDirty = false;
}
/// <summary>
/// Function which returns the sprite index using the hashcode of the name
/// </summary>
/// <param name="hashCode"></param>
/// <returns></returns>
public int GetSpriteIndexFromHashcode(int hashCode)
{
if (m_NameLookup == null)
UpdateLookupTables();
int index;
if (m_NameLookup.TryGetValue(hashCode, out index))
return index;
return -1;
}
/// <summary>
/// Returns the index of the sprite for the given unicode value.
/// </summary>
/// <param name="unicode"></param>
/// <returns></returns>
public int GetSpriteIndexFromUnicode (uint unicode)
{
if (m_SpriteCharacterLookup == null)
UpdateLookupTables();
TMP_SpriteCharacter spriteCharacter;
if (m_SpriteCharacterLookup.TryGetValue(unicode, out spriteCharacter))
return (int)spriteCharacter.glyphIndex;
return -1;
}
/// <summary>
/// Returns the index of the sprite for the given name.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public int GetSpriteIndexFromName (string name)
{
if (m_NameLookup == null)
UpdateLookupTables();
int hashCode = TMP_TextUtilities.GetSimpleHashCode(name);
return GetSpriteIndexFromHashcode(hashCode);
}
/// <summary>
/// Used to keep track of which Sprite Assets have been searched.
/// </summary>
private static HashSet<int> k_searchedSpriteAssets;
/// <summary>
/// Search through the given sprite asset and its fallbacks for the specified sprite matching the given unicode character.
/// </summary>
/// <param name="spriteAsset">The font asset to search for the given character.</param>
/// <param name="unicode">The character to find.</param>
/// <param name="glyph">out parameter containing the glyph for the specified character (if found).</param>
/// <returns></returns>
public static TMP_SpriteAsset SearchForSpriteByUnicode(TMP_SpriteAsset spriteAsset, uint unicode, bool includeFallbacks, out int spriteIndex)
{
// Check to make sure sprite asset is not null
if (spriteAsset == null) { spriteIndex = -1; return null; }
// Get sprite index for the given unicode
spriteIndex = spriteAsset.GetSpriteIndexFromUnicode(unicode);
if (spriteIndex != -1)
return spriteAsset;
// Initialize list to track instance of Sprite Assets that have already been searched.
if (k_searchedSpriteAssets == null)
k_searchedSpriteAssets = new HashSet<int>();
else
k_searchedSpriteAssets.Clear();
// Get instance ID of sprite asset and add to list.
int id = spriteAsset.GetInstanceID();
k_searchedSpriteAssets.Add(id);
// Search potential fallback sprite assets if includeFallbacks is true.
if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
return SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, unicode, true, out spriteIndex);
// Search default sprite asset potentially assigned in the TMP Settings.
if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
return SearchForSpriteByUnicodeInternal(TMP_Settings.defaultSpriteAsset, unicode, true, out spriteIndex);
spriteIndex = -1;
return null;
}
/// <summary>
/// Search through the given list of sprite assets and fallbacks for a sprite whose unicode value matches the target unicode.
/// </summary>
/// <param name="spriteAssets"></param>
/// <param name="unicode"></param>
/// <param name="includeFallbacks"></param>
/// <param name="spriteIndex"></param>
/// <returns></returns>
private static TMP_SpriteAsset SearchForSpriteByUnicodeInternal(List<TMP_SpriteAsset> spriteAssets, uint unicode, bool includeFallbacks, out int spriteIndex)
{
for (int i = 0; i < spriteAssets.Count; i++)
{
TMP_SpriteAsset temp = spriteAssets[i];
if (temp == null) continue;
int id = temp.GetInstanceID();
// Skip sprite asset if it has already been searched.
if (k_searchedSpriteAssets.Add(id) == false)
continue;
temp = SearchForSpriteByUnicodeInternal(temp, unicode, includeFallbacks, out spriteIndex);
if (temp != null)
return temp;
}
spriteIndex = -1;
return null;
}
/// <summary>
/// Search the given sprite asset and fallbacks for a sprite whose unicode value matches the target unicode.
/// </summary>
/// <param name="spriteAsset"></param>
/// <param name="unicode"></param>
/// <param name="includeFallbacks"></param>
/// <param name="spriteIndex"></param>
/// <returns></returns>
private static TMP_SpriteAsset SearchForSpriteByUnicodeInternal(TMP_SpriteAsset spriteAsset, uint unicode, bool includeFallbacks, out int spriteIndex)
{
// Get sprite index for the given unicode
spriteIndex = spriteAsset.GetSpriteIndexFromUnicode(unicode);
if (spriteIndex != -1)
return spriteAsset;
if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
return SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, unicode, true, out spriteIndex);
spriteIndex = -1;
return null;
}
/// <summary>
/// Search the given sprite asset and fallbacks for a sprite whose hash code value of its name matches the target hash code.
/// </summary>
/// <param name="spriteAsset">The Sprite Asset to search for the given sprite whose name matches the hashcode value</param>
/// <param name="hashCode">The hash code value matching the name of the sprite</param>
/// <param name="includeFallbacks">Include fallback sprite assets in the search</param>
/// <param name="spriteIndex">The index of the sprite matching the provided hash code</param>
/// <returns>The Sprite Asset that contains the sprite</returns>
public static TMP_SpriteAsset SearchForSpriteByHashCode(TMP_SpriteAsset spriteAsset, int hashCode, bool includeFallbacks, out int spriteIndex)
{
// Make sure sprite asset is not null
if (spriteAsset == null) { spriteIndex = -1; return null; }
spriteIndex = spriteAsset.GetSpriteIndexFromHashcode(hashCode);
if (spriteIndex != -1)
return spriteAsset;
// Initialize or clear list to Sprite Assets that have already been searched.
if (k_searchedSpriteAssets == null)
k_searchedSpriteAssets = new HashSet<int>();
else
k_searchedSpriteAssets.Clear();
int id = spriteAsset.instanceID;
// Add to list of font assets already searched.
k_searchedSpriteAssets.Add(id);
TMP_SpriteAsset tempSpriteAsset;
// Search potential fallbacks assigned to local sprite asset.
if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
{
tempSpriteAsset = SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, true, out spriteIndex);
if (spriteIndex != -1)
return tempSpriteAsset;
}
// Search default sprite asset potentially assigned in the TMP Settings.
if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
{
tempSpriteAsset = SearchForSpriteByHashCodeInternal(TMP_Settings.defaultSpriteAsset, hashCode, true, out spriteIndex);
if (spriteIndex != -1)
return tempSpriteAsset;
}
// Clear search list since we are now looking for the missing sprite character.
k_searchedSpriteAssets.Clear();
uint missingSpriteCharacterUnicode = TMP_Settings.missingCharacterSpriteUnicode;
// Get sprite index for the given unicode
spriteIndex = spriteAsset.GetSpriteIndexFromUnicode(missingSpriteCharacterUnicode);
if (spriteIndex != -1)
return spriteAsset;
// Add current sprite asset to list of assets already searched.
k_searchedSpriteAssets.Add(id);
// Search for the missing sprite character in the local sprite asset and potential fallbacks.
if (includeFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
{
tempSpriteAsset = SearchForSpriteByUnicodeInternal(spriteAsset.fallbackSpriteAssets, missingSpriteCharacterUnicode, true, out spriteIndex);
if (spriteIndex != -1)
return tempSpriteAsset;
}
// Search for the missing sprite character in the default sprite asset and potential fallbacks.
if (includeFallbacks && TMP_Settings.defaultSpriteAsset != null)
{
tempSpriteAsset = SearchForSpriteByUnicodeInternal(TMP_Settings.defaultSpriteAsset, missingSpriteCharacterUnicode, true, out spriteIndex);
if (spriteIndex != -1)
return tempSpriteAsset;
}
spriteIndex = -1;
return null;
}
/// <summary>
/// Search through the given list of sprite assets and fallbacks for a sprite whose hash code value of its name matches the target hash code.
/// </summary>
/// <param name="spriteAssets"></param>
/// <param name="hashCode"></param>
/// <param name="searchFallbacks"></param>
/// <param name="spriteIndex"></param>
/// <returns></returns>
private static TMP_SpriteAsset SearchForSpriteByHashCodeInternal(List<TMP_SpriteAsset> spriteAssets, int hashCode, bool searchFallbacks, out int spriteIndex)
{
// Search through the list of sprite assets
for (int i = 0; i < spriteAssets.Count; i++)
{
TMP_SpriteAsset temp = spriteAssets[i];
if (temp == null) continue;
int id = temp.instanceID;
// Skip sprite asset if it has already been searched.
if (k_searchedSpriteAssets.Add(id) == false)
continue;
temp = SearchForSpriteByHashCodeInternal(temp, hashCode, searchFallbacks, out spriteIndex);
if (temp != null)
return temp;
}
spriteIndex = -1;
return null;
}
/// <summary>
/// Search through the given sprite asset and fallbacks for a sprite whose hash code value of its name matches the target hash code.
/// </summary>
/// <param name="spriteAsset"></param>
/// <param name="hashCode"></param>
/// <param name="searchFallbacks"></param>
/// <param name="spriteIndex"></param>
/// <returns></returns>
private static TMP_SpriteAsset SearchForSpriteByHashCodeInternal(TMP_SpriteAsset spriteAsset, int hashCode, bool searchFallbacks, out int spriteIndex)
{
// Get the sprite for the given hash code.
spriteIndex = spriteAsset.GetSpriteIndexFromHashcode(hashCode);
if (spriteIndex != -1)
return spriteAsset;
if (searchFallbacks && spriteAsset.fallbackSpriteAssets != null && spriteAsset.fallbackSpriteAssets.Count > 0)
return SearchForSpriteByHashCodeInternal(spriteAsset.fallbackSpriteAssets, hashCode, true, out spriteIndex);
spriteIndex = -1;
return null;
}
/// <summary>
/// Sort the sprite glyph table by glyph index.
/// </summary>
public void SortGlyphTable()
{
if (m_SpriteGlyphTable == null || m_SpriteGlyphTable.Count == 0) return;
m_SpriteGlyphTable = m_SpriteGlyphTable.OrderBy(item => item.index).ToList();
}
/// <summary>
/// Sort the sprite character table by Unicode values.
/// </summary>
internal void SortCharacterTable()
{
if (m_SpriteCharacterTable != null && m_SpriteCharacterTable.Count > 0)
m_SpriteCharacterTable = m_SpriteCharacterTable.OrderBy(c => c.unicode).ToList();
}
/// <summary>
/// Sort both sprite glyph and character tables.
/// </summary>
internal void SortGlyphAndCharacterTables()
{
SortGlyphTable();
SortCharacterTable();
}
/// <summary>
/// Internal method used to upgrade sprite asset.
/// </summary>
private void UpgradeSpriteAsset()
{
m_Version = "1.1.0";
Debug.Log("Upgrading sprite asset [" + this.name + "] to version " + m_Version + ".", this);
// Convert legacy glyph and character tables to new format
m_SpriteCharacterTable.Clear();
m_SpriteGlyphTable.Clear();
for (int i = 0; i < spriteInfoList.Count; i++)
{
TMP_Sprite oldSprite = spriteInfoList[i];
TMP_SpriteGlyph spriteGlyph = new TMP_SpriteGlyph();
spriteGlyph.index = (uint)i;
spriteGlyph.sprite = oldSprite.sprite;
spriteGlyph.metrics = new GlyphMetrics(oldSprite.width, oldSprite.height, oldSprite.xOffset, oldSprite.yOffset, oldSprite.xAdvance);
spriteGlyph.glyphRect = new GlyphRect((int)oldSprite.x, (int)oldSprite.y, (int)oldSprite.width, (int)oldSprite.height);
spriteGlyph.scale = 1.0f;
spriteGlyph.atlasIndex = 0;
m_SpriteGlyphTable.Add(spriteGlyph);
TMP_SpriteCharacter spriteCharacter = new TMP_SpriteCharacter();
spriteCharacter.glyph = spriteGlyph;
spriteCharacter.unicode = oldSprite.unicode == 0x0 ? 0xFFFE : (uint)oldSprite.unicode;
spriteCharacter.name = oldSprite.name;
spriteCharacter.scale = oldSprite.scale;
m_SpriteCharacterTable.Add(spriteCharacter);
}
// Clear legacy glyph info list.
//spriteInfoList.Clear();
UpdateLookupTables();
#if UNITY_EDITOR
UnityEditor.EditorUtility.SetDirty(this);
UnityEditor.AssetDatabase.SaveAssets();
#endif
}
}
}

View File

@@ -0,0 +1,70 @@
using UnityEngine;
using System.Collections.Generic;
namespace TMPro.SpriteAssetUtilities
{
public enum SpriteAssetImportFormats { None = 0, TexturePackerJsonArray = 0x1 };
public class TexturePacker_JsonArray
{
[System.Serializable]
public struct SpriteFrame
{
public float x;
public float y;
public float w;
public float h;
public override string ToString()
{
string s = "x: " + x.ToString("f2") + " y: " + y.ToString("f2") + " h: " + h.ToString("f2") + " w: " + w.ToString("f2");
return s;
}
}
[System.Serializable]
public struct SpriteSize
{
public float w;
public float h;
public override string ToString()
{
string s = "w: " + w.ToString("f2") + " h: " + h.ToString("f2");
return s;
}
}
[System.Serializable]
public struct Frame
{
public string filename;
public SpriteFrame frame;
public bool rotated;
public bool trimmed;
public SpriteFrame spriteSourceSize;
public SpriteSize sourceSize;
public Vector2 pivot;
}
[System.Serializable]
public struct Meta
{
public string app;
public string version;
public string image;
public string format;
public SpriteSize size;
public float scale;
public string smartupdate;
}
[System.Serializable]
public class SpriteDataObject
{
public List<Frame> frames;
public Meta meta;
}
}
}

View File

@@ -0,0 +1,106 @@
using System;
using UnityEngine;
namespace TMPro
{
/// <summary>
/// A basic element of text representing a pictograph, image, sprite or emoji.
/// </summary>
[Serializable]
public class TMP_SpriteCharacter : TMP_TextElement
{
/// <summary>
/// The name of the sprite element.
/// </summary>
public string name
{
get { return m_Name; }
set
{
if (value == m_Name)
return;
m_Name = value;
m_HashCode = TMP_TextParsingUtilities.GetHashCodeCaseSensitive(m_Name);
}
}
/// <summary>
/// The hashcode value which is computed from the name of the sprite element.
/// This value is read-only and updated when the name of the text sprite is changed.
/// </summary>
public int hashCode { get { return m_HashCode; } }
// =============================================
// Private backing fields for public properties.
// =============================================
[SerializeField]
private string m_Name;
[SerializeField]
private int m_HashCode;
// ********************
// CONSTRUCTORS
// ********************
/// <summary>
/// Default constructor.
/// </summary>
public TMP_SpriteCharacter()
{
m_ElementType = TextElementType.Sprite;
}
/// <summary>
/// Constructor for new sprite character.
/// </summary>
/// <param name="unicode">Unicode value of the sprite character.</param>
/// <param name="glyph">Glyph used by the sprite character.</param>
public TMP_SpriteCharacter(uint unicode, TMP_SpriteGlyph glyph)
{
m_ElementType = TextElementType.Sprite;
this.unicode = unicode;
this.glyphIndex = glyph.index;
this.glyph = glyph;
this.scale = 1.0f;
}
/// <summary>
/// Constructor for new sprite character.
/// </summary>
/// <param name="unicode">Unicode value of the sprite character.</param>
/// <param name="spriteAsset">Sprite Asset used by this sprite character.</param>
/// <param name="glyph">Glyph used by the sprite character.</param>
public TMP_SpriteCharacter(uint unicode, TMP_SpriteAsset spriteAsset, TMP_SpriteGlyph glyph)
{
m_ElementType = TextElementType.Sprite;
this.unicode = unicode;
this.textAsset = spriteAsset;
this.glyph = glyph;
this.glyphIndex = glyph.index;
this.scale = 1.0f;
}
/// <summary>
///
/// </summary>
/// <param name="unicode"></param>
/// <param name="glyphIndex"></param>
internal TMP_SpriteCharacter(uint unicode, uint glyphIndex)
{
m_ElementType = TextElementType.Sprite;
this.unicode = unicode;
this.textAsset = null;
this.glyph = null;
this.glyphIndex = glyphIndex;
this.scale = 1.0f;
}
}
}

View File

@@ -0,0 +1,61 @@
using System;
using UnityEngine;
using UnityEngine.TextCore;
namespace TMPro
{
/// <summary>
/// The visual representation of the sprite character using this glyph.
/// </summary>
[Serializable]
public class TMP_SpriteGlyph : Glyph
{
/// <summary>
/// An optional reference to the underlying sprite used to create this glyph.
/// </summary>
public Sprite sprite;
// ********************
// CONSTRUCTORS
// ********************
public TMP_SpriteGlyph() { }
/// <summary>
/// Constructor for new sprite glyph.
/// </summary>
/// <param name="index">Index of the sprite glyph.</param>
/// <param name="metrics">Metrics which define the position of the glyph in the context of text layout.</param>
/// <param name="glyphRect">GlyphRect which defines the coordinates of the glyph in the atlas texture.</param>
/// <param name="scale">Scale of the glyph.</param>
/// <param name="atlasIndex">Index of the atlas texture that contains the glyph.</param>
public TMP_SpriteGlyph(uint index, GlyphMetrics metrics, GlyphRect glyphRect, float scale, int atlasIndex)
{
this.index = index;
this.metrics = metrics;
this.glyphRect = glyphRect;
this.scale = scale;
this.atlasIndex = atlasIndex;
}
/// <summary>
/// Constructor for new sprite glyph.
/// </summary>
/// <param name="index">>Index of the sprite glyph.</param>
/// <param name="metrics">Metrics which define the position of the glyph in the context of text layout.</param>
/// <param name="glyphRect">GlyphRect which defines the coordinates of the glyph in the atlas texture.</param>
/// <param name="scale">Scale of the glyph.</param>
/// <param name="atlasIndex">Index of the atlas texture that contains the glyph.</param>
/// <param name="sprite">A reference to the Unity Sprite representing this sprite glyph.</param>
public TMP_SpriteGlyph(uint index, GlyphMetrics metrics, GlyphRect glyphRect, float scale, int atlasIndex, Sprite sprite)
{
this.index = index;
this.metrics = metrics;
this.glyphRect = glyphRect;
this.scale = scale;
this.atlasIndex = atlasIndex;
this.sprite = sprite;
}
}
}

View File

@@ -0,0 +1,129 @@
using UnityEngine;
using System.Collections;
#pragma warning disable 0649 // Disabled warnings.
namespace TMPro
{
[System.Serializable]
public class TMP_Style
{
public static TMP_Style NormalStyle
{
get
{
if (k_NormalStyle == null)
k_NormalStyle = new TMP_Style("Normal", string.Empty, string.Empty);
return k_NormalStyle;
}
}
internal static TMP_Style k_NormalStyle;
// PUBLIC PROPERTIES
/// <summary>
/// The name identifying this style. ex. <style="name">.
/// </summary>
public string name
{ get { return m_Name; } set { if (value != m_Name) m_Name = value; } }
/// <summary>
/// The hash code corresponding to the name of this style.
/// </summary>
public int hashCode
{ get { return m_HashCode; } set { if (value != m_HashCode) m_HashCode = value; } }
/// <summary>
/// The initial definition of the style. ex. <b> <u>.
/// </summary>
public string styleOpeningDefinition
{ get { return m_OpeningDefinition; } }
/// <summary>
/// The closing definition of the style. ex. </b> </u>.
/// </summary>
public string styleClosingDefinition
{ get { return m_ClosingDefinition; } }
public int[] styleOpeningTagArray
{ get { return m_OpeningTagArray; } }
public int[] styleClosingTagArray
{ get { return m_ClosingTagArray; } }
// PRIVATE FIELDS
[SerializeField]
private string m_Name;
[SerializeField]
private int m_HashCode;
[SerializeField]
private string m_OpeningDefinition;
[SerializeField]
private string m_ClosingDefinition;
[SerializeField]
private int[] m_OpeningTagArray;
[SerializeField]
private int[] m_ClosingTagArray;
[SerializeField]
internal uint[] m_OpeningTagUnicodeArray;
[SerializeField]
internal uint[] m_ClosingTagUnicodeArray;
/// <summary>
/// Constructor
/// </summary>
/// <param name="styleName">Name of the style.</param>
/// <param name="styleOpeningDefinition">Style opening definition.</param>
/// <param name="styleClosingDefinition">Style closing definition.</param>
internal TMP_Style(string styleName, string styleOpeningDefinition, string styleClosingDefinition)
{
m_Name = styleName;
m_HashCode = TMP_TextParsingUtilities.GetHashCode(styleName);
m_OpeningDefinition = styleOpeningDefinition;
m_ClosingDefinition = styleClosingDefinition;
RefreshStyle();
}
/// <summary>
/// Function to update the content of the int[] resulting from changes to OpeningDefinition & ClosingDefinition.
/// </summary>
public void RefreshStyle()
{
m_HashCode = TMP_TextParsingUtilities.GetHashCode(m_Name);
int s1 = m_OpeningDefinition.Length;
m_OpeningTagArray = new int[s1];
m_OpeningTagUnicodeArray = new uint[s1];
for (int i = 0; i < s1; i++)
{
m_OpeningTagArray[i] = m_OpeningDefinition[i];
m_OpeningTagUnicodeArray[i] = m_OpeningDefinition[i];
}
int s2 = m_ClosingDefinition.Length;
m_ClosingTagArray = new int[s2];
m_ClosingTagUnicodeArray = new uint[s2];
for (int i = 0; i < s2; i++)
{
m_ClosingTagArray[i] = m_ClosingDefinition[i];
m_ClosingTagUnicodeArray[i] = m_ClosingDefinition[i];
}
}
}
}

View File

@@ -0,0 +1,109 @@
using UnityEngine;
using System;
using System.Collections.Generic;
namespace TMPro
{
[Serializable][ExcludeFromPresetAttribute]
public class TMP_StyleSheet : ScriptableObject
{
/// <summary>
///
/// </summary>
internal List<TMP_Style> styles
{
get { return m_StyleList; }
}
[SerializeField]
private List<TMP_Style> m_StyleList = new List<TMP_Style>(1);
private Dictionary<int, TMP_Style> m_StyleLookupDictionary;
private void Reset()
{
LoadStyleDictionaryInternal();
}
/// <summary>
/// Get the Style for the given hash code value.
/// </summary>
/// <param name="hashCode">Hash code of the style.</param>
/// <returns>The style matching the hash code.</returns>
public TMP_Style GetStyle(int hashCode)
{
if (m_StyleLookupDictionary == null)
LoadStyleDictionaryInternal();
TMP_Style style;
if (m_StyleLookupDictionary.TryGetValue(hashCode, out style))
return style;
return null;
}
/// <summary>
/// Get the Style for the given name.
/// </summary>
/// <param name="name">The name of the style.</param>
/// <returns>The style if found.</returns>
public TMP_Style GetStyle(string name)
{
if (m_StyleLookupDictionary == null)
LoadStyleDictionaryInternal();
int hashCode = TMP_TextParsingUtilities.GetHashCode(name);
TMP_Style style;
if (m_StyleLookupDictionary.TryGetValue(hashCode, out style))
return style;
return null;
}
/// <summary>
/// Function to refresh the Style Dictionary.
/// </summary>
public void RefreshStyles()
{
LoadStyleDictionaryInternal();
}
/// <summary>
///
/// </summary>
private void LoadStyleDictionaryInternal()
{
if (m_StyleLookupDictionary == null)
m_StyleLookupDictionary = new Dictionary<int, TMP_Style>();
else
m_StyleLookupDictionary.Clear();
// Read Styles from style list and store them into dictionary for faster access.
for (int i = 0; i < m_StyleList.Count; i++)
{
m_StyleList[i].RefreshStyle();
if (!m_StyleLookupDictionary.ContainsKey(m_StyleList[i].hashCode))
m_StyleLookupDictionary.Add(m_StyleList[i].hashCode, m_StyleList[i]);
}
// Add Normal Style if it does not already exists
int normalStyleHashCode = TMP_TextParsingUtilities.GetHashCode("Normal");
if (!m_StyleLookupDictionary.ContainsKey(normalStyleHashCode))
{
TMP_Style style = new TMP_Style("Normal", string.Empty, string.Empty);
m_StyleList.Add(style);
m_StyleLookupDictionary.Add(normalStyleHashCode, style);
}
#if UNITY_EDITOR
//// Event to update objects when styles are changed in the editor.
TMPro_EventManager.ON_TEXT_STYLE_PROPERTY_CHANGED(true);
#endif
}
}
}

View File

@@ -0,0 +1,640 @@
using UnityEngine;
using System;
using System.Collections;
using Object = UnityEngine.Object;
#pragma warning disable 0109 // Disable warning due to conflict between Unity Editor DLL and Runtime DLL related to .renderer property being available in one but not the other.
namespace TMPro
{
[RequireComponent(typeof(MeshRenderer))]
[ExecuteAlways]
public class TMP_SubMesh : MonoBehaviour
{
/// <summary>
/// The TMP Font Asset assigned to this sub text object.
/// </summary>
public TMP_FontAsset fontAsset
{
get { return m_fontAsset; }
set { m_fontAsset = value; }
}
[SerializeField]
private TMP_FontAsset m_fontAsset;
/// <summary>
/// The TMP Sprite Asset assigned to this sub text object.
/// </summary>
public TMP_SpriteAsset spriteAsset
{
get { return m_spriteAsset; }
set { m_spriteAsset = value; }
}
[SerializeField]
private TMP_SpriteAsset m_spriteAsset;
/// <summary>
/// The material to be assigned to this object. Returns an instance of the material.
/// </summary>
public Material material
{
// Return a new Instance of the Material if none exists. Otherwise return the current Material Instance.
get { return GetMaterial(m_sharedMaterial); }
// Assign new font material
set
{
if (m_sharedMaterial.GetInstanceID() == value.GetInstanceID())
return;
m_sharedMaterial = m_material = value;
m_padding = GetPaddingForMaterial();
SetVerticesDirty();
SetMaterialDirty();
}
}
[SerializeField]
private Material m_material;
/// <summary>
/// The material to be assigned to this text object.
/// </summary>
public Material sharedMaterial
{
get { return m_sharedMaterial; }
set { SetSharedMaterial(value); }
}
[SerializeField]
private Material m_sharedMaterial;
/// <summary>
/// The fallback material created from the properties of the fallback source material.
/// </summary>
public Material fallbackMaterial
{
get { return m_fallbackMaterial; }
set
{
if (m_fallbackMaterial == value) return;
if (m_fallbackMaterial != null && m_fallbackMaterial != value)
TMP_MaterialManager.ReleaseFallbackMaterial(m_fallbackMaterial);
m_fallbackMaterial = value;
TMP_MaterialManager.AddFallbackMaterialReference(m_fallbackMaterial);
SetSharedMaterial(m_fallbackMaterial);
}
}
private Material m_fallbackMaterial;
/// <summary>
/// The source material used by the fallback font
/// </summary>
public Material fallbackSourceMaterial
{
get { return m_fallbackSourceMaterial; }
set { m_fallbackSourceMaterial = value; }
}
private Material m_fallbackSourceMaterial;
/// <summary>
/// Is the text object using the default font asset material.
/// </summary>
public bool isDefaultMaterial
{
get { return m_isDefaultMaterial; }
set { m_isDefaultMaterial = value; }
}
[SerializeField]
private bool m_isDefaultMaterial;
/// <summary>
/// Padding value resulting for the property settings on the material.
/// </summary>
public float padding
{
get { return m_padding; }
set { m_padding = value; }
}
[SerializeField]
private float m_padding;
/// <summary>
/// The Mesh Renderer of this text sub object.
/// </summary>
public new Renderer renderer
{
get { if (m_renderer == null) m_renderer = GetComponent<Renderer>();
return m_renderer;
}
}
[SerializeField]
private Renderer m_renderer;
/// <summary>
/// The MeshFilter of this text sub object.
/// </summary>
public MeshFilter meshFilter
{
get
{
if (m_meshFilter == null)
{
m_meshFilter = GetComponent<MeshFilter>();
if (m_meshFilter == null)
{
m_meshFilter = gameObject.AddComponent<MeshFilter>();
m_meshFilter.hideFlags = HideFlags.HideInInspector | HideFlags.HideAndDontSave;
}
}
return m_meshFilter;
}
}
private MeshFilter m_meshFilter;
/// <summary>
/// The Mesh of this text sub object.
/// </summary>
public Mesh mesh
{
get
{
if (m_mesh == null)
{
m_mesh = new Mesh();
m_mesh.hideFlags = HideFlags.HideAndDontSave;
}
return m_mesh;
}
set { m_mesh = value; }
}
private Mesh m_mesh;
/// <summary>
///
/// </summary>
//public BoxCollider boxCollider
//{
// get
// {
// if (m_boxCollider == null)
// {
// //
// m_boxCollider = GetComponent<BoxCollider>();
// if (m_boxCollider == null)
// {
// m_boxCollider = gameObject.AddComponent<BoxCollider>();
// gameObject.AddComponent<Rigidbody>();
// }
// }
// return m_boxCollider;
// }
//}
//[SerializeField]
//private BoxCollider m_boxCollider;
/// <summary>
/// Reference to the parent Text Component.
/// </summary>
public TMP_Text textComponent
{
get
{
if (m_TextComponent == null)
m_TextComponent = GetComponentInParent<TextMeshPro>();
return m_TextComponent;
}
}
[SerializeField]
private TextMeshPro m_TextComponent;
[NonSerialized]
private bool m_isRegisteredForEvents;
public static TMP_SubMesh AddSubTextObject(TextMeshPro textComponent, MaterialReference materialReference)
{
GameObject go = new GameObject("TMP SubMesh [" + materialReference.material.name + "]", typeof(TMP_SubMesh));
go.hideFlags = HideFlags.DontSave;
TMP_SubMesh subMesh = go.GetComponent<TMP_SubMesh>();
go.transform.SetParent(textComponent.transform, false);
go.transform.localPosition = Vector3.zero;
go.transform.localRotation = Quaternion.identity;
go.transform.localScale = Vector3.one;
go.layer = textComponent.gameObject.layer;
subMesh.m_TextComponent = textComponent;
subMesh.m_fontAsset = materialReference.fontAsset;
subMesh.m_spriteAsset = materialReference.spriteAsset;
subMesh.m_isDefaultMaterial = materialReference.isDefaultMaterial;
subMesh.SetSharedMaterial(materialReference.material);
subMesh.renderer.sortingLayerID = textComponent.renderer.sortingLayerID;
subMesh.renderer.sortingOrder = textComponent.renderer.sortingOrder;
return subMesh;
}
void OnEnable()
{
//Debug.Log("***** OnEnable() called on object ID " + GetInstanceID() + "]. Parent Text Object ID [" + (textComponent == null ? "" : textComponent.GetInstanceID().ToString()) + "] *****");
// Register Callbacks for various events.
if (!m_isRegisteredForEvents)
{
#if UNITY_EDITOR
TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Add(ON_MATERIAL_PROPERTY_CHANGED);
TMPro_EventManager.FONT_PROPERTY_EVENT.Add(ON_FONT_PROPERTY_CHANGED);
//TMPro_EventManager.TEXTMESHPRO_PROPERTY_EVENT.Add(ON_TEXTMESHPRO_PROPERTY_CHANGED);
TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Add(ON_DRAG_AND_DROP_MATERIAL);
//TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Add(ON_TEXT_STYLE_CHANGED);
TMPro_EventManager.SPRITE_ASSET_PROPERTY_EVENT.Add(ON_SPRITE_ASSET_PROPERTY_CHANGED);
//TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Add(ON_TMP_SETTINGS_CHANGED);
#endif
m_isRegisteredForEvents = true;
}
// Update HideFlags on previously created sub text objects.
if (hideFlags != HideFlags.DontSave)
hideFlags = HideFlags.DontSave;
// Make the geometry visible when the object is enabled.
meshFilter.sharedMesh = mesh;
// Update _ClipRect values
if (m_sharedMaterial != null)
m_sharedMaterial.SetVector(ShaderUtilities.ID_ClipRect, new Vector4(-32767, -32767, 32767, 32767));
}
void OnDisable()
{
//Debug.Log("***** OnDisable() called on Sub Object ID [" + GetInstanceID() + "]. Parent Text Object ID [" + textComponent.GetInstanceID() + "] *****");
// Hide the geometry when the object is disabled.
m_meshFilter.sharedMesh = null;
if (m_fallbackMaterial != null)
{
TMP_MaterialManager.ReleaseFallbackMaterial(m_fallbackMaterial);
m_fallbackMaterial = null;
}
}
void OnDestroy()
{
//Debug.Log("***** OnDestroy() called on Sub Object ID [" + GetInstanceID() + "]. Parent Text Object ID [" + textComponent.GetInstanceID() + "] *****");
// Destroy Mesh
if (m_mesh != null) DestroyImmediate(m_mesh);
if (m_fallbackMaterial != null)
{
TMP_MaterialManager.ReleaseFallbackMaterial(m_fallbackMaterial);
m_fallbackMaterial = null;
}
#if UNITY_EDITOR
// Unregister the event this object was listening to
TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Remove(ON_MATERIAL_PROPERTY_CHANGED);
TMPro_EventManager.FONT_PROPERTY_EVENT.Remove(ON_FONT_PROPERTY_CHANGED);
//TMPro_EventManager.TEXTMESHPRO_PROPERTY_EVENT.Remove(ON_TEXTMESHPRO_PROPERTY_CHANGED);
TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Remove(ON_DRAG_AND_DROP_MATERIAL);
//TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Remove(ON_TEXT_STYLE_CHANGED);
TMPro_EventManager.SPRITE_ASSET_PROPERTY_EVENT.Remove(ON_SPRITE_ASSET_PROPERTY_CHANGED);
//TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Remove(ON_TMP_SETTINGS_CHANGED);
#endif
m_isRegisteredForEvents = false;
// Notify parent text object
if (m_TextComponent != null)
{
m_TextComponent.havePropertiesChanged = true;
m_TextComponent.SetAllDirty();
}
}
#if UNITY_EDITOR
// Event received when custom material editor properties are changed.
void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat)
{
//Debug.Log("*** ON_MATERIAL_PROPERTY_CHANGED ***");
if (m_sharedMaterial == null)
return;
int targetMaterialID = mat.GetInstanceID();
int sharedMaterialID = m_sharedMaterial.GetInstanceID();
int fallbackSourceMaterialID = m_fallbackSourceMaterial == null ? 0 : m_fallbackSourceMaterial.GetInstanceID();
// Sync culling with parent text object
bool hasCullModeProperty = m_sharedMaterial.HasProperty(ShaderUtilities.ShaderTag_CullMode);
float cullMode = 0;
if (hasCullModeProperty)
{
cullMode = textComponent.fontSharedMaterial.GetFloat(ShaderUtilities.ShaderTag_CullMode);
m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_CullMode, cullMode);
}
// Filter events and return if the affected material is not this object's material.
if (targetMaterialID != sharedMaterialID)
{
// Check if event applies to the source fallback material
if (m_fallbackMaterial != null && fallbackSourceMaterialID == targetMaterialID && TMP_Settings.matchMaterialPreset)
{
TMP_MaterialManager.CopyMaterialPresetProperties(mat, m_fallbackMaterial);
// Re-sync culling with parent text object
if (hasCullModeProperty)
m_fallbackMaterial.SetFloat(ShaderUtilities.ShaderTag_CullMode, cullMode);
}
else
return;
}
m_padding = GetPaddingForMaterial();
m_TextComponent.havePropertiesChanged = true;
m_TextComponent.SetVerticesDirty();
}
// Event to Track Material Changed resulting from Drag-n-drop.
void ON_DRAG_AND_DROP_MATERIAL(GameObject obj, Material currentMaterial, Material newMaterial)
{
// Check if event applies to this current object
#if UNITY_2018_2_OR_NEWER
if (obj == gameObject || UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(gameObject) == obj)
#else
if (obj == gameObject || UnityEditor.PrefabUtility.GetPrefabParent(gameObject) == obj)
#endif
{
if (!m_isDefaultMaterial) return;
// Make sure we have a valid reference to the renderer.
if (m_renderer == null) m_renderer = GetComponent<Renderer>();
UnityEditor.Undo.RecordObject(this, "Material Assignment");
UnityEditor.Undo.RecordObject(m_renderer, "Material Assignment");
SetSharedMaterial(newMaterial);
m_TextComponent.havePropertiesChanged = true;
}
}
// Event received when font asset properties are changed in Font Inspector
void ON_SPRITE_ASSET_PROPERTY_CHANGED(bool isChanged, UnityEngine.Object obj)
{
//if (spriteSheet != null && (obj as TMP_SpriteAsset == m_spriteAsset || obj as Texture2D == m_spriteAsset.spriteSheet))
//{
if (m_TextComponent != null)
{
m_TextComponent.havePropertiesChanged = true;
//m_TextComponent.SetVerticesDirty();
}
//}
}
// Event received when font asset properties are changed in Font Inspector
void ON_FONT_PROPERTY_CHANGED(bool isChanged, Object fontAsset)
{
if (m_fontAsset != null && fontAsset.GetInstanceID() == m_fontAsset.GetInstanceID())
{
// Copy Normal and Bold Weight
if (m_fallbackMaterial != null)
{
if (TMP_Settings.matchMaterialPreset)
{
TMP_MaterialManager.ReleaseFallbackMaterial(m_fallbackMaterial);
TMP_MaterialManager.CleanupFallbackMaterials();
}
}
}
}
/// <summary>
/// Event received when the TMP Settings are changed.
/// </summary>
void ON_TMP_SETTINGS_CHANGED()
{
// //Debug.Log("TMP Setting have changed.");
// //SetVerticesDirty();
// SetMaterialDirty();
}
#endif
public void DestroySelf()
{
Destroy(this.gameObject, 1f);
}
// Function called internally when a new material is assigned via the fontMaterial property.
Material GetMaterial(Material mat)
{
// Check in case Object is disabled. If so, we don't have a valid reference to the Renderer.
// This can occur when the Duplicate Material Context menu is used on an inactive object.
if (m_renderer == null)
m_renderer = GetComponent<Renderer>();
// Create Instance Material only if the new material is not the same instance previously used.
if (m_material == null || m_material.GetInstanceID() != mat.GetInstanceID())
m_material = CreateMaterialInstance(mat);
m_sharedMaterial = m_material;
// Compute and Set new padding values for this new material.
m_padding = GetPaddingForMaterial();
SetVerticesDirty();
SetMaterialDirty();
return m_sharedMaterial;
}
/// <summary>
/// Method used to create an instance of the material
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
Material CreateMaterialInstance(Material source)
{
Material mat = new Material(source);
mat.shaderKeywords = source.shaderKeywords;
mat.name += " (Instance)";
return mat;
}
/// <summary>
/// Method returning the shared material assigned to the text object.
/// </summary>
/// <returns></returns>
Material GetSharedMaterial()
{
if (m_renderer == null)
m_renderer = GetComponent<Renderer>();
return m_renderer.sharedMaterial;
}
/// <summary>
/// Method to set the shared material.
/// </summary>
/// <param name="mat"></param>
void SetSharedMaterial(Material mat)
{
//Debug.Log("*** SetSharedMaterial() *** FRAME (" + Time.frameCount + ")");
// Assign new material.
m_sharedMaterial = mat;
// Compute and Set new padding values for this new material.
m_padding = GetPaddingForMaterial();
SetMaterialDirty();
#if UNITY_EDITOR
if (m_sharedMaterial != null)
gameObject.name = "TMP SubMesh [" + m_sharedMaterial.name + "]";
#endif
}
/// <summary>
/// Function called when the padding value for the material needs to be re-calculated.
/// </summary>
/// <returns></returns>
public float GetPaddingForMaterial()
{
float padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_TextComponent.extraPadding, m_TextComponent.isUsingBold);
return padding;
}
/// <summary>
/// Function to update the padding values of the object.
/// </summary>
/// <param name="isExtraPadding"></param>
/// <param name="isBold"></param>
public void UpdateMeshPadding(bool isExtraPadding, bool isUsingBold)
{
m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, isExtraPadding, isUsingBold);
}
/// <summary>
///
/// </summary>
public void SetVerticesDirty()
{
if (!this.enabled)
return;
// This is called on the parent TextMeshPro component.
if (m_TextComponent != null)
{
m_TextComponent.havePropertiesChanged = true;
m_TextComponent.SetVerticesDirty();
}
}
/// <summary>
///
/// </summary>
public void SetMaterialDirty()
{
//if (!this.enabled)
// return;
UpdateMaterial();
//m_materialDirty = true;
//TMP_UpdateRegistry.RegisterCanvasElementForGraphicRebuild((ICanvasElement)this);
}
/// <summary>
///
/// </summary>
protected void UpdateMaterial()
{
//Debug.Log("*** STO - UpdateMaterial() *** FRAME (" + Time.frameCount + ")");
if (renderer == null || m_sharedMaterial == null) return;
m_renderer.sharedMaterial = m_sharedMaterial;
// Special handling to keep the Culling of the material in sync with parent text object
if (m_sharedMaterial.HasProperty(ShaderUtilities.ShaderTag_CullMode))
{
float cullMode = textComponent.fontSharedMaterial.GetFloat(ShaderUtilities.ShaderTag_CullMode);
m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_CullMode, cullMode);
}
#if UNITY_EDITOR
if (m_sharedMaterial != null && gameObject.name != "TMP SubMesh [" + m_sharedMaterial.name + "]")
gameObject.name = "TMP SubMesh [" + m_sharedMaterial.name + "]";
#endif
}
/// <summary>
///
/// </summary>
//public void UpdateColliders(int vertexCount)
//{
// if (this.boxCollider == null) return;
// Vector2 bl = TMP_Math.MAX_16BIT;
// Vector2 tr = TMP_Math.MIN_16BIT;
// // Compute the bounds of the sub text object mesh (excluding the transform position).
// for (int i = 0; i < vertexCount; i++)
// {
// bl.x = Mathf.Min(bl.x, m_mesh.vertices[i].x);
// bl.y = Mathf.Min(bl.y, m_mesh.vertices[i].y);
// tr.x = Mathf.Max(tr.x, m_mesh.vertices[i].x);
// tr.y = Mathf.Max(tr.y, m_mesh.vertices[i].y);
// }
// Vector3 center = (bl + tr) / 2;
// Vector3 size = tr - bl;
// size.z = .1f;
// this.boxCollider.center = center;
// this.boxCollider.size = size;
//}
}
}

View File

@@ -0,0 +1,851 @@
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using Object = UnityEngine.Object;
#pragma warning disable 0414 // Disabled a few warnings related to serialized variables not used in this script but used in the editor.
namespace TMPro
{
[ExecuteAlways]
[RequireComponent(typeof(CanvasRenderer))]
public class TMP_SubMeshUI : MaskableGraphic
{
/// <summary>
/// The TMP Font Asset assigned to this sub text object.
/// </summary>
public TMP_FontAsset fontAsset
{
get { return m_fontAsset; }
set { m_fontAsset = value; }
}
[SerializeField]
private TMP_FontAsset m_fontAsset;
/// <summary>
/// The TMP Sprite Asset assigned to this sub text object.
/// </summary>
public TMP_SpriteAsset spriteAsset
{
get { return m_spriteAsset; }
set { m_spriteAsset = value; }
}
[SerializeField]
private TMP_SpriteAsset m_spriteAsset;
/// <summary>
///
/// </summary>
public override Texture mainTexture
{
get
{
if (this.sharedMaterial != null)
return this.sharedMaterial.GetTexture(ShaderUtilities.ID_MainTex);
return null;
}
}
/// <summary>
/// The material to be assigned to this object. Returns an instance of the material.
/// </summary>
public override Material material
{
// Return a new Instance of the Material if none exists. Otherwise return the current Material Instance.
get { return GetMaterial(m_sharedMaterial); }
// Assign new font material
set
{
if (m_sharedMaterial != null && m_sharedMaterial.GetInstanceID() == value.GetInstanceID())
return;
m_sharedMaterial = m_material = value;
m_padding = GetPaddingForMaterial();
SetVerticesDirty();
SetMaterialDirty();
}
}
[SerializeField]
private Material m_material;
/// <summary>
/// The material to be assigned to this text object.
/// </summary>
public Material sharedMaterial
{
get { return m_sharedMaterial; }
set { SetSharedMaterial(value); }
}
[SerializeField]
private Material m_sharedMaterial;
/// <summary>
///
/// </summary>
public Material fallbackMaterial
{
get { return m_fallbackMaterial; }
set
{
if (m_fallbackMaterial == value) return;
if (m_fallbackMaterial != null && m_fallbackMaterial != value)
TMP_MaterialManager.ReleaseFallbackMaterial(m_fallbackMaterial);
m_fallbackMaterial = value;
TMP_MaterialManager.AddFallbackMaterialReference(m_fallbackMaterial);
SetSharedMaterial(m_fallbackMaterial);
}
}
private Material m_fallbackMaterial;
/// <summary>
/// The source material used by the fallback font
/// </summary>
public Material fallbackSourceMaterial
{
get { return m_fallbackSourceMaterial; }
set { m_fallbackSourceMaterial = value; }
}
private Material m_fallbackSourceMaterial;
/// <summary>
/// Get the material that will be used for rendering.
/// </summary>
public override Material materialForRendering
{
get
{
return TMP_MaterialManager.GetMaterialForRendering(this, m_sharedMaterial);
}
}
/// <summary>
/// Is the text object using the default font asset material.
/// </summary>
public bool isDefaultMaterial
{
get { return m_isDefaultMaterial; }
set { m_isDefaultMaterial = value; }
}
[SerializeField]
private bool m_isDefaultMaterial;
/// <summary>
/// Padding value resulting for the property settings on the material.
/// </summary>
public float padding
{
get { return m_padding; }
set { m_padding = value; }
}
[SerializeField]
private float m_padding;
/// <summary>
/// The Mesh of this text sub object.
/// </summary>
public Mesh mesh
{
get
{
if (m_mesh == null)
{
m_mesh = new Mesh();
m_mesh.hideFlags = HideFlags.HideAndDontSave;
}
return m_mesh;
}
set { m_mesh = value; }
}
private Mesh m_mesh;
/// <summary>
/// Reference to the parent Text Component.
/// </summary>
public TMP_Text textComponent
{
get
{
if (m_TextComponent == null)
m_TextComponent = GetComponentInParent<TextMeshProUGUI>();
return m_TextComponent;
}
}
[SerializeField]
private TextMeshProUGUI m_TextComponent;
[System.NonSerialized]
private bool m_isRegisteredForEvents;
private bool m_materialDirty;
[SerializeField]
private int m_materialReferenceIndex;
/// <summary>
/// Function to add a new sub text object.
/// </summary>
/// <param name="textComponent"></param>
/// <param name="materialReference"></param>
/// <returns></returns>
public static TMP_SubMeshUI AddSubTextObject(TextMeshProUGUI textComponent, MaterialReference materialReference)
{
GameObject go = new GameObject("TMP UI SubObject [" + materialReference.material.name + "]", typeof(RectTransform));
go.hideFlags = HideFlags.DontSave;
go.transform.SetParent(textComponent.transform, false);
go.transform.SetAsFirstSibling();
go.layer = textComponent.gameObject.layer;
RectTransform rectTransform = go.GetComponent<RectTransform>();
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.sizeDelta = Vector2.zero;
rectTransform.pivot = textComponent.rectTransform.pivot;
LayoutElement layoutElement = go.AddComponent<LayoutElement>();
layoutElement.ignoreLayout = true;
TMP_SubMeshUI subMesh = go.AddComponent<TMP_SubMeshUI>();
//subMesh.canvasRenderer = subMesh.canvasRenderer;
subMesh.m_TextComponent = textComponent;
subMesh.m_materialReferenceIndex = materialReference.index;
subMesh.m_fontAsset = materialReference.fontAsset;
subMesh.m_spriteAsset = materialReference.spriteAsset;
subMesh.m_isDefaultMaterial = materialReference.isDefaultMaterial;
subMesh.SetSharedMaterial(materialReference.material);
return subMesh;
}
/// <summary>
///
/// </summary>
protected override void OnEnable()
{
//Debug.Log("*** SubObject OnEnable() ***");
// Register Callbacks for various events.
if (!m_isRegisteredForEvents)
{
#if UNITY_EDITOR
TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Add(ON_MATERIAL_PROPERTY_CHANGED);
TMPro_EventManager.FONT_PROPERTY_EVENT.Add(ON_FONT_PROPERTY_CHANGED);
//TMPro_EventManager.TEXTMESHPRO_PROPERTY_EVENT.Add(ON_TEXTMESHPRO_PROPERTY_CHANGED);
TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Add(ON_DRAG_AND_DROP_MATERIAL);
//TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Add(ON_TEXT_STYLE_CHANGED);
TMPro_EventManager.SPRITE_ASSET_PROPERTY_EVENT.Add(ON_SPRITE_ASSET_PROPERTY_CHANGED);
//TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Add(ON_TMP_SETTINGS_CHANGED);
#endif
m_isRegisteredForEvents = true;
}
// Update HideFlags on previously created sub text objects.
if (hideFlags != HideFlags.DontSave)
hideFlags = HideFlags.DontSave;
m_ShouldRecalculateStencil = true;
RecalculateClipping();
RecalculateMasking();
//SetAllDirty();
}
protected override void OnDisable()
{
//Debug.Log("*** SubObject OnDisable() ***");
base.OnDisable();
// if (m_MaskMaterial != null)
// {
// TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial);
// m_MaskMaterial = null;
// }
if (m_fallbackMaterial != null)
{
TMP_MaterialManager.ReleaseFallbackMaterial(m_fallbackMaterial);
m_fallbackMaterial = null;
}
}
protected override void OnDestroy()
{
//Debug.Log("*** OnDestroy() ***");
// Destroy Mesh
if (m_mesh != null) DestroyImmediate(m_mesh);
if (m_MaskMaterial != null)
TMP_MaterialManager.ReleaseStencilMaterial(m_MaskMaterial);
if (m_fallbackMaterial != null)
{
TMP_MaterialManager.ReleaseFallbackMaterial(m_fallbackMaterial);
m_fallbackMaterial = null;
}
#if UNITY_EDITOR
// Unregister the event this object was listening to
TMPro_EventManager.MATERIAL_PROPERTY_EVENT.Remove(ON_MATERIAL_PROPERTY_CHANGED);
TMPro_EventManager.FONT_PROPERTY_EVENT.Remove(ON_FONT_PROPERTY_CHANGED);
//TMPro_EventManager.TEXTMESHPRO_PROPERTY_EVENT.Remove(ON_TEXTMESHPRO_PROPERTY_CHANGED);
TMPro_EventManager.DRAG_AND_DROP_MATERIAL_EVENT.Remove(ON_DRAG_AND_DROP_MATERIAL);
//TMPro_EventManager.TEXT_STYLE_PROPERTY_EVENT.Remove(ON_TEXT_STYLE_CHANGED);
TMPro_EventManager.SPRITE_ASSET_PROPERTY_EVENT.Remove(ON_SPRITE_ASSET_PROPERTY_CHANGED);
//TMPro_EventManager.TMP_SETTINGS_PROPERTY_EVENT.Remove(ON_TMP_SETTINGS_CHANGED);
#endif
m_isRegisteredForEvents = false;
RecalculateClipping();
// Notify parent text object
if (m_TextComponent != null)
{
m_TextComponent.havePropertiesChanged = true;
m_TextComponent.SetAllDirty();
}
}
#if UNITY_EDITOR
// Event received when custom material editor properties are changed.
void ON_MATERIAL_PROPERTY_CHANGED(bool isChanged, Material mat)
{
//Debug.Log("*** ON_MATERIAL_PROPERTY_CHANGED ***");
if (m_sharedMaterial == null)
return;
int targetMaterialID = mat.GetInstanceID();
int sharedMaterialID = m_sharedMaterial.GetInstanceID();
int maskingMaterialID = m_MaskMaterial == null ? 0 : m_MaskMaterial.GetInstanceID();
int fallbackSourceMaterialID = m_fallbackSourceMaterial == null ? 0 : m_fallbackSourceMaterial.GetInstanceID();
// Sync culling with parent text object
bool hasCullModeProperty = m_sharedMaterial.HasProperty(ShaderUtilities.ShaderTag_CullMode);
float cullMode = 0;
if (hasCullModeProperty)
{
cullMode = textComponent.fontSharedMaterial.GetFloat(ShaderUtilities.ShaderTag_CullMode);
m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_CullMode, cullMode);
}
// Filter events and return if the affected material is not this object's material.
if (m_fallbackMaterial != null && fallbackSourceMaterialID == targetMaterialID && TMP_Settings.matchMaterialPreset)
{
TMP_MaterialManager.CopyMaterialPresetProperties(mat, m_fallbackMaterial);
// Re-sync culling with parent text object
m_fallbackMaterial.SetFloat(ShaderUtilities.ShaderTag_CullMode, cullMode);
}
// Make sure material properties are synchronized between the assigned material and masking material.
if (m_MaskMaterial != null)
{
UnityEditor.Undo.RecordObject(m_MaskMaterial, "Material Property Changes");
UnityEditor.Undo.RecordObject(m_sharedMaterial, "Material Property Changes");
if (targetMaterialID == sharedMaterialID)
{
//Debug.Log("Copy base material properties to masking material if not null.");
float stencilID = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilID);
float stencilComp = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilComp);
m_MaskMaterial.CopyPropertiesFromMaterial(mat);
m_MaskMaterial.shaderKeywords = mat.shaderKeywords;
m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilID, stencilID);
m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilComp, stencilComp);
}
else if (targetMaterialID == maskingMaterialID)
{
// Update the padding
GetPaddingForMaterial(mat);
m_sharedMaterial.CopyPropertiesFromMaterial(mat);
m_sharedMaterial.shaderKeywords = mat.shaderKeywords;
m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilID, 0);
m_sharedMaterial.SetFloat(ShaderUtilities.ID_StencilComp, 8);
}
else if (fallbackSourceMaterialID == targetMaterialID)
{
float stencilID = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilID);
float stencilComp = m_MaskMaterial.GetFloat(ShaderUtilities.ID_StencilComp);
m_MaskMaterial.CopyPropertiesFromMaterial(m_fallbackMaterial);
m_MaskMaterial.shaderKeywords = m_fallbackMaterial.shaderKeywords;
m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilID, stencilID);
m_MaskMaterial.SetFloat(ShaderUtilities.ID_StencilComp, stencilComp);
}
// Re-sync culling with parent text object
if (hasCullModeProperty)
m_MaskMaterial.SetFloat(ShaderUtilities.ShaderTag_CullMode, cullMode);
}
m_padding = GetPaddingForMaterial();
SetVerticesDirty();
m_ShouldRecalculateStencil = true;
RecalculateClipping();
RecalculateMasking();
}
// Event to Track Material Changed resulting from Drag-n-drop.
void ON_DRAG_AND_DROP_MATERIAL(GameObject obj, Material currentMaterial, Material newMaterial)
{
// Check if event applies to this current object
#if UNITY_2018_2_OR_NEWER
if (obj == gameObject || UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(gameObject) == obj)
#else
if (obj == gameObject || UnityEditor.PrefabUtility.GetPrefabParent(gameObject) == obj)
#endif
{
if (!m_isDefaultMaterial) return;
// Make sure we have a valid reference to the renderer.
//if (m_canvasRenderer == null) m_canvasRenderer = GetComponent<CanvasRenderer>();
UnityEditor.Undo.RecordObject(this, "Material Assignment");
UnityEditor.Undo.RecordObject(canvasRenderer, "Material Assignment");
SetSharedMaterial(newMaterial);
m_TextComponent.havePropertiesChanged = true;
}
}
// Event received when font asset properties are changed in Font Inspector
void ON_SPRITE_ASSET_PROPERTY_CHANGED(bool isChanged, UnityEngine.Object obj)
{
//if (spriteSheet != null && (obj as TMP_SpriteAsset == m_spriteAsset || obj as Texture2D == m_spriteAsset.spriteSheet))
//{
if (m_TextComponent != null)
{
m_TextComponent.havePropertiesChanged = true;
//m_TextComponent.SetVerticesDirty();
}
//}
}
// Event received when font asset properties are changed in Font Inspector
void ON_FONT_PROPERTY_CHANGED(bool isChanged, Object fontAsset)
{
if (m_fontAsset != null && fontAsset.GetInstanceID() == m_fontAsset.GetInstanceID())
{
// Copy Normal and Bold Weight
if (m_fallbackMaterial != null)
{
if (TMP_Settings.matchMaterialPreset)
{
TMP_MaterialManager.ReleaseFallbackMaterial(m_fallbackMaterial);
TMP_MaterialManager.CleanupFallbackMaterials();
}
}
}
}
/// <summary>
/// Event received when the TMP Settings are changed.
/// </summary>
void ON_TMP_SETTINGS_CHANGED()
{
//Debug.Log("TMP Setting have changed.");
//SetVerticesDirty();
//SetMaterialDirty();
}
#endif
/// <summary>
///
/// </summary>
protected override void OnTransformParentChanged()
{
if (!this.IsActive())
return;
m_ShouldRecalculateStencil = true;
RecalculateClipping();
RecalculateMasking();
}
/// <summary>
/// Function returning the modified material for masking if necessary.
/// </summary>
/// <param name="baseMaterial"></param>
/// <returns></returns>
public override Material GetModifiedMaterial(Material baseMaterial)
{
Material mat = baseMaterial;
if (m_ShouldRecalculateStencil)
{
var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform);
m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0;
m_ShouldRecalculateStencil = false;
}
if (m_StencilValue > 0)
{
var maskMat = StencilMaterial.Add(mat, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0);
StencilMaterial.Remove(m_MaskMaterial);
m_MaskMaterial = maskMat;
mat = m_MaskMaterial;
}
return mat;
}
/// <summary>
/// Function called when the padding value for the material needs to be re-calculated.
/// </summary>
/// <returns></returns>
public float GetPaddingForMaterial()
{
float padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_TextComponent.extraPadding, m_TextComponent.isUsingBold);
return padding;
}
/// <summary>
/// Function called when the padding value for the material needs to be re-calculated.
/// </summary>
/// <returns></returns>
public float GetPaddingForMaterial(Material mat)
{
float padding = ShaderUtilities.GetPadding(mat, m_TextComponent.extraPadding, m_TextComponent.isUsingBold);
return padding;
}
/// <summary>
///
/// </summary>
/// <param name="isExtraPadding"></param>
/// <param name="isBold"></param>
public void UpdateMeshPadding(bool isExtraPadding, bool isUsingBold)
{
m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, isExtraPadding, isUsingBold);
}
/// <summary>
///
/// </summary>
public override void SetAllDirty()
{
//SetLayoutDirty();
//SetVerticesDirty();
//SetMaterialDirty();
}
/// <summary>
///
/// </summary>
public override void SetVerticesDirty()
{
if (!this.IsActive())
return;
// This is called on the parent TextMeshPro component.
if (m_TextComponent != null)
{
m_TextComponent.havePropertiesChanged = true;
m_TextComponent.SetVerticesDirty();
}
}
/// <summary>
///
/// </summary>
public override void SetLayoutDirty()
{
}
/// <summary>
///
/// </summary>
public override void SetMaterialDirty()
{
//Debug.Log("*** STO-UI - SetMaterialDirty() *** FRAME (" + Time.frameCount + ")");
//if (!this.IsActive())
// return;
m_materialDirty = true;
UpdateMaterial();
if (m_OnDirtyMaterialCallback != null)
m_OnDirtyMaterialCallback();
//TMP_ITextElementUpdateManager.RegisterTextElementForGraphicRebuild(this);
//TMP_UpdateRegistry.RegisterCanvasElementForGraphicRebuild((ICanvasElement)this);
//m_TextComponent.SetMaterialDirty();
}
/// <summary>
///
/// </summary>
public void SetPivotDirty()
{
if (!this.IsActive())
return;
this.rectTransform.pivot = m_TextComponent.rectTransform.pivot;
}
Transform GetRootCanvasTransform()
{
if (m_RootCanvasTransform == null)
m_RootCanvasTransform = m_TextComponent.canvas.rootCanvas.transform;
return m_RootCanvasTransform;
}
private Transform m_RootCanvasTransform;
/// <summary>
/// Override Cull function as this is handled by the parent text object.
/// </summary>
/// <param name="clipRect"></param>
/// <param name="validRect"></param>
public override void Cull(Rect clipRect, bool validRect)
{
// Do nothing as this functionality is handled by the parent text object.
}
/// <summary>
///
/// </summary>
protected override void UpdateGeometry()
{
// Need to override to prevent Unity from changing the geometry of the object.
//Debug.Log("UpdateGeometry()");
}
/// <summary>
///
/// </summary>
/// <param name="update"></param>
public override void Rebuild(CanvasUpdate update)
{
if (update == CanvasUpdate.PreRender)
{
if (!m_materialDirty) return;
UpdateMaterial();
m_materialDirty = false;
}
}
/// <summary>
/// Function to update the material from the parent text object.
/// </summary>
public void RefreshMaterial()
{
UpdateMaterial();
}
/// <summary>
///
/// </summary>
protected override void UpdateMaterial()
{
//Debug.Log("*** STO-UI - UpdateMaterial() *** FRAME (" + Time.frameCount + ")");
if (m_sharedMaterial == null)
return;
//if (canvasRenderer == null) m_canvasRenderer = this.canvasRenderer;
// Special handling to keep the Culling of the material in sync with parent text object
if (m_sharedMaterial.HasProperty(ShaderUtilities.ShaderTag_CullMode))
{
float cullMode = textComponent.fontSharedMaterial.GetFloat(ShaderUtilities.ShaderTag_CullMode);
m_sharedMaterial.SetFloat(ShaderUtilities.ShaderTag_CullMode, cullMode);
}
canvasRenderer.materialCount = 1;
canvasRenderer.SetMaterial(materialForRendering, 0);
//canvasRenderer.SetTexture(materialForRendering.mainTexture);
#if UNITY_EDITOR
if (m_sharedMaterial != null && gameObject.name != "TMP SubMeshUI [" + m_sharedMaterial.name + "]")
gameObject.name = "TMP SubMeshUI [" + m_sharedMaterial.name + "]";
#endif
}
// IClippable implementation
/// <summary>
/// Method called when the state of a parent changes.
/// </summary>
public override void RecalculateClipping()
{
//Debug.Log("*** RecalculateClipping() ***");
base.RecalculateClipping();
}
/// <summary>
///
/// </summary>
// public override void RecalculateMasking()
// {
// //Debug.Log("RecalculateMasking()");
//
// this.m_ShouldRecalculateStencil = true;
// SetMaterialDirty();
// }
/// <summary>
/// Method which returns an instance of the shared material
/// </summary>
/// <returns></returns>
Material GetMaterial()
{
// Make sure we have a valid reference to the renderer.
//if (m_renderer == null) m_renderer = GetComponent<Renderer>();
//if (m_material == null || m_isNewSharedMaterial)
//{
// m_renderer.material = m_sharedMaterial;
// m_material = m_renderer.material;
// m_sharedMaterial = m_material;
// m_padding = ShaderUtilities.GetPadding(m_sharedMaterial, m_TextMeshPro.extraPadding, false);
// m_isNewSharedMaterial = false;
//}
return m_sharedMaterial;
}
// Function called internally when a new material is assigned via the fontMaterial property.
Material GetMaterial(Material mat)
{
// Check in case Object is disabled. If so, we don't have a valid reference to the Renderer.
// This can occur when the Duplicate Material Context menu is used on an inactive object.
//if (m_renderer == null)
// m_renderer = GetComponent<Renderer>();
// Create Instance Material only if the new material is not the same instance previously used.
if (m_material == null || m_material.GetInstanceID() != mat.GetInstanceID())
m_material = CreateMaterialInstance(mat);
m_sharedMaterial = m_material;
// Compute and Set new padding values for this new material.
m_padding = GetPaddingForMaterial();
SetVerticesDirty();
SetMaterialDirty();
return m_sharedMaterial;
}
/// <summary>
/// Method used to create an instance of the material
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
Material CreateMaterialInstance(Material source)
{
Material mat = new Material(source);
mat.shaderKeywords = source.shaderKeywords;
mat.name += " (Instance)";
return mat;
}
/// <summary>
/// Method returning the shared material assigned to the text object.
/// </summary>
/// <returns></returns>
Material GetSharedMaterial()
{
//if (canvasRenderer == null)
// canvasRenderer = GetComponent<CanvasRenderer>();
return canvasRenderer.GetMaterial();
}
/// <summary>
/// Method to set the shared material.
/// </summary>
/// <param name="mat"></param>
void SetSharedMaterial(Material mat)
{
//Debug.Log("*** SetSharedMaterial UI() *** FRAME (" + Time.frameCount + ")");
// Assign new material.
m_sharedMaterial = mat;
m_Material = m_sharedMaterial;
//m_isDefaultMaterial = false;
//if (mat.GetInstanceID() == m_fontAsset.material.GetInstanceID())
// m_isDefaultMaterial = true;
// Compute and Set new padding values for this new material.
m_padding = GetPaddingForMaterial();
//SetVerticesDirty();
SetMaterialDirty();
#if UNITY_EDITOR
//if (m_sharedMaterial != null)
// gameObject.name = "TMP SubMesh [" + m_sharedMaterial.name + "]";
#endif
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,69 @@
using System;
using UnityEngine;
using UnityEngine.TextCore;
namespace TMPro
{
public enum TextElementType : byte
{
Character = 0x1,
Sprite = 0x2,
}
/// <summary>
/// Base class for all text elements like Character and SpriteCharacter.
/// </summary>
[Serializable]
public class TMP_TextElement
{
/// <summary>
/// The type of text element which can be a character or sprite.
/// </summary>
public TextElementType elementType { get { return m_ElementType; } }
/// <summary>
/// The unicode value (code point) of the character.
/// </summary>
public uint unicode { get { return m_Unicode; } set { m_Unicode = value; } }
/// <summary>
/// The Text Asset to which this Text Element belongs.
/// </summary>
public TMP_Asset textAsset { get { return m_TextAsset; } set { m_TextAsset = value; } }
/// <summary>
/// The glyph used by this text element.
/// </summary>
public Glyph glyph { get { return m_Glyph; } set { m_Glyph = value; } }
/// <summary>
/// The index of the glyph used by this text element.
/// </summary>
public uint glyphIndex { get { return m_GlyphIndex; } set { m_GlyphIndex = value; } }
/// <summary>
/// The relative scale of the character.
/// </summary>
public float scale { get { return m_Scale; } set { m_Scale = value; } }
// =============================================
// Private backing fields for public properties.
// =============================================
[SerializeField]
protected TextElementType m_ElementType;
[SerializeField]
internal uint m_Unicode;
internal TMP_Asset m_TextAsset;
internal Glyph m_Glyph;
[SerializeField]
internal uint m_GlyphIndex;
[SerializeField]
internal float m_Scale;
}
}

View File

@@ -0,0 +1,25 @@
using UnityEngine;
using System;
using System.Collections;
namespace TMPro
{
/// <summary>
/// Base class for all text elements like characters (glyphs) and sprites.
/// </summary>
[Serializable]
public class TMP_TextElement_Legacy
{
public int id;
public float x;
public float y;
public float width;
public float height;
public float xOffset;
public float yOffset;
public float xAdvance;
public float scale;
}
}

View File

@@ -0,0 +1,312 @@
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
namespace TMPro
{
/// <summary>
/// Class which contains information about every element contained within the text object.
/// </summary>
[Serializable]
public class TMP_TextInfo
{
internal static Vector2 k_InfinityVectorPositive = new Vector2(32767, 32767);
internal static Vector2 k_InfinityVectorNegative = new Vector2(-32767, -32767);
public TMP_Text textComponent;
public int characterCount;
public int spriteCount;
public int spaceCount;
public int wordCount;
public int linkCount;
public int lineCount;
public int pageCount;
public int materialCount;
public TMP_CharacterInfo[] characterInfo;
public TMP_WordInfo[] wordInfo;
public TMP_LinkInfo[] linkInfo;
public TMP_LineInfo[] lineInfo;
public TMP_PageInfo[] pageInfo;
public TMP_MeshInfo[] meshInfo;
private TMP_MeshInfo[] m_CachedMeshInfo;
// Default Constructor
public TMP_TextInfo()
{
characterInfo = new TMP_CharacterInfo[8];
wordInfo = new TMP_WordInfo[16];
linkInfo = new TMP_LinkInfo[0];
lineInfo = new TMP_LineInfo[2];
pageInfo = new TMP_PageInfo[4];
meshInfo = new TMP_MeshInfo[1];
}
internal TMP_TextInfo(int characterCount)
{
characterInfo = new TMP_CharacterInfo[characterCount];
wordInfo = new TMP_WordInfo[16];
linkInfo = new TMP_LinkInfo[0];
lineInfo = new TMP_LineInfo[2];
pageInfo = new TMP_PageInfo[4];
meshInfo = new TMP_MeshInfo[1];
}
public TMP_TextInfo(TMP_Text textComponent)
{
this.textComponent = textComponent;
characterInfo = new TMP_CharacterInfo[8];
wordInfo = new TMP_WordInfo[4];
linkInfo = new TMP_LinkInfo[0];
lineInfo = new TMP_LineInfo[2];
pageInfo = new TMP_PageInfo[4];
meshInfo = new TMP_MeshInfo[1];
meshInfo[0].mesh = textComponent.mesh;
materialCount = 1;
}
/// <summary>
/// Function to clear the counters of the text object.
/// </summary>
public void Clear()
{
characterCount = 0;
spaceCount = 0;
wordCount = 0;
linkCount = 0;
lineCount = 0;
pageCount = 0;
spriteCount = 0;
for (int i = 0; i < this.meshInfo.Length; i++)
{
this.meshInfo[i].vertexCount = 0;
}
}
/// <summary>
///
/// </summary>
internal void ClearAllData()
{
characterCount = 0;
spaceCount = 0;
wordCount = 0;
linkCount = 0;
lineCount = 0;
pageCount = 0;
spriteCount = 0;
this.characterInfo = new TMP_CharacterInfo[4];
this.wordInfo = new TMP_WordInfo[1];
this.lineInfo = new TMP_LineInfo[1];
this.pageInfo = new TMP_PageInfo[1];
this.linkInfo = new TMP_LinkInfo[0];
materialCount = 0;
this.meshInfo = new TMP_MeshInfo[1];
}
/// <summary>
/// Function to clear the content of the MeshInfo array while preserving the Triangles, Normals and Tangents.
/// </summary>
public void ClearMeshInfo(bool updateMesh)
{
for (int i = 0; i < this.meshInfo.Length; i++)
this.meshInfo[i].Clear(updateMesh);
}
/// <summary>
/// Function to clear the content of all the MeshInfo arrays while preserving their Triangles, Normals and Tangents.
/// </summary>
public void ClearAllMeshInfo()
{
for (int i = 0; i < this.meshInfo.Length; i++)
this.meshInfo[i].Clear(true);
}
/// <summary>
///
/// </summary>
public void ResetVertexLayout(bool isVolumetric)
{
for (int i = 0; i < this.meshInfo.Length; i++)
this.meshInfo[i].ResizeMeshInfo(0, isVolumetric);
}
/// <summary>
/// Function used to mark unused vertices as degenerate.
/// </summary>
/// <param name="materials"></param>
public void ClearUnusedVertices(MaterialReference[] materials)
{
for (int i = 0; i < meshInfo.Length; i++)
{
int start = 0; // materials[i].referenceCount * 4;
meshInfo[i].ClearUnusedVertices(start);
}
}
/// <summary>
/// Function to clear and initialize the lineInfo array.
/// </summary>
public void ClearLineInfo()
{
if (this.lineInfo == null)
this.lineInfo = new TMP_LineInfo[2];
int length = this.lineInfo.Length;
for (int i = 0; i < length; i++)
{
this.lineInfo[i].characterCount = 0;
this.lineInfo[i].spaceCount = 0;
this.lineInfo[i].wordCount = 0;
this.lineInfo[i].controlCharacterCount = 0;
this.lineInfo[i].width = 0;
this.lineInfo[i].ascender = k_InfinityVectorNegative.x;
this.lineInfo[i].descender = k_InfinityVectorPositive.x;
this.lineInfo[i].marginLeft = 0;
this.lineInfo[i].marginRight = 0;
this.lineInfo[i].lineExtents.min = k_InfinityVectorPositive;
this.lineInfo[i].lineExtents.max = k_InfinityVectorNegative;
this.lineInfo[i].maxAdvance = 0;
//this.lineInfo[i].maxScale = 0;
}
}
internal void ClearPageInfo()
{
if (this.pageInfo == null)
this.pageInfo = new TMP_PageInfo[2];
int length = this.pageInfo.Length;
for (int i = 0; i < length; i++)
{
this.pageInfo[i].firstCharacterIndex = 0;
this.pageInfo[i].lastCharacterIndex = 0;
this.pageInfo[i].ascender = -32767;
this.pageInfo[i].baseLine = 0;
this.pageInfo[i].descender = 32767;
}
}
/// <summary>
/// Function to copy the MeshInfo Arrays and their primary vertex data content.
/// </summary>
/// <returns>A copy of the MeshInfo[]</returns>
public TMP_MeshInfo[] CopyMeshInfoVertexData()
{
if (m_CachedMeshInfo == null || m_CachedMeshInfo.Length != meshInfo.Length)
{
m_CachedMeshInfo = new TMP_MeshInfo[meshInfo.Length];
// Initialize all the vertex data arrays
for (int i = 0; i < m_CachedMeshInfo.Length; i++)
{
int length = meshInfo[i].vertices.Length;
m_CachedMeshInfo[i].vertices = new Vector3[length];
m_CachedMeshInfo[i].uvs0 = new Vector2[length];
m_CachedMeshInfo[i].uvs2 = new Vector2[length];
m_CachedMeshInfo[i].colors32 = new Color32[length];
//m_CachedMeshInfo[i].normals = new Vector3[length];
//m_CachedMeshInfo[i].tangents = new Vector4[length];
//m_CachedMeshInfo[i].triangles = new int[meshInfo[i].triangles.Length];
}
}
for (int i = 0; i < m_CachedMeshInfo.Length; i++)
{
int length = meshInfo[i].vertices.Length;
if (m_CachedMeshInfo[i].vertices.Length != length)
{
m_CachedMeshInfo[i].vertices = new Vector3[length];
m_CachedMeshInfo[i].uvs0 = new Vector2[length];
m_CachedMeshInfo[i].uvs2 = new Vector2[length];
m_CachedMeshInfo[i].colors32 = new Color32[length];
//m_CachedMeshInfo[i].normals = new Vector3[length];
//m_CachedMeshInfo[i].tangents = new Vector4[length];
//m_CachedMeshInfo[i].triangles = new int[meshInfo[i].triangles.Length];
}
// Only copy the primary vertex data
Array.Copy(meshInfo[i].vertices, m_CachedMeshInfo[i].vertices, length);
Array.Copy(meshInfo[i].uvs0, m_CachedMeshInfo[i].uvs0, length);
Array.Copy(meshInfo[i].uvs2, m_CachedMeshInfo[i].uvs2, length);
Array.Copy(meshInfo[i].colors32, m_CachedMeshInfo[i].colors32, length);
//Array.Copy(meshInfo[i].normals, m_CachedMeshInfo[i].normals, length);
//Array.Copy(meshInfo[i].tangents, m_CachedMeshInfo[i].tangents, length);
//Array.Copy(meshInfo[i].triangles, m_CachedMeshInfo[i].triangles, meshInfo[i].triangles.Length);
}
return m_CachedMeshInfo;
}
/// <summary>
/// Function to resize any of the structure contained in the TMP_TextInfo class.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
/// <param name="size"></param>
public static void Resize<T> (ref T[] array, int size)
{
// Allocated to the next power of two
int newSize = size > 1024 ? size + 256 : Mathf.NextPowerOfTwo(size);
Array.Resize(ref array, newSize);
}
/// <summary>
/// Function to resize any of the structure contained in the TMP_TextInfo class.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array"></param>
/// <param name="size"></param>
/// <param name="isFixedSize"></param>
public static void Resize<T>(ref T[] array, int size, bool isBlockAllocated)
{
if (isBlockAllocated) size = size > 1024 ? size + 256 : Mathf.NextPowerOfTwo(size);
if (size == array.Length) return;
//Debug.Log("Resizing TextInfo from [" + array.Length + "] to [" + size + "]");
Array.Resize(ref array, size);
}
}
}

View File

@@ -0,0 +1,146 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace TMPro
{
public class TMP_TextParsingUtilities
{
private static readonly TMP_TextParsingUtilities s_Instance = new TMP_TextParsingUtilities();
/// <summary>
/// Default constructor
/// </summary>
static TMP_TextParsingUtilities() { }
/// <summary>
/// Get a singleton instance of the TextModuleUtilities.
/// </summary>
public static TMP_TextParsingUtilities instance
{
get { return s_Instance; }
}
/// <summary>
/// Function returning the hashcode value of a given string.
/// </summary>
public static int GetHashCode(string s)
{
int hashCode = 0;
for (int i = 0; i < s.Length; i++)
hashCode = ((hashCode << 5) + hashCode) ^ ToUpperASCIIFast(s[i]);
return hashCode;
}
public static int GetHashCodeCaseSensitive(string s)
{
int hashCode = 0;
for (int i = 0; i < s.Length; i++)
hashCode = ((hashCode << 5) + hashCode) ^ s[i];
return hashCode;
}
/// <summary>
/// Table used to convert character to lowercase.
/// </summary>
const string k_LookupStringL = "-------------------------------- !-#$%&-()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[-]^_`abcdefghijklmnopqrstuvwxyz{|}~-";
/// <summary>
/// Table used to convert character to uppercase.
/// </summary>
const string k_LookupStringU = "-------------------------------- !-#$%&-()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[-]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~-";
/// <summary>
/// Get lowercase version of this ASCII character.
/// </summary>
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char ToLowerASCIIFast(char c)
{
if (c > k_LookupStringL.Length - 1)
return c;
return k_LookupStringL[c];
}
/// <summary>
/// Get uppercase version of this ASCII character.
/// </summary>
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static char ToUpperASCIIFast(char c)
{
if (c > k_LookupStringU.Length - 1)
return c;
return k_LookupStringU[c];
}
/// <summary>
/// Get uppercase version of this ASCII character.
/// </summary>
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ToUpperASCIIFast(uint c)
{
if (c > k_LookupStringU.Length - 1)
return c;
return k_LookupStringU[(int)c];
}
/// <summary>
/// Get lowercase version of this ASCII character.
/// </summary>
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ToLowerASCIIFast(uint c)
{
if (c > k_LookupStringL.Length - 1)
return c;
return k_LookupStringL[(int)c];
}
/// <summary>
/// Check if Unicode is High Surrogate
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsHighSurrogate(uint c)
{
return c > 0xD800 && c < 0xDBFF;
}
/// <summary>
/// Check if Unicode is Low Surrogate
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsLowSurrogate(uint c)
{
return c > 0xDC00 && c < 0xDFFF;
}
/// <summary>
///
/// </summary>
/// <param name="highSurrogate"></param>
/// <param name="lowSurrogate"></param>
/// <returns></returns>
internal static uint ConvertToUTF32(uint highSurrogate, uint lowSurrogate)
{
return ((highSurrogate - CodePoint.HIGH_SURROGATE_START) * 0x400) + ((lowSurrogate - CodePoint.LOW_SURROGATE_START) + CodePoint.UNICODE_PLANE01_START);
}
}
}

View File

@@ -0,0 +1,400 @@
using System;
using System.Diagnostics;
using UnityEngine;
namespace TMPro
{
/// <summary>
/// Structure used to track basic XML tags which are binary (on / off)
/// </summary>
public struct TMP_FontStyleStack
{
public byte bold;
public byte italic;
public byte underline;
public byte strikethrough;
public byte highlight;
public byte superscript;
public byte subscript;
public byte uppercase;
public byte lowercase;
public byte smallcaps;
/// <summary>
/// Clear the basic XML tag stack.
/// </summary>
public void Clear()
{
bold = 0;
italic = 0;
underline = 0;
strikethrough = 0;
highlight = 0;
superscript = 0;
subscript = 0;
uppercase = 0;
lowercase = 0;
smallcaps = 0;
}
public byte Add(FontStyles style)
{
switch (style)
{
case FontStyles.Bold:
bold++;
return bold;
case FontStyles.Italic:
italic++;
return italic;
case FontStyles.Underline:
underline++;
return underline;
case FontStyles.UpperCase:
uppercase++;
return uppercase;
case FontStyles.LowerCase:
lowercase++;
return lowercase;
case FontStyles.Strikethrough:
strikethrough++;
return strikethrough;
case FontStyles.Superscript:
superscript++;
return superscript;
case FontStyles.Subscript:
subscript++;
return subscript;
case FontStyles.Highlight:
highlight++;
return highlight;
}
return 0;
}
public byte Remove(FontStyles style)
{
switch (style)
{
case FontStyles.Bold:
if (bold > 1)
bold--;
else
bold = 0;
return bold;
case FontStyles.Italic:
if (italic > 1)
italic--;
else
italic = 0;
return italic;
case FontStyles.Underline:
if (underline > 1)
underline--;
else
underline = 0;
return underline;
case FontStyles.UpperCase:
if (uppercase > 1)
uppercase--;
else
uppercase = 0;
return uppercase;
case FontStyles.LowerCase:
if (lowercase > 1)
lowercase--;
else
lowercase = 0;
return lowercase;
case FontStyles.Strikethrough:
if (strikethrough > 1)
strikethrough--;
else
strikethrough = 0;
return strikethrough;
case FontStyles.Highlight:
if (highlight > 1)
highlight--;
else
highlight = 0;
return highlight;
case FontStyles.Superscript:
if (superscript > 1)
superscript--;
else
superscript = 0;
return superscript;
case FontStyles.Subscript:
if (subscript > 1)
subscript--;
else
subscript = 0;
return subscript;
}
return 0;
}
}
/// <summary>
/// Structure used to track XML tags of various types.
/// </summary>
/// <typeparam name="T"></typeparam>
[DebuggerDisplay("Item count = {m_Count}")]
public struct TMP_TextProcessingStack<T>
{
public T[] itemStack;
public int index;
T m_DefaultItem;
int m_Capacity;
int m_RolloverSize;
int m_Count;
const int k_DefaultCapacity = 4;
/// <summary>
/// Constructor to create a new item stack.
/// </summary>
/// <param name="stack"></param>
public TMP_TextProcessingStack(T[] stack)
{
itemStack = stack;
m_Capacity = stack.Length;
index = 0;
m_RolloverSize = 0;
m_DefaultItem = default(T);
m_Count = 0;
}
/// <summary>
/// Constructor for a new item stack with the given capacity.
/// </summary>
/// <param name="capacity"></param>
public TMP_TextProcessingStack(int capacity)
{
itemStack = new T[capacity];
m_Capacity = capacity;
index = 0;
m_RolloverSize = 0;
m_DefaultItem = default(T);
m_Count = 0;
}
public TMP_TextProcessingStack(int capacity, int rolloverSize)
{
itemStack = new T[capacity];
m_Capacity = capacity;
index = 0;
m_RolloverSize = rolloverSize;
m_DefaultItem = default(T);
m_Count = 0;
}
/// <summary>
///
/// </summary>
public int Count
{
get { return m_Count; }
}
/// <summary>
/// Returns the current item on the stack.
/// </summary>
public T current
{
get
{
if (index > 0)
return itemStack[index - 1];
return itemStack[0];
}
}
/// <summary>
///
/// </summary>
public int rolloverSize
{
get { return m_RolloverSize; }
set
{
m_RolloverSize = value;
//if (m_Capacity < m_RolloverSize)
// Array.Resize(ref itemStack, m_RolloverSize);
}
}
/// <summary>
/// Set stack elements to default item.
/// </summary>
/// <param name="stack">The stack of elements.</param>
/// <param name="item"></param>
internal static void SetDefault(TMP_TextProcessingStack<T>[] stack, T item)
{
for (int i = 0; i < stack.Length; i++)
stack[i].SetDefault(item);
}
/// <summary>
/// Function to clear and reset stack to first item.
/// </summary>
public void Clear()
{
index = 0;
m_Count = 0;
}
/// <summary>
/// Function to set the first item on the stack and reset index.
/// </summary>
/// <param name="item"></param>
public void SetDefault(T item)
{
if (itemStack == null)
{
m_Capacity = k_DefaultCapacity;
itemStack = new T[m_Capacity];
m_DefaultItem = default(T);
}
itemStack[0] = item;
index = 1;
}
/// <summary>
/// Function to add a new item to the stack.
/// </summary>
/// <param name="item"></param>
public void Add(T item)
{
if (index < itemStack.Length)
{
itemStack[index] = item;
index += 1;
}
}
/// <summary>
/// Function to retrieve an item from the stack.
/// </summary>
/// <returns></returns>
public T Remove()
{
index -= 1;
if (index <= 0)
{
index = 1;
return itemStack[0];
}
return itemStack[index - 1];
}
public void Push(T item)
{
if (index == m_Capacity)
{
m_Capacity *= 2;
if (m_Capacity == 0)
m_Capacity = k_DefaultCapacity;
Array.Resize(ref itemStack, m_Capacity);
}
itemStack[index] = item;
if (m_RolloverSize == 0)
{
index += 1;
m_Count += 1;
}
else
{
index = (index + 1) % m_RolloverSize;
m_Count = m_Count < m_RolloverSize ? m_Count + 1 : m_RolloverSize;
}
}
public T Pop()
{
if (index == 0 && m_RolloverSize == 0)
return default(T);
if (m_RolloverSize == 0)
index -= 1;
else
{
index = (index - 1) % m_RolloverSize;
index = index < 0 ? index + m_RolloverSize : index;
}
T item = itemStack[index];
itemStack[index] = m_DefaultItem;
m_Count = m_Count > 0 ? m_Count - 1 : 0;
return item;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public T Peek()
{
if (index == 0)
return m_DefaultItem;
return itemStack[index - 1];
}
/// <summary>
/// Function to retrieve the current item from the stack.
/// </summary>
/// <returns>itemStack <T></returns>
public T CurrentItem()
{
if (index > 0)
return itemStack[index - 1];
return itemStack[0];
}
/// <summary>
/// Function to retrieve the previous item without affecting the stack.
/// </summary>
/// <returns></returns>
public T PreviousItem()
{
if (index > 1)
return itemStack[index - 2];
return itemStack[0];
}
}
}

View File

@@ -0,0 +1,249 @@
using UnityEngine;
using Unity.Profiling;
using UnityEngine.UI;
using System.Collections.Generic;
namespace TMPro
{
public class TMP_UpdateManager
{
private static TMP_UpdateManager s_Instance;
private readonly HashSet<int> m_LayoutQueueLookup = new HashSet<int>();
private readonly List<TMP_Text> m_LayoutRebuildQueue = new List<TMP_Text>();
private readonly HashSet<int> m_GraphicQueueLookup = new HashSet<int>();
private readonly List<TMP_Text> m_GraphicRebuildQueue = new List<TMP_Text>();
private readonly HashSet<int> m_InternalUpdateLookup = new HashSet<int>();
private readonly List<TMP_Text> m_InternalUpdateQueue = new List<TMP_Text>();
private readonly HashSet<int> m_CullingUpdateLookup = new HashSet<int>();
private readonly List<TMP_Text> m_CullingUpdateQueue = new List<TMP_Text>();
// Profiler Marker declarations
private static ProfilerMarker k_RegisterTextObjectForUpdateMarker = new ProfilerMarker("TMP.RegisterTextObjectForUpdate");
private static ProfilerMarker k_RegisterTextElementForGraphicRebuildMarker = new ProfilerMarker("TMP.RegisterTextElementForGraphicRebuild");
private static ProfilerMarker k_RegisterTextElementForCullingUpdateMarker = new ProfilerMarker("TMP.RegisterTextElementForCullingUpdate");
private static ProfilerMarker k_UnregisterTextObjectForUpdateMarker = new ProfilerMarker("TMP.UnregisterTextObjectForUpdate");
private static ProfilerMarker k_UnregisterTextElementForGraphicRebuildMarker = new ProfilerMarker("TMP.UnregisterTextElementForGraphicRebuild");
/// <summary>
/// Get a singleton instance of the registry
/// </summary>
static TMP_UpdateManager instance
{
get
{
if (s_Instance == null)
s_Instance = new TMP_UpdateManager();
return s_Instance;
}
}
/// <summary>
/// Register to receive rendering callbacks.
/// </summary>
TMP_UpdateManager()
{
Canvas.willRenderCanvases += DoRebuilds;
}
/// <summary>
/// Function used as a replacement for LateUpdate() to handle SDF Scale updates and Legacy Animation updates.
/// </summary>
/// <param name="textObject"></param>
internal static void RegisterTextObjectForUpdate(TMP_Text textObject)
{
k_RegisterTextObjectForUpdateMarker.Begin();
instance.InternalRegisterTextObjectForUpdate(textObject);
k_RegisterTextObjectForUpdateMarker.End();
}
private void InternalRegisterTextObjectForUpdate(TMP_Text textObject)
{
int id = textObject.GetInstanceID();
if (m_InternalUpdateLookup.Contains(id))
return;
m_InternalUpdateLookup.Add(id);
m_InternalUpdateQueue.Add(textObject);
}
/// <summary>
/// Function to register elements which require a layout rebuild.
/// </summary>
/// <param name="element"></param>
public static void RegisterTextElementForLayoutRebuild(TMP_Text element)
{
instance.InternalRegisterTextElementForLayoutRebuild(element);
}
private void InternalRegisterTextElementForLayoutRebuild(TMP_Text element)
{
int id = element.GetInstanceID();
if (m_LayoutQueueLookup.Contains(id))
return;
m_LayoutQueueLookup.Add(id);
m_LayoutRebuildQueue.Add(element);
}
/// <summary>
/// Function to register elements which require a layout rebuild.
/// </summary>
/// <param name="element"></param>
public static void RegisterTextElementForGraphicRebuild(TMP_Text element)
{
k_RegisterTextElementForGraphicRebuildMarker.Begin();
instance.InternalRegisterTextElementForGraphicRebuild(element);
k_RegisterTextElementForGraphicRebuildMarker.End();
}
private void InternalRegisterTextElementForGraphicRebuild(TMP_Text element)
{
int id = element.GetInstanceID();
if (m_GraphicQueueLookup.Contains(id))
return;
m_GraphicQueueLookup.Add(id);
m_GraphicRebuildQueue.Add(element);
}
public static void RegisterTextElementForCullingUpdate(TMP_Text element)
{
k_RegisterTextElementForCullingUpdateMarker.Begin();
instance.InternalRegisterTextElementForCullingUpdate(element);
k_RegisterTextElementForCullingUpdateMarker.End();
}
private void InternalRegisterTextElementForCullingUpdate(TMP_Text element)
{
int id = element.GetInstanceID();
if (m_CullingUpdateLookup.Contains(id))
return;
m_CullingUpdateLookup.Add(id);
m_CullingUpdateQueue.Add(element);
}
/// <summary>
/// Callback which occurs just before the cam is rendered.
/// </summary>
void OnCameraPreCull()
{
DoRebuilds();
}
/// <summary>
/// Process the rebuild requests in the rebuild queues.
/// </summary>
void DoRebuilds()
{
// Handle text objects the require an update either as a result of scale changes or legacy animation.
for (int i = 0; i < m_InternalUpdateQueue.Count; i++)
{
m_InternalUpdateQueue[i].InternalUpdate();
}
// Handle Layout Rebuild Phase
for (int i = 0; i < m_LayoutRebuildQueue.Count; i++)
{
m_LayoutRebuildQueue[i].Rebuild(CanvasUpdate.Prelayout);
}
if (m_LayoutRebuildQueue.Count > 0)
{
m_LayoutRebuildQueue.Clear();
m_LayoutQueueLookup.Clear();
}
// Handle Graphic Rebuild Phase
for (int i = 0; i < m_GraphicRebuildQueue.Count; i++)
{
m_GraphicRebuildQueue[i].Rebuild(CanvasUpdate.PreRender);
}
// If there are no objects in the queue, we don't need to clear the lists again.
if (m_GraphicRebuildQueue.Count > 0)
{
m_GraphicRebuildQueue.Clear();
m_GraphicQueueLookup.Clear();
}
// Handle Culling Update
for (int i = 0; i < m_CullingUpdateQueue.Count; i++)
{
m_CullingUpdateQueue[i].UpdateCulling();
}
// If there are no objects in the queue, we don't need to clear the lists again.
if (m_CullingUpdateQueue.Count > 0)
{
m_CullingUpdateQueue.Clear();
m_CullingUpdateLookup.Clear();
}
}
internal static void UnRegisterTextObjectForUpdate(TMP_Text textObject)
{
k_UnregisterTextObjectForUpdateMarker.Begin();
instance.InternalUnRegisterTextObjectForUpdate(textObject);
k_UnregisterTextObjectForUpdateMarker.End();
}
/// <summary>
/// Function to unregister elements which no longer require a rebuild.
/// </summary>
/// <param name="element"></param>
public static void UnRegisterTextElementForRebuild(TMP_Text element)
{
instance.InternalUnRegisterTextElementForGraphicRebuild(element);
instance.InternalUnRegisterTextElementForLayoutRebuild(element);
instance.InternalUnRegisterTextObjectForUpdate(element);
}
private void InternalUnRegisterTextElementForGraphicRebuild(TMP_Text element)
{
k_UnregisterTextElementForGraphicRebuildMarker.Begin();
int id = element.GetInstanceID();
m_GraphicRebuildQueue.Remove(element);
m_GraphicQueueLookup.Remove(id);
k_UnregisterTextElementForGraphicRebuildMarker.End();
}
private void InternalUnRegisterTextElementForLayoutRebuild(TMP_Text element)
{
int id = element.GetInstanceID();
m_LayoutRebuildQueue.Remove(element);
m_LayoutQueueLookup.Remove(id);
}
private void InternalUnRegisterTextObjectForUpdate(TMP_Text textObject)
{
int id = textObject.GetInstanceID();
m_InternalUpdateQueue.Remove(textObject);
m_InternalUpdateLookup.Remove(id);
}
}
}

View File

@@ -0,0 +1,177 @@
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UI.Collections;
using System.Collections;
using System.Collections.Generic;
namespace TMPro
{
/// <summary>
/// Class for handling and scheduling text object updates.
/// </summary>
public class TMP_UpdateRegistry
{
private static TMP_UpdateRegistry s_Instance;
private readonly List<ICanvasElement> m_LayoutRebuildQueue = new List<ICanvasElement>();
private HashSet<int> m_LayoutQueueLookup = new HashSet<int>();
private readonly List<ICanvasElement> m_GraphicRebuildQueue = new List<ICanvasElement>();
private HashSet<int> m_GraphicQueueLookup = new HashSet<int>();
//private bool m_PerformingLayoutUpdate;
//private bool m_PerformingGraphicUpdate;
/// <summary>
/// Get a singleton instance of the registry
/// </summary>
public static TMP_UpdateRegistry instance
{
get
{
if (TMP_UpdateRegistry.s_Instance == null)
TMP_UpdateRegistry.s_Instance = new TMP_UpdateRegistry();
return TMP_UpdateRegistry.s_Instance;
}
}
/// <summary>
/// Register to receive callback from the Canvas System.
/// </summary>
protected TMP_UpdateRegistry()
{
//Debug.Log("Adding WillRenderCanvases");
Canvas.willRenderCanvases += PerformUpdateForCanvasRendererObjects;
}
/// <summary>
/// Function to register elements which require a layout rebuild.
/// </summary>
/// <param name="element"></param>
public static void RegisterCanvasElementForLayoutRebuild(ICanvasElement element)
{
TMP_UpdateRegistry.instance.InternalRegisterCanvasElementForLayoutRebuild(element);
}
private bool InternalRegisterCanvasElementForLayoutRebuild(ICanvasElement element)
{
int id = (element as Object).GetInstanceID();
if (m_LayoutQueueLookup.Contains(id))
return false;
m_LayoutQueueLookup.Add(id);
m_LayoutRebuildQueue.Add(element);
return true;
}
/// <summary>
/// Function to register elements which require a graphic rebuild.
/// </summary>
/// <param name="element"></param>
public static void RegisterCanvasElementForGraphicRebuild(ICanvasElement element)
{
TMP_UpdateRegistry.instance.InternalRegisterCanvasElementForGraphicRebuild(element);
}
private bool InternalRegisterCanvasElementForGraphicRebuild(ICanvasElement element)
{
int id = (element as Object).GetInstanceID();
if (m_GraphicQueueLookup.Contains(id))
return false;
m_GraphicQueueLookup.Add(id);
m_GraphicRebuildQueue.Add(element);
return true;
}
/// <summary>
/// Method to handle objects that need updating.
/// </summary>
private void PerformUpdateForCanvasRendererObjects()
{
//Debug.Log("Performing update of CanvasRenderer objects at Frame: " + Time.frameCount);
// Processing elements that require a layout rebuild.
//this.m_PerformingLayoutUpdate = true;
for (int index = 0; index < m_LayoutRebuildQueue.Count; index++)
{
ICanvasElement element = TMP_UpdateRegistry.instance.m_LayoutRebuildQueue[index];
element.Rebuild(CanvasUpdate.Prelayout);
}
if (m_LayoutRebuildQueue.Count > 0)
{
m_LayoutRebuildQueue.Clear();
m_LayoutQueueLookup.Clear();
}
// Update font assets before graphic rebuild
// Processing elements that require a graphic rebuild.
for (int index = 0; index < m_GraphicRebuildQueue.Count; index++)
{
ICanvasElement element = TMP_UpdateRegistry.instance.m_GraphicRebuildQueue[index];
element.Rebuild(CanvasUpdate.PreRender);
}
// If there are no objects in the queue, we don't need to clear the lists again.
if (m_GraphicRebuildQueue.Count > 0)
{
m_GraphicRebuildQueue.Clear();
m_GraphicQueueLookup.Clear();
}
}
/// <summary>
/// Method to handle objects that need updating.
/// </summary>
private void PerformUpdateForMeshRendererObjects()
{
Debug.Log("Perform update of MeshRenderer objects.");
}
/// <summary>
/// Function to unregister elements which no longer require a rebuild.
/// </summary>
/// <param name="element"></param>
public static void UnRegisterCanvasElementForRebuild(ICanvasElement element)
{
TMP_UpdateRegistry.instance.InternalUnRegisterCanvasElementForLayoutRebuild(element);
TMP_UpdateRegistry.instance.InternalUnRegisterCanvasElementForGraphicRebuild(element);
}
private void InternalUnRegisterCanvasElementForLayoutRebuild(ICanvasElement element)
{
int id = (element as Object).GetInstanceID();
//element.LayoutComplete();
TMP_UpdateRegistry.instance.m_LayoutRebuildQueue.Remove(element);
m_GraphicQueueLookup.Remove(id);
}
private void InternalUnRegisterCanvasElementForGraphicRebuild(ICanvasElement element)
{
int id = (element as Object).GetInstanceID();
//element.GraphicUpdateComplete();
TMP_UpdateRegistry.instance.m_GraphicRebuildQueue.Remove(element);
m_LayoutQueueLookup.Remove(id);
}
}
}

Some files were not shown because too many files have changed in this diff Show More